## Summary
Fixes the `session.subscribe is not a function` crash that breaks every
engine workflow step (Plan Review, Code Review) executed by an ACP agent
(Hermes ACP, Prime, Grok).
Introduced by the ACP custom-tools bridge (#3476) in the sense that it
made the gap reachable: ACP sessions stream through the bridging client
handler onto `callbacks` and never exposed the pi-style `subscribe()`
that the engine's AgentSession contract promises. Two production call
sites call it unconditionally:
- `packages/engine/src/executor/execute-workflow-step.ts` (workflow
steps — Plan Review / Code Review)
- `packages/engine/src/pi.ts` fallback wiring (`wireFallbackHooks`,
`promptableSession.subscribe`)
(`reviewer.ts` guards with `typeof session.subscribe === "function"`;
the other paths do not.)
Real-world symptom (verified on 0.77.0-beta.6 with a Hermes ACP agent):
every Plan Review attempt fails before producing a verdict:
```
[pre-merge] Workflow step failed: Plan Review
outcome: Plan Review failed before producing a verdict: session.subscribe is not a function
Plan Review provider retry budget exhausted
```
## Fix
Fix at the seam rather than guarding every call site forever:
- `AcpRuntimeAdapter.createSession` wraps the raw callbacks so each
forwarded text/thinking/tool event is **also** replayed to subscribers
as the pi-shaped event consumers parse (`message_update` +
`assistantMessageEvent.{text_delta,thinking_delta}`,
`tool_execution_start/end`)
- exposes `session.subscribe(handler)` returning an unsubscribe
function; dispose clears subscribers
- merges engine `taskEnv` into the subprocess env behind the existing
allow-list trust boundary (KTD6b preserved — only allow-listed keys
forward, task values win)
- original callback delivery unchanged; subscriber exceptions are
isolated so a faulty consumer can't break the streaming bridge
## Testing
- New regression tests in `runtime-adapter.test.ts` against the real
echo-agent fixture (`ACP_FIXTURE_RICH_PROMPT=1`):
- subscribe replays text/thinking/tool events as pi-shaped events
- unsubscribe stops delivery
- dual delivery: original `onText` callback AND subscriber both fire
- Full plugin suite: 255 passed (21 files)
- `tsc --noEmit` clean for the plugin
- changeset for `@runfusion/fusion` (patch, bugfix)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added session event subscriptions with unsubscribe support.
* Streamed text, thinking, and tool updates are replayed in a consistent
event format.
* Added support for task-specific environment values when launching
subprocesses.
* **Bug Fixes**
* Ensured streamed events reach both existing callbacks and subscribers.
* Isolated subscriber errors so they do not interrupt other handlers.
* Session cleanup now stops further subscription notifications.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
- resolves renamed review, complete, and archived workflow columns
before admitting late workspace repositories
- avoids workflow resolution when an existing repository or landing
state already decides the result
- syncs the bundled dependency-graph plugin with the current TaskCard
and scoped-storage APIs
## Test plan
- `pnpm --filter @fusion/engine exec vitest run
src/__tests__/workspace-add-repo-midflight.test.ts --silent=passed-only
--reporter=dot`
- `pnpm --filter @fusion/engine typecheck`
- `pnpm --filter @fusion-plugin-examples/dependency-graph test`
- `pnpm --filter @fusion-plugin-examples/dependency-graph build`
- `pnpm check:plugin-interop-drift`
- `pnpm check:lifecycle-columns`
- `pnpm check:changesets`
- `pnpm lint`
- `pnpm build`
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Late workspace repository acquisition now respects renamed review,
complete, and archived workflow columns.
* Tasks entering a blocked lifecycle state during acquisition are
refused safely.
* Existing safeguards for merge status, landed worktrees, legacy
terminal identifiers, and existing worktrees remain supported.
* Concurrent acquisition attempts now avoid duplicate worktrees and
preserve task updates reliably.
* **Improvements**
* Improved dependency-graph dashboard interoperability with current
task-card and storage APIs.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Three defects found while bringing up a container from a clean checkout:
- The dashboard's vite build (~5.7k modules) exceeded V8's default old-space on a
stock Docker Desktop VM and aborted the image build with "Ineffective
mark-compacts near heap limit" (exit 134). Raise the ceiling for that RUN only.
- The documented `-v fusion-home:/home/node/.fusion` mount seeded a root-owned
named volume over a path absent from the image, so embedded Postgres initdb hit
"Permission denied", the supervisor burned its 4 restarts, and the container went
unhealthy on first run. Pre-create the directory node-owned so a fresh named
volume inherits it; document that bind mounts still need a host-side chown.
- Drop the dependency-graph plugin's tsconfig path mapping for the taskStuck module
deleted in 2eae0b2507 / 29d94e0fa3.
Verified: full `docker build` from a clean export of this tree succeeds unpatched,
and a run against brand-new named volumes with no manual chown reaches health=healthy
with /api/health 200.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The dependency-graph plugin imported the deleted taskStuck util through its
dashboard interop shim, breaking the dashboard vite build. Remove the isStuck
gate from graph nodes, the taskStuckTimeoutMs prop threading, the taskStuck
module declaration from dashboard-interop.d.ts, and the stalled-card-as-stuck
test coverage. Covered by the existing remove-stuck-task-tagging changeset.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Refresh the Grok runtime README with the current flagship model invocation.
- Update the optional Grok ACP command to use grok-4.6.
- Document why the illustrative model identifier tracks the provider default.
Files changed:
plugins/fusion-plugin-grok-runtime/README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
Fusion-Task-Id: FN-9015
Fusion-Task-Lineage: 6dd04810-fe06-4462-82b9-5abb38e229c0
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
The released Grok CLI (v1.0.0, latest stable) does not recognize the
--no-auto-update flag and exits immediately with error: unexpected
argument. This causes Fusion to report 'ACP connection closed' when
spawning grok agent stdio.
buildGrokAcpArgs previously defaulted noAutoUpdate to true (via !==
false). Changed to opt-in (=== true) so the flag is only passed when
explicitly enabled. Updated acp-settings.test.ts assertions accordingly.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Bug Fixes**
- Grok ACP startup no longer disables automatic updates by default.
- Automatic update prevention is applied only when explicitly enabled in
settings.
- **Tests**
- Updated startup argument validation to reflect the revised default
behavior.
- **Documentation**
- Added release notes documenting the change.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com>
Both plugins declared dashboardViews only in their src/index.ts module.
PluginLoader.getCurrentManifestDashboardViews treats a successfully-read
manifest as authoritative and returns an empty list when the key is absent,
and getPluginDashboardViews only falls back to the module definition when the
manifest read fails. The module-level entries were therefore discarded and
neither view appeared on any nav surface -- header overflow, desktop sidebar,
or mobile More sheet all consume the same array from usePluginDashboardViews.
Mirror the manifest shape used by fusion-plugin-compound-engineering.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
## Summary
- remove an unavailable jest-dom type from the Todo plugin production
TypeScript build
- update the dashboard navigation fixture for the plugin-owned Todo
destination and root test id
## Test plan
- `corepack pnpm --filter @fusion-plugin-examples/todos build`
- `corepack pnpm --filter @fusion-plugin-examples/todos test`
- `FUSION_DASHBOARD_DEEP=1 corepack pnpm --filter @fusion/dashboard exec
vitest run app/components/__tests__/navigation-history.test.tsx
--project dashboard-app-quality-components-a --silent=passed-only
--reporter=dot`
- `corepack pnpm check:changesets`
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Restored CLI packaging for the bundled Todo Lists plugin.
* Made `AgentStore` available to bundled plugins at runtime.
* **Tests**
* Updated navigation coverage for Todo Lists dashboard views, overflow
placement, and ordering.
* Added coverage for opening and dismissing the Todo view through
browser history navigation.
* Improved validation of runtime exports.
* **Chores**
* Simplified test type configuration for the Todo Lists plugin.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
- export the browser-safe `@fusion/core/column-roles` subpath
- keep Vite/Vitest aliases ahead of broad `@fusion/core` aliases
- restore production dashboard builds after task undo classification
adopted shared column-role helpers
## Test plan
- `node scripts/check-no-node-only-core-imports-in-dashboard.mjs`
- `FUSION_DASHBOARD_DEEP=1 pnpm --filter @fusion/dashboard exec vitest
run app/utils/__tests__/taskRevert.test.ts --pool=threads
--maxWorkers=1`
- `pnpm --filter @fusion/core typecheck`
- `pnpm --filter @fusion/dashboard typecheck`
- `CI=true pnpm check:changesets`
- `pnpm build`
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Fixed dashboard build compatibility for browser-based environments.
* Improved reliability when importing column role functionality across
supported application components.
* **Refactor**
* Made column role utilities available through a dedicated browser-safe
entry point.
* **Chores**
* Updated development and test configurations to consistently resolve
the new entry point.
* Documented the browser-safe module classification and recorded the
release patch.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
> **Re-landing the second half of #3031.** That PR merged into #3029's
branch and only its first commit reached `main` — the arity rule
shipped, the interface rule and its finding did not. Verified on `main`:
the gate reports *"7 mirrored function(s)"* with no interface count, and
the dead prop below is still there.
## What
The arity rule covers exported functions. The same files also mirror
**interfaces**, which is the larger surface — six copies of
`PluginDashboardViewContext` alone.
**One direction only.** A mirror may declare *fewer* properties, and all
six do (6, 8, 7, 7, 3, 6 against the real nine) because a plugin mirrors
the fields it uses. Demanding equality would fail every plugin for not
using everything — which is how a check gets ignored and then deleted. A
property the real type **doesn't have** is the drift that matters: a
rename nobody propagated, where the plugin keeps compiling and reads a
field the host never sends.
## Its first interface run found a live one
```
dashboard-interop.d.ts:67 TaskCardProps.workflowStepNameLookup is not a property of the real TaskCardProps
```
Git history says it **was** one when FN-2466 and FN-7039 added this
threading. The dashboard removed it later; nothing propagated that to
the plugin's hand-written declaration. So the plugin built a lookup map
from `context.workflowSteps` on every render, threaded it through two
components, and handed it to a `TaskCard` with no such prop.
Deleted rather than exempted — a new gate shouldn't ship with a waiver
for its own first finding. Behaviour-preserving: the value never reached
anything.
## Measured on `main`
| check | result |
|---|---|
| population | **7 functions + 10 interfaces across 6 plugins**, all
matching after the deletion |
| control probe | phantom property **caught**; clean tree exits 0 |
| anti-vacuity | now also requires a non-zero *interface* comparison |
| gate's own suite | **5 → 8** |
| dependency-graph suite | 179 green; `tsc` clean |
| other five gates · census | green |
## Running total for this check
Three real drifts, none of which any other instrument reported:
1. `isTaskStuck` stuck at three parameters through the whole lane
conversion (#3003)
2. `taskStuckTimeoutMs?: number` vs the required `number | undefined` —
in **two independent authors'** declarations
3. `workflowStepNameLookup` outliving its removal from `TaskCard`
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>