## Summary
Fusion’s core runtime now treats PostgreSQL as the authoritative
metadata store without leaving current CLI, dashboard, desktop, or
engine composition roots uncompilable between stack layers. This is the
99-file foundation for the larger cutover: subsequent PRs migrate the
remaining consumers, plugins, and operator surfaces.
## Design decisions
- Runtime store construction fails closed when an asynchronous
PostgreSQL layer is unavailable; SQLite remains readable only at
explicit migration and identity-recovery boundaries.
- Project ownership is enforced across active, archived, workflow,
mission, analytics, and plugin-schema data.
- The small set of cross-package files in this layer are
compatibility-critical call sites required for a green intermediate
commit, not the complete consumer migration.
- Schema migration 0008 remains assigned to session-advisor state from
current `main`; mission lineage idempotency advances to 0009 so neither
invariant can be skipped.
## Validation
- All affected package typechecks pass: Core, Engine, Dashboard, CLI,
and Desktop.
- `pnpm test:gate` passes: 478 tests across the engine gate, PostgreSQL
core gate, and CLI workflow shape.
- The PR changes exactly 99 files.
## Stack
This is the base PR. Engine/dashboard, CLI/desktop/ops, plugins, and
docs/release follow as stacked PRs, each below 100 changed files.
Related: #2105
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* PostgreSQL is now the standard runtime backend, with embedded
PostgreSQL enabled by default.
* Added project-scoped storage for tasks, archives, chat sessions,
missions, knowledge pages, and operational data.
* Improved archived-task search, filtering, pagination, and restoration.
* Added safer plugin schema initialization with validation and project
isolation.
* Added PostgreSQL-backed workflow, mission, validator, and dashboard
capabilities.
* **Bug Fixes**
* Improved startup timeout cancellation and resource cleanup.
* Prevented cross-project data access and phantom reservation cleanup
errors.
* Ensured archived tasks remain read-only and asynchronous writes
complete reliably.
* Retired SQLite opt-out settings with clear startup errors.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
- reuse the dashboard command's backend-aware per-project `TaskStore`
cache during project-scoped plugin skill discovery
- obtain plugin state through `TaskStore.getPluginStore()` instead of
constructing bare SQLite-default `PluginStore` / `TaskStore` instances
- keep cached project stores alive for the dashboard process while still
stopping request-scoped plugin loaders
- add a regression covering the real Skills adapter callback and refresh
the dashboard test fixture with `getAsyncLayer()`
## Root cause
`GET /api/skills/discovered` resolved the project correctly, then
`getProjectScopedPluginSkills()` constructed new stores without an
`AsyncDataLayer`. After `VAL-REMOVAL-005`, that enters the physically
removed synchronous SQLite runtime and returns HTTP 500 even when
PostgreSQL health, projects, tasks, and both project engines are
healthy.
The existing route tests mocked the Skills adapter callback, so they did
not exercise this CLI wiring.
## Verification
- targeted dashboard regression: 1 passed, 91 skipped
- `pnpm lint`
- `pnpm --filter @runfusion/fusion typecheck`
- `pnpm --filter @runfusion/fusion build`
- `pnpm check:changesets --strict`
- `git diff --check`
Live Atlas validation against the migrated embedded PostgreSQL runtime:
- `/api/skills/discovered?projectId=proj_84f4645c2da64288`: HTTP 200, 36
skills
- `/api/skills/discovered?projectId=proj_7538a9dd46c24c5f`: HTTP 200, 36
skills
- local dashboard and Tailscale dashboard: HTTP 200
- controlled SIGTERM: launchd restarted the dashboard and both Skills
routes remained healthy
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Bug Fixes**
- Fixed dashboard project-scoped plugin-skill discovery in PostgreSQL
mode with safer store reuse/teardown and request-scoped plugin-loader
lifecycle.
- Improved dashboard cleanup to avoid duplicate concurrent store closes
and ensured proper shutdown behavior per root type.
- Made `fusion_runtime` role creation race-safe during concurrent
PostgreSQL migrations.
- **New Features**
- Added `persistRuntimeState` option to control whether plugin runtime
state changes are persisted.
- **Tests**
- Expanded dashboard and core hot-reload tests to verify scoped,
non-persistent runtime behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Use PostgreSQL workflow selections in the dashboard TUI, authoritative driver, and graph-runner adapter so migrated tasks cannot silently fall back to the coding workflow.
Route workflow selections, model lanes, goals, skills, and reliability reads through project-scoped async stores. Recover heartbeat agents parked against an unrelated project model and preserve workflow JSONB patches atomically.
Report source scans, per-table copy milestones, checksum phases, verification outcomes, and unambiguous failure or finalization status during first-boot and manual migrations.
## Summary
- Add `fusion-plugin-omp-runtime` so Fusion agents can run through
operator-installed **Oh My Pi (`omp`)** over the [Agent Client
Protocol](https://omp.sh/docs/acp) (`omp acp`).
- Wire staged/bundled install, Settings → Authentication card (enable +
binary path), model discovery (`omp models` → `omp-cli/*`), and MCP
eligibility for runtime id `omp`.
- Forward Fusion `systemPrompt` via ACP `session/new`
`_meta.systemPromptOverride`.
## How operators use it
1. Install/auth `omp` (credentials under `~/.omp`).
2. Enable **Oh My Pi — via omp ACP** in Settings → Authentication
(optional binary path).
3. Set agent **Runtime Source → OMP Runtime** (`runtimeHint: "omp"`), or
pick an `omp-cli/*` model when enabled.
## Known v1 gaps
- No Grok-style Fusion `fn_*` loopback tool bridge yet (operator MCP is
forwarded; in-process custom tools are not).
- Model is fixed at spawn (`omp --model … acp`); no mid-session Fusion
model switch.
## Test plan
- [x] `pnpm --filter @fusion-plugin-examples/omp-runtime test` (unit +
live ACP when `omp` is on PATH)
- [x] Auth routes: `POST /api/auth/omp-cli`, `GET
/api/providers/omp-cli/status`
- [x] Engine `runtimeSupportsMcp("omp")`
- [ ] Manual: enable card in dashboard, select OMP runtime on an agent,
run a short chat turn
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added Oh My Pi (OMP) CLI support as an ACP-backed runtime and model
provider, including model discovery and probing.
* Added dashboard auth/status controls to enable OMP, check readiness,
and configure the local binary path (with validation).
* Exposed OMP custom `fn_*` tools via an MCP loopback bridge, plus
optional filesystem capabilities and stricter tool permission gating.
* **Documentation**
* Added/expanded OMP runtime contract and integration docs (including
the ACP session/handshake flow).
* **Tests**
* Added Vitest coverage for settings wiring, provider status, model
discovery, runtime sessions, permissions, MCP bridging, and live
connectivity.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
- add asynchronous PostgreSQL parity to research commands and engine
execution paths
- persist Roadmap, Compound Engineering sessions, and WhatsApp state in
PostgreSQL
- harden cancellation, concurrency, reconnect, replay-claim, and
detached-promise behavior
- bundle the PostgreSQL-backed integration implementations in the
published CLI
This is PR 2 of 2 and is intentionally stacked on #2088. It contains 44
changed files; merge #2088 first, then retarget this PR to `main` if
GitHub does not do so automatically.
## Verification
- `pnpm check:changesets --strict`
- `pnpm lint`
- `pnpm test:gate`: 463 tests passed
- Compound Engineering plugin: 299 tests passed
- Roadmap plugin: 144 tests passed
- WhatsApp plugin: 27 tests passed
- research CLI: 18 tests passed
- `pnpm verify:fast`: all scoped typechecks, builds, CLI build, and boot
smoke passed
## Post-Deploy Monitoring & Validation
- deploy only after #2088 and verify schema migration `0002` is present
- monitor research cancellation, automation claims, agent execution,
plugin schema initialization, and unhandled rejections
- validate Roadmap ownership, Compound Engineering session recovery, and
WhatsApp reconnect/replay deduplication
- compare per-project plugin and workflow counts after cutover
- restore the pre-deploy backup for data rollback; avoid an in-place
schema downgrade
Closes the remaining PG-cutover partitioning gaps:
- getWorkflowSettingsProjectId resolves the bound AsyncDataLayer's central-
registry id first. In backend mode the SQLite stub's getProjectIdentity()
throws, so the old fallback ALWAYS keyed workflow_settings /
workflow_prompt_overrides by the rootDir path string — a namespace nothing
else reads, making workflow settings appear reset after cutover.
- Stamping is extracted into core stampMigratedProjectRows (tasks/archived
NULL->id, config ''->id, workflow_settings + workflow_prompt_overrides
rootDir-key->id, all guarded against clobbering per-project rows), shared by
startup-factory Step 5.5 and 'fn db migrate', which now resolves the
registered project by path after the copy and warns when unregistered.
- The task-id allocator and merge_queue are verified safe WITHOUT project
partitioning: task ids are a global PK, the per-prefix sequence scans are
intentionally global (only the per-project config floor can raise them), so
two projects sharing a prefix cannot mint duplicate ids. FNXC comments lock
the invariant; a cross-project PG regression test proves it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Migrate storage from SQLite to PostgreSQL — full dashboard cutover
Migrates Fusion's storage layer to the embedded PostgreSQL
`AsyncDataLayer` (the default backend) and **completes the
satellite-store + feature cutover** so every dashboard and Command
Center surface works in PG mode.
## Status — every surface works in embedded-PG mode
Verified live against a running embedded-Postgres dashboard (all
**200**, zero 5xx) and gate-tested (**23 files / 99 tests** on embedded
PG, plus engine-core 294 and ci-shape 63 in the blocking merge gate;
core/engine/cli/dashboard typecheck clean).
| Area | Surfaces | State |
|---|---|---|
| Satellite stores | workflows, todos, insights, research, missions,
goals, mailbox | ✅ |
| Views | artifacts, documents, evals | ✅ |
| Command Center | activity, productivity, team, tokens, tools,
**workflows**, **github**, **signals**, **plugin-activations**, **live**
(all 10) | ✅ |
| Run execution | insight generation, research run execution | ✅
(store-path; AI step needs a provider) |
| Live updates | SSE push for mission/research/insight events | ✅ |
| Workflow editing | create / update / delete / select (+ id counter) |
✅ |
| Engine | mission autopilot, incident-signal ingestion, regression
storm-guard, agent wake-on-message | ✅ |
| Core | tasks, agents, secrets, automations, memory, chat, usage, PRs,
git | ✅ |
## Approach
Each satellite store gets an `Async<Store>` wrapper exposing the sync
store's method names over the existing `async-*-store.ts` helpers;
`get<Store>Store()` returns a `Sync | Async` union; consumers `await`
(harmless on sync), and engine/CLI paths that can't convert use
`instanceof Sync` graceful fallback. Analytics aggregators branch on
`"ping" in dbOrLayer` to run schema-qualified raw SQL over `project.*`
(snake_case) in PG. Executors/orchestrators/autopilot are
await-converted to drive the union store; the async store wrappers
extend `EventEmitter` so SSE live-push fires in both backends.
Not-yet-ported capabilities degrade gracefully (never 500) and are
individually called out in commits.
## Sync with main
The branch is kept continuously merged with `main` (currently through
FN-7845, 2026-07-12); the earlier "final rebase deferred" note no longer
applies. Use **Create a merge commit** (or squash) to land it — GitHub's
rebase-merge cannot replay a merge-maintained branch.
## Residual Review Findings
Multi-agent code review of the PostgreSQL satellite-store ports (U1–U5)
applied 3 safe fixes (see `fix(review): apply autofix feedback`). The
following are **real but gated** — recorded here as follow-up work
rather than auto-applied. All are SQLite→PostgreSQL
**concurrency/atomicity regressions**: the sync stores were immune only
by SQLite's single-writer, single-threaded-handler execution; the async
ports open multi-await read-modify-write windows. **Reachability is low
today** because the execution engines that generate concurrent same-run
mutations (insight run executor, research orchestrator/dispatcher) are
`instanceof`-gated to sync mode in PG. No process-crash class survived
(all engine fallbacks correctly guard the sync store).
- **[P1] Research `appendResearchEvent` dual-write is non-atomic**
(`packages/core/src/async-research-store.ts`, corroborated: adversarial
+ reliability). The `research_run_events` insert (own transaction) and
the `run.events` jsonb update are separate writes — a crash between
them, or two concurrent appends, splits the table count from the jsonb
array. **Fix:** perform the seq-insert and the jsonb update in one
`layer.transactionImmediate`.
- **[P1] Research run terminal-reversion via stale full-row persist**
(`async-research-store.ts` `persistResearchRun`/`updateResearchStatus`).
Concurrent `PATCH /runs/:id/status` + `POST /runs/:id/events` can revert
a terminal run to `running` by overwriting the whole row, bypassing the
transition guard. **Fix:** scoped column `UPDATE`s with a `WHERE status
…` guard, or optimistic version column.
- **[P2] `updateResearchRun`/`updateInsightRun` read-then-write TOCTOU**
— concurrent PATCHes last-writer-wins on the lifecycle merge. **Fix:**
`SELECT … FOR UPDATE` / enclosing transaction.
- **[P2] `upsertRun`/`createRunOrThrowConflict` check-then-create race**
(`async-insight-store.ts`) — two callers can each create an "active"
run. **Fix:** partial unique index on `(projectId, trigger) WHERE status
IN ('pending','running')`.
- **[P3] `createResearchRetryRun` return-value divergence** — sync
returns the pre-update `queued` snapshot; async returns the reloaded
`retry_waiting` run (persisted state is identical). Pick one side for
cross-backend parity.
- **[P2/perf] Mission `getMissionWithHierarchy`/`getMissionHealth` N+1
fan-out** — O(milestones×slices) sequential round-trips hold one pool
slot per request; can starve the pool for large hierarchies. **Fix:**
batched/joined reads.
- **Testing gaps:** no PG-mode concurrency tests (interleaved
status/event mutations), no sync↔async parity assertion for the
lifecycle-error codes, and no mission status/health rollup parity test
vs the sync `MissionStore`.
~~Out of scope (deferred): AI run *execution* (insight/research) +
mission autopilot + live SSE mission events remain sync-gated/degraded
in PG mode.~~ **Since ported** — insight/research run execution, mission
autopilot, and SSE live push all run on the async layer now, which also
makes the concurrency findings above genuinely reachable; they remain
open follow-ups.
---
## Update — 2026-07-12: production-readiness hardening & live acceptance
Everything below landed on this branch since the description above was
written:
**Production blockers from review — fixed**
- `recoverStaleTransitionPending` ported to the async layer (backend
moves write + clear the crash-safe marker; startup/maintenance sweeps no
longer throw).
- Lost-update class fixed: `atomicWriteTaskJson`/`WithAudit` write
changed columns only (full-row upserts silently resurrected stale fields
across concurrent store instances — the "task stuck unplanned forever"
bug).
- First-boot **auto-migration**: booting the PG backend over a project
with a legacy `fusion.db` migrates it automatically (loud failure,
SQLite kept as backup), and the dashboard shows a one-time **"your data
was migrated" banner** with the backup paths and a Need-help Discord
link.
- `pg_dump`/`pg_restore` discovered from common install locations for
embedded-mode backups.
- The PG suite is part of the blocking merge gate (`test:pg-gate`).
**Multi-project isolation (PR #2007, merged into this branch)**
- `project_id` partition key on tasks / archived tasks / config,
`taskProjectScope` threaded through every scan/claim/count, per-project
config rows, layer bound to the project at startup.
- Review P1 follow-up: the shared cold-storage `archive.archived_tasks`
table is also partitioned and all archived-board reads/counts/searches
are scoped.
- Schema drift self-heal generalized to schema-qualified columns so
existing databases upgrade in place.
**Other changes**
- Node settings sync **removed** in PG mode (409
`settings-sync-disabled-postgres`) — nodes share state by connecting to
the same database; auth sync kept (per-machine file).
- Perf (review findings): `listTasks` pushes column filter + ORDER BY +
LIMIT/OFFSET into SQL; `getConversation` capped to the most recent 200
messages.
- Fixed a false "operator action required" pause-abort log fired on
every successfully auto-merged task.
**Live acceptance — PASSED (2026-07-12)**
A sandboxed instance (isolated HOME, embedded PG, real Opus executor)
ran a task through the complete cycle: create → triage (AI spec) →
execute → in-review → AI squash-merge landed on the project's `main` →
done. A write+read sweep of every data surface (settings, comments,
documents, attachments + artifact bridge + artifact edit, chat with real
generation, goals, missions, agent mail, secrets, workflows, memory, CC
analytics) was green on embedded PG.
**Known remaining work**
- The per-project `config` PK re-key has no upgrade path for
pre-isolation embedded-PG databases (needs a real `DROP
CONSTRAINT`/re-key migration; fresh databases are fine).
- `pg_dump`/`pg_restore` binaries are not yet bundled in release
artifacts (PATH/common-location discovery only).
- The satellite-store concurrency findings listed above.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Phil Larson <hello@phillarson.xyz>
Co-authored-by: fusion-merge <fusion-merge@local>
## Summary
Fixes shard 3 failures from runs 29258546612 + 29259574946 (FN-7936
drift).
## Fixes
### `package-config.test.ts` — stale TRANSITIVE_EXTERNALS entry
FN-7936 aliased `@fusion/core` to a runtime shim in bundled plugin
outputs; it's no longer a tsup external. Removed the stale allowlist
entry.
### `bundle-output.test.ts` — stale dashboard client hash ENOENT
**Root cause:** Two test files (`bundle-output.test.ts` +
`extension-integration.test.ts`) call
`buildCliWithRealDashboardAssets()` which triggers concurrent vite/tsup
builds. Vitest runs them in parallel (`pool: "forks"`, `fileParallelism:
true`). Without coordination, two builds clean and write `dist/client`
simultaneously, causing `ENOENT` on content-hashed chunk files.
**Fix (3 parts):**
1. **`workspace-tools.ts buildDashboardClient`** — `rm dist/client`
before vite build. Prevents stale content-hash references from previous
builds.
2. **`bundle-output-helpers.ts`** — atomic `mkdirSync` file lock around
`buildCliWithRealDashboardAssets()`. Winner builds; losers poll with
`Atomics.wait`, then re-check `hasBuiltDashboardAssets()`. On timeout,
**throws** (never builds without owning the lock).
3. Lock uses `Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0,
0, 500)` for sync sleep — no child process spawning.
## Verification
- Gate: exit 0 ✅
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved dashboard asset builds by removing stale files before
rebuilding.
* Prevented concurrent builds from producing incomplete or corrupted
dashboard assets.
* Added safeguards to detect stalled asset builds and fail with clearer
errors.
* **Tests**
* Updated package validation checks to reflect current runtime bundling
behavior.
* Improved reliability of CLI build-related test execution.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Fix bundled example plugins (dependency-graph, grok-runtime, roadmap, acp-runtime, compound-engineering) crashing on enable with "Cannot find package '@fusion/core'" by aliasing the private import to a self-contained runtime shim during CLI bundling.
- packages/cli/tsup.config.ts: drop @fusion/core from bundlePluginEntry's external list and alias it to the existing pluginSdkCoreRuntimeShim so bundled.js no longer references the private workspace package at runtime
- packages/cli/src/__tests__/bundle-output.test.ts: add a regression test asserting every staged bundled plugin's bundled.js contains no bare @fusion/core import/reference
- docs/PLUGIN_AUTHORING.md: document that bundled.js outputs must be self-contained and must not leak private @fusion/* workspace imports
- .changeset/fn-7936-bundled-plugin-fusion-core-external.md: add a patch changeset for @runfusion/fusion describing the fix
Files changed:
.changeset/fn-7936-bundled-plugin-fusion-core-external.md | 7 +++++
docs/PLUGIN_AUTHORING.md | 3 +++
packages/cli/src/__tests__/bundle-output.test.ts | 30 ++++++++++++++++++++++
packages/cli/tsup.config.ts | 9 +++++--
4 files changed, 47 insertions(+), 2 deletions(-)
Fusion-Task-Id: FN-7936
Fusion-Task-Lineage: a8a391b2-9441-4a7c-92bc-f1675e1a8a0d
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
## Summary
Fixes ALL failing shards from the latest full-suite run (29225946428)
AND adds a structural gate check to prevent the recurring mock-export
drift pattern that has caused every full-suite failure across rounds
1–9.
## What broke (run 29225946428, commit 504b0f8b0)
| Shard | Root cause | Tests fixed |
|---|---|---|
| **3 (CLI)** | `workflowValidateParams` (FN-7911) missing from
`@fusion/engine` mock | 8 files |
| **3 (CLI)** | `skill-sync.test.ts` — `fn_workflow_validate` missing
from engine-tools.md | 1 file |
| **4 (dashboard)** | 6 chat default settings keys missing from
description allowlist | 1 file |
| **1+2 (engine)** | `additionalSkillPaths` missing from
`buildSessionSkillContext` mocks (FN-1510/1511) | 10 tests |
| **1+2 (engine)** | heartbeat FN-7878 changed paused→error for generic
run failures | 1 test |
| **1+2 (engine)** | executor `updateTask` exact-match →
`objectContaining` (new fields) | 2 tests |
| **1+2 (engine)** | `connectMcpSessionTools` mock missing for pi.test
MCP forwarding | 1 test |
## Structural fix — `scripts/check-mock-completeness.mjs` (the "fix for
good")
**New gate check** added to `pnpm test:gate`. Statically validates every
hardcoded `vi.mock("@fusion/dashboard")` and `vi.mock("@fusion/engine")`
factory covers all named imports the source file uses. Runs in <0.2s, no
module evaluation.
**How it works:**
1. Extracts named exports from each barrel
(`packages/dashboard/src/index.ts`, `packages/engine/src/index.ts`)
2. For each test file with a hardcoded `vi.mock` factory (no
`importOriginal`/`importActual` spread):
- Resolves source files the test covers (static + dynamic imports,
convention mapping)
- Extracts what those source files named-import from the barrel
- Resolves spread helpers (e.g. `...workflowAuthoringEngineMock`) by
reading the helper's exported keys
- Reports any barrel exports that are named-imported by source but
absent from the mock
**Why this fixes the recurring pattern:** Every round 1–9 failure was a
new barrel export imported by source but missing from a test mock. This
check catches it at gate time, before merge — not after the full-suite
fails on main.
Also completed all 15 latent mock gaps the guard found on first run (9
dashboard + 6 engine), including expanding the centralized
`workflowAuthoringEngineMock` helper with all `extension.ts` named
imports.
## Verification
- Gate (with new check): exit 0 ✅
- CLI: 355/355 passed ✅
- Engine (6 fixed files): 250/250 passed ✅
- i18n + settings: verified ✅
- Mock completeness guard: ✅ (0 issues)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Documented a new non-destructive workflow validation tool that
performs a dry-run and returns typed validation errors.
* **Tests**
* Updated and strengthened CLI, dashboard, extension, and engine tests
with more accurate mock exports and more resilient assertions.
* Adjusted expectations for session/heartbeat and retry-related
behaviors.
* **Chores**
* Added an automated mock-completeness gate and integrated it into the
test quality gate to keep mocks aligned with available platform exports.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
**Structural fix** for the recurring full-suite failure pattern where a
new `@fusion/dashboard` barrel export is imported by CLI source code but
missing from the hardcoded `vi.mock("@fusion/dashboard")` factory in CLI
tests.
## What's new
### Gate check script:
`scripts/check-cli-dashboard-mock-completeness.mjs`
Added to the merge gate (`pnpm test:gate`). Statically validates that
every hardcoded `vi.mock("@fusion/dashboard")` factory in CLI tests
includes all `@fusion/dashboard` exports that the corresponding source
files import.
- Pure static analysis (regex + depth-aware brace tracking) — no module
evaluation, <0.1s
- Handles named imports (`import { foo } from "@fusion/dashboard"`) AND
namespace imports (`import * as dashboard from "@fusion/dashboard"` →
scans `dashboard.X` usages)
- Filters against the real barrel exports to avoid false positives from
typos
- Resolves test→source mapping by parsing static/dynamic imports in the
test file (not just naming convention)
**Result:** the next time someone adds `export { newFunc } from
"./mod.js"` to `dashboard/src/index.ts` and `cli/src/commands/daemon.ts`
imports it, the gate catches the missing mock before merge instead of
the full-suite failing on main.
### Completed all 9 incomplete CLI dashboard mocks
Added the missing exports identified by the check:
| File | Missing exports added |
|---|---|
| `daemon.test.ts` | `registerGithubTrackingHook` |
| `serve.test.ts` | `registerGithubTrackingHook` |
| `dashboard.test.ts` | `AttachTicketStore`, `CliInputAttributionLog`,
`CliConfirmAdvanceRegistry`, `CliRelaunchRegistry`,
`registerGithubTrackingHook` |
| `task.test.ts` | `registerGithubTrackingHook`, `GitLabClient`,
`resolveGitlabAuth`, `buildGitLabTaskProvenance`,
`isGitLabAlreadyImported`, `buildGitLabTaskDescription` |
| `extension-*.test.ts` (×4) | `GitLabClient`, `resolveGitlabAuth`,
`buildGitLabTaskProvenance`, `isGitLabAlreadyImported`,
`buildGitLabTaskDescription` |
| `task-command-github-import-tracking.test.ts` | Same GitLab exports |
These were latent issues — the mocks were incomplete but tests passed
because the missing exports weren't called during test execution. Any
test change that exercises those code paths would have broken.
## Why not `importActual` spread?
Tried converting daemon.test.ts to `vi.mock("@fusion/dashboard", async
(importOriginal) => { ... })` — fails because the barrel's `export *
from "./plugins/index.js"` transitively imports
`@agentclientprotocol/sdk` which isn't available at test evaluation
time. The static check approach avoids this entirely.
## Verification
- `pnpm test:gate`: exit 0 (includes new check)
- `pnpm lint`: exit 0
- CLI tests: daemon 21/21, serve 58/58, dashboard 91/91, task 149/149 ✅
- Gate script: `✅ CLI dashboard mock completeness: all hardcoded mocks
cover source imports.`
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Tests**
- Added automated validation to ensure CLI test mocks remain aligned
with available dashboard functionality.
- Updated test coverage setup so GitHub, GitLab, daemon, dashboard,
server, and task scenarios use complete dashboard mocks.
- Test verification now reports missing mocked functionality and blocks
the release gate when inconsistencies are detected.
- **Chores**
- Improved reliability and maintainability of automated verification for
CLI and dashboard integrations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Adds a best-effort, idempotent dashboard inbox notice announcing the upcoming embedded-Postgres storage migration, delivered once per project on the first engine start under the Fusion 0.59.x release line.
- New `deliverPostgresMigrationNoticeIfNeeded` in `@fusion/engine` (`postgres-migration-notice.ts`) builds and sends a `system` -> `user` inbox message via `MessageStore`, gated to version `0.59.x` by `isPostgresMigrationNoticeVersion`
- Idempotency via existing inbox message `metadata.kind = "postgres-migration-notice"` marker (no new settings key or table), so restarts never duplicate the notice
- Delivery is fully best-effort: any `MessageStore` failure is caught, logged as a warning, and never blocks or fails `ProjectEngine.start()`
- `ProjectEngine.start()` invokes the notice after runtime start, using an injected `cliPackageVersion` threaded from the CLI layer through `EngineManagerOptions` / `ProjectEngineOptions` so the engine never imports CLI/dashboard code directly
- `daemon.ts`, `dashboard.ts`, and `serve.ts` resolve the published `@runfusion/fusion` version via `getCliPackageVersion` / `isUnresolvedCliPackageVersion` and pass it into `ProjectEngineManager`
- Exported new symbols (`POSTGRES_MIGRATION_HELP_URL`, `POSTGRES_MIGRATION_NOTICE_KIND`, `deliverPostgresMigrationNoticeIfNeeded`, `isPostgresMigrationNoticeVersion`, related types) from `@fusion/engine`, and `isUnresolvedCliPackageVersion` from `@fusion/dashboard`
- New unit tests covering version matching and single-delivery/idempotency behavior
- Docs updated (`docs/agents.md`, `docs/dashboard-guide.md`) to describe the one-time notice and its dedup key
- Changeset added for `@runfusion/fusion` (minor, feature)
Files changed:
.changeset/fn-7879-postgres-migration-inbox-notice.md | 7 ++
docs/agents.md | 1 +
docs/dashboard-guide.md | 1 +
packages/cli/src/commands/daemon.ts | 6 +-
packages/cli/src/commands/dashboard.ts | 5 +
packages/cli/src/commands/serve.ts | 6 +-
packages/dashboard/src/index.ts | 2 +-
packages/engine/src/__tests__/postgres-migration-notice.test.ts | 140 +++++++++++++++++++++
packages/engine/src/index.ts | 9 ++
packages/engine/src/postgres-migration-notice.ts | 107 ++++++++++++++++
packages/engine/src/project-engine-manager.ts | 6 +
packages/engine/src/project-engine.ts | 12 ++
12 files changed, 299 insertions(+), 3 deletions(-)
Fusion-Task-Id: FN-7879
Fusion-Task-Lineage: 201877e5-6bdc-4168-a8ac-ae0e50ec8308
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Move redactSecrets to the log/warn/error entry points so both the
recorded history (served over /system/logs) and the TUI/console output
are masked — previously only the stored entry was redacted while the raw
message still printed to the terminal. Add assertions for both the
console and TUI-target output paths.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- pnpm dev / new pnpm start default to the dashboard command
- fn dashboard (and bare fn/fusion/npx, incl. packaged binaries) now runs
supervised by default via an attached foreground child (TUI-safe);
--no-supervise opts out; FUSION_RESTART_EXIT_CODE=86 = intentional restart
- New /api/system routes: info, restart, rebuild jobs with SSE output,
engine restart, agents restart-all, plugins reload-all, log tail
- System tab: rebuild & restart (source checkouts only, hidden elsewhere),
restart server/engine/agents, backup DB, live server logs, copy
diagnostics, report bug; new Plugins tab reusing PluginManager
- Desktop restart via Electron app.relaunch(); DashboardLogSink now keeps a
bounded history + listener feed for the log viewer
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Debug org agents error state recovery regression: durable heartbeat-managed
agents with a non-recoverable error (permanent/credential/model-access/
config, not stale-worktree/module-resolution) were previously left
indefinitely in bare `state:"error"` with no operator-visible reason,
and CLI agent inspection tools did not surface error/pause diagnostics.
- Timer path (`HeartbeatMonitor`) and run-entry recovery now classify
non-recoverable durable heartbeat errors and park the agent `paused`
with `pauseReason:"error-unrecoverable"` instead of restart-looping or
sitting in `error` forever.
- `SelfHealingManager` mirrors the same non-recoverable classification in
its recovery sweep, parking with the same reason/metadata and skipping
the exhausted/next-retry gates for that terminal bucket.
- New `agent:error-parked-unrecoverable` run-audit event type emitted by
both the heartbeat and self-healing paths (ids/counts/outcomes-only
metadata).
- `fn_agent_show` now prints `Last Error`, `Pause Reason`, and a compact
`Error Recovery` counter line; `fn_list_agents` prints the same
diagnostics only for agents currently in `error`/`paused`.
- Updated `AGENTS.md`, `docs/agents.md`, and `docs/architecture.md` to
document the new terminal-park behavior and CLI diagnostics surface.
- Added a changeset (`@runfusion/fusion` patch) describing the
operator-facing fix.
Files changed:
.changeset/fn-7859-org-agent-error-diagnostics.md | 7 ++
AGENTS.md | 2 +-
docs/agents.md | 3 +-
docs/architecture.md | 4 +-
packages/cli/src/__tests__/extension.test.ts | 68 ++++++++++++++++
packages/cli/src/extension.ts | 64 +++++++++++++++
.../src/__tests__/heartbeat-error-recovery.test.ts | 47 ++++++++++-
packages/engine/src/__tests__/self-healing.test.ts | 94 ++++++++++++++++++----
packages/engine/src/agent-heartbeat.ts | 71 +++++++++++++++-
packages/engine/src/run-audit.ts | 1 +
packages/engine/src/self-healing.ts | 46 +++++++++--
11 files changed, 375 insertions(+), 32 deletions(-)
Fusion-Task-Id: FN-7859
Fusion-Task-Lineage: 09b2035d-e8a0-438f-b1ab-1b0048b35c76
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Issue #2015: product-code executor tasks were repeatedly routed to a
liaison-only agent because every routing path gated only on the coarse
role field, and several binding primitives had no guard at all.
- Add runtimeConfig.assignmentPolicy ("auto" | "explicit-only" | "none");
"none" can never be bound to implementation tasks by ANY path — no
override bypasses it (the liaison guarantee)
- Route every binding surface through one shared evaluator
(evaluateImplementationTaskBind): claimTaskForAgent, the previously
unguarded checkoutTask/assignTask primitives, selectNextTaskForAgent
(including the in-progress re-selection loop), scheduler auto-assign
pool, heartbeat inbox/auto-claim, fn_delegate_task, CLI agent-id
validation, and dashboard assign/checkout/inbox routes
- Lock project isolation with a regression test: a foreign-project
agent id is rejected by every binding primitive
- Expose Assignment Policy in Agent Detail settings; document in
docs/agents.md; add changeset
Fusion-Task-Id: FN-7851
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Update the TUI splash tagline to match the README's current product positioning, with matching test and changeset.
- Changed FUSION_TAGLINE in packages/cli/src/commands/dashboard-tui/logo.ts from "multi node agent orchestrator" to "software factory", with an FNXC comment explaining the rationale
- Updated the dashboard TUI smoke test assertion to expect "software factory" instead of the old tagline
- Added a patch changeset documenting the tagline change
Files changed:
.changeset/fn-7850-tui-tagline.md | 7 +++++++
packages/cli/src/commands/dashboard-tui/__tests__/app.test.tsx | 2 +-
packages/cli/src/commands/dashboard-tui/logo.ts | 6 +++++-
3 files changed, 13 insertions(+), 2 deletions(-)
Fusion-Task-Id: FN-7850
Fusion-Task-Lineage: d8517d7c-f7dd-43af-bb57-0092c7339aad
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Replace one-shot grok -p JSON with native grok agent stdio (ACP) for realtime
streaming, tool visibility, and multi-turn sessions. Vendor the ACP client
into fusion-plugin-grok-runtime, forward Fusion fn_* tools and operator MCP,
stage Fusion skills via --plugin-dir, authenticate per xAI headless docs, and
align project chat manager store resolution so Grok chat sessions can send.
Video was registrable but effectively unusable, and HTML/PDF deliverables
had no first-class path from agents to the gallery.
- media route now serves HTTP byte ranges (Accept-Ranges, 206 +
Content-Range, 416 on unsatisfiable) so <video>/<audio> seeking works
and Safari plays media at all
- video attachments (mp4/webm/mov, 100MB cap vs 5MB for other types)
bridge into the artifact registry like images; multer transport ceiling
raised to 100MB with per-type caps enforced in the store
- fn_artifact_register path payloads are signature-validated for video
(ftyp box / EBML header) and PDF (%PDF- prefix), mirroring images
- HTML doc artifacts (mimeType text/html) render as live sandboxed
iframe previews by default in the doc viewer, with a Preview/Source
toggle and the same FileEditor edit mode
- executor/heartbeat/planning prompts and tool descriptions now cover
the full type matrix: images, videos, audio, HTML mockups, PDFs, and
markdown docs, each with the registration recipe
Verified live: range requests (200/206/416) via curl, an ffmpeg-generated
mp4 playing to completion in the gallery lightbox, and an interactive
HTML mockup rendering in the sandboxed preview.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Agents could never get screenshots/wireframes/mocks into the Artifacts view:
fn_artifact_register was gated on assignedAgentId (never set in default
ephemeral mode), the only image payload source was inline base64, and no
prompt ever told agents to register visual deliverables.
- always expose fn_artifact_register to executor sessions ("executor" author
fallback), resolve relative paths against the task worktree, and default
taskId to the executing task (heartbeat task lane too)
- add a `path` payload source: file read with 50MB cap, extension MIME
inference, PNG/JPEG/GIF/WebP signature + SVG sniff validation, persisted
through managed artifact storage
- executor/heartbeat/planning prompts + engine-tools reference now instruct
agents to register screenshots, wireframes, mockups, and recordings
- new ArtifactsGallery: Images/Docs/PDFs/Videos/Audio/Other category sections
and filter chips, visual tile grid + lightbox, embedded PDF viewer, audio
player rows, download rows; mobile-responsive down to the 768px breakpoint
- doc artifacts open a full viewer rendered as markdown by default with an
in-place edit mode using the shared CodeMirror FileEditor; persisted via new
GET/PATCH /api/artifacts/:id + TaskStore.updateArtifact and live-refreshed
through the new artifact:updated SSE event
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- store.ts: guard parseStepsFromPrompt in listTasks and searchTasks too, so one
unreadable PROMPT.md can't reject the Promise.all and 500 the whole board
list/search (CodeRabbit). Matches the getTask fallback.
- update-check.ts: isHomebrewInstall now resolves symlinks and matches the real
Cellar/opt install roots, fixing Intel-macOS Homebrew detection that only
checked /usr/local/Homebrew/ (brew's repo dir) and would have shown npm/sudo
guidance instead of `brew upgrade` (CodeRabbit).
- task-detail-prompt-resilience.test.ts: extend to assert the invariant across
all surfaces — listTasks(slim)/searchTasks, reopen-to-todo moveTask
(resetPromptCheckboxes), and deleteTask — not just getTask/updateTask/archive
(CodeRabbit; Surface Enumeration rule).
- serve.test.ts: add SIGINT/SIGTERM exit-code assertions (130/143) so the serve
path's POSIX exit contract can't regress independently of daemon (CodeRabbit).
- update-check.test.ts: add Intel-Homebrew remediation test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses three user-reported bugs:
- API 500 diagnosability: rethrowAsApiError now preserves the original error
as Error `cause` and the /api boundary logs stack + cause for 5xx, so the
opaque "task write API returns 500 for every task" failures are traceable
(client body stays generic in production).
- In-app "Update now": detect EACCES/EPERM install failures and return
actionable remediation (sudo fn update / reinstall without sudo / brew
upgrade) instead of raw npm stderr; do not retry --force for this class.
- Daemon restart: `fn daemon` and `fn serve` exit 128+signal (SIGTERM=143,
SIGINT=130) on signal-initiated shutdown so Restart=on-failure restarts a
memory-pressure kill. Interactive `fn dashboard` TUI intentionally unchanged.
Adds regression tests (update-check EACCES/EPERM, daemon exit codes) and three
@runfusion/fusion patch changesets.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closes the durability gap behind "grok chat returns empty replies". In a source
checkout the dashboard resolved the staged CLI tsup bundle
(packages/cli/dist/plugins/<id>/bundled.js), which resolvePluginEntryPath prefers
verbatim with no freshness check. The FN-7779 dev prebuild rebuilds each plugin's
own plugins/<id>/dist but never the staged tsup bundle, so a source-only plugin
fix ran stale until a manual `pnpm build`.
getCandidatePluginDirs now probes the live workspace source dir (<repo>/plugins/<id>)
before the staged bundle, so dev loads the freshness-checked live plugin (dist-vs-src),
self-healing even when the prebuild is skipped. The global-staged dir stays first,
so published installs (no workspace dir) are unaffected — asserted by the retained
global-install regression test plus a new source-checkout preference test.
Verified on a live dashboard: grok chat streams text and the loader now writes its
reload copies under plugins/fusion-plugin-grok-runtime/dist, not the staged bundle.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two failure modes wedged the PR-based merge flow (dormant under the
current direct-merge config, but the rollback target):
1. Empty-diff branches ("No commits between ...") were marked `failed`,
pinning the serial merge slot + file leases on a task that is a
legitimate no-op. Now finalized as a terminal DONE via
`finalizeNoOpMergeTask`, mirroring the engine's canonical
`noOpResult` decision (merger-ai.ts).
2. A stale-base PR that GitHub reports CONFLICTING never becomes
mergeable on its own (nothing in the PR path rebases the head), so
`awaiting-pr-checks` waited unbounded — no escape, because the PR
path never incremented `mergeRetries`. Now each conflicting poll
counts against `mergeRetries`, so the existing
`getInReviewStallReason` "merge-retries-exhausted" escape disposes
the task after `maxAutoMergeRetries` cycles. Pending/behind PRs
still wait (checks legitimately run; "behind" is fixed by rebase).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Grok stale-dist bug was possible because the dev/build path never
refreshed plugin dist:
- The `client` prebuild (default `pnpm dev dashboard`) rebuilt only
@fusion/core + @fusion/engine + @fusion/dashboard, never plugins.
- The FN-6638 stale-dist startup warning only scanned packages/, never
plugins/, so a source-ahead plugin dist ran phantom-old with no warning.
Changes:
- build-workspace.mjs: add `--plugins-only` to plan/build just the plugins
that changed, reusing the existing content-hash skip cache (cheap no-op when
unchanged).
- scripts/dev-prebuild-client.mjs: new orchestrator — fast core/engine/
dashboard build, then incremental changed-plugin rebuild. The `client`
prebuild now runs this single cross-platform command.
- dist-freshness.mjs: scan plugin roots (plugins/, plugins/examples/) so a
stale plugin dist is warned like a stale package dist; the warning names the
plugin dir.
Verified: --plugins-only plans only plugins, skips unchanged on the second
run, and re-plans exactly the one plugin whose source changed. All script and
CLI lib tests pass.
Fusion-Task-Id: FN-7779
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes plugin skills silently disappearing when the fn daemon is started outside the project that enabled the contributing plugin, by making skill resolution project-aware instead of scoped to the daemon's root PluginLoader.
- getPluginSkills now resolves per requesting rootDir against project_plugin_states rather than the daemon-root PluginLoader scope
- Plugins skipped as disabled are now logged at load time for visibility
- Wired the new project-aware resolution through dashboard.ts, serve.ts, and daemon.ts CLI commands
- Added regression coverage in plugin-loader.test.ts and skills-adapter.test.ts
- Documented the project-scoped behavior in docs/PLUGIN_AUTHORING.md and docs/agents.md
- Added a patch changeset for @runfusion/fusion
Files changed:
.changeset/fn-7778-plugin-skills-project-scope.md | 7 +++
docs/PLUGIN_AUTHORING.md | 2 +
docs/agents.md | 2 +-
packages/cli/src/commands/daemon.ts | 68 +++++++++++++++++++--
packages/cli/src/commands/dashboard.ts | 71 ++++++++++++++++++++--
packages/cli/src/commands/serve.ts | 68 +++++++++++++++++++--
packages/core/src/__tests__/plugin-loader.test.ts | 69 +++++++++++++++++++++
packages/core/src/plugin-loader.ts | 29 ++++++---
.../dashboard/src/__tests__/skills-adapter.test.ts | 29 +++++++++
packages/dashboard/src/skills-adapter.ts | 19 ++++--
10 files changed, 337 insertions(+), 27 deletions(-)
Fusion-Task-Id: FN-7778
Fusion-Task-Lineage: 5d9a8ff2-ed0e-4859-bf9c-a16f715b081d
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Broadens regression coverage for the npm-release update-check invariant so it holds across every consuming surface, not just the reported repro.
- Replace the update-check route/service semver spot-checks with a parametrized case matrix (equal, newer, older, prerelease/build metadata, short/long version segments) to close false-positive/false-negative gaps.
- Add dedicated route-level tests asserting the update-check API route surfaces the same invariant.
- Add CLI update command tests covering notification rendering across version-comparison cases.
- Add desktop native update-check tests covering the same invariant on the desktop shell.
- Add dashboard useUpdateCheck hook tests verifying consistent notification behavior for the hook consumers.
Files changed:
packages/cli/src/commands/__tests__/update.test.ts | 59 ++++++++++++++++++
.../app/hooks/__tests__/useUpdateCheck.test.ts | 25 ++++++++
.../src/__tests__/update-check-route.test.ts | 71 ++++++++++++++++++++++
.../dashboard/src/__tests__/update-check.test.ts | 42 +++++++------
packages/desktop/src/__tests__/native.test.ts | 27 ++++++++
5 files changed, 206 insertions(+), 18 deletions(-)
Fusion-Task-Id: FN-7762
Fusion-Task-Lineage: 6ab34312-fb4e-487a-aefc-2ab133bf79af
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Packaged fn serve/daemon/dashboard hosts previously failed with a misleading missing-API-key error for grok-cli agents even though the operator was already logged in via the Grok CLI. This fixes routing so those hosts eagerly ensure the bundled Grok Runtime plugin is installed/loaded before session creation, and no longer silently falls back to the key-requiring direct endpoint when no key is visible.
- Eagerly ensure the bundled fusion-plugin-grok-runtime in serve, daemon, and dashboard commands before loadAllPlugins() so runtime id "grok" is available on fresh installs without manual plugin-settings setup.
- agent-session-helpers.ts: deriveGrokRuntimeHintForNoVisibleKey now throws an actionable error (naming both remediations: install/enable the Grok CLI runtime plugin, or set GROK_API_KEY) instead of silently falling through to the key-requiring pi/openai-completions path when the runtime can't be loaded.
- Update docs/grok-cli-contract.md to document the FN-7761 packaged-host wiring and new no-silent-fallback behavior.
- Add regression tests for the packaged bootstrap behavior and bundled-plugin install path.
- Add changeset for @runfusion/fusion (patch, category: fix).
Files changed:
.changeset/fn-7761-grok-cli-packaged-routing.md | 7 +++++
docs/grok-cli-contract.md | 19 +++++++++----
.../__tests__/grok-runtime-bootstrap.test.ts | 31 ++++++++++++++++++++++
packages/cli/src/commands/daemon.ts | 17 +++++++++++-
packages/cli/src/commands/dashboard.ts | 20 +++++++++++++-
packages/cli/src/commands/serve.ts | 19 +++++++++++--
.../__tests__/bundled-plugin-install.test.ts | 17 ++++++++++++
.../src/__tests__/grok-runtime-routing.test.ts | 17 ++++++------
packages/engine/src/agent-session-helpers.ts | 15 +++++++++--
9 files changed, 142 insertions(+), 20 deletions(-)
Fusion-Task-Id: FN-7761
Fusion-Task-Lineage: 3be5f054-965c-4e8a-ad91-6e61d4dc4a42
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Bumps the pi SDK dependencies to 0.80.6 across the CLI, dashboard, engine, and pi-claude-cli packages, and adapts the Claude CLI thinking-effort mapper for the new `max` ThinkingLevel.
- Bump @earendil-works/pi-ai and @earendil-works/pi-coding-agent from ^0.80.5 to ^0.80.6 in packages/cli, packages/dashboard, packages/engine, and packages/pi-claude-cli (dependency/peerDependency/devDependency entries)
- Regenerate pnpm-lock.yaml for the new SDK versions
- Map the new `max` ThinkingLevel in packages/pi-claude-cli/src/thinking-config.ts: non-Opus models downgrade to `high` (effort max unsupported), Opus models map to `max`
- Extend packages/pi-claude-cli/src/__tests__/thinking-config.test.ts with coverage for the `max` level
- Add .changeset/fn-7755-pi-sdk-bump.md (patch) documenting the SDK bump
Files changed:
.changeset/fn-7755-pi-sdk-bump.md | 7 ++
packages/cli/package.json | 4 +-
packages/dashboard/package.json | 2 +-
packages/engine/package.json | 4 +-
packages/pi-claude-cli/package.json | 8 +-
.../src/__tests__/thinking-config.test.ts | 12 +++
packages/pi-claude-cli/src/thinking-config.ts | 10 ++-
pnpm-lock.yaml | 92 +++++++++++-----------
8 files changed, 82 insertions(+), 57 deletions(-)
Fusion-Task-Id: FN-7755
Fusion-Task-Lineage: f6a9084b-dfb9-4ad5-bd99-4627dae4c666
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Close leaked TaskStore/AgentStore handles and retry lock errors across fn research, settings import, agent export, git, and project CLI commands.
- fn research create/list/show/export/cancel/retry now close their resolved store on every exit path except the intentional fire-and-forget --wait-for-completion-less create, which stays open to avoid truncating the background run
- fn settings import retries importSettings through a momentary database-is-locked window and closes the store before every process.exit()
- fn agent export closes both the project TaskStore and the AgentStore it opens on every exit path, including the no-agents-to-export guard
- fn git status/fetch/pull/push and fn agent export switch to a path-only project resolution helper so no cached, never-closed TaskStore is left behind for these read/write-nothing-to-board commands
- fn project list/show compute per-project task counts against an uncached TaskStore that is now closed after every call, and the count read retries a momentary lock instead of silently reporting zero tasks
- Adds dedicated lock-retry regression tests for each touched command plus a changeset documenting the fix
Files changed:
.changeset/fn-7740-cli-cmd-lock-retry.md | 7 +
docs/cli-reference.md | 28 +++
.../__tests__/agent-export-lock-retry.test.ts | 122 ++++++++++
.../src/commands/__tests__/git-lock-retry.test.ts | 129 +++++++++++
packages/cli/src/commands/__tests__/git.test.ts | 12 +
.../commands/__tests__/project-lock-retry.test.ts | 195 ++++++++++++++++
.../cli/src/commands/__tests__/project.test.ts | 8 +
.../commands/__tests__/research-lock-retry.test.ts | 248 ++++++++++++++++++++
.../cli/src/commands/__tests__/research.test.ts | 16 +-
.../__tests__/settings-import-lock-retry.test.ts | 170 ++++++++++++++
.../src/commands/__tests__/settings-import.test.ts | 34 +++
packages/cli/src/commands/agent-export.ts | 67 ++++--
packages/cli/src/commands/git.ts | 18 +-
packages/cli/src/commands/project.ts | 40 +++-
packages/cli/src/commands/research.ts | 254 ++++++++++++++-------
packages/cli/src/commands/settings-import.ts | 61 ++++-
16 files changed, 1284 insertions(+), 125 deletions(-)
Fusion-Task-Id: FN-7740
Fusion-Task-Lineage: 5c572332-b81c-4e16-9748-ce8639f53ff3
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Audit non-task.ts CLI command families for store-leak / no-retry-on-lock gaps and fix branch-group.ts and pr.ts to match the FN-7731 retryOnLock + closeProjectStore pattern.
- Add retryOnLock handling (honoring FUSION_CLI_LOCK_RETRY_MS) around board DB access in branch-group.ts and pr.ts so a locked store retries and exits promptly instead of hanging.
- Ensure both cached and uncached CWD-fallback project stores are closed on every exit path (success, error, early return) to stop store leaks.
- Extend project-context.ts with shared helpers used by both commands.
- Add regression tests: branch-group-lock-retry.test.ts and pr-lock-retry.test.ts, plus additional coverage in branch-group.test.ts.
- Document the new lock-retry behavior in docs/cli-reference.md.
- Add a patch changeset for @runfusion/fusion describing the user-facing fix.
Files changed:
.changeset/fn-7738-cli-cmd-lock-retry.md | 7 +
docs/cli-reference.md | 11 +
.../__tests__/branch-group-lock-retry.test.ts | 221 ++++++++
.../src/commands/__tests__/branch-group.test.ts | 10 +
.../src/commands/__tests__/pr-lock-retry.test.ts | 226 ++++++++
packages/cli/src/commands/branch-group.ts | 389 +++++++++-----
packages/cli/src/commands/pr.ts | 575 +++++++++++++--------
packages/cli/src/project-context.ts | 26 +
8 files changed, 1122 insertions(+), 343 deletions(-)
Fusion-Task-Id: FN-7738
Fusion-Task-Lineage: 0bbc8fa2-c4f9-49ed-adb6-7dab57eaee13
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>