Skip to content

[PR] kernel: keep signed thinking with its tool_use across folds (#4885) - #4898

Open
ranxianglei wants to merge 15 commits into
masterfrom
2026-10-27_4885-signed-thinking-fold-split
Open

ranxianglei wants to merge 15 commits into
masterfrom
2026-10-27_4885-signed-thinking-fold-split

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

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-empty thinkingSignature or rawAnthropicBlock.type === "redacted_thinking"; OpenAI reasoningContent echo lanes are unsigned by definition and stay out):

  1. 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.
  2. kernel/src/turn-integrity.ts — new INV3 gate in computeIntegrityWithdrawals: 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).
  3. Version discipline: acp-kernel 0.0.168 → 0.0.169; root package-lock.json synced (lock-sync recipe, mirrors package-lock.json version metadata lags package.json (bili 0.1.190 vs 0.1.191, kernel 0.0.112 vs 0.0.114) #2704 check).

Behavior change disclosure (old → new)

  • Signed-thinking wires only. A model-requested range that starts at a tool_use (or otherwise leaves part of a signed wire-message unit behind) now folds MORE messages than before: the leading signed thinking plus any prose between it and the call joins the fold. Previously those stayed visible and produced an unrecoverable 400 session. Net effect: slightly larger folds for claude extended-thinking sessions; strictly fewer broken sessions.
  • New withdrawal path: bN/carve-boundary ranges that would split a signed run now withdraw the whole run (range may shrink/empty → honest error), where they previously folded the split. Unsigned wires: byte-identical behavior — the pre-existing Compression breaks Responses assistant history ordering, causing repeated invalid_assistant_history 400s #564 directional test ('fold call+result, keep reasoning+text') stays green because the new logic is signature-conditional.
  • No config surface, wire format, or persistence format changes.

Tests

  • New 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.
  • Targeted re-runs green on rebased head: kernel reasoning-pairs / issue684-turn-integrity / recommend-range-integrity / tool-pair(-s)-integrity / issue2886-recent-pair-integrity / wire-reasoning-echo / issue3236-recommend-apply-consistency (64 pass) + bili-level anthropic-thinking-replay / issue1960-thinking-verbatim / issue2915-order-centric-reconcile / issue4292-anthropic-fold-orphan-system (26 pass). Full suite = CI.

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).
@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

📦 Built Package Artifact

Branch: 2026-10-27_4885-signed-thinking-fold-split (5ff3b9f)

Latest build — install this exact version

npm install -g billion-context@0.0.9310-pr.4898.bff8d5e

This exact version is immutable and reproducible. The rolling pr tag tracks the newest test build across all PRs.

Recent builds (commit → version)

commit version build
5ff3b9f billion-context@0.0.9310-pr.4898.bff8d5e build
2234043 billion-context@0.0.8668-pr.4898.3e5dba9 build
5720f21 billion-context@0.0.8658-pr.4898.f183663 build
20d0d8c billion-context@0.0.8550-pr.4898.cfb77b9 build
ec5757b billion-context@0.0.7725-pr.4898.8bc63b2 build
2c9b3a4 billion-context@0.0.7344-pr.4898.7544462 build
73b6f24 billion-context@0.0.7051-pr.4898.8eb1060 build
fb6b276 billion-context@0.0.6850-pr.4898.92b1f7a build
a460a53 billion-context@0.0.6372-pr.4898.9885c3e build
e41d0ac billion-context@0.0.6354-pr.4898.58c018e build

Download artifact

  1. Download the artifact from the Actions run
  2. Extract the tarball and install:
tar xzf billion-context-pr4898.tgz
npm install -g package

Install instructions are refreshed on each push.

@ranxianglei ranxianglei changed the title kernel: keep signed thinking with its tool_use across folds (#4885) [PR] kernel: keep signed thinking with its tool_use across folds (#4885) Oct 27, 2026
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

(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)——机制各异,无同根因并行工作。
标签: bug, pi, scope/kernel, p1(折叠拆签名 thinking → 会话永久 400,按 owner 修订属 p1 第一类)。

开始按 PR Review 规范独立审 #4898(不照抄 #4885 结论):方向关 → 回归审查 → 代码 → 测试有效性 → diff 整洁度,审完给八问结论。PR 落后 master 一个 merge(#4856 hermes),会核对是否冲突。

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

(ai: vllm-qwen/qwen3.8-27b) update-branch: merged origin/master e4cc68575 into the PR head — clean merge, no conflicts, no kernel file touched by the merge itself — pushed as a460a5374; PR is no longer behind (mergeable=true, mergeable_state=blocked only while CI re-runs on the new head).

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

(ai: vllm-qwen/qwen3.8-27b)

PR #4898 审核结论(kernel 变更,human-only review 档;查重与标签见首条评论)。独立通读了全部 diff、追了三个调用点(projectUnit / recommend / bN block-boundary)、做了两轮 stash 实验验证行为钉、并在分支上直接修了审核发现的三个小问题(见下)。

八问:

  1. 是什么问题:Anthropic extended-thinking 会话里,模型请求的折叠范围若从 tool_use 开始,同一 wire message 上的 signed thinking 会被孤儿化;rebuild 后它并进下一条幸存 assistant message,此后每个请求 400 thinking or redacted_thinking blocks ... cannot be modified,直到宿主 transcript 重放才恢复。
  2. 什么原因导致的(三层):① 生产者——reasoning-pairs 的向后边界扫描只走连续 reasoning run,thinking 与 tool_use 之间隔着 prose/redacted_thinking 时停在中间,不拉回 signed 块;② 使能者——[bug] compress 保护区过滤在完整性调整之后挖洞,导致 assistant tool-call 消息缺 reasoning_content(DeepSeek 思考模式 400) #684 方向性 gate 只覆盖「保留侧带 tool-call」的情形,反向拆分对 unsigned wire(Compression breaks Responses assistant history ordering, causing repeated invalid_assistant_history 400s #564)合法、对 signed wire 非法;③ bN block-boundary 范围整体跳过边界调整(unit-projection 里 boundaryKind === "block" 直接返回),唯一兜底是 integrity gate,而它对 signed run 没有规则。
  3. 影响范围与严重程度:所有 Anthropic wire + extended-thinking(claude 系模型)会话,触发条件 = 模型请求的范围起点落在 tool_use 上。p1:会话永久不可用(每次请求 400),只能绕 proxy 重放宿主 transcript 恢复。unsigned wire(DeepSeek/OpenAI)零影响。
  4. 怎么修复的:两个互补机制,都以 signed-thinking 存在为门(isSignedThinkingFamily:非空 thinkingSignature 或 redacted_thinking sidecar;OpenAI 的 reasoningContent echo lane 按定义 unsigned,明确排除):① 边界调整在 stretch 含 signed 块时跨越 text/redacted 分隔,把整条 wire-message unit [think(sig)][prose][tool_use] 拉进折叠(legacy adjacent-run pull 原样保留为 unsigned 路径);② integrity gate 新增 INV3:被部分折叠的连续 assistant signed run 整体撤回(fold-all-or-keep-all),补上边界调整根本不跑的残余路径(bN/carve boundary)。apply 与 recommend 共用同一组函数,KDD refactor: reapOrphanBlocks should live in acp-kernel, not the proxy #12 单一投影纪律保持。
  5. 是否有回归问题(既有行为逐项清点):
  6. 是否破坏缓存:不涉及——prefix-cache 键与折叠经济学不变;唯一效果是 signed 会话的折叠略大(更多内容一起折)。
  7. 是否改了设计方向:无架构转向;修复落在 foldability 唯一定义处(kernel 共享投影)而非 host 侧 guard——这正是 host 侧修不了的原因(PR 描述成立)。
  8. 是否引入新配置:无。

行为变更披露(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):

  • kernel/src/unit-projection.ts:gate 注释在 INV3 落地后仍写 "Both invariants"——文档与代码事实不符,同步为 "All three invariants" 并补一句 INV3 例外说明;
  • kernel/tests/issue4885-signed-thinking-split.test.ts:谓词表里别扭的 cast-spread(...( { reasoningContent } as object) + 尾部多余 cast)改为本地类型上正常声明的字段;另加上述 fixpoint 回归钉。

状态说明:并行会话随后把最新 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.
ci-check added 2 commits October 31, 2026 11:06
…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.
Zayik pushed a commit to Zayik/billion-context that referenced this pull request Nov 1, 2026
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
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

(ai: vllm-qwen/qwen3.8-27b)

#4898 独立二审结论(review-worker-r1,second pass)——不照抄 floor 7 / #4885 的结论,自己重读了全部 diff、追了三个调用点、核了字段来源与既有逻辑。方向关、回归关、代码关、测试关、diff 整洁度全过。

八问:

  1. 是什么问题:Anthropic extended-thinking 会话里,模型请求的折叠范围若起点落在 tool_use 上,同一 wire message 上的 signed thinking 块会被孤儿化;rebuild 把它并进下一条幸存 assistant message,此后每个请求 400 thinking or redacted_thinking blocks ... cannot be modified,只能靠宿主 transcript 重放恢复。
  2. 什么原因导致的:三层因果——① 生产者:reasoning-pairs 向后边界扫描只走连续 reasoning run,thinking 与 tool_use 之间隔着 prose/redacted_thinking 时停在中间,不把 signed 块拉回;② 使能者:[bug] compress 保护区过滤在完整性调整之后挖洞,导致 assistant tool-call 消息缺 reasoning_content(DeepSeek 思考模式 400) #684 方向性 gate 只覆盖「保留侧带 tool-call」的情形,反向拆分对 unsigned(Compression breaks Responses assistant history ordering, causing repeated invalid_assistant_history 400s #564)合法、对 signed 非法;③ 缺口:bN block-boundary 范围整体跳过边界调整,而唯一兜底的 integrity gate 又没有 signed-run 规则。
  3. 影响范围与严重程度:所有 Anthropic wire + extended-thinking(claude 系模型)会话,触发条件 = 模型请求的范围起点落在 tool_use 上。p1:会话永久不可用(每次请求 400)。unsigned wire(DeepSeek/OpenAI)零影响。
  4. 怎么修复的:两个互补机制,都以 signed-thinking 存在为门(isSignedThinkingFamily = 非空 thinkingSignature 或 rawAnthropicBlock.type === "redacted_thinking";OpenAI reasoningContent echo lane 按定义 unsigned,明确排除):① 边界调整在 stretch 含 signed 块时跨越 text/redacted 分隔,把整条 wire-message unit [think(sig)][prose][tool_use] 拉进折叠(legacy adjacent-run pull 原样保留为 unsigned 路径);② integrity gate 新增 INV3:被部分折叠的连续 assistant signed run 整体撤回(fold-all-or-keep-all),补上边界调整根本不跑的残余路径(bN/carve boundary)。apply 与 recommend 共用同一组函数,KDD refactor: reapOrphanBlocks should live in acp-kernel, not the proxy #12 单一投影纪律保持。
  5. 是否有回归问题(逐项清点既有行为):unsigned wire 字节级不变——legacyPull 逐字保留、INV3 signature-gated、新套件有专门 unsigned control 钉住 Compression breaks Responses assistant history ordering, causing repeated invalid_assistant_history 400s #564「fold call+result / keep reasoning+text」;前向 pull([bug] compress 保护区过滤在完整性调整之后挖洞,导致 assistant tool-call 消息缺 reasoning_content(DeepSeek 思考模式 400) #684)、INV2 call/result 配对 gate 未动;定点循环单调只减、终止有保证、signed 撤回计入 splitTurnCount 诚实上报;recommend 侧 signed run 部分成员变 gap 而非可广告范围 → 无 advertise→reject 振荡(issue3236 一致性绿);缓存键/前缀/持久化/wire 格式/配置面均未触碰。无无意回归。
  6. 是否破坏缓存:不涉及——prefix-cache 键与折叠经济学不变;唯一效果是 signed 会话的折叠略大(更多内容一起折)。
  7. 是否改了设计方向:无架构转向;修复落在 foldability 唯一定义处(kernel 共享投影 projectUnit)而非 host 侧 guard——host 侧确实修不了(PR 描述成立)。
  8. 是否引入新配置:无。

我独立复核到、补充在 floor 7 之外的点:

  • 字段真实且已被填充:thinkingSignature / rawAnthropicBlock 见 kernel/src/wire/bili-message.ts,由 kernel/src/wire/anthropic.ts 写入——谓词结构正确,不是凭空假设的 sidecar。
  • 无重复代码:kernel/src/protected.ts 的 hasMediaPayload / hasUnrecoverableMediaPayload 处理的是「媒体负载」(image/tool_result/document/PDF),且刻意排除 redacted_thinking;isSignedThinkingFamily 是另一关注面(签名验证完整性),作用域正确、沿用 MediaSidecar 的结构类型惯例,非复制粘贴。
  • 版本纪律合规:acp-kernel 实际 0.0.176 → 0.0.177,root + kernel lockfile 均同步。PR body 里写的 0.0.168→0.0.169 是初稿数字,经多轮 merge-sync(0.0.171→0.0.173→0.0.177)后已过时——纪律满足,不影响判定,仅提示 body 数字与 head 不符。
  • 无 golden / wire-contract 漂移(折叠判定逻辑不是被 golden 钉住的行为常量,符合预期);未触碰 verif Tier-A 守护文件。
  • 测试有牙:端到端走 processTurn+applyCompression 断言具体消息 id 入块;谓词表四种签名形态齐全;含 unsigned control 钉住旧行为。floor 7 补的 fixpoint 回归钉(INV2 先撤回 call 之后,下一轮定点迭代必须再抓此时才变部分折叠的 signed run)在当前 head 中确认存在。

diff 整洁度:干净——7 个文件全在本 PR 目的内,unit-projection.ts 仅注释同步,无跑题文件 / 空行重排 / 生成物。

判定:技术面可合(方向对、机制钉在根因、逐项清点无回归、测试有牙、diff 干净)。但本 PR 触碰 kernel/** 且带 kernel-change: 声明,按 Kernel Boundary / §7.4 属 human-only review 档,agent 不得 approve。已在下一条以 🔶 需要决策 提请 owner 做最终人工合入决定(PR body 已引 owner 预授权 #4886,供参考)。

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

🔶 需要决策
(ai: vllm-qwen/qwen3.8-27b)

请求 owner 对 kernel 变更做最终人工合入决定——agent 按规则不 approve kernel PR,此处不代声明、不合并。

  • 对象:PR [PR] kernel: keep signed thinking with its tool_use across folds (#4885) #4898(head ec5757b),修 Fold splits signed thinking from its tool_use → permanent 400 on Anthropic wire (0.1.207) #4885(signed thinking 被折叠孤儿化 → Anthropic wire 永久 400)。
  • 被门禁的 diff(全在 kernel/**,带 kernel-change: 声明):
    • kernel/src/reasoning-pairs.ts —— signed-thinking 边界拉回(跨 text/redacted 分隔)
    • kernel/src/turn-integrity.ts —— 新增 isSignedThinkingFamily + INV3「部分折叠的 signed run 整体撤回」
    • kernel/src/unit-projection.ts —— 仅注释同步(Both→All three invariants)
    • kernel/tests/issue4885-signed-thinking-split.test.ts —— 新增回归测(10 例,含 unsigned control)
    • acp-kernel 0.0.176 → 0.0.177 + lockfile 同步
  • 审核结论:本轮独立二审 + 前轮(floor 7)均判技术面可合、无回归、测试有牙、CI 全绿。唯一挡合入的是 kernel human-only 档本身,非代码缺陷。
  • 供参考:PR body 已注明 Authorization: #4886 (owner approved in-thread)——实现前已有 owner 授权记录;仍按流程提请 owner 亲自确认并执行 human merge。

请 owner 拍板:批准合入 #4898(human merge),或给出需改动项。

ci-check added 2 commits November 7, 2026 23:50
# Conflicts:
#	kernel/package-lock.json
#	kernel/package.json
#	kernel/src/turn-integrity.ts
#	package-lock.json
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

(ai: vllm-qwen/qwen3.8-27b)

#4898 复审结论(review-worker-r1,head 20d0d8c)——本头相对我上轮审的 ec5757b 只有两处实质变化:① 分支重新同步到最新 master(merge-base = master tip cda6e73);② 新增一条回归测 kernel/tests/issue5891-recent-zone-signed-orphan.test.ts(commit 20d0d8c)。核心修复文件未变。

核心修复在最新 master 上仍成立:reasoning-pairs.ts 的 signed-stretch 拉回逻辑与上轮逐字一致(现从 turn-integrity.js import isSignedThinkingFamily),turn-integrity.ts / unit-projection.ts 的 PR 增量行数不变。master 期间对 turn-integrity 的重写(#5531 block-covered-calls-invisible、#5751 tool-pair-widening、#5891 recent-zone)经 merge 后与 INV3 gate 共存,全量 kernel 套件(含 issue4885 / issue5891 / issue5531 / issue5751)绿。

新增 #5891 测试判定:走真入口 processTurn+applyCompression,断言 !ids.has("c1") || ids.has("th1")(每个 active block 上「折了 companion tool-call 就必须连 signed run 一起折或一起撤回」)——有牙的真回归钉;触发路径与 #4885 不同(recent-zone / neverPreserveRecentTools 剪裁 vs 边界拉回),同一症状族,由同一 INV3 gate 覆盖。合格。

版本纪律:acp-kernel 0.0.186 → 0.0.187(+1),root + kernel lockfile 同步。合规。

CI(镜像缓存读,非 gh 轮询):30 checks,failed=0,success=20,pending(queued)=10 —— 无红。

八问(在本头复核):

  1. 是什么问题:Anthropic extended-thinking 会话里折叠范围把 signed thinking 与其 tool_use 拆开 → rebuild 孤儿签名 → 每请求永久 400。
  2. 什么原因:向后边界扫描只走连续 reasoning run + [bug] compress 保护区过滤在完整性调整之后挖洞,导致 assistant tool-call 消息缺 reasoning_content(DeepSeek 思考模式 400) #684 方向性 gate 只管「保留侧带 call」+ bN/carve boundary 跳过边界调整(三层叠加)。
  3. 影响面/严重度:Anthropic wire + extended-thinking 会话,范围起点落 tool_use 时触发;p1(会话永久不可用)。unsigned wire 零影响。
  4. 怎么修:signed-stretch 整段拉回 + INV3「部分折叠的 signed run 整体撤回」,双机制均以 isSignedThinkingFamily 为门;apply/recommend 共用(KDD refactor: reapOrphanBlocks should live in acp-kernel, not the proxy #12)。
  5. 回归:unsigned 字节级不变(legacyPull 原样 + INV3 signature-gated + unsigned control 钉);定点循环单调终止;缓存键/前缀/持久化/wire/配置面均未触碰。无无意回归。
  6. 缓存:不涉及(唯一效果是 signed 会话折叠略大)。
  7. 设计方向:无转向,修在 foldability 唯一定义处(kernel 共享投影)。
  8. 新配置:无。

顺手小修:本轮无可修的机械缺陷——上轮两处 polish(unit-projection 注释同步、INV3 fixpoint 钉)已在 head;新增 #5891 测试作为回归钉充分且通过,无需改动。无遗留 open-end。

判定:技术面可合。但触碰 kernel/** 且带 kernel-change: 声明 → human-only 档,agent 不 approve;已按 🔶 需要决策 提请 owner 人工合入(见下条)。

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

🔶 需要决策
(ai: vllm-qwen/qwen3.8-27b)

请求 owner 对 kernel 变更做最终人工合入决定(agent 不 approve kernel PR,不代声明、不合并)。

请 owner 拍板:批准合入 #4898(human merge),或给出需改动项。

ci-check added 2 commits November 8, 2026 22:59
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.
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

(ai: vllm-qwen/qwen3.8-27b)

Red check fixed — new head 2234043b6f is fully green (27 success / 1 skipped / 0 failed).

What happened this run:

  • The single red check test (ubuntu-latest, 24, 1) (run 42492536511) failed in tests/issue5152-owner-beat-survives-host-stall.test.ts — subtest “[Bug] Watchdog takes its stale-heartbeat reading on its own event loop, so a synchronous stall kills a live proxy (parent-gone → uncompressed 400) #5152 a worker that dies mid-flight falls back to the in-thread loop”: AssertionError: receiver port must be a number, got 0 at startReceiver (line 62). All other 27 jobs in that run were green.
  • Root cause: a publication race in the test harness, not this PR’s code. The fixture tests/fixtures/issue5152-beat-receiver.mjs published its bound port via appendFileSync (open(O_CREAT) + write = two syscalls), while startReceiver treated mere file existence as “ready”. On the loaded shard (loadavg 2.02, four concurrent test shards) the child descheduled between those two syscalls, so the parent read an empty file (Number("") === 0). The PR diff (kernel reasoning-pairs / turn-integrity / unit-projection) does not touch launcher/heartbeat at all.
  • Not an inherited master red: master’s CI lane was green at the time (CI@fe963d7f5c success). The mirror check cache was stale for this run, so live job states were verified with one-shot API fetches (no gh-polling).
  • Fix — one clearly-labeled commit test(#5152): close the receiver-port publication race… on the PR branch, test harness only, zero production code:
    • publisher side: atomic publish — write ${out}.port.tmp then renameSync to ${out}.port (atomic on POSIX; the destination never pre-exists, so Windows plain-move semantics hold);
    • consumer side: startReceiver polls until the file content parses as a positive integer port (same 15 s deadline; a receiver that dies before publishing still fails loudly, with the held bytes in the message instead of a bare got 0).
  • Verification: local tsc --noEmit clean; the test file ran 3/3 green under synthetic CPU load; push done after re-fetching and confirming branch ancestry (no concurrent pushes to fight); full CI on the new head (run 42496586209) is all green, including the previously-red ubuntu-24 shard 1.
  • Per CI-coordination policy: one-line conclusion posted to CI Control Center: Centralized CI Failure Handling & Status Sync (Comment all CI issues here; do not open new issues) | CI 总控: CI 失败集中处理与状态同步(所有 CI 问题在此评论, 勿开新 issue) #4599; no new issue opened for this CI flake (owner policy 2026-10-24).
  • Dedup (查重): searched open+closed issues and PRs by failure signature (“receiver port must be a number”, issue5152 owner-beat) — no existing fix or in-flight PR; nothing duplicated.

One observation, left untouched (outside the CI-fix lane): the sync merge 5720f21073 accidentally committed .ework-daemon/issue-body.txt (agent workdir metadata — a copy of the #5891 report body) into the tree; nothing references it. Suggest the author drops it (and gitignores .ework-daemon/) before merge — trivial, but it’s their diff to keep clean.

八问

  1. 是什么问题:PR 分支 CI 单红——[Bug] Watchdog takes its stale-heartbeat reading on its own event loop, so a synchronous stall kills a live proxy (parent-gone → uncompressed 400) #5152 心跳测试在 ubuntu-24 shard1 上断言失败「receiver port must be a number, got 0」,其余 27 个 job 全绿。
  2. 什么原因导致的:测试夹具的端口发布竞态(与本 PR 代码无关):fixture 用 appendFileSync(open(O_CREAT)+write 两次系统调用)发布端口文件,父进程只按文件存在判就绪;高负载分片上子进程在两次调用之间被调度走,父进程读到空文件(Number("")===0)。
  3. 影响范围与严重程度:仅测试脚手架,零生产代码影响;触发条件是高负载 CI 分片上的微秒级时序窗口,属偶发 flake(该轮 3424 项测试仅此 1 项挂);不影响任何用户功能,只阻塞本 PR 合入。
  4. 怎么修复的:同一交接的两端同时收紧——发布端改原子发布(write-tmp + renameSync),消费端改「轮询到文件内容为合法正整数端口」才认定就绪(15s 超时不变、未发布仍大声失败);单个标注清晰的 test: commit,不混其他改动。
  5. 是否有回归问题:无。改动仅限 tests/ 两个文件;负向方向保留(receiver 启动失败仍超时失败);本地带 CPU 压力连跑 3 次全绿;新 head 全量 CI 27 绿/1 skip/0 红,含原红的 ubuntu-24 shard1。
  6. 是否破坏缓存:不涉及(纯测试脚手架,无缓存/重试/游标路径)。
  7. 是否改了设计方向:不涉及。
  8. 是否引入了新的配置:不涉及。

判定:red check 已在本分支根治并验证(原红 shard 复跑通过),PR 可继续走 review/merge;合并仍由人工执行。

ci-check added 2 commits November 12, 2026 13:27
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.

This branch has not been deployed

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fold splits signed thinking from its tool_use → permanent 400 on Anthropic wire (0.1.207)

1 participant