Skip to content

[PR] fix: zcode never compresses — compress prompts advertised a tool the model could not see or call - #4372

Merged
ranxianglei merged 3 commits into
masterfrom
2026-10-23_zcode-mcp-dialect-compress
Oct 23, 2026
Merged

ranxianglei merged 3 commits into
masterfrom
2026-10-23_zcode-mcp-dialect-compress

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

Fixes #4344

What was broken (plain words)

Zcode users got the "time to compress" reminders (5+ nudges at 21-29% context usage) but compression never actually ran — 0 compressions in 46 requests. Same users on DSH/Opencode were fine. Two things were wrong at once, and bili could not tell them apart:

  1. The MCP bridge never delivered the tools (zcode agent modes gate MCP tools; the degraded shim path serves an EMPTY tool list, e.g. after native routing inactive (cert-MITM) — the reporter's log has these lines). The model's tool table held no bili tool at all, yet bili kept advertising compression.
  2. The bridge was healthy but the names didn't match — zcode exposes the tools as mcp__bili__compress (fixed MCP shim prefix, [Bug] MCP 模式下 acp_rule 的工具名带有前缀,可能导致硬保护失效 #2578), while the standing prompt and nudges teach the bare name compress. A strong model can bridge that gap by itself; the reporter's deepseek-v4.1-flash never called it.

Why bili couldn't tell these apart: the reachability gate read only the literal name compress (dialect-blind), and zcode had no host-profile entry so the surface was unconditionally ON in plugin mode. Not a regression — these gaps existed since the mechanisms were born (#3417 registry never had a zcode entry; prompts always taught bare names). The execution path itself is fine (e2e-plugin-grow-compress has folded via mcp__bili__compress since 2026-09-30).

The fix (observe, don't assume)

Effect on other lanes: none change bytes — proxy mode never arms the note (bili injects bare names itself), pi/opencode/omp/dsh keep their prompts as-is. claude-code/kimi (MCP lanes without profile entries) keep their current always-on surface and additionally get the name note when their bridge delivers prefixed names — a strict improvement, same mismatch applies to them.

Config surface

No config field/env/flag added or changed.

Tests (both directions per the #3617 gate discipline)

  • NEW tests/issue4344-mcp-dialect.test.ts (6 tests): unit — zcode no-tools drops surface + surface=off(no-tools) detail; single regular tool still drops; MCP dialect keeps it + arms the note; bare declaration keeps it + note off; both dialects → note off; dsh dialect lane-independence; pi untouched; proxy mode never arms; note gating. Real chain (openai wire, x-bili-plugin: zcode) — bridged: prompt carries the mapping, wire tool table forwarded untouched, NO bare compress injected; bridge-absent: no prompt, no note, nothing injected; pi negative control: full prompt, no note.
  • tests/lane-decisions-matrix.test.ts: +5 rows across all four wires — zcode no-tools / mcp-dialect / bare / single-regular-tool, dsh mcp-dialect. 86 → 91 rows total.
  • Adjacent regression: lane-decisions-b-class, plugin-nudge-injection, all 5 zcode suites, wire-contract, cache-seam-probes, responses-fold-cache-seam — all green.

Pre-flight

  • npm run typecheck clean on the rebased head (base = current master, 0ba857f)
  • Targeted local suites: 189/189 green on the rebased head
  • Full-suite verdict: the CI test lanes on this pushed head

No kernel/ change, no persistence/config/wire-shape change beyond the intended prompt note. Happy to adjust wording of the appended note — it is host-side and cheap to iterate.

@ranxianglei ranxianglei changed the title fix: zcode never compresses — compress prompts advertised a tool the model could not see or call [PR] fix: zcode never compresses — compress prompts advertised a tool the model could not see or call Oct 23, 2026
@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

📦 Built Package Artifact

Branch: 2026-10-23_zcode-mcp-dialect-compress (fc79960)

Option A — Install this exact build (recommended)

npm install -g [email protected]

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

Option B — Download artifact

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

Install instructions are refreshed on each push.

@ranxianglei

Copy link
Copy Markdown
Owner Author

接单

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 (ai: vllm-qwen/qwen3.8-27b)

查重:不重复。 检索了 open+closed 的 issue 与近期 PR(按失败签名而非标题):唯一相关项就是本源 issue #4344(本 PR 的 Fixes 对象);在途 PR 里没有任何另一个 PR 动 zcode 可达性判定 / host-profile / MCP 方言这条机制链(最接近的 #4366 是 zcode/claude/kimi 看门狗重连节奏,不同子系统)。查重声明完毕,开始独立审这个 PR。

标签:bug / zcode / scope/wiring / p2 —— 修的是 zcode 车道的接线缺陷(压缩面宣传了一个模型调不到的工具),严重度沿用 #4344 的最终定级(能用但核心承诺失灵到「装了等于没装」边缘,owner 未提级则维持 p2)。

先报已完成的核对(结论随后补完整八问):

  • 分支已 rebase 到当前 master(base fdbddd0 = master HEAD),单一内容提交 f12f59c + 两次 merge master,12 文件 +321/−19,无 kernel/ 改动、无 version 改动、配置面零新增——与 PR 描述一致。
  • 本地定向跑 tests/issue4344-mcp-dialect.test.ts + tests/lane-decisions-matrix.test.ts:92/92 绿(含新增 5×4 wire 矩阵行与真实链路断言)。
  • CI 在 pushed head 上已开跑,多数 lane 还在排队;全量判定以 CI 为准,绿了我再下最终合并结论。

正在做的独立审查点(不复述 PR 自述):① 逐条核 diff 触碰路径的既有行为清单(dsh/pi/omp/opencode/代理模式字节是否真的不动);② relay round-2 重建的字节平价嵌套是否与四条 steady prepare 完全一致;③ 桥断时 nudge 是否也一并静默(矩阵行已钉 nudge:false,代码侧再确认一次调用链);④ claude-code/kimi 拿到注记的前提(pluginMode + 前缀名)是否如 PR 所述成立。

@ranxianglei ranxianglei added bug Something isn't working p2 scope/wiring zcode 改动范围:内核之外的主机侧接线(src/agent 适配器、server 路由、注入拦截)/范围兜底/客户端标签 labels Oct 23, 2026
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 (ai: vllm-qwen/qwen3.8-27b)

独立审核完成,结论:可合(待 CI 全量 lane 转绿;我这边所有独立验证项全部通过)。 以下是结构化八问(不照抄 PR 自述,逐条自己核过):

1. 是什么问题:zcode 默认安装(native/MCP)的会话永远压缩不了——bili 在提示词和 nudge 里反复教模型调 compress,但模型的工具列表里根本没有这个工具(plugin 模式下 bili 不注入 wire 工具,而 zcode 宿主侧只暴露 mcp__bili__compress 这种带前缀的名字),于是「教了、看不见、调不到」,nudge 空转。

2. 什么原因导致的:两层叠加。① 可达性判定只认字面 compress,不认识 mcp__bili__compress 这个方言;② zcode 没有 host-profile 条目,落到匿名档(toolsHostScopedByDefault:false),plugin 模式下被当成「永远有工具」无条件放行。我在 host-profiles.ts:91 核过匿名档取值,确认旧行为就是无条件 ON。

3. 影响范围与严重程度:只有走 zcode plugin 模式的会话(以及未来任何以 MCP 前缀名暴露工具集的 plugin 宿主);该车道核心承诺静默失效(装了等于没装),但无数据损坏、无其他车道波及。p2 定级沿用 #4344,同意。

4. 怎么修复的:策略是「观测出站工具表这一个事实源」而不是按车道名猜:可达性判定加一个方言分支;仅当模型实际看到 mcp__bili__compress 且看不到裸名时,给常驻系统提示追加一句静态映射注记(四个核心工具名);同时给 zcode 诚实的 host-profile(host-scoped:桥断→整个压缩面+nudge 一起静默并打 surface=off(no-tools) 日志)。明确不选「注入裸工具」是对的——plugin 模式 relay 逐字透传响应,proxy-loop 拦截对 plugin 会话不生效,注入的裸名会变成悬空工具调用。

5. 是否有回归问题:逐路径清点完毕——

  • lane-decisions 可达性:新增分支只在工具表含 mcp__bili__compress 时改变结论,其余车道字节级不变;
  • 四个 prepare + inject:注记包裹是恒等函数除非标志为真,而该标志要求 pluginMode+看到前缀名+无裸名——dsh/pi/opencode/omp 今天都到不了这个组合,字节不动;kimi 确实打 x-bili-plugin(src/kimi/toml-edit.ts:468 custom_headers),若其桥给出前缀名注记会正确触发,这是设计内的泛化而非意外;claude-code 侧我没在本仓找到打点证据,但机制是纯观测式的,那句话只是条件式预测、不是代码假设,不构成风险;
  • relay round-2 重建:同一嵌套同一标志,且注释钉明 round-2 今天是 proxy-loop-only、proxy 模式标志恒假——平价成立;
  • nudge 通道:kernel 文案未动(kernel/ 零改动已核实);zcode 桥断时 nudge 随 compressSurfaceOn 一起静默——这正是修掉的空转本身;
  • Prepared 早退构造器缺省新可选字段:那些路径根本不进压缩面,安全。
    唯一有意的行为变更(old→new):zcode plugin 模式桥断时,从「宣传一个调不到的工具 + nudge 持续空转」变为「压缩面与 nudge 静默 + 一行诊断日志」;桥健康时常驻提示多一句静态注记。均已披露,无无声漂移。

6. 是否破坏缓存:健康桥会话升级瞬间头部系统提示多一句 → 一次性前缀缓存失效,之后字节稳定(注记位置在四条 steady prepare 和 round-2 重建间完全一致,测试钉了平价);桥断会话提示变小,同样一次性失效后稳定;其他车道零字节变化,缓存不受影响。

7. 是否改了设计方向:没有转向。是把 #3884/#3482 既有的「lane-decisions 单一决策 + 矩阵双向断言」纪律扩展到第二个 wire 方言;kernel 未动、无持久化/wire 格式变化。

8. 是否引入新配置:没有。零配置面变化。

证据:本地在精确 head fc79960 上定向跑了 tests/issue4344-mcp-dialect.test.ts + tests/lane-decisions-matrix.test.ts(92/92 绿,含真实链路转发字节断言)和相邻的 cache-seam-probes / plugin-nudge-injection / lane-decisions-b-class(11/11 绿)。测试有效性看过:断言有牙(正负双向、钉转发字节、钉日志文案),回退任何一行修复都会红。CI 说明:fc79960c0 上出现了一对同秒创建的双 run(concurrency 撞车),其中一组被平台秒取消——那是平台产物不是代码失败;存活的一组还在排队等 runner,转绿即最终放行。diff 整洁度:12 文件 +321/−19,每一行都与本 PR 目的相关,无跑题/空白噪音。

判定:可合。合并权限在你(系统规则禁止 agent 合并)——CI 全量 lane 绿了直接 merge 即可,标签「马上合并」名副其实。

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 (ai: vllm-qwen/qwen3.8-27b)

补一条 CI 澄清(上一条结论不变:可合):

fc79960 上那条 ci.yml 的 failure 徽章不是这个 PR 的代码问题,是今天仓库级的 CI 故障——签名:push 事件触发的 ci.yml 运行零 job 秒失败。我核了四件事:

  1. 该 run(39938689238)一个 job 都没跑——PR 代码根本没执行,不可能由 diff 引起;
  2. master 自己同样中招(39938590908 @09:59Z 等,也是零 job),另一条 agent 分支 10:47Z 同样签名——系统性,非本 PR;
  3. 失败起点恰好压在今天那批 CI 重构合入([PR] fix(verif): on Windows, verif test path drive letter is doubled (D:\D:\), cannot open file — loader passes absolute path | fix(verif): windows 下 verif 测试路径盘符翻倍(D:\D:\)打不开文件 — 加载器对绝对路径原样使用 + 回归钉 #4376/[PR] CI: build once, reuse everywhere — cache kernel/pi-subagents dist by content hash so legs stop rebuilding 6 minutes of artifacts every run #4377/[PR] fix(ci): run corrupt index.db degrade checks in a child process so windows cleanup can't hit EBUSY (#4338) #4378/[PR] fix(ci): give timing-flake shapes one bounded retry and widen the two strictest victim budgets (#4161) #4379,~10:00 UTC)后的第一次 master push 上,嫌疑最大的是 b8328f1(concurrency group,当日 447-run 队列事故的补丁);异常点:on.push.branches 只匹配 master/release,功能分支却创建了 push run;
  4. 关键:受影响 sha 上没有 failed check-run 附着(fc79960c0 实测 tally:6 success / 9 skipped / 25 cancelled / 22 queued / 0 failure)——PR 合并门禁实际没被它拦。

已单独立项 #4404(带来源标记)记录该机制家族,含影响面(release 分支依赖 push check-runs 的 #772 路径失效,workflow_dispatch 兜底仍在)、证据 run id 与修法方向——不塞进本 PR。

本 PR 自己的 pull_request 车道现状:Theorem Lane、e2e-codex-persona、e2e-dsh-plugin-lane、Windows codex overlay、e2e-native-opencode、e2e-local-registry 六条已绿,其余 22 条还在排队等 runner(今日队列拥堵所致)。转全绿即最终放行;若排队中有 lane 变红我会被唤醒继续查。

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 (ai: vllm-qwen/qwen3.8-27b)

接单。当前状态(fc79960c0):18 条 lane 已绿、0 红,剩 10 条 gate(core test matrix 的分片入口)还在排队等 runner——今日队列拥堵的尾巴,代码侧没有任何失败信号。我继续盯:全绿了我立刻回帖确认;任何一条变红我当场定向排查(本地只跑相关收窄 case,不预跑全量)。

另外说明:合并动作本身按系统规则只能由人来点(agent 禁止合并 PR)——CI 全绿后 #4372 直接 merge 即可,标签「马上合并」名副其实。

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 (ai: vllm-qwen/qwen3.8-27b)

状态更正——真正的卡点找到了:不是排队慢,是 ci.yml 本身今早起就是坏文件。

上一条我说「只剩 gate 排队、PR 车道正常」,查清了要修正:本 PR 剩余那批 gate check-run 属于 ci.yml 自身,而 ci.yml 自今天早上 CI 重构合入(b8328f1f6,新增顶层 concurrency:)后就有了两个顶层同名 key,GitHub 直接拒绝整个文件('concurrency' is already defined),ci.yml 的每次运行(push 和 pull_request 都算)都以零 job 即死。之前看到的绿 lane 全部来自其他独立且有效的 workflow 文件(Theorem Lane、各 e2e-*、registry 等),所以显得「大部分已绿」。我先前立的 #4404 只观察到 push 车道即死,实际影响面比那更大。

修复已在途:PR #4405(fix(ci): drop the duplicate top-level concurrency block,Fixes #4404)。我已独立核对过它的 diff:只删 8 行(新加的那个块),保留早上 owner 批准过的旧块——同 ref supersede 语义不变,且旧块自带 #3331(schedule 不取消)与 #3772(非 ci:full labeled 走独立 -skip 组)两道守卫,删掉的是没有这两道守卫的新块,方向正确;修完后文件里恰好剩一个 concurrency:。

今天发布的操作顺序(合并动作都在你这边,agent 不能点合并):

  1. 先合 [PR] fix(ci): drop the duplicate top-level concurrency block that invalidates ci.yml #4405(https://github.com/ranxianglei/billion-context/pull/4405)——它自己的 CI 就是活体自测(head 含修好的 ci.yml,能跑起来即证明文件合法);
  2. 合入后我立刻把 master sync 进 [PR] fix: zcode never compresses — compress prompts advertised a tool the model could not see or call #4372 分支(必须让它的 head 带上修好的 ci.yml——重跑旧 head 只会再执行一遍坏文件)并回帖;
  3. sync 后的 head 上 core matrix 真跑转绿;
  4. 合 [PR] fix: zcode never compresses — compress prompts advertised a tool the model could not see or call #4372。

代码侧结论维持上一条八问:可合,zcode 修复本身零回归风险点未决。

@ranxianglei
ranxianglei merged commit 8d24bf9 into master Oct 23, 2026
51 of 77 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

📦 Merged Build Published

This merge to master was published as 0.0.762-master.8d24bf9 (rolling master tag).

npm install -g [email protected]  # this exact build
npm install -g billion-context@master  # always the newest merged build

npm view billion-context@master prints the current version at any time.

ranxianglei added a commit that referenced this pull request Oct 23, 2026
[PR] fix(#2155): keep the nudge/self-heal probe armed on headerless zombie rounds (zcode regression from #4372)
yanhuojiyusheng pushed a commit to yanhuojiyusheng/billion-context that referenced this pull request Oct 23, 2026
…less zombie rounds

ranxianglei#4372's zcode profile (toolsHostScopedByDefault) made every no-tools
plugin round read as unreachable, which starved zombieStreak — the ranxianglei#2155
degrade-to-proxy recovery could never fire for zcode sessions (red on
master: tests/issue2155-self-heal.test.ts:270, 3/3).

Discriminator: ranxianglei#4344's silent bridge sends the lane HEADER with an empty
tool list; the zombie shape (plugin gone from the client) sends NO header
on a sticky plugin-bound session. resolveLaneDecisions now takes
pluginHeaderPresent and keeps compressReachable=true for headerless
plugin rounds; the four prepares pass it from the live request headers.
ranxianglei#4344's header-present silent case is untouched (matrix row pair added).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working p2 scope/wiring zcode 改动范围:内核之外的主机侧接线(src/agent 适配器、server 路由、注入拦截)/范围兜底/客户端标签 马上合并

Projects

None yet

1 participant