-
Notifications
You must be signed in to change notification settings - Fork 3k
feat(transcript): add cross-host contract prevalidation #9355
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
777 changes: 777 additions & 0 deletions
777
docs/design/web-shell/chat-transcript-contract-prevalidation.md
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
27 changes: 27 additions & 0 deletions
27
integration-tests/fixtures/chat-transcript-contract/v1/capability-matrix.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| # Chat transcript contract capability matrix | ||
|
|
||
| | Capability | Native source | Contract mapping | Render/action mapping | Consumers | Fixture/evidence | Owner | Gate | | ||
| | ---------------------- | ------------------------------------------ | ---------------------------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------- | ----------------------------------------------------- | ------------------ | -------------------------------------------- | | ||
| | user/assistant/thought | prompt-scoped live or persisted segment ID | runtime ordinal ID unchanged; candidate probe projects source-keyed IDs | stable source IDs and semantic-copy hash | Web, Tauri, VS Code, HTML | `representative`, SDK append/prepend matrix | CLI + SDK UI | pass; stable under append/prepend/replay | | ||
| | tools and grouping | tool call ID | runtime keeps raw fields; document/export uses typed input and result preview only | group keeps every block/tool call ID; file target is semantic | Web runtime + document | raw compatibility and raw-free document tests | SDK UI + Web Shell | pass | | ||
| | plan/todo | plan tool call ID and plan ID | runtime keeps raw fields; document/export uses typed `todo_list` preview/result | standalone deterministic tool item | Web runtime + document | raw compatibility and raw-free todo document tests | SDK UI + Web Shell | pass | | ||
| | permission history | request ID and safe tool identity | runtime keeps raw tool call; document/export uses safe identity only | resolved history maps to stable tool target | Web runtime + document | raw compatibility and raw-free permission tests | SDK UI + Web Shell | pass | | ||
| | replay/prepend | prompt-scoped live or persisted segment ID | source metadata and persisted record boundaries are preserved | probe compares stable block/item IDs and semantic hash | all | SDK, direct-daemon and ACP append/prepend tests | CLI + SDK UI | pass | | ||
| | scope isolation | host scope key + generation | stale input is rejected before reduction | stale action evidence is absent | VS Code | VS Code contract probe | VS Code | pass | | ||
| | VS Code direct daemon | daemon event plus source segment ID | unchanged SDK reducer plus read-only stable-ID projection | shared Web Shell render/action probe | VS Code | direct-daemon SDK contract probe | VS Code | pass; retained as a validated alternative | | ||
| | VS Code ACP | Qwen ACP live/history segment metadata | thin SDK normalizer/reducer plus read-only stable-ID projection | shared Web Shell render/action probe | VS Code | ACP source + contract probes | VS Code | pass; selected for the later migration phase | | ||
| | Tauri distribution | packaged qwen runtime | same daemon blocks and Web Shell build | same renderer artifact | Tauri | desktop packaging-wiring source assertion | Desktop | pass | | ||
| | export record policy | ChatRecord type/subtype and parent chain | known visible records only | canonical projector output | HTML | export policy unit test | CLI | pass | | ||
| | export block safety | projected block | per-kind allowlist, opaque IDs, zeroed timestamps | direct raw-free renderer input | HTML | export schema/canary tests | CLI | pass | | ||
| | Markdown/resources | Markdown image URL and structured raster | approved data raster only in document mode | no automatic remote image source | HTML | Markdown document test + browser request interception | Web Shell | pass | | ||
| | document budgets | shared V1 constant | block/text/image/envelope/depth/array/object-property/rich-task caps | non-virtualized document probe | HTML | maximum document browser probe | CLI + Web Shell | pass | | ||
|
|
||
| Both VS Code candidates pass the stable identity matrix without changing the | ||
| default reducer's ordinal runtime IDs. Qwen ACP prompt-bound live text is | ||
| stamped at source with a prompt-scoped deterministic segment ID; persisted | ||
| replay keeps its record-derived segment ID. The candidate probes project those native identities | ||
| to scope-keyed block IDs, while existing persisted record boundaries prevent | ||
| unrelated history segments from merging. ACP text that has neither a stable | ||
| prompt source nor segment identity still fails closed. ACP remains the selected path for the later migration phase | ||
| because it is the current production transport; direct-daemon stays a validated | ||
| alternative, not a production migration in Step 0. |
4 changes: 4 additions & 0 deletions
4
...tests/fixtures/chat-transcript-contract/v1/cases/representative/acp-session-updates.jsonl
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| {"sessionUpdate":"user_message_chunk","content":{"type":"text","text":"Inspect the contract"},"_meta":{"qwenTranscript":{"segmentId":"user-1:0"}}} | ||
| {"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"Checking identity"},"_meta":{"qwenTranscript":{"segmentId":"assistant-1:0"}}} | ||
| {"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"The contract is stable."},"_meta":{"qwenTranscript":{"segmentId":"assistant-1:1"}}} | ||
| {"sessionUpdate":"tool_call","toolCallId":"read-1","title":"Read file","status":"completed","rawInput":{"path":"src/index.ts"},"_meta":{"toolName":"read"}} |
4 changes: 4 additions & 0 deletions
4
...ration-tests/fixtures/chat-transcript-contract/v1/cases/representative/chat-records.jsonl
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| {"uuid":"user-1","parentUuid":null,"sessionId":"session-test","timestamp":"2026-08-16T00:00:00.000Z","type":"user","cwd":"/workspace/project","version":"test","message":{"role":"user","parts":[{"text":"Inspect the contract"}]}} | ||
| {"uuid":"assistant-1","parentUuid":"user-1","sessionId":"session-test","timestamp":"2026-08-16T00:00:01.000Z","type":"assistant","cwd":"/workspace/project","version":"test","message":{"role":"model","parts":[{"text":"Checking identity","thought":true},{"text":"The contract is stable."}]}} | ||
| {"uuid":"tool-start","parentUuid":"assistant-1","sessionId":"session-test","timestamp":"2026-08-16T00:00:02.000Z","type":"assistant","cwd":"/workspace/project","version":"test","message":{"role":"model","parts":[{"functionCall":{"id":"read-1","name":"read_file","args":{"path":"/workspace/project/src/index.ts"}}}]}} | ||
| {"uuid":"tool-result","parentUuid":"tool-start","sessionId":"session-test","timestamp":"2026-08-16T00:00:03.000Z","type":"tool_result","cwd":"/workspace/project","version":"test","message":{"role":"user","parts":[{"functionResponse":{"id":"read-1","name":"read_file","response":{"output":"hidden raw output"}}}]},"toolCallResult":{"callId":"read-1","status":"success","resultDisplay":{"type":"vision_bridge_notice","summary":"Visible summary","notice":"Visible notice"}}} |
5 changes: 5 additions & 0 deletions
5
...ation-tests/fixtures/chat-transcript-contract/v1/cases/representative/daemon-events.jsonl
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| {"id":10,"v":1,"type":"session_update","data":{"update":{"sessionUpdate":"user_message_chunk","content":{"type":"text","text":"Inspect the contract"},"_meta":{"qwenTranscript":{"segmentId":"prompt-1:user:0"}}}}} | ||
| {"id":20,"v":1,"type":"session_update","data":{"update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"Checking identity"},"_meta":{"qwenTranscript":{"segmentId":"prompt-1:thought:0"}}}}} | ||
| {"id":30,"v":1,"type":"session_update","data":{"update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"The contract is stable."},"_meta":{"qwenTranscript":{"segmentId":"prompt-1:assistant:0"}}}}} | ||
| {"id":40,"v":1,"type":"session_update","data":{"update":{"sessionUpdate":"tool_call","toolCallId":"read-1","title":"Read file","status":"completed","rawInput":{"path":"src/index.ts"},"_meta":{"toolName":"read"}}}} | ||
| {"id":50,"v":1,"type":"permission_request","data":{"requestId":"permission-1","sessionId":"session-test","title":"Allow read?","options":[{"optionId":"allow","name":"Allow","kind":"allow_once"}],"toolCall":{"toolCallId":"read-2","name":"read","kind":"read","rawInput":{"path":"src/other.ts"}}}} |
15 changes: 15 additions & 0 deletions
15
...tion-tests/fixtures/chat-transcript-contract/v1/cases/representative/expected-export.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| { | ||
| "schemaVersion": 1, | ||
| "forbiddenFields": [ | ||
| "rawInput", | ||
| "rawOutput", | ||
| "toolCall", | ||
| "details", | ||
| "locations", | ||
| "sourceRecordIds", | ||
| "sessionId", | ||
| "sessionLabel" | ||
| ], | ||
| "expectedToolResult": "Visible summary\nVisible notice", | ||
| "timestamps": 0 | ||
| } |
10 changes: 10 additions & 0 deletions
10
...ation-tests/fixtures/chat-transcript-contract/v1/cases/representative/expected-model.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| { | ||
| "kinds": ["user", "thought", "assistant", "tool"], | ||
| "sourceRecordIds": [ | ||
| ["user-1"], | ||
| ["assistant-1"], | ||
| ["assistant-1"], | ||
| ["tool-start", "tool-result"] | ||
| ], | ||
| "rawFreeToolResult": "Visible summary\nVisible notice" | ||
| } |
10 changes: 10 additions & 0 deletions
10
...ion-tests/fixtures/chat-transcript-contract/v1/cases/representative/expected-network.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| { | ||
| "unexpectedRequests": 0, | ||
| "cspViolations": 0, | ||
| "allowedImageSources": [ | ||
| "data:image/png", | ||
| "data:image/jpeg", | ||
| "data:image/gif", | ||
| "data:image/webp" | ||
| ] | ||
| } |
5 changes: 5 additions & 0 deletions
5
...ests/fixtures/chat-transcript-contract/v1/cases/representative/expected-render-items.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "roles": ["user", "thinking", "assistant", "tool_group"], | ||
| "requiredCapabilities": ["copy", "edit-user-message", "open-file"], | ||
| "identityFields": ["renderedItemId", "sourceBlockIds", "sourceToolCallIds"] | ||
| } |
27 changes: 27 additions & 0 deletions
27
integration-tests/fixtures/chat-transcript-contract/v1/cases/representative/manifest.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| { | ||
| "fixtureVersion": 1, | ||
| "name": "representative", | ||
| "generatorVersion": "chat-transcript-prevalidation-v1", | ||
| "sources": ["daemon", "acp", "chat-records"], | ||
| "consumers": ["web", "tauri", "vscode", "html"], | ||
| "capabilities": [ | ||
| "text-thinking-usage-images", | ||
| "streaming-replay-prepend", | ||
| "tools-plan-permission", | ||
| "render-action-identity", | ||
| "scope-generation", | ||
| "export-security-network-budgets" | ||
| ], | ||
| "complete": true, | ||
| "expectedDiagnostics": [], | ||
| "normalizedFields": ["clientReceivedAt", "createdAt", "updatedAt"], | ||
| "hashes": { | ||
| "daemon-events.jsonl": "ea25e535847aea996ee3062b7497272540520a9a213de7223ac705780ccb7ac7", | ||
| "acp-session-updates.jsonl": "2f79f50505bd17de22979d47a63e52183cde541d416e4c44eb58cd5351e6a13a", | ||
| "chat-records.jsonl": "b66abea928c3c65cdedc4ca1c455d86b1b0a46b90e2a6a186afefaa89e87db0e", | ||
| "expected-model.json": "4bbf808871c62219910127b50b3290a5c74b64b4464ec57bf25353faad79a5e5", | ||
| "expected-render-items.json": "3ef27f09cc9b8cd2350fff269262dc0a653e0dba8435cce846a07bee8d0fbc73", | ||
| "expected-export.json": "160f87a0f2c069a8378b27ba3f51e2209d31359e1f86fa21c0786cf9fb02c32b", | ||
| "expected-network.json": "ee14c9469e2f80f1262a23ca8452bc41abb9ce43a625176c1b428608c503ad11" | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Suggestion] Pattern — the contract's machine-readable locks are advisory, not enforced: (1) manifest.json:15 —
expectedDiagnostics/normalizedFields/complete/capabilities/sources/name/generatorVersionhave zero read sites repo-wide, and the hash check iterates only the entriesmanifest.hasheslists without asserting coverage of the case directory; (2) export-transcript-document-v1.schema.json — the published cross-host schema is never used to validate any document instance (no Ajv compile references it) and both under- and over-specifies the runtime gate (base64 length%4,home_path_forbiddenare unexpressible); (3) manifest.schema.json:45 — never compiled or validated againstmanifest.jsonanywhere, though the design doc (line 528) states CI validates schema/hash first; (4) chat-transcript-contract.test.ts:137 — the toolPreview closed-schema assertion filters out$refvariants, sotodoPreviewescapes theadditionalProperties: falselock (the block loop below resolves$refs; the schema files are not hash-locked, so nothing compensates). — Failure scenario: executed: delete one entry frommanifest.hashesand mutate that fixture → contract test 4/4 green (the fixture mutates out from under the gate); set$defs/todoPreview.additionalProperties = true→ 4/4 green; a schema-conformant document with base64 length %4 ≠ 0 is rejected by the runtime gate. The "machine-generated gates" the doc claims do not exist for these faces.Witness (executed):
Suggested fix: In the contract test: assert
hashescovers every file in the case directory; compile both schemas with Ajv (draft 2020-12, mirroring packages/core/src/utils/schemaValidator.ts) and validatemanifest.jsonplus exported document instances; resolve$refs in the toolPreview closed assertion like the block loop does.中文说明
契约的机器可读锁是建议性的:manifest 字段零读取点且 hash 检查不断言目录覆盖;v1 文档 schema 与 manifest.schema.json 从未被实例/编译校验;toolPreview 闭合断言过滤 $ref 变体——实测多处变异仍全绿。
失败场景: 实测:删掉 manifest.hashes 中一个条目并篡改对应夹具 -> 契约测试 4/4 全绿(夹具在门禁下静默漂移);把 $defs/todoPreview.additionalProperties 改为 true -> 4/4 全绿;schema 合规但 base64 长度非 4 倍数的文档被运行时门禁拒绝。设计文档宣称的"机器生成门禁"在这些面上并不存在。
建议修复: 在契约测试中:断言 hashes 覆盖目录全部文件;用 Ajv(draft 2020-12,参照 packages/core/src/utils/schemaValidator.ts)实例校验 manifest.json 与导出的文档实例;toolPreview 闭合断言像 block 循环一样解析 $ref。
— qwen3.8-max via Qwen Code /review (v0.21.13)