Skip to content

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 *.conf drop-ins, the last to set PATH winning (tools #1793). The doctor's chair unit PATH row checks that value and names the file it came from, and cox setup migrate rewrites a drop-in that still names the old checkout root with a new chair_unit_dropin step, 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 migrate recreates 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 as postgres. A group counts when every distribution it names has a dist-info folder in the old .venv; when the old venv cannot be read it installs .[dev] and prints migrate: <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 --regen option (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.