feat: hand a harness its own flags without a -- separator - #30
Merged
Merged
Conversation
`reach build-box claude --dangerously-skip-permissions` is the command an
operator actually wants to type, and until now it failed:
flag provided but not defined: -dangerously-skip-permissions
Usage of claude:
-allow-local-file-tools
...
Every harness flag needed `--` in front of it. That is a tax on every launch,
and the failure it produces when forgotten is worse than verbose: it prints
reach's own usage block under the flag package's message, which reads as reach
rejecting a Claude Code flag rather than merely declining to forward one. The
README has documented `reach build-box --mode mirror --fresh claude --resume`
since the target-first form landed, and `splitTargetArgs` says in a comment
that `--resume` is handed to claude. It was not; the launcher rejected it one
layer further down.
parseHarnessFlags replaces parseFlags in the seven launchers. It walks the
arguments itself rather than handing them to flag.Parse wholesale: a name reach
defines for that launcher is consumed — with the next word too, unless the flag
is boolean or was written with `=` — and everything else is forwarded in the
order it was typed, so a flag and its value stay adjacent.
Two names stay reach's. `--` still separates, because forwarding by default
creates exactly one collision — a harness flag whose name reach also defines —
and there has to be a way to say which was meant: `reach claude -- --session x`
gives --session to Claude Code. And -h/--help stay reach's, so that
`reach claude --help` describes the launcher rather than starting the agent;
its usage block now names reach's flags and says what happens to the rest.
The other commands keep rejecting an unknown flag. `reach up`, `exec`, `fs`,
`opencode`, `harness verify` have no other program to hand one to, and a
silently swallowed `--nmae` is the failure parseFlags was written to prevent.
A drift test parses the launchers' source and fails if one goes back to
parseFlags, since a new adapter added that way would compile, pass its own
tests, and refuse its harness's flags.
Verified end to end with a fake harness on PATH against a local:// session:
`reach crush --session e2e --force --yolo --model sonnet -p "fix it"` reaches
crush as `--yolo --model sonnet -p "fix it"`, in both the target-first and
session-name forms, and `-- --session theirs` still reaches it verbatim.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ni2bjhvWtMMCFN5BD6zxmP
A minor rather than a patch because the command line gains behaviour: a flag reach does not define is now forwarded to the harness instead of rejected, so `reach build-box claude --dangerously-skip-permissions` launches an agent where it used to print "flag provided but not defined". Nothing that worked before means anything different. reach consumes exactly the flags it always defined for each launcher, `--` still hands everything after it to the harness, and no session document, capability document or on-disk format moves — a session written by this build loads unchanged in 0.4.0 and the other way round. Nothing at a harness seam changed, which this project's entries are expected to say one way or the other: the release is above the adapter layer and touches no adapter, seam or launch guard. Version bumps for the helper cache path, which is keyed on it so a release never reuses a stale remote binary. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ni2bjhvWtMMCFN5BD6zxmP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
reach build-box claude --dangerously-skip-permissionsfailed until now:Every harness flag needed
--in front of it. That is a tax on every launch, and the failure it produces when forgotten is worse than verbose — the flag package's message lands on top of reach's own usage block, which reads as reach rejecting a Claude Code flag rather than merely declining to forward one.It was also already documented as working. The README has carried
reach build-box --mode mirror --fresh claude --resumesince the target-first form landed, and the comment onsplitTargetArgssays--resumeis handed to claude. It was not: the launcher rejected it one layer further down.parseHarnessFlagsreplacesparseFlagsin the seven launchers (claude, codex, kimi, goose, gemini, crush, grok). It walks the arguments itself instead of handing them toflag.Parsewholesale: a name reach defines for that launcher is consumed — with the next word too, unless the flag is boolean or was written with=— and everything else is forwarded in the order it was typed, so a forwarded flag and its value stay adjacent.Two names stay reach's.
--still separates, because forwarding by default creates exactly one collision — a harness flag whose name reach also defines — and there has to be a way to say which was meant:reach claude -- --session xgives--sessionto Claude Code. And-h/--helpstay reach's, soreach claude --helpdescribes the launcher rather than starting the agent; its usage block now names reach's flags and says what happens to the rest.The other commands keep rejecting an unknown flag.
reach up,exec,fs,opencodeandharness verifyhave no other program to hand one to, and a silently swallowed--nmaeis the failureparseFlagswas written to prevent.A drift test parses the launchers' source and fails if one goes back to
parseFlags, since a new adapter added that way would compile, pass its own tests, and refuse its harness's flags.Also cuts 0.5.0: minor rather than patch because a command line that was an error now launches an agent. Nothing that worked before means anything different, and no on-disk format moves — a session written by this build loads unchanged in 0.4.0 and the other way round.
How this was verified
Unit tests cover forwarding, order preservation, string-vs-bool value consumption,
--,--help, and reach's own flags still erroring on a missing value.End to end with a fake harness on
PATHagainst alocal://session, in both the session-name and target-first forms:reach's own
--sessionand--forcewere consumed; everything else arrived verbatim.Checklist
make checkandmake lintpass (0 issues, tests under-race)make integrationpasses, if this touches transports or file operations — n/a, this is argument parsing above the transport layerHarness adapter: no seam is touched. This changes what reaches a harness's argv, not how reach gets inside it — no adapter, seam or launch guard is modified, so no new conformance test or
docs/RESEARCH.mdentry is owed.🤖 Generated with Claude Code
https://claude.ai/code/session_01Ni2bjhvWtMMCFN5BD6zxmP