Skip to content

test(managed-agent): commit authority-valid deltas in the restore byte-budget test - #13551

Merged
wenshao merged 1 commit into
mainfrom
fix/session-store-budget-test-deltas
Oct 7, 2026
Merged

wenshao merged 1 commit into
mainfrom
fix/session-store-budget-test-deltas

Conversation

@wenshao

@wenshao wenshao commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

Repairs the restore byte-budget test in the managed session store suite so it commits journal lines the hardened commit gate accepts, and stops that test class from dumping megabyte-sized request bodies into the CI log when it fails.

Each dense transaction in holdsRestorePagesInsideThePerPageByteBudget is now built from 210 well-formed message.delta events plus the commit marker, instead of a placeholder event_v1 line and a 1 MB padded commit_v1 line. message.delta.text is the only raw-text payload field the authority's reader takes, and the reader caps it at 4096 bytes, so the ~1 MB per-transaction density now comes from the event count (210, under MAX_TRANSACTION_EVENTS = 256) rather than one padded line. Sequence, event id and session key are parameterised per transaction, so every event matches its declared place in the range and the test's random budget-<uuid> session. The test now also asserts the arithmetic its 9 + 3 page split relies on: revisions 1..9 fit the 8 MiB page budget and a tenth does not. The budget still counts record bytes, so the test proves the same property as before.

The delta composer lives in the shared TurnEventLines fixture, and HostedCommittedEventLineReplayIT replays the same delta shape through the real TypeScript reader, so a drift in the fixture fails in the hosted lane. The test class switches @AutoConfigureMockMvc to print = MockMvcPrint.NONE, which is already used by ManagedAgentApiContractTest and ManagedEventReplayTest.

Why it's needed

main has been red on SDK Java since ac81c07dc8 (#13355). #13348 (fd4af700c4) added this test with placeholder record lines. #13355 then made the commit gate refuse any non-event line inside a transaction's event range, so every commit after genesis answered 409 managed_session_extension_record_rejected (Record line 1 is not an event line, yet it sits among the transaction's events.). #13355's branch never contained #13348's test, so each PR passed on its own and only the squash-merged combination broke. Every open PR whose SDK Java run includes managed-agent-server inherits the failure (for example #13174, run 37522282573).

The failure also showed up as timeouts instead of a test error, which is why the lanes report "cancelled" rather than "failed". On failure, Spring Boot's MockMvc printed the test's ~1 MB request body, base64-encoded, as one 1,334,121-character log line. The Actions log pipeline stalled on that line for about 45 minutes: streaming stopped right before it, the line was stamped about 23 minutes later, and the rest of the output about 23 minutes after that. As a result, the Hosted Verify Hosted Java, Spring and MySQL processes step (25 min ceiling) and the O4 step (12 min) each ran about 49 minutes before being killed, even though Maven itself finished in 3:43, and Runtime Broker and Managed Agent MariaDB / Java 21 hit its job timeout with its log frozen mid-run. This holds for main runs 37496091771 (ac81c07dc8) and 37499338694 (b585508733) as well as #13174's run, and it also explains the cancelled lanes noted in the #13542 triage. Turning off print-on-failure keeps a future failure in this class a fast, readable test error.

This also settles the three corrections and the open question in the #13542 triage. The pad cannot stay on the commit line (64 KiB marker cap). The event envelope is closed, so the bulk has to sit in a field the per-kind schema allows to be long. Sequences are per transaction. No single authority-valid event can hold ~0.9 MiB, so the density comes from many events instead.

Reviewer Test Plan

How to verify

  1. Build the dependencies once: mvn -q -f packages/sdk-java/qwencode/pom.xml -DskipTests -Dgpg.skip=true -Dmaven.javadoc.skip=true install and mvn -q -f packages/sdk-java/runtime-broker/pom.xml -DskipTests -Dspotbugs.skip=true install.
  2. Run mvn -B -f packages/sdk-java/managed-agent-server/pom.xml -Dtest=ManagedSessionStoreIntegrationTest -Dspotbugs.skip=true test. On main it errors at revision 2 with the 409 above. On this branch it passes.
  3. Optionally, with the repository's node_modules installed, run -Dtest=HostedCommittedEventLineReplayIT -Dnode.executable="$(command -v node)" to replay the new delta shape through the TypeScript reader.

Evidence (Before & After)

Before (main at 8491346647, this branch's test sources reverted): ManagedSessionStoreIntegrationTest has Tests run: 5, Failures: 0, Errors: 1, commit at revision 2 -> 409 … Record line 1 is not an event line, and the longest line in the Maven output is 1,334,092 characters.

After (this branch on 8491346647):

  • managed-agent-server full surefire suite: Tests run: 1039, Failures: 0, Errors: 0, Skipped: 2, You have 0 Checkstyle violations, BUILD SUCCESS, with no output line over 20,000 characters.
  • HostedCommittedEventLineReplayIT passes through the real reader. Raising the delta text to 4097 bytes makes it fail with event 4: payload.text exceeds 4096 UTF-8 bytes., so the oracle really runs.
  • Mutation: dropping the page byte budget in ManagedSessionStore.transactions (keeping only the count limit) fails the test with JSON path "$.transactions.length()" expected:<9> but was:<10>, and the longest failure-output line is 372 characters.

Tested on

OS Status
🍏 macOS ⚠️ not tested locally; covered by CI
🪟 Windows ⚠️ not tested locally; covered by CI
🐧 Linux ✅

Environment (optional)

Linux x86_64, OpenJDK 25.0.2, Maven, H2 in-memory (surefire). The MySQL failsafe ITs of the Hosted and MariaDB lanes were not run locally; they have not completed on main since the break, so this PR's CI run is the first to reach them.

Risk & Scope

  • Main risk or tradeoff: MockMvcPrint.NONE drops Spring's request/response dump for every test in this class on failure. The assertions' own messages still name the mismatched status or JSON path, and the byte-budget commits keep their own truncated (240-character) error body.
  • Not validated / out of scope: the failsafe ITs behind the surefire lane (Hosted MySQL 8.4, O4 and MariaDB) are left to CI. The triage's secondary question, whether a cancelled managed-agent-server lane on a push to main should be allowed to stand, is a workflow change and not part of this PR.
  • Breaking changes / migration notes: none. This is test-only; no production code or validator changes.

Linked Issues

Fixes #13542

Related #13348, #13355, #13174

中文说明

What this PR does

修复 managed session store 测试套件里的 restore 字节预算测试,让它提交的日志行能通过加固后的提交闸门;同时让这个测试类在失败时不再把 MB 级的请求体打印进 CI 日志。

holdsRestorePagesInsideThePerPageByteBudget 的每个稠密事务改为由 210 条合法的 message.delta 事件加 commit marker 组成,不再使用占位的 event_v1 行加一条填充了 1 MB 的 commit_v1 行。message.delta.text 是 authority reader 唯一接受的原始文本 payload 字段,且 reader 把它限制在 4096 字节,所以每个事务约 1 MB 的密度改由事件数量提供(210 条,低于 MAX_TRANSACTION_EVENTS = 256),而不是靠单行填充。sequence、event id 和 session key 都按事务参数化,使每个事件与它在区间中声明的位置以及测试随机生成的 budget-<uuid> session 一致。测试还新增断言,确认 9 + 3 分页所依赖的算术前提成立:revision 1..9 能放进 8 MiB 的页预算,再加第 10 个就超出。预算统计的仍是 record 字节,测试证明的性质与之前相同。

delta 构造方法放在共享的 TurnEventLines fixture 中,HostedCommittedEventLineReplayIT 会用真实的 TypeScript reader 回放同一种 delta 形状,fixture 一旦漂移就会在 hosted lane 失败。该测试类把 @AutoConfigureMockMvc 改为 print = MockMvcPrint.NONE,ManagedAgentApiContractTest 和 ManagedEventReplayTest 已有同样用法。

Why it's needed

自 ac81c07dc8(#13355)起,main 的 SDK Java 一直是红的。#13348(fd4af700c4)新增这个测试时使用的是占位 record 行;随后 #13355 让提交闸门拒绝事务事件区间内任何非 event 行,于是 genesis 之后的每次提交都返回 409 managed_session_extension_record_rejected(Record line 1 is not an event line, yet it sits among the transaction's events.)。#13355 的分支上从来没有 #13348 的这个测试,所以两个 PR 各自都能通过,只有 squash 合并后的组合才会坏。所有 SDK Java run 包含 managed-agent-server 的开放 PR 都会继承这个失败(例如 #13174,run 37522282573)。

这个失败还表现成了超时而不是测试错误,这也是这些 lane 显示 "cancelled" 而不是 "failed" 的原因。失败时,Spring Boot 的 MockMvc 把测试约 1 MB 的请求体(base64 编码后)打印成一行 1,334,121 字符的日志。Actions 日志管线在这一行上卡了约 45 分钟:日志流恰好在这一行之前停止,这一行约 23 分钟后才被打上时间戳,其余输出又过了约 23 分钟才出现。结果,Maven 本身 3:43 就跑完了,Hosted 的 Verify Hosted Java, Spring and MySQL processes 步骤(上限 25 分钟)和 O4 步骤(上限 12 分钟)却各自跑了约 49 分钟才被杀掉;Runtime Broker and Managed Agent MariaDB / Java 21 则在日志冻结于运行中途的状态下撞上 job 超时。main 上的 run 37496091771(ac81c07dc8)、37499338694(b585508733)以及 #13174 的 run 都是如此,这也解释了 #13542 triage 中提到的被取消的 lane。关闭失败时打印后,这个类今后再失败也只会是一个快速、可读的测试错误。

这也回答了 #13542 triage 中的三处修正和那个开放问题:填充不能留在 commit 行上(marker 上限 64 KiB);event envelope 是封闭的,大块数据必须放在 per-kind schema 允许变长的字段里;sequence 按事务计算。没有任何单个 authority-valid 的事件能容纳约 0.9 MiB,所以密度改由多个事件提供。

Reviewer Test Plan

How to verify

  1. 先构建依赖:mvn -q -f packages/sdk-java/qwencode/pom.xml -DskipTests -Dgpg.skip=true -Dmaven.javadoc.skip=true install 和 mvn -q -f packages/sdk-java/runtime-broker/pom.xml -DskipTests -Dspotbugs.skip=true install。
  2. 运行 mvn -B -f packages/sdk-java/managed-agent-server/pom.xml -Dtest=ManagedSessionStoreIntegrationTest -Dspotbugs.skip=true test。在 main 上会在 revision 2 报上面的 409;在本分支上通过。
  3. 可选:在已安装仓库 node_modules 的情况下,用 -Dtest=HostedCommittedEventLineReplayIT -Dnode.executable="$(command -v node)" 让 TypeScript reader 回放新的 delta 形状。

Evidence (Before & After)

Before(main 的 8491346647,本分支的测试源码还原):ManagedSessionStoreIntegrationTest 结果为 Tests run: 5, Failures: 0, Errors: 1,报 commit at revision 2 -> 409 … Record line 1 is not an event line,Maven 输出中最长的一行为 1,334,092 字符。

After(本分支,基于 8491346647):

  • managed-agent-server 全量 surefire:Tests run: 1039, Failures: 0, Errors: 0, Skipped: 2,You have 0 Checkstyle violations,BUILD SUCCESS,没有任何输出行超过 20,000 字符。
  • HostedCommittedEventLineReplayIT 通过真实 reader。把 delta text 提高到 4097 字节会失败并报 event 4: payload.text exceeds 4096 UTF-8 bytes.,说明这个 oracle 确实在运行。
  • 变异测试:去掉 ManagedSessionStore.transactions 中的页字节预算(只保留条数限制)后,测试失败并报 JSON path "$.transactions.length()" expected:<9> but was:<10>,失败输出中最长的一行为 372 字符。

Tested on

OS Status
🍏 macOS ⚠️ 未在本地测试;由 CI 覆盖
🪟 Windows ⚠️ 未在本地测试;由 CI 覆盖
🐧 Linux ✅

Environment (optional)

Linux x86_64,OpenJDK 25.0.2,Maven,H2 内存库(surefire)。Hosted 和 MariaDB lane 的 MySQL failsafe IT 没有在本地运行;自损坏以来它们在 main 上从未跑完,所以本 PR 的 CI 是第一次真正跑到它们。

Risk & Scope

  • Main risk or tradeoff:MockMvcPrint.NONE 会让这个类中所有测试在失败时不再输出 Spring 的请求/响应转储。断言本身的消息仍会指出不匹配的状态码或 JSON path,字节预算测试的提交也保留了自己截断到 240 字符的错误响应体。
  • Not validated / out of scope:surefire lane 之后的 failsafe IT(Hosted MySQL 8.4、O4 和 MariaDB)交由 CI 验证。triage 中的次要问题,即是否应允许 push 到 main 时 managed-agent-server lane 处于取消状态,属于 workflow 改动,不在本 PR 范围内。
  • Breaking changes / migration notes:无。仅改测试,不涉及生产代码或校验器。

Linked Issues

Fixes #13542

Related #13348, #13355, #13174

…e-budget test

How: holdsRestorePagesInsideThePerPageByteBudget now builds each dense
transaction from 210 well-formed message.delta lines (text at the
reader's 4096-byte cap, ~1 MB per transaction) plus the commit marker,
through a shared TurnEventLines composer, and asserts that revisions
1..9 fit the 8 MiB page budget while a tenth does not. The reader replay
IT now also replays that delta shape. The class turns off MockMvc
print-on-failure.

Why: #13348 added the test with placeholder lines ({"subtype":"event_v1"}
plus a padded commit_v1 line), and #13355 (merged later, never run
against it) made the commit gate refuse any non-event line inside the
event range, so every commit after genesis answered 409 and main's SDK
Java lanes went red. The failure was masked as timeouts: Spring Boot
printed the failed test's ~1.3 MB request body as one log line, which
stalled the Actions log pipeline for ~45 min, so the Hosted Verify and
O4 steps blew their ceilings and the MariaDB lane hit its job timeout.

Test: ManagedSessionStoreIntegrationTest and ManagedExtensionRecordStoreTest
green; full managed-agent-server surefire (1077) and checkstyle green;
HostedCommittedEventLineReplayIT green through the real TS reader and
red when the delta text exceeds 4096 bytes; dropping the page byte
budget in ManagedSessionStore fails the test (10 rows instead of 9)
with no log line over 400 chars.
@github-actions github-actions Bot added the review/self-reported The linked issue was opened by the PR author (self-reported) label Oct 6, 2026
@qwen-code-review-bot

qwen-code-review-bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

✅ Qwen Triage finished — view run. See the stage comments in this thread for the result.

✅ Qwen Triage 已完成 —— 查看运行。结果见本线程中的各阶段评论。

@qwen-code-review-bot

qwen-code-review-bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Thanks for the PR — unusually well-evidenced, and it repairs a lane that was red on main at the time it was opened.

This is a re-run of the earlier triage pass on the same commit (8fe4584, unchanged). The gate findings below are the same, but the one thing that was open last time is now closed: the two lanes that actually build managed-agent-server have finished, and both are green. Details in the Stage 2 comment.

Template looks good ✓ — re-checked against .github/pull_request_template.md on this pass: every required heading is present and filled in, including all three Risk & Scope bullets, the Tested-on table and a complete Chinese translation.

Problem: observed, not theoretical, and I re-derived it from the code instead of taking the description's word. ManagedExtensionRecordStore.apply runs require(index >= eventCount, …) on every line inside a transaction's event range. The base fixture put {"subtype":"event_v1"} at index 0 while transactionRequest defaults eventCount to 1, and event_v1 is none of the three subtypes the gate knows (managed_session_event_v1 / managed_session_header_v1 / managed_session_commit_v1) — so 0 >= 1 fails and the commit answers 409 with precisely the message the description and #13542 quote. #13542 is still open and names this exact test. The "cancelled, not failed" shape checks out too, and I measured it myself on main run 37496091771 (ac81c07dc8): Verify Hosted Java, Spring and MySQL processes ran 17:32:44 → 18:22:07 — 49m23s against a 25-minute step ceiling — and the O4 step behind it has a null completed_at, as does the MariaDB lane's Run Managed Agent tests, Checkstyle, and MySQL integration step. That is a stalled log pipeline, not a fast test error.

Direction: clearly aligned. It repairs main and unblocks SDK Java for every open PR inheriting the failure, which is a queue-wide win rather than a local one. Nothing here pulls focus. CHANGELOG has no direct reference, but the area is obviously relevant — the breakage is in CI itself.

Size: not applicable — no core paths touched. Test-only: 0 production lines across 3 files (+74/−22), all under managed-agent-server/src/test. The author is a maintainer and the branch is in-repo, so neither the Stage 0 size gate nor the fork guardrail is in play.

Approach: the scope feels right, and it matches what I would have proposed from the description alone. The binding constraint is that no single authority-valid line can hold ~0.9 MiB — MAX_COMMIT_MARKER_BYTES is 64 KiB and the reader caps message.delta.text at 4096 bytes — so density has to come from event count. 210 deltas under the 256-event cap is the natural answer, and reusing ExtensionRecordJournal.line / PublicationJournalFixture.eventNode / COMMIT_MARKER instead of hand-rolling JSON is the right call: the only line composition it re-implements is the two-line glue in PublicationJournalFixture.event, which is too small to be worth a third helper. Two details I particularly like: the new assertion pins the arithmetic the 9 + 3 split rests on, and because byte_length is bound to validated.recordBytes().length, the locally measured sizes genuinely is the server's page accounting rather than a proxy for it. The MockMvcPrint.NONE change is a second concern riding along, but it is four lines, caused by the same incident, and already precedented in exactly the two classes the description names — fine to keep here rather than split.

Risk: no elevated risk signals — I ran the revert-correlated path check against the changed files and nothing matches.

One flag for the code review, not a gate concern: the five green Java matrix lanes never build managed-agent-server (they run only qwencode and runtime-broker), which is why they stayed green on main while it was broken. They cannot substantiate this fix. The lanes that can are Runtime Broker and Managed Agent MariaDB / Java 21 and Hosted process fault gates / MySQL 8.4 / Java 21 — both now ✅ success on this commit, which is the change since the last pass.

Moving on to code review. 🔍

中文说明

感谢贡献!这个 PR 的证据非常扎实,而且它修复的正是提交时 main 上红着的 lane。

本次是对同一 commit(8fe4584,未变化)的 re-run。gate 层面的结论与上次一致,但上次悬着的那件事已经有了结果:真正会构建 managed-agent-server 的两个 lane 已经跑完,并且都是绿的。细节见 Stage 2 评论。

模板完整 ✓ —— 本轮重新对照 .github/pull_request_template.md 检查过:所有必需标题都存在且已填写,包括 Risk & Scope 的三条、Tested on 表格以及完整的中文翻译。

问题: 是已观测到的问题,不是理论性加固;而且我是从代码重新推导出来的,没有只采信 PR 描述。ManagedExtensionRecordStore.apply 对事务事件区间内的每一行都执行 require(index >= eventCount, …)。原 fixture 在 index 0 放的是 {"subtype":"event_v1"},而 transactionRequest 的 eventCount 默认值是 1,event_v1 又不是闸门认得的三种 subtype 之一(managed_session_event_v1 / managed_session_header_v1 / managed_session_commit_v1)——于是 0 >= 1 失败,提交返回 409,错误信息与描述和 #13542 引用的完全一致。#13542 仍处于 open 状态,标题指的正是这个测试。"显示 cancelled 而不是 failed" 的说法也成立,而且是我自己在 main 的 run 37496091771(ac81c07dc8)上量出来的:Verify Hosted Java, Spring and MySQL processes 从 17:32:44 跑到 18:22:07——49 分 23 秒,而该步骤上限是 25 分钟——其后的 O4 步骤 completed_at 为 null;MariaDB lane 的 Run Managed Agent tests, Checkstyle, and MySQL integration 步骤同样是 null。这是日志管线卡死,不是快速失败的测试错误。

方向: 明确对齐。它修复了 main,并让所有继承该失败的开放 PR 的 SDK Java lane 恢复可用——这是疏通整条队列的收益,而不是局部收益。没有任何偏离重心的内容。CHANGELOG 没有直接对应条目,但这个问题显然相关——坏的正是 CI 本身。

规模: 不适用——没有触及核心路径。纯测试改动:3 个文件、+74/−22,生产代码 0 行,全部位于 managed-agent-server/src/test。作者是维护者、分支在仓库内,因此 Stage 0 的规模闸门和 fork 保护规则都不适用。

方案: 范围合理,和我仅凭描述独立提出的方案一致。真正的约束在于:没有任何单条合法记录行能容纳约 0.9 MiB——MAX_COMMIT_MARKER_BYTES 是 64 KiB,reader 把 message.delta.text 限制在 4096 字节——所以密度只能来自事件数量。在 256 条事件上限内用 210 条 delta 是自然的解法;复用 ExtensionRecordJournal.line / PublicationJournalFixture.eventNode / COMMIT_MARKER 而不是手搓 JSON 也是对的:它唯一"重写"的只是 PublicationJournalFixture.event 里那两行胶水代码,太小了,不值得为此再加一个 helper。有两处我特别认可:新增断言把 9 + 3 分页所依赖的算术前提钉住了;而且由于 byte_length 绑定的就是 validated.recordBytes().length,测试本地统计的 sizes 确实就是服务端的分页计账,而不是它的近似代理。MockMvcPrint.NONE 算是顺带带上的第二个关注点,但只有四行、由同一次事故引起,且描述中点名的那两个测试类确实已有同样用法——留在这个 PR 里比拆出去更合适。

风险: 无升级风险信号——我对改动文件跑了与 revert 相关的路径检查,没有任何命中。

有一点提示给后续代码审查(不属于 gate 层面的顾虑):五个绿色的 Java matrix lane 从不构建 managed-agent-server(只跑 qwencode 和 runtime-broker),这也是为什么 main 坏掉时它们依然是绿的。它们无法证明本次修复。真正能证明的是 Runtime Broker and Managed Agent MariaDB / Java 21 和 Hosted process fault gates / MySQL 8.4 / Java 21——两者现在在这个 commit 上都是 ✅ success,这正是相对上一轮的变化。

进入代码审查 🔍

— Qwen Code · qwen3.8-max-2026-09-02

Reviewed at 8fe458491b25d5cb10953afe6aeb89ac11417a4d · re-run with @qwen-code /triage

@qwen-code-review-bot

qwen-code-review-bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Code review

Re-run on the same commit (8fe4584). I read the gate, the reader, the fixtures and the workflow lanes again rather than carrying the previous pass's conclusions forward, and every load-bearing claim in the description still holds. No blocking findings.

The root cause is real and the fix targets it precisely. ManagedExtensionRecordStore.apply enforces require(index >= eventCount, "Record line N is not an event line, yet it sits among the transaction's events.") on every line inside a transaction's event range. The base fixture put {"subtype":"event_v1"} and a padded commit_v1 there, and transactionRequest defaults eventCount to 1 — event_v1 is none of the three subtypes the gate recognises, so index 0 trips the check and produces exactly the quoted 409. Replacing those lines with real events is the only fix available: MAX_COMMIT_MARKER_BYTES is 64 KiB, so the pad genuinely cannot stay on the commit line.

The non-obvious constraint is handled correctly. requireEvent is called with firstSequence + index and validates the sequence through ManagedExtensionRecords.count(event.get("sequence"), sequence, sequence, …) — min == max, so it is an exact-equality check and each event must carry the sequence of its own position in the range. deltaBytes passes firstSequence + index straight through, and first = (revision - 2) * deltas + 1 keeps the ranges non-overlapping across transactions. The rest of the commit contract lines up: eventCount 210 ≤ MAX_TRANSACTION_EVENTS 256, recordCount 211 = 210 lines + marker ≤ the @Max(MAX_TRANSACTION_EVENTS + 1) of 257, lastSequence - firstSequence + 1 == eventCount, and expectedCommittedSequence = first - 1 chains correctly (0, 210, 420, …). COMMIT_MARKER is {"subtype":"managed_session_commit_v1"}\n, so the marker line at index 210 satisfies index >= eventCount and is measured against the 64 KiB marker cap rather than MAX_EVENT_BYTES.

The delta shape is exactly the reader's schema. In managed-session-records.ts, 'message.delta' maps to messageId:'id', turnId:'id', role:'text', text:'rawText', and the new payload supplies precisely those four fields. rawText occurs exactly once in the whole reader schema — on message.delta.text — so "the only raw-text payload field the authority's reader takes" is accurate, not a paraphrase. The bound is > MANAGED_SESSION_LIMITS.maxTextBytes with maxTextBytes: 4096, so "a".repeat(4096) sits exactly on the passing side. The envelope also clears the closed-shape check: message.delta is not domain.committed, so body == null and the subject is allowed, and eventNode emits eventId of event-<n>, which does not match RESERVED_EVENT_ID and therefore takes the body == null branch instead of the Stage H reservation.

The new arithmetic assertion is sound, not a proxy. This was the thing I most expected to be soft. ManagedSessionStore.transactions pages against MAX_TRANSACTION_BYTES (8 MiB) — the same constant the assertion names — and the journal insert binds byte_length to validated.recordBytes().length. So the locally measured sizes list is the server's page accounting, and asserting sum(revisions 1..9) ≤ budget < sum(revisions 1..10) really does pin the 9 + 3 split the JSONPath assertions depend on. Index alignment is right too: sizes[0] is the genesis, so subList(0, 9) is revisions 1..9 and get(9) is revision 10.

I also settled the mutation claim statically, which the previous pass could not. Last time I wrote that no lane proves the test stays load-bearing — that dropping the byte budget would still fail it with expected:<9> but was:<10> — and that this rested on the author's word. Reading transactions() again, it does not need to: limit is only bounds-checked (limit < 1 || limit > 100), not clamped, so the test's limit=10 reaches the loop as 10. With the byte clause removed, the only remaining break is selectedCount == limit, the journal holds 12 rows, and page 1 returns 10 — failing jsonPath("$.transactions.length()").value(9) with exactly the message the description reports. So the byte budget is the sole reason the page stops at 9, and the test pins it. That claim is now verified here, not merely attributed.

Nothing incidental broke. validateUtf8JsonLines wants a trailing newline and a line count equal to recordCount; both ExtensionRecordJournal.line and COMMIT_MARKER end in \n. Per-line size (~4.7 KB from the fixture shapes) is far under the 1 MiB MAX_EVENT_BYTES. Every identifier the diff newly uses was already imported — ArrayList, List, StandardCharsets and assertThat are all present in the base file, so only MockMvcPrint needed adding. The TurnEventLines doc invariant survives: the oversized-commit derivation does its "sequence":1, / turn-1:accepted string replacement on a turnBytes built locally in a different test method, and the new payload names neither literal (it carries "turnId":"turn-1", not turn-1:accepted). Widening that comment from "The input.accepted turn event" to "The turn events" is correct now that the fixture mints two kinds. message.delta is neither tool.receipt nor activation.changed, so apply() collects no receipts and no activation from 210 of them — the commits stay side-effect-free apart from the journal rows the test is about.

Non-blocking, worth knowing:

  • The density window is narrow, and now I can bound it. For page 1 to hold 8 dense transactions and not 9, per-transaction bytes must sit between (B−G)/9 and (B−G)/8 — roughly 932 KB to 1.048 MB with B = 8 MiB and a tiny genesis G. At 210 lines of ~4.7 KB the transaction lands near the middle, so about 290 bytes of growth per line would flip the upper assertion. Any future field added to ExtensionRecordJournal.line or eventNode shifts it. That is arguably the point — the new assertion makes the drift fail loudly and legibly instead of silently changing the page split — but the failure will look unrelated to whatever envelope edit caused it, so a one-line comment naming the window would save someone an hour.
  • MockMvcPrint.NONE is class-wide, so the other four tests lose Spring's request/response dump on failure as well. You already own this in Risk & Scope, the assertions still name the mismatched status or JSON path, and the byte-budget commits keep their own 240-character error body. Against a 45-minute log stall, that is the right trade.
  • The fixture no longer sits near the per-line cap. The old version packed ~1 MB into one line, incidentally exercising a record close to MAX_EVENT_BYTES; the new lines are ~4.7 KB. That boundary was never this test's subject and is covered elsewhere, so nothing is lost — noting it only so the trade is explicit.
  • Correcting both comments from "twelve dense transactions" to "eleven" is a genuine accuracy fix — the loop is 12 commits, i.e. 1 genesis + 11 dense.

Reuse-wise this is the right shape: the composer went into the shared TurnEventLines fixture and the hosted replay IT now exercises the same shape through the real TypeScript reader, so fixture drift fails in the hosted lane instead of passing silently. No parallel helper, no duplicated logic worth extracting.

I did not run any build, test or PR-derived code — the review is static, per the gate's rules.

CI evidence

Fetched from the API for the reviewed commit; nothing re-run locally. No check is red. The two non-success entries on this SHA are bot orchestration, not PR CI: review-pr (cancelled) and fallback-comment (queued), both from pull_request_target / issue_comment runs.

The point is which checks can prove the claim, and last pass they were still in flight. They are not any more:

  • Runtime Broker and Managed Agent MariaDB / Java 21 is the only lane that runs mvn … clean verify checkstyle:check inside managed-agent-server, so it is the one that executes holdsRestorePagesInsideThePerPageByteBudget. ✅ success, and its Run Managed Agent tests, Checkstyle, and MySQL integration step took 5m13s. Check that every non-Hosted integration test class ran also passed, so nothing was silently skipped.
  • Hosted process fault gates / MySQL 8.4 / Java 21 runs the failsafe ITs including HostedCommittedEventLineReplayIT. ✅ success. scripts/check-failsafe-reports.js hosted derives its expected class list from the source tree (/^Hosted.*IT$/) and asserts each one ran, so the replay IT genuinely executed — meaning CI itself, not just the author, has now put the new message.delta shape through the real TypeScript reader and had it accepted.

Because this PR changes only test code, those two greens against main's cancellations are a real A/B rather than a correlated pass. Here is that A/B, measured from job metadata on both sides:

Step main @ ac81c07dc8 (run 37496091771) this PR @ 8fe4584
Run Managed Agent tests, Checkstyle, and MySQL integration started 17:34:04, never completed (job cancelled) ✅ 5m13s
Verify Hosted Java, Spring and MySQL processes (25 min ceiling) failure after 49m23s (17:32:44 → 18:22:07) ✅ 11m28s
Verify O4 filesystem process and capacity gates (12 min ceiling) started 18:22:07, never completed (job cancelled) ✅ 8m48s

That table is the evidence for the second half of the PR — the MockMvcPrint.NONE change. Two steps that ran roughly 49 minutes against 25- and 12-minute ceilings now finish in 11 and 9 minutes. The stall is gone, and it is gone in the only place it was ever observable.

The five green Java matrix lanes remain non-evidence here: they only run mvn clean test in qwencode and runtime-broker, never managed-agent-server. That is exactly why they stayed green on main while it was broken, and reading them as a pass is the one wrong conclusion this check set invites.

Final CI results for 8fe4584 (auto-updated by the triage finalize job after CI completed):

Check Conclusion
Classify PR ✅ success
Desktop Shell (ubuntu-22.04) ✅ success
Desktop Shell (windows-2022) ✅ success
Flyway migration version uniqueness ✅ success
Hosted process fault gates / MySQL 8.4 / Java 21 ✅ success
Integration Tests (no-AK, No Sandbox) ✅ success
Lint & Static (ubuntu-latest, Node 22.x) ✅ success
macos-latest / Java 21 ✅ success
Real daemon E2E / Java 11 ✅ success
Runtime Broker and Managed Agent MariaDB / Java 21 ✅ success
Test (ubuntu-latest, Node 22.x) ✅ success
ubuntu-latest / Java 11 ✅ success
ubuntu-latest / Java 17 ✅ success
ubuntu-latest / Java 21 ✅ success
web-shell E2E Smoke (ubuntu-latest, Node 22.x) ✅ success
windows-latest / Java 21 ✅ success

One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。

Not verified, and why: the local Maven numbers in the description — Tests run: 1039, Failures: 0, the 4097-byte negative check as an executed run, and the base-red/head-green A/B on the author's machine — are the author's own results, not independently re-run here; the gate does not execute PR code. The same applies to the maintainer verification comment posted on this PR (16/16 assertions, 2 mutation kills): that is the author's harness output and I am treating it as a claim, not as evidence. It is also a narrower gap than last pass, because CI now covers the substance — the MariaDB lane ran the whole managed-agent-server suite green, and the Hosted lane ran the replay IT green behind an anti-skip guard. One detail still speaks for the author's honesty rather than against it: the reported negative check fails as event 4: payload.text exceeds 4096 UTF-8 bytes., and index 4 is exactly where the new delta sits in the replay array (0 activation.changed, 1 tool.intent, 2 checkpoint.committed, 3 input.accepted, 4 message.delta), with the reader's own rawText failure string. That is hard to fabricate without running it.

Sandboxed verification is optional here rather than needed, which is a change from last pass: @qwen-code /verify is the lane that would execute the mutation kill (drop the page byte budget, confirm expected:<9> but was:<10>) instead of leaving it derived from transactions(), and the author has write access so it needs no sponsor. I would not spend a run on it — the derivation above is short enough to check by eye, and the pass/fail and reader-shape halves are already settled by green CI. @qwen-code /tmux does not apply: there is no TUI surface in a Maven module.

This was an unattended CI run, so no real-scenario testing was driven and none is claimed: the evidence in this comment is the PR's own CI results read through the API, plus static reading of the gate, the reader, the fixtures and the workflow definitions. Nothing was built or executed locally.

中文说明

代码审查

本次是对同一 commit(8fe4584)的 re-run。我重新读了提交闸门、reader、各个 fixture 以及 workflow lane,没有直接沿用上一轮的结论——描述里每一条关键论断依然成立。没有阻塞性问题。

根因真实存在,修复也正好打在点上。 ManagedExtensionRecordStore.apply 对事务事件区间内的每一行都执行 require(index >= eventCount, "Record line N is not an event line, yet it sits among the transaction's events.")。原 fixture 在这个区间里放的是 {"subtype":"event_v1"} 和一条填充过的 commit_v1,而 transactionRequest 的 eventCount 默认是 1——event_v1 不是闸门认得的三种 subtype 之一,所以 index 0 就会触发校验,产生描述中引用的那个 409。改用真实事件是唯一可行的修法:MAX_COMMIT_MARKER_BYTES 是 64 KiB,填充确实不可能继续留在 commit 行上。

最容易踩空的约束被正确处理了。 requireEvent 收到的是 firstSequence + index,内部通过 ManagedExtensionRecords.count(event.get("sequence"), sequence, sequence, …) 校验——min == max,是精确相等校验,所以每条事件必须带上它自己在区间中位置的 sequence。deltaBytes 直接把 firstSequence + index 传下去,而 first = (revision - 2) * deltas + 1 保证各事务区间互不重叠。提交契约的其余部分也对得上:eventCount 210 ≤ MAX_TRANSACTION_EVENTS 256,recordCount 211 = 210 行 + marker ≤ @Max(MAX_TRANSACTION_EVENTS + 1) 的 257,lastSequence - firstSequence + 1 == eventCount,expectedCommittedSequence = first - 1 正确串接(0、210、420……)。COMMIT_MARKER 是 {"subtype":"managed_session_commit_v1"}\n,因此 index 210 的 marker 行满足 index >= eventCount,并按 64 KiB 的 marker 上限而非 MAX_EVENT_BYTES 计量。

delta 形状与 reader schema 完全一致。 在 managed-session-records.ts 中,'message.delta' 对应 messageId:'id'、turnId:'id'、role:'text'、text:'rawText',新 payload 恰好只提供这四个字段。整个 reader schema 中 rawText 只出现一次——就在 message.delta.text 上——所以"authority reader 唯一接受的原始文本 payload 字段"是准确表述,不是转述。边界是 > MANAGED_SESSION_LIMITS.maxTextBytes,而 maxTextBytes: 4096,因此 "a".repeat(4096) 正好落在通过的一侧。事件信封也通过了封闭性校验:message.delta 不是 domain.committed,所以 body == null、允许 subject,而 eventNode 生成的 eventId 是 event-<n>,不匹配 RESERVED_EVENT_ID,因此走 body == null 分支,不会撞上 Stage H 保留 id 的检查。

新增的算术断言是可靠的,不是近似代理。 这是我最怀疑会变软的一处。ManagedSessionStore.transactions 分页用的正是 MAX_TRANSACTION_BYTES(8 MiB),与断言引用的常量相同;journal 插入时 byte_length 绑定的就是 validated.recordBytes().length。所以测试本地统计的 sizes 就是服务端的分页计账,断言 sum(revision 1..9) ≤ budget < sum(revision 1..10) 确实钉住了 JSONPath 断言所依赖的 9 + 3 分页。下标对齐也没问题:sizes[0] 是 genesis,因此 subList(0, 9) 是 revision 1..9,get(9) 是 revision 10。

变异那一条我这轮静态地确认了,上一轮没能做到。 上次我写的是:没有任何 lane 能证明这个测试仍然是承载性的——即去掉页字节预算后它仍会以 expected:<9> but was:<10> 失败——这一点当时只能采信作者。重读 transactions() 后发现并不需要采信:limit 只做边界校验(limit < 1 || limit > 100)而不做钳制,所以测试的 limit=10 原样进入循环。去掉字节判断后,唯一剩下的 break 条件是 selectedCount == limit,而 journal 里有 12 行,于是第一页返回 10 行——正好以描述中报告的那条信息让 jsonPath("$.transactions.length()").value(9) 失败。所以页字节预算是分页停在 9 的唯一原因,而这个测试确实钉住了它。这一条现在是本轮核实过的,而不只是转述作者的说法。

没有牵连破坏其他部分。 validateUtf8JsonLines 要求以换行结尾且行数等于 recordCount;ExtensionRecordJournal.line 和 COMMIT_MARKER 都以 \n 结尾。按 fixture 结构估算单行约 4.7 KB,远低于 1 MiB 的 MAX_EVENT_BYTES。diff 新用到的标识符原本都已 import——ArrayList、List、StandardCharsets、assertThat 在基线文件里都在——只需补 MockMvcPrint。TurnEventLines 的文档不变量也保住了:oversized-commit 的推导是在另一个测试方法里对本地构造的 turnBytes 做 "sequence":1, / turn-1:accepted 字符串替换,而新 payload 这两个字面量都不含(它带的是 "turnId":"turn-1",不是 turn-1:accepted)。既然该 fixture 现在会生成两类事件,把注释从 "The input.accepted turn event" 放宽为 "The turn events" 是正确的。message.delta 既不是 tool.receipt 也不是 activation.changed,所以 210 条 delta 不会让 apply() 收集任何 receipt 或 activation——除了测试本来就要的 journal 行以外没有副作用。

非阻塞,但值得知道:

  • 密度窗口很窄,而且现在我能给出边界。 要让第一页装得下 8 个稠密事务、装不下 9 个,单事务字节数必须落在 (B−G)/9 与 (B−G)/8 之间——B = 8 MiB、genesis G 很小时约为 932 KB 到 1.048 MB。按 210 行 × 约 4.7 KB,事务落在中段偏上,因此每行再增长约 290 字节就会翻转上面那条断言。今后给 ExtensionRecordJournal.line 或 eventNode 加任何字段都会移动它。这其实正是新增断言的意义——让漂移以清晰可读的方式失败,而不是悄悄改变分页结果——但失败现场看起来会与真正的信封改动毫无关联,所以加一行注释点明这个窗口能省下别人一小时。
  • MockMvcPrint.NONE 作用于整个类,因此该类另外四个测试失败时也不再输出 Spring 的请求/响应转储。你在 Risk & Scope 里已经承担了这一点,断言本身仍会指出不匹配的状态码或 JSON path,字节预算测试的提交也保留了自己截断到 240 字符的错误响应体。相比日志管线卡 45 分钟,这个取舍是对的。
  • fixture 不再贴近单行上限。 旧版本把约 1 MB 塞进一行,顺带覆盖了一条接近 MAX_EVENT_BYTES 的记录;新版本的行约 4.7 KB。那个边界本来就不是这个测试的对象,别处也有覆盖,所以没有损失——只是把取舍写明。
  • 把两处注释从 "twelve dense transactions" 改为 "eleven" 是真实的准确性修复——循环是 12 次提交,即 1 个 genesis + 11 个稠密事务。

复用方面形状正确:构造方法放进共享的 TurnEventLines fixture,hosted replay IT 现在通过真实的 TypeScript reader 回放同一种形状,所以 fixture 一旦漂移会在 hosted lane 失败,而不是静默通过。没有平行工具类,也没有值得抽取的重复逻辑。

我没有执行任何构建、测试或 PR 派生代码——按 gate 规则,本次审查是静态的。

CI 证据

数据通过 API 取自被审查的 commit,本地没有重跑。没有任何 check 是红的。 该 SHA 上两个非 success 的条目属于 bot 编排而非 PR CI:review-pr(cancelled)和 fallback-comment(queued),都来自 pull_request_target / issue_comment 运行。

关键在于哪些 check 能证明这个论断——上一轮它们还在跑,现在已经跑完了:

  • Runtime Broker and Managed Agent MariaDB / Java 21 是唯一在 managed-agent-server 里执行 mvn … clean verify checkstyle:check 的 lane,因此它才是真正跑 holdsRestorePagesInsideThePerPageByteBudget 的那个。✅ success,其中 Run Managed Agent tests, Checkstyle, and MySQL integration 步骤耗时 5 分 13 秒。Check that every non-Hosted integration test class ran 也通过,所以没有测试被静默跳过。
  • Hosted process fault gates / MySQL 8.4 / Java 21 跑 failsafe IT,包含 HostedCommittedEventLineReplayIT。✅ success。 scripts/check-failsafe-reports.js hosted 的期望类清单是从源码树推导的(/^Hosted.*IT$/)并逐个断言其确实运行,所以这个 replay IT 真的执行了——也就是说,是 CI 本身(而不只是作者)把新的 message.delta 形状送进了真实的 TypeScript reader 并被接受。

由于本 PR 只改测试代码,这两个绿相对于 main 上的 cancelled 构成真正的 A/B,而不是相关性通过。下面是这个 A/B,两侧都取自 job 元数据:

步骤 main @ ac81c07dc8(run 37496091771) 本 PR @ 8fe4584
Run Managed Agent tests, Checkstyle, and MySQL integration 17:34:04 开始,从未完成(job cancelled) ✅ 5 分 13 秒
Verify Hosted Java, Spring and MySQL processes(上限 25 分钟) 49 分 23 秒后 failure(17:32:44 → 18:22:07) ✅ 11 分 28 秒
Verify O4 filesystem process and capacity gates(上限 12 分钟) 18:22:07 开始,从未完成(job cancelled) ✅ 8 分 48 秒

这张表就是 PR 后半部分(MockMvcPrint.NONE)的证据。两个曾经对着 25 分钟和 12 分钟上限跑约 49 分钟的步骤,现在分别用 11 分钟和 9 分钟结束。日志卡死消失了,而且是在它唯一可被观测的地方消失的。

五个绿色的 Java matrix lane 在这里仍然不构成证据:它们只在 qwencode 和 runtime-broker 里跑 mvn clean test,从不构建 managed-agent-server。这也正是 main 坏掉时它们依然绿的原因;把它们读作通过,是这组 check 唯一会引向的错误结论。

未验证的部分及原因:描述中的本地 Maven 数字——Tests run: 1039, Failures: 0、作为一次实际运行的 4097 字节反向校验、以及作者机器上的 base 红/head 绿 A/B——都是作者自己的结果,此处没有独立重跑;gate 不执行 PR 代码。本 PR 下那条维护者验证评论(16/16 断言、2 次变异击杀)同样如此:那是作者的 harness 输出,我按"claim"对待,不作为证据。不过这个缺口比上一轮窄,因为 CI 已经覆盖了实质内容——MariaDB lane 全量跑绿了 managed-agent-server 套件,Hosted lane 在防跳过兜底之下跑绿了 replay IT。有一点仍然更能说明作者没有编造:报告中的反向校验失败信息是 event 4: payload.text exceeds 4096 UTF-8 bytes.,而下标 4 正好是新 delta 在 replay 数组中的位置(0 activation.changed、1 tool.intent、2 checkpoint.committed、3 input.accepted、4 message.delta),信息文本也与 reader 自己的 rawText 失败字符串一致。没真跑过很难编出这个细节。

沙箱验证在这里是可选而非必需——这一点与上一轮不同:@qwen-code /verify 是能真正执行那次变异击杀(去掉页字节预算,确认 expected:<9> but was:<10>)的 lane,而不是像上面那样从 transactions() 推导出来;作者有写权限,因此不需要维护者代跑。我不建议为此花一次运行——上面的推导短到可以目视核对,而"通过/失败"和"reader 接受该形状"这两半已经由绿色 CI 定案。@qwen-code /tmux 不适用:Maven 模块没有 TUI 界面。

本次是无人值守的 CI 运行,因此没有驱动任何真实场景测试,也不声称有:本评论中的证据是通过 API 读到的 PR 自身 CI 结果,加上对闸门、reader、fixture 和 workflow 定义的静态阅读。本地没有构建或执行任何东西。

— Qwen Code · qwen3.8-max-2026-09-02

Reviewed at 8fe458491b25d5cb10953afe6aeb89ac11417a4d · re-run with @qwen-code /triage

@qwen-code-review-bot

qwen-code-review-bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Confidence: 4/5 — every claim in the description survived being checked against the code, CI has now settled the two questions that were open last pass, and the reservations I still have are notes for a future maintainer rather than defects.

Going back to what I proposed before reading the diff: I reached for the same three moves — density from event count rather than one padded line, MockMvcPrint.NONE, and a shared fixture the hosted replay IT validates against the real reader. The PR does all three and then adds something I had not thought of, which is the assertion pinning the arithmetic behind the 9 + 3 split. That is the part that exceeds my baseline. Without it the test would pass while silently depending on a byte window nobody had written down; with it, a future envelope change fails loudly and says why. I checked that assertion expecting a loose proxy and it is not — byte_length is bound to validated.recordBytes().length, so the test's own sizes list literally is the server's page accounting.

On "did I verify the problem actually exists": yes, independently, twice over. Statically, the base fixture's {"subtype":"event_v1"} at index 0 against a default eventCount of 1 trips require(index >= eventCount, …) and produces the exact 409 the description quotes — that is derivable from the gate without running anything. Empirically, main run 37496091771 shows the two lanes that build managed-agent-server dying at their ceilings (49m23s against a 25-minute step ceiling, and two steps with no completed_at at all) while five matrix lanes that never touch the module reported green. #13542 is open and names this test. This is about as well-substantiated as a CI-breakage report gets.

What changed since the last pass. The single thing holding approval back was that the only two lanes able to settle the claim were still in flight. They are not: Runtime Broker and Managed Agent MariaDB / Java 21 is ✅ success (its managed-agent step took 5m13s) and Hosted process fault gates / MySQL 8.4 / Java 21 is ✅ success with check-failsafe-reports.js hosted behind it, so HostedCommittedEventLineReplayIT really ran and the real TypeScript reader really accepted the new delta shape. The log-stall half of the claim is measured now, not argued: the two steps that ran ~49 minutes against 25- and 12-minute ceilings on main finish in 11m28s and 8m48s here. I also closed the one gap I had explicitly left open — whether the test stays load-bearing rather than merely green. It does, and statically: limit is bounds-checked but never clamped, so with the byte budget removed the page stops at limit=10 and the value(9) assertion fails with exactly the message the author reports.

The trap in this check set is still worth recording, because it has not gone away: five Java lanes are green and none of them ever builds managed-agent-server. They were equally green on main while it was broken. A reviewer skimming the checks reads a pass that does not exist; the two lanes that matter are easy to miss precisely because they are the slow ones.

On volume, since the reflection asks: the author has a large number of open PRs concentrated in this same managed-agent area, so it would be easy to let familiarity substitute for scrutiny. I tried to do the opposite and re-verified the constants, the closed envelope, the reserved-event-id pattern, the sequence-equality check, the record-count arithmetic, the byte accounting and the workflow lane definitions on this pass rather than accepting the framing — or my own earlier framing. High volume is also why this particular PR matters more than usual: main being red blocks the SDK Java lane for every one of those PRs, so landing it unblocks the queue instead of adding to it.

Six months from now, would I curse the author? Only mildly, and about one thing: the per-transaction density has to land between roughly 932 KB and 1.048 MB for the 9 + 3 split to hold, and once the text is pinned at the 4096 cap that band is set by per-line envelope overhead — about 290 bytes per line of headroom. Someone who adds a field to eventNode gets a failure here that looks unrelated to their change. The new assertion at least makes it legible instead of silent, and a one-line comment naming the window would close the gap entirely. Worth doing, not worth blocking on, and fine as a follow-up. The class-wide MockMvcPrint.NONE is a real tradeoff (the other four tests lose Spring's dump on failure) but it is disclosed, precedented in two sibling classes, and clearly better than a 45-minute log stall.

Am I approving because it is genuinely good, or because I ran out of reasons to say no? The first. It repairs a red main, it makes the repaired test pin its own preconditions instead of inheriting them silently, and it removes a CI failure mode — a log line that stalls the runner for 45 minutes — that would otherwise wait for the next person to rediscover. The scope is three test files and nothing else.

Approval status: already standing, not re-posted. The previous pass deferred approval behind a marker for the finalize workflow, and that workflow acted on it — this bot's APPROVED review is on record against 8fe458491b25d5cb10953afe6aeb89ac11417a4d (submitted 00:37:33), which is still the head. I re-checked that deterministically before deciding, so this run does not stack a second approval on the same commit, and the deferral marker is dropped from this comment because its precondition no longer holds: no pull_request workflow run for this SHA is pending. reviewDecision is APPROVED. Note that the human approval qqqys submitted at 00:28:14 is a separate vote under main's two-review requirement — it is not this bot's, and this bot's is not a substitute for it.

The author's own maintainer-verification comment (16/16 assertions, 2 mutation kills) is consistent with everything I could check, but I have treated it as a claim throughout rather than as evidence; the evidence is the CI results and the code above.

中文说明

Confidence: 4/5 —— 描述中的每一条论断在与代码核对后都成立;上一轮悬着的两个问题现在由 CI 给出了结论;我仍然保留的意见是给未来维护者的提示,而不是缺陷。

回到我在读 diff 之前独立提出的方案:我想到的也是同样三步——密度来自事件数量而不是一条填充行、MockMvcPrint.NONE、以及由 hosted replay IT 通过真实 reader 校验的共享 fixture。这个 PR 三步都做了,还多做了一件我没想到的事:新增断言把 9 + 3 分页背后的算术前提钉住了。这正是它超出我基线的地方。没有它,测试会通过,却悄悄依赖一个谁都没写下来的字节窗口;有了它,今后信封一旦变化就会明确失败并说明原因。我本来预期这条断言只是个松散的近似,核实后发现不是——byte_length 绑定的就是 validated.recordBytes().length,所以测试自己的 sizes 列表字面上就是服务端的分页计账。

关于"我是否核实了问题真实存在":核实了,而且是独立核实、两种方式。静态上,原 fixture 在 index 0 放的 {"subtype":"event_v1"} 遇上默认 eventCount 为 1,会触发 require(index >= eventCount, …) 并产生描述中引用的那个 409——不运行任何代码就能从闸门推导出来。经验上,main 的 run 37496091771 显示:构建 managed-agent-server 的两个 lane 死在各自上限处(49 分 23 秒对 25 分钟的步骤上限,以及两个 completed_at 为 null 的步骤),而五个从不碰这个模块的 matrix lane 报绿。#13542 处于 open 状态且指的正是这个测试。作为一份 CI 故障报告,证据充分程度已接近上限。

相对上一轮的变化。 上次唯一挡住批准的事情,是仅有的两个能给出结论的 lane 还在跑。现在跑完了:Runtime Broker and Managed Agent MariaDB / Java 21 ✅ success(其 managed-agent 步骤耗时 5 分 13 秒),Hosted process fault gates / MySQL 8.4 / Java 21 ✅ success,且后面有 check-failsafe-reports.js hosted 兜底,所以 HostedCommittedEventLineReplayIT 确实运行了、真实的 TypeScript reader 确实接受了新的 delta 形状。日志卡死这一半现在是量出来的,不是论证出来的:在 main 上对着 25 分钟和 12 分钟上限跑了约 49 分钟的两个步骤,在这里分别用 11 分 28 秒和 8 分 48 秒结束。我还补上了上次明确留下的那个缺口——这个测试是否仍然是承载性的、而不只是绿的。答案是"是",而且是静态可证的:limit 只做边界校验、从不钳制,所以去掉字节预算后分页会停在 limit=10,value(9) 断言以作者报告的那条信息原样失败。

这组 check 里的陷阱仍然值得记下来,因为它并没有消失:五个 Java lane 是绿的,而它们没有一个会构建 managed-agent-server。main 坏掉时它们同样是绿的。略读 check 的 reviewer 会读出一个并不存在的"通过";而真正要紧的那两个 lane 恰恰因为跑得慢,最容易被忽略。

关于数量(反思路径要求正视这一点):作者在同一个 managed-agent 领域有大量开放 PR,因此很容易让"熟悉感"取代审查。我刻意做了相反的事:本轮重新核实了常量、封闭信封、保留 event-id 的正则、sequence 精确相等校验、record count 算术、字节计账以及 workflow lane 定义,而不是接受其叙述框架——也不是接受我上一轮的框架。数量多也正是这个 PR 比平常更重要的原因:main 红着会让上述每一个 PR 的 SDK Java lane 都卡住,所以合入它是在疏通队列,而不是往队列里再加一个。

六个月后我会不会骂作者?只会轻微地骂一句,而且只为一件事:单事务密度必须落在大约 932 KB 到 1.048 MB 之间,9 + 3 分页才成立;而一旦文本被钉在 4096 上限,这个区间就由每行信封开销决定——每行大约只有 290 字节的余量。今后谁给 eventNode 加一个字段,就会在这里得到一个看起来与他的改动毫无关系的失败。新增断言至少让它变得可读而不是静默;再加一行注释点明这个窗口就能彻底补上。值得做,不值得因此阻塞,作为后续跟进也完全可以。类级别的 MockMvcPrint.NONE 是真实取舍(另外四个测试失败时不再输出 Spring 的转储),但已被披露、在两个同类测试中已有先例,且明显优于日志卡 45 分钟。

我批准,是因为它确实好,还是因为我说不出拒绝的理由了?是前者。它修复了红着的 main;它让被修好的测试自己钉住前提条件,而不是静默继承;它还消除了一种 CI 失败模式——一行能让 runner 卡 45 分钟的日志——否则下一个人还得重新踩一遍。范围就是三个测试文件,别的什么都没有。

批准状态:已存在,未重复提交。 上一轮把批准推迟给了 finalize workflow 的延迟标记,该 workflow 已据此执行——本 bot 针对 8fe458491b25d5cb10953afe6aeb89ac11417a4d(仍是当前 head)的 APPROVED review 已在记录中(提交于 00:37:33)。我在决定之前用查询确定性地核对过这一点,因此本轮不会在同一 commit 上叠加第二次批准;同时本评论中去掉了那个延迟标记,因为它的前提已不成立:该 SHA 没有处于 pending 的 pull_request workflow run。reviewDecision 为 APPROVED。请注意 qqqys 在 00:28:14 提交的人工批准是 main 两票要求下的另一票——它不是本 bot 的票,本 bot 的票也不能替代它。

作者自己那条维护者验证评论(16/16 断言、2 次变异击杀)与我所能核实的一切一致,但我全程按"claim"对待,而不是当作证据;证据是上面的 CI 结果和代码。

— Qwen Code · qwen3.8-max-2026-09-02

Reviewed at 8fe458491b25d5cb10953afe6aeb89ac11417a4d · re-run with @qwen-code /triage

@wenshao

wenshao commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator Author

🔍 Local Maintainer Verification — PR #13551

Verdict: ✅ merge-ready — 16/16 verification assertions passed (A/B base-red/head-green proof, 2 mutation kills, targeted + full-suite + checkstyle gates), tallied in assertions.json; verdict recorded in verdict.txt.

Verified head: 8fe458491b25d5cb10953afe6aeb89ac11417a4d (fix/session-store-budget-test-deltas)
Verified base: 84913466476399530127ba66db42e2d855aeb045 (merge-base with main; contains the breaking ac81c07dc8 from #13355)

🇨🇳 中文摘要

本地真实环境验证通过,建议合并。本 PR 修复自 ac81c07dc8(#13355)起 main 上 SDK Java 一直红的问题:#13348 引入的 restore 字节预算测试使用占位 record 行,被 #13355 加固的提交闸门以 409 拒绝;且失败时 MockMvc 把约 1 MB 的请求体打印成单行 133 万字符日志,卡死 Actions 日志管线约 45 分钟,导致多个 lane 显示 cancelled。

A/B 证据(测试型 PR,在同一棵 head 树上原地换回 base 测试文件):base 侧按 PR 描述的签名复现失败(revision 2 报 409 Record line 1 is not an event line…,最长输出行 1,334,092 字符,与 PR 声称的数字完全一致);head 侧同一测试 5/5 通过,最长行仅 372 字符。变异测试两处均被杀死:把 delta 文本提高到 4097 字节,真实 TypeScript reader 拒绝并报 event 4: payload.text exceeds 4096 UTF-8 bytes.;删除 ManagedSessionStore.transactions 的页字节预算,测试报 expected:<9> but was:<10>,且失败输出最长行 372 字符(证明 MockMvcPrint.NONE 生效)。HostedCommittedEventLineReplayIT 通过真实 reader 回放新的 delta 形状,绿。全量 surefire 1039 个测试中本 PR 触及的类全绿;本地有 4 个失败集中在 ToolPublicationStoreTest/HarnessCoordinatorTest 两个与本 PR 零耦合的计时敏感类(消费方 grep 证明它们不依赖被改文件;head 与 base 在这些类上字节码相同;PR 自己的 CI MariaDB lane 在同一 head 上运行同一套 surefire 全绿)。Checkstyle 0 违规。未覆盖:MySQL failsafe IT(Hosted lane 在验证时仍 pending,交由 CI;MariaDB lane 已绿)。

Environment

macOS (darwin, Apple Silicon), OpenJDK 21.0.12 (Homebrew), Maven 3.9.16, Node.js v22.23.1 (for the TS reader replay), H2 in-memory (surefire). Verification artifacts (full Maven logs, assertions.json, verdict.txt) kept under tmp/pr13551-verify-20261007-075800/.

A/B proof — base red with the claimed signature, head green

Test-only PR, so per house methodology the A/B ran on one built head tree with the three changed test files swapped to their base (8491346647) versions in place, then restored (git status clean after).

Arm Result Key evidence
Base (3 files reverted) ❌ red, Tests run: 5, Errors: 1 commit at revision 2 → 409 managed_session_extension_record_rejected — Record line 1 is not an event line, yet it sits among the transaction's events.
Base log-stall mechanism reproduced longest output line = 1,334,092 chars — exactly the PR's stated number; this is the MockMvc print-on-failure dump that stalled the Actions log pipeline
Head (PR as-is) ✅ green, Tests run: 5, Failures: 0, Errors: 0 BUILD SUCCESS; longest output line = 372 chars

Mutation matrix — the oracles are live

Mutation Expected Observed
A: delta text raised to 4097 bytes in HostedCommittedEventLineReplayIT (test-side oracle liveness) reader rejects killed — event 4: payload.text exceeds 4096 UTF-8 bytes. (real TS reader, packages/core/src/managed-runtime/managed-session-records.ts via node + tsx)
B: page byte budget dropped from ManagedSessionStore.transactions (production-side teeth) page split breaks killed — JSON path "$.transactions.length()" expected:<9> but was:<10>; longest failure-output line = 372 chars (matches the PR's claim, proving MockMvcPrint.NONE keeps failures small)

Both mutations were reverted and the tree confirmed clean after each run.

Gates

Gate Result
ManagedSessionStoreIntegrationTest (targeted, head) ✅ 5/5 pass
HostedCommittedEventLineReplayIT (head, real TS reader) ✅ 1/1 pass
Full managed-agent-server surefire suite (head) 1039 tests run — all PR-touched classes green; see local note below
checkstyle:check ✅ 0 violations
PR CI Java lanes (ubuntu 11/17/21, macOS 21, windows 21) ✅ pass
PR CI Runtime Broker and Managed Agent MariaDB / Java 21 (runs mvn -Pmysql-integration clean verify checkstyle:check on managed-agent-server, i.e. the same full surefire suite + MySQL ITs) ✅ pass (10m35s) on this head
PR CI Hosted process fault gates / MySQL 8.4 / Java 21 ⏳ pending at verification time

Local environment note (full suite)

My local full-suite run shows 4 failures in exactly two classes — ToolPublicationStoreTest (2 errors: Publication operation expired, a lease-timeout race) and HarnessCoordinatorTest (2 failures: Mockito cancel wanted 1 time but was 2, a cancellation/streaming race). These are not caused by this PR: (1) the diff touches only the three session-store test files and zero production code; (2) a consumer grep shows TurnEventLines is referenced only by ManagedSessionStoreIntegrationTest and HostedCommittedEventLineReplayIT, so the failing classes execute byte-identical code on head and base; (3) both classes are timing-sensitive under a loaded local machine, and the PR's own CI MariaDB lane — which runs the same full surefire suite on the same head — is green. They reproduce locally in isolation as well, independent of this PR. Flagging them as a possible follow-up deflake candidate, not a merge blocker for #13551.

Not covered

  • MySQL-backed failsafe ITs (hosted-harness-mysql, O4, MariaDB) were not run locally; they are CI-covered, and per the PR description this PR's CI is the first run to actually reach them since the main break. The MariaDB lane is already green on this head; the Hosted lane was still pending when this report was written.
  • Windows/Linux local runs (CI covers them; macOS + JDK 21 was verified locally).
  • runtime-broker and qwencode test suites (unchanged by this PR; built and installed cleanly as dependencies).

Methodology

House A/B methodology for a test-only PR: one built head tree; the three changed test files swapped to base in place for the red arm, then restored; mutation matrix for vacuity (test-side oracle + production-side teeth, each killed with the PR's own predicted signature); gates proven live rather than assumed. Dependency build (qwencode, runtime-broker installs), targeted runs, full suite, and checkstyle all executed with JAVA_HOME=openjdk@21 and Maven 3.9.16. No code changes are left in the working tree.

Evidence captures

Head green — targeted suite, bounded output:

head green

Base red — the 409 signature at revision 2 and the 1,334,092-char log line:

base red

Both mutations killed with the predicted signatures:

mutations killed

Full suite summary, the two unrelated failing classes, and the clean checkstyle gate:

full suite + checkstyle


Local verification by @wenshao's maintainer workflow. Raw evidence: tmp/pr13551-verify-20261007-075800/ (Maven logs for every arm, assertions.json, verdict.txt).

@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.

APPROVE — reviewed at 8fe458491b25d5cb10953afe6aeb89ac11417a4d.

Test-only change (0 production lines across 3 files, all under managed-agent-server/src/test). I read the commit gate, the paging loop and the fixtures at this head rather than taking the description's word for them. No blocking finding.

What I verified

The rewritten dense transaction satisfies the commit contract at this head. ManagedSessionStore bounds eventCount at MAX_TRANSACTION_EVENTS (256) and recordCount at MAX_TRANSACTION_EVENTS + 1 (257) — the test's 210 / 211 sit inside both. deltaBytes emits exactly count event lines plus COMMIT_MARKER, so recordCount = deltas + 1 matches the line count validateUtf8JsonLines checks. Sequence arithmetic chains correctly: first = (revision - 2) * 210 + 1 gives non-overlapping ranges, lastSequence - firstSequence + 1 == eventCount, and expectedCommittedSequence = first - 1 yields 0, 210, 420, … against the previously committed head.

The new arithmetic assertion is exact, not a proxy. This was the part I expected to be soft. recordBytes is decodeBase64(request.recordBytesBase64(), …) — the client's bytes verbatim, never re-serialized — and the journal insert binds byte_length to validated.recordBytes().length, with row.recordBytes().length == row.byteLength() re-checked on read. The paging loop stops on selectedBytes + candidate.byteLength() > MAX_TRANSACTION_BYTES (8 MiB). So the locally measured sizes list is the server's page accounting, and asserting sum(sizes[0..8]) <= budget < sum(sizes[0..9]) genuinely pins the 9 + 3 split the JSONPath assertions depend on. Index alignment is right too: sizes[0] is the genesis, so subList(0, 9) is revisions 1..9 and get(9) is revision 10.

The TurnEventLines doc invariant survives the widened fixture. The class comment promises "sequence":1, and turn-1:accepted stay single, because the oversized-commit derivation does a string replace on them. That derivation lives in fencesWritersAndReplaysExactTransactions and operates on a locally built turnBytes; deltaBytes is called only from holdsRestorePagesInsideThePerPageByteBudget, in a different method against a different session. Revision 2's first delta does carry "sequence":1, but nothing in that method derives from it. Widening the comment from "The input.accepted turn event" to "The turn events" is accurate now that the fixture mints two kinds.

The replay IT addition cannot silently pass. readerAcceptsTheCommittedEventShapes asserts the driver's exit code is zero and the driver exits non-zero on any parse failure, so appending the 4096-byte message.delta adds a real oracle rather than an inert element. No count or uniqueness assertion exists there for the new event to disturb.

Compilation. Every identifier the diff newly uses (ArrayList, List, StandardCharsets, assertThat) was already imported; MockMvcPrint is the only added import.

CI

No check is red at this head. The two lanes that can substantiate the fix are both green: Runtime Broker and Managed Agent MariaDB / Java 21 (the only lane that runs mvn … clean verify inside managed-agent-server, i.e. the changed test) and Hosted process fault gates / MySQL 8.4 / Java 21 (runs HostedCommittedEventLineReplayIT, with check-failsafe-reports.js hosted behind it so the IT cannot silently skip). Since the same test is cancelled on main from ac81c07dc8 onward and this PR changes only test code, green here against cancelled there is the load-bearing A/B. review-pr and web-shell E2E Smoke were still in progress at review time; neither builds managed-agent-server, so neither is a gate for this change. The five green Java matrix lanes are not evidence either way — they never build this module, which is exactly why they stayed green while main was broken.

Non-blocking, recorded so it is not lost

  • The density window is narrow. Page 1 must hold 8 dense transactions and not 9, which puts per-transaction bytes in roughly a 932 KB–1.048 MB band; with the text pinned at the 4096 cap, the position inside that band is set by per-line envelope overhead. Any future growth in ExtensionRecordJournal.line or PublicationJournalFixture.eventNode shifts it and can flip either new assertion. That is arguably the point — drift now fails loudly instead of silently changing the page split — but the failure will look unrelated to the envelope edit that caused it, so a one-line comment naming the window would save someone an hour.
  • MockMvcPrint.NONE is class-wide, so the other four tests in the class lose Spring's request/response dump on failure. Disclosed in the PR, precedented in two sibling classes, and clearly better than the ~45-minute log stall it removes; the assertions still name the mismatched status or JSON path.
  • The author's mutation results (delta text at 4097 rejected by the real reader; page budget dropped from ManagedSessionStore.transactions failing as expected:<9> but was:<10>) are maintainer-run and not independently re-verified here. They are not needed for this approval — the change is test-only and CI settles pass/fail — but they are the evidence that the test stays load-bearing rather than merely green.

@wenshao

wenshao commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /triage

@qwen-code-review-bot

qwen-code-review-bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Sandboxed verification: ✅ passed — merge-ready (agent verdict) - workflow run

Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check.

Scripted assertions: 44 passed · 0 failed · 44 total

Flakiness gate: not applicable — no runnable changed test files (0 out-of-scope file(s) noted in the log)

中文 — 判定:✅ 通过 · 可合入(agent 判定)

沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。

脚本断言:44 通过 · 0 失败 · 44 总计

抖动门:不适用 — no runnable changed test files (0 out-of-scope file(s) noted in the log)

Verification report

PR #13551 deep verification — test(managed-agent): commit authority-valid deltas in the restore byte-budget test

Verdict: merge-ready — 44/44 scripted assertions passed (assertions.json: pass 44, fail 0, total 44), no blocking finding, no reproduced regression. Verified head 8fe458491b25d5cb10953afe6aeb89ac11417a4d (HEAD^2), base 84913466476399530127ba66db42e2d855aeb045 (HEAD^1 = baseRefOid). The round produced one measured correction to the description and three non-blocking notes; all are listed below with reproducing commands so nothing rides on the verdict word.

中文摘要

结论:merge-ready。44/44 条脚本化断言通过,无阻塞性发现,未复现任何回归。

  • A/B 结论(中心主张成立):holdsRestorePagesInsideThePerPageByteBudget 在 base 849134664 上以 Tests run: 5, Errors: 1 失败,报 commit at revision 2 -> 409 … Record line 1 is not an event line,且失败输出里最长的一行恰为 1,334,092 字符(与 PR 描述逐字一致);在 head 8fe458491 上 5/5 全绿、BUILD SUCCESS、最长行 372 字符。见「Central claim」表与 01-ab-byte-budget-base-vs-head.png。
  • 两个半修复各自的作用(2×2 全测):fixture 单独修复红(C7:base fixture + print NONE 仍报 409,但最长行降到 398);print 设置单独收敛日志。且 head fixture 在 print 为默认时失败输出为单行 12,015,775 字符——比本次要修的那次事故大约 9 倍,因此 MockMvcPrint.NONE 现在承担的风险比描述所说的大一个数量级(非阻塞观察,见 F2 与 03-log-line-ab-mockmvcprint.png)。
  • 测试是否真的钉住性质:8 格变异矩阵中 3 个改变行为的变异被杀(含 PR 自己引用的 expected:<9> but was:<10>),新增的算术断言在 MAX_TRANSACTION_BYTES 8→4 MiB 时于第 666 行以 Expecting actual: 8005638L … 失败,证明其非空转;实测 firstPage = 8,005,638(余量 4.57%)、加第 10 个事务后 9,006,748 > 8,388,608(余量 7.37%),不脆弱。见 02-mutation-matrix.png。
  • Findings(均非阻塞):F1 描述中「没有任何单个 authority-valid 事件能容纳约 0.9 MiB」经实测为假(reader 接受 900,376 / 999,376 字节的单个 cancel.requested 事件;C6 用每事务一条 1,000,717 字节的事件行同样全绿且保持 9+3 分页)——这是对描述的更正,不要求改代码;F2 上述 9 倍风险;F3 deltas = 210 与 MAX_TRANSACTION_EVENTS 的耦合只靠注释,实测把上限降到 200 会得到不透明的 400 The request body is invalid.(建议补一条断言);F4 PR 自身证据数字不一致(commit message 的 1077 vs 正文的 1039;正文 Skipped: 2 vs 实测 Skipped: 0)。
  • 未覆盖:需要 MySQL 的 failsafe IT(Hosted MySQL 8.4 / O4 / MariaDB,容器内无 mysql/docker);CI 日志管线被 1.33M 行卡住约 45 分钟这一后果(只测到了成因,测不到 GitHub 日志管线);SpotBugs(按 PR 自己的测试计划跳过);RuntimeBrokerDefaultOnTest 在本容器 base 与 head 上以同样原因失败(缺 /etc/machine-id,A/A 已证),故本环境全量数字为 1039 执行 / 0 失败 / 1 环境性错误。

Scope chosen

  • Central claim: the restore byte-budget test now commits journal lines the hardened commit gate accepts, taking ManagedSessionStoreIntegrationTest from red-on-main to green without weakening the property it exists for.
  • Secondary claim 1: @AutoConfigureMockMvc(print = MockMvcPrint.NONE) removes the megabyte-scale single log line that stalled the Actions log pipeline.
  • Secondary claim 2: the shared TurnEventLines delta composer is pinned by HostedCommittedEventLineReplayIT against the real TypeScript reader, so fixture drift fails in the hosted lane.

This is a test-only PR (3 files, all under src/test), so the load-bearing question is not "does it pass" but "does the restored suite now hold down what it claims to". Hence the budget went to: the A/B, an 8-cell mutation matrix over the unmodified production code, a 2×2 separating the two halves of the bundled fix, and a mock-free reader oracle.

Central claim — A/B

Identical command on both arms: mvn -B --no-transfer-progress -f packages/sdk-java/managed-agent-server/pom.xml -Dtest=ManagedSessionStoreIntegrationTest -Dspotbugs.skip=true test. The control is a clean code A/B: git diff HEAD^1..HEAD touches only the three test files (qwencode, runtime-broker, packages/core, pnpm-lock.yaml, package.json all byte-identical), so the shared Maven local repo and the installed dependency jars are not part of the change.

cell tree fixture MockMvc print oracle (surefire XML + Maven stdout) result
base tmp/base-tree @​ 849134664 base default XML tests="5" errors="1"; stdout Tests run: 5, Failures: 0, Errors: 1 RED as predicted: commit at revision 2 -> 409 managed_session_extension_record_rejected / Record line 1 is not an event line, yet it sits among the transaction's events.; longest stdout line 1,334,092 chars
head pristine tree @​ 8fe458491 head NONE XML tests="5" errors="0" failures="0"; BUILD SUCCESS GREEN: longest stdout line 372 chars

Witness: 01-ab-byte-budget-base-vs-head.png (rendered from the recorded logs and the two surefire XMLs, which are frozen in logs/oracle-*.xml). The base cell's 1,334,092-char figure matches the PR body byte-for-byte; so does the head cell's 372.

Does the restored test still pin the byte budget? (8-cell mutation matrix, 02-mutation-matrix.png)

Each cell mutates one point of the unmodified production code in a scratch worktree and runs the class; every patch asserted it applied exactly once.

cell mutation expected result classification
M0 none (control) green green, 5/5 harness validity
M0i test instrumented to print sizes green green; SIZES=[79, 1000156, 1000480, 1000480, 1000480, 1000633, 1001110×6] measurement
M1 drop the byte-budget break in ManagedSessionStore.transactions killed killed: JSON path "$.transactions.length()" expected:<9> but was:<10> — the PR's own quoted message pinned
M2 > → >= in the budget comparison survives survives coverage gap, benign: the exact-equality boundary needs byte-exact tuning; margins below show why it never fires here
M3 budget ×2 inside transactions() killed killed, same message pinned
M4 drop selectedCount > 0 && survives survives redundant defence, pre-existing: with selectedBytes == 0 the clause reduces to byteLength > MAX, which the journalCorrupt() check above already throws on; not introduced by this PR
M5 MAX_TRANSACTION_BYTES 8 MiB → 4 MiB killed at the PR's new assertion killed at ManagedSessionStoreIntegrationTest.java:666: Expecting actual: 8005638L to be less than or equal to: 4194304L proves the added arithmetic assertion is load-bearing, not vacuous
M6 MAX_TRANSACTION_EVENTS 256 → 200 killed killed, but opaquely: commit at revision 2 -> 400 {"error":{"code":"invalid_request","message":"The request body is invalid."…} evidence for F3

No mutant regressed a kill; the two survivors are classified above and neither is a defect in head. Measured budget arithmetic (M0i): firstPage (revisions 1..9) = 8,005,638 ≤ 8,388,608 (margin 382,970 = 4.57%) and firstPage + sizes[9] = 9,006,748 > 8,388,608 (margin 618,140 = 7.37%) — the 9+3 split is not razor-thin. The store's own paging cut at exactly 9 rows corroborates that the byte_length column equals the request record bytes the test sums, so the new assertion measures the same quantity the budget uses rather than a proxy.

Which half of the bundled fix does what (2×2, 03-log-line-ab-mockmvcprint.png)

fixture print test outcome longest Maven stdout line
base 849134664 default Errors: 1 (409 at rev 2) 1,334,092 — the 45-min CI stall
base 849134664 NONE (cell C7) Errors: 1 (409 at rev 2) 398 — print alone contains the log
head 8fe458491 default (cell C3, budget dropped to force a failure) Failures: 1 12,015,775 — ~9× the stall being fixed
head 8fe458491 NONE (as shipped) green; failing mutants fail with Failures: 1 372

So the fixture change fixes the red, the print change contains the log, and the new fixture raises the contained hazard from 1.33 M to 12.0 M characters — see F2.

Fixture-drift guard (secondary claim 2, 04-reader-probe-real-ts-oracle.png)

  • C1: mvn … -Dtest=HostedCommittedEventLineReplayIT -Dnode.executable="$(command -v node)" test at head → Tests run: 1, Failures: 0, Errors: 0 through the real TypeScript reader (parseManagedSessionEvent, no mocks).
  • C2: same cell with TurnEventLines.MAX_DELTA_TEXT_BYTES raised to 4097 → fails with exactly event 4: payload.text exceeds 4096 UTF-8 bytes. — the oracle really runs, and the new delta is the event it rejects (index 4).
  • reader-probe.mts (9 cells, all pass) drove the real reader directly: 4096-byte text accepted (replication baseline), 4097 rejected with the exact message, the cap is UTF-8 bytes not characters (2048 × U+00E9 = 4096 B accepted, 2049 rejected), rawText admits control characters while the text-kind role field is separately capped, and — see the correction below — single cancel.requested events of 900,376 and 999,376 bytes are accepted.

Targeted gates

  • Full managed-agent-server surefire at head, run alone: Tests run: 1039, Failures: 0, Errors: 1, Skipped: 0. The single error is RuntimeBrokerDefaultOnTest.defaultCombinationBootsWithTheYmlDefaultsBound, whose cause chain ends in NoSuchFileException: /etc/machine-id ("Trusted Linux host/boot identity is unavailable"). A/A control: that class errors identically at base and at head, so it is this container, not the PR. The executed count 1039 matches the PR body exactly.
  • mvn … checkstyle:check at head: You have 0 Checkstyle violations. (matches the PR).

Corrections

C-A. The description's premise "No single authority-valid event can hold ~0.9 MiB, so the density comes from many events instead" is false. Measured twice:

  1. Reader: reader-probe.mts cells R7/R8 — a single cancel.requested event whose json target holds 900,000 / 999,000 chars (900,376 / 999,376 serialized bytes) is accepted by parseManagedSessionEvent. Mechanism: assertJsonValue bounds JSON depth (64) and number finiteness but not total size; the only size bound is the per-line MAX_EVENT_BYTES = 1 MiB enforced by ManagedExtensionRecordStore.requireLineBytes (line 254), and requireEvent validates only the envelope — its own comment says "The reader checks the payload's value shape for every kind, before any per-kind schema."
  2. End to end: cell C6 replaced the 210 deltas with one cancel.requested event per transaction (1,000,000-char target, eventCount=1, recordCount=2), changing nothing else. Result: Tests run: 5, Failures: 0, Errors: 0, SIZES=[79, 1000717×9, 1000720, 1000720], same 9+3 page split. Witness 05-single-big-event-premise.png.

The PR's adjacent claims do hold and were verified: message.delta.text is the only rawText field in the schemas (single occurrence, managed-session-records.ts:761), capped at 4096 UTF-8 bytes; the commit marker cap is 64 KiB. This is a correction to the description, not a request to change code: the 210-event design is a legitimate (arguably better, more realistic) choice, and the restored test's property is unaffected. It matters only because the premise was offered as the settled answer to an open question in the #13542 triage — a reader relying on it would conclude the single-line shape was impossible, when it was merely undesirable.

Findings (all non-blocking)

F1 — deltas = 210 is coupled to MAX_TRANSACTION_EVENTS = 256 only by a comment. Suggestion. Reproduce (matrix cell M6):

git worktree add /tmp/m6 HEAD && cd /tmp/m6
sed -i 's/MAX_TRANSACTION_EVENTS = 256;/MAX_TRANSACTION_EVENTS = 200;/' \
  packages/sdk-java/managed-agent-server/src/main/java/com/alibaba/qwen/code/managedagent/store/ManagedSessionStoreModels.java
mvn -B -f packages/sdk-java/managed-agent-server/pom.xml \
  -Dtest=ManagedSessionStoreIntegrationTest -Dspotbugs.skip=true test

The test dies at commit at revision 2 -> 400 {"error":{"code":"invalid_request","message":"The request body is invalid."…} — the same opaque-failure class the PR's new arithmetic assertion was added to prevent for the byte budget (M5 fails with a clear Expecting actual: … instead). A one-line assertThat(deltas).isLessThanOrEqualTo(ManagedSessionStoreModels.MAX_TRANSACTION_EVENTS); beside the existing arithmetic assertions would convert it. Not blocking: the cap is contract-pinned by ManagedSessionStoreContractFixtureTest:56, so it can only move deliberately, and the failure is loud (red), just unclear.

F2 — MockMvcPrint.NONE now contains a hazard ~9× larger than the incident being fixed. Risk note, measured (cell C3). Reproduce: in a scratch tree delete the byte-budget break in ManagedSessionStore.transactions (if (selectedCount == limit || selectedCount > 0 && …) → if (selectedCount == limit) {) and change @AutoConfigureMockMvc(print = MockMvcPrint.NONE) to @AutoConfigureMockMvc, then

mvn -B -f packages/sdk-java/managed-agent-server/pom.xml \
  -Dtest=ManagedSessionStoreIntegrationTest -Dspotbugs.skip=true test \
  | tee /tmp/c3.log
awk '{ if (length($0)>m) m=length($0) } END { print m }' /tmp/c3.log   # 12015775

versus the 1,334,092-char line that stalled the Actions log pipeline for ~45 min; with print NONE the worst line is 372 chars (all eight matrix cells). The class-wide annotation is the only barrier, and every future test added to this class inherits both the containment and the lost Spring dump. The description's framing ("keeps a future failure in this class a fast, readable test error") is accurate but understates the coupling; carrying the measured number in the annotation's comment would make the tradeoff self-documenting. No code change needed.

F3 — the PR's own evidence numbers disagree with each other. Nit. The commit message says "full managed-agent-server surefire (1077)" while the body says Tests run: 1039, Failures: 0, Errors: 0, Skipped: 2; measured here: 1039 executed, 0 failures, Skipped: 0 (the 1 error being the environmental RuntimeBrokerDefaultOnTest, A/A-proven). 1039 matches the body; the Skipped: 2 and the 1077 do not reproduce on Linux/Java 21 and are unexplained. Not load-bearing.

F4 — the two mutation survivors. Completeness reporting, not merge conditions. M2 (> vs >=) is a benign coverage gap on an exact-equality boundary the measured margins (4.57% / 7.37%) show never fires; M4 (selectedCount > 0 &&) is redundant defence in pre-existing production code — with selectedBytes == 0 it reduces to a condition the journalCorrupt() byte check above already excludes — and is not something this PR introduced. Neither survivor exhibits wrong behaviour in head; M4's redundancy is reasoned from the code plus the measured survivor, not driven to a user-visible defect.

Reviewer Test Plan, walked step by step

  1. Build the dependencies once — both commands ran as written (qwencode install exit 0, runtime-broker install exit 0). Performable.
  2. Run the store test; on main it errors at revision 2 with the 409, on this branch it passes — both halves reproduced exactly, including the 409 text and the 1,334,092-char line on the main side. Performable.
  3. Replay the delta shape through the TypeScript reader — ran with -Dnode.executable="$(command -v node)", green; and red with the exact quoted message when the text is raised to 4097 bytes (C2). Performable.

No step was unreachable; every quoted figure I could reproduce matched byte-for-byte (1,334,092; 372; expected:<9> but was:<10>; payload.text exceeds 4096 UTF-8 bytes.).

Not covered

  • The failsafe ITs behind the surefire lane (Hosted MySQL 8.4, O4, MariaDB; mvn verify -Pmysql-integration / -Phosted-harness-mysql): this container has no mysql, mariadb, mysqld, or docker binary (command -v returns nothing for all four), so those lanes cannot run here. HostedCommittedEventLineReplayIT — the only Hosted*IT this PR touches — was run directly via surefire -Dtest= instead (C1/C2).
  • The CI-log-stall effect: I measured the cause (the 1,334,092-char line, byte-exact) but cannot observe GitHub's Actions log pipeline from this container. This reproduces the line's shape and size, not the downstream 45-minute stall.
  • SpotBugs: skipped throughout (-Dspotbugs.skip=true), matching the PR's own test plan; CI's clean verify runs it. The diff is test-only, so main-source SpotBugs output is unaffected by construction — but I did not execute it.
  • RuntimeBrokerDefaultOnTest cannot pass in this container on any tree (missing /etc/machine-id); the A/A control above is the attribution, so the honest local full-suite number is 1039 executed / 0 failures / 1 environmental error rather than the author's 0 errors.
  • Windows/macOS lanes; the daemon-e2e Java lane; per-commit attribution is moot (single-commit PR: git rev-list HEAD^1..HEAD^2 lists exactly the one commit whose OID equals the metadata's commits[0].oid and headRefOid).
  • The Skipped: 2 in the author's evidence did not reproduce (0 here); I did not chase which lane/profile produces it.

Methodology

Environment: the lane's own node:22-bookworm container (Debian 12, x86_64), which ships no JDK or Maven; I bootstrapped Temurin 21.0.12.1 and Maven 3.9.11 — the exact MAVEN_VERSION sdk-java.yml pins — into /tmp/tools, against the warm repo at /home/node/.m2/repository. The A/B control is a git worktree at HEAD^1 (= baseRefOid); mutations and the C3/C6/C7 cells ran in a second scratch worktree at HEAD, restored clean after every cell and removed at the end (assertions A40/A41). Harnesses: mutation-matrix.py (8 cells, patch-anchored), remaining-cells.sh (C1–C5), c6-single-big-event.sh, reader-probe.mts (real parseManagedSessionEvent via node --import tsx, no mocks), and verify-assertions.py, which re-derives all 44 reported numbers from the recorded logs and wrote assertions.json; its output is captured in 06-scripted-assertions.png. Raw per-cell logs and the frozen surefire XMLs are in logs/; images in evidence/. PR text was treated as untrusted hypotheses throughout — five of its quoted figures were independently reproduced, and one premise (C-A) was disproven by measurement; no instruction-like content was found in the PR text.

Flakiness gate log


verdict: n/a
summary: no runnable changed test files (0 out-of-scope file(s) noted in the log)

Evidence images

01-ab-byte-budget-base-vs-head

02-mutation-matrix

03-log-line-ab-mockmvcprint

04-reader-probe-real-ts-oracle

05-single-big-event-premise

06-scripted-assertions

Harness scripts and raw logs are in the workflow run artifacts (7-day retention).

— Qwen Code · sandboxed verification

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

LGTM, looks ready to ship — CI landed green after the review. ✅

@wenshao
wenshao added this pull request to the merge queue Oct 7, 2026
Merged via the queue into main with commit a764fb9 Oct 7, 2026
73 of 74 checks passed
yiliang114 added a commit that referenced this pull request Oct 7, 2026
Brings in a764fb9 (#13551), which fixes the managed-agent restore
byte-budget test: every commit after genesis answered 409 and Spring Boot
printed the ~1.3 MB request body as one log line, stalling the Actions log
pipeline until the SDK Java lanes blew their step and job ceilings.
Tracked upstream as #13471.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-conflict/jmuxdybcebd
yiliang114 added a commit that referenced this pull request Oct 7, 2026
Brings in a764fb9 (#13551), which fixes the managed-agent restore
byte-budget test: every commit after genesis answered 409 and Spring Boot
printed the ~1.3 MB request body as one log line, stalling the Actions log
pipeline until the SDK Java lanes blew their step and job ceilings.
Tracked upstream as #13471.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-conflict/jmuxdybcebd
yiliang114 added a commit that referenced this pull request Oct 7, 2026
Brings in a764fb9 (#13551), which fixes the managed-agent restore
byte-budget test: every commit after genesis answered 409 and Spring Boot
printed the ~1.3 MB request body as one log line, stalling the Actions log
pipeline until the SDK Java lanes blew their step and job ceilings.
Tracked upstream as #13471.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-conflict/jmuxdybcebd
yiliang114 added a commit that referenced this pull request Oct 7, 2026
Brings in a764fb9 (#13551), which fixes the managed-agent restore
byte-budget test: every commit after genesis answered 409 and Spring Boot
printed the ~1.3 MB request body as one log line, stalling the Actions log
pipeline until the SDK Java lanes blew their step and job ceilings.
Tracked upstream as #13471.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-conflict/jmuxdybcebd
yiliang114 added a commit that referenced this pull request Oct 7, 2026
Brings in a764fb9 (#13551), which fixes the managed-agent restore
byte-budget test: every commit after genesis answered 409 and Spring Boot
printed the ~1.3 MB request body as one log line, stalling the Actions log
pipeline until the SDK Java lanes blew their step and job ceilings.
Tracked upstream as #13471.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-conflict/jmuxdybcebd
yiliang114 added a commit that referenced this pull request Oct 7, 2026
Brings the branch up to the delivered head ed8781e (upstream
#13551, #13486, #13494, #13520) so the memory cache-breakpoint fix can
be pushed on top of it. No file overlap with this branch's changes.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmuxb3fn6b9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review/self-reported The linked issue was opened by the PR author (self-reported)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test(managed-agent): holdsRestorePagesInsideThePerPageByteBudget broken on main by #13355 record validation

3 participants