2e4fcfcaea09b9dcbccd4443e2f67eb6fed04487
11324 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2e4fcfcaea |
fix(FN-7952): establish PostgreSQL core authority (#2108)
## 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 --> |
||
|
|
e97081fb77 |
fix: stop agents exceeding the global concurrency cap (#2107)
## Summary - Operators could see more agents running than Global Max Concurrent (e.g. 5 running with cap 4: 4 planners + 1 executor). - Scheduler now `tryAcquire`s a shared semaphore slot before todo→in-progress and hands that pre-held slot to the executor/graph run. - Triage admits planners against the live top-level running-agent claim (planning + in-progress + active in-review), not only `semaphore.availableCount`. - Executor claims the pre-held slot for the full run and avoids a second top-level acquire on step/seam re-entry (deadlock under a full cap). ## Test plan - [x] `pnpm --filter @fusion/engine exec vitest run src/__tests__/concurrency.test.ts src/__tests__/triage.test.ts` - [x] Regression: triage leaves room when 1 in-progress agent is live under global cap 4 - [x] Regression: pre-held executor slot register/take/drop handoff - [ ] Manual: set Global Max Concurrent and Max triage concurrent to 4, fill Planning + run 1 In Progress; footer should not show 5 running under a full steady state <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Improved global concurrency enforcement so the scheduler and executor never start more agents than the configured limit, including tighter top-level “claimed capacity” accounting. - Updated triage admission control to consider global top-level utilization, factoring processing tasks and agents already running to prevent over-admitting planners. - Added safer pre-held concurrency-slot handoff behavior to avoid capacity leaks and drift during graph routing, step execution, and legacy fallback. - Ensured reserved capacity is reliably released on early exits, failed dispatches, and other aborted paths (with idempotent cleanup). - Refreshed concurrency diagnostics to better explain whether throttling is due to project or global limits, with clearer claimed/processing visibility. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
f4e78abeb7 |
fix: resolve CREATE ROLE fusion_runtime race condition in migration 0006 (#2104)
## Summary Fixes `CREATE ROLE fusion_runtime` race condition in migration `0006_project_ownership.sql` that causes 30 compound-engineering test failures on CI. ## Root Cause Concurrent test databases on the same PostgreSQL service container race on `CREATE ROLE fusion_runtime`: the `IF NOT EXISTS` check is not atomic (roles are cluster-wide, not per-database). Between the check and the `CREATE ROLE`, another session can create the role, causing error `23505` (unique_violation). ## Fix Replace the non-atomic `IF NOT EXISTS` guard with a `BEGIN...EXCEPTION WHEN duplicate_object OR unique_violation THEN NULL; END;` block that safely handles the race. ## Verification | Check | Result | |---|---| | compound-engineering (pipeline-store + orchestrator + session-routes) | ✅ 41 passed | | Engine shard 1/2 | ✅ 3826 passed, 0 failed | | Merge gate | ✅ 471 passed | | Lint | ✅ exit 0 | |
||
|
|
51859148a7 |
fix(engine): implementation-incomplete merge failures fail-closed/resumable (#1991) (#2091)
## What & why Workflow graph merge failures classified `implementation-incomplete` (i.e. the merge node reports there is no implementation proof — no branch / no committed work) could still be routed to the no-op merge requester and false-complete the task as **done**. This hides genuinely unlanded work behind a green "merge" and is the merge-side sibling of the "(no feedback captured)" no-verdict dispatch defect. Closes the truthfulness gap: an `implementation-incomplete` merge-graph failure now **fails closed** when there is no executable proof to resume, or **requeues resumable parsed steps** back to `todo` for execution — it is never handed to a no-branch no-op merge requester. Refs #1991 (no-op merge truthfulness). Sibling of #1946 (no-verdict "(no feedback captured)" dispatch defect). ## Change - New classifier `routeImplementationIncompleteMergeGraphFailure(live, failedNode)`: - clears paused-aborted state + active worktree, - requeues resumable parsed steps via the existing execution-resume router when the task still has non-terminal workflow steps, - otherwise fails closed (`status: "failed"` with a logged, explicit reason). - Defense-in-depth: `isRetryableBenignMergePauseAbort` and the merge-requester route both short-circuit (`return false`) for `implementation-incomplete`, so this value can never reach the no-op merge requester. - `handleGraphFailure` routes genuine (non-global-pause, non-completion-finalize, non-user-paused) `implementation-incomplete` merge-graph failures through the new classifier. - Resume-eligibility predicate treats an `implementation-incomplete` merge failure with **no** incomplete steps as fail-closed, and keeps the premature-merge-with-incomplete-steps requeue path. Legitimate `noCommitsExpected` no-op merges are explicitly preserved (regression test included). ## Tests New regression coverage in: - `packages/engine/src/__tests__/reliability-interactions/merge-node-paused-abort-retryable.test.ts` — parametrized across merge node ids: (a) no-proof `implementation-incomplete` fails closed without requesting a no-op merge; (b) resumable parsed steps are requeued to `todo` for execution resume, not no-op-merged. - `packages/engine/src/__tests__/executor-fast-mode-workflows.test.ts` — a legitimate `noCommitsExpected` builtin:coding merge is still allowed (guard does not over-block). Verification (engine package): pnpm --filter @fusion/engine exec vitest run \ src/__tests__/executor-fast-mode-workflows.test.ts \ src/__tests__/reliability-interactions/merge-node-paused-abort-retryable.test.ts # => 2 files, 69 tests, 0 failures pnpm check:changesets # pass pnpm --filter @fusion/engine typecheck # 0 errors A `patch` changeset for `@runfusion/fusion` is included. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Prevented “implementation-incomplete” workflow merge failures from being treated as successful no-op merges. * Ensured tasks with resumable implementation steps move back to execution to continue where they left off. * Ensured tasks without sufficient implementation evidence fail safely rather than entering misleading retry/no-op paths. * Improved paused/aborted merge-failure handling to avoid incorrect completion states. * **Tests** * Added/expanded coverage for fast-mode coding merges and implementation-incomplete pause/abort retry classification. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Fusion <noreply@runfusion.ai> Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com> |
||
|
|
8cceee4eb3 |
fix: clean up OMP tool-bridge temp schema files on dispose (#2103)
## Summary - Follow-up to #2083: remove the temp `fusion-omp-mcp-schemas-*.json` file when the OMP Fusion `fn_*` MCP tool bridge is disposed. - Prevents schema JSON from accumulating under `tmpdir()` after every OMP ACP session. ## Context PR #2083 was merged before this cleanup commit landed on `feature/omp-acp`. This cherry-picks that fix onto main. ## Test plan - [x] `pnpm --filter @fusion-plugin-examples/omp-runtime test` (includes dispose removes schema path assertion) |
||
|
|
7568705244 |
fix(desktop): boot embedded Postgres in packaged app and ship omp dist (#2106)
## Summary Packaged Fusion desktop Local mode failed after the SQLite→Postgres cutover: 1. **Embedded Postgres** could not start from `app.asar` — platform packages resolve `initdb`/`postgres` via `import.meta.url` into the asar virtual path, and `spawn` fails with `ENOTDIR`. 2. **After Postgres was fixed**, Local mode still fell back to the mode chooser because `@fusion-plugin-examples/omp-runtime` was never built into `dist/` (dashboard imports it from `runtime-provider-probes.ts`). This PR makes packaged Local mode boot embedded Postgres reliably and keep the dashboard shell up. ### Changes - **CJS bootstrap** (`main-bootstrap.cjs`) as Electron `main`: patches `child_process.spawn` / `fs.promises.stat|chmod` before the ESM main loads so asar binary paths rewrite to real files. - **Materialize** the full native PG install (`bin` + `lib` + `share`) under `~/.fusion/embedded-postgres/runtime-bin/<plat-arch>/`. - **electron-builder**: full `asarUnpack` of embedded-postgres packages; allowlist PG deps and `@fusion-plugin-examples/**/*` (+ plugin-sdk / ACP SDK). - **Build** `fusion-plugin-omp-runtime` with the other dashboard-static runtime plugins; export `DASHBOARD_RUNTIME_PLUGIN_PACKAGES` for tests. - Unit coverage for asar path rewrite, packaging allowlists, and omp build inclusion. ## Test plan - [x] `pnpm --filter @fusion/core test:embedded-postgres` (23/23) - [x] Desktop packaging unit tests (`build-bundling`, `electron-builder-config`) - [x] Packaged macOS `Fusion.app` Local mode: - [x] `embedded postgres: ready on port … (database "fusion")` - [x] `desktopMode` stays `"local"` (no chooser fallback) - [x] `GET /api/health` → `status: ok`, `database.healthy: true`, `engine.available: true` - [x] Linux embedded binary lifecycle smoke (Docker aarch64, `@embedded-postgres/linux-arm64`) — initdb/start/persist/restart - [ ] CI release desktop jobs (macOS/Linux) when this lands - [ ] Windows packaged desktop Local + PG (separate agent / host) ## Verification notes | Platform | Embedded Postgres | Packaged Local shell | |----------|-------------------|----------------------| | macOS | Working | Working after this PR | | Linux | Native binary smoke pass | Full AppImage not built on this host | | Windows | Out of scope here | Separate verification | <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved embedded PostgreSQL reliability in Electron-packaged apps by rewriting bundled `app.asar` binary paths to their unpacked/materialized locations. * Ensured embedded PostgreSQL runtime binaries resolve correctly across platforms/architectures, with best-effort executable permissions and macOS dylib link normalization. * **Packaging** * Updated the desktop Electron entry to use a bootstrap module for embedded PostgreSQL binary resolution. * Expanded Electron Builder inclusion and asar-unpack rules for embedded-postgres and related packages, plus required runtime plugin/sdk assets. * **Tests** * Updated and added checks to match the new packaging and plugin/runtime expectations. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
4f037679ad |
feat: planner overseer session advisor (OMP advisor parity) (#2082)
## Summary Adds a **session advisor** to the planner overseer so Fusion can review live executor transcripts the way [oh-my-pi’s advisor](https://github.com/can1357/oh-my-pi/tree/main/packages/coding-agent/src/advisor) does — without replacing the existing lifecycle supervisor (stage watch, retry, merge confirmation, human-control withhold). ### What ships - **Emission guard** (`OverseerEmissionGuard`) — content-free phrase filter, session dedupe with severity-rank escalation, one accept per advisor update - **Session delta runtime** — queues agent-log deltas, drains through an advisor agent, drops backlog after 3 failures - **Session advisor service** — model gate, level matrix (`observe` / `steer` / `autonomous`), human-control re-check at inject, `[session-advisor]` steering comments - **OVERSEER.md / WATCHDOG.md** discovery for project review priorities - **AgentLogger `onEntriesFlushed`** + poll-backed agent-log cursor for durable deltas - Workflow settings: `plannerOverseerAdvisorProvider` + `plannerOverseerAdvisorModelId` (both required; empty = soft-disabled for cost safety) - Docs + changeset ### What does not ship (deferred) - Multi-advisor YAML roster, mutating advisor tools, reviewer/merger shadowing, true tool-abort interrupt ### Plan `docs/plans/2026-07-13-001-feat-overseer-advisor-parity-plan.md` ## Enablement 1. Set workflow **Session advisor model provider** + **Session advisor model id** 2. Oversight level `observe` (log only), `steer`, or `autonomous` (inject) 3. Optional: add `OVERSEER.md` or `WATCHDOG.md` in the project ## Test plan - [x] `pnpm --filter @fusion/core exec vitest run src/__tests__/overseer-emission-guard.test.ts` - [x] `pnpm --filter @fusion/engine exec vitest run` overseer-* unit tests (21 tests) - [x] Related planner-overseer / intervention regression tests - [x] `@fusion/engine` + `@fusion/core` typecheck - [ ] Manual: configure advisor model, run an executor task, confirm `[session-advisor]` inject + timeline metadata when concern is raised ## Residual Review Findings None from autofix pass (log-cursor ordering fix already committed). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added an off-by-default “session advisor” that can review live execution activity and provide severity-based guidance. * Added project and per-task controls to enable it, including a default enable switch and Quick Add / Task Detail toggles. * Enhanced advisor prompting by discovering and incorporating `OVERSEER.md`/`WATCHDOG.md` review files. * **Documentation** * Added architecture and settings documentation for the new session-advisor parity behavior. * **Bug Fixes** * Improved fail-soft handling so advisor behavior won’t disrupt execution. * Fixed concurrent PostgreSQL migration startup failures. * **Tests** * Added coverage for advice parsing, emission guarding, runtime behavior, and watchdog discovery. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
cdf67c1d98 |
fix(dashboard): stop Planning Mode retry loop, make AI sessions multi-tab (#2101)
## Problem Reported: planning gets stuck in a cycle of retrying and regenerating after a response was already supplied. After the user answers a planning question, `submitResponse` pushed the answer to history but left `session.currentQuestion` pointing at the just-answered question for the whole next generation. The planning SSE route's catch-up path re-emits `currentQuestion` to every fresh connection — and each FN-7946 auto-retry (#2073) opens a fresh connection. So after any generation error: 1. Auto-retry connects a fresh stream → the server re-emits the **already-answered** question. 2. The client treats any question event as progress: it **resets the 3-attempt auto-retry budget** and re-shows the answered question. 3. The retry regenerates; if it errors again the cycle repeats with a fresh budget — an unbounded retry/regenerate loop. Re-answering the stale question also 409-collided with the in-flight generation, feeding the same loop. ## Fix Invariant: `currentQuestion` is only set while the session is genuinely awaiting user input. - `submitResponse` clears it the moment an answer is accepted (normal turns and the deepening checkpoint), while preserving the legacy 200 respond contract on generation failure (the modal ignores the body and lets the SSE error drive recovery). - `retrySession` scrubs stale questions persisted by pre-fix builds before regenerating. - `buildSessionFromRow` only restores a question when the persisted row is `awaiting_input`. - `didSubmitSameAnswer` now compares against the last history entry so the duplicate-submit 409 message survives. - Agent onboarding gets the same fix (its SSE route also re-emits `currentQuestion` on connect); retry now asks the next question instead of re-asking the answered one. Surface enumeration: mission and milestone interviews keep questions the same way but their SSE routes never re-emit on connect, and the auto-retry budget machinery is Planning-Mode-only — planning + onboarding were the two affected surfaces. ## Symptom Verification - **Original symptom:** after answering a question, Planning Mode loops between "Retrying…" and regenerating, re-showing the already-answered question, with the auto-retry budget never exhausting. - **Exact reproduction:** answer a question, have the next generation fail (stuck watchdog/provider error), let the client auto-retry open a fresh SSE connection. - **Assertion it is gone:** new regression suite `planning-answered-question-reemit.test.ts` asserts `currentQuestion` is cleared mid-generation, on generation failure, on retry, and on restore from non-`awaiting_input` rows — so the SSE catch-up path has nothing stale to re-emit. All 5 tests fail against pre-fix code and pass with the fix; an onboarding regression test covers the sibling surface. ## Verification - New regression tests: 5/5 fail on pre-fix code, pass with the fix (plus 1 onboarding test). - Existing suites: 137 planning server tests pass (3 failures in `routes-planning.test.ts` fail identically without this change — pre-existing on the branch); all 69 `PlanningModeModal.planning-flow` client tests pass; `tsc --noEmit` clean; `pnpm check:changesets` passes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Made Planning Mode (and related planning controls) lock-free and multi-tab—no more take-over/active-in-another-tab lock overlays. * **Bug Fixes** * Fixed Planning Mode retry/generation flows where already-answered questions could reappear. * Ensured answered questions clear immediately and aren’t re-emitted during session recovery/SSE catch-up. * Improved session restoration and preserved legacy recovery behavior when generation fails after an answer. * **Tests** * Added regression coverage for the answered-question invariant and updated existing tests to reflect lock-free behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --- ## Follow-up: Planning Mode is now multi-tab via DB state (lock-free) Second commit removes all cross-tab coordination from planning — the persisted session row is the single source of truth and multiple tabs can read and interact with the same session: - **Server:** `/planning/*` routes no longer run `checkSessionLock` or parse `tabId`; a stale `tabId` from an older client is ignored instead of 409'd. Subtask/mission interview routes keep their existing lock behavior. - **Client:** `PlanningModeModal` drops `useSessionLock`, the `useAiSessionSync` BroadcastChannel broadcasts, `sessionTabId`/`lockSessionId` state, and the "Take Control" overlay. Tabs stay current via the per-session SSE stream plus the global `ai_session:updated` events `useBackgroundSessions` already consumes; concurrent writes resolve via the server's generation-in-progress guard (409). - **API client:** planning functions lose their `tabId` params. - **Fix uncovered by the refactor:** the 8s stuck-poll now resolves the session id inside each tick — the removed lock state was what previously re-armed the poll after Start Planning resolved the session id. - Also fixes a pre-existing PG-cutover break in `planning-generation-cancellation.test.ts` (`getSession` is async). Verification: 144 client planning tests and 137 server planning tests pass (the 3 remaining `routes-planning.test.ts` failures are pre-existing on the branch and fail identically without these changes); `tsc --noEmit` and eslint clean on changed files; `pnpm check:changesets` passes. Lock-conflict route tests were rewritten to assert lock-free semantics, plus a new modal test proving a session stays fully interactive with no lock acquisition even when another tab is active. --- ## Follow-up 2: the per-tab session lock is gone entirely Third commit extends the multi-tab model from planning to **every** AI interview surface (planning, subtask breakdown, mission interview, milestone/slice interview) and deletes the lock machinery root and branch. **Server** - Deleted the `/ai-sessions/:id/lock`, `/lock/force`, and `/lock/beacon` routes. - Dropped `checkSessionLock` from every planning/subtask/mission/milestone route (both copies — `routes.ts` and `mission-routes.ts`). A `tabId` from an older client is ignored, never 409'd; all `tabId` body parsing is gone. - Dropped `acquireLock` / `releaseLock` / `forceAcquireLock` / `getLockHolder` / `releaseStaleLocks` from `AiSessionStore`, plus the `@fusion/core` async helpers (`acquireAiSessionLock` et al) and core's re-exports. - Removed `lockedByTab`/`lockedAt` from `AiSessionRow`/`AiSessionSummary`, the upsert SQL, and all four session producers. **Client** - Deleted `useSessionLock` and the now-orphaned `getSessionTabId` util. - Removed the Take Control overlay, the "active in another tab" banners, and `BackgroundTasksIndicator`'s active-elsewhere gate (the confirm prompt and lock badge — sessions now just open). - Reduced `useAiSessionSync` to what its own comments already called it — a low-latency *status* supplement to SSE: no `activeTabMap`, `broadcastLock/Unlock/Heartbeat`, `owningTabId`, `tab:*` messages, or stale-heartbeat sweep. - Dropped `tabId` from every session API client function; removed the lock CSS. **Deliberately kept: the two DB columns.** `ai_sessions.locked_by_tab` / `locked_at` remain as dead, always-NULL columns with a deprecation note. Dropping them is an irreversible migration, and released binaries still name those columns explicitly in their upsert — an older install pointed at the same database would fail every session write. They can be dropped once no such binary can reach it. No code reads or writes them. **Verification**: 397 client tests and 137 server planning tests pass (the same 3 `routes-planning.test.ts` failures are pre-existing — verified identical on a clean stash); `tsc --noEmit` clean for `@fusion/core` and `@fusion/dashboard`; eslint clean on all changed files; the 30 PG `schema-applier` tests pass (they exercise the retained columns); `pnpm check:changesets` passes. The lock-conflict route tests and both modal lock tests were rewritten to assert the inverse: routes and modals stay fully interactive while another tab "holds" a lock, and the lock API is never called. --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
be55d0a987 |
fix(cli): reuse project stores for skill discovery (#2102)
## 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 --> |
||
|
|
9bdbdc5f16 |
FN-7955: stage bundled plugin skills
Ensure bundled Compound Engineering skills are present in published CLI packages. - Copy plugin src/skills directories into dist/plugins/<id>/skills during CLI packaging. - Add bundle-output coverage that verifies Compound Engineering SKILL.md files stage and resolve from the plugin root. - Document runtime-read bundled plugin asset staging and add a patch changeset for @runfusion/fusion. Files changed: .changeset/fn-7955-ce-skills-published.md | 7 ++++ docs/PLUGIN_AUTHORING.md | 3 ++ packages/cli/src/__tests__/bundle-output.test.ts | 51 ++++++++++++++++++++++++ packages/cli/tsup.config.ts | 14 +++++++ 4 files changed, 75 insertions(+) Fusion-Task-Id: FN-7955 Fusion-Task-Lineage: 32c4ad31-4f3a-478b-996f-ce6bcafd1e27 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
d0ce7829c0 |
FN-7953: fix mobile OAuth code submit taps
Submit Anthropic OAuth manual codes on the first mobile tap instead of requiring keyboard dismissal first. - Add a reusable touch action gesture hook that handles touch/pointer activation before synthetic clicks. - Wire the OAuth manual code Submit button to invoke submission on the first touch while preventing duplicate click handling. - Cover the mobile double-tap regression and document the UI bug pattern for future fixes. Files changed: .../oauth-manual-code-mobile-double-tap-submit.md | 60 +++++++++++ .../app/components/OAuthManualCodeForm.tsx | 31 +++++- .../__tests__/OAuthManualCodeForm.test.tsx | 110 +++++++++++++++++++++ .../hooks/__tests__/useTouchActionGesture.test.ts | 110 +++++++++++++++++++++ .../dashboard/app/hooks/useTouchActionGesture.ts | 89 +++++++++++++++++ 5 files changed, 399 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-7953 Fusion-Task-Lineage: d387cdbd-25a7-4b7d-add6-27a1ded5cbea Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
6aff4958ad |
fix(FN-7952): finish async workflow selection cutover
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. |
||
|
|
2d61976df0 |
fix(FN-7952): restore runtime state after PostgreSQL migration
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. |
||
|
|
278ede9dfa |
fix(FN-7952): recover provider failures without retry loops
Preserve authenticated CLI usage after migration, surface OAuth remediation, and use a single distinct model fallback before parking permanent failures. Keep transient credential errors retryable and confirm each OAuth expiry notification independently. Fusion-Task-Id: FN-7952 |
||
|
|
79d4299be2 |
fix: preserve provider and workflow behavior after migration
Use canonical Anthropic OAuth refresh, keep CLI-backed providers out of API-key auth rows, parse Grok's omitted zero usage, and carry board workflow context into task creation. |
||
|
|
678265a526 |
fix(cli): show live SQLite migration progress
Report source scans, per-table copy milestones, checksum phases, verification outcomes, and unambiguous failure or finalization status during first-boot and manual migrations. |
||
|
|
0312d2e140 | fix(core): preserve late SQLite columns during cutover | ||
|
|
7677ab07dc |
fix: add chat_sessions columns to schema baseline + fix remaining PG auth bugs (shard 4) (#2096)
## Summary Fixes shard 4 full-suite failures: chat_sessions schema baseline gap + two remaining PG auth bugs missed by PR #2086. **Scope: shard 4 only.** Shards 1/2 (engine timeouts) and shard 3 (compound-engineering CI-only failure) are separate issues not addressed here. ## Changes ### Schema baseline gap — `chat_sessions` missing columns (42703 error) - **`0000_initial.sql`**: Added `validator_thinking_level` and `planning_thinking_level` columns to `CREATE TABLE project.chat_sessions`. These exist in the Drizzle schema (`project.ts:1492-1493`) but were missing from the SQL baseline, causing `column does not exist` on all chat_sessions inserts in fresh test databases. - **`postgres-health.ts`**: Added both columns to `EXPECTED_PROJECT_COLUMNS` self-heal list so existing databases also get them via ALTER TABLE. **Fixes**: `chat-store-content-search-edit.pg.test.ts` (5 tests), `satellite-db-injected-stores.test.ts` (2 tests) ### Remaining auth bugs (password auth failed for user "runner") - **`allocator-cross-project.test.ts`**: Still had `process.env.USER` in inline adminExec — missed by PR #2086's batch fix. Replaced with `PG_TEST_URL_BASE` connection string. - **`connection.test.ts`**: Used `FUSION_PG_TEST_URL` (not set on CI) with a bare default URL lacking credentials. `postgres.js` fell back to OS user `runner`. Changed to derive from `FUSION_PG_TEST_URL_BASE` which includes credentials. **Fixes**: `allocator-cross-project.test.ts` (2 tests), `connection.test.ts` (3 tests) ## Verification | Check | Result | |---|---| | Merge gate (`pnpm test:gate`) | ✅ 294 + 114 + 63 = 471 passed | | chat-store-content-search-edit | ✅ 5 passed | | satellite-db-injected-stores | ✅ 10 passed | | allocator-cross-project | ✅ 2 passed | | connection | ✅ 13 passed | | Lint | ✅ exit 0 | | Typecheck | ✅ clean | ## Not in scope - **Shards 1/2**: Engine test suite timeouts with `getAsyncLayer`/`updateSettings` mock warnings. Pre-existing. - **Shard 3**: `compound-engineering stage-skill-loading.test.ts` — 14 tests fail on CI (`TypeError: Cannot read properties of undefined (reading 'close')`), pass locally. Likely CI-specific teardown issue. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added separate `validator_thinking_level` and `planning_thinking_level` fields to chat session data, including database schema and health-check recognition. * **Bug Fixes** * Improved PostgreSQL test connectivity by using configured connection URL settings instead of hardcoded local defaults. * Made Postgres-related test teardown null-safe to avoid failures when setup doesn’t complete. * **Tests** * Updated automated test quarantine/exclusions for known failing engine and reliability-interaction cases. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
379d450c38 |
fix(core): preserve required empty JSON during migration (#2099)
## Summary - preserve empty and whitespace-only legacy SQLite text as JSON string scalars when the PostgreSQL target is required `jsonb` without a default - keep nullable/defaulted JSON behavior unchanged - canonicalize converted JSON before source/target checksum comparison - cover empty, whitespace, malformed, and scalar workflow IR values ## Test plan - `FUSION_PG_TEST_URL_BASE=postgresql://127.0.0.1:55432 nix shell nixpkgs#postgresql_15 -c bash -c 'corepack pnpm --filter @fusion/core exec vitest run src/__tests__/postgres/sqlite-migrator.test.ts -t "preserves empty, whitespace, malformed, and scalar values" --reporter=dot'`\n- `corepack pnpm --filter @fusion/core typecheck`\n- `corepack pnpm check:changesets --strict`\n- `corepack pnpm --filter @runfusion/fusion build` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Improved SQLite-to-PostgreSQL migrations for required `jsonb` fields. - Preserves empty, whitespace-only, malformed, and scalar JSON values instead of replacing them with defaults or `NULL`. - Maintains existing `nullable` and default-value behavior. - Improved migration verification for converted `jsonb` data. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
945d629e3b |
fix(core): make SQLite cutover lossless and project-local
Preserve legacy-only tables, recover partial migration ownership, and enforce project-local keys, relationships, agents, merge queues, task IDs, archives, and monitor state with PostgreSQL RLS. Report successful cutovers once in the dashboard and system inbox with retained SQLite paths and Discord support details. |
||
|
|
7c8a84fb2f |
fix(core): converge multi-project SQLite cutover
Migrate central SQLite state once per cluster, isolate project metadata, and preserve file-local revision identities while verifying accumulated shared tables. |
||
|
|
1b9dce22c0 | fix(desktop): harden embedded Postgres packaging | ||
|
|
12a4fbe9bb | fix(core): complete legacy SQLite cutover | ||
|
|
99870ba329 | fix(core): recover partial PostgreSQL migrations | ||
|
|
dff864e098 |
feat: harden permanent-agent heartbeat instructions (#2081)
## Summary Hardens permanent-agent operating law while keeping the heartbeat/executor split: - **Critical Rules** in task-scoped and no-task heartbeat system prompts (survive custom `HEARTBEAT.md`) - Stronger default procedures: disposition checklist, scoped-wake, blocked dedup, progress note style - **Wake Delta multi-assign inventory** (ranked, cap 8, coordination-only framing) + `checkout_conflict` regression test - Standing instructions six-section template for blank custom create / empty detail insert - Onboarding interview guidance to prefer structured `instructionsText` - Playbooks, CONCEPTS, agents.md accuracy; remove stale agent gap-analysis doc Plan: `docs/plans/2026-07-12-001-feat-permanent-agent-heartbeat-instructions-plan.md` ## Test plan - [x] `pnpm --filter @fusion/core exec vitest run src/__tests__/assigned-task-ranking.test.ts` - [x] `pnpm --filter @fusion/engine exec vitest run src/__tests__/agent-heartbeat-procedures.test.ts src/__tests__/heartbeat-executor.test.ts -u` - [x] `pnpm --filter @fusion/dashboard exec vitest run app/components/__tests__/standing-instructions-template.test.ts` - [ ] CI gate green on PR ## Residual Review Findings None recorded at open (inline review; no residual sink). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added ranked multi-assignment context to agent heartbeat wake-ups, including task status, ownership, and lease details. * Added standing-instructions templates for creating and editing permanent agents. * Improved onboarding guidance with a consistent six-section instruction structure. * Added clearer heartbeat handling for blocked tasks, no-task runs, and checkout conflicts. * **Documentation** * Added permanent-agent heartbeat playbooks and expanded coordination glossary entries. * Updated documentation indexes and heartbeat behavior guidance. * **Tests** * Added coverage for task ranking, instruction templates, wake-up context, and conflict handling. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
30a83f21fc |
fix(engine): requeue stale assistant continuations (#2095)
## Summary - detect persisted executor sessions that cannot continue from an assistant message - clear the stale session pointer after the executor lock is released - requeue the task with workflow progress preserved instead of marking it failed ## Test plan - `pnpm --filter @fusion/engine exec vitest run src/__tests__/executor-step-session.test.ts -t "clears a stale assistant-continuation resume session and requeues without marking the task failed" --project=engine-default --silent=passed-only --reporter=dot` - `pnpm --filter @fusion/engine typecheck` - `pnpm build` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved recovery when an assistant continuation session becomes stale by restarting a fresh session with bounded retries, preserving overall task progress. * Clears invalid persisted session/continuation state and defers requeue until coordination cleanup is safe. * When retries are exhausted, tasks are marked failed and the error callback runs (without routing to review). * **Tests** * Added coverage for stale-session recovery, repeated-stale behavior, correct (or skipped) requeue decisions, and progress/error handling paths. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
b563b12662 |
feat: add Oh My Pi (omp) ACP runtime plugin (#2083)
## 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 --> |
||
|
|
d7e072a03c |
fix: add psql binary guard + delete expired quarantine tests (ratchet) (#2090)
## Summary Follow-up to PR #2086 addressing two Greptile review findings. ## P2 — Missing `psql` binary guard (Greptile P2) `hasPg` in `_helpers.ts` previously checked only TCP connectivity to PostgreSQL. But `adminExecAsync()` shells out to the `psql` CLI for DDL (`CREATE/DROP DATABASE`). On a runner where Postgres is reachable but `psql` isn't installed, tests would fail with `spawn psql ENOENT` instead of skipping cleanly. **Fix**: Added `hasPsql = spawnSync("psql", ["--version"]).status === 0` to the `hasPg` guard, so tests skip when either Postgres is unreachable OR `psql` is missing. ## P1 — Expired quarantine entries (Greptile P1) The 16 dashboard test files quarantined on 2026-06-25 were past the 14-day deletion ratchet (AGENTS.md: "DELETED after 14 days unless rescued"). Per the ratchet, the test files were deleted and all references removed: - **Deleted 16 test files** (CSS drift, mock drift, mobile-render regressions) - **Removed 16 entries** from `scripts/lib/test-quarantine.json` (only the CLI entry remains) - **Emptied `quarantinedDashboardTests` array** in `packages/dashboard/vitest.config.ts` ## Verification | Check | Result | |---|---| | Merge gate (`pnpm test:gate`) | ✅ 294 + 99 + 63 = 456 passed | | Dashboard curated-gate | ✅ passes (891 files, 892 executed, 1 skip-listed, 1 quarantined) | | Typecheck (engine) | ✅ clean | | Lint | ✅ exit 0 | <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Removed multiple outdated dashboard UI, CSS/token, theme contrast, and API/route test suites. * Updated dashboard test configuration to stop excluding quarantined tests and to prune the quality shard to the current set. * Updated the Vitest split/config guard to match the new test fixture set. * Improved PostgreSQL test detection by requiring the `psql` CLI before running database checks. * Adjusted quarantine tracking by adding a new CLI extension distribution ledger entry and removing obsolete dashboard quarantine entries. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
d8f0b1a268 |
Restore PostgreSQL integration parity (#2089)
## 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 |
||
|
|
c25f8b796d |
Harden PostgreSQL migration foundation (#2088)
## Summary - make SQLite-to-PostgreSQL cutover retryable, fail-closed, versioned, and transactionally serialized - isolate migration sessions from runtime traffic and apply schema upgrades through `0002` - enforce tenant ownership across automations, analytics, activity, usage, agent runs, evals, and todos - replace expired SQLite-only coverage with PostgreSQL parity and concurrency coverage This is PR 1 of 2. The stacked follow-up restores PostgreSQL parity for CLI, engine, dashboard, and bundled integrations. ## Verification - `pnpm check:changesets --strict` - `pnpm --filter @fusion/core typecheck` - migration schema, connection, and SQLite cutover suite: 57 tests passed - `pnpm test:gate`: 463 tests passed ## Post-Deploy Monitoring & Validation - take a restorable PostgreSQL backup before deploy - confirm `fusion_schema_migrations` contains `0002` - confirm each expected project has a complete `fusion_sqlite_migrations` row - verify no null or empty tenant ownership in automations, activity logs, agent runs, and usage events - monitor for ownership inference failures, cutover verification failures, and migration session errors - restore the backup for data rollback; do not downgrade the tenant-isolation schema in place <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * PostgreSQL-backed analytics and live dashboard metrics are now project-scoped (activity, tools, monitor, signals, and live snapshots). * Evaluation runs and scheduled eval batches received lifecycle improvements (ordering, updates, and execution flow). * Todo list changes now emit events; WhatsApp persistence and project-scoped roadmap data are supported. * **Bug Fixes** * SQLite-to-PostgreSQL cutovers now fail safely with stronger verification, serialized cutover handling, and safer project ownership. * PostgreSQL backend writes and reads are now strictly project-isolated and fail closed when project context is missing. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
3fce53640e |
fix(dashboard): keep completed Planning Mode sessions in history after multi-task creation (#2079)
## Problem
In the dashboard **Planning Mode** screen, a planning session that runs
to completion **and creates multiple tasks** disappears from the "saved
sessions" history panel ("No saved sessions yet").
## Root cause
The multi-task route `POST /api/planning/create-tasks` called
`cleanupSession(planningSessionId)` → `unpersistSession` →
`_aiSessionStore.delete`, **deleting the persisted `ai_sessions` row**.
The single-task route `POST /api/planning/create-task` deliberately uses
`releaseSession` instead — it releases the in-memory runtime but
**keeps** the persisted completed row, which is what the history list
reads (`listAll` includes completed sessions). So multi-task creation
erased its own history entry.
## Fix
Switch the multi-task route to `releaseSession`, matching the
single-task path. The completed `type: "planning"` session row now
survives task creation and appears in history.
## Tests
Adds a regression test in `routes-planning.test.ts` asserting the
persisted planning row survives multi-task creation (verified it fails
against the old `cleanupSession` behavior). Merge gate green locally;
changeset included.
Made with Claude (see `Co-Authored-By` trailer).
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Fixed an issue where Planning Mode multi-task sessions could be
removed from planning history after task creation.
* Completed multi-task planning sessions are now reliably retained with
their completed status.
* **Tests**
* Added a regression test for the multi-task Planning Mode flow to
confirm all tasks are created and the planning session remains persisted
in history.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: Claude <noreply@anthropic.com>
|
||
|
|
bc348345a4 |
fix(engine): break Plan Review REVISE replan loop (feedback + bounded cap) (#2078)
## Problem A task whose Plan Review step returns verdict `REVISE` can loop forever: plan → plan-review REVISE → `needs-replan` → re-plan → near-identical plan → REVISE → repeat. The triage **pre-execution** Plan Review gate (`runPlanReviewBeforeExecution`) sets `status: "needs-replan"` on REVISE with **no cap and no escape to `awaiting-approval`** — unlike the executor graph path, which already has `PLAN_REVIEW_REPLAN_HARD_CAP`. Under `planApprovalMode: require-all` there is also no human exit, because the task never reaches `awaiting-approval`. Separately, replan feedback (`triage.ts`) was derived only from `task.log` comment actions + the latest user comment; it never consulted the plan-review verdict stored in `task.workflowStepResults`. ## Fix 1. **Thread plan-review feedback into replan** — when re-planning with no comment-derived feedback, seed `buildSpecificationPrompt` from the most recent `plan-review` REVISE `output` in `workflowStepResults` (existing user/AI-comment precedence preserved). 2. **Bounded cap** — new `planReviewReplanCount` counter (`types.ts`, `store.ts` column + updateTask, `db.ts` migration 146, `manual-retry-reset.ts`). After `PLAN_REVIEW_GATE_REPLAN_CAP = 3` consecutive REVISE replans the task escalates to `awaiting-approval` (`awaitingApprovalReason: "plan-review-replan-cap"`) instead of replanning. Counter resets on APPROVE. ## Tests Adds `triage-replan-feedback-from-plan-review.test.ts` and `triage-plan-review-replan-cap.test.ts`. Merge gate green locally (`verify:fast`, `test:gate` 337+63, `lint`); changeset included. Made with Claude (see `Co-Authored-By` trailer). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Prevented Plan Review “REVISE” from looping indefinitely by enforcing a bounded replan cap. * After repeated Plan Review replans, tasks now escalate to an approval-hold state with a dedicated reason. * Improved replan feedback by seeding from the latest Plan Review output when no explicit feedback is available; the counter clears when Plan Review approves. * Manual retries now reset the Plan Review replan cap counter. * **Documentation** * Added release notes describing the Plan Review replan safeguards. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: gsxdsm <gsxdsm@users.noreply.github.com> Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> |
||
|
|
03966ecb79 |
Fix multi-project branch-group route store scoping (#2085)
## Summary Conflict resolution for closed [#2074](https://github.com/Runfusion/Fusion/pull/2074) (FN-001 multi-project branch-group store scoping), rebased onto current `main`. #2074 closed when its fork head was briefly reset to `main` during a ref update; maintainer write access to the fork head only works while the PR is open, so that PR could not be reopened without new fork commits. This branch carries the same fix: - Request-scoped `TaskStore` for branch-group list/read/assign/promote/abandon - Integrated reconcile/close uses the request store for cwd + persistence - Compatible with async branch-group store APIs and main’s CentralProjectIdentity (`projectId` trim) - Postgres durable FN-7438 tests + padded `projectId` regression ## Verification - `FUSION_DASHBOARD_DEEP=1 pnpm --filter @fusion/dashboard exec vitest run --project dashboard-api src/__tests__/routes-branch-groups.test.ts src/__tests__/integrated-routers-group-pr-token.test.ts src/__tests__/routes-context-project-identity.test.ts --silent=passed-only --reporter=dot` — 3 files, 41 tests passed. --------- Co-authored-by: Tchorizo <295840812+Tchorizo@users.noreply.github.com> Co-authored-by: Fusion <noreply@runfusion.ai> |
||
|
|
8de0fbcd01 |
fix: repair full-suite failures after SQLite-to-PostgreSQL cutover (#2086)
## Summary
Fixes all deterministic full-suite (non-blocking) CI failures on `main`
caused by the SQLite-to-PostgreSQL cutover (VAL-REMOVAL-005).
## Changes
### i18n Key Parity (5 locale files)
- Added missing `taskPopupsBoardListOnly` +
`taskPopupsBoardListOnlyHelp` keys (empty strings per convention) to
zh-CN, zh-TW, fr, es, ko `app.json`
### Dashboard Curated-Gate Guard (`scripts/lib/test-quarantine.json`)
- Repaired "mirror drift": 16 dashboard test files were quarantined in
`vitest.config.ts` but never added to the quarantine ledger. Added all
16 with failing run URLs and `quarantinedAt` dates.
### Line-Count Audit CI Cache (`.github/workflows/full-suite.yml`)
- Removed `skip-install: "true"` from `line-count-audit` job —
`setup-node@v5` with `cache: pnpm` failed post-step because no
`node_modules` existed to cache.
### Engine Slow Tier — Full PG Migration
- **CI**: Added PostgreSQL service container to `test-slow` job (same
config as `test-shards`)
- **`_helpers.ts`**: Migrated `makeReliabilityFixture()` from removed
SQLite `Database.init()` to PG-backed `TaskStore`:
- Added `probeTcpReachable()` (TCP probe, copied from shared harness)
- Added `hasPg` export (uses TCP probe, not env-var guess)
- Added `adminExecAsync()` (`Promise.withResolvers`, psql via
`PG_TEST_URL_BASE`)
- Added `createPgLayer()` (fresh PG database + schema baseline +
`AsyncDataLayer`)
- Updated cleanup: `await store.close()`, close layer, drop database
- **Slow test**: Migrated 24 sync SQLite API calls to async PG APIs:
- `store.getRunAuditEvents()` → `await auditEvents(store, ...)` via
exported `queryRunAuditEvents`
- `store.getDatabase().prepare(...)` → Drizzle queries via
`store.getAsyncLayer()!.db`
- **Core exports**: Added `queryRunAuditEvents` from `async-audit.ts`
and `eq as drizzleEq` from `drizzle-orm`
- **22 reliability test files**: Added `hasPg` guards so tests skip
locally when PG is unavailable
### Shard 3 — PG Test Auth Bug (18 postgres test files)
- Replaced `psql -U ${process.env.USER ?? "postgres"}` with `psql
"${PG_TEST_URL_BASE}/postgres"` connection string. On GitHub Actions,
`process.env.USER` is `'runner'`, not `'postgres'`, causing auth
failure.
### Shard 3 — Removed Function Tests (`mesh-task-replication.test.ts`)
- Deleted 3 tests for functions intentionally removed in PostgresCutover
(`buildMeshReplicatedTaskCreatePayload`, `toReplicatedCreateInput`,
`taskMatchesReplicatedCreate`). Kept `buildBootstrapPrompt` test.
### Shard 3 — Store Thinking Levels (`store-thinking-levels.test.ts`)
- Migrated from removed SQLite path to PG-backed
`createTaskStoreForTest` + `pgDescribe`.
## Verification
| Check | Result |
|---|---|
| Merge gate (`pnpm test:gate`) | ✅ 294 + 99 + 63 = 456 passed |
| Engine slow tier (22 tests) | ✅ 22/22 passed |
| i18n parity tests | ✅ 7 passed |
| mesh-task-replication | ✅ 1 passed |
| PG data-layer | ✅ 14 passed |
| PG taskstore-lifecycle | ✅ 16 passed |
| store-thinking-levels | ✅ 1 passed |
| Dashboard curated-gate | ✅ passes |
| Typecheck (engine + core) | ✅ clean |
| Lint | ✅ exit 0 |
## Parked (not in scope)
- **Shards 1/2 timeout**: Engine test suite exceeds CI time budget.
Pre-existing, unrelated to these fixes.
- **2 latent PG files** (`chat-store-content-search-edit`,
`satellite-db-injected-stores`): Surface a separate pre-existing schema
baseline gap. Out of scope.
|
||
|
|
b5c76af700 |
fix(core): preserve jsonb defaults during PostgreSQL migration (#2080)
## Summary - preserve target defaults when legacy SQLite rows contain `NULL` or empty strings for `NOT NULL` jsonb columns - derive the fallback from PostgreSQL column metadata instead of hard-coding table or column names - keep migration checksum conversion aligned with inserted values - add regression coverage for legacy null JSON fields ## Test plan - `corepack pnpm@10.33.0 --filter @fusion/core typecheck` - `FUSION_PG_TEST_SKIP=1 corepack pnpm@10.33.0 --filter @fusion/core exec vitest run src/__tests__/postgres/sqlite-migrator.test.ts` - `corepack pnpm@10.33.0 --filter @fusion/core build` The PostgreSQL-backed integration suite requires `psql`, which is unavailable in this environment; CI should exercise the added migration case against PostgreSQL. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved SQLite-to-PostgreSQL migration for legacy rows containing `NULL` or empty JSON values. * For eligible `NOT NULL` `jsonb` columns, the migrator now preserves/apply compatible PostgreSQL column defaults instead of writing SQL `NULL`. * Migration verification now aligns with the final values inserted into PostgreSQL to prevent checksum mismatches. * **Tests** * Added an end-to-end legacy migration case to confirm `jsonb` fields materialize as empty defaults (e.g., `[]`) rather than staying `NULL`. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
9aa2852033 |
fix: resolve an explicit central-registry project id for all dashboard API requests
Implements the explicit-project-identity directive at the route layer: a request's store is resolved from request projectId -> the daemon's registered launch project id -> only for unregistered launch directories, the raw launch-dir store (one-time warn). Resolution funnels through a single seam (routes/context.ts resolveRequestProjectId + resolveStoreForProjectId); the server.ts realtime resolveScopedStore delegates to the same function instead of mirroring it. Scattered 'projectId ? getOrCreateProjectStore : store' ternaries in todo/goals/mission/insights/research/evals routes now use the shared seam. Code-review fixes folded in (multi-agent ce-code-review, 10 reviewers): - mission interview drafts list/discard resolve the same project id the start endpoint stamps (write/read no longer split namespaces) - chat stream-attach guard treats legacy null-projectId sessions as launch-owned instead of 404ing; planner-chat dedup retries unscoped to reuse legacy sessions instead of duplicating them - getProjectIdFromRequest trims and rejects whitespace-only ids - evals/research middleware forwards store-resolution failures to Express (previously rethrew inside a detached promise chain and hung the request) - one-time launch-dir fallback warning routes through runtimeLogger - seam + delegation + whitespace + engine-fallthrough covered in routes-context-project-identity.test.ts (10 cases) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
f7d29dd1da |
chore: archive pre-0.60 changelog notes and distill corrupted 0.47–0.59 entries
Raise the durable archive cutoff to 0.60.0, keep only the current release in CHANGELOG.md, and rewrite labeled summary/category/dev package aggregates for 0.47–0.59 into operator-facing Highlights/New/Fixed notes. |
||
|
|
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> |
||
|
|
3ccc9f96e6 |
fix: bind rootDir boots to the central project registry and re-key the migrated config row
Implements the central-project-identity architecture: cwd/rootDir is ONLY a
lookup key into central.projects; project identity (the partition key for
every task/config read and write) comes from the registry.
- createTaskStoreForBackend resolves the registered project id by path for
rootDir-only boots and binds the AsyncDataLayer to it. Previously
'fn dashboard' / 'fn serve' / desktop booted their main store UNBOUND, so
unscoped API requests wrote NULL-project_id rows the projectId-bound engine
could never see, and unbound config reads (id = 1) were indeterminate once
multiple per-project rows existed. The engine already worked registry-first
(resolveLocalProjectWorkingDirectory); this brings the store boots in line.
- Step 5.5 auto-migration now also re-keys the migrated legacy config row
('' -> project id, guarded against clobbering an existing per-project row).
configScope() has no bound->'' fallback, so the migrated project settings,
workflowSteps, taskPrefix, and nextId counters were silently invisible to
bound readers right after a successful migration.
- Unregistered paths resolve to undefined and boot unbound, preserving legacy
single-project behavior with unfiltered readers.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
0f3a3d3f49 |
fix: stamp migrated task rows with the central-registry project id on rootDir-only boots
The SQLite -> PostgreSQL auto-migration leaves project_id NULL and Step 5.5 only stamped rows when options.projectId was bound — but 'fn dashboard' in the project directory (the main cutover path) boots with rootDir only, so every migrated row stayed NULL, project-bound readers (engine InProcessRuntime, dashboard project-store-resolver) filtered them all out, and the board showed no tasks right after a successful migration. The stamping id is now resolved from the freshly-migrated central registry by matching the registered project path to rootDir; projects never registered centrally keep NULL rows, matching their unbound readers. Integration test covers the rootDir-only stamp. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
7aa969892a |
fix: count actually-inserted rows in the SQLite -> PostgreSQL migrator via RETURNING
insertBatch read the driver wrapper's count (result.count ?? result.rowCount ?? rows.length), which reported 0 through drizzle's execute even when every row landed — migration reports showed 'inserted 0' for fully-migrated tables and the startup banner's migratedRows total was wrong. ON CONFLICT DO NOTHING RETURNING 1 yields exactly one row per row actually inserted, making the count driver-agnostic and correctly excluding conflict-skipped rows. Idempotency test now asserts first-run insertedRows == sourceRows and re-run insertedRows == 0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
fbcd00204a |
fix: snake_case legacy SQLite table names in the PG migrator and keep PG-mode boots from touching SQLite
Two post-cutover fixes: 1. The SQLite -> PostgreSQL migrator matched table names verbatim while only column names were snake_cased, so all 22 legacy camelCase tables (activityLog, runAuditEvents, mergeQueue, taskClaims, projectNodePathMappings, ...) resolved zero PostgreSQL columns and were silently skipped as 'no PostgreSQL counterpart'. First observed as 'Project/node path mapping not found' on engine start because central.project_node_path_mappings was never populated. TablePlan now carries a snake_cased pgTable used for every PostgreSQL-side operation; regression test migrates a camelCase activityLog into project.activity_log. 2. The first-boot auto-migration guard opened .fusion/fusion.db with a read-write DatabaseSync on every boot (isValidSqliteDatabaseFile), which performs WAL recovery + checkpoint — writing the legacy file on each PG boot. The PG emptiness count now runs before the SQLite probe, so steady-state PG boots never open the legacy SQLite files at all. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
8596035159 |
fix: bind fallback CentralCore to the async layer so projectId-only boots resolve projects on PostgreSQL
getOrCreateForProjectImpl constructed its fallback CentralCore without the caller's AsyncDataLayer. Post-cutover a layer-less CentralCore has no database at all (the SQLite CentralDatabase path is deleted and init() degrades to a no-op), so project lookups returned empty and every projectId-only boot through the startup factory (engine InProcessRuntime, dashboard project-store-resolver) failed with 'Project "<id>" not found' even though central.projects had the row — dashboard UI came up but the engine never connected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
eb5c81cc59 |
fix: widen ce_sessions.last_activity_at to bigint so PG first-boot migration survives epoch-ms values
project.ce_sessions.last_activity_at stores Date.now() epoch milliseconds but was declared integer in both the Drizzle shape and the CE plugin schema-hook DDL, overflowing PG int4 during the SQLite -> PostgreSQL first-boot auto-migration and blocking startup at task-store init. Now bigint in both sites, with an idempotent ALTER for datadirs that already materialized the integer column, plus a schema-wide invariant test that no numeric *_at/*_time/*_timestamp column is 32-bit integer. 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> |
||
|
|
9a43aa1d24 |
FN-7951: harden runGenerationWithTimeout abort cancellation across planning surfaces
Ensures aborted AI generation (timeout, user-stop, displacement, retries) actually tears down the in-flight agent session instead of only rejecting the Promise.race waiter, since provider SDKs may ignore AbortSignal. - Add a once-only onAbort teardown hook to GenerationGuard, invoked for timeout, user-stop, and displaced abort causes so consumers can dispose their in-flight session exactly once. - Give planning's local generation runner (runGenerationWithTimeout) the same guaranteed once-only abortTeardown for timeout, user-stop, displacement, stuck, and loop aborts, replacing the ad hoc dispose-on-timeout-only logic. - Forward the AbortSignal into planning's history-replay prompt, turn prompts, and JSON-parse-retry prompts, and short-circuit with createAbortError() when the signal is already aborted before/after each prompt call. - Wire subtask-breakdown's onTimeout/onUserStop handlers to the new onAbort hook instead of disposing the agent directly, keeping teardown centralized in the guard. - Add GenerationInProgressError / TargetGenerationInProgressError handling in mission-routes to return 409 Conflict instead of a generic 500 when a generation is already running. - Extend mission-interview and milestone-slice-interview generation paths with matching abort-forwarding and teardown behavior, plus new/expanded tests covering cancellation across timeout, user-stop, displacement, and retry paths. - Add a patch changeset documenting the fix for @runfusion/fusion. Files changed: .changeset/harden-generation-abort.md | 7 ++ .../src/__tests__/ai-session-timeout.test.ts | 41 +++++-- .../__tests__/milestone-slice-interview.test.ts | 72 ++++++++++++- .../src/__tests__/mission-interview.test.ts | 64 ++++++++++- .../planning-generation-cancellation.test.ts | 82 ++++++++++++++ .../src/__tests__/subtask-breakdown.test.ts | 21 +++- packages/dashboard/src/ai-session-timeout.ts | 33 +++++- .../dashboard/src/milestone-slice-interview.ts | 120 +++++++++++++++++++-- packages/dashboard/src/mission-interview.ts | 119 ++++++++++++++++++-- packages/dashboard/src/mission-routes.ts | 12 +++ packages/dashboard/src/planning.ts | 70 +++++++++--- packages/dashboard/src/subtask-breakdown.ts | 10 +- 12 files changed, 589 insertions(+), 62 deletions(-) Fusion-Task-Id: FN-7951 Fusion-Task-Lineage: debcd6a9-f54e-4ef3-87e1-4f06be0b5f64 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
6e0fde860c |
FN-7949: fix deleted planning-mode session resurrecting after in-flight generation completes
Adds a bounded-TTL delete tombstone to AiSessionStore so a straggling post-delete generation write can never resurrect a session the user explicitly deleted. - AiSessionStore now records a 10-minute delete tombstone (id -> deletion timestamp) in delete(), deleteByIdAndType(), and bulk cleanup paths (cleanupOld/cleanupStaleSessions/emitDeletedSessions). - upsert() checks the tombstone first and drops (no-ops) any write for a tombstoned id without touching SQLite or emitting ai_session:updated, fixing the root cause once in the shared store rather than per-producer (planning.ts, subtask-breakdown.ts, mission-interview.ts, milestone-slice-interview.ts). - Tombstone entries are pruned lazily on check and piggyback on the existing cleanupStaleSessions() cadence so the in-memory map cannot grow unbounded. - Adds a changeset (patch) documenting the user-facing fix. - Updates docs/architecture.md and docs/storage.md with the new "AI session delete tombstones" behavior. - Adds regression tests covering the tombstone guard in ai-session-store.test.ts and routes-planning.test.ts. Files changed: .changeset/fn-7949-ai-session-delete-tombstone.md | 7 + docs/architecture.md | 2 +- docs/storage.md | 12 +- packages/dashboard/src/__tests__/ai-session-store.test.ts | 145 +++++++++++++++ packages/dashboard/src/__tests__/routes-planning.test.ts | 200 ++++++++++++++++++++- packages/dashboard/src/ai-session-store.ts | 83 +++++++++ 6 files changed, 446 insertions(+), 3 deletions(-) Fusion-Task-Id: FN-7949 Fusion-Task-Lineage: 8e509dae-0cc5-46cd-9c4b-9048cfda56d3 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
1ff83a2735 |
chore(release): v0.60.0
Version bump via changesets. |
||
|
|
4e7e013d6f |
FN-7947: add Plan action to context menu for pre-execution task cards
Adds a Plan action to Board/List task context menus so triage/hold/intake cards can jump straight into Planning Mode without duplicating a task. - Add `onPlan` handler and `isPreExecutionHoldColumn` gate to `TaskContextMenu` so Plan only appears for pre-execution (triage/intake/hold) columns, and only when a host wires the handler - Wire the Plan action through `Board.tsx`, `Column.tsx`, `ListView.tsx`, and `WorktreeGroup.tsx` so both board and list views expose the new menu item - Surface the Plan entry point on `TaskCard.tsx` - Add test coverage in `TaskContextMenu.test.tsx`, `TaskCard.test.tsx`, and `ListView.test.tsx` for the new gating/wiring behavior - Document the new action in `docs/dashboard-guide.md` - Add a minor changeset for `@runfusion/fusion` Files changed: .changeset/fn-7947-plan-context-menu-action.md | 7 ++ docs/dashboard-guide.md | 10 ++- packages/dashboard/app/components/Board.tsx | 10 ++- packages/dashboard/app/components/Column.tsx | 4 + packages/dashboard/app/components/ListView.tsx | 15 +++- packages/dashboard/app/components/TaskCard.tsx | 24 +++++- packages/dashboard/app/components/TaskContextMenu.tsx | 18 ++++ packages/dashboard/app/components/WorktreeGroup.tsx | 9 ++ packages/dashboard/app/components/__tests__/ListView.test.tsx | 21 +++++ packages/dashboard/app/components/__tests__/TaskCard.test.tsx | 96 ++++++++++++++++++++++ packages/dashboard/app/components/__tests__/TaskContextMenu.test.tsx | 32 ++++++++ 11 files changed, 236 insertions(+), 10 deletions(-) Fusion-Task-Id: FN-7947 Fusion-Task-Lineage: 41c759a2-e76b-4771-9421-c9805c4596e5 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
7cc622bed2 |
FN-7946: auto-retry stuck Planning Mode AI generation up to 3 times
Planning Mode now automatically retries a stuck or terminally-errored AI generation session up to three times before falling back to the permanent Retry/Dismiss error panel, reducing manual retries for transient failures. - Add a bounded (MAX_PLANNING_AUTO_RETRIES = 3) client-side auto-retry that reuses the existing /planning/:id/retry endpoint whenever the SSE stream's onError, a session reload, or the stuck-session poll observes a terminal "error" status. - Track the retry budget in refs (planningAutoRetryAttemptRef, planningAutoRetryInFlightRef) so async SSE/poll/loadSession handlers share a single in-flight guard, with the current attempt mirrored into state (isAutoRetrying/autoRetryAttempt) for the UI. - Reset the retry budget whenever the session makes real progress (reaches a new question or a completed summary), and surface the permanent Retry/Dismiss error view once the budget is exhausted. - Show a "Retrying... (attempt N of 3)" loading message while an automatic retry is in flight, distinct from the manual Retry button state. - Fix a stuck-poll edge case where a terminal error discovered only by the poll (missed SSE event) after the auto-retry budget was exhausted left the modal spinning on "Generating next question..." forever instead of showing the error view. - Document the new auto-retry behavior in docs/dashboard-guide.md and add a minor changeset for @runfusion/fusion. - Extend PlanningModeModal.planning-flow.test.tsx with coverage for the auto-retry budget, single-flight behavior, and the poll-discovered terminal-error fallback. Files changed: .changeset/fn-7946-planning-auto-retry.md | 7 + docs/dashboard-guide.md | 3 + .../dashboard/app/components/PlanningModeModal.tsx | 339 ++++++++++++++------ .../PlanningModeModal.planning-flow.test.tsx | 353 ++++++++++++++++++--- 4 files changed, 567 insertions(+), 135 deletions(-) Fusion-Task-Id: FN-7946 Fusion-Task-Lineage: 42e911dc-9639-46ab-bb4f-bc9060413140 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |