fix: steer a flag written before its subcommand and document the import-event tie-break - #90
Merged
Merged
Conversation
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.
Summary
Two first-run findings. A long flag written before the subcommand that takes it is now told where it belongs instead of being refused as merely unexpected, and
receipt add's tie-break for import events that share animported_atis written down.--archiveand--jsonare defined per subcommand rather than globally, soopenpapir --archive <root> case listandopenpapir --json capabilitiesare the likeliest flag-order mistakes, and the parser on its own said no more than that the token was unexpected.What changed
crates/openpapir-cli/src/usage.rs: the usage walker now classifies the rejected argument rather than only filtering it. A long flag the parser called unexpected counts as misplaced when either it was written before the subcommand the walk went on to recognise and that command defines it, or no recognised command defines it while a command below the deepest one does. The walk records the index of the first recognised subcommand so "before the subcommand" is a fact about the raw arguments rather than a guess. Both forms then say the argument belongs after the subcommand: the JSON form as the refusal'smessage, with the newdetails.placementvalueafter_subcommandbesidedetails.argument; the human form as one line after the parser's own usage text.usage.argumentscode, itscommandand the exit code2are unchanged. A flag written where a flag belongs and refused anyway is unaffected: noplacement, and when only another subcommand defines it, noargumenteither.crates/openpapir-cli/tests/contract.rs: two new tests pin the steer, one per output form, over five invocations including--archive=<root>and a flag written before a nested subcommand. Both assert a marker planted in the caller's value never appears. One existing case moved:--json -- import --archivenow carriesargumentjson, because--jsonreally was written before the subcommand, and its place in the "never echoed" table is taken bycase list --title x, a flag another subcommand defines written where a flag belongs.crates/openpapir-cli/src/usage.rsunit tests: the subtree lookup and the boundary each get a test.crates/openpapir-cli/tests/property.rs: the privacy assertion is untouched. Added alongside it:details.placement, when present, holds the one value the contract defines and names an argument.crates/openpapir-cli/tests/golden.rs: the two usage cases moved into their ownusage_cases()soarchive_cases()stays under the line limit; no case changed.docs/architecture.md: thereceipt addsection now states thatimported_atis recorded to the second, so two imports of the same bytes inside one second tie; the tie goes to the lowest identifier, which is deterministic but is not necessarily the earlier import or the one an earlierimportreported, and--import-eventis the only way to name one explicitly. Behaviour is unchanged. The usage-refusal section documents the steer.docs/error-contract.md:placementadded to thedetailskey list with its single value, and theusage.argumentsentry rewritten for the new case.CHANGELOG.md: two bullets at the top of Fixed.Goldens changed
tests/golden/usage.flag-order/(--archive <root> case list), with its row intests/golden/README.md. Neither the human nor the JSON form holds the archive root. No existing golden changed.Verification
./scripts/check.sh && cargo build --release --locked && cargo run --locked -p openpapir-cli -- capabilities --json, all green.cargo clippy --workspace --all-targets --locked --target x86_64-pc-windows-msvc -- -D warnings, clean.cargo +1.88 check --workspace --all-targets --locked, clean.cargo llvm-cov --workspace --locked --fail-under-lines 90: 97.31% lines.origin/developimmediately before pushing.Notes and exceptions
docs/guide.mdis deliberately untouched: another change is regenerating it. Its step 6 sentence, which teaches that the identifierimportprinted is the onereceipt addechoes, may want the tie-break note once that regeneration lands.crates/openpapir-cli/skills/openpapir/SKILL.mdis deliberately untouched. Its description of the parser refusal stays accurate, and every example in it already writes the flag after the subcommand.