Repository navigation
Releases: ppfenning/coxswain
Release list
coxswain 2026.10.3
2026.10.3
Cut on 2026-10-23. Status: beta. A patch that makes cox setup doctor clean on a migrated machine and lets
the board sync see a working GitHub login on an older gh.
The doctor stops flagging healthy machines. Every migrated host reports its home's providers folder beside the
checkout, where setup migrate copies a team provider profile; the doctor now counts that folder as migrated
instead of telling every host to run setup migrate. With the shipped-cartridge fallback of 2026.10.2 a workspace
needs no base or local cartridge links, so none found is no longer a failure; stale links still fail.
The board sync sees the login. The route sync's check ran gh auth status --active, which gh 2.46 (Ubuntu's
package) does not know, so every land on such a machine reported gh is not authenticated. It falls back to
gh api user, which works on every version.
Docs. Each section links to its own page instead of listing it again as its first child, a component's README
is titled README, and the graphs' reference pages and specifications sit in their own subsections.
coxswain (umbrella)
- docs: sections link to their own page; component READMEs and graph pages stop repeating names (coxswain #378)
- gh login check on any gh; doctor: home providers folder and no cartridge links are fine (coxswain #379)
Known gaps
A profile with no store block still reads as files even when its provider profile names the shared store, and
the doctor warns about the mismatch; set it with cox settings set profile store.mode shared on each machine that
uses the shared store.
Components
coxswain-dash 2026.10.2.
Upgrading
The store schema version is 2026.10.3. This release has no migrations.
coxswain 2026.10.2
2026.10.2
Cut on 2026-10-23. Status: beta. A patch for what running one target repository's work through the chair
turned up, plus a docs pass.
Filing and launching one-task work. cox route file without --intake names the task <slug>-task and files
the initiative row in the store beside it. Before, the task shared its initiative's id, the store kept only one of the
two rows, and launch refused with no initiative row; route edit could not address the task either. --body
given text instead of a path says so instead of printing the whole body as a file name.
Logins. A lapsed Claude login stops the run without quarantining the task or spending an attempt, so the next
launch on a logged-in host builds it. The remote login check runs the claude binary (command claude), not a shell
function of that name: a startup file's claude() wrapper made the check pass while every lane on that host failed
to authenticate.
Remote runs come back. cox runs fetch, and so the chair's fetch of an ended remote run, finds the host the way
cox route launch --on does: the hosts table first, then the profile's lane_hosts, drained hosts included. It read
only the profile, so a run launched on a hosts-table host could never be fetched and its approved tasks never landed.
Cartridges. A cartridge the workspace's cartridges_dir lacks is read from the ones this checkout ships, so a
workspace needs only its own team cartridge and never a symlink into one machine's install.
Faster CI. The extras job runs one job per suite, each still serial with its own Postgres, so CI takes about
three minutes instead of six.
Docs. A component's pulled README no longer replaces its page, and the pulled component pages and the generated
CLI pages join the nav, so every page has previous and next links. The docs call the routing session the chair
throughout, and stale claims about bare cox, install, migrate and upgrade are corrected.
coxswain-dash
- README: cox home opens towpath; bare cox starts a session (dash #142)
coxswain (umbrella)
- route file: distinct task id + initiative row; auth failures don't quarantine; login check runs the binary (coxswain #370)
- docs: component and CLI pages get previous/next; a component README no longer replaces its page (coxswain #371)
- docs: sweep stale claims; the chair, not the chief of staff (coxswain #372)
- cartridges: fall back to the shipped cartridges for a name the workspace lacks (coxswain #373)
- ci: split the extras job per suite (coxswain #375)
- runs fetch: find the host in the hosts table, as route launch does (coxswain #376)
Known gaps
One-task initiatives filed before this release still hold a single colliding row in the store; refile them, or
upsert the task under <initiative>-task and an initiative row under the initiative id. A workspace that tracks
cartridges/base and cartridges/local symlinks keeps working, and can drop them once every machine runs
2026.10.2.
Components
coxswain-dash 2026.10.2.
Upgrading
The store schema version is 2026.10.2. This release has no migrations.
coxswain 2026.10.1
2026.10.1
Cut on 2026-10-19. Status: beta. The first update to the monorepo, built by the lanes in the new repository. It
fixes what moving four real machines to 2026.10.0 turned up, runs every test suite in parallel, and trims the largest
test files.
The move from 0.47 works on real machines. cox setup migrate copies a team provider profile kept inside the old
cartridges checkout into the home and points provider_profile at the copy; writes repo_map back as one-line JSON
the profile reader accepts; releases its checkout leases when it ends and says so when a release fails; and fetches
the release tag into a shallow checkout and syncs the venv before anything runs the checkout's interpreter.
cox install clones the checkout at its own release tag when the home has none, so a first install needs no clone by
hand. Syncing the checkout's venv keeps packages installed into it by hand, so plugins survive cox upgrade.
Faster checks. Every test suite runs with pytest-xdist, in CI and in the lanes' checks: the four suites finish in
about a minute and a quarter instead of five and a half. The extras job stays serial, because its Postgres store tests
back the test database up with pg_dump. Lanes check every component suite through a root .agent-checks file. The
largest test files are consolidated to one literal-input test per rule. On pull requests CI no longer runs the
Python 3.15 job, which takes about twenty minutes; it runs on every push to main.
Smaller fixes. The five-hour spend estimate counts from the meter's window against the profile's window ceiling,
and towpath marks an estimated fraction as one. A decomposed intake's row turns decomposed and links its
initiative, so the empty-queue notice fires again. A release commits its releases-index rewrite even when the version
needs no bump.
coxswain
setup migratekeeps an untracked provider profile (#351), writesrepo_mapas JSON (#365), releases its leases
(#342, #354) and fetches the tag into a shallow checkout before syncing (#353, #359).cox installclones the checkout itself (#341, #362); venv syncs keep hand-installed packages (#357).- Every suite runs in parallel (#340, #352); lanes check every suite (#337); py315 runs on main only (#344).
- Test consolidation: test_route (#348, #366), test_land (#347, #364, #367), test_chair_exec (#346, #356, #358),
test_claude_code_runner (#360, #363), test_epic_driver (#349, #361). - The five-hour estimate follows the meter window (#339, #350).
- A decomposed intake links its initiative (#345); the release commits its index rewrite (#343, #355).
- Docs match the code at 2026.10.0 (#338).
coxswain-dash
- towpath marks an estimated five-hour fraction and caps its text at >100% (coxswain-dash #140).
Known gaps
cox setup migrate leaves a machine's checkout on the release tag, while cox host sync and a lane launch update a
checkout only on main; the fleet runs main, so its checkouts were moved to main by hand. The chair relaunches an epic
whose work graph is invalid without raising a hold. The empty-decompose hold can fire before the decompose's tasks are
imported. Runtime state still lives in the workspace's runs folder.
The gate for 2026.10.1 was the hosted rehearsal of the release commit, whose fresh, upgrade and migrate jobs passed,
and a clean-machine rehearsal in a throwaway container running the same scenarios.
Migrations: none
Components
coxswain is released at 2026.10.1. coxswain-dash changes and is released with it.
Upgrading from 2026.10.0. The store schema is unchanged. Update cox itself (brew upgrade cox or
uv tool upgrade coxswain), then run cox upgrade.
coxswain 2026.10.0
2026.10.0
Cut on 2026-10-19. Status: beta. The first monorepo release, and the first with a CalVer number. Coxswain is now
one repository, ppfenning/coxswain, and one package, coxswain, with one command, cox. The tools, the harness
and graphs, the cartridges and the crew live under src/tools, src/graphs, src/cartridges and crew/, each
with its full git history. coxswain-dash (towpath) stays its own repository and an optional --with dash.
Nothing new for users beyond the move: 0.47.0's behaviour is carried over, plus the fixes below.
One checkout, one venv. A machine now holds one checkout at ~/.local/share/coxswain/installs/coxswain with
one venv, and cox is that checkout installed as a uv tool. Every harness launch, the chair unit's PATH, the store
CLI and the doctor take their interpreter from one rule: the checkout's .venv, or on a machine that has not moved
yet, the harness checkout's own. A chair on the new layout can still route to a lane host on the old one until that
host migrates.
Moving from 0.47. Update cox first: brew upgrade cox, or uv tool uninstall coxswain-tools && uv tool install coxswain. Then cox setup migrate shows the plan and cox setup migrate --apply carries it out: it
checks out the release in the umbrella checkout (which becomes the monorepo), syncs its venv, rewrites the
profile's harness_dir, skills_roots, umbrella_dir and the default provider_profile, repoints the workspace
cartridge links, re-registers the plugin marketplace, swaps the coxswain-tools and agent-cartridges uv tools
for coxswain with the same extras, refreshes the chair unit, and moves the old component checkouts to
installs/.retired-<date>/. Nothing is deleted. A second run plans nothing. See
From 0.47 to the monorepo.
Versions. YYYY.M.N, tagged vYYYY.M.N: the year, the month without a leading zero, and the count of
releases cut that month. Every 0.x tag sorts below every CalVer tag, so cox upgrade moves forward from 0.47.0.
Compatibility is carried by the store and workspace schema and by cox setup migrate, not by the number.
One release. A release is one version bump, one tag, one PyPI publish of coxswain, one GitHub release and
one Homebrew formula PR. The rehearsal takes one commit: a fresh install, and an upgrade from a real 0.47.0
install followed by cox setup migrate.
coxswain
- Histories imported under
src/tools,src/graphs,src/cartridgesandcrew/; one distribution, one lock,
one CI with the four suites (2d1edd7, 42d9bf7). install_rootdecides the checkout and the harness interpreter; every launch, the chair unit and the store CLI
use it (25d1840, 6ec133c).- The governance gate keeps each component's own scope:
src/cartridges/cartridges,skills-plugins,
core/policy.py,core/ledger.pyandsrc/graphs/harnessescalate, and the rest ofsrc/cartridgesdoes not
(a343709, a1c3e7c). - Install and upgrade work on the one checkout;
cox installkeeps a checkout already present (209b62d,
29768d5). cox setup migratemoves a 0.47 machine, under the home or a--rootinstall, to the monorepo, and never
leaves the venv behind the checkout's lock (0fde981, 29768d5, 029f1e0).cox host syncand a resident chair's install bring a lane host to the commit the chair runs, a release tag
included (f76965d, 731e177).- The doctor checks the one checkout; its core probe imports in-process, its plugins row asks the harness
interpreter (acbe35d, 6a90a3b, 3af167c, 07a6ea3). - CalVer tags sort above every 0.x tag; the release planner, publish guard and rehearsal take one repository
(d9883d0, dcdd701, 8efddd5). - Steward proposals and install hints name
coxswain(5457cfd, 8ec9419). --with crewis accepted as a no-op for one release; crew always installs (a2bcc71).
Known gaps
cox install still needs the checkout cloned first, as 0.47 did. Plugins installed into the checkout's venv by
hand are removed by the uv sync in cox upgrade and must be installed again. Runtime state still lives in the
workspace's runs folder. An intake row is not linked to its initiative when it decomposes, so it stays queued
after its work lands. The test suite is unchanged; 2026.10.1 runs it in parallel and consolidates its largest files.
The gate for 2026.10.0 was the hosted rehearsal of the release commit, whose fresh, upgrade and migrate jobs
passed, and a clean-machine rehearsal in a throwaway Proxmox container running the same scenarios, with dash
pinned at v0.42.0.
Migrations: none
Components
coxswain is released at 2026.10.0. coxswain-dash has no commits since v0.42.0 and stays pinned there.
Upgrading from 0.47.0. See Moving from 0.47 above. The store schema is unchanged.
coxswain 0.47.0
0.47.0
Cut on 2026-10-18. Status: beta. A hardening release that closes the known gaps 0.43.0 to 0.46.0 left open around
the coxswain home and the chair loop. The migration now moves the umbrella and 0.40-style installs, lists operator
scripts it cannot rewrite and beats when it ends. The doctor gains the rows the home design promised. The chair holds
an approved task's files until it lands and resolves appended test conflicts at land. Several commands now refuse
input that used to be accepted and then fail later. Only coxswain-tools changes.
The migration finishes on every layout. cox setup migrate now moves the umbrella checkout (coxswain) under the
installs folder with a compatibility link when it is present, so its ../coxswain-tools dependency resolves after
--finish. A host without an umbrella plans nothing for it. An install from 0.40.0 that keeps bare-named checkouts
behind coxswain-<name> links was refused; the plan now moves the link's target and relinks the old path. A successful
--apply lists every operator script that still names an old checkout folder, as migrate: <file> still names <old path>; edit it by hand, and ends with a host beat, so --finish no longer waits for the next beat.
The doctor checks what the migration touches. profile paths now checks umbrella_dir when it is set, and a new
lane host paths row fails when a lane host still beats a checkout path outside the installs folder. The home paths
row accepts a provider profile kept in the team's workspace, which no migration moves. The forge row asks the GitHub
API for the login (gh api user) instead of gh auth status, which rejected a working GH_TOKEN after a rate-limit
episode.
The chair keeps parallel work from colliding. An initiative whose tasks are approved but not yet landed now holds
its files against new launches, as a running one does; a second initiative had built on the same function four times
and conflicted at land each time. When a phase squash conflicts only in test files where both sides append new lines at
the end, the land keeps both sides instead of stopping. An empty decompose now holds with the lint refusal from its log
instead of refusal line not found.
Commands refuse what they cannot do. cox route file refuses a --repo, or a repo: in the intake's frontmatter,
that is not a git checkout on this machine, with route file: refusing, <repo> is not a git checkout on this machine
and exit 2; such intakes used to file and then fail every launch. cox host beat <name> refuses a name other than this
machine's, since the beat records this machine's facts. cox runs triage takes --profile and --repo, so it reads
the profile's store and a named repository from any folder. cox chair watchdog --install writes the five-minute timer
the watchdog was meant for (a launchd job on macOS, a systemd user timer on Linux), --uninstall removes it, and
--interval-minutes changes the period.
coxswain-tools
- An approved task not yet landed holds its surfaces against other launches (tools #1808).
- A phase squash whose only conflicts are appended test lines lands with both sides (tools #1815, #1818).
- The empty-decompose hold names the lint refusal from the decompose log (tools #1809, #1812).
setup migrate --applylists scripts naming old checkouts and ends with a beat (tools #1813, #1817), run through the
installedcox host beatso a reinstall during the apply cannot break it (tools #1827).- The doctor
forgerow asksgh api user(tools #1811). - The doctor checks
umbrella_dirand adds alane host pathsrow (tools #1821). cox chair watchdog --install,--uninstalland--interval-minutes(tools #1810, #1814).cox runs triagetakes--profileand--repo(tools #1825).cox host beatrefuses another host's name (tools #1822).cox route filerefuses a repository that is not a checkout (tools #1824).setup migratemoves the umbrella checkout (tools #1819, #1826).- The
home pathsrow accepts a provider profile kept in the workspace (tools #1823). setup migratemoves a 0.40 install whosecoxswain-<name>paths are links (tools #1816, #1820).
Known gaps
The migration lists operator scripts that name an old checkout but does not rewrite them, and it does not read launchd
jobs or systemd units other than the chair unit. An intake row is not linked to its initiative when the intake
decomposes, so it stays queued in the store after its work lands; the empty-queue notice counts those rows and stays
silent until they are closed by hand.
The gate for 0.47.0 was the hosted rehearsal (run 39131292779), whose fresh, upgrade and migrate jobs passed, and a
clean-machine rehearsal in a throwaway Proxmox container, where the fresh install, the upgrade from 0.46.0 and
cox setup migrate passed. Both ran on tools b11d3d6, graphs 0c138e2, dash b654297, cartridges 8584e34 and crew
80eb36a.
Migrations: none
Components
coxswain-tools changes and is released at 0.47.0. coxswain-graphs has no commits since v0.43.0 and stays pinned there,
coxswain-dash has none since v0.42.0 and stays pinned there, coxswain-cartridges has none since v0.40.0 and stays
pinned there, and coxswain-crew is unchanged since v0.38.0 and stays pinned there.
Upgrading from 0.46.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. A host whose cox setup migrate --finish waited on home paths because its provider profile lives in the workspace can now run it again.
coxswain 0.46.0
0.46.0
Cut on 2026-10-18. Status: beta. A hardening release from moving the maintainers' own machines under the coxswain
home. cox setup migrate --finish can now finish: a lane host that reports a folder inside a moved checkout counts as
migrated, and a second --apply after the move keeps the migration marker and plans no drop-in rewrite. The
migration also rewrites the profile's repo_map entries for the moved checkouts. The chair's status line counts lanes
across every host, an empty queue with idle lanes pushes one notice, and the loop pulls issues at most every ten
minutes, where it had exhausted the GitHub GraphQL limit. Only coxswain-tools changes.
cox setup migrate --finish finishes. A lane host's beat lists coxswain-cartridges/providers among its
repositories, and the check for a migrated host wanted every path directly under the installs folder, so --finish
answered finish: waiting on host <name> to migrate for every host however long it waited. A path anywhere inside the
installs folder now counts. After the move every profile setting names the installs folder, so a second
cox setup migrate --apply resolved the old root to the installs folder itself: it overwrote migrate.json with the
old root equal to the new one, and it planned the chair unit drop-in rewrite on every run, so the local plan never came
down to the single skip that --finish waits for. The marker is now written only while the old root differs from the
installs folder, and the unit and drop-in steps are planned only when a folder actually moves.
The migration rewrites repo_map. The profile's repo_map, which maps a repository name to its checkout, kept
naming the old checkout folders after a migration and had to be rewritten by hand before --finish removed the
compatibility links. A repo_map value that equals an old checkout path, in its absolute or tilde spelling, is now
rewritten to the checkout under the installs folder in one rewrite_profile_key step, and every other entry is left as
it is. A second run plans nothing for it.
The chair sees the whole fleet. The status line's lanes part reads lanes L/C (here l/c) when there are lane
hosts: L and C add every host's live lanes and capacity to the chair's own, which stay in the parenthesis. It had shown
only the chair's machine, lanes 0/4 while ten lanes were building elsewhere. An empty queue now pushes one notice,
Queue empty, when no initiative has a ready task, no intake is queued, no task is approved, no lane is live on any
host and the tick raised no hold; it pushes once until the queue fills again. And the issue pull, which the chair
planned on every idle tick for every routed repository, now waits ten minutes after the last one.
coxswain-tools
setup migrate: a folder inside a checkout under the installs folder counts as migrated (tools #1801), and a re-run
after the move keeps the marker and plans no unit or drop-in rewrite (tools #1802).setup migraterewrites the moved checkouts' entries in the profile'srepo_map(tools #1805).- The status line counts lanes across every host (tools #1803).
- An empty queue with idle lanes pushes one
Queue emptynotice (tools #1806). - The chair plans the issue pull at most every 600 seconds, from a new
pull_age_sfact (tools #1804).
Known gaps
The umbrella's development environment declares the tools checkout as ../coxswain-tools. cox setup migrate moves
the tools checkout under the home and leaves the umbrella where it is, and --finish removes the compatibility link,
so uv run in the umbrella cannot resolve the tools package afterwards. On a maintainer's machine that cuts releases,
keep a coxswain-tools link beside the umbrella until the migration moves the umbrella too. The migration does not
list operator scripts, launchd jobs or systemd units outside the chair unit that still name an old checkout folder;
check them by hand before --finish.
From 0.44.0, still open: the doctor has no lane host paths or migration state row, and profile paths does not
check umbrella_dir; the migration emits no beat when it ends, so --finish waits on each host's next beat. From
0.43.0, still open: the empty-decompose hold always says no refusal line was found in the decompose log, cox runs triage takes no --profile or --repo, and nothing sets up the timer for cox chair watchdog.
The gate for 0.46.0 was the hosted rehearsal (run 39099135025), whose fresh, upgrade and migrate jobs passed, and a
clean-machine rehearsal in a throwaway Proxmox container, where the fresh install, the upgrade from 0.45.0 and
cox setup migrate passed. Both ran on tools 50d302f, graphs 0c138e2, dash b654297, cartridges 8584e34 and crew
80eb36a.
Migrations: none
Components
coxswain-tools changes and is released at 0.46.0. coxswain-graphs has no commits since v0.43.0 and stays pinned there,
coxswain-dash has none since v0.42.0 and stays pinned there, coxswain-cartridges has none since v0.40.0 and stays
pinned there, and coxswain-crew is unchanged since v0.38.0 and stays pinned there.
Upgrading from 0.45.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. If a cox setup migrate --finish
was waiting on hosts, run cox host beat on each host and run it again.
coxswain 0.45.0
0.45.0
Cut on 2026-10-18. Status: beta. A chair that cannot reach a shared store lease now refuses the chair instead of
falling back to the lock file, which only sees chairs on its own machine. cox setup migrate and cox setup doctor
read the chair unit's PATH across its systemd drop-ins as well as the unit file, and the migration rewrites a drop-in
that still names the old checkout root. A fetched remote run no longer moves a task that the chair changed after the run
started, and cox setup migrate keeps each virtualenv's extras when it recreates it. The test suite drops the
byte-for-byte help snapshots and the help-wording tests, and the stats CLI tests pin each decision once. Only
coxswain-tools changes.
A chair that cannot reach a shared store lease refuses the chair. When the store is postgres, a lease that cannot
be acquired, whether the lease call fails, answers released instead of granted, or finds no harness, now refuses with
chair: cannot reach the store lease (<detail>); not taking the chair, and cox chair take exits 2. Before, every
one of those cases warned and fell back to the lock file, which cannot see a chair on another machine. During the 0.44
home migration a rebuilt virtualenv on a standby lacked the postgres driver, the lease call failed, and the standby
ticked as chair for 17 seconds. The files store keeps the fallback and its warning, since its lock
file is the whole truth there. A chair that already holds the lease and fails to renew it still warns and keeps the
chair, so one store blip does not abandon a chair in the middle of a land; only acquiring refuses. A chair with no
lease sidecar, one taken before the lease existed or one that had fallen back to the lock file, acquires at its next
beat and so refuses in the same way (tools #1794).
The chair unit's PATH is read from its drop-ins too. systemd lets a *.conf file under
~/.config/systemd/user/coxswain-chair.service.d/ override the unit's Environment=PATH=, and a machine that set the
PATH that way looked fine to the doctor and the migration while the chair ran from the old folders. The effective PATH
is now read from the unit file first, then its drop-ins in file-name order, the last one that sets PATH winning
(tools #1793). The doctor's chair unit PATH row checks that value, adds (from <file>) to its detail, and fails with
no Environment=PATH= line in the unit or any drop-in (both checked) only when neither sets one. In cox setup migrate, when the effective PATH still names a coxswain- checkout under the old root and comes from the unit file,
the plan ends with the chair_unit step as in 0.44.0; when it comes from a drop-in, the plan ends with a
chair_unit_dropin step that names that drop-in. On --apply that step replaces each old checkout folder with its new
one on the drop-in's Environment=PATH= lines, leaves the unit file and every other drop-in alone, runs systemctl --user daemon-reload, and restarts the chair only when systemctl --user is-active said it was running when the plan
was made. An unreadable drop-in is left as it is with a line saying so, and a failed systemctl call fails the step
(tools #1796).
A fetched remote run never overwrites a task the chair changed after the run started. When the store holds the task
records, fetching a remote run moves each task the run approved from ready to approved. It now first compares the
task's updated_at in the store with the run's started_at, and a task updated strictly later keeps its state with
fetch: <task> kept <state>, changed after run <run> started, so a task the chair edited or requeued while the run was
out is not approved from the run's stale outcome. A missing or unparseable timestamp leaves the fetch as it was
(tools #1797).
The test suite is lighter. The consolidation that began in 0.43.0 (tools #1755 to tools #1760) continues and removes
tests that pinned what development changes on purpose, on the principle that a test which hinders development has lost
its value and that functional code is largely self-testing. The byte-for-byte help snapshots are gone: the 101 fixture
files under tests/fixtures/help, the snapshot suite that compared every command group's --help with them, and the
pytest --regen option that rewrote them (tools #1791, tools #1795). The tests that pinned help wording in the CLI
help, chair run, chair holder and beat loop deprecation suites go with them (tools #1791), and the stats CLI tests drop
their duplicates so each decision is pinned once (tools #1792). Counted as def test_ lines, the consolidation removes
19 test functions and the new features add 36, so the suite goes from 7,158 at v0.44.0 to 7,175; the snapshot suite
alone ran 116 parametrized cases. The test tree goes from 610 files to 510.
coxswain-tools
- A chair on a postgres store refuses the chair when its store lease cannot be acquired, where it had fallen back to
the lock file; the files store keeps the fallback, and a failed renewal of a held lease still only warns
(tools #1794). - The chair unit's effective PATH is read across the unit file and its
*.confdrop-ins, the last to set PATH winning
(tools #1793). The doctor'schair unit PATHrow checks that value and names the file it came from, andcox setup migraterewrites a drop-in that still names the old checkout root with a newchair_unit_dropinstep, restarting
the chair only when it was running (tools #1796). - Fetching a remote run keeps the state of a task whose store row was updated after the run started (tools #1797).
cox setup migraterecreates each virtualenv with the optional-dependency groups the old one satisfied,dev
always among them, where it had installed.[dev]only and dropped extras such aspostgres. A group counts when
every distribution it names has adist-infofolder in the old.venv; when the old venv cannot be read it installs
.[dev]and printsmigrate: <repo> venv extras unknown; installed .[dev] only(tools #1798, tools #1799).- The byte-for-byte help snapshot suite, its fixtures and the help-wording tests are removed (tools #1791), and so is
thepytest --regenoption (tools #1795). - The stats CLI tests drop duplicated cases in the examples, ingest and query suites (tools #1792).
- The suite goes from 7,158 test functions at v0.44.0 to 7,175.
Known gaps
From 0.44.0, still open: some of what docs/design/coxswain-home.md promises is not built. The doctor has no lane host paths or migration state row, and profile paths does not check umbrella_dir. The migration marker records only
the old and new roots, not the last finished step. The migration does not list operator scripts or launchd jobs that
still name an old bin folder, and emits no beat when it ends, so a lane host's row shows the new paths only on its next
beat and --finish waits on that beat. The repository paths are rewritten only by --finish, after every host has
moved, so until then the queue keeps the old paths and reaches the checkouts through the compatibility links.
From 0.43.0, still open: the empty-decompose hold always says no refusal line was found in the decompose log, and
nothing records that it was reported. cox runs triage takes no --profile or --repo, nothing sets up the timer
for cox chair watchdog, and closing an initiative on its last task works in store mode only.
The gate for 0.45.0 was the hosted rehearsal (run 39085227867), whose fresh, upgrade and migrate jobs passed, and a
clean-machine rehearsal in a throwaway Proxmox container, where the fresh install, the upgrade from 0.44.0 and
cox setup migrate passed. Both ran on tools 4e3e6be, graphs 0c138e2, dash b654297, cartridges 8584e34 and crew
80eb36a.
Migrations: none
Components
coxswain-tools changes and is released at 0.45.0. coxswain-graphs has no commits since v0.43.0 and stays pinned there,
coxswain-dash has none since v0.42.0 and stays pinned there, coxswain-cartridges has none since v0.40.0 and stays
pinned there, and coxswain-crew is unchanged since v0.38.0 and stays pinned there. The umbrella has no changes since
v0.44.0 beyond these notes and its manifest.
Upgrading from 0.44.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. On a postgres store, check that
each chair machine's tools virtualenv has the postgres extra before starting the chair, since a chair that cannot
reach the store lease now refuses. A machine whose chair unit sets its PATH in a drop-in can run cox setup migrate
again to see whether that drop-in still names the old checkout root, and cox setup doctor names the file the PATH
came from.
coxswain 0.44.0
0.44.0
Cut on 2026-10-17. Status: beta. The dedicated coxswain home is finished. cox install and cox upgrade without
--root lay out under the home's installs folder, cox setup doctor shows the home rows, and cox setup migrate --apply moves an existing install with a preflight that refuses while anything holds a checkout. The migration now
recreates the virtualenvs and reinstalls the cox and cartridge uv tools from the moved checkouts, re-registers the
coxswain-cartridges plugin marketplace, and regenerates the chair systemd unit on Linux. Once every host has moved,
cox setup migrate --finish --apply rewrites the shared repository paths and removes the compatibility links. A
machine not yet migrated warns on the home rows instead of failing its doctor, and cox setup install keeps the extras
of the cox and cartridge uv tools. A land freeze is re-checked before each land of a running tick, an
empty-decompose hold waits for the decompose process to exit, land heads resolve for a repository recorded in tilde
form, and cox upgrade --to takes a bare version. The 0.42.0 and 0.43.0 known gaps about the home not being wired are
closed, except where the known gaps below say otherwise.
A new install lays out under the home. cox install and cox upgrade no longer require --root. Without it the
installs folder is <home>/installs, where the home is --home, then COXSWAIN_HOME, then the recorded home, then
$XDG_DATA_HOME/coxswain, then ~/.local/share/coxswain. The manifest is read from <home>/installs/coxswain/ manifest.toml, so the umbrella is cloned there first, and a missing manifest is refused with a line that says so.
cox install creates the installs folder before it runs its steps, and its setup step runs cox setup install --home <home> in place of --root. With no --workspace, that step makes the default workspace <home>/workspaces/main with
its work, runs, intake and cartridges folders, and its last write records the home in
~/.config/agent-tools/home. cox setup install without --root already laid out under the home in the 0.43.0
packages, though the 0.43.0 notes said it required --root; it still refuses a home named by --home or
COXSWAIN_HOME that differs from the recorded one and tells you to run cox setup migrate --home. With --root, all
three commands behave as in 0.43.0, and cox setup install --root still requires --workspace.
cox setup doctor shows the home rows. After the install rows and before the store rows, the doctor prints home root, marketplace registered and marketplace location, then old layout, home paths, home recorded,
cartridge links and install bins. home root fails when the home is missing or not writable. The two marketplace
rows warn, without failing, when claude is not on PATH. home paths passes when every skills root, the provider
profile and the harness dir sit under the home after tilde expansion, and home recorded passes when the home record
names the resolved home. cartridge links accepts a base or local link that points at its folder under
coxswain-cartridges/cartridges in the installs folder, written relative or absolute. install bins compares four
editable sources with their checkouts under the installs folder: the graphs and tools virtualenvs, read from each
package's direct_url.json, and the cox and cartridge uv tools, read from their receipts in uv tool dir; it is
left out when uv tool dir cannot be read. On a machine with no home record, old layout, home paths, home recorded and cartridge links warn not migrated: run cox setup migrate, and a failing home root, marketplace registered, marketplace location or install bins row warns not migrated: run cox setup migrate; <detail>
instead of failing, so an install made before 0.44.0 keeps a passing doctor until it is moved. Every row compares text
and follows no link, and without a profile every home row is skipped.
cox setup migrate --apply refuses while anything holds a checkout. When the plan holds any step beyond a skip or
a print, local or, with --hosts, on a lane host, --apply first takes a one-hour repository lease on each old
checkout, held as migrate@<host>:<pid>, and only then reads the live lanes, the chair lease and this host's row in
the store's hosts table. It refuses, prints one line per holder and exits 1 when a lease is held by someone else, a
live lane runs on this machine, the chair holds a live lease here, or this host's row is active, with cox host drain <host> named as the remedy for the last. It does not wait and stops no run. A preflight that passes writes
migrate.json in the coxswain state folder with the old and new checkout roots, and the leases are released when the
command ends, whether it moved anything or not. The refusals were written in 0.43.0 and are now called (tools #1766).
The migration recreates the virtualenvs and reinstalls the uv tools. When any of the four editable sources the
install bins row checks is not its checkout under the installs folder, the plan gains eight steps after the worktree
repair. For coxswain-tools, coxswain-graphs and coxswain-cartridges in that order, it runs uv venv --clear -q and
uv pip install -q -e .[dev] in the moved checkout, the graphs install also taking the cartridges checkout editable.
It then runs uv tool install --force -q -e on the tools checkout and on the cartridges checkout, each with the extras
its uv tool receipt names, so a cox installed with an extra keeps it. When uv tool dir cannot be read the plan has
no reinstall steps. Because the reinstall replaces the environment cox runs from, --apply imports every module it
may need before the first step runs. The 0.42.0 printed note that left the editable installs to you is gone.
The migration re-registers the plugin marketplace. When claude is on PATH and claude plugin marketplace list
shows coxswain-cartridges anywhere but the cartridges checkout under the installs folder, the plan runs claude plugin marketplace remove coxswain-cartridges when an entry exists, then claude plugin marketplace add with the moved
checkout. It then runs claude plugin install again for each plugin id ending @coxswain-cartridges that claude plugin list --json lists, or for local-skills@coxswain-cartridges when none is listed. When claude is not on PATH
the plan prints that the marketplace was not checked. This replaces the 0.42.0 printed note that left the marketplace
to you.
The migration regenerates the chair systemd unit on Linux. On Linux the plan reads coxswain-chair.service from
~/.config/systemd/user, and when its Environment=PATH= line still names a coxswain- checkout under the old root,
the plan ends with a chair_unit step; a machine with nothing else left to move gets that step alone. The step reads
--label, --interval and --profile back out of the unit's chair run line and the file its EnvironmentFile=
names, and rebuilds the unit the way cox chair service --install does, with the cox now on PATH. It writes the
unit, runs systemctl --user daemon-reload, and restarts the service only when it was active. A unit with no chair run line, no usable label or interval, or values that the install would refuse is left as it is with a line saying
why. A failed systemctl call fails the step.
cox setup migrate --finish closes the move once every host has moved. cox setup migrate --finish [--old-root PATH] [--apply] [--dry-run] is its own mode: it plans by default and changes things only with --apply, and
--dry-run keeps it a plan even beside --apply. The old checkout root is --old-root, else the old_root that
migrate.json recorded; with neither it prints finish: no old root known; pass --old-root and exits 2. It refuses
--hosts, and --old-root without --finish, with exit 2. It first checks its gates and prints one finish: waiting on ... line per gate that fails, exiting 1: this machine's plain migrate plan must be a single skip, every home row of
the doctor must pass, and every lane host must have a beat row whose checkouts all sit under an installs folder. With
--apply it also refuses, exit 1, while a live lane runs on this machine, the chair holds a live lease here, or this
host's row is active, as the plain --apply preflight does. Then, for each of the three checkouts, it rewrites the
tilde prefix under the old root to the tilde prefix under the installs folder: in the store's queue rows in store mode,
else in the work files, the same rewrite as cox route paths --move. It reads the rows again afterwards and exits 1
with finish: N value(s) still under <old root> when any remain, removing nothing. Only then does it remove each link
on this machine at <old root>/<checkout> whose target is exactly that checkout under the installs folder, keeps
anything else there with a finish: kept <path>: <reason> line, and deletes the marker. Each change is printed as
finish: rewrite, finish: remove or finish: delete the marker, as finish: would ... without --apply, and a
machine with nothing left prints finish: nothing to do. A store it cannot read, or a profile that does not say where
the queue lives, exits 2 with nothing removed. The plain migrate plan now prints run cox setup migrate --finish once every host has migrated. in place of the note that left the repository paths to you.
The rest of the move shipped in 0.43.0. A launch on a lane host maps the harness to the host's recorded checkout
of the same name (tools #1746), so hosts on the old and the new layout run side by side while the fleet moves, and
cox route paths --move OLD NEW swaps a moved prefix by hand (tools #1743).
Smaller fixes. A land freeze set while a tick runs now stops the lands that tick had already planned: each land
re-reads the freeze at the tick'...
coxswain 0.43.0
0.43.0
Cut on 2026-10-17. Status: beta. Lands can be frozen for a release cut with cox chair freeze-lands while the
lanes keep building. A quarantine caused by the ticket or the code is relaunched once by the loop instead of held for
the chair, and the relaunched run sends the task to triage before it builds again. cox chair watchdog checks the loop
from outside it, run from a five-minute timer. An initiative is marked done on the tick its last task closes, cox runs triage prints a quarantined task's facts in one command, and cox route paths --move swaps a moved repository prefix
in the queue rows and work files. The home directory install is still not wired: a new install lays out under
--root, and the install into the home moves to 0.44.0, as the known gaps explain.
Freezing lands for a release cut. cox chair freeze-lands --for MINUTES --reason TEXT writes a land-freeze row
to the store's leases table that expires after MINUTES. While it stands, the chair plans no phase land and no task
land. Fetches, launches and holds are still planned, so the lanes keep building and approved work waits on its
branches. The status line gains a lands frozen until HH:MM (reason) part, with the time in UTC. With neither flag the
command prints whether lands are frozen, --off lifts the freeze, and a freeze that outlives its minutes lifts itself,
so a forgotten freeze never needs deleting. --for and --reason go together and never with --off, MINUTES is at
least 1, and any other mix prints the usage line and exits 1.
A ticket or code quarantine is relaunched once. A quarantine whose cause is the ticket or the code held its
initiative for the chair. The loop now relaunches it once on the ticket's current body, and a second quarantine on that
body reaches the chair with the first and the latest attempt's reasons, 300 characters each. In the relaunched run the
epic harness caps a task whose newest attempt was caused by the ticket or the code at one attempt instead of two, so the
task goes to triage before it builds again. When triage proposes a ticket amendment that only appends text to the body,
the harness applies it at once, without waiting on the approval gate, provided the amendment is eligible and the
cartridge names a model apply arm. It then checks that the ticket file holds the old body with text appended, and
rebuilds the task on the new body in the same run with its attempt count reset. An apply that fails or writes anything
else is undone: the ticket file is restored to the bytes read before, and the task is quarantined with the reason.
A watchdog outside the loop. cox chair watchdog [--idle-minutes N] [--stale-minutes N] [--dry-run] is a one-shot
check meant for a five-minute timer. It reads the chair lease and the recent status lines from the store. It pushes
Chair loop down when the lease has no row or no beat in --stale-minutes (default 10), or when no status line was
written in the last --idle-minutes (default 30). It pushes Lanes idle <minutes> min, naming the holds, when every
status line in that window shows no busy lane and at least one hold. It pushes through the profile's notify setting
with the loop's own sent-state file, --dry-run prints the alerts and pushes nothing, and it exits 1 only when the
store cannot be read. This command and the empty-decompose report below were already in the 0.42.0 packages, though the
0.42.0 notes left them out and called the watchdog planned.
An initiative closes on the tick its last task closes. In store mode, each tick the chair finds every initiative
still in state todo whose task rows are all done or dropped, and moves it to done, or to dropped when none of its tasks
is done. An initiative with no task rows is left alone, and the close is planned even while a limit has stopped
launches.
cox runs triage TASK_ID [--json] prints a quarantined task's facts. It reads the store and git and makes no model
call. It prints the task's initiative, phase, state and newest run, each recorded attempt with its kind, cause and
reason, the review verdict and its objection, the last failed check with the last 20 lines of its output, the phase's
latest land refusal with its time, head and reason, and each surface as present, missing, or new when a surface marked
(new) is not on main yet. The store is the one the profile names, and surfaces are looked up on main, else
origin/main, of the git repository in the current directory. An unknown task id exits 1 with a line saying so.
cox route paths --move OLD NEW swaps a moved repository prefix. In store mode it rewrites each row's repo and
its absolute or tilde surfaces; otherwise it rewrites each work file's repo: line and surfaces. OLD and NEW are
taken to the tilde form first, a path moves only when it is OLD or lies under OLD/, and a relative surface never
moves. Like the rest of route paths it lists the changes and writes nothing without --apply. With --apply it reads
the rows or files again, prints how many values are still under OLD, and exits 1 when any are. It is the step cox setup migrate --apply prints, but does not run, after it moves the checkouts.
cox setup migrate --apply migrates only this machine. In 0.42.0 --apply went on to each lane host after the
local steps. The host steps now run only with --apply --hosts, on the chair's machine; without --hosts the command
prints a line saying the hosts were not touched.
Smaller fixes. A needs-chair hold now pushes once while it stands. The one-hour repeat window had pushed a standing
hold again every hour, 19 notices in 46 minutes on 2026-10-17; its key is now kept while the hold stands and pruned an
hour after it clears. Holds the loop clears by itself (a land between its fetch and its record, a launch deferred behind
a running initiative that shares a file, a work file the export left alone) never push. A decomposed intake whose
decompose run ended with no stored tasks is reported as a hold with cox route requeue <initiative> as its remedy,
cox route requeue finds the intake through the intake: field of initiative.md when no store row links them, and
an initiative already done or dropped is never reported as an empty decompose. A launch on a lane host syncs the
harness and the repository at the checkout of the same name that the host's heartbeat recorded, migrated under
installs/ or not, and pushes the repository there. A decompose names its initiative from the intake's title, where it
had used the whole intake file, and the ticket lint no longer refuses a path under tests/ as corpus.
coxswain-graphs
- A task whose newest attempt was caused by the ticket or the code is capped at one attempt instead of two, so a
relaunch sends it to triage before it builds again (graphs #800). - An append-only ticket amendment from triage is applied at once when it is eligible and the cartridge names a model
apply arm. The written file is checked for the old body with text appended, and the task is rebuilt on the new body in
the same run with its attempt count reset. A failed or mismatched apply restores the file and quarantines the task
with the reason (graphs #803). - A decompose takes its initiative's title from the intake's
title:, else its first#heading, else its first line,
as one line of at most 120 characters. The intake's frontmatter is dropped from thewhy, itsrepo:is used when
the launch names no repository, and a title that spans more than one line is refused (graphs #802). - The ticket lint reads a path under
tests/as repository code, never corpus, so a ticket naming a folder such as
tests/fixtures/help/runs/is no longer refused at decompose (graphs #798). - Tests only: the store DDL tests pin only their own upgrade, and the decompose, tier, handoff and infrastructure tests
stop pinning constants, private helpers and prompt wording. They remove 44 tests and add 5, and the suite goes from
2,406 test functions at v0.42.0 to 2,389. No DDL or migration changed (graphs #804, graphs #805, graphs #806, graphs
#807).
coxswain-tools
cox chair freeze-lands [--for MINUTES] [--reason TEXT] [--off]sets, shows or lifts a land freeze kept as an
expiringland-freezerow in theleasestable (tools #1738). The land planner plans no land while it stands
(tools #1744), and the status line shows it (tools #1747).- A ticket or code quarantine with one attempt on its current body is relaunched, and the second attempt on that body
reaches the chair with the first and latest reasons (tools #1742). cox chair watchdogpushes when the loop is down or its lanes sit idle on a hold, as described above. It shipped in
the 0.42.0 packages (tools #1735) on the rules and readers that 0.42.0 described as groundwork.- A decompose that wrote an initiative with no tasks is reported as a hold with the requeue command as its remedy, and
cox route requeuereads the intake frominitiative.mdwhen no store row links it. This also shipped in the
0.42.0 packages (tools #1736). An initiative already done or dropped is left out (tools #1739). - The chair reads each todo initiative with its task rows' states (tools #1745) and closes one whose tasks are all done
or dropped, as done when any task is done and as dropped otherwise (tools #1761). cox runs triage TASK_ID [--json]prints a task's attempts, review, failed check, land refusal and surfaces: the pure
core shipped in the 0.42.0 packages (tools #1734), then came the store and git readers (tools #1740) and the command
(tools #1754).cox route paths --move OLD NEWswaps a repository prefix in the store rows or work files (tools #1743) and reports
what is still under OLD after--apply(tools #1748).- A notice is pushed once while its hold stands and pruned an hour after it clears, and the
no_run,steer_deferred
andheld_fileholds never push (tools ...
coxswain 0.42.0
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 thebaseand
localfolders 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), installs
coxon 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...