MissionControlPanel renders the U6a /live snapshot with push+poll convergence
(SSE-triggered refetch + 5s interval armed only while sessions are in-flight,
cleared when idle), stale-node marking, and the live SDLC funnel. Wired into the
shell's Mission Control tab.
Tokens/Tools/Activity/Productivity/Ecosystem/Signals area components fetch the
U9 endpoints and render via the U4 chart primitives, wired into the shell tabs.
SWR reset effects key on a derived signature (not array identity) to survive
revalidation. LOC/plugin gaps show unavailable sentinels; Signals degrades to
empty until U11/U13 data lands.
routeModel + conservative v0 allowlist (dependabot/lint → cheap tier) wired
into execution/planning/validation lanes in model-resolution.ts; governance
(isPermitted) and column-agent override are absolute, OFF by default. Routing
decisions (with counterfactual) emit via the U1 usage_events seam.
Adds autoResolveReviewComments project setting (default on) gating the existing
Review-response loop, a single-sourced summarizePrThreadActivity counter, and
fixed/acted thread counts in the dashboard PR summary. Resolution stays
independent of the auto-merge gate (disabled merge still resolves threads).
composeLiveSnapshot (core, U6a) feeds GET /api/command-center/live; register-
command-center-routes exposes tokens/tools/activity/productivity/live as thin
adapters over the U2 aggregators + U3 cost. All endpoints inherit session auth
(401 unauth) and apply getScopedStore (no cross-project leak). Vite proxy verified.
costFor() derives USD from token counts via a hand-maintained provider:model
rate map with pricingAsOf + staleness flag; unknown models report unavailable
rather than guessing. Cost wired additively into token-analytics per-task so it
is correct for any groupBy.
SignalSource adapter seam with mandatory HMAC verification, replay window +
nonce dedup, SSRF-safe URL handling, body-size/rate-limit/field caps, and a
groupingKey on every normalized Signal for U13's storm guard. Inbound webhooks
create triage tasks via the existing store (no schema change).
Schema migration 117→118 adds usage_events; events captured via a dedicated
emitUsageEvent seam wired through AgentLogger tool hooks + executor session
context (model/provider/nodeId), not by widening log signatures. meta is
size-capped and carries only non-sensitive descriptors.
- CodeRabbit: spinner class `spin` -> `animate-spin` (matches the card's other
Loader2 usages).
- CodeRabbit (major): tighten auth-failure detection so it only fires when the
WHOLE turn is the short "Not logged in" message (<=80 chars), not when a long
legitimate answer merely mentions the phrase — avoids false positives.
- CodeRabbit (major): expand the auth-signal test to assert the full invariant —
set on a not-logged-in turn, clear (unlink) on a real response, and NOT flag a
long answer that mentions the phrase.
pi-claude-cli acp-driver 5/5; typecheck clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Greptile P2: `acp.active` now reflects the ACTUAL dispatch determinant
(FUSION_CLAUDE_ACP, which includes the operator force-override), not the
experimental flag alone — so the status isn't misleading when forced on/off.
- CodeRabbit/Greptile P2: add FNXC:ClaudeAcp comments to the new code blocks
per the AGENTS.md greppable-comment convention.
Already fixed in the prior commit (daa37d08c): the P1 "sticky env" / latch
(applyClaudeAcpEnable now recomputes each call + FUSION_CLAUDE_ACP_FORCE
override) and the enable->disable-on-same-env regression test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When the bridged `claude` can't authenticate (detached daemon / no keychain),
the turn returns "Not logged in" instead of a real answer. Rather than silently
relay that, detect it and let the user choose.
- Driver: detect a "Not logged in"-only turn and write a cross-process signal
(fusion-acp-bridge-auth.json); a real response clears it (acp-driver test).
- Dashboard status: GET /providers/claude-cli/status reports
acp.authFailed + authReason from the signal.
- UI: the Claude CLI provider card shows an auth-failure banner with
"Use claude -p" (sets experimentalFeatures.claudeCliAcp=false) and
"I fixed auth — re-test", plus a fix hint (run `claude` to log in).
- Enable resolution now recomputes each call with an operator force-override
(FUSION_CLAUDE_ACP_FORCE), so the "Use -p" fallback takes effect on the next
turn — no restart. claude-acp-enable tests updated.
pi-claude-cli + engine tests green; dashboard typecheck clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GET /providers/claude-cli/status now returns an `acp` block:
{ enabled (experimental flag), bridgeAvailable (KTD10 published path), active
(enabled && acpEnabled && bridgeAvailable) } so operators can see whether Claude
CLI is routing through the ACP bridge vs `claude -p` — important for the
default-on rollout. Additive; typechecks clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the manual FUSION_CLAUDE_ACP env enable with an experimental feature
switch. `experimentalFeatures.claudeCliAcp` is ON by default (off only when
explicitly set false); the engine translates it into the FUSION_CLAUDE_ACP
dispatch the pi-claude-cli provider reads, at registerExtensionProviders time.
- Still fail-closed: with no bridge path published (acp-runtime plugin absent),
the provider falls back to `claude -p`.
- Explicit FUSION_CLAUDE_ACP env always wins (operator / test override).
- New testable helper claude-acp-enable.ts (6/6 tests); flag documented in the
core experimentalFeatures doc.
So with the acp-runtime plugin installed, Claude CLI now routes through the ACP
bridge by default; set experimentalFeatures.claudeCliAcp=false to force `-p`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
streamSimple routes to streamViaAcp ONLY when FUSION_CLAUDE_ACP=1 AND a bridge
path is provided; otherwise stays on the -p streamViaCli path. Covers the three
cases (off / flag-without-path / flag+path) and asserts the bridge path + env
are forwarded. 333/333 pi-claude-cli tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three-reviewer pass (correctness/security/reliability) on the highest-risk file.
P0:
- Break-early now gates on isPiKnownClaudeTool: Claude's internal ToolSearch
(used to load deferred MCP tools) no longer aborts the turn before the real
fn_* call. Surface+break works from both tool_call updates and
request_permission. New test replays the U9 [ToolSearch, fn_task_list] sequence.
- Downgrade a tool_use turn that surfaced zero pi tool calls -> stop (mirrors
provider.ts), so pi never dispatches non-existent tools.
- register the bridge child in the process registry (no orphan on teardown).
- inactivity timeout (30 min, re-armed per chunk) + per-RPC timeouts on
newSession (a hung bridge now ends the stream and dies).
P1:
- capture bridge stderr + child 'close' handler -> surface exit code/stderr
(no more silent, undebuggable failures).
- sanitize untrusted agent output: strip ANSI/control chars, per-chunk +
per-turn caps, bound tool ids/names (no terminal-escape injection / DoS).
- validate bridge path (absolute + exists) before spawn.
- preserve image content blocks in the prompt (flatten-to-text dropped vision).
P2:
- enforce the bridge env allow-list INSIDE the driver (HOME/PATH/terminal only),
not trusting the caller-supplied object.
Documented residual (kill-switch stays OFF until verified): the bridge's
tool-execution ordering and native-tool (Bash/Read/Write) execution-prevention
need a live behavioral test before any lane enables this path.
pi-claude-cli: 330/330 tests green; typecheck clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds streamViaAcp: a drop-in alternative to streamViaCli that drives Claude
through the claude-code-cli-acp bridge over ACP instead of `claude -p`. Returns
the same AssistantMessageEventStream, so streamSimple dispatches to either
transport behind a kill-switch (FUSION_CLAUDE_ACP=1 + an injected bridge path),
OFF by default — the live `-p` path is byte-for-byte untouched until soak.
- Full-history prompt every turn (buildPrompt) — the ACP path has no --resume (R13).
- Forwards schema-only MCP servers so Claude emits correct tool calls; breaks
early on the first tool_call (cancel turn, surface to pi) so the bridge never
executes Fusion's tools — mirrors the `-p` break-early pattern.
- Translation reuses the tested createEventBridge by synthesizing Claude stream
events from ACP session/updates, sharing pi sequencing + tool-name mapping.
- Bridge env forwards only HOME/PATH so `claude` authenticates from the login
session (R17); never inherited process.env or API keys.
Verified: 3/3 translation unit tests; real-bridge session/update shapes confirmed
(agent_message_chunk text + tool_call); 326/326 existing pi-claude-cli tests green;
typecheck clean.
Remaining for Route A: engine injection of the bridge path (KTD10), U12 picker/
auth/status, U13 workflow verification.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Route A unblock + the first Route A code increment.
- U9 verdict recorded (plan OQ1 + docs/acp-contract.md): in an authenticated
interactive session the pinned claude-code-cli-acp 0.1.1 bridge forwards
session/new mcpServers to Claude, Claude invokes the forwarded Fusion tool,
and the call traverses the ACP permission gate (session/request_permission).
Both security-critical answers resolve positively — overturns the headless
NOT-GO chain (FN-6466/6467/6473/6476), whose only blocker was running
detached from the login keychain session.
- R17 (daemon auth) recorded and closed for the supported setup: creds are
macOS Keychain-only; the user's login-session fn daemon has keychain access
(the existing claude -p provider authenticates there), so the bridge does too.
- U10: thread an optional mcpServers list through the ACP runtime contract.
newAcpSession now forwards it (was hardcoded []); AgentRuntimeOptions (engine
+ plugin-local copy) gains the field; defaults to [] to preserve Route B's
read-only ask posture. Tool calls still route through the U5 permission floor.
Plugin typechecks clean; provider-session tests 12/12 (incl. 2 new forwarding
tests). U11-U13 (provider transport swap, picker/auth, workflow verify) remain.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Index the upstream ACP MCP forwarding sponsorship doc so the docs README and CLI index test keep it discoverable.
- Add the upstream ACP MCP passthrough and permission forwarding sponsorship doc link under Audit Reports.
- Add the upstream doc to the required docs README index coverage.
- Guard that CLI Printing Press docs remain indexed in Audit Reports only, not duplicated under Plugins.
Files changed:
docs/README.md | 5 +++++
.../cli/src/__tests__/docs-readme-index.test.ts | 21 +++++++++++++++++++++
2 files changed, 26 insertions(+)
Fusion-Task-Id: FN-6479
Fusion-Task-Lineage: f23afeba-a989-4552-8857-fe2984df6081
Ensure AI-merge clean-room directories are cleaned up even when setup fails before worktree registration.
- Move clean-room directory creation inside the cleanup guard so terminal setup failures can remove it.
- Skip cleanup only when no merge root was ever created, while still unregistering any registered paths.
- Add regression coverage for active-session registration failure after mkdtemp and before git worktree add.
Files changed:
.../ai-merge-worktree-cleanup.test.ts | 31 ++++++++++++++++++++++
packages/engine/src/merger-ai.ts | 21 ++++++++++-----
2 files changed, 45 insertions(+), 7 deletions(-)
Fusion-Task-Id: FN-6453
Fusion-Task-Lineage: 364b329b-c3e7-4902-8ca8-673360b0d9ac
Add a durable proof-point runbook for validating external plugins against released Fusion CLI builds.
- Document release selection, npm integrity capture, scaffold/build/test/install/enable validation, and pass/fail criteria for external plugin proof points.
- Link the runbook from the docs index so it remains discoverable with other plugin authoring docs.
- Extend the docs README index test to require the new proof-point runbook entry.
Files changed:
docs/README.md | 1 +
docs/plugins/external-proof-point-runbook.md | 211 +++++++++++++++++++++
.../cli/src/__tests__/docs-readme-index.test.ts | 1 +
3 files changed, 213 insertions(+)
Fusion-Task-Id: FN-6438
Fusion-Task-Lineage: 5c768d7e-4465-42ac-9535-f16dc42ab6d0
Enable mobile users to swipe across the agent detail tab strip.
- Restores horizontal touch panning on the agent detail tabs while preserving vertical pan behavior.
- Documents the mobile touch-action requirement next to the tab-strip CSS.
- Adds a mobile regression test covering pan-x touch-action and horizontal overflow.
Files changed:
packages/dashboard/app/components/AgentDetailView.css | 6 ++++++
.../__tests__/AgentDetailView.mobile-scroll.test.tsx | 15 +++++++++++++++
2 files changed, 21 insertions(+)
Fusion-Task-Id: FN-6450
Fusion-Task-Lineage: 7006af83-4490-4fe0-a357-7d19b7b522c0
Rescue the session reconnect SSE test by shutting down route-owned background work before temp cleanup.
- Isolate the focused API harness from TaskStore EventEmitter startup workers.
- Dispose API routes and scheduled AI session cleanup before deleting the test temp root.
- Remove session-reconnect from the dashboard quarantine ledger and vitest skip list.
Files changed:
.../src/__tests__/session-reconnect.test.ts | 22 ++++++++++++++++++----
packages/dashboard/vitest.config.ts | 6 ++++--
scripts/lib/test-quarantine.json | 5 -----
3 files changed, 22 insertions(+), 11 deletions(-)
Fusion-Task-Id: FN-6447
Fusion-Task-Lineage: a3fef28c-b92b-4f47-9181-a63bb31e4f48
Ensure the Secrets page root expands within the project content flex row on narrow screens.
- Let the Secrets view root flex to available width with a zero min-width.
- Preserve existing height and spacing behavior while avoiding intrinsic card-width collapse.
- Cover the mobile layout contract with a root-container CSS assertion.
Files changed:
packages/dashboard/app/components/SecretsView.css | 7 +++++++
.../dashboard/app/components/__tests__/SecretsView.mobile.test.tsx | 7 +++++++
2 files changed, 14 insertions(+)
Fusion-Task-Id: FN-6446
Fusion-Task-Lineage: 34f55d8e-ee57-432c-b1ce-165cc43333ee
Remove the CLI extension integration suite's hidden Vitest timeout override while keeping its explicit build-only allowance.
- Replace the file-wide 30s Vitest test and hook timeout override with a scoped comment documenting default caps.
- Derive the extension bundle path from the CLI root in the test file.
- Use a SQLite collision trigger instead of a TaskStore prototype spy and fix the agent delete tool payload.
- Clear the timeout appeasement allowlist now that the CLI test exemption is gone.
Files changed:
.../src/__tests__/extension-integration.test.ts | 25 ++++++++++++++++------
.../lib/test-timeout-appeasement-allowlist.json | 8 +------
2 files changed, 20 insertions(+), 13 deletions(-)
Fusion-Task-Id: FN-6436
Fusion-Task-Lineage: 670f41ea-f98f-400c-aa16-6a1cbb1e9bea