0.42.0
Cut on 2026-10-17. Status: beta. cox setup migrate moves an existing install under a dedicated home directory,
towpath gains a Spend page that breaks spend down by role, tier and model, and a release is now gated on a passing
rehearsal of its exact component commits. Every hold that stops the lanes now pushes a notice, after two nights in which
one silent hold idled the loop for about nine hours. The land back-off retries a phase as soon as its branch moves, a
phase is no longer landed twice, take --steal --model keeps the model, and the install page gives the Python and PATH
steps a clean machine needs. Of the home
directory promised for this release, only the migration ships: a new install still lays out under --root, as the known
gaps explain.
Moving an install under a home directory. cox setup migrate [--home HOME] [--dry-run] [--apply] prints a plan
and changes nothing; --dry-run is the same as the default, and only --apply moves anything. The home is --home,
then COXSWAIN_HOME, then the recorded home, then $XDG_DATA_HOME/coxswain, then ~/.local/share/coxswain. With
--apply it moves the cartridges, graphs and tools checkouts into <home>/installs/ and leaves a link at each old
path, points the workspace's base and local cartridge links at the cartridges/<base|local> folders inside the
moved checkout, repairs each checkout's git worktrees, rewrites skills_roots, provider_profile and harness_dir in
the profile, and records the home in ~/.config/agent-tools/home. A second run reports each step as already done. It
never moves the workspace, and it prints, but does not run, the steps it leaves to you: recreating editable installs,
registering the plugin marketplace again, and rewriting repository paths with cox route paths. It refuses when no
profile setting names a checkout, when cartridges_dir is missing from the profile, or when a destination already
exists, and a profile key written as a block-style YAML list cannot be rewritten. After the local steps, each lane host
whose heartbeat reports checkouts outside an installs folder gets ssh <host> cox setup migrate --apply, a host
already moved has a stale workspace_dir in lane_hosts rewritten, and a host with no heartbeat is listed for you to
move by hand. A failed local step stops the run before any host step, and the command exits 1 when any host fails.
A Spend page in towpath. The feed's spend_split section gains two fields in each of its five-hour and weekly
windows: by_node, one row per role, tier and model with calls, turns, tokens and cost, most expensive first, and
cost_per_landed_task, the window's cox seat spend divided by the tasks landed in it (chair session and chair agent
spend are not in it). Only ended runs count. Towpath shows them on a third page, Spend, after Regatta and Slipstream:
Tab and Shift-Tab move between pages and f flips the window between five-hour and weekly. The page draws the
three-way split, the table, and the cost per landed task; a feed from an older cox shows the split with a line saying
it carries no per-role breakdown. Towpath remembers the last page.
A release is gated on a rehearsal. python -m devtools release now refuses to cut unless a release-rehearsal
workflow run for the candidate passed: the newest run whose title names each component's current main commit, the
previous tag and the declared migrations must have concluded success with its fresh and upgrade jobs green, and its
migrate job green when the notes declare a migration. --allow-no-rehearsal REASON cuts without one, and then the notes
must carry the line Cut with `--allow-no-rehearsal`: <reason>.. --tap-only skips the gate. The rehearsal workflow
runs the install page's own sequence (uv tool install --python 3.14 coxswain-tools, the umbrella clone, cox install
--with crew --with dash, the workspace folders, git init, cartridge init) on a clean runner, and its upgrade
scenario installs the previous release first.
A refused land retries as soon as its branch moves. The land back-off rule that 0.41.0 added had nothing to read: refusals did not record a head and the planner did not pass the current one, so a refused phase sat out its whole window, up to two hours, even after its branch had been rebuilt. A land now records the tip of its phase branch just before it runs, a refusal carries that head, the chair's facts read the current tip of each refused phase's branch, and the planner passes it in. When the two differ, the window is released and the phase lands on the next tick. A refusal recorded before this release, or a branch that does not resolve, keeps the old full window.
Smaller fixes. cox route chair take --steal --model M records the model when the loop already holds its own
lease, where it had been dropped; without --model the recorded model is kept, and the retaken line names it. The
install page and README install cox with uv tool install --python 3.14 coxswain-tools, since on an older default
Python uv resolved a release from before the 3.14 floor, and they say to run uv tool update-shell and open a new shell
when cox is not found. A phase the loop has just landed is no longer planned for a second land on the next tick, and
a land that finds its phase branch already gone while every task of that phase is done counts as landed instead of
raising a land conflict.
Every hold that stops the lanes reaches you. The notifier had pushed a needs-chair hold only when it was a stranded
task or a repeated harness failure, so a ticket or review quarantine could hold the lanes for a night without a word:
it happened on 2026-10-16 and 2026-10-17, about nine hours each. Every needs-chair hold now pushes one notice, with its
cause, reason and remedy command, through the profile's notify setting, and the existing repeat window keeps it from
repeating. A task waiting for another task of its own initiative to land is the normal state of a multi-phase epic and
pushes nothing. The idle-stall check now names a blocked task before an empty initiative, and an initiative whose tasks
are all finished no longer counts as empty, so its notice names the real blocker instead of a long-finished initiative.
coxswain-dash
- A third page, Spend, after Regatta and Slipstream, reached with
TabandShift-Tab. It draws the three-way split, a Role, Tier, Model, Calls, Turns, Tokens and Cost table, and the cost per landed task for the five-hour or weekly window, flipped withf. A feed without the per-role rows shows the split and a line saying so, and the last page shown is remembered (dash #137, dash #138).
coxswain-graphs
- A build node's run summary keeps a
deniedlist, one entry per tool call that came back refused rather than run, with the tool, its target and the message, so a quarantine can be read after the run's trace is pruned (graphs #795).
coxswain-tools
cox setup migrateplans moving the cartridges, graphs and tools checkouts under the home directory and moves them with--apply, as described above (tools #1719, tools #1722). The workspace cartridge links point at thebaseandlocalfolders inside the moved cartridges checkout instead of its root (tools #1725).- The feed's
spend_splitwindows carryby_nodeandcost_per_landed_task: a fixture contract (tools #1720), the per-node builder (tools #1721), the activity reader that cuts store rows at each window start (tools #1724), and the feed wiring (tools #1726). cox route chair take --steal --modelrecords the model on a lease its own holder retakes (tools #1723).- The approved rows the land planner reads leave out a task the store holds as done, and a land stopped by a missing phase branch whose tasks are all done is recorded as landed, not a conflict (tools #1732).
- Groundwork for a watchdog that runs outside the loop: its decision rules (loop down on a stale chair lease heartbeat,
lanes idle on a hold for a window of status rows) and its store reads and push are in place, but no command calls
them yet;
cox chair watchdogis planned for 0.43.0 (tools #1730, tools #1733). - A land records its phase branch's tip, a refusal keeps it, and the chair reads the current tip of each refused phase and passes it to the land planner, so the back-off releases on a moved branch (tools #1727, tools #1728).
- Every needs-chair hold pushes a notice with its cause, reason and remedy, except a wait on another task of the same initiative; the idle-stall diagnosis checks blocked tasks before empty initiatives, and an initiative whose tasks are all done or dropped is not an empty one (tools #1729, tools #1731).
coxswain
- The release command checks for a passing rehearsal of the candidate's commits and takes
--allow-no-rehearsal REASON(coxswain #310). - The rehearsal script runs the install page's sequence instead of a missing
install.sh(coxswain #309), installscoxon Python 3.14 and puts uv's tool directory onPATH(coxswain #311), and lints the initiative directory it writes, the argumentcox route linttakes (coxswain #312). - The install page and README pin Python 3.14 for
uv tool installand give theuv tool update-shellstep (coxswain #311).
Known gaps
Only the migration half of the home directory ships. cox install and cox setup install still lay out under
--root, so a new install does not use the home until you run cox setup migrate --apply afterwards. The doctor rows
for the home, including one that checks the cartridge links, exist but cox setup doctor does not show them, so after a
migration it reports what it reported before.
The rehearsal workflow's migrate scenario runs only for a declared migration id, and its mapping from an id to a
command is a placeholder; this release declares none. cox setup migrate was rehearsed by hand instead, in a throwaway
Proxmox container on the same commits. A dry run changed nothing, --apply moved the checkouts under the home and
repointed the profile and the cartridge links, cox setup doctor reported no failing rows, no link or path was left
dangling, every work record survived, and a second run changed nothing. The fresh install and the upgrade from 0.41.0
passed both in that container and in the hosted rehearsal (run 39037740345) on tools a061ac0, graphs 304a533, dash
882c528, cartridges 8584e34 and crew 80eb36a.
Migrations: none
Components
coxswain-dash, coxswain-graphs and coxswain-tools change, so graphs rejoins the release at 0.42.0. coxswain-cartridges has no commits since v0.40.0 and stays pinned there, and coxswain-crew is unchanged since v0.38.0 and stays pinned there.
Upgrading from 0.41.0. The store schema is unchanged and nothing migrates on its own. Update cox itself first,
with uv tool upgrade coxswain-tools or brew upgrade cox, then run cox upgrade. To move an existing install under
the home directory, run cox setup migrate to see the plan, then cox setup migrate --apply, and run cox setup
doctor afterwards.