Retiring Gantry's Hard-Coded Harness Default
A hand-editable global file, a first-run picker, and a config command replace the assumed Claude backend.
0 milestones 5 sprints
3 plan 5 execute 5 review
53m 51s total 8m 28s per sprint
Gantry runs coding agents through named harness backends such as `claude`, `codex`, `opencode`, and `gemini`. Before this job, ordinary build roles fell through to a compiled default when no command flag or plan pin named a harness, which made a user preference something to repeat rather than a setting Gantry could remember.
This job replaced that assumption with a global config file, startup resolution based on installed harnesses and terminal context, build-role precedence that includes the new tier, a repo-free `gantry config` command, and docs that explain the result. The current tree still carries those pieces, although the original global harness file has since grown into the wider global settings surface.
How this walkthrough is structured
Feature
What did the run build and ship?
The tree now treats build harness choice as a user-owned setting, resolved through documented tiers instead of a hidden compiled default.
Build
How did Gantry structure the work?
The run built the global default from storage through startup resolution, precedence, command dispatch, and docs, with the only recorded re-plan attached to the fallback resolver.
Milestone 1
Configurable global default harness
The milestone's result is still visible as a layered harness-selection system. `src/engine/global_config.rs` loads and writes the global settings file, `src/engine/startup_fallback.rs` resolves the last fallback, `src/engine/harness/mod.rs` applies the role order, and `src/commands.rs` exposes the config command. The same behavior is documented in the harness guide and help text, while later work has expanded the config file beyond this milestone's original default-harness purpose.
The milestone bundled the whole product surface because every sprint depended on a shared answer to the same question: what build harness should Gantry use when the run itself says nothing. Its scope was still decomposable because each child unit owned a distinct boundary, from file storage to fallback selection to command and documentation. The run facts show no failed gates or retries, but they do show a re-plan after startup fallback resolution. That places the architectural adjustment at the point where the decision core met the remaining integration work.
Global Config Store
`src/engine/global_config.rs` still owns `config.toml` in Gantry's resolved global config directory. The default harness helpers remain, but the module now carries schema-driven settings, role harness preferences, model pins, assistant preferences, and effective-value reporting as well. The original contribution therefore survives less as a small standalone file than as the foundation of Gantry's broader hand-editable settings surface.
This sprint was the base layer: a forgiving reader and writer for the global settings file. A fresh worker could hold it because no command dispatch, startup prompt, or role resolution depended on it yet; the unit only had to parse, validate, preserve unrelated content, and round-trip set and clear operations. Its boundary stayed quiet in the run facts, with no retry or re-plan attached to this sprint. That made it a stable API for every later unit to call.
Startup Fallbacks
`src/engine/startup_fallback.rs` remains a compact injectable decision core. It saves a sole installed harness or an interactive picker result, and treats non-interactive startup as an unsaved deterministic guess. `src/main.rs` wraps that core with terminal checks, install probing, persistence through global config, and user-facing reporting, so the no-default case no longer silently means a fixed backend.
The fallback sprint carried the hardest decision boundary in the plan: decide a job-wide harness when global config is unset, without blocking headless or assume-yes runs. Its brief kept the work testable by separating a pure resolver from the shell that probes installed harnesses, asks a terminal, and persists only deliberate answers. The sprint did not fail a gate, but review triggered a re-plan after it landed. That record says the resolver was concrete enough to change how the remaining precedence, command, and docs work should be framed.
Role Precedence Rules
`src/engine/harness/mod.rs` now resolves build roles through explicit command-line choices, remembered run selections, project pins, global per-role preferences, the global default, and the startup fallback. The older wrapper remains for compatibility, but the current startup path uses the source-aware form and passes the resolved fallback explicitly from `src/main.rs`. The assistant path is separate, so changing the build default does not rewrite the harness used by bare chat diagnostics.
This sprint connected the already-built storage and fallback answers to the role map used by real jobs. The unit was narrow because it did not need to invent either source; it only had to order them under existing CLI flags and plan pins, while leaving assistant selection out of scope. The run facts show no repair or re-plan here. Its boundary held because the hard choice had already been isolated in the previous resolver.
Config Command
`gantry config` still dispatches before the repository requirement and prints the resolved global config file. The original `gantry config harness <id>`, unset, and empty-value forms remain, now alongside broader role harness and model setters. Dedicated command coverage has moved under the current integration-test layout, so the old standalone test file is gone but the command surface is still exercised.
This sprint gave users an explicit control path without requiring a repository or a running build. The brief was bounded around early command dispatch, validation, setting, clearing, and state reporting against a temporary config directory. The run facts show this unit stayed green and needed no plan change of its own. It could lean on the storage API from the first sprint rather than editing TOML directly in command code.
Docs And Help
The harness guide still documents the build-role precedence from per-role flags down to startup fallback, names `gantry config`, and explains when Gantry saves or merely guesses a harness choice. The main help text still lists the config command and the harness-selection flags together. The docs have since been updated for newer assistant defaults, but they preserve this job's key distinction between build-role defaults and assistant harness selection.
The final sprint was intentionally downstream of the code: it updated docs and help only after the implemented precedence and fallback behavior existed. That made the unit small enough for a fresh worker because its proof was consistency with named command text and harness guidance, not new runtime behavior. The run facts show no repair or re-plan attached to it. As a closing unit, it translated the settled behavior rather than carrying design risk.