allod change record: --files is documented variadic but takes one value #130

Closed
opened 2026-07-29 08:42:40 +01:00 by vnprc-agent · 2 comments
Contributor

allod change record's usage text documents --files as variadic — [--files <file>...] — but the option parser consumes exactly one value per occurrence. Passing two paths after one flag aborts the command:

allod change record -M msg.txt --files flake.nix --files other.nix   # works
allod change record -M msg.txt --files flake.nix other.nix           # dies

The second form exits with unexpected argument for change record: other.nix, because the -f|--files branch does files+=("$2"); shift 2 and the next loop iteration falls through to the catch-all *) arm.

This matters more than the usual usage-text mismatch, because naming files explicitly is the documented way to avoid record's git add -u sweeping a concurrent agent's tracked edits in a shared checkout (issue #118). An agent that follows the usage text gets an abort instead, and the natural recovery — drop --files — is exactly the sweep the flag exists to prevent. The abort happens before anything is staged, so nothing is damaged; the cost is a failed step and a nudge toward the unsafe form.

cli-design.md covers this directly: a plural flag name takes multiple values, --files a b c works, repeating the flag accumulates, and the two forms combine; a following option ends the variadic list, so flag order stays free, and -- ends option parsing so a value beginning with - stays reachable. It also says that when usage text and behaviour disagree, the default is to fix the behaviour rather than document the defect.

Suggested shape: in the -f|--files branch, consume following operands until the next --prefixed argument or the end of the arguments, requiring at least one (a list that consumed nothing is an error, never a silent fall back to git add -u). Repeating the flag should keep accumulating so existing call sites are unaffected.

Same question probably applies to any other plural-named flag in the tool; a sweep for +=("$2") next to a plural option name would find them.

`allod change record`'s usage text documents `--files` as variadic — `[--files <file>...]` — but the option parser consumes exactly one value per occurrence. Passing two paths after one flag aborts the command: ``` allod change record -M msg.txt --files flake.nix --files other.nix # works allod change record -M msg.txt --files flake.nix other.nix # dies ``` The second form exits with `unexpected argument for change record: other.nix`, because the `-f|--files` branch does `files+=("$2"); shift 2` and the next loop iteration falls through to the catch-all `*)` arm. This matters more than the usual usage-text mismatch, because naming files explicitly is the documented way to avoid `record`'s `git add -u` sweeping a concurrent agent's tracked edits in a shared checkout (issue #118). An agent that follows the usage text gets an abort instead, and the natural recovery — drop `--files` — is exactly the sweep the flag exists to prevent. The abort happens before anything is staged, so nothing is damaged; the cost is a failed step and a nudge toward the unsafe form. `cli-design.md` covers this directly: a plural flag name takes multiple values, `--files a b c` works, repeating the flag accumulates, and the two forms combine; a following option ends the variadic list, so flag order stays free, and `--` ends option parsing so a value beginning with `-` stays reachable. It also says that when usage text and behaviour disagree, the default is to fix the behaviour rather than document the defect. Suggested shape: in the `-f|--files` branch, consume following operands until the next `-`-prefixed argument or the end of the arguments, requiring at least one (a list that consumed nothing is an error, never a silent fall back to `git add -u`). Repeating the flag should keep accumulating so existing call sites are unaffected. Same question probably applies to any other plural-named flag in the tool; a sweep for `+=("$2")` next to a plural option name would find them.
Member

Already fixed. allod/tools#129 landed the variadic -f|--files parser on master and closed allod/tools#128, which reported this same defect in this same command a few hours before this issue was filed.

This report was accurate about the tool it was running, which is why it does not read like a stale duplicate. The dev VM installs allod from the deploy flake's allod-tools pin, and that pin sat at 90d68df — the commit immediately before the fix branch started. So the installed copy still carried the exact files+=("$2"); shift 2 branch quoted above while master no longer had it anywhere. The lock has since been updated to cd40bb1 and the VM rebuilt.

Verified against the rebuilt tool on PATH:

allod change record -m t --files a.txt b.txt      # parses; proceeds past argument handling
allod change record --files a.txt b.txt -m t      # parses; flag order is free
allod change record -m t -f a.txt b.txt --files c.txt   # parses; forms combine
allod change record -m t --files                  # allod: --files requires a value
allod change record --files -m t                  # allod: --files requires a value
allod change record -m t stray.txt                # allod: unexpected argument for change record: stray.txt

An empty list refuses rather than falling back to the git add -u sweep, which was the safety concern this flag exists to serve (allod/tools#118). tests/allod-change.sh covers these forms; the suite is at 185 tests.

The sweep suggested at the end of this issue was run across the whole repo — allod, forge, pm/*, workspace/*, flake/*, git-hooks/*. There are no remaining +=("$2") branches behind a plural flag name, and --files was the only flag whose usage string ever promised a variadic list. flake-update-cascade <input-name>... is a variadic positional and collects all of its operands correctly.

The sweep did surface a real inconsistency, which is not a bug: allod change record --files a b is space-variadic, while forge's multi-value flags (-l|--label, --add-label, --remove-label, --set) are singular-named and take comma-separated values with repetition accumulating. Tracked in allod/tools#131.

Already fixed. allod/tools#129 landed the variadic `-f|--files` parser on master and closed allod/tools#128, which reported this same defect in this same command a few hours before this issue was filed. This report was accurate about the tool it was running, which is why it does not read like a stale duplicate. The dev VM installs `allod` from the deploy flake's `allod-tools` pin, and that pin sat at `90d68df` — the commit immediately before the fix branch started. So the installed copy still carried the exact `files+=("$2"); shift 2` branch quoted above while master no longer had it anywhere. The lock has since been updated to `cd40bb1` and the VM rebuilt. Verified against the rebuilt tool on PATH: ``` allod change record -m t --files a.txt b.txt # parses; proceeds past argument handling allod change record --files a.txt b.txt -m t # parses; flag order is free allod change record -m t -f a.txt b.txt --files c.txt # parses; forms combine allod change record -m t --files # allod: --files requires a value allod change record --files -m t # allod: --files requires a value allod change record -m t stray.txt # allod: unexpected argument for change record: stray.txt ``` An empty list refuses rather than falling back to the `git add -u` sweep, which was the safety concern this flag exists to serve (allod/tools#118). `tests/allod-change.sh` covers these forms; the suite is at 185 tests. The sweep suggested at the end of this issue was run across the whole repo — `allod`, `forge`, `pm/*`, `workspace/*`, `flake/*`, `git-hooks/*`. There are no remaining `+=("$2")` branches behind a plural flag name, and `--files` was the only flag whose usage string ever promised a variadic list. `flake-update-cascade <input-name>...` is a variadic positional and collects all of its operands correctly. The sweep did surface a real inconsistency, which is not a bug: `allod change record --files a b` is space-variadic, while `forge`'s multi-value flags (`-l|--label`, `--add-label`, `--remove-label`, `--set`) are singular-named and take comma-separated values with repetition accumulating. Tracked in allod/tools#131.
Member

Duplicate of #128.

Duplicate of #128.
Sign in to join this conversation.
No description provided.