Commit Graph

11374 Commits

Author SHA1 Message Date
Phil Larson
883f38d68f Fix agent AI interview model routing (#2142)
## Summary
- resolve the configured planning model when agent onboarding requests
omit an explicit override
- align the onboarding prompt with supported runtime/model hint fields,
allowing AI-created agents to select runtimes such as Hermes
- refresh the generated GitHub issue import limits required by the
repository sync gate

## Root cause
The agent onboarding route loaded project settings but passed only
request-body model fields. The AI Interview UI omits those fields, so
`createFnAgent` was called with `provider=undefined, model=undefined`;
the session returned no usable assistant JSON. The prompt catalog also
prohibited `runtimeHint` despite the parser and form already supporting
it.

## Verification
- targeted agent onboarding tests: 22 passed
- `pnpm --filter @fusion/core typecheck`
- `pnpm --filter @fusion/dashboard typecheck`
- `pnpm lint`
- `pnpm build`
- `pnpm smoke:boot`
- engine merge-gate subset: 294 passed

Full `pnpm test` reached the PostgreSQL gate but this host has no `psql`
binary, so 23 PostgreSQL suites could not start; this is an environment
prerequisite failure, not a test assertion failure.

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

* **Bug Fixes**
* Agent onboarding interviews now use the configured planning model when
no override is provided.
* Runtime suggestions and runtime-hint guidance are preserved during
onboarding and reflected in generated configurations.
* On onboarding start streaming, planning provider/model resolution now
comes from settings with stricter override validation, and test mode
continues to take priority.

* **Documentation**
* Updated onboarding prompt guidance to support additional configuration
fields and optional runtime draft hints.
  * Reduced the maximum GitHub issue import/browse limit from 100 to 50.

* **Tests**
* Added coverage for runtime-hints prompting and planning-model override
behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-15 13:57:15 -07:00
gsxdsm
5caf360a58 fix(desktop): green Windows smoke + Linux AppImage PG packaging checks (#2138)
## Summary
- **Windows CI:** run the embedded Postgres smoke as non-admin
`fusion-pg` (with profile prewarm) so elevated `windows-latest` runners
stop failing with PostgreSQL’s admin-token refusal. Packaging still runs
as the job user.
- **Linux AppImage:** add a packaging content verifier for
`main-bootstrap`, `@embedded-postgres` natives, and `omp-runtime` dist
entrypoints; wire it into `release.yml`, `test-release.yml`, and the
advisory **Desktop packaging** PR lane (after `electron-builder --dir`).
- Fix eslint `no-undef` on bare `URL` in the verifier script (was red on
#2131).

## Context
Desktop packaging on Ubuntu was mostly green; Windows desktop builds and
the AppImage packaging PR (#2131 lint) were the remaining red paths. The
win-pg-diag pivot (run smoke as non-admin) proved green on CI; this
ports that approach without removing main’s elevated-token product path
for end-user “Run as administrator” cases (smoke simply does not take
that path when the process is non-admin).

## Test plan
- [x] `pnpm --filter @fusion/desktop exec vitest run
src/__tests__/release-workflow.test.ts`
- [x] `pnpm exec eslint scripts/verify-desktop-linux-pg-packaging.mjs`
- [ ] Desktop packaging workflow on this PR
- [ ] Desktop Windows Build (workflow_dispatch)
- [ ] Confirm #2131 supersession if this lands the same AppImage checks

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

* **Bug Fixes**
* Strengthened Linux desktop AppImage validation to confirm embedded
PostgreSQL artifacts, required binaries, symlink hydration, and the
expected app entrypoints are present after packaging.
* Improved Windows embedded PostgreSQL smoke testing by running under a
non-administrator helper user with a prewarmed profile environment.

* **Tests**
* Added automated packaging/release workflow verification steps (Linux
and Windows) to catch embedded PostgreSQL content regressions earlier,
including during artifact build and release verification.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-15 13:56:10 -07:00
gsxdsm
599a509d22 refactor: package code organization (god-file peels, wave 1) (#2139)
## Summary

First wave of package-internal code organization: split oversized
modules into domain-named files/folders while preserving public import
paths via re-exports, and refresh the line-count ratchet scoreboard.

- **Plan:**
`docs/plans/2026-07-14-001-refactor-package-code-organization-plan.md`
(multi-wave program; this PR lands U1–U4 + first U3/U6 slices)
- **Core types:** peel `types.ts` into
`types/{board,merge-queue,execution-and-ui,merge-policy,workflow-steps}.ts`
with browser-safe Vite alias preserved
- **Core TaskStore:** rename `remaining-ops-9` →
`task-commit-associations` (domain-named, not ordinal dump)
- **Engine executor:** peel pure helpers into
`executor/{browser-probe,requeue-loop,pseudo-pause,workflow-step-failures}.ts`
- **Engine heartbeat:** peel system prompts/procedures into
`agent-heartbeat-prompts.ts`
- **Ratchet:** one-time baseline truth-up + ratchet-down for touched
files

### Deferred to follow-up PRs (plan U5, U7–U9 + remaining waves)
- Self-healing folder split
- Further remaining-ops domain peels
- Dashboard `legacy.ts` / routes / UI monofiles
- CLI extension + TUI peels

## Test plan

- [x] `pnpm --filter @fusion/core exec tsc --noEmit`
- [x] `pnpm --filter @fusion/engine exec tsc --noEmit`
- [x] Focused vitest: `detect-pseudo-pause`,
`executor-browser-verification`, `clear-terminal-workflow-step-failures`
- [x] `node scripts/check-file-line-count.mjs` clean against updated
baseline
- [ ] CI merge gate (lint/typecheck/build/gate)
- [ ] Browser smoke: N/A for this PR (no dashboard UI route changes)

## Residual Review Findings

None. Review autofix applied dual-home wiring for
`clearTerminalWorkflowStepFailures` only.

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

* **New Features**
* Added configurable heartbeat procedures for task and no-task scenarios
(including patrol-aware rendering).
* Improved agent-browser availability verification with clearer
availability/status reporting.
  * Added detection for pseudo-pauses and review-handoff requests.
* Expanded core configuration/contract options for
execution/UI/localization, merges, merge queues, and workflow steps.
* **Bug Fixes**
* Improved handling of transient execute-requeue and workflow-step
retry/cleanup behavior, including better Windows path support.
  * Preserved existing public interfaces during internal restructuring.
* **Documentation**
  * Added a multi-phase roadmap for future package reorganization.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-15 13:34:30 -07:00
gsxdsm
85f8b1f909 feat: shared Postgres multi-node — retire mesh data-plane replication (#2130)
## Summary

- Treat **shared PostgreSQL** (`DATABASE_URL`) as the multi-node durable
data plane; mesh HTTP is membership + optional auth, not task/settings
replication.
- **Peer exchange**: under Postgres backend mode, write queue is
**topology/auth-only**; non-topology pending rows fail rather than
replaying multi-leader task/settings payloads.
- **Mesh routes**: task-ID reserve/commit/abort always hit local shared
allocator rows (ignore remote `coordinatorNodeId`); mesh sync ignores
settings and only exchanges `authMaterial`.
- **Docs**: rewrite multi-project runbook, shared cluster protocol, and
architecture mesh sections for shared-Postgres + claims/leases.

## Context

Follows the SQLite→Postgres cutover. Multiple Fusion nodes can share one
external Postgres while keeping **per-node execution** (worktrees,
processes, claims via `central.task_claims`). Explicit non-goals remain:
scheduler failover and live process migration.

Plan:
`docs/plans/2026-07-15-001-refactor-mesh-shared-postgres-multinode-plan.md`

## Test plan

- [x] `pnpm --filter @fusion/engine exec vitest run
src/__tests__/peer-exchange-service.test.ts`
- [x] `pnpm --filter @fusion/dashboard exec vitest run
src/__tests__/mesh-routes.test.ts`
- [x] `pnpm --filter @fusion/core exec vitest run
src/__tests__/shared-mesh-state.test.ts`
- [ ] CI gate (lint/typecheck/build/gate)
- [ ] Manual (optional): two processes, same `DATABASE_URL`, create task
on A visible on B; settings change without mesh settings sync; claim
exclusivity

## Operator note

Multi-node shared board requires **external** `DATABASE_URL` on every
node. Default embedded Postgres is still single-host.

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

* **New Features**
* Improved multi-node deployments using shared PostgreSQL as the durable
source of execution state.
* Task ID reservation/commit/abort now run locally (no remote
coordinator forwarding).
* Mesh syncing now prioritizes topology visibility and authentication
material; settings replication is disabled in shared-Postgres mode.
* **Bug Fixes**
* Prevented task/settings replication over mesh HTTP in shared-Postgres
deployments.
* Refined lease ownership, recovery, and reconciliation to converge via
shared-database primitives.
* **Documentation**
* Updated architecture and shared-mesh protocol guidance, including
multi-node setup and lease/task-ID allocation behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-15 13:32:33 -07:00
gsxdsm
f6e43d7232 fix: reclaim merger-ai autostashes and stop dropping untracked work
merger-ai's local-checkout sync stashed under fusion-ai-merge-sync-<taskId>,
a label none of merger.ts's reclamation machinery matches — every path keys
off the fusion-merger-autostash: prefix. Those entries were never classified,
never subsumed-dropped, never age-swept, and never surfaced as orphans holding
work, so they accumulated indefinitely: six entries dating back a month were
found on one working tree, and their age made real lost work indistinguishable
from litter. merger-ai now labels through buildAutostashLabel, and the legacy
prefix stays recognized so already-leaked entries are reclaimed rather than
stranded in developers' stash lists.

Routing them into that machinery first required fixing what it does with
untracked files. A stash created with --include-untracked keeps them in a
third parent (<sha>^3) that git stash show omits, so an untracked-only stash
read as empty — and all three copies of the liveness check treated empty as
"subsumed, safe to drop". Every leaked ai-sync stash carried untracked files,
so the fix would otherwise have destroyed the work it was meant to reclaim.
Liveness now resolves through one authority, classifyStashContent, which reads
both sides, diffs untracked paths against <sha>^3 rather than the stash commit
(whose tree never contained them), and treats unreadable state as unknown and
therefore undroppable.

Age-based sweeping is left alone: it drops by timestamp without consulting
content, which is deliberate bounded retention and the backstop against this
same accumulation, not a safety gap.

Regression test uses real git — the defect lives in git's stash object model,
so a mocked git can neither express nor catch it — and asserts the invariant
across tracked-only, untracked-only, and mixed stashes in both live and
subsumed states. The mixed shape (tracked subsumed, untracked live) is the one
that silently lost work.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 13:22:22 -07:00
gsxdsm
5e5fa9a2be fix: auto-approve plans whose approval predates the prompt-hygiene injection
An operator was re-asked to approve a plan they had already approved and
that had not changed.

POST /tasks/:id/approve-plan fingerprints the on-disk PROMPT.md, so a plan
approved before the `## Original Description` hygiene injection
(applyOriginalDescription) shipped carries a hash over PRE-injection
content. On the task's next pass the injection rewrites PROMPT.md, the
fingerprint moves, and FN-7569's idempotency short-circuit misses — so the
manual gate re-parks an unchanged, already-approved plan.

finalizeApprovedTask now also compares the recorded fingerprint against the
as-read (pre-injection) content. This does not weaken the gate: `written`
diverges from `writtenInput` only via that injection, so both arms hash
bytes the operator actually approved — only the representation differs. A
genuinely changed plan matches neither arm and still parks.

On a legacy match the stored fingerprint is migrated forward, so the
reconciliation is one-time per task rather than a comparison carried
forever. The migration is a direct updateTask — the taskUpdates batch is
flushed well before this gate runs.

Covers both finalizeApprovedTask callers (direct + recoverApprovedTask),
asserts the changed-plan safety edge still parks, and asserts no redundant
fingerprint write when the approval is already post-hygiene.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 13:17:24 -07:00
gsxdsm
3b938887c9 test: fix FN-7569 plan-approval fixtures to model approved on-disk PROMPT.md
The recoverApprovedTask idempotency test failed deterministically, and its
siblings passed for the wrong reason. Both traced to the same stale fixture,
not a product defect.

finalizeApprovedTask injects `## Original Description` into PROMPT.md
(applyOriginalDescription) BEFORE computing the approval fingerprint, and
POST /tasks/:id/approve-plan fingerprints the on-disk file — so the
fingerprint an approval records is always over post-injection content. The
fixtures wrote RAW planner text and fingerprinted that, modelling a state
approve-plan can never produce: the injection then rewrote the content, the
fingerprint moved, and the short-circuit looked broken.

Verified the product is correct: the injection is idempotent, so the real
approve -> recover round-trip fingerprint matches (checked end to end).

- recoverApprovedTask test: write and fingerprint the approved on-disk
  content. It now exercises the real short-circuit — the run logs "plan
  unchanged since prior approval" then "recovered and moved to todo",
  where before it logged "awaiting manual approval".
- same-plan test: it only passed because the injection's rewrite ENOENT'd
  (no task dir), the failure was swallowed, and `written` stayed raw — so
  the fingerprint matched by accident. Feed it the approved content so the
  injection is a genuine no-op and the assertion means something.

Fixtures derive from applyOriginalDescription rather than hard-coding
post-injection text, so they keep meaning "the content the operator
approved" if the hygiene injection changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 13:10:17 -07:00
gsxdsm
d48537b7a5 docs: correct FN-7779 changeset dev field to the shipped ACP path
The dev field described the pre-ACP adapter: stderr capture, NDJSON error
event bridging, and subprocess exit-code inspection. That implementation
was replaced by the native ACP transport rewrite (grok agent stdio) on
2026-07-11, which carries the FN-7779 invariant forward through onText
diagnostics rather than stderr scraping.

The user-facing summary was already accurate; only the developer-facing
mechanism was stale. Describe the paths that actually ship.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 13:07:59 -07:00
gsxdsm
2b8df56cb8 fix: escalate reviewer provider errors instead of looping on them
A rate-limited reviewer filled a task's Chat tab with 14 identical
"Reviewer using model: ..." markers and no review text, hammering an
already-limited provider.

Root cause: the reviewer was the only AI lane that never classified
provider errors, so a 429 became an UNAVAILABLE verdict. With no
validator fallback configured the fallback ladder re-ran the SAME model
instantly, and fn_review_step answered with "code review remains
blocking; retry once" — bounding the loop with prompt text rather than
code. The tool's catch-all also swallowed the error into tool output, so
withRateLimitRetry, UsageLimitPauser and RetryStormError never fired.

- reviewer: throw ReviewerProviderError for usage-limit/transient errors
  instead of laundering them into UNAVAILABLE, and never spend the
  fallback budget (which bounds bad reviews) on an outage.
- reviewer: absorb flaky-network blips in-lane via withRetry with
  jittered backoff; rate limits still escalate immediately.
- executor: re-raise the fatal after the prompt via
  throwDeferredReviewerFatal — pi-agent-core converts tool throws into
  tool_error results, so a tool cannot throw out of session.prompt().
- executor: give code review a real MAX_CODE_REVIEW_UNAVAILABLE_RETRIES
  counter, mirroring the plan/spec limiter.
- reviewer: dedupe the model marker on text, so same-model retries stay
  silent while a genuine model switch still emits.

Also fixes the run-on rendering: AgentLogType gains `status` for complete
engine messages. `text` means "streamed delta" and is re-glued with
join(""), which is why N standalone markers rendered as one string. The
split is at the type, not a separator — a separator would reintroduce the
FN-5787/5789/5803 streamed-spacing regression.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 12:54:16 -07:00
gsxdsm
3e978e1540 fix: quiet per-poll scheduler hold-release and routing log spam
Both lines fired on every scheduler poll while nothing changed: a held
card re-attempts release each sweep, and every dispatch candidate logged
its resolved node. On a busy board that filled the operator log pane with
"Hold release for FN-XXXX deferred" and "routed to node=local" within
seconds, burying real scheduler events.

Add a Logger.debug() level, off by default and opted into per subsystem
via FUSION_DEBUG, and demote both lines to it. Routing to a remote node
stays at info since it explains where work actually went; only the local
default is demoted. Lines reporting a real transition (capacity
rejection, racing sweep, release failure) are untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 12:49:43 -07:00
gsxdsm
d893a026df FN-7971: hide GitLab import tab when GitLab is disabled
Hide the Import Tasks GitLab provider affordance when gitlabEnabled is off, coerce restored GitLab state to GitHub, and document the behavior.

- Gate GitLab provider tab visibility on effective gitlabEnabled and wait for settings before replaying persisted GitLab auto-load
- Coerce disabled GitLab provider preference to GitHub without firing GitLab fetch/import requests
- Cover hide/show/coerce paths in GitHubImportModal tests and update dashboard guide copy
- Add patch changeset for the published package

Files changed:
 .changeset/fn-7971-hide-gitlab-when-disabled.md    |  7 +++
 docs/dashboard-guide.md                            |  4 +-
 .../dashboard/app/components/GitHubImportModal.tsx | 27 ++++++++--
 .../__tests__/GitHubImportModal.test.tsx           | 62 +++++++++++++++++++---
 4 files changed, 88 insertions(+), 12 deletions(-)

Fusion-Task-Id: FN-7971

Fusion-Task-Lineage: e645a4a7-85e0-4635-8dad-f5839390e5c5

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-15 12:47:14 -07:00
gsxdsm
6e3a338cac FN-7968: defer slow cleanup off task deletion critical path
Make soft-delete return after the DB mutation while branch and agent cleanup run in the background.

- Schedule cleanupBranchForTask after the soft-delete transaction instead of awaiting it under withTaskLock
- Persist cleaned-branch log entries on the deleted row asynchronously; warn on deferred failures
- Respond from DELETE /tasks/:id after deleteTask and schedule execution-agent binding release off the HTTP path
- Add core and dashboard regression tests for non-blocking delete cleanup
- Document the fast-path contract in architecture.md and add a patch changeset

Files changed:
 .changeset/fn-7968-task-delete-latency.md          |   7 +
 docs/architecture.md                               |   1 +
 .../task-delete-nonblocking-cleanup.test.ts        | 160 +++++++++++++++++++++
 packages/core/src/task-store/archive-lifecycle.ts  |  57 +++++++-
 .../routes-task-delete-nonblocking.test.ts         | 139 ++++++++++++++++++
 .../src/routes/register-task-workflow-routes.ts    |  19 ++-
 6 files changed, 370 insertions(+), 13 deletions(-)

Fusion-Task-Id: FN-7968

Fusion-Task-Lineage: f218a91e-aee3-46c9-a80f-182751b3ccc4

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-15 12:44:30 -07:00
gsxdsm
836e53c6c0 FN-7975: exclude engine-paused wall-clock from task active timing
Reconcile active task segment anchors on full Global/Engine unpause so stopped-engine wall-clock does not inflate execution time, reusing the FN-7011 downtime path with a transition-captured heartbeat.

- Pass optional engineLastActiveAtOverride into reconcileActiveTimingForEngineDowntime so unpause callers freeze the stopped-window proof against racing scheduler heartbeats
- Await downtime reconciliation in resumeAfterUnpauseAndSweepInReview before resuming agentic work or sweeping in-review tasks
- Fold Global/Engine unpause into the unified pause-lifecycle listener (single reconcile when both clear together; no-op while either pause remains)
- Soft-fail reconcile errors so unpause resume still proceeds
- Add store and project-engine coverage for override, await-before-resume, dual-source clear, and fail-soft paths; document FN-7975 in AGENTS.md run-audit notes
- Add patch changeset for the operator-facing timing fix

Files changed:
 .changeset/fn-7975-engine-pause-active-timing.md   |   7 ++
 AGENTS.md                                          |   2 +-
 .../core/src/__tests__/store-active-timing.test.ts |  86 +++++++++++++
 packages/core/src/store.ts                         |  23 ++--
 .../project-engine-unpause-active-timing.test.ts   |  94 ++++++++++++++
 .../engine/src/__tests__/project-engine.test.ts    | 139 +++++++++++++++++++++
 packages/engine/src/project-engine.ts              |  64 +++++-----
 packages/engine/src/self-healing.ts                |   6 +-
 8 files changed, 378 insertions(+), 43 deletions(-)

Fusion-Task-Id: FN-7975

Fusion-Task-Lineage: 84a46e6f-92bf-452a-ab67-c25ba85cbffb

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-15 12:41:05 -07:00
gsxdsm
a1672726d6 FN-7984: keep chat agents from switching checkout branch unless asked
Add branch-stickiness guardrails to the chat system prompt so agents do not switch the live checkout unless the user explicitly requests it.

- Extend CHAT_SYSTEM_PROMPT to forbid git checkout/switch unless asked; allow read-only Git inspection
- Add regression coverage for the branch-stickiness clause
- Add patch changeset for @runfusion/fusion

Files changed:
 .changeset/fn-7984-chat-branch-guardrail.md                 | 7 +++++++
 packages/dashboard/src/__tests__/chat-system-prompt.test.ts | 9 +++++++++
 packages/dashboard/src/chat.ts                              | 7 +++++--
 3 files changed, 21 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-7984

Fusion-Task-Lineage: b093b1da-f45c-49fc-918b-50b06f99d7f6

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-15 12:38:02 -07:00
gsxdsm
a8b6387420 FN-7964: fix Project Models workflow model lane persistence
Keep pending Project Models workflow lane overrides registered across section navigation so primary Settings Save still flushes them.

- Stop clearing the workflow-lane saver ref on Project Models unmount
- Add regression coverage for save-after-section-nav and reload override display
- Add patch changeset for the dashboard fix

Files changed:
 .changeset/fn-7964-project-models-workflow-lanes.md                            |  7 +++++++
 packages/dashboard/app/components/SettingsModal.tsx                            |  8 +++++++-
 packages/dashboard/app/components/__tests__/SettingsModal.models-auth.test.tsx | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 72 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7964

Fusion-Task-Lineage: ed4e6268-d5b7-44ba-bbcc-2d44b301403e

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-15 12:33:06 -07:00
gsxdsm
de25e32eac FN-7963: add plannerHeartbeatPatrolEnabled to gate idle heartbeat task creation
Add a workflow setting that disables idle/no-task heartbeat proactive task creation without turning off planner overseer stuck-task recovery.

- Declare plannerHeartbeatPatrolEnabled (default true) in BUILTIN_OVERSIGHT_SETTINGS
- Resolve the flag via resolveEffectivePlannerHeartbeatPatrolEnabled and wire it into agent-heartbeat/triage prompts
- Render patrol-off instruction when disabled; keep FN-7962 outage backoff lines when patrol stays enabled
- Cover setting defaults, prompt builders, and heartbeat executor paths with tests
- Document the setting in settings-reference and add a changeset

Files changed:
 .changeset/fn-7963-planner-heartbeat-patrol.md     |   7 ++
 docs/settings-reference.md                         |  11 +-
 packages/core/src/__tests__/agent-prompts.test.ts  |  29 +++++
 .../builtin-workflow-settings-triage.test.ts       |  21 ++++
 .../plannerHeartbeatPatrolEnabled-default.test.ts  |  64 ++++++++++
 packages/core/src/agent-prompts.ts                 |  55 +++++++--
 packages/core/src/builtin-workflow-settings.ts     |  14 +++
 packages/core/src/index.gate.ts                    |   5 +
 packages/core/src/index.ts                         |   5 +
 packages/core/src/workflow-settings-resolver.ts    |  15 ++-
 .../src/__tests__/heartbeat-executor.test.ts       |  59 ++++++++-
 packages/engine/src/agent-heartbeat.ts             | 135 +++++++++++++++++++--
 packages/engine/src/triage.ts                      |   8 +-
 13 files changed, 402 insertions(+), 26 deletions(-)

Fusion-Task-Id: FN-7963

Fusion-Task-Lineage: c5e7a382-52c1-4cc1-8b21-aba7dc7d2b97

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-15 12:27:15 -07:00
gsxdsm
b2977d1a7a FN-7982: clear stale planner chat streaming on fresh reply
Fix task chat showing a previous agent message while a new reply is generating by clearing streaming carriers on fresh generations.

- Clear streamingThinking and the streaming-assistant row when starting a generation without an in-flight snapshot
- Preserve text/thinking/tool restore only when attaching to a live in-flight generation
- Add regression coverage for consecutive replies that must not reuse prior stream content
- Add patch changeset for the planner chat stale-message fix

Files changed:
 .changeset/fn-7982-planner-chat-stale-message.md   |  7 ++++
 .../app/components/TaskPlannerChatTab.tsx          | 19 ++++++++--
 .../__tests__/TaskPlannerChatTab.test.tsx          | 44 ++++++++++++++++++++++
 3 files changed, 67 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-7982

Fusion-Task-Lineage: 71daaa80-6d66-4940-a656-a2c42a6b03bb

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-15 12:22:33 -07:00
gsxdsm
ec7898163c FN-7962: back off idle triage patrol task creation during model outages
Teach idle heartbeat patrol prompts to skip fn_task_create when recent model-availability failures are visible, and to base progress claims only on board state fetched in the current heartbeat.

- Add standard and concise triage heartbeat guidance to check for model-availability, fallback exhaustion, 429/rate-limit, and 404/model-unavailable failures before creating work
- Require existing-task progress/status claims to come from fn_task_list or fn_task_show results in the current heartbeat run
- Extend agent-prompts tests to cover both template variants
- Add patch changeset for the published package

Files changed:
 .changeset/fn-7962-idle-heartbeat-patrol-backoff.md |  7 +++++++
 packages/core/src/__tests__/agent-prompts.test.ts   | 14 ++++++++++++++
 packages/core/src/agent-prompts.ts                  |  8 ++++++++
 3 files changed, 29 insertions(+)

Fusion-Task-Id: FN-7962

Fusion-Task-Lineage: 0a233b5d-00d8-4dda-b627-95d4b8122a55

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-15 12:15:37 -07:00
gsxdsm
37db66965c FN-7958: loosen mobile agent header and overview hero spacing
Ease cramped mobile agent detail identity and Overview hero rows while preserving the non-overlap header grid.
- Increase mobile header padding, column/row gaps, and identity gap for breathing room
- Add badge wrap gaps and keep lifecycle controls on the FN-6865 non-overlap grid
- Give Overview hero heading/meta/skills deliberate row gaps and wrap long health text
- Nudge summary-card padding and hero gaps further under 480px
- Extend mobile scroll and core tests for spacing invariants

Files changed:
 packages/dashboard/app/components/AgentDetailView.css   |  51 +++++-
 .../__tests__/AgentDetailView.core.test.tsx        |   4 +
 .../AgentDetailView.mobile-scroll.test.tsx         | 182 ++++++++++++++++++++-
 3 files changed, 230 insertions(+), 7 deletions(-)

Fusion-Task-Id: FN-7958

Fusion-Task-Lineage: c555811f-9dce-4725-81b5-2705aeaaa1db

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-15 12:12:53 -07:00
gsxdsm
d956abceee FN-7956: bundle three plugins as install-safe JS entrypoints
Ship reports, cli-printing-press, and whatsapp-chat through tsup so global/npm installs no longer load raw .ts under node_modules (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING).

- Route WhatsApp Chat, Reports, and CLI Printing Press through bundlePluginEntry instead of copying src/
- Re-export postgresSchema from the core runtime shim for published plugin bundles
- Keep Baileys optional deps external for WhatsApp Chat bundling
- Extend bundle-output helpers/tests to assert self-contained plugin bundles
- Add patch changeset for @runfusion/fusion packaging fix

Files changed:
 .changeset/fn-7956-bundle-three-plugins.md         |  7 ++
 .../cli/src/__tests__/bundle-output-helpers.ts     | 30 +++++++-
 packages/cli/src/__tests__/bundle-output.test.ts   | 83 ++++++++++++++--------
 packages/cli/src/plugin-sdk-core-runtime-shim.ts   |  6 ++
 packages/cli/tsup.config.ts                        | 71 +++++++-----------
 5 files changed, 123 insertions(+), 74 deletions(-)

Fusion-Task-Id: FN-7956

Fusion-Task-Lineage: e32736d9-e637-450c-966b-6b2d6bc69b88

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-15 12:09:29 -07:00
gsxdsm
ecd497190d fix: abort host tools on timeout and tighten store-injection cleanup
Code review follow-up for the dual-boot hang fixes:
- Outer tool wrap aborts a linked AbortController on timeout so nested work (npx) stops
- fn_skills_install uses SIGTERM then delayed SIGKILL instead of immediate double-kill
- clearHostTaskStores only drops external entries (does not wipe unrelated CLI boot state)
- Align import/browse schema max with the 50-item hard clamp
- Tests for host-store cache injection and timeout-driven signal abort
2026-07-15 11:36:49 -07:00
gsxdsm
64db34da98 fix: share engine TaskStore with host extension and harden hang paths
Kill the dual-boot FN-7956 class hang for in-process agent tools:
- setHostTaskStore/clearHostTaskStores inject the live dashboard/serve/daemon store
- Prefer host-injected store over createTaskStoreForBackend; race-safe with external overwrite
- fn_skills_install kills npx on abort/timeout so orphan install processes cannot outlive the turn
- Raise budgets for task plan, experiment finalize, and mission backfill
- Hard-cap import/browse batch size at 50 (GitHub + GitLab)
2026-07-15 11:34:12 -07:00
gsxdsm
c6050785bf fix: remove fn_research_* from the host pi extension
Host-extension research tools dual-booted a second TaskStore and could wedge
agent turns via wait_for_completion polling (same hang class as FN-7956).

Leave research available only when the engine injects createResearchTools under
experimentalFeatures.researchView. Operators still use fn research CLI and the
dashboard Research view. Regen fusion skill docs from extension.ts.
2026-07-15 11:25:55 -07:00
gsxdsm
93baf482f9 fix: tighten extension tool budgets after hang-fix review
Address review findings on the FN-7956 hang fix:
- Per-tool outer timeouts so fn_research_run(wait_for_completion) is not clipped by a flat 60s budget
- Longer budgets for skills install, import/browse, and web_fetch
- Boot-failure cooldown + orphan-boot log when store boot times out
- Log timeout/abort/errors from the extension wrap; clearer host-extension skip reason
- Tests for budgets, research wait, and sessionPurpose forwarding
2026-07-15 11:23:29 -07:00
gsxdsm
779954afee fix: seed rejected PROMPT.md on replan so Plan Review can converge
Plan Review REVISE previously fed feedback without the rejected plan body, so triage rewrote from title/description and looped. Seed the draft for surgical revision, use reviewType spec for the pre-execution gate, and tighten planner/reviewer prompts toward blocking-only REVISE with concrete edits.
2026-07-15 11:18:55 -07:00
gsxdsm
335b6a4dc2 fix: raise Plan Review replan cap to 8 and explain approval holds
Give planner/reviewer pairs more room to converge before escalating, and surface why a task is parked for plan approval—especially plan-review-replan-cap non-convergence—on cards, detail, and notifications.
2026-07-15 11:14:17 -07:00
gsxdsm
508453ad03 fix: stop merger/extension tools from wedging on hung fn_task_show
AI merge review could park forever when the host fusion extension loaded
fn_task_show and booted a second TaskStore without a tool timeout (FN-7956).

- Skip host @runfusion/fusion extensions for sessionPurpose "merger"
- Forward sessionPurpose into createFnAgent for that policy
- Coalesce + 30s-bound extension TaskStore boots; ALS-propagate AbortSignal
- Wrap every extension registerTool execute with 60s timeout/abort fail-closed
- Unit tests for merger host-extension skip and tool timeout helpers
2026-07-15 11:13:56 -07:00
gsxdsm
e172b13612 fix(dashboard): never show raw landing/reviewing on task status badges
Share one badge label mapper for board and list so AI-merge pipeline
statuses always display as Merging… instead of engine strings.
2026-07-15 10:39:57 -07:00
gsxdsm
49a459a869 FN-7954: fix plugin skill toggle keys for custom skillFiles paths
Align plugin skill enable/disable reads with the resolved skillFiles path so Skills-view toggles persist and sessions honor them.

- Accept optional skillRelativePath in resolvePluginSkillEnabled for custom skillFiles keys
- Pass resolved relativePath from dashboard skills adapter when merging plugin skills
- Reuse resolved body path in session-skill-context for enable checks and additionalSkillPaths
- Add unit coverage for custom-path round-trip, enable, and disable behavior
- Ship patch changeset for @runfusion/fusion

Files changed:
 .changeset/fn-7954-plugin-skill-toggle-key-fix.md  |  7 +++
 packages/core/src/__tests__/skill-settings.test.ts | 15 ++++++
 packages/core/src/skill-settings.ts                |  8 +++-
 .../dashboard/src/__tests__/skills-adapter.test.ts | 55 ++++++++++++++++++++++
 packages/dashboard/src/skills-adapter.ts           |  1 +
 .../src/__tests__/session-skill-context.test.ts    | 45 ++++++++++++++++++
 packages/engine/src/session-skill-context.ts       | 29 +++++++-----
 7 files changed, 147 insertions(+), 13 deletions(-)

Fusion-Task-Id: FN-7954

Fusion-Task-Lineage: 3399186b-7325-4ed7-8900-85eb2ef98c7e

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-15 10:38:04 -07:00
gsxdsm
49114a27ad fix(dashboard): show Merging badge during AI-merge reviewing/landing
AI merge spends most of its time in reviewing and landing, not merging.
Treat the full merge pipeline as active so cards, workflow switcher, and
stall suppression show Merging… while the pump owns a task.
2026-07-15 10:36:10 -07:00
gsxdsm
ddc8e6dd1a FN-7961: backfill blank titles on terminal triage failures
Give terminally failed planning tasks deterministic non-LLM titles so orphaned blank-title rows stay visible after model unavailability.

- Add deriveFallbackTaskTitle / FALLBACK_TASK_TITLE for description-based title derivation
- Export the helper from @fusion/core (public + gate entrypoints)
- Backfill blank titles on terminal triage failure paths without overwriting existing titles
- Cover helper and all terminal specifyTask failure surfaces with tests
- Add patch changeset for the operator-visible fix

Files changed:
 .changeset/fn-7961-blank-title-fallback.md       |   7 +
 packages/core/src/__tests__/ai-summarize.test.ts |  42 +++++
 packages/core/src/ai-summarize.ts                |  42 +++++
 packages/core/src/index.gate.ts                  |   2 +
 packages/core/src/index.ts                       |   2 +
 packages/engine/src/__tests__/triage.test.ts     | 209 +++++++++++++++++++++++
 packages/engine/src/triage.ts                    |  23 +++
 7 files changed, 327 insertions(+)

Fusion-Task-Id: FN-7961

Fusion-Task-Lineage: 1984c31d-e184-4592-b32f-1736a9ce27f6

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-15 10:32:48 -07:00
gsxdsm
16b0109300 FN-7959: focus Planning compose textarea on New session
Focus the Planning compose textarea when New session is pressed, even if blank compose is already active, and preserve in-progress draft text.

- Add a click-driven newSessionFocusSignal so New session always re-focuses the compose textarea after rAF (mobile detail pane visibility)
- Preserve initialPlan when starting a new session from the already-active blank compose view
- Cover focus, caret placement, draft preservation, and mobile show-detail surfaces in PlanningModeModal tests
- Add patch changeset for @runfusion/fusion

Files changed:
 .changeset/fn-7959-planning-new-session-focus.md   |   6 +
 .../dashboard/app/components/PlanningModeModal.tsx |  37 ++++-
 .../__tests__/PlanningModeModal.initial.test.tsx   | 174 +++++++++++++++++++++
 3 files changed, 213 insertions(+), 4 deletions(-)

Fusion-Task-Id: FN-7959

Fusion-Task-Lineage: ad71ad28-3e22-454a-907f-3423475180f9

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-15 10:28:38 -07:00
gsxdsm
3676586460 fix(cli): pass engine PluginRunner into hosts, not PluginLoader
Stop publishing the bare PluginLoader as createServer.pluginRunner so Grok CLI
routing can resolve getRuntimeById. Dashboard engine mode relies on engine.onMerge;
UI-only/bare CLI omit the runner (dual-remediation). Conflict resolver drops
non-capable runners instead of casting them.
2026-07-15 10:24:30 -07:00
gsxdsm
3748eca073 docs(agents): allow direct main work; use worktrees only for branches
Relax the worktree standing rule: agents may commit on main when the change
belongs there. When work needs a feature branch, create a worktree instead of
switching the primary checkout off main.
2026-07-15 10:17:23 -07:00
gsxdsm
fc37db3701 docs(agents): require worktrees — never switch or commit on main
Standing rule: agents must create an isolated git worktree (prefer Worktrunk)
for all implementation and commits, and must not check out, switch, or mutate
the primary main working tree.
2026-07-15 10:12:22 -07:00
gsxdsm
30f9cac46a fix(cli): forward PluginRunner into UI/CLI merge and PR conflict doors
Thread a real engine PluginRunner (getRuntimeById) into runAiMerge,
landWorkspaceTask, and create-PR conflict resolution so grok-cli/no-key
sessions resolve the Grok runtime. Bare fn task merge keeps pluginRunner
undefined rather than inventing a bootstrap.
2026-07-15 10:07:21 -07:00
gsxdsm
4d78f9641b feat(dashboard): build/link local fn and npm restore from System panel
Add Command Center System controls for source/dev hosts to build the
standalone fn binary and install it as the default PATH binary, switch
back to the global npm install, and force-check for published updates.
Build jobs stream into the shared job log and scroll that view into focus.
2026-07-15 10:07:20 -07:00
gsxdsm
a67c2763af fix(merge-queue): serialize reclaim and status-aware silence policy
Prevent concurrent orphan merge after abort, protect long merging-phase
tools from false reclaim, emit run-audit on wedged reclaim, and race PR
merge dispatch the same way as direct AI merge.
2026-07-15 10:06:32 -07:00
gsxdsm
0eb46f2a89 fix(engine): reclaim wedged single-flight merge pump automatically
AI-merge review hangs left activeMergeTaskId/mergeRunning set while
status=reviewing and overseer logEntry noise kept updatedAt fresh, so
self-healing never reclaimed the owner and the board showed no merging
badge. Race merge work with abort, force-abort on pause/reclaim, treat
reviewing as merge-active, and recover on merger agent silence; also
forward PluginRunner into AI merge so grok-cli merger matches chat.
2026-07-15 09:52:41 -07:00
gsxdsm
7a4a9c8229 fix(engine): auto-recover false-positive heartbeat-model-unavailable parks
Admit under-budget paused/heartbeat-model-unavailable agents to the shared
heartbeatErrorRecovery budget so timer, self-healing, and startup paths
retry without a manual Retry. Keep the pause reason when the budget is
exhausted so operators still see credential guidance.
2026-07-15 08:58:33 -07:00
gsxdsm
e9f14bf024 perf: speed up local pnpm build and cap stacked verifications (#2134)
## Summary

- Extend the workspace content-hash skip cache to **all** packages (not
just plugins), with `--force` / `--full` flags
- Default local CLI packaging to a **fast mode** (bin/extension +
migrations only); full desktop/plugin/DTS staging runs on CI or `pnpm
build:full`
- Enable TypeScript `incremental` builds for warm recompiles
- Add `maxConcurrentVerifications` (default **1**) so concurrent tasks
cannot stack monorepo typecheck/build and peg CPU

Warm `pnpm build` measured ~**126s → ~0.8s** when nothing changed.

## Test plan

- [x] `node --test scripts/__tests__/build-workspace.test.mjs` (12 pass)
- [x] `pnpm --filter @fusion/engine exec vitest run
src/__tests__/verification-concurrency.test.ts`
- [x] `pnpm --filter @fusion/core exec vitest run
src/__tests__/settings-parity.test.ts`
- [x] Local: first `pnpm build` rebuilds as needed; second warm `pnpm
build` skips all packages (~0.8s)
- [x] Fast CLI packaging logs skip of desktop/plugin staging without
`FUSION_CLI_FULL_PACKAGE`
- [ ] CI: `pnpm build` still full-packages under `CI=true` (plugin
staging / release surfaces)

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

* **New Features**
* Added a Scheduling setting to limit concurrent verification tasks from
1–8, with a default of 1.
* Verification tasks now support cancellation while waiting or running.
  * Added options for forced and full workspace builds.

* **Performance**
* Local builds can skip unchanged packages and use incremental
compilation for faster rebuilds.
* Local CLI packaging is faster by default, while full packaging remains
available when needed.

* **Documentation**
* Updated the settings reference with the new verification concurrency
option.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-15 08:44:11 -07:00
gsxdsm
8fe122d77e feat: preserve original description at top of generated PROMPT.md (#2129)
## Summary

Generated PROMPT.md (after triage/planning — not the bootstrap stub) now
keeps the operator's original task description near the top under `##
Original Description`, so executors always see the source request even
after Mission/Steps rewrites.

- **AI-planned path:** planning templates (standard/fast/concise)
require a verbatim `## Original Description` section;
`buildSpecificationPrompt` instructs the planner; `finalizeApprovedTask`
deterministically injects/rewrites it as hygiene.
- **Non-AI path:** `generateSpecifiedPrompt` uses the same pure helper
so direct creates into non-intake columns get the same contract.
- **Description edits:** real specs keep `## Original Description` in
sync when `task.description` changes.
- **Unchanged:** bootstrap stubs and `isUnplannedSeedPrompt` equality
detection.

## Surfaces

| Surface | Change |
|--------|--------|
| `original-description-policy.ts` | Shared inject/rewrite helper |
| `agent-prompts.ts` | Template + requirement text |
| `triage.ts` finalize + `buildSpecificationPrompt` | Instructions +
post-write pin |
| `generateSpecifiedPromptImpl` | Non-AI specified PROMPT.md |
| `task-update.ts` | Description sync on real specs |

## Test plan

- [x] `pnpm --filter @fusion/core exec vitest run
src/__tests__/original-description-policy.test.ts
src/__tests__/agent-prompts.test.ts
src/__tests__/mesh-task-replication.test.ts
src/__tests__/store-create-intake-column.test.ts --silent=passed-only
--reporter=dot`
- [x] `pnpm --filter @fusion/engine exec vitest run
src/__tests__/triage.test.ts -t "Original Description|injects ##
Original" --silent=passed-only --reporter=dot`
- [ ] CI gate (Lint / Typecheck / Build / Gate)

## How to verify manually

1. Create a task with a distinctive description, let triage plan it (or
finalize a mock plan).
2. Open `.fusion/tasks/<id>/PROMPT.md` and confirm `## Original
Description` appears after title/metadata with the raw description,
before Mission / Before → After.
3. Direct-create into `todo` (non-intake) and confirm the non-AI
generated prompt also has the section.


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

## Summary by CodeRabbit

- **New Features**
- Generated `PROMPT.md` specifications now include an `## Original
Description` section near the top.
- Operator task descriptions are preserved verbatim for AI-planned and
specified prompts.
  - Updated prompts remain synchronized when task descriptions change.

- **Bug Fixes**
- Replaced paraphrased original descriptions with the correct task
description.
  - Preserved existing prompt content during review and retry workflows.

- **Tests**
- Added coverage for placement, formatting, replacement, and idempotent
behavior.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-15 02:19:19 -07:00
gsxdsm
bc2d22df6e feat(dashboard): offer AI translation in Import Tasks preview (#2128)
## Summary

Import Tasks can now offer on-demand AI translation when a selected
GitHub or GitLab issue/PR title and body appear to be in a different
language than the active dashboard locale.

- Detect foreign-language content with a conservative client heuristic
(Unicode scripts + Latin stopwords)
- Show an opt-in banner: **Translate**, then **Show original / Show
translation**, plus **Dismiss**
- Call new `POST /api/ai/translate-text` (shared AI-helper rate limit
with refine/draft)
- Translation is **display-only** in the preview; imported task text
stays the original source language

## Why

Operators working in a non-English dashboard (or reading
non-dashboard-language issues) needed a way to understand import
candidates without leaving the preview or changing what gets imported.

## Test plan

- [x] Unit tests for language detection (`detectContentLanguage`)
- [x] Unit tests for translate request validation, response parsing, and
AI agent path
- [x] GitHub import modal: French content shows translate controls;
English content does not
- [x] Dashboard typecheck clean for app + server packages
- [ ] Manual: open Import Tasks with dashboard language English, select
a French/Korean issue, translate and toggle original
- [ ] Manual: confirm Import still creates the task with original
title/body
- [ ] Manual: dismiss banner for a selection and confirm it stays
dismissed for that item

## Notes

- Comments are not translated (title + body only)
- zh-CN / zh-TW share a CJK family so Chinese content does not prompt
translation when the UI is either Chinese locale
- Secondary locale catalogs have empty placeholders for the new
`git.translate*` keys (runtime falls back to English)
2026-07-15 02:18:43 -07:00
gsxdsm
78ef3075f6 fix(core): prevent plugin migration startup crash
Run retained SQLite plugin recovery through the privileged startup connection before handing stores to the restricted PostgreSQL runtime role.
2026-07-15 02:16:58 -07:00
gsxdsm
c63637ff44 fix(dashboard): make title auto-summarize settings searchable
Index Project Models for summarize/auto-summarize phrases and control labels so Settings search surfaces autoSummarizeTitles.
2026-07-15 00:29:24 -07:00
gsxdsm
a242f1b449 fix(FN-7952): migrate bundled plugins to PostgreSQL (#2111)
## Summary

Bundled plugins now persist shared runtime state in project-scoped
PostgreSQL tables instead of maintaining independent SQLite authority.
Reports, CLI Printing Press, Compound Engineering, Roadmap, Even
Realities, and WhatsApp all follow the same ownership and startup
contract as Fusion core.

## Design decisions

- Plugin schema hooks run through the host’s PostgreSQL owner and
enforce project isolation.
- The SDK exposes the host contract needed by bundled plugins without
importing engine internals.
- Legacy Roadmap ownership fixtures use the supported empty-owner
sentinel, preserving current composite primary/foreign keys while
exercising backfill behavior.
- The lockfile travels with the Even Realities PostgreSQL dependency so
packaged installs remain reproducible.

## Validation

- All six affected plugin builds pass.
- Affected plugin suites pass: 773 tests across Printing Press, Compound
Engineering, Even Realities, Reports, Roadmap, and WhatsApp.
- `pnpm test:gate` passes all 478 gate tests.
- This PR changes 40 files.

## Stack

- Depends on #2110 → #2109 → #2108.
- The documentation/release PR completes the stack.

Related: #2105


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

## Summary by CodeRabbit

* **Breaking Changes**
* PostgreSQL is now required for runtime storage; SQLite files are used
only as one-time migration inputs.
  * The legacy `FUSION_NO_EMBEDDED_PG` fallback has been removed.

* **New Features**
* Added project-isolated PostgreSQL storage for plugins, reports, tasks,
notifications, and other plugin data.
  * Added agent tools for reports and CLI service drafts.
  * Added PostgreSQL schema initialization support for plugin authors.

* **Bug Fixes**
  * Improved migration and recovery of legacy plugin state.
* Prevented cross-project data access and strengthened transactional
schema updates.

* **Documentation**
* Updated storage, migration, deployment, plugin authoring, CLI, and
dashboard guidance for PostgreSQL.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-15 00:27:59 -07:00
gsxdsm
6c008418fe fix(core): boot embedded Postgres under non-admin user on elevated Windows (#2117)
## Summary

Windows embedded Postgres verification (CI `windows-latest` and elevated
desktop) fails because PostgreSQL refuses to run under an administrative
token:

> Execution of PostgreSQL by a user with administrative permissions is
not permitted.

GitHub Actions runners execute as `runneradmin` elevated, so the
existing `test:embedded-postgres` smoke (and any elevated Local-mode
desktop launch) cannot start the server.

### Fix

- When `isWindowsElevatedAdmin()` is true, **initdb / clients stay as
the launcher**, but the **postgres server** is started as a dedicated
non-admin local user (`fusion-pg`) via PowerShell `Start-Process
-Credential`.
- Readiness waits on the postgres log line `database system is ready to
accept connections` with a lightweight poll (no per-iteration
`tasklist`).
- Real-process vitest cases use a **180s** timeout on Windows (package
default is 15s, which killed healthy boots mid-start).
- Builds on top of the packaged-desktop asar materialization work
already on main (#2106).

## Test plan

- [x] `pnpm --filter @fusion/core test:embedded-postgres` on macOS
(33/33)
- [ ] `desktop-windows.yml` on `feature/win-pg-verify`:
  - [ ] Smoke embedded Postgres on Windows
  - [ ] Build + package Windows EXE
  - [ ] Verify app.asar assets
- [ ] Optional: download portable EXE and manual Local mode smoke on a
Windows host

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

- **Bug Fixes**
- Improved embedded PostgreSQL startup on Windows when Fusion runs with
elevated administrator privileges.
- When elevated, the embedded database now boots under a dedicated
non-administrator local account, with more reliable readiness detection,
logging, and shutdown cleanup.
- Enhanced database provisioning and now prefers `127.0.0.1` for Windows
connection addressing.

- **Tests**
- Added coverage for Windows elevation detection without starting
embedded PostgreSQL.
- Increased platform-dependent timeouts for embedded real-process tests
to avoid premature failures on Windows.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-15 00:15:10 -07:00
gsxdsm
97172fdcf2 fix(FN-7952): require PostgreSQL in CLI and desktop (#2110)
## Summary

CLI commands, daemon/dashboard startup, packaged desktop startup, and
live-data maintenance scripts now share the mandatory PostgreSQL
lifecycle. Operators no longer risk a command silently reading or
writing a disconnected SQLite shadow when PostgreSQL setup fails.

## Design decisions

- Every startup owner retains and awaits its PostgreSQL shutdown
callback, including partial-startup failure paths.
- CLI project context and lock-retry flows resolve through asynchronous
project stores.
- Maintenance scripts use the shared backend helper; explicit database
migration/inspection remains the only CLI surface allowed to read legacy
SQLite sources.

## Validation

- CLI and Desktop typechecks pass on the stacked branch.
- `pnpm test:gate` passes all 478 gate tests.
- This PR changes 54 files.

## Stack

- Depends on #2109, which depends on #2108.
- Bundled plugins and docs/release follow in later PRs.

Related: #2105


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

## Summary by CodeRabbit

* **New Features**
* PostgreSQL is now the authoritative store for structured project and
task metadata.
* Projects can be recognized and initialized using
`.fusion/project.json`, without creating a legacy SQLite database.
  * CLI commands now retry transient PostgreSQL contention errors.

* **Bug Fixes**
* Improved cleanup when commands complete, fail, or run in the
background, preventing lingering resources.
  * Improved desktop, server, and session shutdown reliability.

* **Documentation**
* Updated storage and standalone binary guidance to reflect PostgreSQL
and legacy SQLite compatibility.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-14 23:18:55 -07:00
Phil Larson
7261083781 fix(dashboard): isolate MCP settings scopes (#2116)
## Summary

- bind Global and Project MCP editors to their raw values from
`/api/settings/scopes`
- keep edits isolated in the owning scope instead of mutating the merged
project-effective form
- persist changed MCP scopes independently of which Settings section is
visible when Save is pressed
- add unit and SettingsModal regressions for opposite scope values and
edit → navigate → save

## Root cause

`SettingsModal` passed the merged project-effective `form.mcpServers` to
both MCP sections. A project override could therefore appear as the
Global MCP value. Saving the apparent global change could then be
dropped as a no-op when compared with the actual global-scoped value.

The first fix still tied save routing to the active section. The
follow-up carries both raw scoped MCP values through
`splitSettingsSave`, where changed-only comparisons persist each owning
scope even after navigation.

## Testing

- `pnpm exec vitest run --project dashboard-app
app/__tests__/settings-save-split.test.ts` (32 passed on PR branch)
- `pnpm exec vitest run --project dashboard-app-quality-settings
app/components/__tests__/SettingsModal.general.test.tsx` (84 passed)
- `pnpm run typecheck`
- `pnpm exec eslint app/components/SettingsModal.tsx
app/components/settings/save-split.ts
app/__tests__/settings-save-split.test.ts
app/components/__tests__/SettingsModal.general.test.tsx`
- `pnpm run build`


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

## Summary by CodeRabbit

* **Bug Fixes**
* Fixed MCP settings so global and project configurations remain
correctly separated.
* Prevented inherited global settings from appearing as project
overrides.
  * Preserved MCP edits when navigating between Settings sections.
  * Ensured unchanged settings are not unnecessarily saved.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-14 22:58:59 -07:00
gsxdsm
ba1e82381e fix(FN-7952): cut runtime services over to PostgreSQL (#2109)
## Summary

Engine and dashboard traffic now stays on the authoritative PostgreSQL
layer across execution, recovery, project discovery, planning sessions,
analytics, and shutdown. The dashboard no longer presents a migration
notice for a cutover that is already mandatory.

## Design decisions

- Runtime composition requires an async data layer instead of
constructing a hidden SQLite fallback.
- Engine workflow, mission, claim, and self-healing reads await their
PostgreSQL-backed store contracts.
- Project-scoped dashboard stores retain and close their backend owner
exactly once.
- The dashboard test quarantine entry remains paired with its Vitest
exclusion, preserving the repository’s deletion-ratchet policy.

## Validation

- Core, Engine, Dashboard, CLI, and Desktop typechecks pass on the
stacked branch.
- `pnpm test:gate` passes all 478 gate tests.
- This PR changes 62 files.

## Stack

- Depends on #2108.
- CLI/desktop/ops, plugins, and docs/release follow in later PRs.

Related: #2105


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

## Summary by CodeRabbit

* **New Features**
* Project discovery now recognizes projects using the
`.fusion/project.json` marker.
* Knowledge indexing and search are more reliable across project-scoped
storage.
* **Bug Fixes**
* Improved session, audit timeline, approval, monitoring, and analytics
data consistency.
* Prevented stale planning-session updates and project-store shutdown
races.
* Ensured chat usage and CLI session status are saved before continuing.
* **UI Changes**
* Removed the storage migration notice banner now that the PostgreSQL
transition is complete.
* **Reliability**
* Improved shutdown handling, workflow execution, and worktree behavior.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-14 22:16:16 -07:00