The one-time SQLite→PostgreSQL migration runs inside createTaskStoreForBackend
before any HTTP server listens, so browsers saw "connection refused" and open
tabs failed silently for minutes. Now:
- CLI: a temporary holding server binds the dashboard port for the boot window,
serving an auto-reloading "Database migration in progress" page and an
/api/health payload with status "migrating" + structured progress; the port
is handed off (awaited) to the real app.listen().
- Dashboard SPA: already-open tabs render the new MigrationInProgressBanner
from the 15s health poll when status is "migrating".
- Desktop: LocalRuntimeManager publishes migration progress on
DesktopRuntimeStatus via the new core onMigrationProgress option;
DesktopLaunchGate shows the live label and extends its 30s startup timeout
while progress advances (2min stall cap), in both boot and first-run flows.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Legacy SQLite databases can hold U+0000 in TEXT cells and inside stored
JSON, which PostgreSQL rejects in text and jsonb columns and which
aborted the first-boot auto-migration. Strip NUL from plain text cells,
JSON string values and object keys, malformed-JSON scalars, and opaque
legacy-preservation cells; content-checksum verification compares the
sanitized source against the sanitized target so migrations still verify.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
shared_memory_type=mmap (defaulted 2026-07-16 for SysV shm exhaustion)
is invalid on Windows — PostgreSQL only accepts "windows" there and
dies with FATAL invalid value for parameter before opening the port.
Every Windows embedded start broke, failing the Windows release smoke
in both the v0.70.0 and v0.70.1 tag runs. Default flags now come from
defaultEmbeddedPostgresFlagsFor(platform): empty on win32 (no override
needed; SysV exhaustion cannot occur there), mmap elsewhere. Regression
test asserts the per-platform flag invariant.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The bun-compiled exe has been unbootable since the PG cutover: bun
standalone binaries do no node_modules resolution, so the deliberately
out-of-graph require("embedded-postgres") failed from /$bunfs, and
readFile'd migration .sql files were never embedded, so even external
DATABASE_URL mode died at schema init.
- schema-applier: resolveMigrationsDir() — FUSION_MIGRATIONS_DIR env >
module-relative dist/migrations (npm/desktop, unchanged) >
execPath-relative migrations/ (standalone exe), probe-based.
- embedded-lifecycle: require("embedded-postgres") first (npm/desktop
untouched), falling back to a self-contained staged bundle at
<execDir>/runtime/<platform>/embedded-postgres/dist/index.cjs
(FUSION_EMBEDDED_PG_RUNTIME_DIR override) with the native
initdb/pg_ctl/postgres payload beside it.
- build.ts: stage dist/migrations plus the per-target embedded-postgres
bundle + native payload (warn when a cross-target payload is absent on
the host, mirroring desktop's verifyEmbeddedPostgresPayloads).
- release.yml: package fn-cli-<os>-<arch>.tar.gz (binary + migrations +
runtime + client) with sha256 per leg; prune staged payload files from
the release-collection globs; bare fn-cli-* binaries still uploaded.
E2E-verified on the compiled binary: embedded mode initdb→/api/health
200 database healthy; DATABASE_URL mode applied migrations 0000–0019
(109 tables). Core typecheck clean; schema-applier 58/58 and
embedded-lifecycle 44/44 tests pass.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
engine's child-process-runtime forks dist/child-process-worker.js as a
sibling of the bundled bin.js, but the CLI build never emitted it, so
isolationMode: "child-process" could not spawn its runtime worker from
any published install. Add a named tsup entry bundling the engine worker
with bin.js's exact externals; verified via fork() with the runtime's
spawn options (IPC handlers register, no module-resolution errors) and
npm pack --dry-run listing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tarball-completeness audit found two more assets stripped from the
published package: dist/pi-llama-cpp (staged by tsup, matched no files
glob — useLlamaCpp silently reported not-installed) and the dashboard's
registry-manifest.json (resolved beside the bundled bin.js by
plugin-routes but never staged into cli dist — published installs served
an empty plugin registry). Stage the manifest in tsup onSuccess and add
both to files; npm pack --dry-run now lists them.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The files globs (dist/**/*.js, *.d.ts, maps, named dirs) matched no .sql
file, so npm pack stripped dist/migrations from every published tarball.
npm-installed CLIs crashed schema init (ENOENT dist/migrations/
0000_initial.sql) and the dashboard supervisor crash-looped on boot.
Add dist/migrations/** to files; npm pack --dry-run now lists all 21
migration files. Sibling of the desktop staging fix 6e5bb5d3d.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bare tsc never copies .sql files into core's dist, and the desktop staging
had no equivalent of the CLI's tsup migrations hook, so every packaged
desktop build shipped without dist/postgres/migrations and Local mode
crashed schema init (ENOENT 0000_initial.sql) after embedded Postgres
started. buildCore() now stages the migrations beside the compiled output,
stageDesktopDeploy() re-stages them into the deployed closure, and
verifyCoreMigrationsStaged() fails the build if the baseline migration is
missing from the stage.
Verified end to end: packed mac-arm64 app boots with isolated home,
embedded PG 15.18 initializes from packaged migrations, /api/health 200.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
## Summary
In a centrally-installed, multi-project Fusion server (one process
serving several repos, `process.cwd()` = the install dir, not any repo),
every task under `mergeStrategy: "pull-request"` fails at the auto-merge
stage with:
```
Could not determine repository. Specify owner/repo in params or run from a git repository with a GitHub remote.
```
PR creation from the dashboard and status polling work; only the
engine's automatic PR path fails. This is the **non-workspace sibling of
#1924** (FN-7610 routed workspace-mode tasks to direct merge but does
not cover regular multi-project tasks) and the completion of
#1797/FN-7133 (which fixed only the `getPrMergeStatus` arguments).
## Root cause
`GitHubClient.resolveRepo()` (`packages/dashboard/src/github.ts`) falls
back to a cwd-less `getCurrentRepo()` — i.e. `git remote get-url origin`
in `process.cwd()` — whenever a PR method is called without explicit
`owner`/`repo`. The engine merge path already resolves the correct repo
from the per-project cwd (`prRepo = getCurrentRepo(cwd)`, FN-7133) but
only threaded it into `getPrMergeStatus`. Every other GitHub call
omitted it:
- `processPullRequestMergeTask`: `findPrForBranch` / `createPr` /
`mergePr` on both the per-task and shared-branch-group paths
- `createGroupPrCallback` (group-PR promotion): `findPrForBranch` /
`createPr`
- `createPrNodeGithubOps` (`pr-create`/`pr-merge` workflow nodes):
cwd-less `getCurrentRepo()` persisted `entity.repo` as `""` (poisoning
the downstream `splitRepoSlug` consumers), and the git
push/`createPr`/`mergePr` ran against `process.cwd()`
- the engine's review-response run (`buildRespondCallback`):
`respondOps.getCwd` collapses to `process.cwd()` because no CLI
composition site wires `getTaskWorktree`, so its git ops and response
agent ran outside the project repo
In a central install the fallback throws; worse, if `process.cwd()`
happens to be inside some *other* git repo, it silently targets the
**wrong repository**.
## What changed
- `fix(pr-merge): thread repo identity into PR auto-merge GitHub calls`
— widens the CLI-local `GitHubOperations` interface (optional
`owner`/`repo`, already accepted by `GitHubClient`'s
`FindPrParams`/`CreatePrParams`/`MergePrParams`) and passes `prRepo` at
all six call sites in `processPullRequestMergeTask`.
- `fix(pr-merge): resolve group-PR repo from project cwd in
createGroupPrCallback` — resolves via `getCurrentRepo(cwd)` from the
callback input (same T4 pattern as `syncGroupPrCallback`) with a loud
failure instead of a silent wrong-repo fallback.
- `fix(pr-merge): resolve PR-node repo from task worktree instead of
process cwd` — `resolvePrSource` resolves from `task.worktree`, git ops
run in `getTaskWorktree(...) ?? task.worktree ?? process.cwd()`, and
`createPr`/`mergePr` pass `owner`/`repo` parsed from `entity.repo`.
- `fix(pr-merge): resolve review-response run cwd from the task
worktree` — the engine owns the store, so `buildRespondCallback` prefers
the task's recorded `worktree` for the response run's git ops + agent,
keeping `ops.getCwd` as the single-project fallback (defensive against
structural `PrNodeStore`s without `getTask`).
- Changeset (`@runfusion/fusion` patch, structured body) included.
Deliberately **not** done: a constructor-scoped default repo on
`GitHubClient` — one client instance is shared across all projects in a
central install (`serve.ts`/`daemon.ts`/`dashboard.ts`), so per-call
`owner`/`repo` is the only correct scope.
## Testing
- New regression tests simulate the central-install topology
(`getCurrentRepo` mocked as `(cwd?) => cwd ? repo : null`, exactly the
failing environment) and drive the merge flow end-to-end on the per-task
path, the shared-branch-group path, `createGroupPrCallback`, and all
three `createPrNodeGithubOps` ops, asserting every GitHub call carries
explicit `owner`/`repo` (45 tests in
`packages/cli/src/commands/__tests__/task-lifecycle.test.ts`, all
green).
- `packages/engine/src/__tests__/pr-respond-cwd-resolution.test.ts`
covers the respond-run cwd: worktree preferred, `ops.getCwd` fallback
when the task has no worktree, when the lookup fails, and when a
structural store has no `getTask`.
- Existing exact-argument assertions were extended to the new call
contract (no assertions weakened or removed).
- `pnpm lint`, `pnpm typecheck`, and `pnpm build` green locally; `pnpm
test:gate`'s engine-core suite green (294/294) — its PostgreSQL-backend
lane needs local PG credentials this environment lacks, so that lane
defers to CI. `pnpm verify:fast` (scoped typecheck/build + CLI build +
boot smoke) also passes.
## Repro
1. Install the CLI centrally; run the server from a dir that is not a
git repo, serving ≥1 project with a GitHub `origin` and `mergeStrategy:
"pull-request"`.
2. Run a task to completion and let it reach the merge stage.
3. Before this fix: the auto-merger throws `Could not determine
repository …` (tasks with a persisted PR poll fine but never merge).
Merging the same task from the Pull Requests tab succeeds, because the
dashboard route resolves the repo explicitly (`parseBadgeUrl(...) ??
getCurrentRepo(rootDir)`).
Full analysis: https://github.com/Tchori-Labs/Fusion/issues/4
---
Developed with Claude (co-authored on all commits).
https://claude.ai/code/session_01ChEa8SHFYNAzjCdFbwFMfh
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Bug Fixes**
- Resolved pull request auto-merge failures in centrally installed,
multi-project deployments.
- Ensured explicit repository context (`owner/repo`) is used for pull
request lookup, creation, and merging throughout the merge workflow.
- Improved pull request response handling to prefer the task worktree
for working-directory resolution, with safe error behavior when task
details are unavailable.
- **Tests**
- Expanded coverage for multi-repository merge workflows and
worktree-based repository/cwd resolution in PR response handling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Claude <noreply@anthropic.com>
Next release should be 0.x, not 1.0.0 (which also already exists on npm
as a deprecated erroneous April publish). Keeps category: breaking so
release notes still flag the SQLite→PostgreSQL cutover.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pin the omitted Settings destination beneath the mobile More-menu divider.
- Render Settings after the More-menu separator when omitted from primary tabs.
- Prevent duplicate Settings entries when it is a primary tab.
- Cover ordering and duplicate-prevention behavior with MobileNavBar tests.
- Add a patch changeset for the mobile navigation fix.
Files changed:
.changeset/fn-8250-mobile-more-settings.md | 7 +++++++
packages/dashboard/app/components/MobileNavBar.tsx | 11 ++++++++++-
.../app/components/__tests__/MobileNavBar.test.tsx | 22 ++++++++++++++++++++++
3 files changed, 39 insertions(+), 1 deletion(-)
Fusion-Task-Id: FN-8250
Fusion-Task-Lineage: 4eea65d8-2e01-4fa4-986e-ffc15e906553
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Make triage duplicate-finalization tests provide the activity recorder used by the implementation.
- Add an awaited recordActivity mock to the shared TaskStore fixture.
- Select delete resolution in the reviewer-outage retry scenario so it reaches deleteTask.
Files changed:
packages/engine/src/__tests__/triage.test.ts | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
Fusion-Task-Id: FN-8242
Fusion-Task-Lineage: 8f9fd4b6-8071-47f3-8091-747884762ec9
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Mobile mailbox overlays now close through the shared navigation history before leaving the message list.
- Register mobile message, composer, and approval overlays as navigation modals.
- Remove matching history entries when mailbox overlays close or change state.
- Add mobile back-navigation coverage and document the behavior.
Files changed:
docs/dashboard-guide.md | 3 +-
packages/dashboard/app/components/MailboxView.tsx | 117 ++++++++++++----
.../app/components/__tests__/MailboxView.test.tsx | 148 +++++++++++++++++++++
3 files changed, 241 insertions(+), 27 deletions(-)
Fusion-Task-Id: FN-8231
Fusion-Task-Lineage: b6f06ed9-b03f-45d9-b90b-28b565231bcc
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>