Operator on a fresh install was never prompted to pick a default model, and the
dashboard later advertised "Continue Setup" at the AI Setup step even though
they had finished the flow.
availableModels was loaded once at mount and re-fetched only when a CUSTOM
provider was added. On a fresh install nothing is connected at mount, so the
list starts empty and the Default Model section renders its empty state — and
after an OAuth login or an API-key save it was never refreshed, so it kept
saying "No models available yet. Connect a provider above to see model options."
while a provider sat connected right above it.
Both connect paths now refresh the catalogue. Once a provider is connected and
no model is chosen, the section retitles from "Default Model (Optional)" to
"Choose your default model" and scrolls itself into view once — the moment it
becomes actionable is exactly the moment it is off screen, below every provider
card. It stays skippable.
scrollIntoView is called only when it exists: JSDOM does not implement it, and a
cosmetic nudge must never throw out of an effect and take the modal down (it did
— 24 suites failed until guarded).
Completion is additionally marked in a `finally`, so a failed default-model
settings write cannot leave onboarding recorded as unfinished. That part is
hardening, NOT a proven repro: a test written against it passed with and without
the change, so it was removed rather than kept as false coverage, and the reason
is recorded in the suite.
Also folds in the container's default git identity, which the merge investigation
needed (see the follow-up commit for the per-agent identity work).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Operator asked for cloudflared, tailscale, rg, git and gh available by default
in the container. git/ca-certificates/ripgrep already landed; this adds the
remaining three.
Each comes from its vendor's own signed apt repository rather than a
curl-to-shell installer, so signature checking and upgrades follow the normal
apt path:
gh https://cli.github.com/packages
tailscale https://pkgs.tailscale.com/stable/debian
cloudflared https://pkg.cloudflare.com/cloudflared
Why each belongs in the image: gh backs Fusion's gh-cli GitHub auth mode (the
auth route instructs operators to run `gh auth login`, impossible without the
binary), cloudflared backs the dashboard's remote-access feature whose in-app
installer cannot bootstrap itself reliably in a slim container, and tailscale is
the private-network option for the same box.
Installing tailscale does NOT make tailscaled runnable by itself: the daemon
also needs --cap-add NET_ADMIN --device /dev/net/tun at docker run. Shipping the
binary is the image's part; granting kernel capabilities stays an explicit
operator decision.
Commands were validated live in a running container before being written here;
the guard test asserts both the repo wiring and the package names so half a
change cannot silently ship.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The coding agents Fusion drives reach for `rg` as their primary search tool. It
was absent from the image, so inside a container they silently fall back to
slower or partial search while working fine on a developer machine that has it
installed. Operator asked for it by default.
Installed alongside git and ca-certificates in the runner stage, and covered by
the same runner-stage guard so it cannot quietly drop out again.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Operator hit "Git clone failed: ... server certificate verification failed.
CAfile: none CRLfile: none" the moment they tried to add a project in the
container.
The runner stage installed git but not ca-certificates, and the slim base ships
zero CA certificates (/etc/ssl/certs was empty). git verifies TLS against the
SYSTEM trust store, so every HTTPS remote failed and project setup — the first
thing anyone does after logging in — was impossible in Docker.
It hid because Node carries its OWN bundled CA store: the dashboard, model API
calls, and the OAuth token exchanges against platform.claude.com and OpenAI all
worked fine, so the image looked healthy right up until the first clone. Nothing
else in the image exercises the system trust store, so a guard is added rather
than trusting someone to notice next time.
Verified in the running container: installing ca-certificates took it from 0 to
301 certs and `git clone https://github.com/Runfusion/Fusion.git` then succeeded
as the node user.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three defects found while bringing up a container from a clean checkout:
- The dashboard's vite build (~5.7k modules) exceeded V8's default old-space on a
stock Docker Desktop VM and aborted the image build with "Ineffective
mark-compacts near heap limit" (exit 134). Raise the ceiling for that RUN only.
- The documented `-v fusion-home:/home/node/.fusion` mount seeded a root-owned
named volume over a path absent from the image, so embedded Postgres initdb hit
"Permission denied", the supervisor burned its 4 restarts, and the container went
unhealthy on first run. Pre-create the directory node-owned so a fresh named
volume inherits it; document that bind mounts still need a host-side chown.
- Drop the dependency-graph plugin's tsconfig path mapping for the taskStuck module
deleted in 2eae0b2507 / 29d94e0fa3.
Verified: full `docker build` from a clean export of this tree succeeds unpatched,
and a run against brand-new named volumes with no manual chown reaches health=healthy
with /api/health 200.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The runner stage installed the application at /project, which was also the
documented bind-mount point — mounting a host project there shadowed the CLI
and the container exited with MODULE_NOT_FOUND on packages/cli/dist/bin.js.
- Install the app under /app and run the entrypoint by absolute path.
- Reserve /workspace (empty in the image, container workdir) as the project
mount point.
- Update docs/docker.md: mount at /workspace, and document that embedded
Postgres/global state lives in /home/node/.fusion with a named-volume
example so persistence actually captures it.
Verified: image builds; `docker run -v host:/workspace` boots, embedded
Postgres initializes, /api/health returns ok.
Fixes#2414
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Dockerfile's pre-install COPY block was missing a line for
plugins/fusion-plugin-linear-import, which was added as a workspace
member in dfb6e5270d but never got a
matching COPY line. Without it, that plugin's package.json (and its
@types/node / @types/react devDependencies) is never staged before
'pnpm install --frozen-lockfile', causing the later 'pnpm build' step
to fail with TS2688.
Fixes#1939
The Dockerfile pre-copies each workspace package's package.json before
'RUN pnpm install --frozen-lockfile' (a layer-caching optimization), but
the list omits plugins/fusion-plugin-linear-import, which pnpm-workspace.yaml
lists as a workspace member. Because its package.json is absent during the
frozen install layer, pnpm never links that package's devDependencies
(@types/node, @types/react), so the later 'pnpm build' fails compiling it
with TS2688 'Cannot find type definition file for node/react'.
Add the missing COPY line so the plugin's manifest is present during the
install layer, matching every other workspace plugin.
- Add a root .dockerignore to reduce build context and exclude local runtime state
- Add a multi-stage Dockerfile that builds the workspace, runs as non-root, and includes health checks
- Document Docker build/run usage, env vars, persistence, and quick-start commands in README and docs/docker.md
- Add Docker configuration tests in packages/cli/src/docker.test.ts to validate required image and docs expectations