Skip to content

fix(managed-agent): Settle task event and cancel semantics - #12998

Merged
wenshao merged 8 commits into
mainfrom
codex/settle-task-contract-semantics
Sep 30, 2026
Merged

wenshao merged 8 commits into
mainfrom
codex/settle-task-contract-semantics

Conversation

@wenshao

@wenshao wenshao commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

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

  • Check that a cursor equal to the durable floor is valid, a cursor below it expires even for an empty stream, and concurrent publication cannot introduce an event behind any returned cursor.
  • Follow recovery with delayed archival, a lagging task projection, continuous output and more than 100 historical Artifacts. The contract must either preserve complete discovery or prevent unsupported rotation, preserve the page checkpoint and require backpressure before capacity is exhausted.
  • Check cancellation retries after capability or state changes: an authorized retained request returns the same operation and latest state; revoked access remains denied, and another payload under the same key conflicts. Command completion must not imply task settlement.
  • Confirm the task-cancel schema rejects cancelled and failed-without-reason instances, directly and through both operation unions, while retaining valid command states and the existing shapes of other operation kinds.
  • Regenerate the client contract and confirm planned routes, fields and the new planned conditional remain excluded. Unknown event fields are rejected by the producer's current schema; this is not a test of cross-minor client response validation.

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.

  • Java focused contract suites: 29 tests passed (PlannedTaskContractTest 7 plus the four other contract suites); Checkstyle reports 0 violations.
  • WebShell contract generation/tests: 2 tests passed; the published schema structure is identical after excluding descriptions and version metadata.
  • Independent schema validation: 76 baseline checks and 76 updated checks passed. Ten separate schema mutations were caught by the corresponding behavioral assertions.
  • Frozen-lockfile install passed and its prepare lifecycle completed the full build and bundle. Root typecheck, focused ESLint, Prettier and diff checks passed.
  • Three rounds of full-diff open-ended and reverse audits completed before commit. Round 1 corrected recovery waiting for a live stream to reach its end; rounds 2 and 3 each reported Critical 0 / Suggestion 0. The requested Critical-only rule after round 5 was not reached.
  • Maintainer Linux re-verification at head b1754087cd passed (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

OS Status
🍏 macOS ✅
🪟 Windows ⚠️
🐧 Linux ✅

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

  • Main risk or tradeoff: H3 must implement archival visibility, bounded backlog/backpressure and complete recovery; cancellation slices must implement the specified ordering and reconciliation. These are concrete future acceptance gates.
  • Not validated / out of scope: runtime task-event and task-cancel E2E, because those routes remain unmapped; other groups of feat(managed-agent): Track the task contract gaps deferred from the H0a review #12847; catalog error semantics; Windows/Linux execution. Contract tests do not establish runtime timing or durability.
  • Breaking changes / migration notes: no served behavior or published client structure changes. The new task-cancel-only condition remains planned and must be activated together with its planned task-ID and failure-code fields when the route is implemented. No shared status enum is narrowed. The contract version is 1.23.0 on top of main's 1.22.0.

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、语义成为兼容承诺之前,确定这些保证的成本最低。

评审者测试计划

如何验证

  • 确认等于持久下限的游标合法,低于下限的游标即使面对空流也过期,并发发布不能在已返回游标之前插入事件。
  • 检查归档延迟、任务投影滞后、持续输出和超过 100 个历史 Artifact 时的恢复流程。契约必须保证完整发现能力或阻止不支持的轮转,保留分页检查点,并在容量耗尽之前要求背压。
  • 检查能力或状态变化后的取消重试:仍获授权且记录保留的请求返回同一 operation 的最新状态;撤销权限仍被拒绝,同键不同载荷冲突。命令完成不能代表任务已经结算。
  • 确认任务取消 schema 直接校验以及经两侧 operation union 校验时,都拒绝 cancelled 和 failed 缺少原因的实例,同时保留合法命令状态及其他 operation 类型的既有结构。
  • 重新生成客户端契约,确认 planned 路由、字段及新增的 planned 条件均被过滤。未知事件字段被生产者当前 schema 拒绝;这不是跨 minor 版本的客户端响应校验测试。

证据(变更前后)

无 UI 变化。保存的基线接受 task_cancel cancelled 和 failed 缺少原因;更新后的契约在两侧接口及其 operation union 中均拒绝它们。

  • Java 定向契约测试:29 个通过(PlannedTaskContractTest 7 个及其余四个契约套件);Checkstyle 0 违规。
  • WebShell 契约生成与测试:2 个通过;排除描述与版本元数据后,已发布 schema 结构完全相同。
  • 独立 schema 校验:基线 76 项与更新后 76 项均通过。10 个独立 schema 变异均被对应行为断言捕获。
  • frozen-lockfile 安装通过,其 prepare 生命周期完成了全量 build 和 bundle。根目录 typecheck、定向 ESLint、Prettier 及 diff 检查均通过。
  • 提交前完成 3 轮完整 diff 的无方向审计和反向审计。第 1 轮修复等待活跃事件流读到末尾的问题;第 2、3 轮均为 Critical 0 / Suggestion 0。未进入第 5 轮后的仅处理 Critical 阶段。
  • 维护者 Linux 复验(head b1754087cd)通过:frozen-lockfile 构建、29 个 Java 契约测试、WebShell 重新生成零漂移、90+90 项独立 Ajv 校验、10/10 schema 变异捕获、发布客户端结构与 main 一致,见评论。

测试平台

OS 状态
🍏 macOS ✅
🪟 Windows ⚠️
🐧 Linux ✅

环境(可选)

macOS arm64;Node 22.22.2;pnpm 11.24.0;JDK 21.0.11;Maven 3.8.4。全局 qwen 0.24.6 用于 CLI 能力发现,本地 bundle 的版本冒烟检查通过。

风险与范围

  • 主要风险或取舍:H3 必须实现归档可见性、持久积压上限/背压和完整恢复;取消切片必须实现规定的顺序和对账。这些都是明确的后续验收门槛。
  • 未验证/不在范围内:运行时任务事件与任务取消 E2E,因为路由仍未映射;feat(managed-agent): Track the task contract gaps deferred from the H0a review #12847 的其他组;catalog 错误语义;Windows/Linux 执行。契约测试不能证明运行时的时序或持久性。
  • 破坏性变更/迁移:不改变已提供的行为或已发布客户端结构。新增任务取消专属条件仍为 planned,路由实现时必须与其 planned 的任务 ID、失败原因字段一起启用。不收窄共用状态枚举。契约版本为 1.23.0,基于 main 的 1.22.0。

设计:English 与 简体中文。

关联 Issue

关联 #12847(A1–A8),属于 #12827。A9 和任务路由 A10 已由 #12966 处理;更大的跟踪 issue 保持打开。

@wenshao

wenshao commented Sep 29, 2026

Copy link
Copy Markdown
Collaborator Author

Contract verification report for d6e6448 (macOS arm64).

  • Global CLI discovery: qwen 0.24.6 --help / --version passed. These planned Java task-event/cancel routes cannot be exercised through the CLI, so verification used the real contract validator and independent schema checks. Runtime E2E is deferred to H3 and the cancellation implementation slices.
  • Java: PlannedTaskContractTest, ManagedAgentApiContractTest, ManagedSessionStoreContractFixtureTest: 15 tests passed; Checkstyle: 0 violations. JDK 21.0.11, Maven 3.8.4.
  • WebShell: regeneration and both managed API tests passed. Published structure comparison against the baseline was identical after excluding descriptions/version metadata.
  • Baseline: all four task-cancel schema/union paths accepted cancelled and failed without a reason. Updated: all four reject those cases and retain valid states and other command kinds. Independent checks: 76 baseline + 76 updated passed.
  • Reverse verification: 10 one-rule mutations across public and WebShell schemas were caught by the intended assertions (cancelled status, missing failure reason, unknown event field, missing cursor, null cursor). Mutations used temporary copies, not the working tree.
  • Frozen-lockfile install, complete build/bundle via prepare, root typecheck, focused ESLint, Prettier and diff checks passed. Local bundle version smoke check passed.
  • Three open-ended/reverse audit rounds completed before commit; the last two independently reported Critical 0 / Suggestion 0. The first round corrected recovery waiting indefinitely for a live stream to reach its end.

Final OpenAPI SHA256: 29183869ef7e0107bae8b92fe73beda137a597448e85e2ee88d499572ee90882. These results establish schema constraints and existing consumer compatibility, not the future runtime's retention, publication, archival or cancellation guarantees.

中文说明

提交 d6e6448 的契约验证报告,平台为 macOS arm64。

  • 全局 qwen 0.24.6 的 --help / --version 通过。planned 的 Java 任务事件/取消路由无法经 CLI 测试,因此使用真实契约校验器和独立 schema 检查;运行时 E2E 留待 H3 和取消实现切片。
  • 三个 Java 定向契约套件共 15 个测试通过,Checkstyle 0 违规。环境为 JDK 21.0.11、Maven 3.8.4。
  • WebShell 重新生成和两个 managed API 测试通过。排除描述/版本元数据后,已发布结构与基线相同。
  • 基线在四个任务取消 schema/union 路径中均接受 cancelled 与 failed 缺少原因;新版均拒绝,并保留合法状态和其他命令类型。独立检查:基线 76 项、新版 76 项通过。
  • 反向验证:公共/WebShell 两侧共 10 个单规则变异均被预期断言识别,覆盖 cancelled、失败缺原因、未知事件字段、缺少游标和 null 游标。变异仅使用临时副本。
  • frozen-lockfile 安装、prepare 中的完整 build/bundle、根目录 typecheck、定向 ESLint、Prettier 和 diff 检查通过,本地 bundle 版本冒烟检查通过。
  • 提交前完成 3 轮无方向/反向审计,最后两轮独立为 Critical 0 / Suggestion 0;首轮修复了持续输出时恢复可能无限等待流末尾的问题。

最终 OpenAPI SHA256 为 29183869ef7e0107bae8b92fe73beda137a597448e85e2ee88d499572ee90882。这些结果证明 schema 约束及现有消费者兼容性,不证明后续运行时的保留、发布、归档或取消保证。

@wenshao
wenshao marked this pull request as ready for review September 29, 2026 07:51
@wenshao

wenshao commented Sep 29, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /takeover

@qwen-code-dev-bot qwen-code-dev-bot added the autofix/takeover Summon the autofix loop to manage this PR (remove to release; needs triage+) label Sep 29, 2026
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤝 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/takeover label (or comment @qwen-code /takeover stop) to release.

中文说明

🤝 已接管:autofix 循环现在管理此 PR —— 将持续处理新的评审反馈与 base 冲突,直到移除标签或达到轮次上限。移除 autofix/takeover 标签(或评论 @qwen-code /takeover stop)即可释放。

@qwen-code-dev-bot

qwen-code-dev-bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

✅ AutoFix round 5 finished — view run. See this round's report below.

中文说明

✅ AutoFix 第 5 轮已完成 —— 查看运行。本轮报告见下方。

@wenshao wenshao removed the autofix/takeover Summon the autofix loop to manage this PR (remove to release; needs triage+) label Sep 29, 2026
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Addressed the latest review feedback (round 1/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/100 轮)。改动内容与我反驳保留之处如下:

Round summary

Both inline suggestions from the automated reviewer were reproduced as real
coverage gaps on the current spec and implemented in
packages/sdk-java/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/PlannedTaskContractTest.java.
No production code, spec, or documentation changed. Commit:
test(managed-agent): Pin the task artifact_refs bound and event field matrix (#12998).

[rc:4134535131] R1-1 — artifact_refs maxItems: 100 / uniqueItems unexercised — Implemented

Reproduced the gap before editing: deleting maxItems: 100 from both
artifact_refs mirrors left all 7 PlannedTaskContractTest tests green.

In taskViewKeepsItsStateInvariants, next to the existing
"duplicate capability" reject, added two rejects built from the existing
task(...) helper (so the instances still satisfy PublicTask's state
conditionals): one with 101 artifact_refs entries, one with a duplicated
entry. The existing check() mirror machinery validates each instance against
both PublicTask and the camelCase WebShellTask copy.

[rc:4134535150] R1-2 — newly permitted optional event field not covered — Implemented

Reproduced the gap before editing: adding an unconditioned optional
exit_code/exitCode property to both event mirrors left all 7 tests green.

Added pinEventFieldTotality(), called at the end of
taskEventsKeepOneShapePerType. It derives the optional property set from
each schema (properties minus top-level required) and asserts that for
each known type (state_changed, output, artifact) every optional
property is either in that type's then.required or in its
then.not.anyOf[].required, apart from one explicit companions allow-table
(state_changed → runtime_state, output → truncated, artifact → none,
per design section 4.3). The same table is camelCased with the existing
camelCase() helper for the WebShellTaskEvent mirror reached through
MIRRORS, so the WebShell copy is checked against the same table rather than
a second hand-listed set. A conditional added for a type missing from the
table also fails, keeping the matrix explicit.

Mutation probes

Each probe mutated the spec, re-ran mvn test -Dtest=PlannedTaskContractTest,
and was reverted with git checkout afterwards:

  • maxItems: 100 removed from both mirrors → RED:
    PublicTask/WebShellTask artifact_refs over the bound: expected invalid.
  • uniqueItems: true removed from both mirrors → RED:
    PublicTask/WebShellTask duplicate artifact_ref: expected invalid.
  • {"required": ["artifact_id"]} removed from the output conditional's
    not.anyOf on both mirrors → RED: output leaves artifact_id /
    artifactId neither required nor forbidden (the reviewer's acceptance
    mutation).
  • Unconditioned optional exit_code/exitCode added to both mirrors → RED:
    totality failure on all three known types on both mirrors.
  • Conditional added for an unlisted fourth type (cancelled) → RED:
    has an unlisted conditional for cancelled on both mirrors.

After restoring the spec, the suite is green again.

Verification

  • mvn test -Dtest=PlannedTaskContractTest (managed-agent-server) — 7 tests passed
  • mvn test (managed-agent-server, full unit suite) — 184 tests passed
  • mvn checkstyle:check (managed-agent-server) — passed, 0 violations
  • npm run build — passed (with COREPACK_HOME=/tmp/corepack-home; the sandbox's HOME is read-only for the build user)
  • npm run typecheck — passed
  • npm run lint — passed
  • Focused Vitest: not applicable — the touched package (packages/sdk-java/managed-agent-server) is Maven-only and has no JS test gate; no TypeScript file changed this round.
中文说明

本轮摘要

自动评审的两条行内建议都在当前 spec 上复现为真实的覆盖缺口,并在
packages/sdk-java/managed-agent-server/src/test/java/com/alibaba/qwen/code/managedagent/PlannedTaskContractTest.java
中实现。未改动任何生产代码、spec 或文档。提交:
test(managed-agent): Pin the task artifact_refs bound and event field matrix (#12998)。

[rc:4134535131] R1-1 — artifact_refs 的 maxItems: 100 / uniqueItems 未被执行 — 已实现

修改前先复现了缺口:从两侧 artifact_refs 镜像中删除 maxItems: 100
后,PlannedTaskContractTest 的全部 7 个测试仍然通过。

在 taskViewKeepsItsStateInvariants 中、紧邻既有的 "duplicate capability"
reject,新增了两个 reject,均用现有的 task(...) 辅助方法构造(因此实例
仍满足 PublicTask 的状态条件):一个包含 101 个 artifact_refs 条目,
另一个包含重复条目。现有的 check() 镜像机制会把每个实例同时校验
PublicTask 和 camelCase 的 WebShellTask 副本。

[rc:4134535150] R1-2 — 新允许的可选事件字段无覆盖 — 已实现

修改前先复现了缺口:向两侧事件镜像添加一个无条件的可选
exit_code/exitCode 属性后,全部 7 个测试仍然通过。

新增 pinEventFieldTotality(),在 taskEventsKeepOneShapePerType 末尾
调用。它从每个 schema 推导可选属性集合(properties 减去顶层
required),并断言对每个已知类型(state_changed、output、
artifact),每个可选属性要么位于该类型的 then.required 中,要么位于
其 then.not.anyOf[].required 中,仅豁免一张显式的伴随字段表
(state_changed → runtime_state、output → truncated、artifact → 无,
依据设计文档 4.3 节)。同一张表通过现有的 camelCase() 辅助方法转为
camelCase 后,用于经 MIRRORS 触达的 WebShellTaskEvent 镜像,因此
WebShell 副本按同一张表校验,而不是第二套手工列举。若新增的 conditional
对应一个不在表中的类型,同样会失败,从而保持矩阵显式。

变异探针

每个探针都修改了 spec,重新运行 mvn test -Dtest=PlannedTaskContractTest,
随后用 git checkout 还原:

  • 从两侧镜像删除 maxItems: 100 → 变红:
    PublicTask/WebShellTask artifact_refs over the bound: expected invalid。
  • 从两侧镜像删除 uniqueItems: true → 变红:
    PublicTask/WebShellTask duplicate artifact_ref: expected invalid。
  • 从两侧镜像 output 条件的 not.anyOf 中删除
    {"required": ["artifact_id"]} → 变红:output leaves artifact_id /
    artifactId neither required nor forbidden(即评审给出的验收变异)。
  • 向两侧镜像添加无条件的可选 exit_code/exitCode → 变红:两侧镜像的
    全部三个已知类型都报穷尽性失败。
  • 为未列出的第四种类型(cancelled)添加 conditional → 变红:两侧镜像
    均报 has an unlisted conditional for cancelled。

还原 spec 后,测试套件恢复全绿。

验证

  • mvn test -Dtest=PlannedTaskContractTest(managed-agent-server)— 7 个测试通过
  • mvn test(managed-agent-server 完整单元套件)— 184 个测试通过
  • mvn checkstyle:check(managed-agent-server)— 通过,0 违规
  • npm run build — 通过(使用 COREPACK_HOME=/tmp/corepack-home;沙箱的 HOME 对构建用户只读)
  • npm run typecheck — 通过
  • npm run lint — 通过
  • 针对性 Vitest:不适用 — 触达的包(packages/sdk-java/managed-agent-server)仅使用 Maven,无 JS 测试关口;本轮未修改任何 TypeScript 文件。

Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。

🧵 Resolved all 2 selected review thread(s). · 已关闭全部选中的 2 条评审线程。

Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human. · 有空请复审;第 100 轮后本 bot 停止并将 PR 交给人工。


🧠 Handled by Qwen Code · model/模型 kimi-k3 · CLI 0.24.7

@wenshao

wenshao commented Sep 29, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /takeover from 3

@qwen-code-dev-bot qwen-code-dev-bot added the autofix/takeover Summon the autofix loop to manage this PR (remove to release; needs triage+) label Sep 29, 2026
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤝 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/takeover label (or comment @qwen-code /takeover stop) to release.

中文说明

🤝 已接管:autofix 循环现在管理此 PR —— 将持续处理新的评审反馈与 base 冲突,直到移除标签或达到轮次上限。本窗口轮次计数从 3 起算(即本 PR 托管前已进行的评审轮数),因此再经过 2 个产生改动的轮次即进入 Critical-only,而非重新计满 5 轮。移除 autofix/takeover 标签(或评论 @qwen-code /takeover stop)即可释放。

@wenshao

wenshao commented Sep 30, 2026

Copy link
Copy Markdown
Collaborator Author

@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-dev-bot

Copy link
Copy Markdown
Collaborator

Qwen Code resolved the merge conflicts and pushed the branch update.

Merge resolution: PR #12998 ← main

Merge commit 05047ae6c7 (parents edae9a9e73 + a1c90bd2a8). Only conflicted — and only edited — file: managed-agent-public-api.openapi.json (sdk-java).

Root cause

A contract-version race. On main, #12955 (G0 public Workspace file turns) bumped info.version 1.21.0 → 1.22.0 and appended a v1.22 … sentence to info.description. This PR bumped the same two lines to 1.22.0 for the settled task event/cancel semantics. Both wrote to the same append point, so git could not order them. The rest did not overlap and auto-merged; main's four G0 edits all survived.

Textual + one semantic decision

Textually adjacent, but info.version is an append-only shared namespace and both claimed 1.22. main's v1.22 is already published, so this PR's entry renumbers to v1.23 (junction elided):

"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

  • Append order. main's v1.22 sentence must precede this PR's v1.23 sentence, and info.version must equal the highest vN.NN in that history. Appending without raising info.version, or reusing a number main has since published, breaks this.
  • Nothing pins the version. OpenApiContract.java asserts none, server.test.ts's sole OpenAPI hit is a comment, and the web-shell generator never emits info. So no regeneration was needed — managed-agent-api.ts auto-merged as an exact union.
  • Union verified. Diff vs main = 94 insertions / 23 deletions, identical to this PR's diff vs the merge-base; parses as JSON, no duplicate keys.

Not verified / follow-up

  • Two stale version references remain in files that did not conflict, so I left them: docs/design/2026-09-27-managed-agent-task-contract.md:52 ("in contract v1.22.0") and …task-contract.zh-CN.md:41. A follow-up must renumber both to v1.23.0 or the docs contradict the contract; nothing else cites a version.
  • No build/typecheck/lint/test was run. PlannedTaskContractTest.java and managed-agent-api.test.ts read paths/schemas, not info, so the renumber should not reach them — unverified.
中文说明

合并提交 05047ae6c7(父 edae9a9e73 + a1c90bd2a8),唯一冲突且唯一修改的文件即上述 openapi 契约。

根因:契约版本号撞车。main 的 #12955(G0 公共 Workspace 文件轮次)把 info.version 升到 1.22.0,并在单行 info.description 末尾追加 v1.22 …;本 PR 也把同两行升到 1.22.0,用于确定任务事件与取消语义。双方写在同一追加点,git 无法排序。其余部分不重叠、已自动合并,main 四处 G0 描述均在。

处理:info.version 是只增不改的共享命名空间,main 的 v1.22 已发布,故本 PR 条目改号为 v1.23、版本升为 1.23.0,保留 main 的 v1.22 句、其后接改号后的本 PR 句。

关键约束:v1.22 句须排在 v1.23 句之前,且 info.version 须等于历史中最高的 vN.NN;追加句子却不升版本号、或复用 main 已发布的号即为破坏。无消费方钉住版本号(无测试断言版本、生成脚本不输出 info),故无需重新生成,managed-agent-api.ts 已是精确并集;对 main 差异 94 增 / 23 删,与本 PR 原始改动一致,JSON 合法无重复键。

未验证 / 需后续:任务契约设计文档中英文版仍写 v1.22.0(.md:52、.zh-CN.md:41),因文件未冲突故未改,需后续改为 v1.23.0,否则与契约矛盾;main 的 G0 文档未写版本号,无其他过时引用。本次未执行构建与测试;两个契约测试读取 paths/schemas 而非 info,理论上不受改号影响,但未验证。

…tract-semantics

# Conflicts:
#	packages/sdk-java/managed-agent-server/src/main/resources/openapi/managed-agent-public-api.openapi.json
@wenshao

wenshao commented Sep 30, 2026

Copy link
Copy Markdown
Collaborator Author

Local real-environment verification — PR #12998 @ 05047ae6 (macOS arm64)

Verdict: mergeable. I found no correctness defect in the contract, the schema conditionals or the generated client. Before merging, I recommend fixing F1: two design-doc lines and the PR description still say v1.22.0 after the /resolve renumber. F2 (the three open bot threads) is test strength only; a verified candidate is linked below. Coordinate N1 (version numbering) with #12946, which also claims 1.23.0.

Environment. JDK 21.0.12 · Maven 3.9.16 · Node 24.18.1 · pnpm 11.24.0 (frozen, offline) · Spring Boot 3.5.16 jars · dedicated MySQL 8.4.11 container (JVM and DB both UTC). The base arm is the merge base a1c90bd2. origin/main is now 19684f37, one unrelated commit ahead, and a trial merge with it is clean.

What I ran

# 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

zero runtime change and real server A/B

Ajv differential and generated client

mutation matrix

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_refs maxItems from 100 to 50 survives, because no instance accepts a full list.
  • R2-2: making state_changed forbid its companion runtime_state survives.
  • R1-2 (fix-induced): replacing output's {required:[artifact_id]} with a conjunction {artifact_id, state} survives, because pinEventFieldTotality counts 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_changed with runtime_state: ready;
  • count only single-name not.anyOf branches 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.

version numbering and doc drift

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

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:52
  • docs/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 评审自述缺口

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 差分、生成器探针、变异体和出图脚本。

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Addressed the latest review feedback (round 4/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 4/100 轮)。改动内容与我反驳保留之处如下:

Autofix round — PR #12998

Two commits this round: a merge of origin/main resolving the contract-version conflict, and one commit resolving all three review findings in PlannedTaskContractTest.java (net 46 insertions / 46 deletions).

Merge conflict resolution (--conflict true)

packages/sdk-java/managed-agent-server/src/main/resources/openapi/managed-agent-public-api.openapi.json conflicted in info: both this PR and main's #12955 (G0 public Workspace file turns) had bumped the contract from 1.21.0 to 1.22.0 with different change entries. Resolved by keeping main's v1.22 entry intact and renumbering this PR's entry to v1.23 ("version": "1.23.0", description sentence v1.23 settles the planned task event and cancel semantics: ...). The two design docs that named the follow-up's contract version were renumbered to match (docs/design/2026-09-27-managed-agent-task-contract.md and its .zh-CN.md twin, one occurrence each). The rest of the file merged textually; the merged JSON parses, has no duplicate keys, and the WebShell client was regenerated with npm run generate:managed-agent-api --workspace=@qwen-code/web-shell, which produced no diff — the auto-merged generated file was already correct, and it embeds no version string.

Findings

R2-1 [rc:4138276315] — artifact_refs bound pinned only in the widening direction — RESOLVED

Claim verified by reading the test: task() seeds an empty artifact_refs and only the two rejects populated it, so lowering maxItems below 100 was unobservable. Implemented the suggested fix: added an at-bound accept of exactly 100 distinct refs (artifact-0…artifact-99, satisfying uniqueItems and the 1–128 item length bounds) ahead of the over-bound reject, mirroring the file's own two-sided pin of the limit bound (100 valid / 101 invalid).

Mutation probe: set maxItems 100 → 99 on both PublicTask.artifact_refs and WebShellTask.artifactRefs, ran the suite — taskViewKeepsItsStateInvariants failed on both surfaces with artifact_refs at the bound: expected valid (the previously invisible mutation). Restored; suite green.

R2-2 [rc:4138276348] — no positive assertion that state_changed permits its runtime_state companion — RESOLVED

Resolved by the probe rewrite below: the generated probe set includes state_changed with runtime_state (runtime_state: "ready", a valid TaskRuntimeState literal) expecting valid on both surfaces, which is exactly the requested positive assertion.

Mutation probe: added {"required": ["runtime_state"]} to the state_changed conditional's then.not.anyOf on PublicTaskEvent (and {"required": ["runtimeState"]} on WebShellTaskEvent) — taskEventsKeepOneShapePerType failed on both surfaces with state_changed with runtime_state: expected valid. Restored; suite green.

R1-2 [rc:4138276359] — totality helper flattens not.anyOf[].required, so a conjunction branch silently widens — RESOLVED

Implemented the finding's class-closing fix rather than the narrower fail-loudly guard: pinEventFieldTotality() no longer re-implements the constraint grammar. It still discovers the conditional types from the document (failing closed on an unlisted conditional) and the top-level optional property set, but now builds each type's minimal valid event and, for every optional field, issues a validator probe via the existing check(...): companions expect valid, every other field expects invalid. Prohibition spelling no longer matters — the validator decides — and the mirror leg comes free from check(), so the hand-rolled mirror loop and camelCase branch are gone. A new optional field without a known valid probe value fails closed (no known valid value for ...). Three hand-written crossing checks (state_changed with text, output with state, artifact with truncated) were removed because the probe loop regenerates byte-identical instances for them; required-ness pins (state_changed without state, output without text, empty output, artifact without artifact_id) are untouched. This is a .java test file, outside the JS/TS test-weakening watch; the regeneration preserves every removed assertion.

Mutation probe: merged output's not.anyOf branches [{required:[runtime_state]}, {required:[artifact_id]}] into [{required:[runtime_state, artifact_id]}] on both surfaces (the exact witness from the finding) — taskEventsKeepOneShapePerType failed on both surfaces with output with runtime_state: expected invalid and output with artifact_id: expected invalid. Restored; suite green.

Verification

  • mvn -o -B -Dmaven.repo.local=/tmp/pr13061-m2/repository test -Dtest=PlannedTaskContractTest (JDK 21.0.12.1, Maven 3.9.11) — 7/7 green on the merged contract before the fix (baseline), and 7/7 green after the fix.
  • Mutation probe A (R2-1): maxItems 100 → 99 on both artifact_refs surfaces — FAIL as expected (artifact_refs at the bound: expected valid, both surfaces); restored and re-green.
  • Mutation probe B (R2-2): {"required": ["runtime_state"]} added to state_changed's then.not.anyOf on both event schemas — FAIL as expected (state_changed with runtime_state: expected valid, both surfaces); restored and re-green.
  • Mutation probe C (R1-2): output's forbid branches merged into one conjunction on both event schemas — FAIL as expected (output with runtime_state / output with artifact_id: expected invalid, both surfaces); restored and re-green.
  • mvn -o -B -Dmaven.repo.local=/tmp/pr13061-m2/repository test (full managed-agent-server module) — 196 tests, 0 failures, BUILD SUCCESS.
  • mvn -o -B -Dmaven.repo.local=/tmp/pr13061-m2/repository checkstyle:check — BUILD SUCCESS.
  • npx vitest run client/components/managed/managed-agent-api.test.ts (packages/web-shell) — 2 passed.
  • COREPACK_HOME=/tmp/corepack-cache npm run build — passed (COREPACK_HOME override needed because corepack cannot write to the sandboxed $HOME/.cache).
  • COREPACK_HOME=/tmp/corepack-cache npm run typecheck — passed (exit 0).
  • COREPACK_HOME=/tmp/corepack-cache npm run lint — passed (exit 0).
中文说明

Autofix 轮次 — PR #12998

本轮有两个提交:一次合并 origin/main 并解决契约版本冲突,一次在 PlannedTaskContractTest.java 中解决全部三条评审发现(净增 46 行、净删 46 行)。

合并冲突解决(--conflict true)

packages/sdk-java/managed-agent-server/src/main/resources/openapi/managed-agent-public-api.openapi.json 在 info 段发生冲突:本 PR 与 main 上的 #12955(G0 公开 Workspace 文件 Turn)都把契约从 1.21.0 提升到 1.22.0,且变更条目不同。解决方式是完整保留 main 的 v1.22 条目,把本 PR 的条目重新编号为 v1.23("version": "1.23.0",描述句为 v1.23 settles the planned task event and cancel semantics: ...)。两份提到本次后续修订契约版本的设计文档同步改为 v1.23(docs/design/2026-09-27-managed-agent-task-contract.md 及其 .zh-CN.md 中文版,各一处)。文件其余部分均为文本级自动合并;合并后的 JSON 可解析、无重复键,并用 npm run generate:managed-agent-api --workspace=@qwen-code/web-shell 重新生成了 WebShell 客户端——无任何 diff,说明自动合并得到的生成文件本就正确,且其中不含版本号字符串。

发现

R2-1 [rc:4138276315] — artifact_refs 上限只钉住了放宽方向 — 已解决

通过阅读测试核实了该主张:task() 把 artifact_refs 初始化为空数组,且只有那两个 reject 用例填充过它,因此把 maxItems 降到 100 以下原本无法被观测。按建议实现修复:在越界 reject 之前新增一个恰好 100 条互不相同引用(artifact-0…artifact-99,满足 uniqueItems 与条目 1–128 长度限制)的边界 accept,与该文件对 limit 上限的双向钉法(100 合法 / 101 非法)保持一致。

变异探针:把 PublicTask.artifact_refs 与 WebShellTask.artifactRefs 两侧的 maxItems 从 100 改为 99 并运行测试套件——taskViewKeepsItsStateInvariants 在两侧接口上失败,报 artifact_refs at the bound: expected valid(此前不可见的变异)。随后还原,套件恢复全绿。

R2-2 [rc:4138276348] — 缺少「state_changed 允许其 runtime_state 伴随字段」的正向断言 — 已解决

通过下文的探针改写解决:生成的探针集合包含 state_changed with runtime_state(runtime_state: "ready",是 TaskRuntimeState 的合法枚举值),在两侧接口上期望合法,这正是所要求的正向断言。

变异探针:向 PublicTaskEvent 的 state_changed 条件的 then.not.anyOf 加入 {"required": ["runtime_state"]}(WebShellTaskEvent 侧加入 {"required": ["runtimeState"]})——taskEventsKeepOneShapePerType 在两侧接口上失败,报 state_changed with runtime_state: expected valid。随后还原,套件恢复全绿。

R1-2 [rc:4138276359] — 穷尽性辅助方法摊平 not.anyOf[].required,合取分支会静默放宽 — 已解决

采用该发现给出的「闭合整类问题」修复,而非更窄的「大声失败」守卫:pinEventFieldTotality() 不再重新实现约束语法。它仍然从契约文档中发现条件类型(遇到未列出的条件仍失败关闭)和顶层可选字段集合,但现在改为构造每个类型的最小合法事件,并对每个可选字段经由既有的 check(...) 发起校验器探针:伴随字段期望合法,其余字段期望非法。禁止关系如何书写不再重要——由校验器判定——且镜像侧经由 check() 免费获得,手写的镜像循环和 camelCase 分支随之删除。新增的可选字段若没有已知的合法探针值,会失败关闭(no known valid value for ...)。三个手写的交叉用例(state_changed with text、output with state、artifact with truncated)被移除,因为探针循环会为它们生成字节级相同的实例;必填性钉点(state_changed without state、output without text、empty output、artifact without artifact_id)保持不变。该文件是 .java 测试文件,不在 JS/TS 测试削弱监控范围内;再生成完整保留了每一条被移除的断言。

变异探针:把 output 的 not.anyOf 分支 [{required:[runtime_state]}, {required:[artifact_id]}] 合并为 [{required:[runtime_state, artifact_id]}](两侧接口,即该发现中的原始证据)——taskEventsKeepOneShapePerType 在两侧接口上失败,报 output with runtime_state: expected invalid 与 output with artifact_id: expected invalid。随后还原,套件恢复全绿。

验证

  • mvn -o -B -Dmaven.repo.local=/tmp/pr13061-m2/repository test -Dtest=PlannedTaskContractTest(JDK 21.0.12.1,Maven 3.9.11)——修复前在合并后的契约上 7/7 全绿(基线),修复后 7/7 全绿。
  • 变异探针 A(R2-1):两侧 artifact_refs 的 maxItems 100 → 99 —— 按预期失败(artifact_refs at the bound: expected valid,两侧);随后还原并恢复全绿。
  • 变异探针 B(R2-2):向两侧事件 schema 的 state_changed 的 then.not.anyOf 加入 {"required": ["runtime_state"]} —— 按预期失败(state_changed with runtime_state: expected valid,两侧);随后还原并恢复全绿。
  • 变异探针 C(R1-2):把两侧事件 schema 中 output 的禁止分支合并为一个合取 —— 按预期失败(output with runtime_state / output with artifact_id: expected invalid,两侧);随后还原并恢复全绿。
  • mvn -o -B -Dmaven.repo.local=/tmp/pr13061-m2/repository test(managed-agent-server 全模块)—— 196 个测试,0 失败,BUILD SUCCESS。
  • mvn -o -B -Dmaven.repo.local=/tmp/pr13061-m2/repository checkstyle:check —— BUILD SUCCESS。
  • npx vitest run client/components/managed/managed-agent-api.test.ts(packages/web-shell)—— 2 个通过。
  • COREPACK_HOME=/tmp/corepack-cache npm run build —— 通过(需要 COREPACK_HOME 覆盖,因为 corepack 无法写入沙箱内 $HOME/.cache)。
  • COREPACK_HOME=/tmp/corepack-cache npm run typecheck —— 通过(退出码 0)。
  • COREPACK_HOME=/tmp/corepack-cache npm run lint —— 通过(退出码 0)。

Base-conflict check · 基分支冲突检查: conflicted with main — resolved in this push. · 与 main 有冲突——已在本次推送中解决。

⚠️ The branch received new commits while this round ran; they were merged into this push, but this round's verification predates that merge — re-check anything that landed mid-run. · 本轮运行期间分支收到了新的提交;本次推送已将其合并,但本轮验证在合并之前完成——请复查运行期间落地的改动。

⚠️ Review-thread resolution skipped — guard: salvage merge; resolved 0 of 3 selected thread(s), 3 left for a later round. · 评审线程关闭被跳过——守卫:salvage merge;选中 3 条,本轮关闭 0 条,其余 3 条留待后续轮次。

Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human. · 有空请复审;第 100 轮后本 bot 停止并将 PR 交给人工。


🧠 Handled by Qwen Code · model/模型 kimi-k3 · CLI 0.24.7

…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.
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Addressed the latest review feedback (round 5/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 5/100 轮)。改动内容与我反驳保留之处如下:

AutoFix round — PR #12998 (commit b1754087cd)

All eight round-3 inline findings are addressed in the code; the maintainer's
verification comment is answered below. Five files changed, all inside the
PR's footprint: the two design docs, the OpenAPI spec, the planned-contract
test, and the regenerated WebShell types (+253/−63).

Findings

R3-1 — 409 session_operation_active missing from cancel admission (rc:4141536997).
Resolved, taking the finding's first option: cancel admission IS subject to
the Session-scoped open-operation guard, because cancel operations share
managed_agent_operation with the lifecycle commands and the implemented
store admits one open operation per Session. A6 step 5 now states this, §4.7
gains the session_operation_active row, and A7's "different keys" promise is
scoped to keys that each pass admission including step 5 — in both languages.
Both cancel route descriptions (public and WebShell) now name the code, so a
client mapping the documented 409s has a branch for it. No Java change: the
guard already exists and is what the cancel slice will reuse.

R3-2 — A7 settled only status (rc:4141537004). Resolved. A7 now fixes
the sibling required fields per outcome (both languages): pending/running
carry java_durable and pending/leased with no receipt; completed
carries harness_confirmed, confirmed and the task authority's
receipt_id; failed and recovery_blocked carry java_durable and
delivery_state: blocked — delivery stopped, never claimable again — with
failed adding failure_code. This defines the previously unproduced
blocked value rather than retiring it (the alternative the finding offered),
because the terminal non-completed outcomes need a non-claimable delivery
state. The pairing is encoded as two task_cancel conditionals inside the
same planned allOf member on both surfaces, so the generator still filters
it out. PlannedTaskContractTest.taskCancelOutcomesDescribeTheCommand now
pins one instance per settled outcome with its required pair, plus negatives
(still pending, still java_durable, still unconfirmed) — the instances
the finding named now go red without the conditionals (measured, see
Verification).

R3-3 — §6.1 lacks the capabilities precondition (rc:4141537012).
Resolved. Both §6.1 lists gain a bullet: before routes become partial, H3
must demonstrate that a Session with capabilities.tasks: true and
capabilities.artifacts: false admits no output-producing task, including one
whose output goes only to Artifacts. This also implements the maintainer's
optional N3 note.

R3-4 — rotation prohibition not propagated (rc:4141537018). Resolved.
Both artifact_refs/artifactRefs field descriptions now forbid rotation
until older Artifacts can be enumerated and attributed to the task (an evicted
Artifact stays readable by id but is not discoverable from the task); design
§4.2 points at the bound instead of assuring safe rotation; §7's "should add"
became "must land", matching §4.7. The WebShell types were regenerated, so the
stale assurance no longer reaches SDK authors.

R3-5 — task-events 409 resolves to the replaced rule (rc:4141537027).
Resolved with the finding's first option: a new TaskCursorExpired response
component ("strictly below the task's durable retention floor, which survives
an empty retained set; equality is valid; replay_floor_sequence and
snapshot_through_sequence stay absent because task cursors are opaque") now
backs the 409 of both planned task-events routes. The shared CursorExpired
is untouched, so the implemented Session events route keeps its true
retained-floor description. The §4.7 table rows in both languages already
stated this rule, so no doc edit was needed there.

R3-6 — failure_code has no durable home (rc:4141537036). Resolved as
tracked follow-up work, which is what the finding asked for: §4.1's
cancel-slice sentence now requires the migration (task_id and
failure_code columns on managed_agent_operation, the reason written in the
same transaction as the FAILED transition so it survives re-lease and
restart), and §7 gains a "Cancel operation storage" bullet beside the Artifact
attribution one. Both languages.

R3-7 — failure_code rule has no scoping test (rc:4141537046). Resolved
with the finding's exact instance: another command may fail without a code
(a failed submit_input with no code stays valid). The hoist mutant the
finding described reddens it on all four schemas (measured, see Verification).

R2-2 (fix-induced) — mirror blind spot in pinEventFieldTotality
(rc:4141537053).
Resolved: the helper now asserts the WebShellTaskEvent
property set equals the camelCased PublicTaskEvent set and enumerates the
mirror's conditionals for unlisted types. The mirrorOnly mutant and a
mirror-only conditional both red-den it (measured). The javadoc's guarantee
now covers both surfaces.

Maintainer comment (ic:5902574154)

  • F1 (stale v1.22.0 references): both design-doc lines already read
    v1.23.0 at this head (the renumber commit fixed them; verified by grep).
    The PR description still says v1.22.0 — I have no GitHub credentials, so the
    body edit is left for the workflow or a maintainer.
  • F2 (three test-strength gaps): R2-1 (100-entry bound accept/reject
    instances) and R1-2 (validator-probe totality, no name counting) are present
    at this head and were verified through the surrogate matrix. R2-2's
    replacement gap is fixed above.
  • N1 (merge order with feat(managed-agent): Implement private Hosted MCP runtime (H1) #12946): a maintainer decision — whichever PR lands
    second must bump to 1.24.0. Not settled here.
  • N2: noted, no action. N3: implemented via the R3-3 §6.1 bullet.

Verification

Java/Maven is not installed on this runner, so the Maven suites could not be
run here; CI's Java legs re-run them. As the surrogate, the full
PlannedTaskContractTest instance matrix (173 checks at v1, 193 at v2,
including the camelCase WebShell mirrors, the union schemas, and the
totality/parity logic) was ported to Ajv's draft 2020-12 dialect — the same
draft the networknt validator implements — and calibrated against the
pristine spec before any edit.

  • Surrogate calibration, current matrix vs pristine spec: 173 checks, 0 failures
    (matches the green Java suite at this head).
  • Current matrix vs edited spec: exactly 12 failures — the three
    pre-existing instances the new pairing conditionals correctly reject
    (recovery_blocked/completed/failed with delivery_state: pending),
    on all four schemas. The conditionals have teeth.
  • Updated matrix vs edited spec: 193 checks, 0 failures.
  • Updated matrix vs pristine (pre-round) spec: exactly 16 failures, all of
    them this round's new negative instances — the changed tests fail pre-round
    and pass post-round.
  • Mutation probes against the edited spec (each caught by the intended test):
    • hoist-failure-code (R3-7's mutant, both surfaces) → 4 failures, all
      another command may fail without a code.
    • mirrorOnly property on WebShellTaskEvent (R2-2's mutant B2) → parity
      assertion fails.
    • pairing conditionals dropped → all 16 pairing negatives fail.
    • mirror-only conditional for an unlisted type → WebShellTaskEvent has an unlisted conditional failure.
    • pairing dropped from the mirror only → 8 mirror-side failures.
  • npm run generate:managed-agent-api --workspace=@qwen-code/web-shell —
    regenerated; diff is exactly the two description lines (the planned
    conditionals and the new component stay filtered out).
  • npx vitest run client/components/managed/managed-agent-api.test.ts
    (packages/web-shell) — 2/2 passed.
  • npm run build — passed (with COREPACK_HOME=/tmp/corepack-cache; the
    sandbox's default corepack cache path is not writable, which otherwise
    aborts the pnpm shim before any compilation).
  • npm run typecheck — passed.
  • npm run lint — passed.
  • node scripts/lint.js --prettier (the CI format gate) — passed.
  • Local $ref integrity sweep over the full spec — all resolve;
    CursorExpired keeps its one implemented referrer, TaskCursorExpired has
    exactly the two planned task-events referrers.

Not run here (no JDK on this runner): mvn -o test / checkstyle:check for
managed-agent-server. The Java edits are instance data plus the finding's
own helper code following the file's existing patterns; the surrogate above
reproduces their verdicts, and CI's Java legs remain the final gate.

中文说明

AutoFix 本轮结果 — PR #12998(提交 b1754087cd)

第 3 轮的 8 条行内发现已全部在代码中处理;维护者的验证评论在下方逐条回应。共改动 5 个文件,全部在 PR 既有范围内:两份设计文档、OpenAPI 规范、planned 契约测试以及重新生成的 WebShell 类型(+253/−63)。

发现处理

R3-1 — 取消准入缺少 409 session_operation_active(rc:4141536997)。 已解决,采用发现给出的第一种方案:取消准入受 Session 级未完成 operation 守卫约束,因为取消 operation 与生命周期命令共用 managed_agent_operation 表,而已实现的存储层每个 Session 只允许一个未完成 operation。A6 第 5 步现在写明这一点,§4.7 增加 session_operation_active 行,A7 的「不同键」承诺收窄为「分别通过准入(含第 5 步)的键」——两种语言均已同步。两条取消路由(公共与 WebShell)的描述现在都点名该错误码,映射了文档中 409 的客户端有了对应分支。未改 Java:守卫已存在,取消切片将直接复用它。

R3-2 — A7 只确定了 status(rc:4141537004)。 已解决。A7 现在按结果固定同级必填字段(两种语言):pending/running 携带 java_durable 与 pending/leased,无回执;completed 携带 harness_confirmed、confirmed 和任务权威方签发的 receipt_id;failed 与 recovery_blocked 携带 java_durable 和 delivery_state: blocked —— 投递已停止、永不再被认领 —— failed 另带 failure_code。这选择了发现提供的「定义 blocked」方案(而非移除它),因为终态未完成的结果需要一个不可认领的投递状态。该配对已编码为两侧接口同一个 planned allOf 成员内的两条 task_cancel 条件,生成器仍会将其过滤。PlannedTaskContractTest.taskCancelOutcomesDescribeTheCommand 现在为每个已确定的结果各钉一个携带所要求配对的实例,并增加反向用例(still pending、still java_durable、still unconfirmed)——发现点名的实例在去掉条件后会变红(已实测,见「验证」)。

R3-3 — §6.1 缺少能力前提(rc:4141537012)。 已解决。两处 §6.1 清单各增加一条:路由标记为 partial 之前,H3 必须演示 capabilities.tasks: true 且 capabilities.artifacts: false 的 Session 不能接纳产生输出的任务,包括输出只写入 Artifact 的任务。这同时实现了维护者的可选项 N3。

R3-4 — 轮转禁令未传播(rc:4141537018)。 已解决。两处 artifact_refs/artifactRefs 字段描述现在禁止在能够枚举并归属更早的 Artifact 之前轮转(被逐出的 Artifact 仍可按 id 读取,但无法再按任务发现);设计文档 §4.2 改为指向该上限而不再保证安全轮转;§7 的「应该补齐」改为「必须落地」,与 §4.7 一致。WebShell 类型已重新生成,过时表述不再到达 SDK 作者。

R3-5 — 任务事件 409 解析到被替换的旧规则(rc:4141537027)。 已解决,采用发现给出的第一种方案:新增 TaskCursorExpired 响应组件(「严格低于任务的持久保留下限,该下限在保留集为空时依然存在;相等合法;replay_floor_sequence 与 snapshot_through_sequence 保持缺省,因为任务游标不透明」),作为两条 planned 任务事件路由的 409。共享的 CursorExpired 未改动,已实现的 Session 事件路由保留其真实的保留 floor 描述。两种语言的 §4.7 表格行本来就写着新规则,无需改动。

R3-6 — failure_code 没有持久化归属(rc:4141537036)。 已按发现的要求记录为后续工作:§4.1 的取消切片句子现在要求该迁移(managed_agent_operation 增加 task_id 与 failure_code 列,失败原因与 FAILED 状态转换在同一事务写入,从而在重新租约与重启后存活),§7 在 Artifact 归属条目旁新增「取消 operation 存储」条目。两种语言均已同步。

R3-7 — failure_code 规则缺少作用域测试(rc:4141537046)。 已按发现给出的实例解决:another command may fail without a code(不带失败码的 failed submit_input 保持合法)。发现描述的「提升」变异体在全部四个 schema 上让它变红(已实测,见「验证」)。

R2-2(修复引入)— pinEventFieldTotality 的镜像盲区(rc:4141537053)。 已解决:辅助方法现在断言 WebShellTaskEvent 的属性集合与 PublicTaskEvent 的 camelCase 集合一致,并枚举镜像的条件是否出现未登记的类型。mirrorOnly 变异体和仅镜像侧条件都会让它变红(已实测)。javadoc 的保证现在覆盖两个接口面。

维护者评论(ic:5902574154)

  • F1(v1.22.0 残留): 两处设计文档在当前 head 都已是 v1.23.0(改号提交已修复,grep 验证过)。PR 描述仍写 v1.22.0 —— 我没有 GitHub 凭据,正文修改留给工作流或维护者。
  • F2(三处测试强度缺口): R2-1(100 条上限的接受/拒绝实例)和 R1-2(改为校验器探针而非按名计数)在当前 head 已存在,并通过替代矩阵验证。R2-2 替代实现的新缺口已在上面修复。
  • N1(与 feat(managed-agent): Implement private Hosted MCP runtime (H1) #12946 的合并顺序): 这是维护者的决定 —— 后合入的一方需手动升到 1.24.0。此处不作处理。
  • N2: 已知悉,无需动作。N3: 已通过 R3-3 的 §6.1 条目实现。

验证

本 runner 未安装 Java/Maven,无法在此运行 Maven 套件;CI 的 Java 各腿会重新运行。作为替代,把 PlannedTaskContractTest 的完整实例矩阵(v1 共 173 项、v2 共 193 项校验,包括 camelCase 的 WebShell 镜像、union schema 以及 totality/parity 逻辑)移植到 Ajv 的 2020-12 draft(与 networknt 校验器实现的 draft 相同),并在任何修改之前用未改动的规范完成了校准。

  • 替代校验器校准(现有矩阵 × 未改动规范):173 项校验,0 失败(与当前 head 的全绿 Java 套件一致)。
  • 现有矩阵 × 修改后规范:恰好 12 处失败 —— 即新配对条件正确拒绝的三个既有实例(delivery_state: pending 的 recovery_blocked/completed/failed),覆盖全部四个 schema。新条件确有牙齿。
  • 更新后矩阵 × 修改后规范:193 项校验,0 失败。
  • 更新后矩阵 × 未改动(本轮前)规范:恰好 16 处失败,全部是本轮新增的反向实例 —— 改动的测试在本轮前失败、本轮后通过。
  • 针对修改后规范的变异探测(每个都被预期测试捕获):
    • hoist-failure-code(R3-7 的变异体,两个接口面)→ 4 处失败,全部为 another command may fail without a code。
    • WebShellTaskEvent 增加 mirrorOnly 属性(R2-2 的变异体 B2)→ 一致性断言失败。
    • 删除配对条件 → 16 个配对反向用例全部失败。
    • 仅镜像侧新增未登记类型的条件 → WebShellTaskEvent has an unlisted conditional 失败。
    • 仅从镜像删除配对条件 → 镜像侧 8 处失败。
  • npm run generate:managed-agent-api --workspace=@qwen-code/web-shell —— 已重新生成;diff 恰好是两行描述(planned 条件与新组件仍被过滤)。
  • npx vitest run client/components/managed/managed-agent-api.test.ts(packages/web-shell)—— 2/2 通过。
  • npm run build —— 通过(使用 COREPACK_HOME=/tmp/corepack-cache;沙箱默认的 corepack 缓存路径不可写,否则 pnpm shim 会在编译前中止)。
  • npm run typecheck —— 通过。
  • npm run lint —— 通过。
  • node scripts/lint.js --prettier(CI 格式门禁)—— 通过。
  • 全规范本地 $ref 完整性扫描 —— 全部可解析;CursorExpired 保留其唯一的已实现引用方,TaskCursorExpired 恰好有两个 planned 任务事件引用方。

此处未运行(本 runner 无 JDK):managed-agent-server 的 mvn -o test / checkstyle:check。Java 侧的改动只是实例数据加上发现自带的辅助代码,遵循该文件的既有写法;上述替代验证复现了其判定结果,CI 的 Java 各腿仍是最终门禁。

Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。

🧵 Resolved all 8 selected review thread(s). · 已关闭全部选中的 8 条评审线程。

Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human. · 有空请复审;第 100 轮后本 bot 停止并将 PR 交给人工。


🧠 Handled by Qwen Code · model/模型 kimi-k3 · CLI 0.24.7

@wenshao

wenshao commented Sep 30, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /triage

@wenshao wenshao removed the autofix/takeover Summon the autofix loop to manage this PR (remove to release; needs triage+) label Sep 30, 2026
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

👋 Takeover released: the autofix loop will no longer engage this PR (an in-flight round, if any, completes its bounded work). Re-apply autofix/takeover (or comment @qwen-code /takeover) to re-engage.

中文说明

👋 已释放:autofix 循环不再介入此 PR(在飞的一轮如有,将完成其有界工作)。重新打上 autofix/takeover 标签(或评论 @qwen-code /takeover)即可再次接管。

wenshao added a commit to wenshao/qwen-code that referenced this pull request Sep 30, 2026
@wenshao

wenshao commented Sep 30, 2026

Copy link
Copy Markdown
Collaborator Author

Maintainer real-environment verification (Linux) — head b1754087cd

Verdict: all green. Independently re-verified on a Linux box against the current head (main at e8fc540be7), covering every item of the reviewer test plan that is checkable before the routes are mapped. Evidence (logs + harness + screenshots) is preserved on the assets branch: wenshao/qwen-code@assets-pr12998 → pr12998/linux-b1754087/.

Environment: 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. Build & static gates

  • corepack pnpm install --frozen-lockfile passed; the prepare lifecycle completed the full build and bundle.
  • Root typecheck ✅, focused eslint ✅, prettier --check on all changed files ✅.

2. Java contract suites — 29/29 (PR body says 15; head has grown)

mvn test -Dtest='*Contract*' in packages/sdk-java/managed-agent-server:

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-api re-run: byte-identical to the committed client (no drift).
  • Planned surface confirmed excluded from the published client: 0 x-qwen-implementation-status markers, no WebShellTaskEventPage, no WebShellTaskCancelRequest; the only published task paths remain tasks/get and tasks/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

build, Java suites, WebShell tests, static gates

task_cancel before/after on both operation unions

independent validation: 90+90 checks, 10/10 mutations

published client parity & planned exclusion

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 qqqys left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-status is "planned" for both listSessionTaskEvents and cancelSessionTask — the two routes whose semantics this PR settles. listSessionTasks and getSessionTask remain "partial".
  • Both of the new conditional blocks that make failure_code / failureCode mandatory for a failed task 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_refs is 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 PublicCommandOperation and PublicOperation. A recovery_blocked cancel is accepted with the settled delivery state and rejected while still pending; a completed cancel is accepted with admission_stage: harness_confirmed, delivery_state: confirmed and a receipt, and rejected when the receipt is missing, when it is still java_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
wenshao enabled auto-merge September 30, 2026 09:07
@wenshao
wenshao added this pull request to the merge queue Sep 30, 2026
Merged via the queue into main with commit e263741 Sep 30, 2026
83 of 85 checks passed

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Downgraded from Approve to Comment: self-PR; PR head advanced during review: reviewed a98c24d, PR is now at b175408 (+1 unreviewed commit(s) touching 5 file(s)). Partially reviewed — gaps disclosed.

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.

⚠️ 2 finding(s) still carried the — [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.)

中文说明

⚠️ 已从批准降级为评论:self-PR; PR head advanced during review: reviewed a98c24d, PR is now at b175408 (+1 unreviewed commit(s) touching 5 file(s))。 仅完成部分审查,审查缺口已披露。

未探索到全部深度(达到工具调用预算):"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 承载的方法。

⚠️ 循环结束时仍有 2 条发现带着 — [unverified] 标记——验证者从未对它们作出裁决,它们不算已确认。

机制健康:本轮未能干净收尾,因而扣留了增量锚点,而它恢复到的那一轮也没有留下本轮可用的锚点——要么完全没有、要么没有认证者、要么由本轮运行身份之外的身份认证、要么被本轮的获取拒绝或解析为头提交——因此下一次评审将重读整个 diff,除非恢复流程把本轮能使用的更早自有锚点嫁接到本轮留下的完整工作清单上;并会一直如此,直到某一轮的标记重新带上锚点,或落地的嫁接能被运行该轮的评审使用。(仅陈述,不据此行动——这不改变本轮发布的任何内容。)

— glm-5.3-flash via Qwen Code /review (v0.24.6)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants