Repository navigation
Shipwright 4.0 hardens how agents, the dashboard and CI are trusted. It also starts the
move from bash to TypeScript on Bun. Several defaults changed, so read the upgrade notes
below (or docs/UPGRADING.md) before you upgrade a server, fleet or CI setup.
Breaking changes / upgrade notes
- The dashboard binds
127.0.0.1by default (#8534). Before this release it listened on every interface.
Migrate: for server or fleet mode, runshipwright dashboard --host 0.0.0.0or setSW_DASHBOARD_HOST=0.0.0.0. See docs/dashboard-exposure.md before you expose it. shipwright connect joinno longer accepts the invite token on the command line (#8627).--token <t>and a positional token are refused.GET /api/join/<token>andGET /api/team/invite/<token>return 410.
Migrate: runshipwright connect join --url <url>and paste the token at the prompt, pipe it on stdin, or pass--token-file <path>. Upgrade workers and the dashboard together.- Bun is the root toolchain, and
package-lock.jsonis gone (#8670, #8535).bun.lockis the only root lockfile. Commands that moved to TypeScript (hello, plus the newgates,failure,breaker,budget,intake,pause,resumeandpaused) need Bun ≥ 1.2.19.
Migrate: install Bun (https://bun.sh) and usebun installin a source checkout instead ofnpm install/npm ci. - Agents never receive GitHub credentials (#8624).
GH_TOKEN,GITHUB_TOKENand similar variables are removed from every agent's environment.
Migrate: keepghcalls,git pushand private-repo fetches in the pipeline or your own scripts, not in agent prompts or custom agent instructions. - Agents run under a least-privilege permission policy (#8660, #8675). Every Claude call gets
config/agent-permissions.json: an allowlist of tools and commands, the Claude Code sandbox with no network access for shell commands, and no reads of~/.ssh,~/.config/gh,~/.awsand similar paths. The CLI refuses to start when the sandbox is unavailable.
Migrate: on Linux hosts and runners that run agents, installbubblewrapandsocat(CI workflows runscripts/ci/agent-sandbox-setup.sh). If your tasks need extra commands, adjustconfig/agent-permissions.jsonin your checkout. - Models are chosen by alias from
config/models.json(#8676, #8634). The aliases areopus,sonnet(the default) andhaiku, mapped to the Claude 5.5 family.ANTHROPIC_MODELandANTHROPIC_SMALL_FAST_MODELare no longer passed to agents. A pipeline refuses to start when--model,--fallback-modelorSW_MODEL_*is neither an alias nor a model ID listed inconfig/models.json.
Migrate: use--model opus|sonnet|haiku. To use another model ID, add it as an alias inconfig/models.json, and change model choices there rather than throughANTHROPIC_MODEL. .claude/runtime state is no longer tracked (#8667). Loop logs, loop state, caches and evidence files are now git-ignored. The daemon writes itslast_optimizationrecord to.claude/daemon-runtime.jsoninstead of.claude/daemon-config.json.
Migrate: if a branch committed those files, resolve the modify/delete conflicts withgit rm --cached <path>and keep your working copies..claude/daemon-config.jsonstays tracked.
Features
- Dispatch safety gates (#8539): a global circuit breaker and a
shipwright:pauselabel stop all dispatch. New commands:shipwright gates check,breaker,budget,intake,pause,resumeandpaused. Workflows fail closed. See docs/safety-runbook.md. - Failure classifier (#8669, #8678):
shipwright failure classify --log FILEdecides retry or escalate for a failed run. The CI auto-retry workflow and the daemon use it to categorise failures. - Model aliases (#8676) and the Claude 5.5 models across templates and CI (#8634).
- Differential test gate (#8646): the build loop blocks only on test failures that are new compared with the loop's start commit.
- Cheaper build loop (#816): a stable prompt prefix keeps the prompt cache warm (on by default; set
LOOP_STABLE_PROMPT_PREFIX=falseto opt out).shipwright loop --session-continuity(off by default) continues one Claude session across iterations. - TypeScript foundation (#8535): a typed core, architecture fitness rules enforced by
bun run arch, and the first command (hello) ported from bash. - Website and dashboard design (#8705, #8707, #8717): shared design tokens and a new brand mark, a themed docs site with light and dark modes, and a move to Astro 7 and Starlight 0.42.
- Daemon auto-scaling reference (#8630): docs/daemon-autoscaling.md.
Fixes
- Linux no longer prunes all self-optimize memory, because epoch-to-date conversion is portable (#8613). Date handling is portable everywhere else too, and BSD-only
dateflags are banned (#8629). - The loop's test gate runs without credentials, and the real-service e2e suite runs only when you opt in (#8610, #8641).
- Daemon patrol never commits to the checked-out branch: docs sync runs in its own worktree (#8651). A concurrent docs-sync run skips instead of failing (#8661).
- Org-mode failure tombstones are keyed per repository, and issue writes target the job's repository (#8622).
- Piped
grep -qno longer causes SIGPIPE failures underpipefail(#8640). - CI is green again: the dead-code scan is linear instead of O(N²), and model pricing defaults are current (#815).
- Test suites run against a private tmux server, HOME and process tree, so they never touch your live session (#8532, #8653, #8645).
Security hardening
- Workflow injection: no
run:script interpolates${{ }}expressions, and an architecture rule enforces it (#8531, #8533, #8618). - Dashboard: command injection in the server is fixed and it binds to loopback by default (#8534). Wave 1a added a fleet token for workers, CORS, Origin and Host allowlists, a WebSocket Origin check and single-use join tokens (#8539).
- Fleet invite tokens never travel in URLs or argv (#8627).
- Agents: they get no GitHub credentials (#8624) and run under a least-privilege permission policy with a sandbox (#8660). Review remediation runs CI scripts from the default branch, never from the PR branch (#8675).
- Classifier escalations (auth, contamination, invalid input) come only from structured error lines, so log text cannot forge them (#8709). A 1,012-row corpus and a CI fuzz job guard this (#8718).
- Runtime state untracked:
.claude/runtime files are git-ignored, an architecture check enforces it, and the loop never commits them (#8667). - Pinned actions: third-party GitHub Actions are pinned to commit SHAs. This release bumps
claude-code-action,action-gh-releaseandrepository-dispatch(#8683). - Dependencies: vitest, vite, postcss, undici and happy-dom are patched (#8670). The website build check runs on every website PR (#8717) and now also fails on broken internal links (#8719).
- Architecture rules close fitness-rule evasions: they catch GitHub tokens passed to
claudeand flag GitHub API writes from scripts (#8619).
Website
- Fixed 35 internal links that were missing the
/shipwrightbase, and one that pointed to a missing anchor. Added the favicon. A version bump now redeploys the site (#8719).
Known limitations / coming next
- The dashboard does not yet enforce role authorization on every mutating route (#8720). Keep it on loopback or behind your own access control.
- Known classifier limits: forged structured tokens and prose-filter gaps (#8674), benign rows that still escalate (#8722), and build-stage auth failures that are retried (#8714).
- Coming next, not in this release:
- a differential pipeline test stage and a pinned Bun for the gate fixtures (#8671, for #8663 and #8664);
- a blast-radius guard for the build loop (#8662, for #8644);
- the new dashboard shell and command palette (#8712, for #8699);
- a trust boundary where agent output reaches the pipeline only as a patch (#8706).
What's Changed
- fix: green CI — O(N²) dead-code scan, dependabot claude-review, model currency by @sethdford in #815
- feat: stop the build loop cold-starting a Claude session every iteration by @sethdford in #816
- fix(ci): stop expression injection in pipeline triage (100% failure loop) by @sethdford in #8531
- fix(tests): run suites against a private tmux server; stop copying system jq by @sethdford in #8532
- fix(ci): remove all expression interpolation from workflow run scripts by @sethdford in #8533
- fix(dashboard): stop command injection; bind loopback by default by @sethdford in #8534
- Wave 0: TypeScript foundation, fitness rules, agent-ready repo, hello ported by @sethdford in #8535
- Wave 1a: fail-closed safety gates + architecture fitness functions by @sethdford in #8539
- fix(self-optimize): portable epoch→date so Linux stops pruning all memory by @sethdford in #8613
- fix(loop): test gate runs without credentials; real-service e2e suite opts in by @sethdford in #8610
- fix(arch): close accidental evasions in the fitness rules (#8582) by @sethdford in #8619
- fix(ci): workflow follow-ups from Wave 1a (#8572 #8573 #8574 #8575 #8576 #8577 #8578 #8583) by @sethdford in #8618
- docs: daemon auto-scaling and fleet worker pool reference by @sethdford in #8630
- fix: test and daemon hygiene follow-ups (#8584 #8585 #8609 #8615) by @sethdford in #8622
- fix(fleet): invite tokens never travel in URLs (#8579) by @sethdford in #8627
- fix(tests): gate-env and server-api suites pass inside the pipeline's test gate by @sethdford in #8641
- fix(bash): no grep -q reading a pipe; new F11 error rule by @sethdford in #8640
- feat(models): use the Claude 5.5 family everywhere by @sethdford in #8634
- fix: portable date handling; ban BSD-only date flags (#8621) by @sethdford in #8629
- Wave 2 plan: closed-loop intelligence (ADR-0009) by @sethdford in #8588
- test(memory): parse export stdout only (#8643) by @sethdford in #8645
- fix(test): isolate suites from the real tmux server, HOME and process tree (#8648) by @sethdford in #8653
- fix(daemon): patrol never commits to the checked-out branch (#8649) by @sethdford in #8651
- fix(agents): agents never receive GitHub credentials (#8614 #8580 #8616 #8617) by @sethdford in #8624
- fix(loop): differential test gate — block only on new failures (#8633) by @sethdford in #8646
- fix(daemon): docs-sync losing run skips (rc=2) instead of failing; lock follow-ups (#8658, #8656) by @sethdford in #8661
- fix(agents): least-privilege permission policy replaces --dangerously-skip-permissions (#8614, #8581) by @sethdford in #8660
- fix(ci): review-remediation runs trusted base-ref CI scripts; tighten agent-exec gate (#8673) by @sethdford in #8675
- feat(quality): W2-01 incident fixtures + W2-02 failure classifier core (#8589, #8590) by @sethdford in #8669
- fix(deps): bun.lock is the only root lockfile; patch vitest/vite/postcss/undici/happy-dom (#8647) by @sethdford in #8670
- feat(learning): W2-03 auto-retry categories + W2-04 daemon classify bridge (#8591, #8592) by @sethdford in #8678
- feat(web): T1 design tokens, brand mark and budget harness (#8698) by @sethdford in #8705
- feat(intelligence): W2-07 deterministic model routing via config/models.json aliases (#8595) by @sethdford in #8676
- feat(web): T6 docs theming and category comparison (#8703) by @sethdford in #8707
- chore: stop tracking .claude runtime state; loop never commits it (#8665) by @sethdford in #8667
- fix(retry): classifier escalations only from structured error lines (#8696) by @sethdford in #8709
- chore(deps): bump the actions group across 1 directory with 3 updates by @dependabot[bot] in #8683
- chore(website): migrate to Astro 7 and Starlight 0.42 by @sethdford in #8717
- test(retry): classifier corpus, fuzz generator and CI fuzz; no recovery passes the false-positive bar (#8715) by @sethdford in #8718
- release: v4.0.0 — docs, website, changelog by @sethdford in #8723
New Contributors
- @dependabot[bot] made their first contribution in #8683
Full Changelog: v3.3.0...v4.0.0