FN-6465: document ACP Route A recovery blockers

Record the unrecovered Route A U9 verdict and the design-only U14 confirmation so follow-up work preserves the ACP permission-gate blocker.\n\n- Add an ACP contract open-question entry that marks Route A as NOT GO until real MCP forwarding and ACP permission-gate traversal are proven.\n- Capture the FN-6465 recovery outcome in the Claude ACP runtime plan, including the missing FN-6459 evidence and required rerun criteria.\n- Confirm the internal Route A design mechanisms while keeping implementation blocked on U9.\n\nFiles changed:\n docs/acp-contract.md                               | 24 ++++++++++++++++++++++\n .../2026-06-14-001-feat-claude-acp-runtime-plan.md |  9 ++++++++\n 2 files changed, 33 insertions(+)

Fusion-Task-Id: FN-6465

Fusion-Task-Lineage: d4762440-cbb9-4206-b4b2-be578eba30dd
This commit is contained in:
gsxdsm
2026-06-14 21:48:31 -07:00
parent 863ebfaa96
commit 41d4704138
2 changed files with 33 additions and 0 deletions

View File

@@ -100,3 +100,27 @@ enabled (writes default OFF).
- `@agentclientprotocol/sdk` v0.24.0 — https://www.npmjs.com/package/@agentclientprotocol/sdk
- Validation: the SDK example echo agent (CI) + an in-repo controllable fixture
(`src/__tests__/fixtures/echo-agent.mjs`); Gemini CLI / Claude-adapter for manual e2e.
## Open Questions
<!--
FNXC:ACPRoute 2026-06-14-21:33:
FN-6459 originally intended to store the Route-A U9/U14 feasibility decision in a task document, but that task-local deliverable was not recoverable after archive. Keep the security-critical OQ1 decision in this committed contract and the route plan so FN-6460 cannot be re-blocked by lost task metadata.
-->
### OQ1 — Route A MCP-over-ACP forwarding and permission-gate traversal
**Status:** UNRESOLVED / BLOCKED as of FN-6465 (2026-06-14). **Combined Route A verdict: NOT GO** until this OQ records both required U9 answers as GO.
**Recovery status:** NOT-RECOVERED. `fn_task_show FN-6459` retained only archived task metadata plus an archive log entry, `.fusion/tasks/FN-6459/` is absent in the FN-6465 worktree, and `fn_task_document_read(key="research")` returned not found from FN-6465's execution context. No surviving authoritative FN-6459 U9 verdict was available to transcribe.
**U9 answers required before Route A implementation:**
1. Whether `claude-code-cli-acp` can forward the real Fusion MCP server(s) supplied through ACP `session/new.mcpServers` to the underlying interactive `claude`, using the actual `packages/pi-claude-cli/src/mcp-config.ts` stdio shape (`{ command: "node", args: [serverPath, schemaFilePath] }`), not a stub.
2. Whether a forwarded Fusion tool invocation surfaces back to Fusion as ACP `session/request_permission` and therefore traverses the existing permission gate, or whether the bridge lets `claude` invoke the MCP tool autonomously inside the bridge, bypassing the gate.
**FN-6465 result:** these answers remain unproven. Local binaries were present during recovery (`claude` 2.1.177 and pinned `claude-code-cli-acp` 0.1.1), but FN-6465 did not complete an authenticated, instrumented spike against the real Fusion MCP config with ACP permission telemetry. Do not infer a GO from binary presence.
**Escalation path:** rerun U9 with an authenticated `claude`, the pinned bridge, a non-empty `session/new.mcpServers` generated from the real Fusion MCP config builder, and explicit instrumentation for `session/request_permission`. If MCP servers are ignored, forwarded tool calls cannot be invoked, or tool calls bypass the ACP permission gate without an MCP-layer permission hook or sensitive-tool exclusion, Route A remains blocked and the missing capability must be sponsored upstream in the bridge and/or ACP forwarding layer. A `claude -p` fallback is not an acceptable Route-A completion path.
**U14 internal mechanisms:** GO for design, subject to U9. Route A should use a second `acp-claude` runtime posture rather than mutating the generic `acp` runtime; inject the ACP bridge client from the engine `registerExtensionProviders` seam into the vendored `@fusion/pi-claude-cli` provider options; and add `AgentRuntimeOptions.mcpServers` to both the engine runtime contract and the ACP plugin-local structural copy, with `newAcpSession` defaulting to `[]` for Route-B compatibility.

View File

@@ -136,6 +136,7 @@ sequenceDiagram
## Open Questions
- **OQ1 (blocking for Route A; resolved by U9) — Can Fusion's MCP tools traverse the ACP bridge to Claude, *through the permission gate*?** Two parts: (a) does `claude-code-cli-acp` plumb `session/new` `mcpServers` to the underlying `claude` (its README does not mention MCP); and (b) **do the resulting tool calls surface as ACP `session/request_permission` (gated), or does `claude` invoke them autonomously inside the bridge, bypassing the gate?** U9 must test **(b) with the real Fusion MCP config** that `mcp-config.ts` builds — not a trivial stub — and record both answers. If tool calls bypass the gate, a separate control (MCP-layer hooks, or excluding sensitive-category tools from forwarding) is required before U10. Mandatory-`-p` means a no-go escalates to upstream work, not a `-p` fallback.
- **FN-6465 recovery outcome (2026-06-14): UNRESOLVED / BLOCKED; combined Route A verdict: NOT GO.** Recovery status: **NOT-RECOVERED** — `fn_task_show FN-6459` retained only archived task metadata plus an archive log entry, this worktree has no `.fusion/tasks/FN-6459/`, and `fn_task_document_read(key="research")` returned not found in FN-6465's context. No authoritative U9 verdict survived to transcribe. The U9 spike was **not re-run to a verdict** in this recovery task: local binaries are present (`claude` 2.1.177 and pinned `claude-code-cli-acp` 0.1.1), but no authenticated, instrumented run against the real Fusion MCP config and ACP `session/request_permission` telemetry was completed. Therefore both security-critical U9 answers remain unknown: (1) forwarded real Fusion MCP tool invocation through the bridge is **unproven**; (2) permission-gate traversal versus bridge-local autonomous invocation is **unproven**. FN-6460 must not start U10-U13 until a follow-up spike records both answers here and in `docs/acp-contract.md`; the no-go path is upstream bridge/ACP MCP passthrough or permission-hook work, not a `claude -p` fallback.
- **OQ2 (blocking sub-gate of U11) — Resume loss is amnesia, not a slowdown.** On resume the provider sends **only the latest user turn** (`buildResumePrompt`, `packages/pi-claude-cli/src/provider.ts:114-125`) and relies on `--resume` to load prior conversation from disk. The ACP path opens a **fresh session per turn** with no `sessionId` passthrough (`loadAcpSession` deferred). Dropping resume **without** switching to full-history prompts makes Claude answer multi-turn chat/executor conversations with zero prior context — silently. **Decision required in U11:** either thread `sessionId` → `loadAcpSession`, or send full flattened history (`buildPrompt`) every turn. No path may send latest-turn-only without resume.
- **OQ3 (blocking sub-gate of U11) — Tool-call & partial-message fidelity through the round-trip.** The provider consumes native `stream-json` with `--include-partial-messages` (exact tool-call argument boundaries); the ACP path re-derives chunks from transcript-JSONL → ACP `session/update` → the event bridge, which sanitizes/space-repairs/bounds the stream. Confirm tool-call arguments survive with intact start/end correlation and no space-repair corruption of JSON args, and that executor/reviewer lanes tolerate the transformed deltas. Capture exact tool-call argument bytes in U11's characterization tests, not just token ordering.
@@ -301,6 +302,8 @@ plugins/fusion-plugin-acp-runtime/src/
**Approach:** Drive the bridge over ACP with a non-empty `session/new` `mcpServers` carrying **the real Fusion MCP config that `mcp-config.ts` builds today** (not a trivial stub — size, server count, and stdio transport assumptions must be exercised). Verify two things and record both: (1) Claude can invoke a real forwarded Fusion tool; (2) **whether that invocation surfaces as an ACP `session/request_permission` (gated) or is invoked autonomously inside the bridge (gate bypassed)** — this is the security-critical answer (OQ1/security F3). If `mcpServers` is ignored, OR tool calls bypass the gate with no mitigation, Route A is blocked → escalate to upstream bridge/ACP work (mandatory-`-p`: no `-p` fallback). This is a hard go/no-go gate; it is **necessary but not sufficient** — see U14 for the internal blockers.
**Test scenarios:** `Test expectation: none -- spike; the deliverable is a recorded go/no-go decision (with the gate-traversal answer), not shipped code.`
**FN-6465 status (2026-06-14):** **UNRESOLVED / BLOCKED**. The original FN-6459 decision was not recovered, and this task did not complete an authenticated, instrumented bridge spike with the real `mcp-config.ts` output. U9 remains a hard NOT-GO gate: do not implement U10-U13 until a follow-up proves both forwarded-tool invocation and ACP permission-gate traversal (or records a definitive no-go/escalation).
### U14. Design-confirmation: resolve Route A's internal blockers (Route A gate 2 of 2)
**Goal:** Resolve the internal blockers that no spike screens — knowable today — before committing U10–U13. **KTD9, KTD10, KTD11.**
@@ -313,6 +316,12 @@ plugins/fusion-plugin-acp-runtime/src/
3. **Per-route posture (KTD9):** confirm the `acp-claude` second runtime id (bridge-pinned, tool-bearing) vs. a per-call override, and how lanes select it (model-id/`useClaudeCli` → `runtimeHint`).
**Test scenarios:** `Test expectation: none -- design gate; deliverable is the recorded mechanisms that unblock U10/U11.`
**FN-6465 U14 confirmation (2026-06-14):** **GO for the internal design mechanisms, subject to U9.** Current source still matches the planned seams:
- **KTD10 / pi-extension injection seam:** `packages/engine/src/pi.ts:1366-1422` is the provider-registration seam (`registerExtensionProviders`) that discovers the vendored `@fusion/pi-claude-cli` and registers pending providers into the pi `ModelRegistry`. The implementation task should construct/inject an ACP bridge client at this engine-owned seam and thread it through the provider options just as `packages/pi-claude-cli/index.ts:222-234` currently threads `mcpConfigPath` into `streamViaCli`; `packages/pi-claude-cli/src/provider.ts:73-77` reads that option shape and `provider.ts:136-156` passes it to the subprocess layer. Add the ACP client/driver as the analogous option field so `@fusion/pi-claude-cli` stays dependency-clean and never imports `@fusion/engine` or plugin internals.
- **KTD11 / `AgentRuntimeOptions.mcpServers` contract:** `packages/engine/src/agent-runtime.ts:35-106` currently has no `mcpServers` option, and the plugin-local structural copy at `plugins/fusion-plugin-acp-runtime/src/types.ts:81-95` mirrors only the fields the runtime reads. `plugins/fusion-plugin-acp-runtime/src/provider.ts:348-356` still hardcodes `mcpServers: []` in `newAcpSession`, and `plugins/fusion-plugin-acp-runtime/src/runtime-adapter.ts:85-89` calls `newAcpSession(connection, { cwd })` without MCP data. U10 should add an optional `mcpServers` field to both option types, change `newAcpSession` to accept it, have `runtime-adapter.ts` pass it through, and default to `[]` when absent for Route-B back compatibility. The existing `packages/pi-claude-cli/src/mcp-config.ts` output is one stdio server under `mcpServers.custom-tools` with `{ command: "node", args: [serverPath, schemaFilePath] }`, which maps directly to an ACP `mcpServers` entry.
- **KTD9 / per-route posture:** `plugins/fusion-plugin-acp-runtime/src/index.ts:13-24` still exposes one global runtime id (`acp`) whose `acpRuntimeFactory` constructs one `AcpRuntimeAdapter` from a frozen settings blob; `plugins/fusion-plugin-acp-runtime/src/runtime-adapter.ts:29-35` resolves that blob once in the adapter constructor. Route A therefore needs a distinct `acp-claude` runtime id/posture pinned to the bridge and tool-bearing defaults rather than a per-call override of the generic `acp` runtime. Lanes should select it through the existing model/provider selection path (`pi-claude-cli` / `useClaudeCli` resolving to a Route-A `runtimeHint`), leaving the generic `acp` contract available for arbitrary ACP agents and Route-B read-only asks.
### U10. ACP MCP-server forwarding in the runtime (Route A enabler)
**Goal:** Forward Fusion's MCP server(s) on `session/new` so the agent can call Fusion tools — implementing the contract change KTD11 specifies.