Skip to content

test: e2e the #1685 plugin tool routing ladder + MCP shim lane - #1773

Merged
ranxianglei merged 3 commits into
masterfrom
2026-09-30_e2e-plugin-tool-routing
Sep 30, 2026
Merged

ranxianglei merged 3 commits into
masterfrom
2026-09-30_e2e-plugin-tool-routing

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

Sediment of the 2026-09-30 real-machine verification matrix for #1685 (issue #1611 / PR #1692 live session). Every scenario here was first probed against a production instance with curl before being pinned hermetically.

What it covers

Routing ladder end-to-end (real startServer, real plugin headers, zero-config /bili/ relay lane):

  1. rung 3 — single-active arbitration: one fresh conversation, no witness, no id → 200
  2. rung 1 — outbound tool_use witness: second conversation streams an acp_status tool_use in a plain-JSON (non-stream) response — the 5th recording lane of fix: zero-injection session identity — outbound tool_use witness routing (#1685) #1692; with TWO fresh conversations arbitration can only refuse, so a 200 proves the witness matched
  3. rung 4 — refuse-to-guess: unwitnessed tool, no id, two fresh conversations → 400 naming the ambiguity
  4. rung 2 — loud unknown-id failure: body conversationId no model request ever arrived under → 404 with the 方式一安装的dsh 原生插件:llm-pi-ai 传输(OpenAI SDK + options.fetch 注入)绕过 globalThis.fetch 拦截,模型请求不进代理、压缩完全不生效 #1158 diagnostic

MCP shim leg (codex-style host): spawns the real src/mcp.ts over stdio JSON-RPC with env BILI_MCP_PROXY + BILI_CONVERSATION_ID only — the exact client contract bili codex exercises; asserts acp_status returns ACTIVE SURFACE through the body-id lane (ring reset for determinism).

Hermeticity note (the interesting bug found while writing it)

loadConversations() at boot reads XDG_STATE_HOME — pointing at the developer's real state dir, a test server hydrates live production conversations into its routing table and they count as fresh, breaking the arbitration assertions (and worse, a dirty-gated flush could flow back). Fixed with the established env-first pattern (auto-restart.test.ts): XDG roots redirected before the server import, restored in after().

Verification

  • targeted: 2/2 new; neighbors tool-ring + tool-witness-pipes + plugin-protocol 47/47; issue760 + search-context-cross-session + e2e-grow-compress 17/17
  • full suite: 3401 tests / 3382 pass / 0 fail / 19 skipped
  • npm run typecheck clean

Sediment of the 2026-09-30 real-machine verification matrix (issue #1611 /
PR #1692 live session): every scenario was first probed against a
production instance with curl, then pinned hermetically.

- routing ladder: single-active arb → outbound tool_use witness (plain-JSON
  recording lane, 5th of #1692) beats a two-active refuse-to-guess → loud
  404 on an unknown body conversation id (#1158 diagnostic)
- MCP shim leg: spawns the real src/mcp.ts over stdio JSON-RPC the way a
  codex host drives it (BILI_MCP_PROXY + BILI_CONVERSATION_ID env only) and
  routes a tools/call through the body-id lane
- hermetic: XDG_STATE_HOME redirected before server import — boot's
  loadConversations() would otherwise read the developer's production
  plugin-conversations.json and count live sessions as fresh conversations,
  breaking single-active arbitration assertions (auto-restart.test.ts
  env-first pattern)
@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

📦 Built Package Artifact

Branch: 2026-09-30_e2e-plugin-tool-routing (4f7620d)

Option A — Install this exact build (recommended)

npm install -g billion-context@0.0.1723-pr.1773.375f376

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-pr1773.tgz
npm install -g package

Install instructions are refreshed on each push.

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 PR #1773 review — test: e2e the #1685 plugin tool routing ladder + MCP shim lane
#1773

Reviewed branch 2026-09-30_e2e-plugin-tool-routing (head 1872c3b0, base = master HEAD 80b50b7a, which already carries #1692 zero-id-ring-routing). Single commit, one new file tests/e2e-plugin-tool-routing.test.ts (+273), zero source changes.

① Direction. Right target. Pins the #1685/#1611/#1692 zero-injection routing ladder and the codex-style MCP-shim contract hermetically against the real startServer and the real src/mcp.ts over stdio JSON-RPC — no mocks. Matches the live-session matrix in the OP.

② Regression audit. Pure test addition, no source path touched → nothing existing changes. I confirmed every assertion maps onto the actual decision code (so the tests pin real invariants, not invented ones):

  • Routing ladder src/plugin.ts:900–988: rung 1 witness (witnessIds.size===1, wins over a differing body id, L913–923) · rung 4 refuse-to-guess (fresh≠1 → 400 "refuse to guess", L946–950) · rung 3 single-active arb (fresh===1, L929–945) · rung 2 body-id → unknown = 404 carrying the 方式一安装的dsh 原生插件:llm-pi-ai 传输(OpenAI SDK + options.fetch 注入)绕过 globalThis.fetch 拦截,模型请求不进代理、压缩完全不生效 #1158 diagnostic (L984). All four asserted with distinct status codes + regexes.
  • Witness ring src/tool-ring.ts: keys on sha256(normalizeToolName(name)+"\0"+stableStringify(args)); resetToolRingForTest (L155) used for determinism. Rung 1 posts {tool:"acp_status"} (no args → {}) right after the relay streams acp_status/arguments:"{}" → identical hash ✓.
  • The "5th recording lane" is real: recordJsonToolWitnesses (src/plugin.ts:2730) — its OpenAI-chat branch (L2776–2792) records message.tool_calls[].function.{name,arguments} from non-stream bodies, invoked at L2706.
  • MCP shim src/mcp.ts: BILI_MCP_PROXY→resolveProxyOrigin (L45), BILI_CONVERSATION_ID→env channel (L67); tools/call needs no prior initialize; after resetToolRingForTest() the call routes through the body-id lane deterministically. ACTIVE SURFACE confirmed present in the kernel's acp_status panel output.
  • Hermeticity fix is correct and load-bearing: XDG roots redirected before the server import, restored in after() (same pattern as auto-restart.test.ts) — otherwise boot's loadConversations() would hydrate live production conversations into the routing table and break the single-active arbitration.
  • All four helpers exist: resetToolRingForTest (tool-ring.ts:155), _resetPluginStateForTest (plugin.ts:2798), _setStoreForTest (persist.ts:925), _setForTest (registry.ts:537).

③ Code. Clean and deterministic (port 0, isolated XDG, ephemeral relay port, ring reset between legs). No as any/@ts-ignore; rung comments explain intent.

④ Test validity (teeth). Strong, not tautological. Test 1 walks all four rungs in order and asserts exact statuses + diagnostic substrings (refuse to guess, no model request has arrived with this conversation id yet) — any regression in rung ordering, witness hashing, or the plain-JSON witness feed flips a 200↔400/404 and fails. Test 2 drives the genuine shim over stdio through the body-id lane.

⑤ Diff. One file, all insertions, no whitespace/reformat churn. Clean.

Local evidence (I ran it): npm ci OK (acp-kernel 0.0.99); npm run typecheck exit 0 (confirms the OP claim). Ran the new file directly: test 1 passes as-is; test 2 failed only in this sandbox because /tmp is mounted read-only here and the shim child's deliberately-minimal env omits TMPDIR (tsx fell back to /tmp → EACCES: permission denied, mkdir '/tmp/tsx-*'). Forwarding a writable temp dir makes it pass 2/2, so the logic is sound and the failure is purely environmental — it will not affect CI (ubuntu/windows runners have writable /tmp). Non-blocking note: if you'd like this suite runnable inside read-only-/tmp sandboxes, forwarding TMPDIR into the child env does it — but that deviates from the intentional minimal-env contract that mirrors exactly what bili codex spawns, so I've left it to your call rather than pushing it.

CI (this head): 9/12 green — all five real-client lanes, hermetic registry e2e, build-artifact, version-guard, and test (ubuntu-latest, 22); ubuntu-24 / windows-22 / windows-24 still running and will turn green. No blockers found on my side.

Verdict: mergeable once the three remaining test-matrix jobs finish green. Not merging myself — merges are human-only per repo rules.

中文一句话:这是纯测试新增(只加一个 e2e 文件、零源码改动);我逐条核对了它断言的四级路由阶梯与 MCP shim 契约确实对应 src/plugin.ts/src/tool-ring.ts/src/mcp.ts 的真实决策代码并本地跑通(typecheck 干净,路由阶梯用例直接通过,MCP 用例仅因本沙盒 /tmp 只读而需补 TMPDIR 后通过),测试有真实断言牙齿、无回归风险,CI 已 9/12 绿且剩余矩阵作业在可写 /tmp 的 runner 上会转绿——可以合并。

- env spreads process.env (issue760 spawnShell pattern): a stripped env
  loses SystemRoot/PATH on Windows and the shim dies before replying —
  the likely windows-lane CI failure on the first push
- drive initialize → notifications/initialized → tools/call, the exact
  order a codex host uses, instead of a bare tools/call
src/mcp.ts exits on stdin end — the 300ms timer could kill an in-flight
tools/call reply on slower CI runners (all four lanes failed this way;
local runs passed by timing luck). Wait for the target reply, then kill.
@ranxianglei
ranxianglei merged commit 30a23cb into master Sep 30, 2026
12 checks passed
@ranxianglei
ranxianglei deleted the 2026-09-30_e2e-plugin-tool-routing branch October 15, 2026 05:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant