Skip to content

0.17.0

Cut on 2026-09-25. coxswain-cartridges, coxswain-graphs and coxswain-tools are tagged v0.17.0. coxswain-crew is unchanged and stays pinned at v0.12.0. coxswain-tools is published to PyPI from its tag. Status: beta. This release carries two headlines.

Run records live in the store alone. A run writes no usage file, no per-phase manifest file and no per-call file. Its totals, phases, gate decisions, ledger rows and calls are rows in the run store (runs/cox.db). Each call's stream events are compacted into runs/traces when the run ends. The task records under runs/<run>/tasks/ stay files, because landing and resuming read them.

A vendor-neutral core, with liveness from the store. Every integration that names a vendor is a plugin in the public ppfenning/coxswain-plugins, found by entry point: intake sources, system-one backends and runners, as forges and trackers already were. A run holds a lease in the store and renews it with a heartbeat, so "is this lane alive" no longer depends on a process id. One live run per epic is enforced by that lease, and a stale run's writes are fenced.

This workspace ran on the store alone from 8:01 AM ET on the day of the cut: 59 runs and 763 model calls, with nothing written outside the store but task records and logs. Parallel runs are called lanes now, in the regatta vocabulary. The umbrella's release-check no longer files a bullet under the umbrella for naming coxswain.forges or coxswain-sources, and keeps a dotted path inside its sentence (coxswain #96). The docs gain a Plugins page (coxswain #97) and a Dashboards page (coxswain #103).

Upgrading from 0.16.0. Nothing to change in the profile. zstandard is now installed with coxswain-tools. System one is off unless a provider profile configures a backend. Install coxswain-plugins in place of the archived coxswain-sources for the Jira and Asana intake sources. Record files from earlier runs are still read first. python -m harness.store_backfill imports them into the store, and archiving them is optional. The store migrates itself to schema version 3 on the next run; the migration adds a nullable runs.host. The new extras are optional: postgres to read a Postgres store, parquet for Parquet traces, and lake for the Iceberg lake.

coxswain-cartridges

  • core.manifest.append_ledger writes a manifest's ledger rows without writing the manifest file, and record_run is the file write plus append_ledger (cartridges #160).
  • The provider profiles no longer turn system one on. The local kNN backend could not reach the graduation bar, and it is removed (cartridges #161).

coxswain-graphs

  • Store-only run records. The CLI prints a run's totals from the store and writes no usage file (graphs #372). The epic driver writes no per-phase manifest file and requires a store (graphs #374, #375). The runner writes no per-call file, and the CLI compacts the run's trace files into the trace store at run end (graphs #377). A single-graph run records to the store like a one-phase run (graphs #393).
  • Leases. Every run holds the store lease runs:<prefix> and renews it every 30 seconds from a heartbeat thread. A transient store error is retried (graphs #399). A second run of the same epic is refused while the lease is held, and the epic driver's writes are fenced by the lease's epoch (graphs #386). A run stopped with SIGTERM still records its end and releases its lease (graphs #370).
  • Plugins. Runners are found by name, with claude-code and anthropic built in; an unknown runner refuses instead of falling through to another (graphs #384). System-one backends are found by entry point, and the kNN and Jev built-ins are gone (graphs #382). A hosted backend reads its key only from system_one.key_env, never from the provider's own key (graphs #378).
  • An OpenAI-compatible runner for local models: a chat client, schema-shaped answers from plain text, and the runner (graphs #395, #397, #403). It is built in as runner: openai-compatible, and a hybrid profile hands its anthropic/ models to the Anthropic runner on the same run (graphs #414, #416).
  • Each call records a runner-neutral summary (tool uses, reads, result) in the store (graphs #380).
  • The store's tests run against a real Postgres in CI (graphs #405). A built-in model-tier system-one backend and an offline evaluator exist but are not turned on (graphs #388, #391).
  • An epic run branches from the repository's default branch, not from whatever the checkout has out (graphs #368).
  • A lint fix is amended into the task's commit, so a task branch stays one commit and lands without a hand squash (graphs #412).
  • python -m harness.store_copy copies a SQLite store into Postgres, idempotently (graphs #407).
  • The decompose ticket lint treats a URL route as notation and flags a ticket that names another repository's file (graphs #389, #401).

coxswain-tools

  • Reads for the store alone. A killed run's usage, a live run's phases, and a finished run's calls and last tools all come from the store (tools #563, #565, #576). cox runs trace uses each call's stored summary (tools #586).
  • Liveness from the lease. Route status, the launch guard, runs top and the docket decide a run is live from its store lease, with the pid as a fallback for older runs (tools #588). One read of the leases table serves a whole docket (tools #610). The docket shows each busy lane's heartbeat age, and the regatta and runs top flag a lane whose heartbeat has stopped (tools #593, #598).
  • Lanes. cox says lanes where it said runs in flight (tools #584). cox runs wait blocks until a busy lane finishes and prints its outcome. cox stats lanes reports busy and idle lanes by hour (tools #602, #604, #605).
  • cox runs land never moves the checkout. The pull-request branch is built and checked in its own worktree, which links the repository's .venv (tools #570, #572).
  • A launch never reuses a run number whose files are gone (tools #581).
  • cox runs land fast-forwards a local default branch that is behind its remote before it builds the pull request, and refuses one that has diverged (tools #632).
  • cox setup doctor lists the plugins each side can load (tools #591).
  • The run store can be read by URL, SQLite or Postgres, with the postgres extra (tools #596, #600, #609, #622). The README says what each command reads from a shared store (tools #613).
  • cox setup doctor has a store row: the store's kind and run count, or why it did not answer, never the URL (tools #624). It also says whether Parquet traces are readable (tools #630).
  • CI runs the lake, Parquet and Postgres tests in an extras job with a Postgres 16 service (tools #623).
  • zstandard is a base dependency, because a run's traces live only in the trace store (tools #578). The Go port is removed: Coxswain stays with Python (tools #579).
  • Usage assessment reads each run record once, only inside the last seven days (tools #574).
  • The route lint treats a URL route as notation and flags a ticket that names another repository's file (tools #594, #607). cox route sync's help names the profile's tracker (tools #561).

Groundwork for 0.18.0 (in the tags, not yet wired)

  • Parquet traces: the column mapping, the pyarrow extra, the traces-root resolver, and reading and writing a run's Parquet file with the zstd day files as fallback, in graphs (graphs #409, #411, #418, #420). Compaction does not write Parquet yet. tools reads a run's Parquet trace file before the zstd day files (tools #615, #617, #628). tools reads them from the profile's traces_url, once per run (tools #646).
  • The Iceberg lake: the lake extra (pyiceberg with a SQL catalog, duckdb), table definitions, and a sync that appends each ended run's rows once behind a per-table mark (tools #619, #621, #626). Also trace registration in place with add_files, read-only DuckDB queries over the lake tables, and lake doctor checks (tools #634, #636, #638). No cox lake command yet.
  • Superset: a compose deploy under deploy/superset with DuckDB and read-only access to the runs directory (coxswain #99, #104). A Coxswain dashboard as code, installed by a one-shot service once Superset is healthy (coxswain #101), and a Dashboards docs page (coxswain #103).
  • Lanes on several machines: runs gain a nullable host column (graphs #422, migration 0003), and tools reads the lanes a shared store says are live. The docket and runs top can render a remote lane, but the commands do not list them yet (tools #640, #642, #644).

Known gaps

Lanes on several machines are the headline of 0.18.0. The chair stays on the machine that runs cox and keeps the workspace; other machines run lanes against a shared Postgres store. The pieces above are in the tags, but nothing launches a lane on another machine yet. Graphs does not yet compact traces to Parquet, and cox lake has no commands yet. Superset charts do not save a query context, so the chart data API cannot run them. The dashboard itself builds its queries in the browser. System one is parked: neither the local kNN nor a cheap model reached the graduation bar offline.

Components

coxswain-graphs carries the cutover, the leases, the plugin lookups, the OpenAI-compatible runner and the store copy. coxswain-tools carries the store-only reads, liveness from the lease, the lane commands, the store URL and the lake modules. coxswain-cartridges carries append_ledger and the profiles without system one. The umbrella carries the Superset deploy and dashboards. coxswain-crew is unchanged and stays pinned at v0.12.0.