Pārlūkot izejas kodu

test(web): separate session snapshots from goldens

Tianyi Cui 1 mēnesi atpakaļ
vecāks
revīzija
6189e4a374
100 mainītis faili ar 62 papildinājumiem un 62 dzēšanām
  1. 2 2
      .agents/notes/implemented/architecture/2026-07-30-command-row-copy-contract.i18n.yaml
  2. 1 1
      .agents/notes/implemented/architecture/2026-07-30-command-row-copy-contract.md
  3. 1 1
      .agents/notes/implemented/architecture/2026-07-30-command-row-copy-contract.zh.md
  4. 2 2
      .agents/notes/implemented/architecture/2026-08-08-bounded-session-persistence-write-batching.i18n.yaml
  5. 1 1
      .agents/notes/implemented/architecture/2026-08-08-bounded-session-persistence-write-batching.md
  6. 1 1
      .agents/notes/implemented/architecture/2026-08-08-bounded-session-persistence-write-batching.zh.md
  7. 2 2
      .agents/notes/implemented/feature/2026-07-30-web-search-card.i18n.yaml
  8. 1 1
      .agents/notes/implemented/feature/2026-07-30-web-search-card.md
  9. 1 1
      .agents/notes/implemented/feature/2026-07-30-web-search-card.zh.md
  10. 2 2
      .agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.i18n.yaml
  11. 1 1
      .agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.md
  12. 1 1
      .agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.zh.md
  13. 1 1
      apps/web/tests/access-confirmation.e2e.ts
  14. 1 1
      apps/web/tests/agent-preset-authoring.e2e.ts
  15. 1 1
      apps/web/tests/agent-preset-selection.e2e.ts
  16. 1 1
      apps/web/tests/approval-composer.e2e.ts
  17. 2 2
      apps/web/tests/background-job-list.e2e.ts
  18. 1 1
      apps/web/tests/bash-abort-row.e2e.ts
  19. 2 2
      apps/web/tests/code-mode-round.e2e.ts
  20. 1 1
      apps/web/tests/cold-blank-session.e2e.ts
  21. 1 1
      apps/web/tests/composer-draft-scroll.e2e.ts
  22. 1 1
      apps/web/tests/composer-tab-geometry.e2e.ts
  23. 1 1
      apps/web/tests/conversation-column-overflow.e2e.ts
  24. 2 2
      apps/web/tests/cordis-tool-round.e2e.ts
  25. 2 2
      apps/web/tests/declared-reasoning.e2e.ts
  26. 3 3
      apps/web/tests/details-session-lifecycle.e2e.ts
  27. 1 1
      apps/web/tests/feedback-command.e2e.ts
  28. 1 1
      apps/web/tests/github-ready-review.e2e.ts
  29. 1 1
      apps/web/tests/goal-bar.e2e.ts
  30. 2 2
      apps/web/tests/goal-command-presentation.e2e.ts
  31. 1 1
      apps/web/tests/goal-multi-turn-actions.e2e.ts
  32. 0 0
      apps/web/tests/goldens/access-confirmation/ui.expected.md
  33. 0 0
      apps/web/tests/goldens/agent-preset-authoring/copy-dialog.expected.md
  34. 0 0
      apps/web/tests/goldens/agent-preset-authoring/created.expected.md
  35. 0 0
      apps/web/tests/goldens/agent-preset-authoring/damaged.expected.md
  36. 0 0
      apps/web/tests/goldens/agent-preset-authoring/section.expected.md
  37. 0 0
      apps/web/tests/goldens/agent-preset-selection/header.expected.md
  38. 0 0
      apps/web/tests/goldens/agent-preset-selection/hero.expected.md
  39. 0 0
      apps/web/tests/goldens/agent-preset-selection/menu.expected.md
  40. 0 0
      apps/web/tests/goldens/cold-blank-session/sidebar.expected.md
  41. 0 0
      apps/web/tests/goldens/composer-draft-scroll/geometry.expected.md
  42. 0 0
      apps/web/tests/goldens/composer-tab-geometry/geometry.expected.md
  43. 0 0
      apps/web/tests/goldens/conversation-column-overflow/geometry.expected.md
  44. 0 0
      apps/web/tests/goldens/declared-reasoning/ui.expected.md
  45. 0 0
      apps/web/tests/goldens/github-ready-review/conversation.expected.md
  46. 0 0
      apps/web/tests/goldens/goal-bar/active.expected.md
  47. 0 0
      apps/web/tests/goldens/goal-command-presentation/ui.expected.md
  48. 0 0
      apps/web/tests/goldens/home-path-tilde/workspace-hover.expected.txt
  49. 0 0
      apps/web/tests/goldens/markdown-cjk-strong/ui.expected.md
  50. 0 0
      apps/web/tests/goldens/markdown-images/ui.expected.md
  51. 0 0
      apps/web/tests/goldens/markdown-inline-code-links/ui.expected.md
  52. 0 0
      apps/web/tests/goldens/markdown-wide-table/geometry.expected.md
  53. 0 0
      apps/web/tests/goldens/math-rendering/ui.expected.md
  54. 0 0
      apps/web/tests/goldens/max-tokens-notice/history-turn.expected.txt
  55. 0 0
      apps/web/tests/goldens/models-settings/configured.expected.md
  56. 0 0
      apps/web/tests/goldens/models-settings/declared-edit.expected.md
  57. 0 0
      apps/web/tests/goldens/models-settings/declared.expected.md
  58. 0 0
      apps/web/tests/goldens/models-settings/delete.expected.md
  59. 0 0
      apps/web/tests/goldens/models-settings/empty.expected.md
  60. 0 0
      apps/web/tests/goldens/models-settings/model-picker.expected.md
  61. 0 0
      apps/web/tests/goldens/models-settings/native-delete.expected.md
  62. 0 0
      apps/web/tests/goldens/onboarding-deepseek-config/missing.expected.md
  63. 0 0
      apps/web/tests/goldens/onboarding-deepseek-config/models.expected.md
  64. 0 0
      apps/web/tests/goldens/onboarding-deepseek-config/welcome.expected.md
  65. 0 0
      apps/web/tests/goldens/onboarding-usable-provider/dismissed.expected.md
  66. 0 0
      apps/web/tests/goldens/plugin-config/section.expected.md
  67. 0 0
      apps/web/tests/goldens/reference-composer/caret-edits.expected.md
  68. 0 0
      apps/web/tests/goldens/reference-composer/menu.expected.md
  69. 0 0
      apps/web/tests/goldens/reference-composer/order.expected.md
  70. 0 0
      apps/web/tests/goldens/schedule-after/at-conversation.expected.md
  71. 0 0
      apps/web/tests/goldens/schedule-after/conversation.expected.md
  72. 0 0
      apps/web/tests/goldens/schedule-after/every-conversation.expected.md
  73. 0 0
      apps/web/tests/goldens/search-card/grep-card.expected.txt
  74. 0 0
      apps/web/tests/goldens/settings-chrome/dialog-en.expected.md
  75. 0 0
      apps/web/tests/goldens/settings-chrome/dialog.expected.md
  76. 0 0
      apps/web/tests/goldens/settings-chrome/plugins.expected.md
  77. 0 0
      apps/web/tests/goldens/sidebar-subagent-activity/owner-running.expected.md
  78. 0 0
      apps/web/tests/goldens/skill-invocation-policy/menu.expected.md
  79. 0 0
      apps/web/tests/goldens/skill-user-invoke/ui.expected.md
  80. 0 0
      apps/web/tests/goldens/stats-paged-history/ui.expected.md
  81. 0 0
      apps/web/tests/goldens/steer-all/mid-steer.expected.md
  82. 0 0
      apps/web/tests/goldens/steer-all/replay.override.json
  83. 0 0
      apps/web/tests/goldens/steer-all/settled.expected.md
  84. 0 0
      apps/web/tests/goldens/todo-row/parallel-plan.expected.txt
  85. 0 0
      apps/web/tests/goldens/trajectory-virtualization/load-more.expected.md
  86. 0 0
      apps/web/tests/goldens/web-runtime-context/file-reference-prompt.expected.md
  87. 0 0
      apps/web/tests/goldens/web-runtime-context/web-surface-prompt.expected.md
  88. 1 1
      apps/web/tests/home-path-tilde.snapshot.ts
  89. 1 1
      apps/web/tests/lifecycle-chrome.e2e.ts
  90. 1 1
      apps/web/tests/live-interactions.e2e.ts
  91. 2 2
      apps/web/tests/markdown-cjk-strong.e2e.ts
  92. 2 2
      apps/web/tests/markdown-images.e2e.ts
  93. 2 2
      apps/web/tests/markdown-inline-code-links.e2e.ts
  94. 2 2
      apps/web/tests/markdown-wide-table.e2e.ts
  95. 2 2
      apps/web/tests/math-rendering.e2e.ts
  96. 1 1
      apps/web/tests/max-tokens-notice.snapshot.ts
  97. 2 2
      apps/web/tests/message-actions.e2e.ts
  98. 2 2
      apps/web/tests/message-feedback-layout.e2e.ts
  99. 1 1
      apps/web/tests/message-feedback-protocol.snapshot.ts
  100. 1 1
      apps/web/tests/message-feedback.e2e.ts

+ 2 - 2
.agents/notes/implemented/architecture/2026-07-30-command-row-copy-contract.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-07-30-command-row-copy-contract.md
-2026-07-30-command-row-copy-contract.md: f6d5199389b3907780c501894e2861e6add85e77
-2026-07-30-command-row-copy-contract.zh.md: 86f0c044e841a6a0148b1f785582868bba5bb3d0
+2026-07-30-command-row-copy-contract.md: d410adf62dbbc4445936c70309d5902bc184b16f
+2026-07-30-command-row-copy-contract.zh.md: 0f680f9beb8b335dd6311cadc6c7e022809fb8f2

+ 1 - 1
.agents/notes/implemented/architecture/2026-07-30-command-row-copy-contract.md

@@ -32,4 +32,4 @@ The log is unchanged: `command/run` keeps the structured `name`/`args` split, so
 
 ## Consequences
 
-Every command row gets shorter, and the rule scales: a new command's author writes its outcome without knowing which surface renders it, and no surface has to de-duplicate. The cost is that the dispatched arguments leave the collapsed row — while a command is still executing the row shows only its name and `执行中…` — and that the no-caption rule is a convention the reviewer enforces, not a gate. The `/permission` texts are pinned by the permission package's command tests, and the assembled row copy by the [seeded-history](../../../../apps/web/tests/snapshots/seeded-history/command-row.expected.md) web golden, which reaches a real settled command row keylessly because `/permission` runs entirely on the host.
+Every command row gets shorter, and the rule scales: a new command's author writes its outcome without knowing which surface renders it, and no surface has to de-duplicate. The cost is that the dispatched arguments leave the collapsed row — while a command is still executing the row shows only its name and `执行中…` — and that the no-caption rule is a convention the reviewer enforces, not a gate. The `/permission` texts are pinned by the permission package's command tests, and the assembled row copy by the [seeded-history](../../../../snapshots/web/seeded-history/command-row.expected.md) web golden, which reaches a real settled command row keylessly because `/permission` runs entirely on the host.

+ 1 - 1
.agents/notes/implemented/architecture/2026-07-30-command-row-copy-contract.zh.md

@@ -32,4 +32,4 @@ Web 命令条目由一对落库的[命令生命周期事件](../../proposed/arch
 
 ## 后果
 
-每一条命令条目都变短了,而且这条规则可扩展:新命令的作者写结果时无需知道由哪个界面渲染,任何界面也都不必再去重。代价是分派参数离开了折叠行——命令仍在执行时,行上只有名字和 `执行中…`——以及「不加题头」这条规则是靠评审执行的约定,而非门禁。`/permission` 的文案由 permission 包的命令测试钉住,装配后的行文案由 [seeded-history](../../../../apps/web/tests/snapshots/seeded-history/command-row.expected.md) web 预期输出钉住:由于 `/permission` 完全在 host 上执行,该预期输出无需密钥即可覆盖一条真实的已落定命令条目。
+每一条命令条目都变短了,而且这条规则可扩展:新命令的作者写结果时无需知道由哪个界面渲染,任何界面也都不必再去重。代价是分派参数离开了折叠行——命令仍在执行时,行上只有名字和 `执行中…`——以及「不加题头」这条规则是靠评审执行的约定,而非门禁。`/permission` 的文案由 permission 包的命令测试钉住,装配后的行文案由 [seeded-history](../../../../snapshots/web/seeded-history/command-row.expected.md) web 预期输出钉住:由于 `/permission` 完全在 host 上执行,该预期输出无需密钥即可覆盖一条真实的已落定命令条目。

+ 2 - 2
.agents/notes/implemented/architecture/2026-08-08-bounded-session-persistence-write-batching.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-08-08-bounded-session-persistence-write-batching.md
-2026-08-08-bounded-session-persistence-write-batching.md: 6d77ef90276dcf143bb63019f801379b702b43b2
-2026-08-08-bounded-session-persistence-write-batching.zh.md: e773f20ade706e97a5f84d4a4cc15cc9d3b2d836
+2026-08-08-bounded-session-persistence-write-batching.md: fc22a10537ebb9009ab1ae1ec21ca625c9025651
+2026-08-08-bounded-session-persistence-write-batching.zh.md: c2763e157fcfc2e004b14116694054c604fbfb9c

+ 1 - 1
.agents/notes/implemented/architecture/2026-08-08-bounded-session-persistence-write-batching.md

@@ -12,7 +12,7 @@ Dropping chunk events or replacing them with assembled messages would reduce log
 
 ### Quantified baseline
 
-Repository fixtures make the logical volume concrete. Decoding the current packed rows in [`goal-multi-turn-actions`](../../../../apps/web/tests/snapshots/goal-multi-turn-actions/session.jsonl) yields 2,098 events: 2,017 chunks (96.1%). Their unpacked JSONL lines occupy 332,647 of 379,225 event bytes (87.7%), while chunk packing reduces the committed file to 89,176 bytes and 182 storage rows, including 23 packed chunk rows. [`permission-policy-context`](../../../../apps/web/tests/snapshots/permission-policy-context/session.jsonl) yields 813 events: 746 chunks (91.8%) and 118,935 of 184,821 unpacked event bytes (64.4%); its packed file is 84,917 bytes and 123 storage rows, including 14 packed rows. These are tracked deterministic fixtures, not a production workload distribution, but they demonstrate why deleting chunks would reduce logical volume and why the existing packed-row layout already removes much of their JSON envelope cost.
+Repository fixtures make the logical volume concrete. Decoding the current packed rows in [`goal-multi-turn-actions`](../../../../snapshots/web/goal-multi-turn-actions/session.jsonl) yields 2,098 events: 2,017 chunks (96.1%). Their unpacked JSONL lines occupy 332,647 of 379,225 event bytes (87.7%), while chunk packing reduces the committed file to 89,176 bytes and 182 storage rows, including 23 packed chunk rows. [`permission-policy-context`](../../../../snapshots/web/permission-policy-context/session.jsonl) yields 813 events: 746 chunks (91.8%) and 118,935 of 184,821 unpacked event bytes (64.4%); its packed file is 84,917 bytes and 123 storage rows, including 14 packed rows. These are tracked deterministic fixtures, not a production workload distribution, but they demonstrate why deleting chunks would reduce logical volume and why the existing packed-row layout already removes much of their JSON envelope cost.
 
 SQLite stores one row per logical event, so those same logical logs would retain 2,098 and 813 event rows respectively; batching does not change those counts. JSONL writes one Zstandard frame and fsync per durable append batch, while SQLite performs one transaction and one session-revision increment per batch. Runtime files do not record former append boundaries, so fixture row counts cannot honestly be presented as fsync or transaction counts.
 

+ 1 - 1
.agents/notes/implemented/architecture/2026-08-08-bounded-session-persistence-write-batching.zh.md

@@ -12,7 +12,7 @@ Status: implemented
 
 ### 量化基线
 
-仓库 fixture(测试前置数据)让逻辑数据量有了具体依据。对当前 [`goal-multi-turn-actions`](../../../../apps/web/tests/snapshots/goal-multi-turn-actions/session.jsonl) 中的打包行进行解码,可得到 2,098 个事件,其中 2,017 个是分片(96.1%)。这些分片解包后的 JSONL 行共 332,647 字节,占全部事件 379,225 字节的 87.7%;分片打包则把仓库中的已提交文件缩小到 89,176 字节和 182 个存储行,其中包括 23 个打包分片行。[`permission-policy-context`](../../../../apps/web/tests/snapshots/permission-policy-context/session.jsonl) 可得到 813 个事件,其中 746 个是分片(91.8%);这些分片解包后的 JSONL 行共 118,935 字节,占全部事件 184,821 字节的 64.4%。其打包文件为 84,917 字节,共 123 个存储行,其中包括 14 个打包行。这些是纳入版本控制的确定性 fixture,不代表生产工作负载分布;但它们说明了删除分片为何会降低逻辑数据量,也说明现有打包行布局已经消除了大量 JSON 包装开销。
+仓库 fixture(测试前置数据)让逻辑数据量有了具体依据。对当前 [`goal-multi-turn-actions`](../../../../snapshots/web/goal-multi-turn-actions/session.jsonl) 中的打包行进行解码,可得到 2,098 个事件,其中 2,017 个是分片(96.1%)。这些分片解包后的 JSONL 行共 332,647 字节,占全部事件 379,225 字节的 87.7%;分片打包则把仓库中的已提交文件缩小到 89,176 字节和 182 个存储行,其中包括 23 个打包分片行。[`permission-policy-context`](../../../../snapshots/web/permission-policy-context/session.jsonl) 可得到 813 个事件,其中 746 个是分片(91.8%);这些分片解包后的 JSONL 行共 118,935 字节,占全部事件 184,821 字节的 64.4%。其打包文件为 84,917 字节,共 123 个存储行,其中包括 14 个打包行。这些是纳入版本控制的确定性 fixture,不代表生产工作负载分布;但它们说明了删除分片为何会降低逻辑数据量,也说明现有打包行布局已经消除了大量 JSON 包装开销。
 
 SQLite 每个逻辑事件存储一行,因此同样的逻辑日志会分别保留 2,098 和 813 个事件行;批处理不会改变这些数量。JSONL 每个持久化追加批次会写入一个 Zstandard 帧并执行一次 fsync,SQLite 每个批次会执行一次事务并递增一次会话修订版本。运行时文件不记录原有追加边界,因此不能把 fixture 的存储行数当作 fsync 或事务次数。
 

+ 2 - 2
.agents/notes/implemented/feature/2026-07-30-web-search-card.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-07-30-web-search-card.md
-2026-07-30-web-search-card.md: 930a705fc227d7961e9cb1a51b0b04774a62c6a4
-2026-07-30-web-search-card.zh.md: 7771097423bda24c7fe254d5747205c1f06a5474
+2026-07-30-web-search-card.md: 7b16c401a4cac45a218ba0b42743b38171f99d9e
+2026-07-30-web-search-card.zh.md: 4fd653ef519d3ba1cb5e6e3e9c9275b3cc8127bb

+ 1 - 1
.agents/notes/implemented/feature/2026-07-30-web-search-card.md

@@ -56,7 +56,7 @@ Three sites consume the derivation, mirroring the terminal card's placement exac
 
 `packages/client/ui-primitives/tests/search-block.client.spec.tsx` pins the component at per-file 100%: both kinds, the folded pre-cap total in the summary, the empty arm, per-file collapse/re-expand without touching neighbours, a file header counting as one capped row alongside its matches, the tail slice restoring its owning file header when the cut falls mid-file, the head/tail cap and its expand control across both shapes and the no-tail and default-cap edges, and the copy control writing the whole structured result on the accepted and refused clipboard paths.
 
-`packages/client/ui-tool/tests/search-card.client.spec.tsx` pins the wiring at every render site: `searchCardModel`'s derivation for both kinds, the truncation signal, the replacement title, the recovery text surfaced only when capped, each null arm (running, no views, generic, terminal, unknown card, an uncompiled `kind`, and a known kind with a missing/malformed shape); the chat row's expand-gated matches and paths bodies through `GenericToolCard` (with the recovery footer) against the non-search args-JSON body; `SearchRow`'s resident card for both kinds, its recovery footer, its fallback body for both an errored search and a settled cardless result, its agreement with the summary row's run state, the replacement-title precedence, and the keyed registration under both `grep` and `glob` with one component; and the details panel's Output section for both kinds (with the recovery footer) against the non-search flattened form. `packages/client/ui-tool/src/*` sits on the coverage exclude list, so this file is written against no gate pressure. `packages/client/connection/src/client/fixture.ts` gains a `grep` turn emitting `kind: 'matches'` (three files, twelve rows over the row cap, `truncated` with a spill-recovery footer, so it exercises the head/tail cap and the recovery footer in the assembled snapshot) and a `glob` turn emitting `kind: 'paths'`, both driving the built-boot snapshot and the live `?fixture` server. `apps/web/tests/search-card.snapshot.ts` is the assembled-output check the repo contract asks for: it boots the real built `client.js` bundles through the keyless fixture transport, opens the fixture session, and pins the grep card's assembled shape — kind, truncation summary, the head/tail slice, and its expand control — under `apps/web/tests/snapshots/search-card/`, so a broken SearchRow registration or a dropped card fails a golden the built-boot smoke (boot-only by contract) cannot.
+`packages/client/ui-tool/tests/search-card.client.spec.tsx` pins the wiring at every render site: `searchCardModel`'s derivation for both kinds, the truncation signal, the replacement title, the recovery text surfaced only when capped, each null arm (running, no views, generic, terminal, unknown card, an uncompiled `kind`, and a known kind with a missing/malformed shape); the chat row's expand-gated matches and paths bodies through `GenericToolCard` (with the recovery footer) against the non-search args-JSON body; `SearchRow`'s resident card for both kinds, its recovery footer, its fallback body for both an errored search and a settled cardless result, its agreement with the summary row's run state, the replacement-title precedence, and the keyed registration under both `grep` and `glob` with one component; and the details panel's Output section for both kinds (with the recovery footer) against the non-search flattened form. `packages/client/ui-tool/src/*` sits on the coverage exclude list, so this file is written against no gate pressure. `packages/client/connection/src/client/fixture.ts` gains a `grep` turn emitting `kind: 'matches'` (three files, twelve rows over the row cap, `truncated` with a spill-recovery footer, so it exercises the head/tail cap and the recovery footer in the assembled snapshot) and a `glob` turn emitting `kind: 'paths'`, both driving the built-boot snapshot and the live `?fixture` server. `apps/web/tests/search-card.snapshot.ts` is the assembled-output check the repo contract asks for: it boots the real built `client.js` bundles through the keyless fixture transport, opens the fixture session, and pins the grep card's assembled shape — kind, truncation summary, the head/tail slice, and its expand control — under `apps/web/tests/goldens/search-card/`, so a broken SearchRow registration or a dropped card fails a golden the built-boot smoke (boot-only by contract) cannot.
 
 ## Related
 

+ 1 - 1
.agents/notes/implemented/feature/2026-07-30-web-search-card.zh.md

@@ -56,7 +56,7 @@ Status: implemented
 
 `packages/client/ui-primitives/tests/search-block.client.spec.tsx` 以 per-file 100% 覆盖固定组件:两种 kind、折入摘要的截断前总数、空结果分支、逐文件折叠/再展开且不影响邻居、一个文件头与匹配行一样,在高度上限中单独计为一行、切口落在文件中间时尾部切片恢复其所属文件头、跨两种形态的头/尾上限及其展开控件(含无尾与默认上限的边界),以及复制控件在接受与拒绝的剪贴板路径上写入整个结构化结果。
 
-`packages/client/ui-tool/tests/search-card.client.spec.tsx` 固定每个渲染点的接线:`searchCardModel` 对两种 kind 的推导、截断信号、替换标题、仅在截断时暴露的恢复文本,以及每个 null 分支(运行中、无视图、generic、terminal、未知卡片、本版本无法编译的 `kind`、以及一个形态缺失/错误的已知 kind);通过 `GenericToolCard` 的展开门控 matches 与 paths body(含恢复脚注),对照非搜索的 args-JSON body;`SearchRow` 对两种 kind 的常驻卡片、它的恢复脚注、它对出错搜索与已结算无卡片结果两者的 fallback body、它与摘要行运行状态的一致、替换标题优先级,以及一个组件在 `grep` 与 `glob` 两个键下的 keyed 注册;以及 details panel 的 Output 段对两种 kind(含恢复脚注),对照非搜索的压平形态。`packages/client/ui-tool/src/*` 在覆盖排除清单上,因此该文件不受 gate 压力。`packages/client/connection/src/client/fixture.ts` 新增一个发出 `kind: 'matches'` 的 `grep` turn(三个文件、十二行超过行内上限、`truncated` 且带溢出恢复脚注,因此在组装快照里同时演练头/尾上限与恢复脚注)与一个发出 `kind: 'paths'` 的 `glob` turn,两者都驱动 built-boot snapshot 与实时 `?fixture` 服务。`apps/web/tests/search-card.snapshot.ts` 是仓库约定要求的组装输出检查:它通过 keyless fixture 传输启动真实构建的 `client.js` bundle,打开 fixture 会话,并把 grep 卡片的组装形态——kind、截断摘要、头/尾切片及其展开控件——固定在 `apps/web/tests/snapshots/search-card/` 下,因此一个损坏的 SearchRow 注册或被丢弃的卡片会让一个 golden 失败,而 built-boot smoke(按约定只测启动)无法捕获它。
+`packages/client/ui-tool/tests/search-card.client.spec.tsx` 固定每个渲染点的接线:`searchCardModel` 对两种 kind 的推导、截断信号、替换标题、仅在截断时暴露的恢复文本,以及每个 null 分支(运行中、无视图、generic、terminal、未知卡片、本版本无法编译的 `kind`、以及一个形态缺失/错误的已知 kind);通过 `GenericToolCard` 的展开门控 matches 与 paths body(含恢复脚注),对照非搜索的 args-JSON body;`SearchRow` 对两种 kind 的常驻卡片、它的恢复脚注、它对出错搜索与已结算无卡片结果两者的 fallback body、它与摘要行运行状态的一致、替换标题优先级,以及一个组件在 `grep` 与 `glob` 两个键下的 keyed 注册;以及 details panel 的 Output 段对两种 kind(含恢复脚注),对照非搜索的压平形态。`packages/client/ui-tool/src/*` 在覆盖排除清单上,因此该文件不受 gate 压力。`packages/client/connection/src/client/fixture.ts` 新增一个发出 `kind: 'matches'` 的 `grep` turn(三个文件、十二行超过行内上限、`truncated` 且带溢出恢复脚注,因此在组装快照里同时演练头/尾上限与恢复脚注)与一个发出 `kind: 'paths'` 的 `glob` turn,两者都驱动 built-boot snapshot 与实时 `?fixture` 服务。`apps/web/tests/search-card.snapshot.ts` 是仓库约定要求的组装输出检查:它通过 keyless fixture 传输启动真实构建的 `client.js` bundle,打开 fixture 会话,并把 grep 卡片的组装形态——kind、截断摘要、头/尾切片及其展开控件——固定在 `apps/web/tests/goldens/search-card/` 下,因此一个损坏的 SearchRow 注册或被丢弃的卡片会让一个 golden 失败,而 built-boot smoke(按约定只测启动)无法捕获它。
 
 ## Related
 

+ 2 - 2
.agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.md
-2026-07-30-web-browser-snapshot-ci-gate.md: abb197f2a8e3488e2ae1edc7c2e77cbdcf919556
-2026-07-30-web-browser-snapshot-ci-gate.zh.md: 0239ae6062d2828b8b92683d4b0e7103205756fc
+2026-07-30-web-browser-snapshot-ci-gate.md: e94141070ba283434f49108899f743fab6073ebc
+2026-07-30-web-browser-snapshot-ci-gate.zh.md: 9976034bb0ff8ba0435be41fe9e6bd406267fadb

+ 1 - 1
.agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.md

@@ -6,7 +6,7 @@ English | [中文](2026-07-30-web-browser-snapshot-ci-gate.zh.md)
 
 ## Problem
 
-The [keyless web browser e2e lane](2026-07-24-web-gui-browser-e2e-lane.md) runs only under the local `pnpm run test:web` command, and PR CI does not compare `apps/web/tests/snapshots/**/*.expected.md`. A PR that changes user-visible web output can therefore remain green when its expected outputs are not refreshed; when any later branch explicitly runs `DSH_SNAPSHOT=refresh`, it backfills the earlier change and produces a diff unrelated to that branch. Ordinary local runs already default to read-only replay, so the gap is mandatory enforcement at the PR level, not a ban on writes in refresh mode.
+The [keyless web browser e2e lane](2026-07-24-web-gui-browser-e2e-lane.md) runs only under the local `pnpm run test:web` command, and PR CI does not compare Web expectations under `snapshots/web/` or `apps/web/tests/goldens/`. A PR that changes user-visible web output can therefore remain green when its expected outputs are not refreshed; when any later branch explicitly runs `DSH_SNAPSHOT=refresh`, it backfills the earlier change and produces a diff unrelated to that branch. Ordinary local runs already default to read-only replay, so the gap is mandatory enforcement at the PR level, not a ban on writes in refresh mode.
 
 ## Decision
 

+ 1 - 1
.agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.zh.md

@@ -6,7 +6,7 @@ Status: implemented
 
 ## 问题
 
-[无密钥 Web 浏览器 e2e 车道](2026-07-24-web-gui-browser-e2e-lane.zh.md)只由本地 `pnpm run test:web` 运行,PR CI 不比较 `apps/web/tests/snapshots/**/*.expected.md`。因此,改变用户可见 Web 输出的 PR 可以在漏刷预期输出时保持绿色;后来任意分支显式运行 `DSH_SNAPSHOT=refresh`,都会替前序变更补账并产生与本分支无关的 diff。普通本地运行已经默认使用只读 replay,缺口是 PR 级的强制执行,而不是禁止 refresh 写入。
+[无密钥 Web 浏览器 e2e 车道](2026-07-24-web-gui-browser-e2e-lane.zh.md)只由本地 `pnpm run test:web` 运行,PR CI 不比较 `snapshots/web/` 或 `apps/web/tests/goldens/` 下的 Web 预期输出。因此,改变用户可见 Web 输出的 PR 可以在漏刷预期输出时保持绿色;后来任意分支显式运行 `DSH_SNAPSHOT=refresh`,都会替前序变更补账并产生与本分支无关的 diff。普通本地运行已经默认使用只读 replay,缺口是 PR 级的强制执行,而不是禁止 refresh 写入。
 
 ## 决策
 

+ 1 - 1
apps/web/tests/access-confirmation.e2e.ts

@@ -13,7 +13,7 @@ import {
 } from './scaffold.ts'
 import { ZH_BROWSER_LOCALE, connectFreshWorkspaceZh, saveFailureShot } from './support.ts'
 
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/access-confirmation', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('./goldens/access-confirmation', import.meta.url))
 const UI_EXPECTED = join(SNAPSHOT_DIR, 'ui.expected.md')
 const MODE = webSnapshotMode()
 

+ 1 - 1
apps/web/tests/agent-preset-authoring.e2e.ts

@@ -22,7 +22,7 @@ import {
 } from './scaffold.ts'
 import { ZH_BROWSER_LOCALE, connectFreshWorkspaceZh, saveFailureShot } from './support.ts'
 
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/agent-preset-authoring', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('./goldens/agent-preset-authoring', import.meta.url))
 const SECTION_EXPECTED = join(SNAPSHOT_DIR, 'section.expected.md')
 const COPY_DIALOG_EXPECTED = join(SNAPSHOT_DIR, 'copy-dialog.expected.md')
 const CREATED_EXPECTED = join(SNAPSHOT_DIR, 'created.expected.md')

+ 1 - 1
apps/web/tests/agent-preset-selection.e2e.ts

@@ -24,7 +24,7 @@ import {
 } from './scaffold.ts'
 import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts'
 
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/agent-preset-selection', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('./goldens/agent-preset-selection', import.meta.url))
 const HERO_EXPECTED = join(SNAPSHOT_DIR, 'hero.expected.md')
 const MENU_EXPECTED = join(SNAPSHOT_DIR, 'menu.expected.md')
 const HEADER_EXPECTED = join(SNAPSHOT_DIR, 'header.expected.md')

+ 1 - 1
apps/web/tests/approval-composer.e2e.ts

@@ -16,7 +16,7 @@ import {
 } from './scaffold.ts'
 import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts'
 
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/approval-composer', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/approval-composer', import.meta.url))
 const FIXTURE = join(SNAPSHOT_DIR, 'session.jsonl')
 // The golden covers the stable waiting panel; direct assertions cover its answer.
 const UI_EXPECTED = join(SNAPSHOT_DIR, 'ui.expected.md')

+ 2 - 2
apps/web/tests/background-job-list.e2e.ts

@@ -16,8 +16,8 @@ import {
 } from './scaffold.ts'
 import { newEnglishPage, saveFailureShot } from './support.ts'
 
-const FIXTURE = fileURLToPath(new URL('./snapshots/fresh-round-trip/session.jsonl', import.meta.url))
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/background-job-list', import.meta.url))
+const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/fresh-round-trip/session.jsonl', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/background-job-list', import.meta.url))
 const RUNNING_EXPECTED = join(SNAPSHOT_DIR, 'running.expected.md')
 const SETTLED_EXPECTED = join(SNAPSHOT_DIR, 'settled.expected.md')
 const MODE = webSnapshotMode()

+ 1 - 1
apps/web/tests/bash-abort-row.e2e.ts

@@ -14,7 +14,7 @@ import {
 import { newEnglishPage, saveFailureShot } from './support.ts'
 
 const FIXTURE = fileURLToPath(new URL('../../../snapshots/acp/cancel-tool-calls/session.jsonl', import.meta.url))
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/bash-abort-row', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/bash-abort-row', import.meta.url))
 const UI_EXPECTED = join(SNAPSHOT_DIR, 'ui.expected.md')
 const MODE = webSnapshotMode()
 const SEED_ID = 'bash-abort-row-web-e2e'

+ 2 - 2
apps/web/tests/code-mode-round.e2e.ts

@@ -13,8 +13,8 @@ import {
 } from './scaffold.ts'
 import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts'
 
-const FIXTURE = fileURLToPath(new URL('./snapshots/code-mode-round/session.jsonl', import.meta.url))
-const UI_EXPECTED = fileURLToPath(new URL('./snapshots/code-mode-round/ui.expected.md', import.meta.url))
+const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/code-mode-round/session.jsonl', import.meta.url))
+const UI_EXPECTED = fileURLToPath(new URL('../../../snapshots/web/code-mode-round/ui.expected.md', import.meta.url))
 const MODE = webSnapshotMode()
 
 // Elicits the successful and failed sub-rows this scenario asserts.

+ 1 - 1
apps/web/tests/cold-blank-session.e2e.ts

@@ -12,7 +12,7 @@ import {
 } from './scaffold.ts'
 import { newEnglishPage, saveFailureShot } from './support.ts'
 
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/cold-blank-session', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('./goldens/cold-blank-session', import.meta.url))
 const SIDEBAR_EXPECTED = join(SNAPSHOT_DIR, 'sidebar.expected.md')
 const MODE = webSnapshotMode()
 const SESSION_ID = 'cold-blank-session-web-e2e'

+ 1 - 1
apps/web/tests/composer-draft-scroll.e2e.ts

@@ -12,7 +12,7 @@ import {
 } from './scaffold.ts'
 import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts'
 
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/composer-draft-scroll', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('./goldens/composer-draft-scroll', import.meta.url))
 /** Scroll geometry is absent from ARIA snapshots, so this scenario records it directly. */
 const GEOMETRY_EXPECTED = join(SNAPSHOT_DIR, 'geometry.expected.md')
 const MODE = webSnapshotMode()

+ 1 - 1
apps/web/tests/composer-tab-geometry.e2e.ts

@@ -13,7 +13,7 @@ import {
 } from './scaffold.ts'
 import { newEnglishPage, saveFailureShot } from './support.ts'
 
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/composer-tab-geometry', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('./goldens/composer-tab-geometry', import.meta.url))
 /** Records platform-neutral distances between the two tabs' card rectangles. */
 const GEOMETRY_EXPECTED = join(SNAPSHOT_DIR, 'geometry.expected.md')
 const MODE = webSnapshotMode()

+ 1 - 1
apps/web/tests/conversation-column-overflow.e2e.ts

@@ -31,7 +31,7 @@ import {
 } from './scaffold.ts'
 import { newEnglishPage, saveFailureShot } from './support.ts'
 
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/conversation-column-overflow', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('./goldens/conversation-column-overflow', import.meta.url))
 /**
  * Committed golden of the one-axis relation at every stop. It records
  * relations and booleans, never absolute coordinates: the column width follows

+ 2 - 2
apps/web/tests/cordis-tool-round.e2e.ts

@@ -20,8 +20,8 @@ import {
 } from './scaffold.ts'
 import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts'
 
-const FIXTURE = fileURLToPath(new URL('./snapshots/cordis-tool-round/session.jsonl', import.meta.url))
-const UI_EXPECTED = fileURLToPath(new URL('./snapshots/cordis-tool-round/ui.expected.md', import.meta.url))
+const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/cordis-tool-round/session.jsonl', import.meta.url))
+const UI_EXPECTED = fileURLToPath(new URL('../../../snapshots/web/cordis-tool-round/ui.expected.md', import.meta.url))
 const MODE = webSnapshotMode()
 const CORDIS_TOOLS = ['cordis_inspect_self', 'cordis_define', 'cordis_run', 'cordis_stop'] as const
 const PACKAGE_CODE = 'return { name: "snapshot-noop", apply(ctx) {} }'

+ 2 - 2
apps/web/tests/declared-reasoning.e2e.ts

@@ -18,8 +18,8 @@ import { ZH_BROWSER_LOCALE, connectFreshWorkspaceZh, saveFailureShot } from './s
 
 /** Starts the shipped default on this scenario's declared reasoning model. */
 const OVERLAY = fileURLToPath(new URL('./declared-reasoning.overlay.yml', import.meta.url))
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/declared-reasoning', import.meta.url))
-const UI_EXPECTED = fileURLToPath(new URL('./snapshots/declared-reasoning/ui.expected.md', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('./goldens/declared-reasoning', import.meta.url))
+const UI_EXPECTED = fileURLToPath(new URL('./goldens/declared-reasoning/ui.expected.md', import.meta.url))
 const MODE = webSnapshotMode()
 
 describe.skipIf(MODE === 'record')('web e2e: declared reasoning efforts reach the composer', () => {

+ 3 - 3
apps/web/tests/details-session-lifecycle.e2e.ts

@@ -14,10 +14,10 @@ import {
 } from './scaffold.ts'
 import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts'
 
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/details-session-lifecycle', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/details-session-lifecycle', import.meta.url))
 const HANDLES_EXPECTED = join(SNAPSHOT_DIR, 'handles.expected.md')
-const FIXTURE = fileURLToPath(new URL('./snapshots/lifecycle-chrome/session.jsonl', import.meta.url))
-const SEED_FIXTURE = fileURLToPath(new URL('./snapshots/seeded-history/seed.jsonl', import.meta.url))
+const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/lifecycle-chrome/session.jsonl', import.meta.url))
+const SEED_FIXTURE = fileURLToPath(new URL('../../../snapshots/web/seeded-history/seed.jsonl', import.meta.url))
 const PROMPT = 'Reply with the single word LIGHTHOUSE and stop.'
 const MODE = webSnapshotMode()
 

+ 1 - 1
apps/web/tests/feedback-command.e2e.ts

@@ -20,7 +20,7 @@ import {
 } from './scaffold.ts'
 import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts'
 
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/feedback-command', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/feedback-command', import.meta.url))
 const FIXTURE = join(SNAPSHOT_DIR, 'session.jsonl')
 const ACK_EXPECTED = join(SNAPSHOT_DIR, 'ack.expected.md')
 const MODE = webSnapshotMode()

+ 1 - 1
apps/web/tests/github-ready-review.e2e.ts

@@ -22,7 +22,7 @@ import { saveFailureShot } from './support.ts'
 
 const MODE = webSnapshotMode()
 const OVERLAY = fileURLToPath(new URL('../../../examples/web-github-review/cordis.yml', import.meta.url))
-const EXPECTED = fileURLToPath(new URL('./snapshots/github-ready-review/conversation.expected.md', import.meta.url))
+const EXPECTED = fileURLToPath(new URL('./goldens/github-ready-review/conversation.expected.md', import.meta.url))
 const PROVIDER = 'github-webhook-review-test'
 const MODEL = 'reply'
 const SECRET = 'github-webhook-review-secret'

+ 1 - 1
apps/web/tests/goal-bar.e2e.ts

@@ -14,7 +14,7 @@ import {
 } from './scaffold.ts'
 import { newEnglishPage, saveFailureShot } from './support.ts'
 
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/goal-bar', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('./goldens/goal-bar', import.meta.url))
 const ACTIVE_EXPECTED = join(SNAPSHOT_DIR, 'active.expected.md')
 const OVERLAY = fileURLToPath(new URL('./goal-bar.overlay.yml', import.meta.url))
 const MODE = webSnapshotMode()

+ 2 - 2
apps/web/tests/goal-command-presentation.e2e.ts

@@ -14,9 +14,9 @@ import {
 } from './scaffold.ts'
 import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts'
 
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/goal-command-presentation', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('./goldens/goal-command-presentation', import.meta.url))
 const UI_EXPECTED = fileURLToPath(new URL(
-  './snapshots/goal-command-presentation/ui.expected.md', import.meta.url,
+  './goldens/goal-command-presentation/ui.expected.md', import.meta.url,
 ))
 const MODE = webSnapshotMode()
 

+ 1 - 1
apps/web/tests/goal-multi-turn-actions.e2e.ts

@@ -16,7 +16,7 @@ import {
 } from './scaffold.ts'
 import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts'
 
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/goal-multi-turn-actions', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/goal-multi-turn-actions', import.meta.url))
 const FIXTURE = join(SNAPSHOT_DIR, 'session.jsonl')
 const OVERRIDE = join(SNAPSHOT_DIR, 'replay.override.json')
 const UI_EXPECTED = join(SNAPSHOT_DIR, 'ui.expected.md')

+ 0 - 0
apps/web/tests/snapshots/access-confirmation/ui.expected.md → apps/web/tests/goldens/access-confirmation/ui.expected.md


+ 0 - 0
apps/web/tests/snapshots/agent-preset-authoring/copy-dialog.expected.md → apps/web/tests/goldens/agent-preset-authoring/copy-dialog.expected.md


+ 0 - 0
apps/web/tests/snapshots/agent-preset-authoring/created.expected.md → apps/web/tests/goldens/agent-preset-authoring/created.expected.md


+ 0 - 0
apps/web/tests/snapshots/agent-preset-authoring/damaged.expected.md → apps/web/tests/goldens/agent-preset-authoring/damaged.expected.md


+ 0 - 0
apps/web/tests/snapshots/agent-preset-authoring/section.expected.md → apps/web/tests/goldens/agent-preset-authoring/section.expected.md


+ 0 - 0
apps/web/tests/snapshots/agent-preset-selection/header.expected.md → apps/web/tests/goldens/agent-preset-selection/header.expected.md


+ 0 - 0
apps/web/tests/snapshots/agent-preset-selection/hero.expected.md → apps/web/tests/goldens/agent-preset-selection/hero.expected.md


+ 0 - 0
apps/web/tests/snapshots/agent-preset-selection/menu.expected.md → apps/web/tests/goldens/agent-preset-selection/menu.expected.md


+ 0 - 0
apps/web/tests/snapshots/cold-blank-session/sidebar.expected.md → apps/web/tests/goldens/cold-blank-session/sidebar.expected.md


+ 0 - 0
apps/web/tests/snapshots/composer-draft-scroll/geometry.expected.md → apps/web/tests/goldens/composer-draft-scroll/geometry.expected.md


+ 0 - 0
apps/web/tests/snapshots/composer-tab-geometry/geometry.expected.md → apps/web/tests/goldens/composer-tab-geometry/geometry.expected.md


+ 0 - 0
apps/web/tests/snapshots/conversation-column-overflow/geometry.expected.md → apps/web/tests/goldens/conversation-column-overflow/geometry.expected.md


+ 0 - 0
apps/web/tests/snapshots/declared-reasoning/ui.expected.md → apps/web/tests/goldens/declared-reasoning/ui.expected.md


+ 0 - 0
apps/web/tests/snapshots/github-ready-review/conversation.expected.md → apps/web/tests/goldens/github-ready-review/conversation.expected.md


+ 0 - 0
apps/web/tests/snapshots/goal-bar/active.expected.md → apps/web/tests/goldens/goal-bar/active.expected.md


+ 0 - 0
apps/web/tests/snapshots/goal-command-presentation/ui.expected.md → apps/web/tests/goldens/goal-command-presentation/ui.expected.md


+ 0 - 0
apps/web/tests/snapshots/home-path-tilde/workspace-hover.expected.txt → apps/web/tests/goldens/home-path-tilde/workspace-hover.expected.txt


+ 0 - 0
apps/web/tests/snapshots/markdown-cjk-strong/ui.expected.md → apps/web/tests/goldens/markdown-cjk-strong/ui.expected.md


+ 0 - 0
apps/web/tests/snapshots/markdown-images/ui.expected.md → apps/web/tests/goldens/markdown-images/ui.expected.md


+ 0 - 0
apps/web/tests/snapshots/markdown-inline-code-links/ui.expected.md → apps/web/tests/goldens/markdown-inline-code-links/ui.expected.md


+ 0 - 0
apps/web/tests/snapshots/markdown-wide-table/geometry.expected.md → apps/web/tests/goldens/markdown-wide-table/geometry.expected.md


+ 0 - 0
apps/web/tests/snapshots/math-rendering/ui.expected.md → apps/web/tests/goldens/math-rendering/ui.expected.md


+ 0 - 0
apps/web/tests/snapshots/max-tokens-notice/history-turn.expected.txt → apps/web/tests/goldens/max-tokens-notice/history-turn.expected.txt


+ 0 - 0
apps/web/tests/snapshots/models-settings/configured.expected.md → apps/web/tests/goldens/models-settings/configured.expected.md


+ 0 - 0
apps/web/tests/snapshots/models-settings/declared-edit.expected.md → apps/web/tests/goldens/models-settings/declared-edit.expected.md


+ 0 - 0
apps/web/tests/snapshots/models-settings/declared.expected.md → apps/web/tests/goldens/models-settings/declared.expected.md


+ 0 - 0
apps/web/tests/snapshots/models-settings/delete.expected.md → apps/web/tests/goldens/models-settings/delete.expected.md


+ 0 - 0
apps/web/tests/snapshots/models-settings/empty.expected.md → apps/web/tests/goldens/models-settings/empty.expected.md


+ 0 - 0
apps/web/tests/snapshots/models-settings/model-picker.expected.md → apps/web/tests/goldens/models-settings/model-picker.expected.md


+ 0 - 0
apps/web/tests/snapshots/models-settings/native-delete.expected.md → apps/web/tests/goldens/models-settings/native-delete.expected.md


+ 0 - 0
apps/web/tests/snapshots/onboarding-deepseek-config/missing.expected.md → apps/web/tests/goldens/onboarding-deepseek-config/missing.expected.md


+ 0 - 0
apps/web/tests/snapshots/onboarding-deepseek-config/models.expected.md → apps/web/tests/goldens/onboarding-deepseek-config/models.expected.md


+ 0 - 0
apps/web/tests/snapshots/onboarding-deepseek-config/welcome.expected.md → apps/web/tests/goldens/onboarding-deepseek-config/welcome.expected.md


+ 0 - 0
apps/web/tests/snapshots/onboarding-usable-provider/dismissed.expected.md → apps/web/tests/goldens/onboarding-usable-provider/dismissed.expected.md


+ 0 - 0
apps/web/tests/snapshots/plugin-config/section.expected.md → apps/web/tests/goldens/plugin-config/section.expected.md


+ 0 - 0
apps/web/tests/snapshots/reference-composer/caret-edits.expected.md → apps/web/tests/goldens/reference-composer/caret-edits.expected.md


+ 0 - 0
apps/web/tests/snapshots/reference-composer/menu.expected.md → apps/web/tests/goldens/reference-composer/menu.expected.md


+ 0 - 0
apps/web/tests/snapshots/reference-composer/order.expected.md → apps/web/tests/goldens/reference-composer/order.expected.md


+ 0 - 0
apps/web/tests/snapshots/schedule-after/at-conversation.expected.md → apps/web/tests/goldens/schedule-after/at-conversation.expected.md


+ 0 - 0
apps/web/tests/snapshots/schedule-after/conversation.expected.md → apps/web/tests/goldens/schedule-after/conversation.expected.md


+ 0 - 0
apps/web/tests/snapshots/schedule-after/every-conversation.expected.md → apps/web/tests/goldens/schedule-after/every-conversation.expected.md


+ 0 - 0
apps/web/tests/snapshots/search-card/grep-card.expected.txt → apps/web/tests/goldens/search-card/grep-card.expected.txt


+ 0 - 0
apps/web/tests/snapshots/settings-chrome/dialog-en.expected.md → apps/web/tests/goldens/settings-chrome/dialog-en.expected.md


+ 0 - 0
apps/web/tests/snapshots/settings-chrome/dialog.expected.md → apps/web/tests/goldens/settings-chrome/dialog.expected.md


+ 0 - 0
apps/web/tests/snapshots/settings-chrome/plugins.expected.md → apps/web/tests/goldens/settings-chrome/plugins.expected.md


+ 0 - 0
apps/web/tests/snapshots/sidebar-subagent-activity/owner-running.expected.md → apps/web/tests/goldens/sidebar-subagent-activity/owner-running.expected.md


+ 0 - 0
apps/web/tests/snapshots/skill-invocation-policy/menu.expected.md → apps/web/tests/goldens/skill-invocation-policy/menu.expected.md


+ 0 - 0
apps/web/tests/snapshots/skill-user-invoke/ui.expected.md → apps/web/tests/goldens/skill-user-invoke/ui.expected.md


+ 0 - 0
apps/web/tests/snapshots/stats-paged-history/ui.expected.md → apps/web/tests/goldens/stats-paged-history/ui.expected.md


+ 0 - 0
apps/web/tests/snapshots/steer-all/mid-steer.expected.md → apps/web/tests/goldens/steer-all/mid-steer.expected.md


+ 0 - 0
apps/web/tests/snapshots/steer-all/replay.override.json → apps/web/tests/goldens/steer-all/replay.override.json


+ 0 - 0
apps/web/tests/snapshots/steer-all/settled.expected.md → apps/web/tests/goldens/steer-all/settled.expected.md


+ 0 - 0
apps/web/tests/snapshots/todo-row/parallel-plan.expected.txt → apps/web/tests/goldens/todo-row/parallel-plan.expected.txt


+ 0 - 0
apps/web/tests/snapshots/trajectory-virtualization/load-more.expected.md → apps/web/tests/goldens/trajectory-virtualization/load-more.expected.md


+ 0 - 0
apps/web/tests/snapshots/web-runtime-context/file-reference-prompt.expected.md → apps/web/tests/goldens/web-runtime-context/file-reference-prompt.expected.md


+ 0 - 0
apps/web/tests/snapshots/web-runtime-context/web-surface-prompt.expected.md → apps/web/tests/goldens/web-runtime-context/web-surface-prompt.expected.md


+ 1 - 1
apps/web/tests/home-path-tilde.snapshot.ts

@@ -8,7 +8,7 @@ import { act, fireEvent, screen, waitFor, within } from '@testing-library/react'
 import { describe, expect, it } from 'vitest'
 import { installAssembledBootEnv, mountAssembledApp, REFRESHING_GOLDEN } from './assembled-boot.ts'
 
-const EXPECTED = join(process.cwd(), 'apps/web/tests/snapshots/home-path-tilde/workspace-hover.expected.txt')
+const EXPECTED = join(process.cwd(), 'apps/web/tests/goldens/home-path-tilde/workspace-hover.expected.txt')
 
 installAssembledBootEnv()
 

+ 1 - 1
apps/web/tests/lifecycle-chrome.e2e.ts

@@ -23,7 +23,7 @@ import {
 } from './scaffold.ts'
 import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts'
 
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/lifecycle-chrome', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/lifecycle-chrome', import.meta.url))
 const FIXTURE = join(SNAPSHOT_DIR, 'session.jsonl')
 const REPLAY_OVERRIDE = join(SNAPSHOT_DIR, 'replay.override.json')
 const HERO_EXPECTED = join(SNAPSHOT_DIR, 'hero.expected.md')

+ 1 - 1
apps/web/tests/live-interactions.e2e.ts

@@ -28,7 +28,7 @@ import {
 } from './scaffold.ts'
 import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts'
 
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/live-interactions', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/live-interactions', import.meta.url))
 const FIXTURE = join(SNAPSHOT_DIR, 'session.jsonl')
 // One golden pins the empty mid-turn loading state, one pins the sendable draft
 // state, and the other four capture what remains after cancel, after a

+ 2 - 2
apps/web/tests/markdown-cjk-strong.e2e.ts

@@ -17,8 +17,8 @@ import {
 } from './scaffold.ts'
 import { newEnglishPage, saveFailureShot } from './support.ts'
 
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/markdown-cjk-strong', import.meta.url))
-const UI_EXPECTED = fileURLToPath(new URL('./snapshots/markdown-cjk-strong/ui.expected.md', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('./goldens/markdown-cjk-strong', import.meta.url))
+const UI_EXPECTED = fileURLToPath(new URL('./goldens/markdown-cjk-strong/ui.expected.md', import.meta.url))
 const MODE = webSnapshotMode()
 const SEED_ID = 'markdown-cjk-strong-web-e2e'
 const DONE = 'CJK_STRONG_DONE'

+ 2 - 2
apps/web/tests/markdown-images.e2e.ts

@@ -26,8 +26,8 @@ import {
 } from './scaffold.ts'
 import { newEnglishPage, saveFailureShot } from './support.ts'
 
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/markdown-images', import.meta.url))
-const UI_EXPECTED = fileURLToPath(new URL('./snapshots/markdown-images/ui.expected.md', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('./goldens/markdown-images', import.meta.url))
+const UI_EXPECTED = fileURLToPath(new URL('./goldens/markdown-images/ui.expected.md', import.meta.url))
 const MODE = webSnapshotMode()
 const SEED_ID = 'markdown-images-web-e2e'
 const REMOTE_ALT = 'Remote test image'

+ 2 - 2
apps/web/tests/markdown-inline-code-links.e2e.ts

@@ -17,8 +17,8 @@ import {
 } from './scaffold.ts'
 import { newEnglishPage, saveFailureShot } from './support.ts'
 
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/markdown-inline-code-links', import.meta.url))
-const UI_EXPECTED = fileURLToPath(new URL('./snapshots/markdown-inline-code-links/ui.expected.md', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('./goldens/markdown-inline-code-links', import.meta.url))
+const UI_EXPECTED = fileURLToPath(new URL('./goldens/markdown-inline-code-links/ui.expected.md', import.meta.url))
 const MODE = webSnapshotMode()
 const SEED_ID = 'markdown-inline-code-links-web-e2e'
 const DONE = 'INLINE_CODE_LINK_DONE'

+ 2 - 2
apps/web/tests/markdown-wide-table.e2e.ts

@@ -40,9 +40,9 @@ import {
 } from './scaffold.ts'
 import { newEnglishPage, saveFailureShot } from './support.ts'
 
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/markdown-wide-table', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('./goldens/markdown-wide-table', import.meta.url))
 const GEOMETRY_EXPECTED = fileURLToPath(
-  new URL('./snapshots/markdown-wide-table/geometry.expected.md', import.meta.url),
+  new URL('./goldens/markdown-wide-table/geometry.expected.md', import.meta.url),
 )
 const MODE = webSnapshotMode()
 const SEED_ID = 'markdown-wide-table-web-e2e'

+ 2 - 2
apps/web/tests/math-rendering.e2e.ts

@@ -17,8 +17,8 @@ import {
 } from './scaffold.ts'
 import { newEnglishPage, saveFailureShot } from './support.ts'
 
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/math-rendering', import.meta.url))
-const UI_EXPECTED = fileURLToPath(new URL('./snapshots/math-rendering/ui.expected.md', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('./goldens/math-rendering', import.meta.url))
+const UI_EXPECTED = fileURLToPath(new URL('./goldens/math-rendering/ui.expected.md', import.meta.url))
 const MODE = webSnapshotMode()
 const SEED_ID = 'math-rendering-web-e2e'
 const DONE = 'MATH_RENDERING_DONE'

+ 1 - 1
apps/web/tests/max-tokens-notice.snapshot.ts

@@ -15,7 +15,7 @@ import { fireEvent, screen, waitFor, within } from '@testing-library/react'
 import { describe, expect, it } from 'vitest'
 import { hasClass, installAssembledBootEnv, mountAssembledApp, REFRESHING_GOLDEN } from './assembled-boot.ts'
 
-const EXPECTED = join(process.cwd(), 'apps/web/tests/snapshots/max-tokens-notice/history-turn.expected.txt')
+const EXPECTED = join(process.cwd(), 'apps/web/tests/goldens/max-tokens-notice/history-turn.expected.txt')
 
 installAssembledBootEnv()
 

+ 2 - 2
apps/web/tests/message-actions.e2e.ts

@@ -15,10 +15,10 @@ import {
 } from './scaffold.ts'
 import { newEnglishPage, saveFailureShot } from './support.ts'
 
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/message-actions', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/message-actions', import.meta.url))
 // Borrowed read-only: this scenario needs any settled user+assistant pair, not
 // a new recording (workspace-management / sidebar-scrollbar pattern).
-const SEED = fileURLToPath(new URL('./snapshots/seeded-history/seed.jsonl', import.meta.url))
+const SEED = fileURLToPath(new URL('../../../snapshots/web/seeded-history/seed.jsonl', import.meta.url))
 const UI_EXPECTED = join(SNAPSHOT_DIR, 'ui.expected.md')
 const FORK_EXPECTED = join(SNAPSHOT_DIR, 'fork.expected.md')
 const MODE = webSnapshotMode()

+ 2 - 2
apps/web/tests/message-feedback-layout.e2e.ts

@@ -33,7 +33,7 @@ import {
 } from './scaffold.ts'
 import { newEnglishPage, saveFailureShot } from './support.ts'
 
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/message-feedback-layout', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/message-feedback-layout', import.meta.url))
 /**
  * Committed golden of the popover relations at every stop. Booleans and counts
  * only, never absolute coordinates.
@@ -41,7 +41,7 @@ const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/message-feedback-layout'
 const GEOMETRY_EXPECTED = join(SNAPSHOT_DIR, 'geometry.expected.md')
 const MODE = webSnapshotMode()
 /** Borrowed read-only: this scenario needs any settled assistant message to rate. */
-const SEED = fileURLToPath(new URL('./snapshots/seeded-history/seed.jsonl', import.meta.url))
+const SEED = fileURLToPath(new URL('../../../snapshots/web/seeded-history/seed.jsonl', import.meta.url))
 const SEED_ID = 'message-feedback-layout-e2e'
 /** Viewport widths from full-screen desktop down to a narrow window. */
 const WIDTHS = [1680, 1280, 1024, 900, 700, 600]

+ 1 - 1
apps/web/tests/message-feedback-protocol.snapshot.ts

@@ -10,7 +10,7 @@ import {
   type WebScaffold,
 } from './scaffold.ts'
 
-const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/message-feedback-protocol', import.meta.url))
+const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/message-feedback-protocol', import.meta.url))
 const SESSION_FIXTURE = join(SNAPSHOT_DIR, 'session.jsonl')
 const PROTOCOL_EXPECTED = join(SNAPSHOT_DIR, 'protocol.expected.json')
 const SESSION_ID = 'message-feedback-protocol'

+ 1 - 1
apps/web/tests/message-feedback.e2e.ts

@@ -15,7 +15,7 @@ import { newEnglishPage, saveFailureShot } from './support.ts'
 
 // Borrowed read-only: this scenario needs any settled assistant message to
 // address, not a new recording (message-actions / sidebar-scrollbar pattern).
-const SEED = fileURLToPath(new URL('./snapshots/seeded-history/seed.jsonl', import.meta.url))
+const SEED = fileURLToPath(new URL('../../../snapshots/web/seeded-history/seed.jsonl', import.meta.url))
 const MODE = webSnapshotMode()
 const SEED_ID = 'message-feedback-web-e2e'
 const NOTE = 'Read both files before answering.'

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels