Repository navigation
A flag where a value belongs is refused in all three commands — park Atlas --lane --dry-run no longer parks with the lane set to --dry-run — and --flag=value is the spelling that gives a --led value back, read through one flag_value held byte-identical in park, resume and status - #37
Conversation
…park Atlas --lane --dry-run` no longer parks with the lane set to `--dry-run` — and every value-taking arm takes `--flag=value`, which is how a value that really does begin with `-` is still said PROPOSAL, awaiting Brett Heap's ruling on the note PR #17 left: "a value beginning with `-` is still accepted (`park Atlas --lane --dry-run` runs a REAL park with lane `--dry-run`); refusing `-*` forecloses dash-led values and the arms have no `=` form." He has ruled nothing about it; this branch exists so that the ruling can be made against something concrete, and if it goes the other way the whole of it is one revert. THE TWO HALVES ARE ONE COMMIT because neither is safe alone: refusing `-*` on its own takes a value away with nowhere to put it, and the refusal's own words name the `=` spelling as the exit, so the exit has to exist the moment the refusal does. All nine value-taking arms across the three commands — `park`'s --lane, --repo, --name | --project; `resume`'s --repo, --workspace, --org, --name | --project; `status`'s --repo, --name | --project, none of them in the shared estate resolver — now read their value through `flag_value`, a new function held BYTE-IDENTICAL in the three files the way `expand_home` already is, and outside that block for the reason the block's own header gives: it answers "which estate is this", and this answers "did this flag get a value". It asks the arity question #14 asked, the emptiness question #17 added, and one more: a next argument beginning with `-` is a flag, and nothing here swallows the next word whatever it is. Each arm keeps its own words and its own `die` at exit 2; the `-`-led refusal adds one sentence naming the spelling that does take such a value (`--lane=--dry-run`), asserted per flag so a hint cannot name the wrong one. `--flag=value` takes whatever follows the FIRST `=` and asks nothing about it, so `--lane=--weird` and `--repo=https://…` both arrive whole; `--flag=` is an empty value and is refused as #17 refuses `""`. Each `--help` gains one line for the `=` spelling and nothing else — the blocks document one form per flag, and two of them do not list `--repo` at all; README's "Using them" block and AGENTS.md rule 2 show example invocations in the spaced form, every one of which still works, and README names the three `--help`s as the reference, so neither is touched and no line cap moved — both sit exactly at their caps, so documenting the `=` spelling there is a cap bump for whoever rules it worth one. THE STANDARD'S OWN PYTHON HAS ALWAYS DONE THIS: openRepoShape's `scripts/repo_shape.py` (`checked_value`, "ARGUMENT INJECTION IS THE THREAT … A value that begins with `-` is therefore refused outright") and `setup-project.py` ("THE HOUSE PATTERN, COPIED RATHER THAN IMPORTED") refuse a `-`-led value, and their argparse entry points take `--flag=value` for free — this makes the bash commands agree with them. The standard's own bash front door, `openRepoShape --org`, does not yet (`ORG="${2:?…}"`, no `=` arm), and that is a pull request there, then a pin bump here. PROVED IN A TEMP ESTATE with a probe Makefile, before and after. Before: `park Atlas --lane --dry-run` printed `make park ARGS='--lane --dry-run'` and RAN it, exit 0 — `DRY_RUN` never set, the lane being the flag the person typed; `resume Atlas --org --dry-run` went past the rehearsal it was asked for and on to the workspace record, which on a machine that has one is a clone; `status --name --fetch` fetched nothing and refused about an estate named `--fetch`. After: each is `REFUSED: <flag> needs a value: …`, exit 2, from the argument loop with an empty stdout, and `park Atlas --lane=--dry-run --dry-run` gives `ARGS='--dry-run --lane --dry-run'` — the rehearsal flag and the `-`-led lane both through. Tests: the hygiene test now reads every arm through `flag_value` and holds the case label to both spellings of every flag it names (the count is still nine, hand-kept), and a sibling holds the function byte-identical and asserts the three questions it asks. Eighteen parametrized command cases across `park` and `resume` — nine flags, a `-`-led value refused with nothing run, and the `=` form byte-for-byte the same run as the spaced one, `--flag=` refused — plus `status`'s own three flags in both shapes, `park`'s lane case end to end, and `resume --workspace=` / `--org=` doing the work those flags name rather than being echoed. Every new test fails against the base, proved against a scratch tree with origin/main's own `park`, `resume` and `status` dropped in: 24 failed — the 23 tests this commit adds, and `status`'s own `test_refusals_read_nothing`, which took the flag-shaped readings. Suite at the branch tip, which adds no test after this commit: 571 passed, 0 skipped. REBASED FOR THE FIRST PUSH (2026-09-14), from `a40cbb7` onto `8a36eb3` — 35 commits, in four steps as `main` moved under it (`d4b5710`, then `7efc850`, then `63a74af`, then `8a36eb3`) and NOT ONE CONFLICT at any of them. Unpushed history, tree unchanged, and EVERY NUMBER ABOVE IS THE RE-MEASUREMENT on that base: the probe was run again against origin/main's own three commands, the 24 recounted, the suite re-run. `park` and `resume` had not moved on `main` at all — both byte-identical to `a40cbb7`'s — so their arms replayed untouched; `status` grew from 2,619 lines to 5,767, and all of that growth lands BETWEEN the regions this commit edits, which is why there was nothing to resolve: the globals at line 102 and the OPTIONS block at 222 sit above it, `flag_value` and the argument loop at 5,631 and 5,704 below it. The shared estate resolver is byte-identical to `main`'s in all three files — `flag_value` is held OUTSIDE it, which is the whole reason it is a second byte-identity test and not a line in the first — and `expand_home` is still byte-identical in `status` and `resume`. README.md and AGENTS.md are untouched and still sit exactly at their caps, which have moved to 409 and 224, so this branch still moves no cap. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…park --help` shows the `=` spelling on a flag whose value stays in the command, and `flag_value`'s three answers are declared with the other globals The independent REPORT-ONLY review of this branch (2026-09-11; 168 runs across the twelve flag spellings and fourteen forms, main against the branch: every accepted form byte-identical to the spaced one, every `-`-led value refused with an empty stdout, the `--` passthrough and the no-value flags unchanged; 24 tests failing against the main of that day, `a40cbb7`; verdict merge after fixes), every finding taken. The one that mattered: `test_every_value_taking_arm_reads_its_value_through_flag_value` counted only arms that read `$FLAG_VALUE`, so a tenth arm written the old way — `X="$2"`, no emptiness test, no `-` test, no `=` spelling — left the count at nine and passed the suite, where the test it replaced had caught exactly that; it now names any `="$2"` outside `flag_value` itself, proved by injecting such an arm into a scratch copy of `park` (`AssertionError: park: TENTH="$2"; shift 2 ;; takes $2 straight from the command line`). The second: for `--lane` the `=` spelling buys `park`'s own parsing and not the extension's — the recipe word-splits `$(ARGS)`, so `--lane=--dry-run` hands the extension the same two words the accident did — which the lane test's docstring now says instead of claiming the value "survived", and `park --help`'s example moves to `--repo=<owner/name>`, a value that never leaves the command; `status --help`'s example moves to `--project=<Name>`, the one value-taking flag its OPTIONS block lists. And `FLAG_VALUE`, `FLAG_SHIFT` and `FLAG_HINT` are declared beside the other globals in all three files, so a `die` that reached `$FLAG_HINT` before any arm had asked would print an empty hint under `set -u` rather than "unbound variable" at exit 1, the one exit these arms must not produce — unreachable today, since every arm asks first, and cheap to keep that way. Suite: 571 passed. REBASED WITH ITS PARENT FOR THE FIRST PUSH (2026-09-14), onto `8a36eb3`: unpushed history, tree unchanged, no conflict. The review's finding above was re-proved on that base rather than carried over — a tenth arm written the old way, injected into a scratch copy of this commit's `park`, still gives `AssertionError: park: TENTH="$2"; shift 2 ;; takes $2 straight from the command line` — and the suite count is the re-measurement. The three `--help` examples this commit moves still name what they claim on that base: `--repo=<owner/name>` is a value that never leaves `park`, and `--repo` is listed in neither `park`'s OPTIONS block nor `status`'s, whose one value-taking flag is still `--project=<Name>`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Sorry @brettheap, you've used your own review budget of 250,000 diff characters for the last 7 days.
You can request another review in 6 hours and 21 minutes by commenting @sourcery-ai review. Upgrade to get a review now.
Reviewer's GuideThe PR fixes option-value injection across park, resume, and status by introducing a byte-identical parser that rejects missing, empty, and dash-prefixed spaced values, supports --flag=value, and prevents execution or resolution before refusal. Tests cover all value-taking flags, command-specific behavior, side effects, parser consistency, and future-arm hygiene. File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
🔵 Needs a closer look
The hygiene detector must scope its FLAG_VALUE="$2" exemption to the helper body and detect general assignments outside it.
Pull request overview
This PR hardens value-taking option parsing across park, resume, and status, adding safe --flag=value support.
Changes:
- Reject missing, empty, and flag-shaped values.
- Add consistent
flag_valueparsing and help documentation. - Expand command and hygiene test coverage.
File summaries
| File | Summary |
|---|---|
tests/test_status_command.py |
Tests status parsing, equals-form values, and refusals. |
tests/test_repo_hygiene.py |
Verifies parser consistency; moderate issue remains in the detector’s exemption logic (1 vote). |
tests/test_park_resume_commands.py |
Tests refusal and equals-form behavior for park and resume. |
status |
Adds validated option parsing and equals-form values. |
resume |
Adds validated value parsing for all value-taking options. |
park |
Adds validated option parsing and equals-form values. |
Review details
Suppressed comments (1)
tests/test_repo_hygiene.py:416
- This detector exempts any line exactly matching
FLAG_VALUE="$2"without proving it is insideflag_value(). A future value-taking arm could bypass the helper with that same assignment andcontinue, leavingfound == 9so the regression passes; the uppercase-only regex also missesvalue="$2". Scope the exemption to the helper body and match a general shell variable assignment outside it.
if re.match(r'\s*[A-Z_]+="\$2"', line):
assert line.strip() == 'FLAG_VALUE="$2"', (
f"{name}: {line.strip()} takes $2 straight from the "
f"command line; every value-taking arm reads its value "
f"through flag_value")
- Files reviewed: 6/6 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…tring, so an arm that writes `FLAG_VALUE="$2"` itself is caught rather than excused, and `value2="$2"` and `X="${2}"` are the same arm in a different case and in braces
Copilot's review of #37 (2026-09-14), one suppressed comment, no inline
comments; VERIFIED BEFORE ANYTHING MOVED and real in both of its halves.
`test_every_value_taking_arm_reads_its_value_through_flag_value` exempted the
TEXT `FLAG_VALUE="$2"` wherever it appeared, without asking whether it was
inside `flag_value`. So a tenth arm that wrote that very line and then read
the value back as `TENTH="${FLAG_VALUE}"` was exempted at the first test and
invisible to the second — the counter matched `="$FLAG_VALUE"` and not the
braced spelling — leaving `found` at nine and PASSING: the same silent pass
the 2026-09-11 review caught in the version before this one, reached through
a different door. Its other half is the alphabet: `[A-Z_]+` never matched
`value2="$2"`, which is one arm in a different case with a digit in its name.
Proved on a scratch copy of `park` against the pushed detector, which caught
one of four shapes — `TENTH="$2"` — and MISSED the other three; the detector
here catches all four, and the clean tree still passes.
THE EXEMPTION IS NOW A RANGE. `flag_value() {` to the `}` in column 0 that
closes it is located once per file, asserted to be exactly one definition and
to close, and "the legitimate read" means a line number inside it. The
assignment this reads is any shell variable in either spelling of `$2`
(`[A-Za-z_][A-Za-z0-9_]*="\$\{?2\}?"`), and the counter reads `${FLAG_VALUE}`
as well as `$FLAG_VALUE` so that bracing hides nothing either.
THE MATCH STAYS ANCHORED at the start of the statement, and that is the one
thing the comment asked for that is NOT taken as written: "match a general
shell variable assignment outside it" as an unanchored search makes a finding
of `local file="$1" key="$2" line` — a function reading its own parameters,
which is not an arm and never was. There are thirty-six such lines across the
three files — two in `park`, six in `resume`, twenty-eight in `status`, counted on the
comment-stripped text this reads and outside `flag_value`'s own body — so an
unanchored search would report thirty-six failures on a tree with no fault in
it at all, and the anchored one reports none of them. The arms are at the top level of the argument loop, where `local`
is not even legal, so anchoring costs the check nothing it was buying.
The count is still nine and still hand-kept. No cap moves: no document line
moves. Suite: 571 passed, 0 skipped.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Copilot's first round on this branch (2026-09-14): one suppressed comment, no inline comments. TAKEN, in The comment, verbatim:
Both halves reproduce. Against the pushed detector (
One of four, and the second row is the comment's own scenario: the arm writes the exempted line itself, then reads the value back in the braced spelling, which the counter's The exemption is a LINE RANGE now, not a string. One part is not taken as written, and the difference is not convenience. "Match a general shell variable assignment outside it" as an unanchored search makes a finding of The count is still nine and still hand-kept. No document line moves, so no cap moves. Suite: 571 passed, 0 skipped. 🤖 Generated with Claude Code |
|



Two commits, the second the fix round of this branch's independent review. The two halves of the first are ONE commit because neither is safe alone.
THE HOLE, and what it cost. Every value-taking arm tested
[ $# -ge 2 ] && [ -n "$2" ], which asks whether a second argument EXISTS and whether it is non-empty and never what it HOLDS — sopark Atlas --lane --dry-runran a REAL park with the lane set to--dry-run: the flag a person typed was swallowed as a value,--dry-runitself never took effect, and the only sign was a WIP commit subject labelled with it. PR #17 named the hole and left it open; this is the ruling made against something concrete, and if it goes the other way the whole of it is one revert. Proved again on this base, in a temp estate with a probe Makefile, against origin/main's OWN three commands:park Atlas --lane --dry-runprintedmake park ARGS='--lane --dry-run'and RAN it, exit 0;resume Atlas --org --dry-runwent past the rehearsal it was asked for and on to the workspace record, which on a machine that has one is a clone;status --name --fetchfetched nothing and refused about an estate named--fetch. After: each isREFUSED: <flag> needs a value: …, exit 2, from the argument loop with an EMPTY stdout, and the refusal's own sentence names the spelling that does take such a value. THE TWO HALVES ARE ONE COMMIT because refusing-*alone takes a value away with nowhere to put it, and the refusal's words name the=spelling as the exit, so the exit has to exist the moment the refusal does. All nine value-taking arms —park's--lane,--repo,--name | --project;resume's--repo,--workspace,--org,--name | --project;status's--repo,--name | --project, none of them in the shared estate resolver — read their value throughflag_value, a new function held BYTE-IDENTICAL in the three files the wayexpand_homealready is, and OUTSIDE that block for the reason its own header gives: the resolver answers "which estate is this", and this answers "did this flag get a value". It asks the arity question #14 asked, the emptiness question #17 added, and one more: a next argument beginning with-is a flag, and nothing here swallows the next word whatever it is. Each arm keeps its own words and its owndieat exit 2; the--led refusal adds one sentence naming the spelling that does take such a value, asserted per flag so a hint cannot name the wrong one.--flag=valuetakes whatever follows the FIRST=and asks nothing about it, so--lane=--weirdand--repo=https://…both arrive whole;--flag=is an empty value and is refused as #17 refuses"".park Atlas --lane=--dry-run --dry-rungivesARGS='--dry-run --lane --dry-run'— the rehearsal flag and the--led lane both through — andstatus --name=--fetchis read as a NAME, refused as "no estate named '--fetch'" and not as a flag. THE STANDARD'S OWN PYTHON HAS ALWAYS DONE THIS: openRepoShape'sscripts/repo_shape.py(checked_value, "ARGUMENT INJECTION IS THE THREAT … A value that begins with-is therefore refused outright") andsetup-project.pyrefuse a--led value, and their argparse entry points take--flag=valuefor free; the standard's bash front door,openRepoShape --org, does not yet —ORG="${2:?--org needs a value}", no=arm, still so at the pinnedba14e1d— and that is a pull request THERE, then a pin bump here. Each--helpgains one line for the=spelling and nothing else. Tests: eighteen parametrized command cases acrossparkandresume(nine flags, a--led value refused with nothing run, the=form byte-for-byte the same run as the spaced one,--flag=refused), plusstatus's own three flags in both shapes,park's lane case end to end,resume --workspace=/--org=doing the work those flags name rather than being echoed, and two hygiene tests. Suite at the branch tip, which adds no test after this commit: 571 passed, 0 skipped.The fix round of the independent REPORT-ONLY review of this branch (2026-09-11; 168 runs across the twelve flag spellings and fourteen forms, main against the branch: every accepted form byte-identical to the spaced one, every
--led value refused with an empty stdout, the--passthrough and the no-value flags unchanged; verdict MERGE AFTER FIXES), every finding taken. THE ONE THAT MATTERED:test_every_value_taking_arm_reads_its_value_through_flag_valuecounted only arms that read$FLAG_VALUE, so a TENTH arm written the old way —X="$2", no emptiness test, no-test, no=spelling — would have left the count at nine and PASSED the suite, where the test it replaced had caught exactly that. It now names any="$2"outsideflag_valueitself, and the count is still nine and still hand-kept, so a tenth arm is read rather than silently passed; re-proved on THIS base by injecting such an arm into a scratch copy ofpark—AssertionError: park: TENTH="$2"; shift 2 ;; takes $2 straight from the command line. THE SECOND: for--lanethe=spelling buyspark's own parsing and NOT the extension's — the recipe word-splits$(ARGS), so--lane=--dry-runhands the extension the same two words the accident did — which the lane test's docstring now says instead of claiming the value "survived";park --help's example moves to--repo=<owner/name>, a value that never leaves the command, andstatus --help's to--project=<Name>, the one value-taking flag its OPTIONS block lists. THE THIRD:FLAG_VALUE,FLAG_SHIFTandFLAG_HINTare declared beside the other globals in all three files, so adiethat reached$FLAG_HINTbefore any arm had asked prints an empty hint underset -urather than "unbound variable" at exit 1 — the one exit these arms must not produce; unreachable today, since every arm asks first, and cheap to keep that way. Suite: 571 passed, 0 skipped.BASED ON THE MAIN OF THE MOMENT. The branch was built on
a40cbb7and rebased onto8a36eb3for this first push — 35 commits, in four steps asmainmoved under it, and NOT ONE CONFLICT at any of them:parkandresumehad not moved onmainat all (both byte-identical toa40cbb7's), andstatus's growth from 2,619 lines to 5,767 lands BETWEEN the regions these commits edit — the globals at line 102 and the OPTIONS block at 222 above it,flag_valueand the argument loop at 5,631 and 5,704 below it. Every number here is the re-measurement on that base, not one carried over. THE BITE: all 24 of these tests FAIL against origin/main's ownpark,resumeandstatus— the 23 the first commit adds andstatus's owntest_refusals_read_nothing, which took the flag-shaped readings — measured in a scratch tree with main's three commands dropped in beside this branch's tests:24 failed, 436 passed. Suite on the branch: 571 passed, 0 skipped, against main's 549 collected; 23 test ids added, one renamed away (test_every_value_taking_arm_refuses_an_empty_value, which this replaces). INVARIANTS, each asserted by the suite and checked again by hand: the shared estate resolver byte-identical across the three AND byte-identical to main's, because this branch does not touch it;expand_homebyte-identical instatusandresume;flag_valuebyte-identical in all three, which is the new test;bash -nclean on all three; no${x,,},mapfile,declare -Aorlocal -n, so it parses under macOS bash 3.2; every"${arr[@]}"guard is main's own, since this branch adds no array. NO CAP MOVES: README.md and AGENTS.md are untouched and still sit exactly at their caps, 409 and 224.Ruling: "ruling (c): take dash-values as built, push and PR" — Brett Heap, 2026-09-14.
Lane: openrepotools-3 (openRepoTools-3)
🤖 Generated with Claude Code
Summary by Sourcery
Harden value-taking option parsing across the estate commands while adding an explicit equals-sign form for values that begin with dashes.
New Features:
--flag=valuesyntax for all value-taking options inpark,resume, andstatus, including values beginning with-.Bug Fixes:
Enhancements:
flag_valuehelper shared across the three estate commands.Documentation:
--flag=<value>spelling.Tests:
park,resume, andstatusbehavior.