Commit Graph

46 Commits

Author SHA1 Message Date
gsxdsm
89284df85e E2E: table-driven converted-sweep coverage, two new sites, and an honest unproven-sites ledger (#2485)
Follow-up to #2475 (merged). Test-only, plus one test-utility seam.

## Why a table

#2475 proved one converted sweep. The count has since gone to **three**,
twice while this work was open — `surfaceStalePausedTodos` appeared
during #2475's review, and #2478 landed `recovery-reconciler.ts` while
this branch was open. A suite with a bespoke `describe` per sweep is a
coverage claim that quietly becomes false.

Replaced with a table of `(seed, run, acted, roles, observability)`. The
driver derives four assertions per entry:

| | positive | negative |
|---|---|---|
| **renamed vocabulary** | acts on the card | inert in a non-target
column |
| **default vocabulary** | acts (regression floor) | inert |

Adding a converted sweep is **one entry** — the #2478 site proved that
in practice, not in principle. `actsOnRole`/`inertRole` are keys of
`Vocabulary`, not column strings, so an entry cannot hardcode `todo` and
pass for the wrong reason.

## Two findings, both from mutation rather than reading

**1. The census was wrong about `recovery-reconciler.ts:198.`** It was
flagged as a `resolveLifecycleColumns` site, so the row was first
labelled as covering it. **Destroying that role resolution leaves all 18
tests green** — `decideRecovery` looks policy up by *column id* and
never consults a role. The row is relabelled to what it actually proves,
and mutation-verified against that instead: keying the reconciler's
policy lookup on the `todo` literal fails exactly its renamed test.

**2. `resolveRoleRecovery` is an unreachable export.** It is the only
use of `resolveLifecycleColumns` in that file and has **no production
caller anywhere** in engine, core, or dashboard. So that census line is
not a live converted site — it is a helper written ahead of its
consumer. **Not fixed here:** it is production code owned by the U4
slice, and whether the consumer is still to land or it should be deleted
is its author's call.

## Observability is now explicit in the type

`persisted-row` is the strong form. `returned-decision` is recorded as
**weaker evidence** and the reconciler row uses it, because
`reconcileRecovery` decides and does not apply — there is no row to
read. Naming it in the type is what stops a return-value assertion from
quietly passing as observed state, and it is what keeps the ledger
truthful per site.

## Harness seam

`PgTestHarness` now exposes its raw admin SQL client. The store
**stamps** `updatedAt`/`columnMovedAt` on every write, so `updateTask`
cannot express an aged row at all — the patch is accepted and the value
silently replaced with `now`. **Found by the new case failing on BOTH
vocabularies**, which is what distinguishes a broken fixture from a
broken guard. Seeding only; assertions still read back through the real
`getTask` path.

## Mutation verification

| Mutation | Result |
|---|---|
| revert **only** `recoverStrandedCompletedTodoTasks`'s resolution |
**exactly** that row's renamed test fails |
| revert **only** `surfaceStalePausedTodos`'s resolution | **exactly**
its own renamed test fails |
| reconciler policy lookup keyed on `todo` | exactly the reconciler
row's renamed test fails |
| `resolveRoleRecovery` role resolution destroyed | **nothing fails** →
finding #2 |
| `hold-release` `isHeldTask` keyed on `todo` | 5 of 18 fail; default
spine survives |
| `markMoveInFlight` dropped | both spine tests fail |

Per-site verification matters here: three rows could all be riding one
guard. They are not.

## The honest number

**Proven end to end: 5** (two self-healing sweeps, the reconciler's
policy lookup at the weaker observability, hold-release's capacity
release, and the graph boundary + `moveTask` + post-commit bus).

**Not proven: 11 call sites** — `merger.ts:324-326`,
`merger-ai.ts:1022,1039`, `auto-merge-finalization.ts:20-22`,
`executor.ts:1763,6339,6341`, `self-healing.ts:713,6732`,
`mesh-lease-manager.ts:61`, `task-agent-sync.ts:59`,
`core/task-store/reads.ts:130`, `core/live-agent-count.ts:63-75`, and
four dashboard route sites.

The ledger lives in the file, not just here, so it stays with the code.

## Where the table does not fit — reported, not papered over

The **merge/rebound family** cannot be a table row: those sweeps have no
observable persisted effect without a real git repository, so `acted`
cannot be written against the row at all. They need an engine-slow
real-git lane. The dashboard sites need an HTTP route test with a live
store. Both are different lanes, not missing entries.

## Verification

- 18/18 green; engine + core `tsc --noEmit` clean; `pnpm test:gate`
green (299 + 10 + 71)
- full core PG suite run (the harness is shared): 1036 passed, 3 failed
in `central-archive-secrets` and `workflow-settings-project-identity` —
**reproduce identically with this change stashed**, pre-existing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 15:25:17 -07:00
gsxdsm
0307476f35 fix(tests): restore listTasks hot-path contract and PG harness fs isolation
- usage-limit-detector + provider-health-monitor: make three bare listTasks()
  callers explicit with { slim: true }, restoring the architecture-hot-paths
  contract (they only read scalar pause/column/model-provider fields).
- pg-test-harness beforeEach: wipe <rootDir>/.fusion/tasks after TRUNCATE ...
  RESTART IDENTITY so filesystem isolation matches the id reset; stale task
  dirs from prior tests no longer collide with reused IDs (fixes
  store-reservation-atomicity rollback assertions).
2026-07-23 17:27:59 -07:00
gsxdsm
e5d6be4123 Fix Windows embedded PostgreSQL 0xC0000142 crashes (issue #2411) (#2418)
## Summary

Two-part fix for #2411 — Windows embedded PostgreSQL backends dying with
exception `0xC0000142` and taking the whole dashboard down.

### 1. Crash hardening + recovery (FN-8522)
- Child-only native `PATH` hardening so forked backends can always
resolve their runtime DLLs.
- Non-blocking `.pgrunner` log monitoring (shared read), eliminating the
self-inflicted ~30s `sharing violation` retry window at boot.
- Detection of the ordered 0xC0000142 shutdown sequence with a single
automatic restart of owned clusters on their resolved port, plus
operator diagnostics.

### 2. Platform-aware `max_connections` default (follow-up from
[operator
report](https://github.com/Runfusion/Fusion/issues/2411#issuecomment-5054900702))
On Windows every PostgreSQL connection is a separate process; the
embedded cluster's unconfigured `max_connections=500` cap lets backend
spawn bursts exhaust the non-interactive desktop heap, which kills
forked backends with exactly `0xC0000142`. The reporter confirmed
stability after lowering the cap.

- `embeddedPostgresMaxConnections` is now schema-unset so the server can
distinguish "operator never set it" from an explicit choice
(`getSettings()` merges schema defaults, which previously pinned 500
unconditionally and made the runtime fallback dead code).
- New `resolveEmbeddedMaxConnections()` resolves the unset default
platform-aware: **150 on win32, 500 elsewhere**. Explicit settings are
honored on every platform, clamped to [32, 2000] as before.
- Settings UI renders the cap empty ("auto") with platform-aware help
copy across all six locales.
- Fixed a latent reset bug this exposed: global "Reset this menu" wrote
`undefined` for undefined-default keys, which JSON serialization drops —
the stored value silently survived reset. Now uses null-as-delete.

## Testing
- New unit tests for `resolveEmbeddedMaxConnections` (platform defaults,
clamping, non-integer handling).
- Updated settings-defaults, default-descriptions, and SettingsModal
tests; embedded lifecycle + recovery coverage from FN-8522.
- `@fusion/core` builds clean; changesets included for both parts.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 23:41:48 -07:00
gsxdsm
1e05793876 fix(ci): green full-suite bookkeeping after origin/main cutover (#2392)
## Summary

Restores green merge-gate and package-default suites after repeated
`origin/main` merges brought workflow-graph ownership cutover drift into
CI.

- Align engine/dashboard/core tests with post-cutover contracts
(`moveTaskIf`/`deleteTaskIf`, graph handoff, worktree-pool reclaim via
`removeWorktree` + `RemovalReason`, multi-step RESUMING parse,
soft-pause merge requester, graph-terminal failure surfaces).
- Small product fixes needed for real regressions uncovered by the
suite: soft-delete refuse before graph routing, skip DUPLICATE
step-heading withhold when an explicit marker is present, PG schema
applier guards, and related bookkeeping (research promote tool inventory
/ migration seed, stop shell `psql` in PG admin DDL).
- Quarantine/ledger hygiene only where required by standing rules; no
timeout/worker appeasement.

## Verification

- `pnpm test:gate` ×2 green
- `@fusion/engine` full package suite green (~9083 tests)
- Targeted core/dashboard clusters green (schema applier, agent-runs UI,
settings descriptions, mobile close)

## Test plan

- [x] `pnpm test:gate` (twice)
- [x] `pnpm --filter @fusion/engine test`
- [ ] CI full suite / PR checks on this branch
- [ ] Confirm no unrelated product behavior changes beyond the listed
regression fixes

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

* **New Features**
* Added support for `roadmap-item` native structure kinds, including
native structure embeds and metadata validation.
  * Added Stable and Beta release channel options in General settings.
* Added per-action reporting target configuration with clearer “unset”
guidance.

* **Bug Fixes**
  * Improved heartbeat/prompt behavior when patrol is disabled.
  * Prevented deleted tasks from continuing through execution.
  * Made recovery for explicit duplicate redirects more permissive.
* Hardened database migration and test database cleanup to reduce flaky
failures.

* **Documentation**
* Updated settings text for release channels, reporting targets, and
inheritance/unset behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-21 23:09:30 -07:00
gsxdsm
7fe12ae124 test(core): share one golden pg schema template across the gate
The pg-gate rebuilt a full schema baseline (~530ms of DDL) per isolated test
file. Fanned across forks against one Postgres, those baselines and their
CREATE DATABASE calls serialized and pushed per-file beforeAll past the 15s
hookTimeout nondeterministically on high-core machines. Complements the
committed fork cap: apply the baseline ONCE per run into a run-shared,
advisory-lock-coordinated golden template, then copy each test DB directly
from it (concurrent, connection-free copies are safe). Per-module templates
and their lifecycle hooks are retained for the concurrency regression test.
No timeout was changed. pg-gate: ~46s tests / ~17s wall, 6/6 clean runs
(was flaky/364s).

Fusion-Task-Id: automation-slow-test
2026-07-20 18:26:01 -07:00
gsxdsm
caf425eaea test(core): cap pg-gate fork fan-out for DB-bound suite
The test:pg-gate suite runs only *.pg.test.ts files, each building/copying a
per-file schema-template database (heavy CREATE/DROP DATABASE DDL serialized by
the single shared Postgres). Worker count derived from CPU cores over-scales on
high-core machines (6 forks on a 28-core box), oversubscribing the one Postgres
until every beforeAll exceeds the 15s hookTimeout (23/23 hook timeouts). CI's
low-core runners stay near 2 forks and pass, so it only bites high-core locals.

Add a maxCap clamp to computeMaxWorkers and a dedicated vitest.pg.config.ts
(maxCap=4) for the pg-gate, right-sizing concurrency to the actual constraint (a
single shared Postgres) rather than raising the timeout (forbidden appeasement).
Low-core machines keep their smaller CPU-derived count via min(4, cpuCap).

Verified: full test:pg-gate now passes 23 files / 126 tests on a 28-core host.
2026-07-20 18:16:58 -07:00
gsxdsm
ecffdbb14b fix: isolate test-mode database access
Prevent automated tests from inheriting production PostgreSQL URLs and route global test-mode startups to a dedicated external or embedded test database.
2026-07-19 00:10:02 -07:00
gsxdsm
59815fd563 FN-8240: restore dashboard API test coverage
Restore quarantined dashboard API coverage and harden PostgreSQL template setup.

- Re-enable 18 dashboard API tests by clearing their quarantine ledger and Vitest exclusions.
- Preserve remote tunnel providers in route test mocks.
- Prevent PostgreSQL template cleanup races and terminate stale template sessions before copies.

Files changed:
 .../core/src/__test-utils__/pg-test-harness.ts     | 28 ++++---
 .../src/__tests__/routes-remote-access.test.ts     |  7 +-
 packages/dashboard/vitest.config.ts                | 25 ++----
 scripts/lib/test-quarantine.json                   | 90 ----------------------
 4 files changed, 30 insertions(+), 120 deletions(-)

Fusion-Task-Id: FN-8240

Fusion-Task-Lineage: 19269246-4eb7-418f-ab0d-bf90ba5dfb49

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-17 15:02:25 -07:00
gsxdsm
56773f691c FN-8244: fix PG template database isolation
Prevent same-process Vitest modules from racing over a shared PostgreSQL schema template.

- Assign each harness module instance a nonce-bearing template database.
- Clean up successful-worker templates and preserve live sibling templates during sweeps.
- Add isolated-module concurrency and template lifecycle regression coverage.

Files changed:
 .../core/src/__test-utils__/pg-test-harness.ts     | 100 +++++++++++++++-----
 ...pg-test-harness-template-concurrency.pg.test.ts | 101 +++++++++++++++++++++
 2 files changed, 177 insertions(+), 24 deletions(-)

Fusion-Task-Id: FN-8244

Fusion-Task-Lineage: 5a693232-6740-404f-a3da-763fecc5950f

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-17 14:50:59 -07:00
gsxdsm
c64c1014e2 test(FN-8146): speed up PG test harness with per-process schema template
Applying the full schema baseline on every fresh test database cost ~530ms
per file and dominated PG gate wall-time under fork contention. Apply the
schema once per worker process into a pid-keyed template database, then
create each test DB via CREATE DATABASE ... TEMPLATE (fast server-side copy).

Dead-pid templates from crashed/prior runs are swept before creating a new
one; template copies are serialized since CREATE DATABASE ... TEMPLATE forbids
concurrent access to the source. Removes the throwaway probe2 timing test.

Fusion-Task-Id: FN-8146

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 22:22:21 -07:00
gsxdsm
478f226a54 test: green full-suite CI after main drift (#2229)
## Summary
Restores green **Full Suite (non-blocking)** runs on `main`. Recent main
merges left i18n key parity, schema baseline bookkeeping (0011→0012),
heartbeat tool inventory (FN-8058 `fn_task_logs_read`), and merger
whitespace-classification mocks (execFile `git diff -p -w :2: :3:`) out
of date, so all four test shards failed.

## Root causes observed on main
- **Shard 4 / `@fusion/i18n`**: missing `skipConfirmationDialogs*` +
`reviewBudgetExhausted` in non-en locales; orphan
`awaitingApprovalPlanReviewReplanCap`
- **Shard 3 / `@fusion/core`**: `SCHEMA_BASELINE_VERSION` advanced to
`0012` while tests still equated it with
`OWNER_PROJECT_ID_SPLIT_VERSION` (`0011`) and omitted `0012` from
applied-migration lists
- **Shards 1–2 / `@fusion/engine`**: tool count/snapshot drift for
`fn_task_logs_read`; merger tests still mocked `git diff-tree` for
trivial classification after the execFile `:2:`/`:3:` cutover; mock
provider `updateTask` arity drift

## Changes
- Locale catalogs: add missing keys, drop orphan key
- Schema applier tests: immutable 0011 identity + baseline 0012 lists
- Heartbeat + gating snapshots: include `fn_task_logs_read`
- Merger unit mocks: recognize `git diff -p -w :2:path :3:path`
- Mock provider: accept optional third `updateTask` arg

## Test plan
- [x] `pnpm --filter @fusion/i18n exec vitest run` — 23/23
- [x] `pnpm --filter @fusion/core exec vitest run
src/__tests__/postgres/schema-applier.test.ts` (immutable + automation
upgrade) — pass
- [x] `pnpm --filter @fusion/core exec vitest run` project-identity +
satellite-fusiondir — pass
- [x] Engine suites from failed CI shards (file-scoped,
hermes/openclaw/paperclip/grok, reliability post-finalize/mission,
heartbeat, gating, merger recovery/prompt, mock-provider, etc.) — pass
- [ ] Full Suite workflow green on merge to main

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

- **New Features**
  - Improved project data isolation across backend operations.
- Added safer optional toast handling when UI components render outside
the full application shell.
  - Added support for reading task logs during agent heartbeat sessions.

- **Bug Fixes**
- Prevented runtime probes from hanging and avoided scanning large
binary files.
  - Improved path handling for workspaces with missing descendants.
- Corrected task retry state resets and GitHub import/issue-close
behavior.

- **Style**
  - Improved chat, terminal, and settings spacing.
  - Added clearer accessibility labeling for the auto-merge control.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-16 16:01:29 -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
d3ea8dff33 FN-6627: guard dist-barrel tests on complete core dist
Align dist-barrel regression guards so partial @fusion/core dist artifacts skip instead of failing mismatched dependency checks.

- Add a shared @fusion/test-utils predicate for complete built core dist barrels.
- Use the predicate in CLI and core dist-barrel regression tests before importing runtime dist modules.
- Cover absent and partial dist directories with focused predicate tests.

Files changed:
 packages/cli/src/__tests__/extension.test.ts       |  9 +++--
 .../src/__test-utils__/__tests__/core-dist.test.ts | 46 ++++++++++++++++++++++
 packages/core/src/__test-utils__/workspace.ts      | 11 ++++++
 .../core/src/__tests__/task-list-format.test.ts    | 17 ++++----
 4 files changed, 72 insertions(+), 11 deletions(-)

Fusion-Task-Id: FN-6627

Fusion-Task-Lineage: a05ad009-229d-44c2-a49d-2d9b3f3d6094
2026-06-18 01:45:18 -07:00
gsxdsm
d0be3e462d FN-6610: harden engine test isolation recovery
Harden shared test isolation seams so engine tests survive mid-run cleanup.

- Recreate owned worker roots, HOME directories, and cwd before child-process launches.
- Add regression coverage for tmpdir redirect, HOME, cwd, SQLite, and git config recovery.
- Revalidate worktree database scratch directories before direct SQLite opens and document the rescue pattern.

Files changed:
 docs/testing.md                                    |  2 +
 packages/core/src/__test-utils__/vitest-setup.ts   | 92 ++++++++++++++++++----
 .../__tests__/vitest-setup-tmp-redirect.test.ts    | 33 ++++++++
 .../src/__tests__/executor-step-session.test.ts    |  6 +-
 .../src/__tests__/worktree-db-hydrate.test.ts      | 22 +++++-
 5 files changed, 135 insertions(+), 20 deletions(-)

Fusion-Task-Id: FN-6610

Fusion-Task-Lineage: 18233ee2-1dfe-4b0d-bd12-e4f5b7f9cc29
2026-06-18 01:20:03 -07:00
gsxdsm
c4878514f9 FN-6600: harden core worker-root teardown retries
Harden the core Vitest worker-root cleanup path and rescue the related broad-suite quarantines.

- Increase bounded retries for transient ENOTEMPTY/EBUSY worker-root cleanup races.
- Add teardown coverage proving transient ENOTEMPTY retries remove the worker root.
- Remove rescued core quarantine entries from the ledger and Vitest exclude list.
- Document the FN-6600 core cleanup rescue pattern in testing guidance.

Files changed:
 docs/testing.md                                    |  2 ++
 .../core/src/__test-utils__/vitest-teardown.ts     |  7 +++++-
 .../vitest-teardown-worker-root-cleanup.test.ts    | 27 ++++++++++++++++++++++
 packages/core/vitest.config.ts                     |  6 +++--
 scripts/lib/test-quarantine.json                   | 15 ------------
 5 files changed, 39 insertions(+), 18 deletions(-)

Fusion-Task-Id: FN-6600

Fusion-Task-Lineage: 107fb5cf-82c8-4e65-915d-0ef69725369b
2026-06-17 19:19:05 -07:00
gsxdsm
2fc6d4d667 FN-6430: rescue CLI quarantine tests
Rescue the quarantined CLI suites by fixing shared test isolation instead of extending timeouts.

- Remove rescued CLI files from the quarantine ledger and Vitest exclude list while preserving an empty rescue ledger comment.
- Tighten Vitest HOME isolation to reject inherited worker homes and sweep legacy top-level fn-test-home roots with bounded cleanup.
- Reset affected CLI fixtures, close research stores, and narrow the slow mission store seam so rescued tests run on default timeouts.
- Document the CLI shared-fixture rescue pattern for future quarantine recoveries.

Files changed:
 docs/testing.md                                    |   2 +
 .../cli/src/__tests__/extension-task-tools.test.ts |   7 +-
 packages/cli/src/__tests__/extension.test.ts       | 117 +++++++++----------
 .../cli/src/commands/__tests__/mission.test.ts     |  16 ++-
 packages/cli/src/commands/__tests__/plugin.test.ts |   5 +
 packages/cli/vitest.config.ts                      |  52 ++-------
 packages/core/src/__test-utils__/vitest-setup.ts   |  25 ++++-
 .../core/src/__test-utils__/vitest-teardown.ts     |  28 ++++-
 .../vitest-teardown-worker-root-cleanup.test.ts    |  15 +++
 scripts/lib/test-quarantine.json                   | 124 +--------------------
 10 files changed, 157 insertions(+), 234 deletions(-)

Fusion-Task-Id: FN-6430

Fusion-Task-Lineage: 943b73b4-5f92-4703-8e93-0ae3207eb63c
2026-06-14 15:29:35 -07:00
gsxdsm
066c919ace test: quarantine slow and flaky test lanes
Move observed load-sensitive and slow tests out of the default lanes per the deletion-ratchet policy, keep the quarantine ledger in sync, and preserve corrupt databases when recovery fails during verification.
2026-06-13 19:22:00 -07:00
gsxdsm
80fbcdd5a3 FN-6396: harden test worker temp cleanup
Prevent stale Fusion test worker roots from leaking across merge-gate runs.

- Add per-run tokens to worker-root owner markers and pruning checks.
- Remove self-minted fallback worker roots during Vitest exit cleanup.
- Cover stale pid reuse, markerless redir roots, and SIGKILL-style orphan pruning with regression tests.

Files changed:
 packages/core/src/__test-utils__/vitest-setup.ts   | 83 +++++++++++++++++++---
 .../core/src/__test-utils__/vitest-teardown.ts     |  8 ++-
 .../vitest-teardown-worker-root-cleanup.test.ts    | 19 ++++-
 scripts/__tests__/test-changed.test.mjs            | 74 ++++++++++++++++++-
 scripts/test-changed.mjs                           | 76 ++++++++++++++++----
 5 files changed, 233 insertions(+), 27 deletions(-)

Fusion-Task-Id: FN-6396

Fusion-Task-Lineage: 711d966d-c70e-4cd7-81cd-accd18f17202
2026-06-13 16:30:28 -07:00
gsxdsm
97a49ac196 FN-6382: unquarantine stabilized flaky tests
Restore quarantined tests by fixing their flaky harness seams instead of extending the deletion ratchet.

- Mark active Vitest worker roots and skip live worker roots during prune cleanup.
- Make bubblewrap backend coverage deterministic with an injectable runner and restore it to the engine gate.
- Remove rescued core and bubblewrap tests from the quarantine ledger and Vitest excludes.

Files changed:
 .../core/src/__test-utils__/vitest-teardown.ts     | 10 +++++-
 packages/core/vitest.config.ts                     |  8 +----
 .../__tests__/sandbox/bubblewrap-backend.test.ts   | 29 +++++++++------
 packages/engine/src/sandbox/bubblewrap-backend.ts  |  9 +++--
 packages/engine/vitest.config.ts                   |  1 -
 scripts/__tests__/test-changed.test.mjs            | 23 ++++++++++++
 scripts/lib/test-quarantine.json                   | 34 ++----------------
 scripts/test-changed.mjs                           | 41 ++++++++++++++++++++++
 8 files changed, 102 insertions(+), 53 deletions(-)

Fusion-Task-Id: FN-6382

Fusion-Task-Lineage: 018dc7ac-1ef1-495e-a5fd-96ea44fcd43b
2026-06-13 11:39:37 -07:00
gsxdsm
10972bbdce FN-6360: clean up leaked test worker temp roots
Ensure test isolation removes stale worker temp roots after interrupted or busy Vitest runs.

- Add bounded retry cleanup for Vitest worker roots during teardown.
- Prune orphaned fusion-test-workers-* directories before changed-test isolation checks.
- Cover worker-root retry and pruning behavior with targeted tests.

Files changed:
 .../core/src/__test-utils__/vitest-teardown.ts     | 50 ++++++++++--
 .../vitest-teardown-worker-root-cleanup.test.ts    | 88 ++++++++++++++++++++++
 scripts/__tests__/test-changed.test.mjs            | 33 ++++++++
 scripts/test-changed.mjs                           | 32 ++++++++
 4 files changed, 196 insertions(+), 7 deletions(-)

Fusion-Task-Id: FN-6360

Fusion-Task-Lineage: d537941d-dd58-403a-a82e-f0aeee9c1eb0
2026-06-13 08:11:46 -07:00
gsxdsm
21cb8abcf3 FN-6310: recreate temp redirect sink after cleanup
Recreate Vitest tmpdir redirect sinks on demand after recovery cleanup removes them.

- Ensure cached tmpdir redirect sinks are recreated before later mkdtemp calls.
- Preserve live/current redirect sinks when sweeping stale pid-owned sinks.
- Add regression coverage for sync/async mkdtemp recovery and stale sink sweeping.

Files changed:
 packages/core/src/__test-utils__/vitest-setup.ts   | 21 ++++++-
 .../__tests__/vitest-setup-tmp-redirect.test.ts    | 66 +++++++++++++++++++++-
 2 files changed, 85 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-6310

Fusion-Task-Lineage: 311cb7f6-9c7b-4e4d-9235-e787382dcc3a
2026-06-12 17:21:14 -07:00
gsxdsm
89654b2a90 FN-6282: isolate vitest worker temp roots
Use per-invocation Vitest worker roots to keep compound-engineering tests from timing out on stale temp fixtures.

- Allocate a fresh FUSION_TEST_WORKER_ROOT during Vitest global setup and remove it during teardown.
- Preserve per-worker fallback root creation when global setup is not available.
- Clean compound-engineering harness project roots on close and cover the setup invariants with regression tests.

Files changed:
 packages/core/src/__test-utils__/vitest-setup.ts   | 20 +++++++---
 .../core/src/__test-utils__/vitest-teardown.ts     | 38 +++++++++++--------
 .../src/__tests__/_harness.ts                      |  7 +++-
 .../src/__tests__/setup-invariant.test.ts          | 43 ++++++++++++++++++++++
 4 files changed, 85 insertions(+), 23 deletions(-)

Fusion-Task-Id: FN-6282

Fusion-Task-Lineage: 8b842dd0-4f51-44de-b2db-8e8bfa97239c
2026-06-12 04:19:42 -07:00
gsxdsm
bdf813654e fix: resolve botched stash-pop conflict from 245e1280e
- scripts/lib/test-quarantine.json: remove stray <<<<<<< / ======= / >>>>>>> stash markers that broke JSON parsing
- packages/core/src/__test-utils__/vitest-setup.ts: restore \r?\n regex and \n template escapes; drop stray extra brace
2026-06-11 12:24:16 -07:00
gsxdsm
245e1280ed Fix agents 2026-06-11 12:22:02 -07:00
gsxdsm
2add48c8b6 FN-6255: redirect tmpdir mkdtemp calls in tests
Keep test-created temp directories under the Fusion worker root.

- Redirect fs.mkdtemp and fs.promises.mkdtemp prefixes rooted at the OS temp dir into per-process worker sinks.
- Sweep stale redirect sinks and clean current-process sinks on exit.
- Add regression coverage for sync, async, realpath, nested, and Buffer prefix behavior.
- Remove the restored merger file-scope invariant test from quarantine.

Files changed:
 packages/core/src/__test-utils__/vitest-setup.ts   | 119 +++++++++++++++++++--
 .../__tests__/vitest-setup-tmp-redirect.test.ts    |  68 ++++++++++++
 scripts/lib/test-quarantine.json                   |   5 -
 3 files changed, 181 insertions(+), 11 deletions(-)

Fusion-Task-Id: FN-6255

Fusion-Task-Lineage: 4cc855c6-37dd-4dfb-a545-1fd1885779c4
2026-06-11 12:18:09 -07:00
gsxdsm
5d2add417e Merge main into fast-tests: take @fusion/i18n dashboard dep; keep promotion-gate test deletion over main's .slow rename 2026-06-03 23:54:09 -07:00
gsxdsm
cd0e3e26d6 test: prevent Linux-CI git hangs (no prompt/editor/pager)
Extends the test-setup git hardening. The engine --shard=2/2 worktree-heavy
reliability suite was hanging in CI (progressing healthily, then ~2 min of
silence before the job timeout killed it with no test failure) — the signature
of a git command blocking on an interactive prompt. A dev macOS git config
suppresses these; a bare Linux CI git does not. Disable terminal credential
prompts (GIT_TERMINAL_PROMPT=0), the editor (GIT_EDITOR=true), and the pager
(GIT_PAGER=cat) for every test process so no git invocation can block on a TTY.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 19:20:55 -07:00
gsxdsm
3432e0615c test: pin git init to 'main' in test setup (fix Linux-CI branch-group failures)
Git defaults the initial branch to 'master' unless init.defaultBranch is set —
true on Linux CI runners but typically overridden to 'main' on developer macOS
machines. That host gap made git-worktree tests assuming 'main' (the
shared-branch-group reliability suite in engine shard 2/2) pass locally but
fail only in CI with 'fatal: path ... does not exist in main'.

Set init.defaultBranch=main for every test process via GIT_CONFIG_* env vars in
the shared core test setup (inherited by all child git invocations, without
mutating the developer's global config). Appends rather than clobbers any
pre-existing GIT_CONFIG_COUNT.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 19:07:20 -07:00
gsxdsm
211c0fd557 perf(test): cut inner-loop fixed overhead to sub-second on cache-fresh runs
- skill-sync check conditioned on content hash of its inputs (skips ~0.3s spawn)
- ensure-test-artifacts: git-blob content-hash staleness; branch switches no longer trigger spurious ~2.6s tsc rebuilds (mtime fallback when dirty)
- isolation guard: cheap --before-fast reusing prior post-run baseline (~2.1s -> ~0.07s); detection proven preserved via injected-leak failure test
- vitest-setup: CI skips 4040-4045 discovery probe unless FUSION_RESERVED_PORTS set; kill-guard wrapper untouched, asymmetry pinned by port-probe-policy tests
- cache-fresh fast path skips sync/artifacts/HOME-prune entirely (mode line: fast-path=cache-fresh)
2026-06-03 17:57:29 -07:00
gsxdsm
a6a57dc40a test: guard tests from killing the live dashboard port
Adds a static pretest check and a runtime vitest-setup wrapper that block
shell/process calls matching `kill|pkill|killall|fuser|lsof ... <port>` or
`.listen(<port>)` against reserved Fusion ports. Reserved set is dynamic:
default 4040 plus $PORT, $FUSION_SERVER_PORT, $FUSION_RESERVED_PORTS, and any
port responding to /api/health on 4040..4045 at worker startup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 19:51:36 -07:00
gsxdsm
23a0c27bd5 fix(engine-tests): subprocess timeout attribution + ENOTEMPTY retry on cleanup
Two test-infrastructure fixes from agent #1's flake-stabilization pass.
Skipped its DESC-order assertion rewrite (semantically equivalent no-op
that conflicted with the prior boilerplate consolidation in 848a226ca)
and its vitest.config additions (already in flight on main).

1. vitest-setup.ts: completedSubprocessFailures was a plain string[]. When
   a 30s subprocess-guard timer fired during a *later* test's execution
   window (because the owning test ran for e.g. 40s under its 60s
   timeout budget), the failure surfaced in the innocent successor test's
   afterEach. Typed the array as { ownerTestName, message }[] and filter
   on the current test name; orphaned entries are dropped silently.

2. worktree-contamination-attribution.real-git.test.ts: afterEach rm
   occasionally hits ENOTEMPTY on macOS when a git rebase internal dir
   isn't fully flushed. Added maxRetries: 3, retryDelay: 100.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 22:10:45 -07:00
gsxdsm
ec6643e4d9 fix(test-utils): cancel subprocess tracking timer for every proc in afterEach
The vitest subprocess guard's 60 s tracking timer could outlive the test
that spawned it and fire during a later test's afterEach, producing
spurious "Timed out after 60000ms" failures attributed to a different
test name under concurrent recursive test load.

Scope "Left running" reporting + SIGKILL to the current test's procs but
always clear each tracked subprocess's timer so it cannot fire later.
Bump the post-test grace from 200 ms to 1 s to absorb event-loop
contention from slow git shells.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 18:38:51 -07:00
gsxdsm
1f0bb7e018 fix(core): skip real keychain in tests to stop 15s keytar hangs
MasterKeyManager.loadKeytar() now bails out early when
FUSION_MASTER_KEY_DISABLE_KEYCHAIN=1, and the core vitest setup sets that
flag for every worker. Eight tests across master-key/secrets-store/
secrets-sync-passphrase were timing out at exactly the 15s testTimeout
because they constructed MasterKeyManager without injecting a fake
KeytarLike, which made loadKeytar() reach the real OS keychain.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 22:45:29 -07:00
Aron Prins
0b4a7bbe4e test(FN-000): address follow-up review feedback 2026-05-07 18:08:08 +02:00
Aron Prins
eb1b3b6f5b test(FN-000): slim dashboard default tests 2026-05-07 17:27:16 +02:00
Aron Prins
f85b124125 test(FN-000): resolve dashboard test noise 2026-05-07 17:27:15 +02:00
Fusion
f6394cc22b test(FN-3607): harden test workflow verification and isolation checks
- Expand test-changed coverage and shard selection assertions for CI workflows
- Improve vitest worker temp-directory utilities and related core/CLI tests
- Refine test isolation guardrails and runtime ignore handling for live .fusion noise
- Update contributing guidance and root test script usage for the verified workflow

Fusion-Task-Id: FN-3607
2026-05-06 11:51:48 -07:00
gsxdsm
9f2f1fd450 chore: silence node:sqlite ExperimentalWarning in tests, fix QR mock type
Patch process.emitWarning in the shared vitest setup to drop the
"SQLite is an experimental feature" notice; align the test mock for
getQrPayload with the canonical RemoteQrPayload signature.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 14:33:26 -07:00
Fusion
0bb923b044 feat(FN-3263): strengthen worker-budget regression test coverage
Strengthens regression test coverage for worker-budget behavior in the vitest workers test suite, adding 14 lines to cover edge cases.

Fusion-Task-Id: FN-3263
2026-05-03 09:51:43 -07:00
gsxdsm
1aa1e723f6 chore(build,test): split desktop/mobile from default build; share vitest worker budget
- pnpm build now excludes @fusion/desktop and @fusion/mobile by default
  (recursive build still available as pnpm build:all). Saves time on
  workspace-wide builds that don't need the native shells.
- Hoist the per-package max-worker computation into a shared
  packages/core/src/__test-utils__/vitest-workers.ts util. Every
  vitest.config.ts now calls computeMaxWorkers(), which honors
  VITEST_MAX_WORKERS, FUSION_TEST_TOTAL_WORKERS, and a per-config
  defaultCap, clamped to cpus-1.
- pnpm test sets VITEST_MAX_WORKERS=2 so the workspace run keeps total
  fan-out modest with --workspace-concurrency=2.
- Switch dashboard vitest pool from forks to threads so jsdom/React
  suites share a V8 heap instead of duplicating ~500MB per worker.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 15:56:15 -07:00
gsxdsm
62f54dab46 fix(test-harness): restore promisify(exec) + unblock CLI introspection probes
The vitest child-process guard wrapped exec/execFile without preserving the
`[util.promisify.custom]` symbol, so awaited `execAsync` resolved to a raw
stdout string instead of `{stdout, stderr}`. That single regression cascaded
through ~60 "failing" tests across cli, core, engine, and dashboard whose
production code was actually correct. Also relax the AI-CLI blocklist for
cheap introspection (--version/--help/which …), give SIGTERM'd subprocesses a
brief grace period before being flagged as "left running", fix a few real
test-side bugs uncovered along the way (executor mock step transitions, iOS
last-resort keyboard path, mission SSE replay tests racing with the real AI
agent), and convert dashboard route tests' dynamic `await import("../server.js")`
to static imports so first-test timings drop from 2–5s to <200ms.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 17:40:14 -07:00
gsxdsm
3db00440d9 fix(FN-000): harden vitest child process isolation 2026-04-30 13:44:59 -07:00
gsxdsm
822bde7829 test(FN-2360): harden test isolation around repo fusion state 2026-04-29 12:02:56 -07:00
Fusion
f528b379c3 feat(FN-2089): merge fusion/fn-2089 2026-04-19 01:03:36 -07:00
gsxdsm
a2d0711c33 fix(teardown): improve cleanup logic to avoid shared directory deletion 2026-04-19 01:03:36 -07:00
gsxdsm
1cbf9e1015 test: enforce test-directory isolation across all packages
Introduce a shared test-utils module and global vitest setup that
guarantee tests never write to the real .fusion directory or leak temp
directories under /tmp.

Infrastructure:
- packages/core/src/__test-utils__/workspace.ts — tempWorkspace(),
  useIsolatedCwd(), trackForCleanup(), assertOutsideRealFusion() with
  auto-cleanup in afterEach.
- packages/core/src/__test-utils__/vitest-setup.ts — per-worker guard:
  chdirs each worker into an isolated tmp dir, wraps process.chdir to
  refuse the real .fusion, scopes tmp dirs under fusion-test-workers/
  (skips cwd change in thread-pool workers where chdir isn't supported).
- packages/core/src/__test-utils__/vitest-teardown.ts — globalSetup
  hook that wipes the shared parent even when workers are SIGKILLed.
- scripts/check-test-isolation.mjs + `test:isolated` / `test:check-
  isolation` scripts for CI.
- @fusion/test-utils alias + setupFiles + globalSetup wired into core,
  cli, engine, dashboard, tui vitest configs; matching tsconfig paths.

Test refactors (no behavior change):
- cli provider-settings, auth-paths, provider-auth — switch leaking
  mkdtempSync calls to tempWorkspace().
- core migration, first-run, store-backward-compat — replace manual
  process.chdir save/restore with useIsolatedCwd().
- tui fusion-context — replace 9 hardcoded tmp paths (collision-prone
  under parallelism) with tempWorkspace().
- dashboard useTheme, FileBrowser, TaskCard — resolve source-file reads
  against a PACKAGE_ROOT computed from import.meta.url instead of cwd,
  so tests don't depend on the process working directory.

Verified: full suite (~15,500 tests across 8 packages + plugins) passes
and the orphan-detector reports zero leaked temp directories after a
complete run.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 23:41:47 -07:00