The Verbose Flag That Kept Runs Alive
A one-flag compatibility fix that stops a CLI change from halting whole Gantry runs.
Gantry builds software by driving coding agents, and its most-used agent is Claude Code, which Gantry starts as a command-line process. To stream an agent's output as it happens, Gantry runs that CLI with `--print` and `--output-format stream-json`. A recent Claude Code release changed the rules for that exact combination: it now refuses to run unless `--verbose` is also passed, and instead of starting it prints an error and exits before producing any output. Gantry, seeing nothing come back, concluded the agent had never started — the signature it uses for a headless authentication failure — and because the same empty result recurred on every unit, it stopped entire runs as though something systemic had broken. Nothing was actually wrong with authentication; the CLI had simply tightened its argument rules. This job made Gantry pass `--verbose` in that invocation, so the harness keeps working against the newer CLI while behaving identically on the older ones that never needed the flag.
Build
The plan was a single sprint because the fix was genuinely indivisible — one argument added in one place — and nothing ever sent it back. The only real constraint was position: the new flag had to sit with Gantry's own arguments, ahead of the user's escape-hatch arguments.
The planner treated this as one low-effort sprint, and the brief says why: the change is self-contained with no follow-on, so there was nothing to divide across agents. That makes the architectural question not whether the cut was good but whether one sprint was the right container for the whole thing — and it was, because the sprint had two small jobs that belong together: add the flag, and pin it so a later edit can't drop it again.
Where the work could have gone wrong was position, not presence. The claude harness appends a user escape hatch (`LOOM_CLAUDE_ARGS`) at the very end of the argument list, so a user's extra flags always come last. The brief required the new flag to go in before that hatch, and named an existing test that asserts the final two arguments are exactly the escape-hatch ones — an invariant that had to keep holding with the flag present. That ordering was the single spot a careless change would have broken, and the tests were written to hold it.
Nothing sent the sprint back: no gate failure, no repair, no re-plan. For a problem the brief itself declared indivisible, that is the expected shape — there was no boundary under load because there was no boundary to draw, only a single low-effort change to land and lock in.
Feature
Before this job the claude harness assembled its stream-json command without `--verbose`, which the newer CLI rejects outright. The harness now always includes it, placed ahead of the user escape hatch, guarded by a test that fails if it ever goes missing. Both are still in the tree today.
Before this change, `src/engine/harness/claude.rs` built the agent command with `--print` and `--output-format stream-json` but no `--verbose`. Against a Claude Code that demands the three together, that command aborted at argument parsing and returned nothing, which Gantry read as the agent never having run. The failure stayed invisible until a run halted, because textually the command still looked fine.
The answer is a single unconditional argument. The harness now emits `--verbose` alongside its own stream-json flags, positioned before the `LOOM_CLAUDE_ARGS` escape hatch so a user's own flags still trail everything else. A guard test asserts that whenever the invocation pairs `-p` with `--output-format stream-json` it also carries `--verbose`, so a future edit can't quietly remove it. The module's comments record the reasoning: mandatory on newer CLIs, a harmless no-op on older ones that already accepted the pair.
Standing in the tree at today's `HEAD`, all of it holds. The flag is still constructed at the documented position, the guard test is still present under its explaining comment, and every line the job introduced survives even though the file has been revised many times since — later work built around the change rather than over it.