Commit Graph

3227 Commits

Author SHA1 Message Date
gsxdsm
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 -->
2026-07-14 22:13:30 -07:00
gsxdsm
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 -->
2026-07-14 21:34:45 -07:00
Elite X
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>
2026-07-14 20:47:21 -07:00
gsxdsm
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 -->
2026-07-14 20:27:35 -07:00
gsxdsm
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.
2026-07-14 17:08:29 -07:00
gsxdsm
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.
2026-07-14 17:02:47 -07:00
gsxdsm
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
2026-07-14 15:54:44 -07:00
gsxdsm
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.
2026-07-14 15:07:26 -07:00
gsxdsm
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 -->
2026-07-14 13:23:29 -07:00
gsxdsm
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.
2026-07-14 12:41:10 -07:00
gsxdsm
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 -->
2026-07-14 08:23:11 -07:00
Phil Larson
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 -->
2026-07-14 08:21:19 -07:00
gsxdsm
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 -->
2026-07-14 08:18:52 -07:00
gsxdsm
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 -->
2026-07-14 08:18:10 -07:00
gsxdsm
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
2026-07-14 08:17:36 -07:00
Victor Canô
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>
2026-07-14 08:15:09 -07:00
gsxdsm
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.
2026-07-14 00:11:06 -07:00
gsxdsm
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>
2026-07-13 19:07:58 -07:00
gsxdsm
1ff83a2735 chore(release): v0.60.0
Version bump via changesets.
2026-07-13 10:32:12 -07:00
gsxdsm
d4001ab0ee feat: make merger AI model configurable under Global and Project Models
Add a dedicated merger model lane (project + global provider/model/thinking) so merge-agent sessions no longer share only the default model, without inheriting executor/planner/reviewer lanes.
2026-07-13 08:10:56 -07:00
gsxdsm
e35620c9aa FN-7939: supervise heartbeat timer-audit interval and bound non-advancing zombie re-arms
Fixes agents silently going stale for hours even though the heartbeat repair audit process was running.

- HeartbeatTriggerScheduler now runs an independent watchdog (armTimerAuditWatchdog/checkTimerAuditLiveness) that tracks the audit loop's last-run timestamp and re-arms + immediately re-runs the 60s audit interval if it goes stale beyond a bounded multiple of the cadence, so a silently dropped audit driver self-heals instead of leaving active agents unrepaired for hours.
- Tracks consecutive non-advancing zombie-timer re-arms per agent (nonAdvancingRearmState) and escalates once the count crosses a threshold, recording consecutiveNonAdvancingRearms/nonAdvancingEscalated in agent.metadata.heartbeatTimerRepair and logging reason=heartbeat-rearm-nonadvancing-escalated instead of silently churning the same zombie-timer-rearmed repair forever.
- Clears non-advancing rearm state on unregister, non-eligible agents, paused settings, and stale-run-reap skip paths so tracking never leaks stale per-agent counters.
- Watchdog and its interval handle are armed in start() and cleared in stop() alongside the existing audit interval.
- Adds a changeset (patch) describing the fix, and updates docs/agents.md and docs/architecture.md to document the FN-7939 audit watchdog and non-advancing escalation behavior.
- Adds heartbeat-scheduler.test.ts coverage for watchdog re-arm/liveness and non-advancing escalation.

Files changed:
 .changeset/fn-7939-heartbeat-audit-supervision.md  |   7 +
 docs/agents.md                                     |   8 +-
 docs/architecture.md                               |   1 +
 .../src/__tests__/heartbeat-scheduler.test.ts      | 209 +++++++++++++++++++++
 packages/engine/src/agent-heartbeat.ts             | 128 ++++++++++++-
 5 files changed, 341 insertions(+), 12 deletions(-)

Fusion-Task-Id: FN-7939
Fusion-Task-Lineage: 9fa90240-4333-4588-b595-aef3811b1524
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-13 07:51:07 -07:00
gsxdsm
316d4fa034 FN-7941: anchor execute-requeue loop guard to monotonic terminal-step progress
Hardens the FN-7863 execute-node self-requeue loop guard so residual execute_loop_stall cases (#2043/#2045/#2046/#2047) can no longer reset the loop counter forever via non-terminal signature drift.

- Change buildExecuteRequeueLoopSignature to track terminal step count (done/skipped) plus total step count instead of raw currentStep + every step status, so pending/in-progress oscillation no longer produces a "new" signature each cycle.
- Add buildExecuteRequeueLoopHighWaterSignature, which derives current terminal-step progress via the shared signature parser (parseExecuteRequeueLoopProgressSignature) and only resets the streak on monotonic forward progress, keeping a high-water mark across cycles so decreases/oscillation below the high-water still count toward exhaustion.
- Update executor.ts's execute self-requeue dispatch path to use the new high-water helper when deciding whether to reset (1) or increment executeRequeueLoopCount, replacing the previous raw signature-equality check.
- Extend execute-requeue-loop-guard.test.ts with regression coverage: a drifting-signature case that oscillates step order/status with no terminal progress (still terminalizes at MAX_EXECUTE_REQUEUE_LOOP_CYCLES), a done/in-progress oscillation case bounded after the high-water stops increasing, and an updated "real progress never terminalizes" case driven by genuine monotonic done-step advancement.
- Update docs/architecture.md's FN-7863/FN-7926 self-healing notes to describe the new terminal-step high-water signature and cross-reference FN-7941.

Files changed:
 docs/architecture.md                               |  4 +-
 .../execute-requeue-loop-guard.test.ts             | 83 +++++++++++++++++++++-
 packages/engine/src/executor.ts                    | 54 ++++++++++++--
 3 files changed, 130 insertions(+), 11 deletions(-)

Fusion-Task-Id: FN-7941

Fusion-Task-Lineage: cbf1e536-d29b-40da-bdd8-8c34d8d6b1ca

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-13 07:46:54 -07:00
gsxdsm
502c4c132f chore(release): v0.59.0
Version bump via changesets.
2026-07-13 01:23:43 -07:00
gsxdsm
9ba8a2e575 FN-7932: add per-lane Reviewer and Planning thinking-level overrides
Adds validatorThinkingLevel and planningThinkingLevel task fields so the Reviewer and Planning AI lanes can override reasoning effort independently of the shared task thinkingLevel, with dashboard UI, storage, and runtime fallback wiring.

- Add validatorThinkingLevel and planningThinkingLevel to Task/TaskCreateInput types (packages/core/src/types.ts)
- Persist the new fields in the SQLite schema and store read/write/replication paths (packages/core/src/db.ts, store.ts, mesh-task-replication.ts)
- Wire executor and triage lanes to fall back per-lane thinking level -> task.thinkingLevel -> existing settings/lane fallback (packages/engine/src/executor.ts, triage.ts)
- Add per-lane thinking-level selectors to the ModelSelectorTab UI, alongside the existing thinking-level control (packages/dashboard/app/components/ModelSelectorTab.tsx)
- Expose the new fields through the legacy task API and task-workflow routes (packages/dashboard/app/api/legacy.ts, packages/dashboard/src/routes/register-task-workflow-routes.ts)
- Document the new settings in dashboard-guide.md and settings-reference.md
- Add a minor changeset and unit/integration test coverage for store persistence, routes, UI, and agent-session helpers

Files changed:
 .changeset/per-lane-task-thinking.md               |   7 ++
 docs/dashboard-guide.md                            |   2 +
 docs/settings-reference.md                         |   2 +-
 .../src/__tests__/store-thinking-levels.test.ts    |  43 +++++++
 packages/core/src/db.ts                            |  15 ++-
 packages/core/src/mesh-task-replication.ts         |   4 +
 packages/core/src/store.ts                         |  24 +++-
 packages/core/src/types.ts                         |  12 ++
 packages/dashboard/app/api/legacy.ts               |   2 +
 .../dashboard/app/components/ModelSelectorTab.tsx  | 126 ++++++++++++++++++++-
 .../components/__tests__/ModelSelectorTab.test.tsx |  50 +++++++-
 .../src/__tests__/routes-tasks-ops.test.ts         |  74 ++++++++++++
 .../src/routes/register-task-workflow-routes.ts    |  19 +++-
 .../src/__tests__/agent-session-helpers.test.ts    |  15 +++
 packages/engine/src/executor.ts                    |  16 ++-
 packages/engine/src/triage.ts                      |   8 +-
 16 files changed, 395 insertions(+), 24 deletions(-)

Fusion-Task-Id: FN-7932

Fusion-Task-Lineage: 4202f774-aab9-41d2-86a0-f5277dd0f848

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-13 00:44:59 -07:00
gsxdsm
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 504b0f8b0)

| Shard | Root cause | Tests fixed |
|---|---|---|
| **3 (CLI)** | `workflowValidateParams` (FN-7911) missing from
`@fusion/engine` mock | 8 files |
| **3 (CLI)** | `skill-sync.test.ts` — `fn_workflow_validate` missing
from engine-tools.md | 1 file |
| **4 (dashboard)** | 6 chat default settings keys missing from
description allowlist | 1 file |
| **1+2 (engine)** | `additionalSkillPaths` missing from
`buildSessionSkillContext` mocks (FN-1510/1511) | 10 tests |
| **1+2 (engine)** | heartbeat FN-7878 changed paused→error for generic
run failures | 1 test |
| **1+2 (engine)** | executor `updateTask` exact-match →
`objectContaining` (new fields) | 2 tests |
| **1+2 (engine)** | `connectMcpSessionTools` mock missing for pi.test
MCP forwarding | 1 test |

## Structural fix — `scripts/check-mock-completeness.mjs` (the "fix for
good")

**New gate check** added to `pnpm test:gate`. Statically validates every
hardcoded `vi.mock("@fusion/dashboard")` and `vi.mock("@fusion/engine")`
factory covers all named imports the source file uses. Runs in <0.2s, no
module evaluation.

**How it works:**
1. Extracts named exports from each barrel
(`packages/dashboard/src/index.ts`, `packages/engine/src/index.ts`)
2. For each test file with a hardcoded `vi.mock` factory (no
`importOriginal`/`importActual` spread):
- Resolves source files the test covers (static + dynamic imports,
convention mapping)
   - Extracts what those source files named-import from the barrel
- Resolves spread helpers (e.g. `...workflowAuthoringEngineMock`) by
reading the helper's exported keys
- Reports any barrel exports that are named-imported by source but
absent from the mock

**Why this fixes the recurring pattern:** Every round 1–9 failure was a
new barrel export imported by source but missing from a test mock. This
check catches it at gate time, before merge — not after the full-suite
fails on main.

Also completed all 15 latent mock gaps the guard found on first run (9
dashboard + 6 engine), including expanding the centralized
`workflowAuthoringEngineMock` helper with all `extension.ts` named
imports.

## Verification
- Gate (with new check): exit 0 ✅
- CLI: 355/355 passed ✅
- Engine (6 fixed files): 250/250 passed ✅
- i18n + settings: verified ✅
- Mock completeness guard: ✅ (0 issues)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Documented a new non-destructive workflow validation tool that
performs a dry-run and returns typed validation errors.

* **Tests**
* Updated and strengthened CLI, dashboard, extension, and engine tests
with more accurate mock exports and more resilient assertions.
* Adjusted expectations for session/heartbeat and retry-related
behaviors.

* **Chores**
* Added an automated mock-completeness gate and integrated it into the
test quality gate to keep mocks aligned with available platform exports.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-12 23:29:06 -07:00
gsxdsm
6dcecb0c34 FN-7926: park completed-but-blocked tasks instead of looping execute-requeue
Stops the execute → pause-abort → re-queue-to-todo infinite loop for tasks whose implementation work is done but a dependency/blockedBy blocker is still live, by diverting them into a dedicated parked state instead of feeding the FN-7863 no-progress backstop or looping forever.

- Add TaskExecutor.parkCompletedBlockedTask(): when work is complete but getTaskCompletionBlocker() still reports a blocker, park the task in todo with pausedReason:"completed-work-blocked", status:"queued", preserved worktree/branch/steps, and a cleared execute-requeue signature.
- Replace shouldFinalizeCompletedTask's boolean with getCompletedTaskFinalizationDecision() returning "finalize" | "blocked" | "incomplete" so both the paused-after-completion and finalization call sites can react to the new "blocked" outcome without re-entering execution.
- Divert completed-but-blocked tasks before the FN-7863 execute-requeue-loop counter increments, so waiting-on-dependency states are no longer misclassified as EXECUTION_DISPATCH_LOOP_EXHAUSTED.
- Add SelfHealingManager.reconcileCompletedBlockedTasks(): a bounded sweep (wired into both startup/maintenance and periodic self-healing passes) that clears the park and advances the task to review once getTaskCompletionBlockerForStore() resolves, guarded by auto-merge eligibility, user-pause, and live-execution checks; failed advances re-park rather than strand the row.
- Add run-audit mutation types task:completed-blocked-parked and task:completed-blocked-advanced (ids/counts/outcomes-only metadata) plus AGENTS.md/docs/architecture.md entries documenting the new lifecycle.
- Extend execute-requeue-loop-guard.test.ts with coverage for the park/advance flow, including the zero-step task edge case.

Files changed:
 AGENTS.md                                          |   1 +
 docs/architecture.md                               |   2 +
 .../execute-requeue-loop-guard.test.ts             | 256 ++++++++++++++++++++-
 packages/engine/src/executor.ts                    |  85 ++++++-
 packages/engine/src/run-audit.ts                   |   4 +
 packages/engine/src/self-healing.ts                |  95 ++++++++
 6 files changed, 432 insertions(+), 11 deletions(-)

Fusion-Task-Id: FN-7926
Fusion-Task-Lineage: e47945f4-a816-447e-9ea1-7c13105d0ba9
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 23:26:02 -07:00
gsxdsm
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>
2026-07-12 21:39:23 -07:00
gsxdsm
9f8db7d1b5 FN-7903: wire thinkingLevel into AI session creation for automation steps
Threads the persisted per-step Thinking Level (from FN-7900) into runtime AI session creation and task spawning, so scheduled, routine, and manual automation runs actually apply the chosen reasoning effort instead of only storing it.

- CronRunner passes step.thinkingLevel through AiPromptExecutor to createFnAgent's defaultThinkingLevel for scheduled AI-prompt steps
- RoutineRunner forwards step.thinkingLevel to the shared AiPromptExecutor seam for routine AI-prompt steps
- Cron/routine create-task steps map step.thinkingLevel onto TaskCreateInput.thinkingLevel so spawned tasks inherit the configured reasoning effort
- Dashboard's inline/manual AI-prompt and create-task automation routes apply the same defaultThinkingLevel / TaskCreateInput.thinkingLevel behavior
- Updated docs (dashboard-guide.md, settings-reference.md) to describe the now-active runtime behavior
- Added a changeset for @runfusion/fusion (minor) and expanded cron-runner/routine-runner/routes-automation test coverage

Files changed:
 .changeset/fn-7903-automation-thinking-level.md    |  7 ++
 docs/dashboard-guide.md                            |  5 +-
 docs/settings-reference.md                         |  2 +-
 .../src/__tests__/routes-automation.test.ts        | 67 +++++++++++++++++++
 packages/dashboard/src/routes.ts                   | 10 +++
 packages/engine/src/__tests__/cron-runner.test.ts  | 75 +++++++++++++++++++++-
 .../engine/src/__tests__/routine-runner.test.ts    | 68 +++++++++++++++++++-
 packages/engine/src/cron-runner.ts                 | 21 +++++-
 packages/engine/src/routine-runner.ts              | 11 +++-
 9 files changed, 255 insertions(+), 11 deletions(-)

Fusion-Task-Id: FN-7903

Fusion-Task-Lineage: c7eb4660-975d-4c5d-820e-0f1a3ac8b6a6

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 20:09:57 -07:00
gsxdsm
d4bbbcccc6 fix: stop Ideas-intake cards from auto-processing and keep replans in the workflow's own planner column
Root cause of the reported incident: store init ran the retired flag-off
evacuation on every open, dumping Coding (Ideas) intake cards into triage
where they were auto-planned and executed. Init now always runs the
workflow-aware integrity pass (with a stale-selection mis-mapping guard and
per-pass IR memoization) and evacuation remains toggle-only.

Engine rebounds (Plan Review REVISE, stale-spec, fs-validation) resolve a
workflow-aware replan column instead of hardcoding triage; needs-replan now
counts as unplanned for hold-release dispatch so rejected plans cannot
re-execute; triage rediscovers needs-replan todo cards and refinement seed
prompts (shared buildRefinementSeedPrompt/isUnplannedSeedPrompt); the
fs-validation rebound sets needs-replan so unreadable-prompt tasks re-spec
instead of livelocking.

Dashboard: the All-workflows board renders column-orphaned tasks instead of
silently dropping them (hidden columns stay hidden), and the FN-7591 refetch
also fires for present-but-unrepresentable workflow mappings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 19:47:27 -07:00
gsxdsm
7a51f95b38 FN-7901: persist thinkingLevel for insight model selection
Adds a persisted Thinking Level (reasoning-effort) selector to manual insight generation, threading the selection through the dashboard API, insight run metadata, and retries.

- Add inline Thinking Level selector to the InsightsView model-config popover, persisted to localStorage (fusion-insight-thinking)
- Thread thinkingLevel through triggerInsightRun (legacy API client) and useInsights.runInsights
- Validate and store thinkingLevel in insight run inputMetadata.metadata on the POST /insights/run route; resolve it via resolvePlanningThinkingLevel for the actual generation call
- Recover and reapply the original run's thinkingLevel on retry (retryInsightRunLifecycle) so retries reuse the same reasoning-effort setting
- Export resolvePlanningThinkingLevel from @fusion/engine
- Document the new Thinking Level selector in docs/dashboard-guide.md
- Add a minor changeset for @runfusion/fusion

Files changed:
 .changeset/fn-7901-insight-thinking-level.md       |  7 ++
 docs/dashboard-guide.md                            |  1 +
 .../app/__tests__/insight-model-selector.test.tsx  | 41 ++++++++++-
 packages/dashboard/app/api/legacy.ts               |  2 +
 packages/dashboard/app/components/InsightsView.tsx | 24 +++++-
 .../app/hooks/__tests__/useInsights.test.ts        | 36 ++++++++-
 packages/dashboard/app/hooks/useInsights.ts        |  6 +-
 .../src/__tests__/insights-routes.test.ts          | 86 ++++++++++++++++++++++
 packages/dashboard/src/insights-routes.ts          | 36 ++++++++-
 packages/engine/src/index.ts                       |  1 +
 10 files changed, 227 insertions(+), 13 deletions(-)

Fusion-Task-Id: FN-7901

Fusion-Task-Lineage: a6249526-e97d-403e-b853-e497d16f425b

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 19:39:42 -07:00
gsxdsm
2e7fce21ae FN-7884: reset durable-agent error state on engine restart
Engine startup now treats itself as an implicit operator retry for durable heartbeat agents stuck in error, clearing eligible error states and re-arming heartbeats instead of waiting for the steady-state sweep's cooldown/exhaustion gates.

- Add SelfHealingManager.resetDurableAgentErrorStateOnStartup(), run first in runStartupRecovery(), which resets shared heartbeatErrorRecovery/legacy durableErrorRecovery metadata, clears lastError/pauseReason, flips eligible error and error-retry-exhausted-parked durable agents to active, and re-arms their heartbeat
- Preserve suppression for operator-actionable, stale worktree/module-resolution, user-paused, error-unrecoverable, ephemeral, disabled-runtime, and actively-executing agents
- Add agent:reset-error-state-on-startup run-audit mutation type with ids/counts/outcomes-only metadata (agentId, priorState, priorPauseReason, source)
- Add changeset FN-7884 (patch) documenting the operator-facing behavior
- Update AGENTS.md and docs/agents.md, docs/architecture.md to describe the new startup reset path alongside existing FN-7835/FN-7844/FN-7859/FN-7878 recovery docs
- Extend self-healing.test.ts with coverage for the new startup reset behavior and its exclusions

Files changed:
 .changeset/fn-7884-restart-error-reset.md          |   7 ++
 AGENTS.md                                          |   1 +
 docs/agents.md                                     |   4 +-
 docs/architecture.md                               |   2 +-
 packages/engine/src/__tests__/self-healing.test.ts | 127 ++++++++++++++++++++-
 packages/engine/src/run-audit.ts                   |   1 +
 packages/engine/src/self-healing.ts                |  88 +++++++++++++-
 7 files changed, 223 insertions(+), 7 deletions(-)

Fusion-Task-Id: FN-7884
Fusion-Task-Lineage: fe64f6af-3ff3-4876-8308-8a75591c45f1
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 17:39:29 -07:00
gsxdsm
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>
2026-07-12 17:02:33 -07:00
gsxdsm
504dc69f02 FN-7878: default heartbeat error recovery to recoverable for generic durable-agent failures
Durable agents were parking as error-unrecoverable on any non-transient-pattern failure, even generic/unknown blips that manual Retry immediately fixed; this changes the default to recoverable and reserves immediate unrecoverable parking for operator-actionable errors.

- isHeartbeatErrorRecoverable now returns true unless the error is operator-actionable (auth/model/billing/scope) or a stale worktree/module-resolution error, instead of requiring a transient-pattern match via classifyError
- Add OAuth scope-requirement and insufficient-scope patterns to the operator-actionable error detector so those still park immediately
- Update heartbeat-error-recovery, heartbeat-executor, self-healing, and transient-error-detector tests to cover the new default-recoverable behavior
- Update AGENTS.md and docs/architecture.md durable-agent error recovery notes to describe the new recoverable-by-default policy
- Add changeset documenting the fix

Files changed:
 .changeset/fn-7878-recoverable-default.md          |  7 ++
 AGENTS.md                                          |  2 +-
 docs/architecture.md                               |  4 +-
 .../src/__tests__/heartbeat-error-recovery.test.ts | 90 +++++++++++++++++++---
 .../src/__tests__/heartbeat-executor.test.ts       | 17 ++--
 packages/engine/src/__tests__/self-healing.test.ts | 45 ++++++-----
 .../src/__tests__/transient-error-detector.test.ts |  7 +-
 packages/engine/src/agent-heartbeat.ts             |  8 +-
 packages/engine/src/transient-error-detector.ts    |  2 +
 9 files changed, 137 insertions(+), 45 deletions(-)

Fusion-Task-Id: FN-7878

Fusion-Task-Lineage: 6f929af9-ceef-404f-95c9-98f26478f020

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 16:25:48 -07:00
gsxdsm
cbe07ee86b fix(engine): address PR #2027 review — tighten auth exclusions, accurate park accounting
- Exclude revoked/suspended/disabled/deactivated keys, inactive subscriptions,
  and locked accounts from the transient-auth classifier: no retry fixes those,
  so they stay operator-actionable even inside an authentication_error envelope.
- Self-healing sweep logs unrecoverable-error parks separately from
  recovered-to-active agents (return value still counts actions taken).
- Document same-session retry continuation semantics at the heartbeat
  withRateLimitRetry call site (side-effect replay concern).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 13:57:08 -07:00
gsxdsm
c4fad2d793 fix(engine): auto-recover agents from transient OAuth token-rotation 401s
A routine Claude Max OAuth token rotation (~8h) fails the in-flight call with
401 authentication_error "Invalid authentication credentials" even though
refreshed credentials already exist on disk. Three compounding defects turned
that into a fleet-wide operator-action park:

- The heartbeat prompt path never ran under withRateLimitRetry (executor/
  triage/merger all do), so the 401 immediately failed the run. Now wrapped.
- The 401 matched the operator-actionable /credential/ pattern and defaulted
  to "permanent", so FN-7859 parked agents paused/error-unrecoverable. A new
  shared isTransientAuthCredentialError classifier (also used by
  rate-limit-retry) classifies rotation 401s transient + not operator-
  actionable; OAuth scope-grant and API-key failures still park.
- Heartbeat failure classification ran on the stack-bearing error detail;
  stack frames like "at withRateLimitRetry (.../rate-limit-retry.ts)" match
  the usage-limit /rate[_\s]?limit/ pattern. Classification and
  agent.lastError now use the message; stderrExcerpt keeps the full detail.

Self-healing additionally un-parks agents previously paused with
error-unrecoverable whose lastError now classifies recoverable, bounded by
the shared heartbeat error-recovery budget.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 13:57:08 -07:00
gsxdsm
ee7af2513f fix(MAIN-008): address PR review feedback (#2020)
- Label namespaced mcp__* tools as resourceType "mcp" (not "research") so approvals/audit/dedupe keys describe external MCP actions
- Guard getTask in resumeApprovalAfterUnwindIfNeeded so deferred resume cannot mask execute() finally outcomes
2026-07-12 13:56:36 -07:00
Tchorizo
b688266a02 test(MAIN-008): complete Step 4 — validate shared MCP lifecycle surfaces
Agent: engineer
Fusion-Task-Id: MAIN-008
Co-authored-by: Fusion <noreply@runfusion.ai>
2026-07-12 13:56:36 -07:00
Tchorizo
555f916ebb fix(MAIN-008): complete Step 3 — resume approved MCP calls once
Agent: engineer
Fusion-Task-Id: MAIN-008
Co-authored-by: Fusion <noreply@runfusion.ai>
2026-07-12 13:56:36 -07:00
Tchorizo
e977fadda9 fix(MAIN-008): complete Step 2 — stabilize MCP executor bootstrap
Agent: engineer
Fusion-Task-Id: MAIN-008
Co-authored-by: Fusion <noreply@runfusion.ai>
2026-07-12 13:56:36 -07:00
gsxdsm
bc30ce8aa1 FN-7857: deliver plugin skill bodies to agent sessions and the Skills view
Plugin-contributed skills previously registered only a name for sessions and the dashboard, so their SKILL.md bodies were never actually loaded — fix threads real body paths through to both session creation and the Skills UI.

- Resolve each enabled plugin skill's body path via @fusion/core's resolvePluginSkillBodyPath and thread its body dir (plus parent dir) into every session-creating lane (executor primary/retry/verification-fix/step/child-agent, triage, reviewer, merger, agent-heartbeat, cron-runner) as additionalSkillPaths, unioned with existing CE skill dirs.
- Add collectPluginSkillNames/mergePluginSkills additionalSkillPaths plumbing in session-skill-context.ts so plugin skill discovery paths flow the same way as native/role-fallback skills.
- Update dashboard skills-adapter.ts to read plugin skill SKILL.md and reference files from disk (via the traversal-guarded reader) instead of returning a runtime-placeholder/"not found" response for plugin-sourced skills.
- Document the plugin skill body delivery mechanism in docs/PLUGIN_AUTHORING.md.
- Add regression coverage: plugin-skill-body-delivery.test.ts, expanded session-skill-context.test.ts and skills-adapter.test.ts.
- Add changeset fn-7857-plugin-skill-body-delivery.md (minor, fix).

Files changed:
 .changeset/fn-7857-plugin-skill-body-delivery.md   |  7 ++
 docs/PLUGIN_AUTHORING.md                           |  3 +
 .../dashboard/src/__tests__/skills-adapter.test.ts | 92 ++++++++++++++++------
 packages/dashboard/src/skills-adapter.ts           | 33 ++------
 .../__tests__/plugin-skill-body-delivery.test.ts   | 75 ++++++++++++++++++
 .../src/__tests__/session-skill-context.test.ts    | 84 +++++++++++++++++++-
 packages/engine/src/agent-heartbeat.ts             |  3 +-
 packages/engine/src/cron-runner.ts                 |  2 +
 packages/engine/src/executor.ts                    | 25 ++++--
 packages/engine/src/merger.ts                      | 10 ++-
 packages/engine/src/reviewer.ts                    |  2 +
 packages/engine/src/session-skill-context.ts       | 43 ++++++++--
 packages/engine/src/step-session-executor.ts       |  5 +-
 packages/engine/src/triage.ts                      |  3 +-
 14 files changed, 318 insertions(+), 69 deletions(-)

Fusion-Task-Id: FN-7857

Fusion-Task-Lineage: 9ba4c305-8b38-4ae8-85b3-4c87205ef767

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 12:47:38 -07:00
gsxdsm
7fccd73d84 test(engine): update moveTask 3rd-arg assertion undefined -> {} (executor pause-abort always passes options object) 2026-07-12 12:44:05 -07:00
gsxdsm
73172bb45c test(engine): update heartbeat expectations for FN-7835/FN-7859 error-unrecoverable reason + paused state transitions 2026-07-12 12:44:05 -07:00
gsxdsm
8d59df4531 test(engine): add wrapToolsWithRtkRewrite/PermanentAgentGating/ActionGate to pi.js mocks (openclaw + reviewer) 2026-07-12 12:44:05 -07:00
gsxdsm
95a808af6e FN-7864: add inline artifact preview/link to artifact-registered mail messages
Artifact-registration mailbox notifications now render a shared inline preview and open-artifact link instead of plain text metadata.

- Add MailboxArtifactAttachment component rendering an inline image/document preview plus an "open artifact" link from message.metadata (artifactId/artifactType/mimeType) via artifactMediaUrl
- Wire MailboxModal and MailboxView to render the new attachment for artifact-registered messages, with supporting CSS
- Emit metadata.mimeType from notifyArtifactRegistered in agent-tools.ts so mailbox surfaces can pick the right preview affordance without an extra artifact fetch
- Add/extend tests for the new component and for MailboxView/agent-artifact-tools coverage
- Update dashboard guide docs and add a changeset for the feature

Files changed:
 .changeset/fn-7864-artifact-mail-link.md           |   7 ++
 docs/dashboard-guide.md                            |   2 +-
 .../app/components/MailboxArtifactAttachment.tsx   | 103 +++++++++++++++++++++
 packages/dashboard/app/components/MailboxModal.css |  74 +++++++++++++++
 packages/dashboard/app/components/MailboxModal.tsx |  15 +++
 packages/dashboard/app/components/MailboxView.tsx  |  15 +++
 .../__tests__/MailboxArtifactAttachment.test.tsx   |  65 +++++++++++++
 .../app/components/__tests__/MailboxView.test.tsx  |  93 +++++++++++++++++++
 .../src/__tests__/agent-artifact-tools.test.ts     |  32 ++++++-
 packages/engine/src/agent-tools.ts                 |   5 +
 10 files changed, 409 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-7864

Fusion-Task-Lineage: a6502e18-5f7f-4c67-80fb-a709e4a52c50

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 12:01:00 -07:00
gsxdsm
9cfb40e137 FN-7863: add bounded execute-node self-requeue loop guard
Bounds the execute->pause-abort->todo dispatch loop so a task can no longer requeue forever with no visible signal or terminal state.

- Track a progress-anchored `executeRequeueLoopCount`/`executeRequeueLoopSignature` pair on the task row (current step + step statuses) so slow no-progress requeue cycles are counted independently of the scheduler's wall-clock `dispatchStormCount` guard.
- Warn visibly in the task log at `EXECUTE_REQUEUE_LOOP_VISIBLE_THRESHOLD` (3) and terminalize non-paused, non-terminal tasks at `MAX_EXECUTE_REQUEUE_LOOP_CYCLES` (6) with `status:"failed"` and an `EXECUTION_DISPATCH_LOOP_EXHAUSTED:` error, preserving worktree/branch/step progress.
- Emit a new `task:execution-dispatch-loop-terminalized` run-audit mutation type with ids/counts/outcomes-only metadata.
- Reset the loop counters on real progress, manual retry, forward moves (in-review/done/archived), and unpause, in both the executor and scheduler.
- Add DB migration 142 (`executeRequeueLoopCount`, `executeRequeueLoopSignature` columns) plus store read/write/reset plumbing.
- Add reliability-interactions coverage for the new loop guard and extend store-persistence tests for the new columns.
- Document the new behavior in AGENTS.md and docs/architecture.md.

Files changed:
 AGENTS.md                                              |   1 +
 docs/architecture.md                                   |   2 +
 packages/core/src/__tests__/store-persistence.test.ts  |  45 +++++
 packages/core/src/db.ts                                |  17 +-
 packages/core/src/manual-retry-reset.ts                |   1 +
 packages/core/src/store.ts                             |  22 ++-
 packages/core/src/types.ts                             |  11 ++
 .../execute-requeue-loop-guard.test.ts                 | 188 +++++++++++++++
 packages/engine/src/executor.ts                        |  67 +++++++-
 packages/engine/src/run-audit.ts                       |   2 +
 packages/engine/src/scheduler.ts                       |   8 +-
 11 files changed, 355 insertions(+), 9 deletions(-)

Fusion-Task-Id: FN-7863
Fusion-Task-Lineage: db40507f-5851-435e-8854-c1ed695b4154
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 11:56:54 -07:00
gsxdsm
0c97c161ee FN-7860: honor plugin skillFiles paths for skill body resolution
Plugin skills declared with PluginSkillContribution.skillFiles were silently ignored by the host, forcing plugin authors into a flat skills/<name>/SKILL.md layout instead of category subdirectories.

- Add packages/core/src/plugin-skill-paths.ts with resolvePluginSkillBodyPath (honors skillFiles[0] relative to plugin root, falls back to skills/<name>/SKILL.md, rejects path traversal) and resolvePluginRootFromEntryPath
- Track per-plugin absolute roots in PluginLoader and expose pluginRoot alongside each getPluginSkills() contribution
- Thread pluginRoot/skillFiles through PluginRunner, dashboard server/chat structural types, and skills-adapter so discovered plugin skill path/relativePath resolve via the new traversal-guarded resolver when a pluginRoot is available, keeping the old name-derived path for backward compatibility otherwise
- Export resolvePluginSkillBodyPath/resolvePluginRootFromEntryPath/PluginSkillBodyPath from @fusion/core
- Update docs/PLUGIN_AUTHORING.md and add unit tests covering the new resolver and updated plugin-loader/skills-adapter/plugin-runner behavior
- Add changeset (@runfusion/fusion: minor, category: fix)

Files changed:
 .changeset/fn-7860-plugin-skillfiles.md            |  7 ++
 docs/PLUGIN_AUTHORING.md                           |  4 +-
 packages/core/src/__tests__/plugin-loader.test.ts  | 23 +++++++
 .../core/src/__tests__/plugin-skill-paths.test.ts  | 75 ++++++++++++++++++++++
 packages/core/src/index.ts                         |  5 ++
 packages/core/src/plugin-loader.ts                 | 20 +++++-
 packages/core/src/plugin-skill-paths.ts            | 58 +++++++++++++++++
 .../dashboard/src/__tests__/skills-adapter.test.ts | 75 +++++++++++++++++++++-
 packages/dashboard/src/chat.ts                     |  2 +-
 packages/dashboard/src/server.ts                   |  2 +-
 packages/dashboard/src/skills-adapter.ts           | 19 ++++--
 .../engine/src/__tests__/plugin-runner.test.ts     |  2 +-
 packages/engine/src/plugin-runner.ts               |  4 +-
 13 files changed, 280 insertions(+), 16 deletions(-)

Fusion-Task-Id: FN-7860

Fusion-Task-Lineage: 720cf527-9c6f-4877-838e-5fb64bd86556

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 11:52:24 -07:00
gsxdsm
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>
2026-07-12 11:34:48 -07:00
gsxdsm
c13d2ee9c2 FN-7858: honor per-project plugin-skill toggles in session merging
Session skill merging (collectPluginSkillNames) previously ignored per-project
Skills view enable/disable toggles and only consulted each plugin's static
default, so a user disabling a plugin skill in the Skills view would still see
it merged into live agent sessions. Extracted the effective-enablement
resolver shared by dashboard discovery and engine session assembly into
@fusion/core so both surfaces stay in sync.

- Added packages/core/src/skill-settings.ts with computeSkillId/parseSkillId/
  normalizeStoredSkillPath/getSkillSettingState/resolvePluginSkillEnabled,
  exported from @fusion/core's index.
- packages/dashboard/src/skills-adapter.ts now re-exports and delegates to the
  shared @fusion/core resolver instead of duplicating its own
  getSkillSettingState/computeSkillId/parseSkillId implementations.
- packages/engine/src/session-skill-context.ts: collectPluginSkillNames now
  accepts a projectRootDir, reads project settings via skill-resolver's newly
  exported readProjectSettings/resolveProjectRoot, and calls
  resolvePluginSkillEnabled instead of only checking the plugin's static
  skill.enabled flag; mergePluginSkills passes projectRootDir through.
- packages/engine/src/skill-resolver.ts: exported readProjectSettings and
  ProjectSkillSettings for reuse by session-skill-context.
- Updated docs/plugin-management.md to document that per-project Skills view
  toggles now apply to runtime agent sessions, not just discovery.
- Added unit tests for the new core resolver and updated dashboard/engine
  tests to cover per-project toggle overrides in session merging.
- Added a patch changeset for @runfusion/fusion.

Files changed:
 .changeset/fn-7858-plugin-skill-session-toggle.md  |   7 ++
 docs/plugin-management.md                          |   4 +-
 packages/core/src/__tests__/skill-settings.test.ts |  62 +++++++++
 packages/core/src/index.ts                         |   8 ++
 packages/core/src/skill-settings.ts                | 102 +++++++++++++++
 .../dashboard/src/__tests__/skills-adapter.test.ts |  60 ++++++++-
 packages/dashboard/src/skills-adapter.ts           | 107 +++-------------
 .../src/__tests__/session-skill-context.test.ts    | 140 ++++++++++++++++++++-
 packages/engine/src/session-skill-context.ts       |  23 +++-
 packages/engine/src/skill-resolver.ts              |   4 +-
 10 files changed, 409 insertions(+), 108 deletions(-)

Fusion-Task-Id: FN-7858

Fusion-Task-Lineage: 90e44d24-e385-4a74-b8e4-3c864ec39a95

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 11:15:52 -07:00
gsxdsm
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>
2026-07-12 10:04:29 -07:00
gsxdsm
f23619c2d4 fix: preserve user pause across executor pause teardown (FN-7851 pause-bounce loop)
Pausing an in-progress task never stuck: the pause teardown re-queued the
row to todo with a plain engine move, and the reopen block wiped
paused/pausedByAgentId/pausedReason. The graph-failure classifier then saw
an unpaused row, misread the hard-cancel as an engine-internal abort, and
auto-continued the session (graphResumeRetryCount 1/2, 2/2); once the
budget was exhausted the benign re-queue left the row dispatchable and the
scheduler re-dispatched it seconds later — an indefinite pause/resume
bounce, burning a fresh worktree + pnpm install per cycle.

- store: new moveTask option `preservePause` keeps the pause park across a
  reopen-to-todo/triage move (flag-ON trait hook + flag-OFF legacy inline,
  kept in sync). It never SETS a pause, only prevents clearing one.
- executor teardown: when the pause that caused the abort is still in
  force, move with preservePause so the row lands in todo still parked
  (scheduler skips paused/userPaused rows until explicit unpause).
- classifier: a live task pause is labeled operator intent, never
  "engine abort during pause/resume"; the benign log now says
  "parked … awaiting explicit unpause" instead of the contradictory
  "cleared for normal scheduling" for parked rows.

Surfaces covered by tests: flag-ON hook (preserve + never-set + default
clear), classifier no-auto-continue for task-pause/user-pause/global-pause
rows in todo, provenance labels.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 10:04:29 -07:00