bc348345a469e07cb4e2cd008a87fddbb725e79a
986 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8e4514e585 |
fix: key workflow settings by the central project id and stamp all partitioned tables on both migration paths
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> |
||
|
|
c15c78feeb |
feat: migrate storage from SQLite to PostgreSQL (#1793)
# 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> |
||
|
|
c3c3861efa |
fix: stale TRANSITIVE_EXTERNALS + dashboard dist/client clean + cross-worker build lock (round 12) (#2065)
## 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 -->
|
||
|
|
281bb05161 |
FN-7936: alias @fusion/core to a runtime shim in bundled plugin outputs
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> |
||
|
|
f7e942e6f4 |
fix: resolve all full-suite failures + add structural mock-completeness gate check (round 10) (#2040)
## 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
|
||
|
|
c8999369c3 |
feat: add gate check for CLI dashboard mock completeness — prevents recurring full-suite barrel-export drift (#2035)
## 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 -->
|
||
|
|
4ea5dd6739 |
fix: CLI shouldSuperviseDashboard mock + getCliPackageVersion/isUnresolvedCliPackageVersion + executeRequeueLoopCount + i18n buttonShort (round 9) (#2034)
## Summary Fixes shard 3 (CLI) + shard 4 (i18n) failures from full-suite run 29223788088. ## Fixes ### CLI (shard 3) — 5 files, ~140 tests - **`bin.test.ts`** — `bin.ts:851` now imports `shouldSuperviseDashboard` and `bin.ts:865` imports `runDashboardSupervised` from `./commands/dashboard.js`. Test mock only exported `runDashboard`. Missing `shouldSuperviseDashboard` → TypeError → caught → `process.exit:1`. Added `shouldSuperviseDashboard: vi.fn(() => false)` + `runDashboardSupervised` to mock + `commandMocks`. - **`daemon/serve/dashboard.test.ts`** — `@fusion/dashboard` barrel (index.ts:115) re-exports `getCliPackageVersion`, `isUnresolvedCliPackageVersion`, `resolveCliPackageVersionInfo`. Added all 3 to each file's `@fusion/dashboard` mock. - **`task.test.ts`** — `executeRequeueLoopCount: 0` added to TaskResetField set by recent commit; retry test assertions needed the new field in both `mockUpdateTask` expectations. ### i18n (shard 4) — 4 locale files - **`settings.reset.buttonShort`** missing from zh-TW, fr, es, ko (zh-CN already had it). ### Engine (shards 1+2) — already fixed in PR #2025 (merged on main) - Run 29223788088 was at commit `b85a6b866` (pre-PR-2025-merge), so engine + i18n `storageMigrationNotice` failures visible in that run are already resolved on main. ## Verification - daemon: 21/21 ✅ | serve: 58/58 ✅ | dashboard: 91/91 ✅ | task retry: 5/5 ✅ - i18n parity + gate-coverage: 7/7 ✅ - Gate (`pnpm test:gate`): exit 0 ✅ - `bin.test.ts`: cannot verify locally (`@agentclientprotocol/sdk` not installed locally; CI resolves from lockfile). Mock exports verified against `dashboard.ts` source. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved task retry recovery by correctly resetting execution counters. * **Localization** * Added support for shorter reset-button labels in Spanish, French, Korean, and Traditional Chinese. * **Tests** * Updated command and startup checks to reflect current dashboard and version-handling behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
1ea185daa5 |
FN-7911: add workflow validate dry-run command, tool, and API route
Adds a non-mutating `fn workflow validate` dry-run path across CLI, agent tools, and dashboard API so custom workflow IR can be checked before create/update. - Add `packages/cli/src/commands/workflow.ts` implementing `fn workflow validate <id> | --file <path>` with JSON/text output, wired into `bin.ts`. - Add `fn_workflow_validate` agent tool (`agent-tools.ts`, `index.ts`) reusing the existing parseWorkflowIr/trait/code-node/column-agent validation used by create/update, performing no persistence. - Add `POST /api/workflows/validate` route in `register-workflow-routes.ts` plus dashboard route test coverage. - Extend heartbeat tool-gating/exposure tests and gating classifications to include `fn_workflow_validate` alongside the other workflow tools. - Update CLI/agent extension docs (`docs/cli-reference.md`, `docs/agents.md`, `docs/workflow-steps.md`, fusion skill references) to document the new command/tool. - Add changeset `.changeset/fn-7911-workflow-validate.md` (minor) describing the new capability. Files changed: .changeset/fn-7911-workflow-validate.md | 7 ++ docs/agents.md | 5 +- docs/cli-reference.md | 13 ++ docs/workflow-steps.md | 3 +- packages/cli/skill/fusion/SKILL.md | 2 +- .../cli/skill/fusion/references/extension-tools.md | 10 ++ .../skill/fusion/references/fusion-capabilities.md | 1 + .../src/__tests__/extension-workflow-tools.test.ts | 1 + packages/cli/src/__tests__/extension.test.ts | 1 + .../src/__tests__/workflow-docs-current.test.ts | 1 + packages/cli/src/bin.ts | 22 ++++ packages/cli/src/commands/workflow.ts | 80 ++++++++++++ packages/cli/src/extension.ts | 10 ++ .../dashboard/src/__tests__/chat-manager.test.ts | 1 + .../dashboard/src/__tests__/chat.rooms.test.ts | 1 + .../planning-document-tools-exposure.test.ts | 1 + .../__tests__/workflow-validate-route.test.ts | 101 +++++++++++++++ .../src/routes/register-workflow-routes.ts | 27 +++- .../engine/src/__tests__/agent-action-gate.test.ts | 2 +- .../agent-workflow-tools-exposure.test.ts | 70 ++++++++++- .../src/__tests__/gating-classifications.test.ts | 3 +- .../src/__tests__/heartbeat-executor.test.ts | 37 +++--- .../src/__tests__/heartbeat-session-prompt.test.ts | 5 +- .../src/__tests__/permanent-agent-gating.test.ts | 2 +- packages/engine/src/agent-heartbeat.ts | 5 +- packages/engine/src/agent-tools.ts | 140 ++++++++++++++++++++- packages/engine/src/executor.ts | 6 + packages/engine/src/gating-classifications.ts | 2 + packages/engine/src/index.ts | 4 + 29 files changed, 532 insertions(+), 31 deletions(-) Fusion-Task-Id: FN-7911 Fusion-Task-Lineage: 903d15fe-a7ec-458f-aa34-8f2e895a9603 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
3326984a6d |
FN-7913: add fn plugin publish --dry-run preflight command
Adds a non-mutating `fn plugin publish` CLI command that preflights a plugin before manual pack/publish, giving external plugin authors an offline readiness check. - New `packages/cli/src/commands/plugin-publish.ts` with `runPluginPublish`, `collectPluginPreflight`, and `classifyVersionBump` (strict x.y.z semver bump classification), reusing `loadManifestFromPath` / `resolvePluginEntryFile` from the install path - Wire `fn plugin publish <path> [--dry-run] [--previous-version <semver>]` into `bin.ts` command routing, dynamic import list, and help text - Add test coverage in `plugin-publish.test.ts` and update `bin.test.ts` for the new subcommand - Update `docs/PLUGIN_AUTHORING.md`, `docs/cli-reference.md`, and `docs/plugins/external-authoring.md` to document the new preflight command - Add changeset `.changeset/fn-7913-plugin-publish-dry-run.md` (minor, @runfusion/fusion) Files changed: .changeset/fn-7913-plugin-publish-dry-run.md | 7 + docs/PLUGIN_AUTHORING.md | 9 +- docs/cli-reference.md | 5 +- docs/plugins/external-authoring.md | 14 +- packages/cli/src/__tests__/bin.test.ts | 2 +- packages/cli/src/__tests__/plugin-publish.test.ts | 197 ++++++++++++++++ packages/cli/src/bin.ts | 22 +- packages/cli/src/commands/plugin-publish.ts | 272 ++++++++++++++++++++++ 8 files changed, 521 insertions(+), 7 deletions(-) Fusion-Task-Id: FN-7913 Fusion-Task-Lineage: 27bdf937-3195-4619-9d01-b6af4fbba487 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
c745990aa2 |
FN-7879: deliver one-time Postgres-migration inbox notice on first 0.59 startup
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> |
||
|
|
2ffebef022 |
Address PR feedback: redact TUI/console log path too (#2028)
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> |
||
|
|
f1b6a6340c |
Address review feedback: System panel hardening (#2028)
Correctness/reliability: - engine restart-all: compensating pause on resume failure so a project is never left marked active with a dead engine - desktop restart: app.quit() (runs before-quit teardown) not app.exit(0) - supervisor: SIGINT/SIGTERM during crash-backoff exit immediately; stopping-latch prevents respawn after intentional shutdown - coalesce concurrent /system/restart requests (restartScheduled guard) - rebuild job completion chain given a .catch (no stranded activeJob) Security: - same-origin CSRF guard on all mutating /system/* POSTs (safe under --no-auth / desktop) - redact secrets in host-process log history (reuse core redactSecrets) - report-bug: confirm before including server logs + escape ``` fences - sanitize restart reason; Object.hasOwn scope-guard (prototype-key 500) Frontend: - log tail dedup (drop redundant REST backfill; SSE heartbeat stops 45s reconnect churn) + X-Accel-Buffering:no on both SSE routes - restart-wait 90s timeout re-enables controls - hydrate buffered rebuild lines on mount (mid-build panel open) Maintainability + tests: - engineAvailable reflects centralCore; hoisted systemLogs option; typed desktop systemControl DTO - add tests: SSE routes, exit-86 respawn, compiled-binary respawn, engine-restart recovery, log redaction Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
a227b19a22 |
feat: add Command Center System panel with rebuild/restart controls, Plugins tab, and supervised-by-default dashboard
- 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> |
||
|
|
67cc025562 |
FN-7859: park non-recoverable durable heartbeat errors instead of stalling in bare error
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> |
||
|
|
8b601810e0 |
fix(FN-7851): enforce per-agent assignment policy across all task-routing binding primitives
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> |
||
|
|
0a74059113 |
FN-7850: change dashboard TUI splash tagline to "software factory"
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> |
||
|
|
c12653e22c | test(cli): add isInReviewMissingWorktreeSessionStartFailure mock + update docs screenshot list | ||
|
|
1bf52758e4 | test(cli): fix stale project-context/desktop/core mocks + version pin + engine-tools doc (shard 3) | ||
|
|
21fb8f6786 |
FN-7802: recover phantom-worktree tasks stuck merge-active with scopeOverride
Fixes phantom-worktree context bleed where the engine refused to start a coding agent in a missing worktree for in-review/merge-active tasks even when scopeOverride=1, stranding them past the normal recovery paths and retry budget. - Add isMergeActiveMissingWorktreeSessionStartFailure/isInReviewMissingWorktreeSessionStartFailure classifiers and MERGE_ACTIVE_MISSING_WORKTREE_STATUSES (merging/merging-pr/merging-fix) in restart-recovery-coordinator.ts, exported from @fusion/engine. - Self-healing: reorder missing-worktree-review-failures sweep earlier, extend the in-review sweep to also match merge-active missing-worktree failures with a triple-proof-guarded, bounded (recoveryRetryCount) stale-metadata clear and fresh session-start retry budget reset. - Self-healing: extend scopeOverride worktree-metadata reconciliation to safely clear phantom worktree/branch/session metadata for in-review tasks stuck in a merge-active sub-status, narrowly scoped to avoid clobbering genuinely live in-progress/mid-step tasks (FN-5256 guard preserved). - CLI (task.ts), pi extension (extension.ts), and dashboard route (register-task-workflow-routes.ts) retry paths now bypass the merge-active status gate via a signature-only check, clearing worktree/branch/sessionFile and requeuing to todo while preserving progress. - Add regression coverage across self-healing.test.ts, restart-recovery-coordinator.test.ts, extension.test.ts, task-retry.test.ts, and routes-tasks-ops.test.ts; update mockCoreEngine.ts test scaffolding. - Update docs/architecture.md, docs/self-healing-backward-move-audit.md, docs/task-management.md, and AGENTS.md to describe the new merge-active missing-worktree recovery behavior. - Add changeset (patch) for @runfusion/fusion. Files changed: .changeset/fn-7802-phantom-worktree-merge-active-recovery.md | 7 + AGENTS.md | 1 + docs/architecture.md | 4 +- docs/self-healing-backward-move-audit.md | 5 +- docs/task-management.md | 2 +- packages/cli/src/__tests__/extension.test.ts | 64 +++++ packages/cli/src/__tests__/task-retry.test.ts | 49 ++++ packages/cli/src/commands/task.ts | 28 +- packages/cli/src/extension.ts | 26 +- packages/dashboard/src/__tests__/routes-tasks-ops.test.ts | 52 ++++ packages/dashboard/src/routes/register-task-workflow-routes.ts | 25 +- packages/dashboard/src/test/mockCoreEngine.ts | 11 + packages/engine/src/__tests__/restart-recovery-coordinator.test.ts | 20 ++ packages/engine/src/__tests__/self-healing.test.ts | 297 +++++++++++++++++++++ packages/engine/src/index.ts | 13 + packages/engine/src/restart-recovery-coordinator.ts | 19 +- packages/engine/src/self-healing.ts | 157 +++++++++-- 17 files changed, 744 insertions(+), 36 deletions(-) Fusion-Task-Id: FN-7802 Fusion-Task-Lineage: 5897105b-6b5c-49d5-a8e8-519902182861 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
54960672d7 |
fix: address PR review feedback (surface coverage, list/search guard, Intel Homebrew, serve exit tests)
- 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> |
||
|
|
23e36b8935 |
fix: surface API 500 causes, guide update EACCES, non-zero daemon signal exit
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> |
||
|
|
ea1e4fe062 |
fix: load live workspace plugin over stale staged bundle in dev
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> |
||
|
|
87b787319f |
fix(cli): harden dormant PR merge path against stale-base dead-ends
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>
|
||
|
|
8f7089ecc9 |
fix(FN-7779): rebuild changed plugins on pnpm dev + warn on stale plugin dist
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> |
||
|
|
a32307f8f1 |
FN-7778: resolve plugin-contributed skills per requesting project instead of daemon root
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> |
||
|
|
cc901c2b9d |
FN-7762: expand update-notification test coverage across CLI, dashboard, and desktop surfaces
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> |
||
|
|
18841d76a0 |
FN-7761: fix Grok CLI auth to use logged-in CLI instead of requiring API key
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> |
||
|
|
4726af673a |
FN-7740: fix store-leak and no-retry-on-lock gaps in research/settings-import/agent-export/git/project CLI commands
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> |
||
|
|
5304af8d0e |
FN-7739: retry locked store access in backup/mcp/db CLI commands
Audits and hardens `fn backup`, `memory-backup`, `mcp`, and `db vacuum` CLI commands so they retry a locked board database instead of hanging, and reliably close store handles on every exit path. - Add retryOnLock + closeProjectStore/asLocalProjectContext pattern to backup.ts, memory-backup.ts, mcp.ts, and db.ts (following the FN-7731/FN-7738 pattern) - Close cached, uncached CWD-fallback, and ad-hoc MCP secrets TaskStores on every exit path (success, error, early-return) - Retry MCP settings writes and DB VACUUM on lock contention, honoring FUSION_CLI_LOCK_RETRY_MS - Add lock-retry regression test suites for backup, db, mcp, and memory-backup commands - Update docs/cli-reference.md with the new retry/lock behavior - Add changeset (patch) documenting the fix Files changed: .changeset/fn-7739-cli-cmd-lock-retry.md | 7 + docs/cli-reference.md | 17 + .../commands/__tests__/backup-lock-retry.test.ts | 202 +++++++++++ packages/cli/src/commands/__tests__/backup.test.ts | 15 + .../src/commands/__tests__/db-lock-retry.test.ts | 182 ++++++++++ packages/cli/src/commands/__tests__/db.test.ts | 15 + .../src/commands/__tests__/mcp-lock-retry.test.ts | 234 ++++++++++++ packages/cli/src/commands/__tests__/mcp.test.ts | 14 + .../__tests__/memory-backup-lock-retry.test.ts | 201 +++++++++++ .../src/commands/__tests__/memory-backup.test.ts | 15 + packages/cli/src/commands/backup.ts | 252 +++++++++---- packages/cli/src/commands/db.ts | 62 +++- packages/cli/src/commands/mcp.ts | 391 +++++++++++++++------ packages/cli/src/commands/memory-backup.ts | 179 +++++++--- 14 files changed, 1538 insertions(+), 248 deletions(-) Fusion-Task-Id: FN-7739 Fusion-Task-Lineage: 6dbab086-2bf8-4cd4-950b-04fe39131933 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
86bd434c11 |
FN-7738: retry locked board DB in fn branch-group/pr CLI commands
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> |
||
|
|
1e79a236b2 |
FN-7734: generalize lock-retry and store-close pattern across all fn task subcommands
Applies the FN-7731 retryOnLock + closeProjectStore pattern to the ~26 runTask* handlers in task.ts so every 'fn task' subcommand retries a momentarily locked board database and exits promptly instead of hanging or leaking a store handle. - Generalize retryOnLock/closeProjectStore usage across all runTask* handlers in packages/cli/src/commands/task.ts - Honor FUSION_CLI_LOCK_RETRY_MS for configurable retry timing - Close both cached and uncached CWD-fallback stores on exit - Multi-step flows (create/retry/delete/merge/imports) retry each discrete write independently instead of retrying the whole flow - Add task-lock-retry.test.ts covering the new retry/close behavior across subcommands - Update docs/cli-reference.md and task.test.ts for the new behavior - Add changeset fn-7734-task-cmd-lock-retry-generalized.md (patch) Files changed: .../fn-7734-task-cmd-lock-retry-generalized.md | 7 + docs/cli-reference.md | 18 +- .../src/commands/__tests__/task-lock-retry.test.ts | 282 ++++ packages/cli/src/commands/__tests__/task.test.ts | 9 +- packages/cli/src/commands/task.ts | 1541 +++++++++++--------- 5 files changed, 1173 insertions(+), 684 deletions(-) Fusion-Task-Id: FN-7734 Fusion-Task-Lineage: cd5c864b-8a33-4962-803e-bbb353a41085 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
7420abe80e |
FN-7731: add CLI-level lock retry for task show/move commands
Add bounded exponential-backoff retry above the DB layer so `fn task show`/`fn task move` ride out transient SQLite lock contention instead of failing outright or hanging. - Add packages/cli/src/lock-retry.ts: retries a thunk on SQLite lock errors (via @fusion/core's isSqliteLockError) with exponential backoff capped by a wall-clock deadline (default 15s, override via FUSION_CLI_LOCK_RETRY_MS); non-lock errors propagate immediately; raises LockRetryExhaustedError on deadline exhaustion. - Wire lock-retry into packages/cli/src/commands/task.ts for the show/move task-store operations, and close the resolved TaskStore for deterministic exit. - Export isSqliteLockError from packages/core/src/index.ts for CLI reuse. - Add packages/cli/src/commands/__tests__/task-lock-retry.test.ts covering retry/backoff/deadline/error-passthrough behavior; extend task.test.ts. - Document the new behavior/env var in docs/cli-reference.md. - Add changeset (@runfusion/fusion: patch). Files changed: .changeset/fn-7731-task-cmd-lock-retry.md | 7 + docs/cli-reference.md | 9 + .../src/commands/__tests__/task-lock-retry.test.ts | 430 +++++++++++++++++++++ packages/cli/src/commands/__tests__/task.test.ts | 11 + packages/cli/src/commands/task.ts | 117 +++++- packages/cli/src/lock-retry.ts | 117 ++++++ packages/core/src/index.ts | 5 + 7 files changed, 688 insertions(+), 8 deletions(-) Fusion-Task-Id: FN-7731 Fusion-Task-Lineage: 85543164-10d6-4da9-8c3c-a84cd86827aa Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
626e00288c |
FN-7720: add operator review-lane bypass for stranded pre-merge review failures
Add a policy-gated review-lane bypass primitive so operators can unstick cards stranded by a failed pre-merge review step (e.g. the no-feedback review-engine defect), without exposing it to agent-driven lanes.
- Add `store.bypassFailedPreMergeReviewStep(id, { reason, actor })` in @fusion/core plus `getLatestFailedPreMergeReviewStep` in task-merge.ts, and new `bypassedBy`/`bypassedAt`/`bypassReason`/`bypassedFromStatus`/`bypassedFromVerdict` fields on `WorkflowStepResult`
- Add operator-only `fn_task_bypass_review` CLI/pi-extension tool; explicitly withheld from executor/reviewer/triage agent tool lists
- Add `POST /tasks/:id/bypass-review` dashboard API route and wire it through `register-task-workflow-routes.ts` and legacy API compatibility layer
- Add dashboard UI affordance (context menu action + task detail modal + right-dock controller wiring) to trigger the bypass with a reason
- Add i18n strings for the bypass action/labels across en/es/fr/ko/zh-CN/zh-TW locales
- Update `gating-classifications.ts` to recognize the bypassed state
- Add unit tests: `store-bypass-review.test.ts`, `task-merge-bypass.test.ts`, extension test coverage, and `useTasks` hook test coverage
- Update docs (`docs/workflow-steps.md`, `docs/dashboard-guide.md`, AGENTS.md, fusion skill references) to describe the new bypass tool/route
- Add changeset `.changeset/fn-7720-review-lane-bypass-primitive.md` (minor)
Files changed:
$(git diff --cached --stat)
Fusion-Task-Id: FN-7720
Fusion-Task-Lineage: 590b020a-ae02-4b51-8189-df8f54bf3044
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
|
||
|
|
7dc271027f |
FN-7711: add built-in Grok CLI provider to fix pi model registry lookup
Registers a built-in grok-cli provider so Grok CLI model executions no longer hard-fail with "not found in the pi model registry". - Add packages/core/src/grok-provider.ts: built-in grok-cli provider config (xAI OpenAI-compatible endpoint https://api.x.ai/v1, api openai-completions, apiKey $GROK_API_KEY), mirroring the existing Z.ai provider - Register the provider in packages/engine/src/pi.ts (registerExtensionProviders) and packages/engine/src/provider-registration.ts (seedDashboardProviders) - Wire the provider into CLI entrypoints: packages/cli/src/commands/daemon.ts, dashboard.ts, serve.ts - Export grok-provider from packages/core/src/index.ts and packages/core/src/index.gate.ts - Add unit tests: packages/core/src/__tests__/grok-provider.test.ts, and extend packages/engine/src/__tests__/pi-create-fn-agent.test.ts and provider-registration.test.ts - Document the new provider in docs/settings-reference.md - Add changeset .changeset/fn-7711-grok-cli-model-registry.md (patch, category: fix) Note: Grok CLI binary remains discovery/probe only; GrokRuntimeAdapter streaming is a stub (tracked follow-up). Files changed: .changeset/fn-7711-grok-cli-model-registry.md | 7 + docs/settings-reference.md | 2 + packages/cli/src/commands/daemon.ts | 4 + packages/cli/src/commands/dashboard.ts | 4 + packages/cli/src/commands/serve.ts | 4 + packages/core/src/__tests__/grok-provider.test.ts | 130 ++++++++++++ packages/core/src/grok-provider.ts | 224 +++++++++++++++++++++ packages/core/src/index.gate.ts | 7 + packages/core/src/index.ts | 7 + .../src/__tests__/pi-create-fn-agent.test.ts | 78 ++++++- .../src/__tests__/provider-registration.test.ts | 4 +- packages/engine/src/pi.ts | 4 + packages/engine/src/provider-registration.ts | 4 + 13 files changed, 476 insertions(+), 3 deletions(-) Fusion-Task-Id: FN-7711 Fusion-Task-Lineage: ae90b54f-206e-46fd-8365-b0a4488ceb84 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
081dae0e0f |
FN-7705: Add Grok CLI runtime support as a bundled plugin
Adds a new bundled Grok CLI runtime plugin, wiring it end-to-end into settings, auth routes, model discovery, and the dashboard authentication UI. - New `fusion-plugin-grok-runtime` package with CLI spawn, probe, provider, process-manager, and runtime-adapter modules plus tests - Bundled-plugin install list (CLI + core) updated to auto-install the grok-cli plugin - New `useGrokCli`/`grokCliBinaryPath` settings in `settings-schema.ts` and `types.ts` - Dashboard: `GrokCliProviderCard` component/styles, `ProviderIcon` grok entry, `AuthenticationSection` wiring - New `grok-model-cache.ts` for caching `grok models` discovery results, registered model/auth routes for `/auth/grok-cli` and `/providers/grok-cli/status`, merged into `/api/models` - `runtime-provider-probes.ts` extended with Grok CLI probe/model-discovery delegation - Docs updated (`PLUGIN_AUTHORING.md`, `settings-reference.md`) and changeset added (minor, feature) - Workspace config (`pnpm-workspace.yaml`, `pnpm-lock.yaml`) updated to register the new plugin package Files changed: .changeset/fn-7705-grok-cli-runtime.md | 7 + docs/PLUGIN_AUTHORING.md | 2 +- docs/settings-reference.md | 4 + packages/cli/src/plugins/bundled-plugin-install.ts | 8 + .../cli/src/plugins/staged-bundled-plugin-ids.ts | 1 + packages/cli/vitest.config.ts | 12 + .../core/src/__tests__/grok-cli-settings.test.ts | 34 +++ packages/core/src/index.ts | 1 + .../core/src/plugins/bundled-plugin-install.ts | 10 + packages/core/src/settings-schema.ts | 6 + packages/core/src/types.ts | 9 + packages/dashboard/app/api/legacy.ts | 40 ++++ .../app/components/GrokCliProviderCard.css | 65 ++++++ .../app/components/GrokCliProviderCard.tsx | 204 ++++++++++++++++ packages/dashboard/app/components/ProviderIcon.tsx | 5 + .../__tests__/GrokCliProviderCard.test.tsx | 105 +++++++++ .../app/components/__tests__/ProviderIcon.test.tsx | 8 + .../settings/sections/AuthenticationSection.tsx | 8 +- packages/dashboard/package.json | 1 + .../src/__tests__/grok-model-cache.test.ts | 152 ++++++++++++ .../register-model-routes-grok-cli.test.ts | 214 +++++++++++++++++ .../dashboard/src/__tests__/routes-auth.test.ts | 258 ++++++++++++++++++++- packages/dashboard/src/grok-model-cache.ts | 166 +++++++++++++ packages/dashboard/src/routes.ts | 1 + .../dashboard/src/routes/register-auth-routes.ts | 134 ++++++++++- .../dashboard/src/routes/register-model-routes.ts | 51 ++++ packages/dashboard/src/runtime-provider-probes.ts | 43 ++++ packages/dashboard/vitest.config.ts | 12 + packages/desktop/scripts/workspace-tools.ts | 3 +- plugins/fusion-plugin-grok-runtime/CHANGELOG.md | 7 + plugins/fusion-plugin-grok-runtime/README.md | 54 +++++ plugins/fusion-plugin-grok-runtime/manifest.json | 6 + plugins/fusion-plugin-grok-runtime/package.json | 40 ++++ .../src/__tests__/cli-spawn.test.ts | 103 ++++++++ .../src/__tests__/index.test.ts | 12 + .../src/__tests__/probe.test.ts | 135 +++++++++++ .../src/__tests__/process-manager.test.ts | 96 ++++++++ .../src/__tests__/provider.test.ts | 57 +++++ .../src/__tests__/runtime-adapter.test.ts | 21 ++ .../fusion-plugin-grok-runtime/src/cli-spawn.ts | 50 ++++ plugins/fusion-plugin-grok-runtime/src/index.ts | 74 ++++++ plugins/fusion-plugin-grok-runtime/src/probe.ts | 107 +++++++++ .../src/process-manager.ts | 86 +++++++ plugins/fusion-plugin-grok-runtime/src/provider.ts | 25 ++ .../src/runtime-adapter.ts | 25 ++ plugins/fusion-plugin-grok-runtime/src/types.ts | 12 + plugins/fusion-plugin-grok-runtime/tsconfig.json | 10 + .../fusion-plugin-grok-runtime/vitest.config.ts | 22 ++ pnpm-lock.yaml | 25 ++ pnpm-workspace.yaml | 1 + 50 files changed, 2525 insertions(+), 7 deletions(-) Fusion-Task-Id: FN-7705 Fusion-Task-Lineage: b8194ea8-c773-4199-a52a-b0e4e7347192 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
55dae49b37 |
FN-7704: fix fn agent stop/start hanging up to 60s due to unclosed store handles
Fix CLI process exit so `fn agent stop`/`fn agent start` no longer hang up to 60s and eventually time out on repeated retries against the same agent. - Root cause: `resolveProject()` cached an unclosed `TaskStore`, and `createAgentStore()` never closed the `AgentStore` it opened, leaving SQLite handles alive after the command's real work was done. - Add `resolveProjectPathOnly`/`closeProjectStore` helpers in `project-context.ts` so path-only callers never leak a `TaskStore`. - Explicitly close `AgentStore` on every exit/return path in `agent.ts`, since `process.exit()` skips pending `finally` blocks. - Add a bounded fast-fail timeout around the state-store write (default 10s, overridable via `FUSION_AGENT_CMD_TIMEOUT_MS`) so a genuinely stuck operation fails fast with a clear error and non-zero exit instead of hanging. - Add regression tests covering process-exit/store-closing behavior and update CLI reference docs. - Add changeset for the patch release. Files changed: .changeset/fn-7704-agent-cmd-hang-fix.md | 7 + docs/cli-reference.md | 3 + .../commands/__tests__/agent-process-exit.test.ts | 114 +++++++++++ packages/cli/src/commands/__tests__/agent.test.ts | 111 +++++++++- packages/cli/src/commands/agent.ts | 223 ++++++++++++++++----- packages/cli/src/project-context.ts | 44 ++++ 6 files changed, 444 insertions(+), 58 deletions(-) Fusion-Task-Id: FN-7704 Fusion-Task-Lineage: 4679d1a0-3ab8-48ce-86b7-5919bba805fb Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
7cb76669db |
fix: resolve 3 staff-engineer review findings from #1957 (init_error mislabel, unbounded off-screen polling, duplicate toasts) (#1960)
## Summary A Staff Engineer pre-landing review (Greptile/CodeRabbit) on #1957 (merged) flagged four structural issues. This PR fixes the three that were confirmed still present on `main`; the fourth (an unregistered-rule `eslint-disable-next-line react-hooks/exhaustive-deps` comment) was already fixed in #1957's second commit before merge and needed no further change. 1. **`resolvePluginRuntime()` mislabeled "found but failed to init" as `not_found`.** When a `runtimeHint` plugin registration is found but `pluginContext`/`createRuntimeContext(...)` comes back falsy, the resolver returned `reason: "not_found"` — indistinguishable from "never registered" — defeating the point of a distinct `FallbackReason`. Now returns `reason: "init_error"`. Updated the existing test that wrongly asserted `"not_found"` for this path, and added a new test asserting all three reachable `FallbackReason` values (`not_found`, `init_error`, `factory_error`) are pairwise distinct. 2. **`ActiveAgentsPanel.tsx`/`AgentsView.tsx` hardcoded `isInViewport={true}`.** Every agent card (live-agent header, board card, list card) polled the runtime-fallback endpoint every 30s forever, even scrolled off-screen — unlike `TaskCard.tsx`'s correct `IntersectionObserver`-gated pattern. Both files now thread a real `IntersectionObserver`-backed viewport signal into `RuntimeFallbackBadge`. Added regression tests proving polling stops once a badge instance's `isInViewport` transitions to `false` and resumes once it goes back to `true` (desktop + a mobile-breakpoint variant), plus verified via `tsc --noEmit` for `@fusion/dashboard`. 3. **Toast dedupe was per-hook-instance, not shared.** `useRuntimeFallbackStatus`'s `lastToastedEventIdRef` was a local `useRef`, so the same task rendered simultaneously in two card surfaces (e.g. `ActiveAgentsPanel` + `AgentsView`) fired two separate toasts for one fallback event. Dedupe now lives in module-level shared state (a bounded `Map` keyed by `taskId:eventId`, FIFO-evicted past 500 entries) so a fallback event toasts exactly once across every simultaneously-mounted badge instance for the same task. Added a cross-instance regression test mounting two badges for the same `taskId`/`eventId` and asserting exactly one toast fires. ## Test evidence - `pnpm --filter @fusion/engine exec vitest run src/__tests__/runtime-resolution.test.ts --reporter=dot` — 25/25 pass - `pnpm --filter @fusion/dashboard exec vitest run app/components/__tests__/RuntimeFallbackBadge.test.tsx --reporter=dot` — 11/11 pass - `pnpm --filter @fusion/dashboard run typecheck` — clean - `pnpm --filter @fusion/engine run typecheck` — clean ## Scope Isolated 6-file diff on top of current `main` (`packages/engine/src/runtime-resolution.ts`, `packages/engine/src/__tests__/runtime-resolution.test.ts`, `packages/dashboard/app/hooks/useRuntimeFallbackStatus.ts`, `packages/dashboard/app/components/ActiveAgentsPanel.tsx`, `packages/dashboard/app/components/AgentsView.tsx`, `packages/dashboard/app/components/__tests__/RuntimeFallbackBadge.test.tsx`). No behavior outside the three findings above was touched. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - The desktop dashboard now supports plugin-backed runtime features, improving how plugin-enabled workflows are loaded and run. - Agent cards now pause background fallback polling when they’re off-screen, helping the dashboard feel smoother and more responsive. - **Bug Fixes** - Improved runtime fallback handling so missing runtimes and initialization failures are reported more accurately. - Toast notifications are now better deduplicated, reducing repeated alerts when multiple views show the same fallback state. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
64d4bb753a |
FN-7690: fix custom-provider anthropic-compatible apiType resolution
Reconciles a naming drift where resolveApiType() mapped anthropic-compatible custom providers to an unregistered pi-ai api key, causing streaming failures. - resolveApiType() now maps anthropic-compatible to "anthropic-messages" (was "anthropic"), matching pi.ts's resolveCustomProviderApiType and the built-in Anthropic provider config - Added FNXC:CustomProviders comment documenting why anthropic-messages is the only key pi-ai's ModelRegistry actually registers - Added/updated regression tests in custom-provider-registry.test.ts and provider-registration.test.ts - Added changeset (patch) documenting the fix Files changed: .changeset/fn-7690-apitype-resolver-reconcile.md | 7 +++++++ packages/cli/src/commands/__tests__/custom-provider-registry.test.ts | 21 ++++++++++++++++++--- packages/engine/src/__tests__/provider-registration.test.ts | 20 ++++++++++++++++++++ packages/engine/src/custom-provider-registry.ts | 15 ++++++++++++++- 4 files changed, 59 insertions(+), 4 deletions(-) Fusion-Task-Id: FN-7690 Fusion-Task-Lineage: 461c340f-bc29-44a2-b8ec-19f0b03224d7 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
c3c726cff4 |
fix(FUX-039): return init_error for found-but-uninitialized plugin runtime
Co-authored-by: Fusion <noreply@runfusion.ai> |
||
|
|
dd9fa2d3cb |
FN-7661: expose removeLineageReferences on fn_task_archive/fn_task_delete
Fixes fn_task_archive and fn_task_delete rejecting tasks still referenced as a lineage parent, with no tool-exposed way to clear that reference. - Add optional removeLineageReferences boolean param to fn_task_archive and fn_task_delete tool schemas, forwarded to store.archiveTask/store.deleteTask - Update tool descriptions and prompt guidelines to advertise the recovery path (removeLineageReferences:true) when a lineage-parent block occurs - Add task-lineage-unlink.test.ts covering the new parameter behavior - Document the change in docs/storage.md - Add changeset (@runfusion/fusion minor, category: fix) Files changed: .changeset/fn-7661-lineage-unlink-tools.md | 7 + docs/storage.md | 1 + packages/cli/src/__tests__/task-lineage-unlink.test.ts | 199 +++++++++++++++++++++ packages/cli/src/extension.ts | 28 ++- 4 files changed, 232 insertions(+), 3 deletions(-) Fusion-Task-Id: FN-7661 Fusion-Task-Lineage: 414c046c-43df-4995-85a5-ff00b345de50 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
4e8c621e9c |
FN-7641: fix cards stranded after out-of-band/workspace merges by allowing proven-merge rehome
Fixes a state-machine bug family where cards got stranded after out-of-band or workspace merges landed: store.moveTask now allows a proven-merge recoveryRehome to cross legacy columns (e.g. todo→done), and nodeId='end' finalize no longer silently no-ops — it finalizes on durable merge proof or returns an explicit error, consistently across the dashboard route, the CLI task-update tool, and store.updateTask. - packages/core/src/store.ts: allow proven-merge recoveryRehome moves across legacy columns (e.g. todo→done) instead of rejecting them - packages/core/src/node-override-guard.ts: nodeId='end' finalize now checks for durable merge proof and returns an explicit error instead of silently no-op'ing - packages/dashboard/src/routes/register-task-workflow-routes.ts: dashboard workflow route surfaces the new explicit finalize error/behavior - packages/cli/src/extension.ts: CLI task-update tool surfaces the same explicit finalize error/behavior - docs/task-management.md: documented the updated finalize/rehome behavior - Added regression tests across core (node-override-guard, store-movement, task-node-override), dashboard (register-task-workflow-routes.nodeid-finalize), engine (merger-merge-lifecycle), and CLI (extension) covering the stranded-card invariant - Added changeset for @runfusion/fusion (patch) Files changed: .changeset/fn-7641-stranded-cards-after-merge.md | 7 ++ docs/task-management.md | 2 + packages/cli/src/__tests__/extension.test.ts | 59 ++++++++++++++ packages/cli/src/extension.ts | 10 +++ .../core/src/__tests__/node-override-guard.test.ts | 93 +++++++++++++++++++++ packages/core/src/__tests__/store-movement.test.ts | 94 ++++++++++++++++++++++ .../core/src/__tests__/task-node-override.test.ts | 73 +++++++++++++++++ packages/core/src/node-override-guard.ts | 69 +++++++++++++++- packages/core/src/store.ts | 69 +++++++++++++++- ...er-task-workflow-routes.nodeid-finalize.test.ts | 90 +++++++++++++++++++++ .../src/routes/register-task-workflow-routes.ts | 10 +++ .../src/__tests__/merger-merge-lifecycle.test.ts | 58 +++++++++++++ 12 files changed, 631 insertions(+), 3 deletions(-) Fusion-Task-Id: FN-7641 Fusion-Task-Lineage: 48ea7851-ee68-48f1-92f9-302d0da5acff Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
26f22861fa |
FN-7637: port bundled-plugin auto-install into @fusion/core for the desktop runtime
Move the host-agnostic bundled-plugin auto-install logic (manifest loading, entry-path resolution, install/update/enable flow) out of the CLI package into @fusion/core so the desktop embedded runtime can auto-install bundled runtime plugins without depending on the CLI package; the CLI module becomes a thin adapter that supplies its own bundle-dir resolution to the shared helper. - Add packages/core/src/plugins/bundled-plugin-install.ts with the shared, host-agnostic ensureBundledPluginInstalled / ensureBundledDependencyGraphPluginInstalled / ensureBundledCursorRuntimePluginInstalled implementation and BUNDLED_PLUGIN_IDS/ isBundledPluginId/resolvePluginEntryPath, exported from @fusion/core's index. - Slim packages/cli/src/plugins/bundled-plugin-install.ts to a CLI-specific candidate-bundle-dir resolver that delegates to @fusion/core and re-exports the same public surface dashboard.ts/serve.ts/daemon.ts already depend on. - Remove the now-redundant packages/cli/src/plugins/__tests__/resolve-plugin-entry-path-sync.test.ts (coverage moved with the implementation to @fusion/core). - Add packages/desktop/src/bundled-plugin-dirs.ts to resolve each bundled plugin's staged package directory via import.meta.resolve, mirroring the CLI's dist/plugins/<id> resolver. - Wire local-runtime.ts and local-server.ts to call ensureBundledPluginInstalled before loadAllPlugins() and expose a lazy-install callback for PUT /api/plugins/:id/settings, mirroring the CLI dashboard command's startup auto-install pass. - Update docs/PLUGIN_AUTHORING.md to describe the shared bundled-plugin-install location. Files changed: docs/PLUGIN_AUTHORING.md | 11 + .../__tests__/bundled-plugin-install.test.ts | 619 ++------------------- .../resolve-plugin-entry-path-sync.test.ts | 97 ---- packages/cli/src/plugins/bundled-plugin-install.ts | 250 +-------- packages/core/src/index.ts | 8 + .../__tests__/bundled-plugin-install.test.ts | 391 +++++++++++++ .../core/src/plugins/bundled-plugin-install.ts | 186 +++++++ .../src/__tests__/bundled-plugin-dirs.test.ts | 59 ++ .../desktop/src/__tests__/local-runtime.test.ts | 183 +++++- .../desktop/src/__tests__/local-server.test.ts | 96 +++- packages/desktop/src/bundled-plugin-dirs.ts | 61 ++ packages/desktop/src/local-runtime.ts | 66 ++- packages/desktop/src/local-server.ts | 36 +- 13 files changed, 1171 insertions(+), 892 deletions(-) Fusion-Task-Id: FN-7637 Fusion-Task-Lineage: 953c5b82-a079-4600-b3af-45c974cd5014 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
fe5a595984 |
FN-7622: unify desktop and CLI provider seeding to fix truncated provider list
The Electron desktop app's in-process dashboard server skipped the CLI's provider seeding sequence, so /api/providers and /api/models returned a truncated catalog (missing built-in API-key providers and user customProviders[]) compared to the identical config on the web build. - Move provider-auth.ts and custom-provider-registry.ts from @fusion/cli into @fusion/engine as the single shared implementation - Add engine/src/provider-registration.ts exposing seedDashboardProviders(), mirroring the CLI's exact startup order (built-in Zai provider registration -> wrapAuthStorageWithApiKeyProviders -> model merge/refresh -> registerCustomProviders -> settings:updated resubscription) - Update desktop/src/local-runtime.ts and local-server.ts to call the shared seedDashboardProviders() helper instead of constructing a raw authStorage/modelRegistry - Convert packages/cli/src/commands/provider-auth.ts and custom-provider-registry.ts into re-export shims preserving unchanged observable behavior - Add engine/src/__tests__/provider-registration.test.ts and expand desktop local-runtime/local-server tests to cover the shared seeding path - Add changeset for @runfusion/fusion (patch) Files changed: .changeset/fn-7622-desktop-provider-parity.md | 7 + .../cli/src/commands/custom-provider-registry.ts | 122 +---- packages/cli/src/commands/provider-auth.ts | 517 +-------------------- .../desktop/src/__tests__/local-runtime.test.ts | 93 ++++ .../desktop/src/__tests__/local-server.test.ts | 62 ++- packages/desktop/src/local-runtime.ts | 33 +- packages/desktop/src/local-server.ts | 21 +- .../src/__tests__/provider-registration.test.ts | 192 ++++++++ packages/engine/src/custom-provider-registry.ts | 117 +++++ packages/engine/src/index.ts | 18 + packages/engine/src/provider-auth.ts | 513 ++++++++++++++++++++ packages/engine/src/provider-registration.ts | 105 +++++ 12 files changed, 1172 insertions(+), 628 deletions(-) Fusion-Task-Id: FN-7622 Fusion-Task-Lineage: fb6fbbf3-745e-4623-b7af-11471e13f138 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
ca8447304f |
FN-7619: guard fn_task_attach against worktree boundary bypass
Add a path-containment check to fn_task_attach so it can no longer read files outside the task's worktree via traversal or absolute paths. - Resolve the requested path and confine it to ctx.cwd (the task worktree) before any readFile call, rejecting "../" traversal, absolute paths, and other boundary-escaping inputs - Add regression tests in extension.test.ts covering traversal/absolute-path attack vectors - Add changeset (patch, category: security) documenting the fix Files changed: .changeset/fn-7619-attach-boundary.md | 7 ++ packages/cli/src/__tests__/extension.test.ts | 133 ++++++++++++++++++++++++++- packages/cli/src/extension.ts | 23 ++++- 3 files changed, 161 insertions(+), 2 deletions(-) Fusion-Task-Id: FN-7619 Fusion-Task-Lineage: d35d9218-d678-4989-945d-6c1e1a322c5c Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
203f879c8f |
FN-7611: respect workflow intake column on task creation
Task creation surfaces stopped hardcoding column:"triage", so new tasks now land in the selected-or-default workflow's resolved intake column instead of always jumping to Planning/triage. - Removed hardcoded column:"triage" override in engine's createTaskCreateTool (fn_task_create), letting TaskStore.createTask resolve the landing column from the workflow's intake-trait column. - Removed the equivalent hardcoded override in the pi extension's fn_task_create, and updated its response text to echo the actual landing column instead of a fixed "Column: triage" string. - Fixed signal-route, GitHub-import, and planning-subtask-route task creation to stop forcing column when no workflowId is given (or, for planning subtask routes, even when one is provided). - Custom workflows with a non-triage intake column (e.g. Inbox) now correctly capture new cards inert until released, while the default builtin:coding workflow still resolves to "triage" byte-identically. - Added regression coverage (agent-tools-intake-column.test.ts, extension-workflow-tools.test.ts) and a patch changeset documenting the fix. Files changed: .changeset/fn-7611-intake-column.md | 7 ++ .../src/__tests__/extension-workflow-tools.test.ts | 70 +++++++++++ packages/cli/src/extension.ts | 10 +- .../src/__tests__/register-signal-routes.test.ts | 8 +- .../dashboard/src/__tests__/routes-github.test.ts | 2 - .../dashboard/src/routes/register-git-github.ts | 16 ++- .../src/routes/register-planning-subtask-routes.ts | 24 +++- .../dashboard/src/routes/register-signal-routes.ts | 8 +- .../__tests__/agent-tools-intake-column.test.ts | 138 +++++++++++++++++++++ packages/engine/src/__tests__/agent-tools.test.ts | 1 - packages/engine/src/agent-tools.ts | 21 +++- 11 files changed, 288 insertions(+), 17 deletions(-) Fusion-Task-Id: FN-7611 Fusion-Task-Lineage: daf7f755-b1c7-4859-b74f-f15593d5e79e Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
60081fb1f4 |
FN-7610: route workspace-mode tasks around PR-merge auto-merge strategy
Fixes workspace-mode (workspaceWorktrees) tasks failing auto-merge under mergeStrategy=pull-request, where processPullRequestMergeTask threw "could not determine repository" because the workspace root is a container of independent git sub-repos, not itself a git repo. - Hoist an isWorkspaceTask check in ProjectEngine's merge dispatch (project-engine.ts) before the mergeStrategy branch, so workspace tasks always fall through to the existing direct/landWorkspaceTask path regardless of configured mergeStrategy. - Add processPullRequestMergeTask and syncGroupPrCallback defense-in-depth guards (task-lifecycle.ts) that throw the new named WorkspaceTaskMergeError if a workspace task ever reaches the PR-merge path. - Add engine tests covering multi-repo, single-repo, and zero-commit no-op workspace tasks under mergeStrategy=pull-request, plus a non-regression test for the legacy single-worktree PR path. - Add CLI tests asserting the new guards throw WorkspaceTaskMergeError. - Add a patch changeset describing the fix. Files changed: .changeset/fn-7610-workspace-pr-merge-routing.md | 7 ++ .../src/commands/__tests__/task-lifecycle.test.ts | 56 +++++++++ packages/cli/src/commands/task-lifecycle.ts | 33 ++++- .../engine/src/__tests__/project-engine.test.ts | 140 +++++++++++++++++++++ packages/engine/src/project-engine.ts | 18 ++- 5 files changed, 252 insertions(+), 2 deletions(-) Fusion-Task-Id: FN-7610 Fusion-Task-Lineage: 31768b77-d9a9-4a79-a055-bbc6b228a1c4 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
196abb5af9 |
FN-7576: delegate anthropic-subscription getApiKey to engine authStorage
Reads for the anthropic-subscription provider now delegate to the real engine authStorage so expired OAuth tokens are refreshed instead of silently failing.
- mergeAuthStorageReads getApiKey("anthropic-subscription") now calls target.getApiKey(providerId) directly, triggering the engine's refresh-token HTTP round trip, instead of a local static Date.now() >= credential.expires check
- Falls back to the read-only fallback storages' local resolution only when the primary engine call yields no key
- Added regression tests exercising the wrapper directly against the engine delegation and fallback behavior
- Added a patch changeset documenting the fix for @runfusion/fusion
Files changed:
.changeset/fn-7576-cli-wrapper-subscription-getapikey-delegation.md | 7 +
packages/cli/src/commands/__tests__/provider-auth.test.ts | 148 +++++++++++++++++++++
packages/cli/src/commands/provider-auth.ts | 12 +-
3 files changed, 166 insertions(+), 1 deletion(-)
Fusion-Task-Id: FN-7576
Fusion-Task-Lineage: 31e495ec-8487-468b-9b80-36e8d0f53806
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
|
||
|
|
71dfd3aeca |
FN-7568: rename CLI release binary assets to fn-cli-<platform>
Renames the downloadable GitHub Release CLI binaries so they don't collide with other well-known fn-named tools on a user's PATH; the local dev binary name is unchanged. - Update binaryNameForTarget in packages/cli/build.ts to emit fn-cli-<suffix> instead of fn-<suffix> (local dev binary stays fn/fn.exe) - Update release.yml and test-release.yml build matrices to use fn-cli-linux-x64, fn-cli-linux-arm64, fn-cli-darwin-arm64, fn-cli-windows-x64.exe - Update build-exe-cross, ci-workflow, and package-config tests to assert the new fn-cli-<platform> asset names - Add changeset documenting the release-asset rename Files changed: .changeset/fn-7568-fn-cli-release-asset.md | 7 +++++++ .github/workflows/release.yml | 8 ++++---- .github/workflows/test-release.yml | 8 ++++---- packages/cli/build.ts | 10 ++++++++-- packages/cli/src/__tests__/build-exe-cross.test.ts | 14 +++++++------- packages/cli/src/__tests__/ci-workflow.test.ts | 8 ++++---- packages/cli/src/__tests__/package-config.test.ts | 10 +++++----- 7 files changed, 39 insertions(+), 26 deletions(-) Fusion-Task-Id: FN-7568 Fusion-Task-Lineage: 1c04d763-5e2f-43e3-84b7-df8dcff8467f Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
36bd74e337 |
FN-7532: stamp branch group merge attribution
Ensure shared branch group members record merge attribution so completion checklists reflect real landed state. - Route AI merges through branch-group merge routing before selecting the integration target. - Stamp mergeDetails merge target fields for both landed and no-op finalize paths. - Record shared-group member landing state and best-effort managed PR checklist sync after AI merges. - Cover dashboard, CLI lifecycle, and merger scenarios for accurate branch-group completion counts. Files changed: .changeset/fn-7532-branch-group-completion.md | 7 ++ docs/dashboard-guide.md | 2 + .../src/commands/__tests__/task-lifecycle.test.ts | 29 +++++++ .../src/__tests__/routes-branch-groups.test.ts | 45 +++++++++++ packages/engine/src/__tests__/merger-ai.test.ts | 68 +++++++++++++++- packages/engine/src/merger-ai.ts | 90 +++++++++++++++++++++- 6 files changed, 235 insertions(+), 6 deletions(-) Fusion-Task-Id: FN-7532 Fusion-Task-Lineage: cd65c18a-f1ad-4f8b-99b2-2e61e233f042 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
b545083249 |
FN-7530: isolate flaky dist-barrel extension test
Restore the stable extension suite by quarantining only the dist-barrel recompilation case. - Move the built @fusion/core dist-barrel extension test into its own file. - Re-admit extension.test.ts while keeping the isolated dist-barrel file quarantined. - Update the quarantine ledger and velocity baseline to reflect the narrowed quarantine. Files changed: docs/test-velocity-baseline.md | 10 +- .../src/__tests__/extension-dist-barrel.test.ts | 230 +++++++++++++++++++++ packages/cli/src/__tests__/extension.test.ts | 103 +-------- packages/cli/vitest.config.ts | 5 +- scripts/lib/test-quarantine.json | 4 +- 5 files changed, 247 insertions(+), 105 deletions(-) Fusion-Task-Id: FN-7530 Fusion-Task-Lineage: 7b07540f-689b-4133-b590-a39427095397 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |