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,devalways 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
the
pytest --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.