Repository navigation
[PR] kernel: keep signed thinking with its tool_use across folds (#4885) - #4898
ranxianglei wants to merge 15 commits into
Conversation
A fold range starting at a tool_use could leave the same wire message's signed thinking block behind when prose (or redacted_thinking) sat between them: the backward reasoning-pair scan only walked contiguous reasoning runs, so [thinking(sig)][prose][tool_use] split into orphaned thinking + folded call. The orphan merged into the next surviving assistant message on rebuild and every later Anthropic request failed with 400 'thinking or redacted_thinking blocks in the latest assistant message cannot be modified' — unrecoverable until the host transcript was replayed. Two complementary changes, both gated on signed-thinking presence so unsigned wires stay byte-identical (#564 directional allowance intact): - reasoning-pairs.ts: the backward boundary pull now also crosses assistant text / redacted_thinking separators when the stretch holds a signed thinking family block (thinkingSignature or rawAnthropicBlock type redacted_thinking), pulling the whole wire-message unit into the fold. Double runs ([think][text][think][tool_use]) covered by scanning back to the stretch start. - turn-integrity.ts: new INV3 gate in computeIntegrityWithdrawals — a partially folded run of consecutive assistant messages holding signed thinking is withdrawn wholesale (the existing directional gate only covered turns whose kept side carried a tool-call). Closes the residual paths where boundary adjustment does not run (bN block-boundary ranges, unit-projection.ts carve pipeline). Exported isSignedThinkingFamily() is the single predicate both sides use. kernel 0.0.168 -> 0.0.169 (version discipline, Kernel Boundary rule 2); root package-lock.json synced (lock-sync recipe). Regression suite: kernel/tests/issue4885-signed-thinking-split.test.ts (9 tests incl. unsigned control pinning the unchanged #564 behavior). Authorized via #4886 (owner approved).
📦 Built Package ArtifactBranch: Latest build — install this exact versionnpm install -g billion-context@0.0.9310-pr.4898.bff8d5eThis exact version is immutable and reproducible. The rolling Recent builds (commit → version)
Download artifact
tar xzf billion-context-pr4898.tgz
npm install -g packageInstall instructions are refreshed on each push. |
(ai: vllm-qwen/qwen3.8-27b) 查重: 不重复——open PR 中仅此一个修 #4885。近邻逐一排除:#4207(pairing 不动点收敛)、#4681(round≥2 thinking 截断保留)、#3940(#651 reasoning-drop 形状钉测)、#4045(codex forge handoff)、#3746(threads reforge)、#4782(tag-change key)——机制各异,无同根因并行工作。 开始按 PR Review 规范独立审 #4898(不照抄 #4885 结论):方向关 → 回归审查 → 代码 → 测试有效性 → diff 整洁度,审完给八问结论。PR 落后 master 一个 merge(#4856 hermes),会核对是否冲突。 |
(ai: vllm-qwen/qwen3.8-27b) update-branch: merged origin/master |
(ai: vllm-qwen/qwen3.8-27b) PR #4898 审核结论(kernel 变更,human-only review 档;查重与标签见首条评论)。独立通读了全部 diff、追了三个调用点(projectUnit / recommend / bN block-boundary)、做了两轮 stash 实验验证行为钉、并在分支上直接修了审核发现的三个小问题(见下)。 八问:
行为变更披露(old→new):① signed wire 上从 tool_use 起点的范围现在折叠更多(把前置 signed thinking + 中间 prose 一并拉入)——旧行为是留下一个必坏会话;净效果 = 折叠略大、坏会话严格更少;② 会拆散 signed run 的 bN/carve-boundary 范围现在整体撤回该 run(范围可能缩到空 → 诚实报错),旧行为是照折不问地拆开。 测试有效性:10 个测试(原 9 + 审核补 1)断言都有牙——端到端走 processTurn+applyCompression 并断言具体消息 id 入块;谓词表覆盖四种签名形态;stash 实验确认 3 个行为测试在未修复代码上真失败(第二轮实验排除了「加载期失败冒充行为失败」的弱证据)。我补了一个缺失的属性钉:INV2 先撤回 call 之后,下一轮定点迭代必须抓住此时才变成部分折叠的 signed run 再撤回 thinking——钉住「acted 才标 handled」的迭代顺序,防止未来重构静默破坏。 审核中直接在分支上修的小问题(两个标注 commit,e41d0acc→7b7c35c33):
状态说明:并行会话随后把最新 master(#4856/#4855,hermes/docs)合入了 PR 分支解决 behind——无冲突,kernel 文件零改动,我的两个 polish commit 完整保留在其之下(a460a53)。当前 head 上 e2e 各车道已绿,主 CI(gates + 全量 test 矩阵)运行中,全量判定以 CI 为准。 判定:方向正确、机制钉在根因、逐项清点无回归、测试有牙、diff 干净——可合(待 CI 全绿确认)。kernel 变更为 human-only review 档,等 owner 合并。 |
…merge-guard sync, PR #4898) Conflicts were version fields only: acp-kernel resolved to 0.0.171 (union of master's 0.0.170 bumps and this PR's kernel changes, which none of them cover). unit-projection.ts auto-merged as the union of this PR's INV3 gate comment and master's #4484 merge-coverage code.
…merge-guard sync, PR #4898) Conflicts were version fields only: acp-kernel resolved to 0.0.173 (master's 0.0.172 covered #4521/#5109; this PR's #4885 kernel changes need their own step for kernel-guard Rule 2). unit-projection.ts auto-merged as the union of master's last-user anchor note (#5109) and this PR's INV3 signed-thinking comment.
Syncs 158 upstream commits. Keeps the fork's pi-subagents fold-delegation work, the ranxianglei#4885 signed-thinking fold fix (upstream ranxianglei#4898 not merged yet) and the fold-anchor cache markers (#10). Conflict in kernel/src/turn-integrity.ts with upstream ranxianglei#5337: the ranxianglei#4885 signed runs now live in IntegrityPre so the recommend screen stays linear. Kernel 0.0.176. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013kJPjjyBgMdqSBtM99jRPR
…sync for #4885 re-validation) # Conflicts: # kernel/package-lock.json # kernel/package.json # kernel/src/turn-integrity.ts # package-lock.json
(ai: vllm-qwen/qwen3.8-27b) #4898 独立二审结论(review-worker-r1,second pass)——不照抄 floor 7 / #4885 的结论,自己重读了全部 diff、追了三个调用点、核了字段来源与既有逻辑。方向关、回归关、代码关、测试关、diff 整洁度全过。 八问:
我独立复核到、补充在 floor 7 之外的点:
diff 整洁度:干净——7 个文件全在本 PR 目的内,unit-projection.ts 仅注释同步,无跑题文件 / 空行重排 / 生成物。 判定:技术面可合(方向对、机制钉在根因、逐项清点无回归、测试有牙、diff 干净)。但本 PR 触碰 |
🔶 需要决策 请求 owner 对 kernel 变更做最终人工合入决定——agent 按规则不 approve kernel PR,此处不代声明、不合并。
请 owner 拍板:批准合入 #4898(human merge),或给出需改动项。 |
# Conflicts: # kernel/package-lock.json # kernel/package.json # kernel/src/turn-integrity.ts # package-lock.json
(ai: vllm-qwen/qwen3.8-27b) #4898 复审结论(review-worker-r1,head 20d0d8c)——本头相对我上轮审的 ec5757b 只有两处实质变化:① 分支重新同步到最新 master(merge-base = master tip cda6e73);② 新增一条回归测 核心修复在最新 master 上仍成立: 新增 #5891 测试判定:走真入口 processTurn+applyCompression,断言 版本纪律:acp-kernel 0.0.186 → 0.0.187(+1),root + kernel lockfile 同步。合规。 CI(镜像缓存读,非 gh 轮询):30 checks,failed=0,success=20,pending(queued)=10 —— 无红。 八问(在本头复核):
顺手小修:本轮无可修的机械缺陷——上轮两处 polish(unit-projection 注释同步、INV3 fixpoint 钉)已在 head;新增 #5891 测试作为回归钉充分且通过,无需改动。无遗留 open-end。 判定:技术面可合。但触碰 |
🔶 需要决策 请求 owner 对 kernel 变更做最终人工合入决定(agent 不 approve kernel PR,不代声明、不合并)。
请 owner 拍板:批准合入 #4898(human merge),或给出需改动项。 |
kernel/package.json + lockfiles: 0.0.188 (master) vs 0.0.187 (branch bump for #4885 kernel changes) -> 0.0.189, required by Kernel Boundary rule 2 because this branch carries kernel/src changes relative to master. All other 75 master commits auto-merged cleanly.
…t got 0" CI flake On a loaded CI shard (PR #4898 run 42492536511, test ubuntu-24 shard 1) "#5152 a worker that dies mid-flight falls back to the in-thread loop" failed with "receiver port must be a number, got 0": startReceiver gated on mere existence of beats.txt.port, while the fixture published it with appendFileSync (open(O_CREAT) + write are two syscalls). Under load the child descheduled between them and the parent read an empty file (Number("") === 0). Both sides of the same handoff are closed: - fixture: publish atomically (write .port.tmp, renameSync — atomic on POSIX; the destination never pre-exists, so Windows plain-move semantics hold); - test: poll until the content parses as a positive integer port (same 15 s deadline); a receiver that dies before publishing still fails loudly. Test harness only; no production code touched.
(ai: vllm-qwen/qwen3.8-27b) Red check fixed — new head What happened this run:
One observation, left untouched (outside the CI-fix lane): the sync merge 八问
判定:red check 已在本分支根治并验证(原红 shard 复跑通过),PR 可继续走 review/merge;合并仍由人工执行。 |
Merge origin/master (33104b1) into the PR branch. The only conflicts were the acp-kernel version string in kernel/package.json, kernel/package-lock.json and the root package-lock.json: the PR carried 0.0.189 (its #4885 kernel change), master had advanced to 0.0.193. Resolved to 0.0.194 = master's 0.0.193 + 1 for the PR's kernel source changes (kernel-guard requires a version line change whenever the PR diff touches kernel/src). Same resolution pattern as the previous sync 5720f21 (master 0.0.188 -> 0.0.189). No source conflict: all kernel/src files auto-merged.
Second sync needed after the first (364c77b, against 33104b1) was built but not yet pushed: master advanced to 9674cfa with the acp-kernel version at 0.0.195. Same shape as before — the only conflicts were the acp-kernel version string in kernel/package.json, kernel/package-lock.json and the root package-lock.json. Resolved to 0.0.196 = master's 0.0.195 + 1 for the PR's kernel source changes (kernel-guard requires a version line change whenever the PR diff touches kernel/src). All kernel/src files auto-merged; no source conflict.
kernel-change: a fold range starting at a tool_use can orphan the same wire message's SIGNED thinking block when prose/redacted_thinking sits between them — the backward reasoning-pair scan only walked contiguous reasoning runs. The orphan merges into the next surviving assistant message on rebuild and every later Anthropic request fails with 400 'thinking or redacted_thinking blocks in the latest assistant message cannot be modified', unrecoverable until host-transcript replay (#4885, reporter build 0.1.207; fold-decision code identical v0.1.207..master, so still present). The host side cannot fix this: foldability has exactly one definition (projectUnit, KDD #12) shared by apply and recommend — a host-side guard would leave advertised ranges failing at apply time.
Closes #4885
Authorization: #4886 (owner approved in-thread).
What changes
Two complementary changes, both gated on signed-thinking presence (
isSignedThinkingFamily: non-emptythinkingSignatureorrawAnthropicBlock.type === "redacted_thinking"; OpenAIreasoningContentecho lanes are unsigned by definition and stay out):kernel/src/reasoning-pairs.ts— the backward boundary pull now also crosses assistant-text / redacted_thinking separators when the stretch holds a signed thinking-family block, pulling the whole wire-message unit[thinking(sig)][prose][tool_use]into the fold instead of splitting it. Double runs ([think][text][think][tool_use]) are covered by scanning back to the stretch start. The legacy adjacent-run pull is kept verbatim as the unsigned path.kernel/src/turn-integrity.ts— new INV3 gate incomputeIntegrityWithdrawals: a partially folded run of consecutive assistant messages holding signed thinking is withdrawn wholesale (fold it all or keep it all). The existing directional gate only covered turns whose KEPT side carried a tool-call; this closes the residual paths where boundary adjustment does not run at all (bN block-boundary ranges skip adjustment per unit-projection.ts, carve pipeline).Behavior change disclosure (old → new)
Tests
kernel/tests/issue4885-signed-thinking-split.test.ts(9 tests): reported shape end-to-end through processTurn+applyCompression, redacted_thinking separator variant, double-run variant, whole-unit fold, INV3 unit cases (partial-fold withdrawal / full-fold ok / unsigned reverse split allowed / predicate table), and an UNSIGNED control pinning the unchanged Compression breaks Responses assistant history ordering, causing repeated invalid_assistant_history 400s #564 behavior. Verified the suite FAILS on the unfixed code (stash-check) and passes with the fix.