Repository navigation
fix(managed-agent): Settle task event and cancel semantics - #12998
Conversation
|
Contract verification report for d6e6448 (macOS arm64).
Final OpenAPI SHA256: 中文说明提交 d6e6448 的契约验证报告,平台为 macOS arm64。
最终 OpenAPI SHA256 为 |
|
@qwen-code /takeover |
|
🤝 Takeover engaged: the autofix loop now manages this PR — it will address new review feedback and resolve base conflicts until the label is removed or the round cap is reached. Remove the 中文说明🤝 已接管:autofix 循环现在管理此 PR —— 将持续处理新的评审反馈与 base 冲突,直到移除标签或达到轮次上限。移除 |
|
🤖 Addressed the latest review feedback (round 1/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/100 轮)。改动内容与我反驳保留之处如下: Round summaryBoth inline suggestions from the automated reviewer were reproduced as real [rc:4134535131] R1-1 —
|
|
@qwen-code /takeover from 3 |
|
🤝 Takeover engaged: the autofix loop now manages this PR — it will address new review feedback and resolve base conflicts until the label is removed or the round cap is reached. This window's round counter starts at 3 (the rounds this PR spent in review before takeover), so the Critical-only brake engages after 2 more change-producing round(s) instead of a full fresh 5. Remove the 中文说明🤝 已接管:autofix 循环现在管理此 PR —— 将持续处理新的评审反馈与 base 冲突,直到移除标签或达到轮次上限。本窗口轮次计数从 3 起算(即本 PR 托管前已进行的评审轮数),因此再经过 2 个产生改动的轮次即进入 Critical-only,而非重新计满 5 轮。移除 |
|
@qwen-code /resolve |
main already published contract v1.22 for the G0 public Workspace file turns, so the settled task event and cancel semantics move to v1.23 in info.version and in the description history. Both sides' description edits are kept; the rest of the contract merged without conflict.
|
Qwen Code resolved the merge conflicts and pushed the branch update. Merge resolution: PR #12998 ← mainMerge commit Root causeA contract-version race. On main, #12955 (G0 public Workspace file turns) bumped Textual + one semantic decisionTextually adjacent, but "version": "1.23.0",
"description": "… 404. v1.22 adds deployment-opted-in creation with an initial Workspace file-tool Turn (G0) … v1.23 settles the planned task event and cancel semantics: … remain planned."Load-bearing
Not verified / follow-up
中文说明合并提交 根因:契约版本号撞车。main 的 #12955(G0 公共 Workspace 文件轮次)把 处理: 关键约束: 未验证 / 需后续:任务契约设计文档中英文版仍写 v1.22.0( |
…tract-semantics # Conflicts: # packages/sdk-java/managed-agent-server/src/main/resources/openapi/managed-agent-public-api.openapi.json
Local real-environment verification — PR #12998 @
|
| # | Check | Result |
|---|---|---|
| 1 | Head mvn clean verify checkstyle:check (managed-agent-server) |
196/196 tests, Checkstyle 0. Focused suites: PlannedTaskContractTest 7, ManagedAgentApiContractTest 5, ManagedSessionStoreContractFixtureTest 3 |
| 2 | web-shell managed-agent-api.test.ts |
2/2 |
| 3 | Zero runtime change: compare CRC-32 of every jar entry, base vs head | 406/407 identical, including all 52 nested jars. Only BOOT-INF/classes/openapi/…openapi.json differs. Nothing in src/main/java reads that resource |
| 4 | Real servers: base jar and head jar, same 19-request sequence (create, tasks, planned task events/cancel on both surfaces, close/replay/archive/delete, operation reads) | 19/19 identical status and normalized body. 76/76 Ajv checks of real bodies pass against both contracts, including real close/archive/delete operations under the PublicCommandOperation/WebShellCommandOperation that now carry the planned conditional. Planned routes return 404 on both jars |
| 5 | Independent schema differential (Ajv 2020-12, a different validator from the networknt one the PR tests use), exhaustive operation matrix across 4 schemas | 12,288 validations per contract. 132 verdict flips, all valid→invalid, all task_cancel (cancelled 88, failed without a code 44). 0 flips for any other type. Excluding descriptions and info, the only structural change in the whole contract is the two new planned allOf[4] conditionals |
| 6 | Generated client (PR generator + openapi-typescript) | Regenerating from the head spec gives a file byte-identical to the committed one. Base→head generated diff is 1 line (a description) |
| 7 | 15 single-rule contract mutants on the test classpath | 11 killed by the Java suites. C7 (planned marker removed) is caught by the web-shell sync test. 3 survive: exactly R2-1, R2-2 and R1-2 (fix-induced) |
| 8 | EN/ZH design docs | Same A1–A9 marker sequence, 16 identical headings, same code-token set and table rows |
Findings
F1 — stale version references (docs; small fix, recommended before merge). 05047ae6 renumbered the contract to 1.23.0 and added a v1.23 history sentence. Three places still say v1.22.0:
docs/design/2026-09-27-managed-agent-task-contract.md:52: "settles A1–A8 … in contract v1.22.0"docs/design/2026-09-27-managed-agent-task-contract.zh-CN.md:41: "在契约 v1.22.0 中确定"- the PR description: "Contract v1.22.0 …" and "The contract version is 1.22.0 on top of main's 1.21.0."
The /resolve result already listed the two doc lines as a follow-up; they are still unchanged at this head.
F2 — the three open Suggestions are real test-strength gaps (non-blocking). I confirmed each one independently with a mutant that the current suites let through:
- R2-1: lowering
artifact_refsmaxItemsfrom 100 to 50 survives, because no instance accepts a full list. - R2-2: making
state_changedforbid its companionruntime_statesurvives. - R1-2 (fix-induced): replacing
output's{required:[artifact_id]}with a conjunction{artifact_id, state}survives, becausepinEventFieldTotalitycounts each name of a conjunction as forbidden.
A +15/−6 candidate fixes all three:
- accept exactly 100 unique refs, and reject 101 from the same instance;
- accept
state_changedwithruntime_state: ready; - count only single-name
not.anyOfbranches as forbidding a field.
With it, all three mutants are killed by the intended assertions, the unmutated baseline stays 15/15, the other 12 mutants give the same results, and Checkstyle is 0. AutoFix round 4 is still running. If it lands a different fix, the linked harness re-checks it against the same mutants in a few minutes.
N1 — merge order with #12946 (both claim contract 1.23.0). #12946 (H1 Hosted MCP) also sets "version": "1.23.0" and appends its own v1.23 sentence; each PR merges cleanly with main on its own. After this PR lands, merging #12946 conflicts only on info.description. The "version": "1.23.0" line is identical on both sides, so git merges it silently. A resolver who just concatenates the two sentences leaves two v1.23 entries under one version. Whichever PR lands second must bump to 1.24.0 by hand, in the JSON and in any design-doc reference (see F1). #13037 (draft) still claims v1.22, which main's G0 already uses.
N2 — correction to triage Stage 2, note 3 (activation coupling). Un-planning only the new conditional does not produce a generated type that requires an undeclared field. openapi-typescript does not render if/then; the output just gains one more & unknown. The §4.1 rule (un-plan the conditional together with taskId/failureCode) is still right for contract consistency. Either way, the generated-sync test forces a regeneration (mutant C7), so the trap cannot pass CI silently.
N3 — live observation for the H3 gate. Real Sessions advertise capabilities.tasks: true, artifacts: false. That fits the new rule "any output-producing task requires capabilities.artifacts" only because no served task produces output yet: ManagedTaskService always returns empty artifact_refs. Consider adding "flip artifacts before the first output-producing task" to the §6.1 acceptance list. Optional.
Gaps disclosed by the bot reviews that this run covers
- Round 1 said it "did not run
PlannedTaskContractTestunder the real networknt validator". This run did, and also checked the same instances with Ajv (pre-release: fix ci #1, TypeError in Authentication Selection Interface #5). - Round 2 said "zero mutants probed" and that its Suggestions had "no independent verifier ruling". This run used 15 mutants, and each of the three Suggestions reproduces (API Key是要设成阿里云的API Key吗? #7, F2).
- Round 2 also said the consumers "were never systematically traced". The runtime never reads the spec.
artifact_refsis always empty inManagedTaskService. The generator output matches byte for byte (如何自定义密钥文件 .env可能与其他文件冲突 #3, OpenAI API Error: 401 Incorecct API Key provided #6).
CI and scope
At this head, 21 checks pass and only review-pr is pending. Hosted process fault gates / MySQL 8.4 was red on d6e6448 (a pre-existing main failure) and is green on 05047ae6.
Not covered:
- Runtime retention, publication, archival and cancellation guarantees. Those routes are unmapped; §6.1 correctly defers them to H3 and the cancel slice.
- Windows/Linux locally. The change is platform-independent contract data, and the CI Java legs on ubuntu, windows and macOS are green.
The harness is here: build, real-server A/B, Ajv differential, generator probe, mutants and figures.
中文说明
本地真实环境验证 — PR #12998 @ 05047ae6(macOS arm64)
结论:可以合并。 契约、schema 条件和生成客户端里都没有发现正确性缺陷。合并前建议先修 F1:/resolve 改号之后,设计文档两处和 PR 描述仍写着 v1.22.0。F2(三条未解决的 bot 建议)只涉及测试强度,候选补丁已实测。N1(版本号)需要与同样声明 1.23.0 的 #12946 协调合并顺序。
环境:JDK 21.0.12、Maven 3.9.16、Node 24.18.1、pnpm 11.24.0(frozen、离线)、Spring Boot 3.5.16 jar,独立的 MySQL 8.4.11 容器(JVM 与库均为 UTC)。base 臂用合并基 a1c90bd2。origin/main 现为 19684f37,只多一个无关提交,与它的试合并是干净的。
实测内容
| # | 检查 | 结果 |
|---|---|---|
| 1 | head 上 mvn clean verify checkstyle:check(managed-agent-server) |
196/196 测试通过,Checkstyle 0;定向套件 7 + 5 + 3 |
| 2 | web-shell managed-agent-api.test.ts |
2/2 |
| 3 | 零运行时变化:base/head jar 逐条目比对 CRC-32 | 407 个条目中 406 个相同(含全部 52 个内嵌 jar),只有 openapi JSON 不同;src/main/java 不读取该资源 |
| 4 | 真服务 A/B:base jar 与 head jar 跑同一组 19 个请求(建会话、任务、两侧 planned 事件/取消、close/重放/archive/delete、operation 读取) | 19/19 状态码与归一化响应体一致;真实响应体用 Ajv 分别对两份契约做 76/76 次校验全部合法,包括挂上新 planned 条件的 PublicCommandOperation/WebShellCommandOperation 上真实的 close/archive/delete operation;planned 路由在两个 jar 上都返回 404 |
| 5 | 独立 schema 差分(Ajv 2020-12,与 PR 测试所用的 networknt 是不同实现),operation 全组合 × 4 个 schema | 每份契约 12,288 次校验;132 个判定翻转,全部是 task_cancel 由合法变不合法(cancelled 88、failed 缺 failure code 44);其他类型 0 翻转;去掉描述与 info 后,整个契约的结构变化只有两处新增的 planned allOf[4] |
| 6 | 生成客户端(PR 自带生成器 + openapi-typescript) | 用 head spec 重生成,结果与已提交文件逐字节一致;base→head 生成差异 1 行(描述) |
| 7 | 15 个单规则契约变异体(替换测试 classpath 上的契约) | 11 个被 Java 套件杀死;C7(去掉 planned 标记)由 web-shell 同步测试兜住;存活 3 个,恰好是 R2-1、R2-2、R1-2(修复引入) |
| 8 | 中英文设计文档 | A1–A9 标记序列、16 个标题、代码 token 集合与表格行都一致 |
发现
F1 — 版本号引用过期(文档,改动很小,建议合并前修)。 05047ae6 把契约改号为 1.23.0 并追加了 v1.23 历史句,但以下三处仍写 v1.22.0:
docs/design/2026-09-27-managed-agent-task-contract.md:52docs/design/2026-09-27-managed-agent-task-contract.zh-CN.md:41- PR 描述中的 "Contract v1.22.0 …" 和 "1.22.0 on top of main's 1.21.0"
/resolve 的报告已把这两行文档列为后续事项,但在当前 head 上仍未修改。
F2 — 三条未解决的建议确实是测试强度缺口(非阻塞)。 我分别用变异体独立复现,现有测试都放过了它们:
- R2-1:把
artifact_refs的maxItems从 100 降到 50 仍然存活,因为没有任何实例接受满 100 条的列表。 - R2-2:让
state_changed禁止它的伴随字段runtime_state仍然存活。 - R1-2(修复引入):把
output的{required:[artifact_id]}换成合取{artifact_id, state}仍然存活,因为pinEventFieldTotality把合取里的每个名字都算作已禁止。
+15/−6 的候选补丁修复这三处:
- 接受恰好 100 个不重复引用,并从同一实例派生出 101 个的拒绝用例;
- 接受带
runtime_state: ready的state_changed; - 只有单名
not.anyOf分支才算禁止该字段。
应用后三个变异体都被预期断言杀死,未变异基线仍为 15/15,其余 12 个变异体结果不变,Checkstyle 为 0。AutoFix 第 4 轮仍在运行;如果它推送了不同的修法,用附带的装置几分钟即可对同一组变异体复验。
N1 — 与 #12946 的合并顺序(两者都声明契约 1.23.0)。 #12946(H1 Hosted MCP)同样设 "version": "1.23.0" 并追加自己的 v1.23 句,两个 PR 各自与 main 合并都是干净的。本 PR 先合入后,合 #12946 只会在 info.description 处冲突;"version": "1.23.0" 这一行两边相同,git 会静默合并。解决冲突的人如果只是把两句描述拼起来,就会在同一版本号下留下两条 v1.23。后合入的一方必须手动升到 1.24.0,JSON 和设计文档里的引用都要改(见 F1)。#13037(draft)仍声明 v1.22,而这个版本号已被 main 的 G0 占用。
N2 — 更正 triage Stage 2 第 3 条(激活耦合)。 只去掉新条件的 planned 标记,不会生成"要求一个未声明字段"的类型:openapi-typescript 不渲染 if/then,生成结果只是多一个 & unknown。§4.1 要求条件与 taskId/failureCode 一起去掉标记,这条对契约一致性仍然正确;而且无论哪种情况,生成同步测试都会强制重新生成(变异体 C7),所以这个坑不会悄悄通过 CI。
N3 — H3 门槛的真实环境观察。 真实 Session 返回 capabilities.tasks: true, artifacts: false。它能符合新规则"产生输出的任务必须具备 capabilities.artifacts",只是因为目前已上线的任务都不产生输出:ManagedTaskService 返回的 artifact_refs 恒为空。可以考虑在 §6.1 验收清单里加上"首个产生输出的任务上线前先把 artifacts 置为 true"。可选。
本轮补上的 bot 评审自述缺口
- 第 1 轮称"未在真实 networknt 校验器下运行
PlannedTaskContractTest"。本轮已运行,并用 Ajv 对同样的实例做了校验(pre-release: fix ci #1、TypeError in Authentication Selection Interface #5)。 - 第 2 轮称"零变异体",且其建议"没有独立验证者裁决"。本轮用了 15 个变异体,三条建议全部复现(API Key是要设成阿里云的API Key吗? #7、F2)。
- 第 2 轮还称相关消费方"未经系统追踪"。实际情况是:运行时不读取 spec,
ManagedTaskService里的artifact_refs恒为空,生成结果逐字节一致(如何自定义密钥文件 .env可能与其他文件冲突 #3、OpenAI API Error: 401 Incorecct API Key provided #6)。
CI 与范围
当前 head 上 21 项检查通过,只有 review-pr 仍在运行。Hosted process fault gates / MySQL 8.4 在 d6e6448 上为红(main 既有失败),在 05047ae6 上已转绿。
未覆盖:
- 运行时的保留、发布、归档与取消保证。相关路由尚未映射,§6.1 已正确地把它们留给 H3 和取消切片。
- 本地的 Windows/Linux。本 PR 只改平台无关的契约数据,CI 上 ubuntu、windows、macOS 的 Java 各腿均为绿。
装置见这里:构建、真服务 A/B、Ajv 差分、生成器探针、变异体和出图脚本。
….com/QwenLM/qwen-code into codex/settle-task-contract-semantics
|
🤖 Addressed the latest review feedback (round 4/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 4/100 轮)。改动内容与我反驳保留之处如下: Autofix round — PR #12998Two commits this round: a merge of Merge conflict resolution (
|
…lds (#12998) Address the round-3 review findings on the planned task contract: - A6/§4.7: name the Session-scoped open-operation guard — cancel shares managed_agent_operation with the lifecycle commands, so any open operation answers 409 session_operation_active, and scope A7's different-keys promise accordingly. - A7: fix admission_stage, delivery_state and the receipt issuer per settled task_cancel outcome (completed: harness_confirmed/confirmed with the authority's receipt; failed/recovery_blocked: java_durable/blocked, defining the previously unproduced blocked value) and encode the pairing as task_cancel conditionals inside the planned allOf member on both surfaces, with per-outcome instances in PlannedTaskContractTest. - §6.1: H3 must show a tasks-without-artifacts Session admits no output-producing task. - artifact_refs: propagate the no-rotation bound to both field descriptions, design §4.2 and §7's modal, regenerating the WebShell types. - Task events: give the two planned task-events routes a TaskCursorExpired 409 component instead of the shared retained-floor CursorExpired. - §4.1/§7: track the managed_agent_operation migration that persists task_id and failure_code atomically with the FAILED transition. - Tests: pin that failure_code stays task_cancel-only and that WebShellTaskEvent mirrors PublicTaskEvent's properties and conditionals.
|
🤖 Addressed the latest review feedback (round 5/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 5/100 轮)。改动内容与我反驳保留之处如下: AutoFix round — PR #12998 (commit
|
|
@qwen-code /triage |
|
👋 Takeover released: the autofix loop will no longer engage this PR (an in-flight round, if any, completes its bounded work). Re-apply 中文说明👋 已释放:autofix 循环不再介入此 PR(在飞的一轮如有,将完成其有界工作)。重新打上 |
Co-Authored-By: Claude Code <[email protected]>
Maintainer real-environment verification (Linux) — head
|
| Suite | Tests |
|---|---|
| PlannedTaskContractTest (new pins: task_cancel outcomes, artifact_refs bound, event field totality, empty-page cursor) | 7 ✅ |
| ManagedAgentApiContractTest | 5 ✅ |
| ManagedExtensionRecordContractTest | 7 ✅ |
| ManagedExtensionProjectionContractTest | 7 ✅ |
| ManagedSessionStoreContractFixtureTest | 3 ✅ |
| Total | 29 ✅, 0 failures |
Checkstyle: 0 violations.
3. WebShell contract generation — 2/2 tests, zero drift
managed-agent-api.test.ts: 2/2 passed.generate:managed-agent-apire-run: byte-identical to the committed client (no drift).- Planned surface confirmed excluded from the published client: 0
x-qwen-implementation-statusmarkers, noWebShellTaskEventPage, noWebShellTaskCancelRequest; the only published task paths remaintasks/getandtasks/query.
4. Before/after: the four task_cancel tightenings (Ajv, independent of the Java suite)
Key instances validated against main's contract (v1.22.0) vs the PR contract (v1.23.0), through PublicCommandOperation and the PublicOperation union:
| Instance | main | PR |
|---|---|---|
task_cancel status=cancelled |
ACCEPT | REJECT |
task_cancel failed without failure_code |
ACCEPT | REJECT |
recovery_blocked with delivery still pending |
ACCEPT | REJECT |
completed without harness_confirmed admission |
ACCEPT | REJECT |
completed + confirmed + receipt |
ACCEPT | ACCEPT |
failed + failure_code + blocked |
ACCEPT | ACCEPT |
submit_input cancelled / failed without code (other kinds unchanged) |
ACCEPT | ACCEPT |
5. Independent schema validation — 90 + 90 checks, 10/10 mutations caught
A self-contained Ajv harness (validate.mjs) ran 90 behavioral checks with per-version expectations against both contracts — both operation unions plus the camelCase WebShell mirrors, task state invariants, artifact_refs 100/101/duplicate bounds, event unknown-field rejection, per-type field totality probes, and empty-page cursor rules (null/missing next_cursor rejected; empty page keeping its position accepted). 90/90 pass on the baseline and 90/90 on the updated contract. Separately, 10 hand-written schema mutations (drop the whole outcome conditional, allow cancelled, drop failure_code requirement, drop each settled-state pin, open additionalProperties, drop the output/state prohibition, drop uniqueItems, …) were all caught (10/10) by the corresponding behavioral assertion.
6. Published client structure: identical to main
Regenerated the client from main's spec and from the PR spec and diffed: exactly 4 changed lines, all inside the two shared @description strings — zero structural change, matching the PR's claim.
7. Design docs
EN/ZH decisions synchronized: identical section structure (16/16 numbered sections), both pinned to contract v1.23.0.
Screenshots
Notes for the author (non-blocking)
- The PR body is stale relative to head: it says "15 Java tests" (head runs 29 across the contract suites) and "contract 1.22.0 on top of main's 1.21.0" (actual: 1.23.0 on main's 1.22.0). Consider refreshing the Evidence section when convenient.
- Runtime task-event / task-cancel E2E remains unverifiable by design (routes unmapped, still
planned) — consistent with the PR's declared scope; the cursor-floor and backpressure semantics were verified at the contract level only.
中文验证报告(与上文内容一致)
维护者真实环境验证(Linux)— head b1754087cd
结论:全绿。 在 Linux 机器上基于当前 head(main 为 e8fc540be7)独立复验,覆盖评审者测试计划中路由上线前所有可验证项。证据(日志 + 校验脚本 + 截图)保存在 assets 分支:wenshao/qwen-code@assets-pr12998 → pr12998/linux-b1754087/。
环境:Linux x86_64 · Node v24.14.0 · pnpm 11.24.0(corepack,--frozen-lockfile)· Temurin JDK 21.0.12.1 · Maven · Ajv 8.20.0(draft 2020-12)。
1. 构建与静态检查
corepack pnpm install --frozen-lockfile通过,prepare生命周期完成全量 build 与 bundle。- 根目录
typecheck✅、定向eslint✅、变更文件prettier --check✅。
2. Java 契约测试套件 — 29/29(PR 描述写的 15 个已落后于 head)
packages/sdk-java/managed-agent-server 中执行 mvn test -Dtest='*Contract*':PlannedTaskContractTest 7 ✅(新增 task_cancel 结局、artifact_refs 上限、事件字段完备性、空页游标等钉子测试)、ManagedAgentApiContractTest 5 ✅、ManagedExtensionRecordContractTest 7 ✅、ManagedExtensionProjectionContractTest 7 ✅、ManagedSessionStoreContractFixtureTest 3 ✅,合计 29 个全部通过,0 失败。Checkstyle 0 违规。
3. WebShell 契约生成 — 2/2 通过,零漂移
managed-agent-api.test.ts:2/2 通过。- 重新执行
generate:managed-agent-api:与已提交客户端逐字节一致。 - planned 表面确认未泄漏到发布客户端:0 个
x-qwen-implementation-status标记,无WebShellTaskEventPage、无WebShellTaskCancelRequest;已发布任务路径仍只有tasks/get与tasks/query。
4. 前后对比:四项 task_cancel 收紧(独立 Ajv 校验)
关键实例分别用 main 契约(v1.22.0)与 PR 契约(v1.23.0)校验,并经 PublicCommandOperation 与 PublicOperation union 两个入口:
| 实例 | main | PR |
|---|---|---|
task_cancel status=cancelled |
接受 | 拒绝 |
task_cancel failed 缺少 failure_code |
接受 | 拒绝 |
recovery_blocked 但投递仍为 pending |
接受 | 拒绝 |
completed 缺少 harness_confirmed 受理阶段 |
接受 | 拒绝 |
completed + confirmed + receipt |
接受 | 接受 |
failed + failure_code + blocked |
接受 | 接受 |
submit_input cancelled / failed 无 code(其他类型不变) |
接受 | 接受 |
5. 独立 schema 校验 — 90 + 90 项,10/10 变异被捕获
自包含 Ajv 校验脚本(validate.mjs)对两个版本契约各执行 90 项带逐版本预期的行为校验:两个 operation union 及 camelCase WebShell 镜像、任务状态不变量、artifact_refs 100/101/重复上限、事件未知字段拒绝、按类型的字段完备性探针、空页游标规则(null/缺失 next_cursor 拒绝;空页保留位置接受)。基线 90/90、更新后 90/90 全部通过。 另做 10 个手写 schema 变异(删除整个结局条件、允许 cancelled、删除 failure_code 要求、删除各结算状态钉子、放开 additionalProperties、删除 output/state 互斥、删除 uniqueItems 等),全部(10/10)被对应行为断言捕获。
6. 发布客户端结构:与 main 完全一致
分别用 main 与 PR 的 spec 重新生成客户端并 diff:仅 4 行变化,全部位于两条共用 @description 字符串内——零结构变化,与 PR 声明一致。
7. 设计文档
中英文决定同步:章节结构一致(各 16 节编号一一对应),均标注契约 v1.23.0。
给作者的建议(不阻塞合并)
- PR 描述相对 head 已过时:描述称 "15 个 Java 测试"(head 实际 29 个)、"契约 1.22.0 基于 main 的 1.21.0"(实际为 1.23.0 基于 main 的 1.22.0),方便时可刷新 Evidence 一节。
- 运行时任务事件/任务取消 E2E 按设计仍无法验证(路由未映射、仍为
planned),与 PR 声明范围一致;游标下限与背压语义本次仅在契约层面验证。
🤖 Generated with Claude Code
qqqys
left a comment
There was a problem hiding this comment.
Verdict: APPROVE — reviewed at head b1754087cd162945787862fd353c860ce100fb05.
Critical-only scan found no merge-blocking defect. This diff contains no server implementation code — it settles a contract, its design record, the contract test that pins it, and the regenerated client — so the shapes that could block are a contract that mis-certifies shipped behaviour, or a contract test that passes without pinning anything. I checked both.
No blocker on record
No CHANGES_REQUESTED was ever filed. Three review rounds across three heads (ac6b8fa0, edae9a9e, a98c24d4) recorded two, three and eight findings respectively, and every one is severity S. Nothing blocking was left open for this head to inherit.
The settled routes are marked planned, so no shipped behaviour is re-certified
The compatibility risk in a contract-only change is a description that clients are generated from promising something the server does not do. That is not what happens here, and I verified it against the contract at this head rather than against the changelog prose:
x-qwen-implementation-statusis"planned"for bothlistSessionTaskEventsandcancelSessionTask— the two routes whose semantics this PR settles.listSessionTasksandgetSessionTaskremain"partial".- Both of the new conditional blocks that make
failure_code/failureCodemandatory for afailedtask cancel carry"x-qwen-implementation-status": "planned"on the conditional itself, so the requirement attaches to a shape nothing serves yet. The contract's own preamble also instructs consumers to "Filter planned fields as well as routes from production SDKs." - The v1.23 entry states plainly that it "settles the planned task event and cancel semantics" and closes with "Task events and cancel remain planned."
The regenerated client confirms the blast radius: both hunks in managed-agent-api.ts are @description text only, and artifactRefs: string[] and every other type shape are unchanged. No shipped caller's types move.
The one description change that does touch a partial route is artifact_refs — "at most 100, oldest first. Entries must not rotate out until older Artifacts can be enumerated and attributed to the task". That is prose stating a producer obligation and naming the slice that must bound the backlog; it adds no schema constraint, and the maxItems: 100 bound it refers to is pre-existing. It cannot change what a generated client accepts.
The contract test pins both directions, and cannot silently under-cover
The test changes are the part that could have been a false green, so I read them for whether each new rule can actually fail:
- The bound is pinned from both sides.
artifact_refsis accepted at exactly 100 entries, rejected at 101, and rejected on a duplicate — closing the earlier finding that only the widening direction was covered. - The new cancel conditionals are exercised in both directions for each of
PublicCommandOperationandPublicOperation. Arecovery_blockedcancel is accepted with the settled delivery state and rejected while stillpending; acompletedcancel is accepted withadmission_stage: harness_confirmed,delivery_state: confirmedand a receipt, and rejected when the receipt is missing, when it is stilljava_durable, and when it is still unconfirmed. The required-field rules this diff adds are therefore load-bearing rather than decorative. - Page semantics are bidirectional too: an empty page keeps its position with a cursor, and is rejected with a null cursor or none.
Most importantly, the three hand-written cross-type checks that were removed (state_changed with text, output with state, artifact with truncated) are not lost — pinEventFieldTotality() subsumes and generalises them. It reads the contract's own PublicTaskEvent schema, derives the required and optional field sets and every allOf conditional keyed by type.const, then for each event type probes each optional field with a known-valid value and asserts acceptance exactly when that field is a declared companion of the type (state_changed→runtime_state, output→truncated, artifact→none). Each removed check is one cell of that matrix, and the matrix also covers every cell the hand-written list never reached. It then asserts WebShellTaskEvent's properties mirror PublicTaskEvent under camelCase and walks the mirror's conditionals.
Crucially it fails loudly rather than shrinking: an allOf conditional for a type absent from the companion table records "has an unlisted conditional", and an optional field with no known valid value records "has no known valid value". So adding an event type or field without updating the helper breaks the build instead of quietly narrowing coverage — which is the property that makes deleting three checks safe here.
The managed-agent-api.test.ts addition extends the planned-field filter fixture with allOf shapes and asserts an unmarked top-level allOf survives while a planned-marked one is stripped, matching the allOf-based conditionals this diff introduces.
CI, and one honest limit on my evidence
Green at this head: ubuntu-latest / Java 11, Java 17, Java 21, windows-latest / Java 21, macos-latest / Java 21, Runtime Broker and Managed Agent MariaDB / Java 21, Real daemon E2E / Java 11, Test (ubuntu-latest, Node 22.x), Lint & Static, Integration Tests (no-AK, No Sandbox), web-shell E2E Smoke and Capture web-shell visuals all pass. review-pr and Hosted process fault gates / MySQL 8.4 / Java 21 are still pending; neither is treated as a gate here, and the Hosted gate is not attributable to this diff, which touches no hosted integration test, driver or workflow.
Because the run is still in progress the per-job logs are not downloadable yet, so I could not read the Tests run: … -- in PlannedTaskContractTest line to prove the class executed. My evidence is the passing Java matrix jobs, which compile and run this module's surefire phase — a failure or error in it fails the job.
Coverage disclosure
I read every normative line of the contract diff and verified the route and conditional status markers in the file at this head, the whole generated-client diff, the whole client-test diff, and the contract test's new cases and pinEventFieldTotality() in full (its final mirror-conditional loop was where my read stopped). I did not read the two design-doc files line by line (+217/−69 and +101/−46). That is a deliberate trade: the docs are a design record for routes the contract marks planned, all eight round-3 findings against them and the test were Suggestions, and the certification that actually reaches clients — the contract's status markers and the generated types — is what I verified directly.
wenshao
left a comment
There was a problem hiding this comment.
Not explored to full depth (tool budget reached): "agent 4": 无(未触发预算上限,所有检查均已完成)。; "agent 6c": web-shell 生成器一致性测试未能本地执行(本工作树 node_modules 缺 openapi-typescript ,vitest 加载失败);"再生成不变" 的结论基于对 withoutPlanned 逻辑与已提交生成文件 diff 的阅读,而非执行。; "agent 6c": Java 测试( PlannedTaskContractTest )未执行(按 brief 约束不在共享树构建 mvn 模块);新条件的拒绝/接受行为经 JSON Schema 2020-12 语义人工推演确认,并核对 networknt V202012 的能力,但未跑通运行时验证。; "agent 6c": 独立的 Python jsonschema 库未安装,无法做第二实现交叉复核新条件的校验行为。; "agent 1c": ManagedAgentApiContractTest 全套 Spring 集成测试未运行 —— mvn test 在编译与本 PR 无关的 main 源码( WorkspaceRuntimeTransport.java 解析不到本地陈旧 runtimebroker 构件的 installPublish…, and 1 more.
Not reviewed: "Reply with exactly the word OK and nothing else. Do not…" — the agent made no tool call: it read nothing.
Not reviewed: reverse audit — no auditor was launched with a prompt this skill builds — the pass that hunts what the rest of the review missed ran, if at all, without the method its brief carries.
— [unverified] tag when the loop ended — the verifier never ruled on them, and they are not confirmed.
Mechanism health: this round did not close cleanly, so it withholds the incremental anchor — and the round it recovered had no anchor this round could use either — none at all, one with no certifier, one certified by an identity other than the one this round runs under, or one this round's fetch refused or resolved to the head — so the next review re-reads the whole diff unless recovery grafts an earlier own anchor that the round running it can use onto the complete work list this round leaves behind, and keeps doing so until a round's marker carries an anchor again or a graft lands that the round running it can use. (Stated, not acted on — this changes nothing about what the round posts.)
中文说明
未探索到全部深度(达到工具调用预算):"agent 4":无(未触发预算上限,所有检查均已完成)。;"agent 6c":web-shell 生成器一致性测试未能本地执行(本工作树 node_modules 缺 openapi-typescript ,vitest 加载失败);"再生成不变" 的结论基于对 withoutPlanned 逻辑与已提交生成文件 diff 的阅读,而非执行。;"agent 6c":Java 测试( PlannedTaskContractTest )未执行(按 brief 约束不在共享树构建 mvn 模块);新条件的拒绝/接受行为经 JSON Schema 2020-12 语义人工推演确认,并核对 networknt V202012 的能力,但未跑通运行时验证。;"agent 6c":独立的 Python jsonschema 库未安装,无法做第二实现交叉复核新条件的校验行为。;"agent 1c":ManagedAgentApiContractTest 全套 Spring 集成测试未运行 —— mvn test 在编译与本 PR 无关的 main 源码( WorkspaceRuntimeTransport.java 解析不到本地陈旧 runtimebroker 构件的 installPublish…,另有 1 条。
未审查:"Reply with exactly the word OK and nothing else. Do not…"——该 agent 未发起任何工具调用:它什么都没读。
未审查:反向审计——没有审计 agent 是用本 skill 构建的 prompt 启动的——负责搜寻评审其余部分遗漏问题的这道工序,即便运行过,也缺失了 brief 承载的方法。
— [unverified] 标记——验证者从未对它们作出裁决,它们不算已确认。
机制健康:本轮未能干净收尾,因而扣留了增量锚点,而它恢复到的那一轮也没有留下本轮可用的锚点——要么完全没有、要么没有认证者、要么由本轮运行身份之外的身份认证、要么被本轮的获取拒绝或解析为头提交——因此下一次评审将重读整个 diff,除非恢复流程把本轮能使用的更早自有锚点嫁接到本轮留下的完整工作清单上;并会一直如此,直到某一轮的标记重新带上锚点,或落地的嫁接能被运行该轮的评审使用。(仅陈述,不据此行动——这不改变本轮发布的任何内容。)
— glm-5.3-flash via Qwen Code /review (v0.24.6)








What this PR does
Settles A1–A8 of #12847 before the task event and cancellation routes become available. Contract v1.23.0 defines a durable retention floor even when no events remain, publishes a committed prefix with stable cursor identities, and requires archived output to be discoverable before its events expire. Recovery reads one event page before refreshing the task and reading its Artifacts, so a continuously producing task does not prevent recovery from reaching the archive step. Archive failures preserve accepted output; bounded backlog, backpressure and complete Artifact discovery are explicit gates for H3.
Cancellation validates the request and current access before replay, and replays the same operation with its latest durable state before checking new-request capabilities and Session/task state. The contract distinguishes durable command admission, owner acceptance and physical task settlement, defines concurrent keys and unknown outcomes, rejects a cancelled cancellation operation, and requires a reason for a definitively failed one. Clients tolerate unknown optional event fields and types across minor versions while producers retain closed schemas for their own version.
The English and Chinese design decisions are synchronized. Task events and cancel remain planned. The published WebShell structure is unchanged; only a shared description changes in generated output. A9 and the task-route portion of A10 are already settled by #12966.
Why it's needed
The previous wording could silently lose expired output when the retained stream was empty, permit late commits behind a returned cursor, or rely on an Artifact projection that did not yet expose archived text. Cancellation replay could also be confused with fresh admission or physical stop. These guarantees are cheapest to settle before the corresponding routes become partial and their semantics become a compatibility commitment.
Reviewer Test Plan
How to verify
Evidence (Before & After)
N/A for UI. A saved baseline accepted task_cancel cancelled and failed without a reason; the updated contract rejects both on both surfaces and through their operation unions.
b1754087cdpassed (frozen-lockfile build, 29 Java contract tests, WebShell regen drift-free, 90+90 independent Ajv checks, 10/10 schema mutations caught, published client structurally identical to main): comment.Tested on
Environment (optional)
macOS arm64; Node 22.22.2; pnpm 11.24.0; JDK 21.0.11; Maven 3.8.4. Global qwen 0.24.6 was used for CLI discovery, and the local bundle version smoke check passed.
Risk & Scope
Design: English and 简体中文.
Linked Issues
Related #12847 (A1–A8). Part of #12827. A9 and task-route A10 were handled by #12966; the broader tracking issue remains open.
中文说明
本 PR 做了什么
在任务事件与取消路由上线之前,确定 #12847 的 A1–A8。契约 v1.23.0 规定:即使没有保留事件,也保留持久化保留下限;事件以已提交前缀发布,游标身份稳定;输出事件过期之前,其归档必须可被发现。恢复先读一页事件,再刷新任务并读取 Artifact,因此持续输出不会阻止恢复进入归档读取。归档失败时保全已接受输出;持久积压上限、背压和完整 Artifact 发现能力明确列为 H3 的验收门槛。
取消先校验请求与当前访问权,再重放;在检查新请求的能力和 Session/任务状态之前,返回同一 operation 的最新持久状态。契约区分命令持久受理、owner 接受和物理任务结算,定义不同键并发及未知结果,禁止取消 operation 自身为 cancelled,并要求确定失败时携带原因。客户端在 minor 版本之间容忍未知可选事件字段和类型,生产者仍按自身版本的封闭 schema 校验。
英文和中文设计决定同步更新。任务事件和取消仍为 planned。已发布的 WebShell 结构不变,生成结果只改变一条共用描述。A9 和 A10 的任务路由部分已经由 #12966 完成。
为什么需要
旧措辞可能在保留流为空时静默遗漏已过期输出,允许晚提交出现在已返回游标之前,或依赖尚未暴露归档文本的 Artifact 投影。取消重放也可能与新请求准入或物理停止混淆。在对应路由变为 partial、语义成为兼容承诺之前,确定这些保证的成本最低。
评审者测试计划
如何验证
证据(变更前后)
无 UI 变化。保存的基线接受 task_cancel cancelled 和 failed 缺少原因;更新后的契约在两侧接口及其 operation union 中均拒绝它们。
b1754087cd)通过:frozen-lockfile 构建、29 个 Java 契约测试、WebShell 重新生成零漂移、90+90 项独立 Ajv 校验、10/10 schema 变异捕获、发布客户端结构与 main 一致,见评论。测试平台
环境(可选)
macOS arm64;Node 22.22.2;pnpm 11.24.0;JDK 21.0.11;Maven 3.8.4。全局 qwen 0.24.6 用于 CLI 能力发现,本地 bundle 的版本冒烟检查通过。
风险与范围
设计:English 与 简体中文。
关联 Issue
关联 #12847(A1–A8),属于 #12827。A9 和任务路由 A10 已由 #12966 处理;更大的跟踪 issue 保持打开。