Skip to content

fix(cli): validate mutually-exclusive flags instead of silently resolving by precedence - #2039

Merged
gfargo-horizon-agent[bot] merged 2 commits into
mainfrom
agent/coco-1744-coco-1889-cmd-16-fix-cli-mutually-exclus
Aug 2, 2026
Merged

fix(cli): validate mutually-exclusive flags instead of silently resolving by precedence#2039
gfargo-horizon-agent[bot] merged 2 commits into
mainfrom
agent/coco-1744-coco-1889-cmd-16-fix-cli-mutually-exclus

Conversation

@gfargo-horizon-agent

@gfargo-horizon-agent gfargo-horizon-agent Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

What

commit, amend, changelog, and doctor accepted several contradictory flag combinations and silently picked a winner by code-path precedence instead of erroring — so a user (or a script) passing a redundant/incorrect combination got a different behavior than requested, with no diagnostic.

Why

Closes #1889
Plane: OSS-1744

How

  • commit: builder .check() rejects --plan+--apply, --print-message combined with any split flag (--split/--plan/--apply/--strict-split), --apply without split mode (--split/--plan/the split positional), and --strict-split without split mode. coco commit split --apply still works since the positional counts as split mode. The existing --json+split emitJson error in handler.ts is untouched.
  • amend: builder .check() rejects --dry-run+--apply.
  • doctor: builder .check() rejects combining --clear/--cost/--fix.
  • changelog: extended the existing handler.ts exclusivity guard (kept in the handler, not the builder, since it reads config-merged sinceLastTag/range) to reject --only-diff combined with --range/--tag/--since-last-tag. --only-diff --branch still works — branch is the diff base ref for --only-diff.

All four follow the .check() pattern already used by review's builder.

Testing

  • build passes (npm run build)
  • tests pass / added (npm run test:jest — new configCheck.test.ts for commit/amend/doctor using the review/config.test.ts extractCheckFn stub pattern, plus --only-diff cases added to changelog.test.ts)
  • lint clean (npm run lint)
  • CI: pending

🤖 Generated by the harbor agent loop. Reviewed by a human before merge.

Closes #1889

…ving by precedence

`commit --split --plan --apply`, `amend --dry-run --apply`, `changelog
--only-diff --range`, and `doctor --clear --cost` all parsed cleanly but
silently let one flag win, leaving scripts that pass a contradictory
combination with a different result than requested and no diagnostic
(#1889).

- commit: reject --plan+--apply, --print-message with any split flag,
  --apply without --split/--plan/`split` positional, and --strict-split
  without a split mode
- amend: reject --dry-run+--apply
- doctor: reject combining --clear/--cost/--fix
- changelog: reject --only-diff combined with --range/--tag/--since-last-tag
  (--only-diff --branch still works — branch is the diff base)

@gfargo-horizon-agent gfargo-horizon-agent Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔎 Agent review (sonnet→opus) — BLOCK

REVIEW: BLOCK
RESOLVES: partial

The new .check() validators correctly enforce the mutually-exclusive flag rules for commit/amend/doctor/changelog, but the commit builder's check runs during yargs parsing — before the handler — so --json --apply and --json --plan --apply now throw a plain-text yargs error instead of the machine-readable emitJson payload the PR's own acceptance criteria require to stay unchanged. The regression is real and untested; everything else is sound.

1 blocker · 1 concern — 2 inline on the diff

Comment thread src/commands/commit/config.ts
Comment thread src/commands/commit/configCheck.test.ts

@gfargo-horizon-agent gfargo-horizon-agent Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔎 Agent re-review (sonnet, delta) — CONCERNS

REVIEW: CONCERNS
RESOLVES: partial

The revise correctly fixes both prior findings (the check() bail-out for --json defers to handler's emitJson contract, and new tests cover --json + split-flag combos), verified by running the actual test suite. But the bail-out is broader than needed: it skips ALL check() validation whenever --json is set, not just the split/plan/apply combos handler.ts actually guards against, silently reintroducing the ticket's own bug for --json --strict-split (no --split/--plan).

1 concern — 1 inline on the diff

Comment thread src/commands/commit/config.ts

@gfargo-horizon-agent gfargo-horizon-agent Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔎 Agent re-review (sonnet, delta) — BLOCK

REVIEW: BLOCK
RESOLVES: partial

The mutually-exclusive-flag validation is correct for amend, doctor, and changelog, but the new commit .check() fires during yargs parsing before handler.ts ever runs, so it preempts commit's pre-existing --json structured-error contract for --split/--plan/--apply combos and replaces it with a plain-text yargs failure.

1 blocker — 1 inline on the diff

Comment thread src/commands/commit/config.ts
The commit builder's .check() ran during yargs parsing, before the
handler — so --json combined with --plan/--apply threw a plain-text
yargs error instead of reaching handler.ts's structured emitJson error
contract for machine consumers. Scope the check's early-return to only
combos handler.ts's own json guard already covers (json + one of
split/plan/apply); --strict-split alone still validates in .check()
since the handler guard doesn't consider it.

@gfargo-horizon-agent gfargo-horizon-agent Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔎 Agent re-review (sonnet, delta) — LGTM

REVIEW: LGTM
RESOLVES: full

The revise commit correctly scopes the commit builder's .check() to defer json+split/plan/apply conflicts to handler.ts's existing structured emitJson guard, fixing the plain-text-vs-JSON regression it introduced without weakening any other validation (--strict-split-alone-under-json still throws in check() as required). New test coverage matches the implementation and the full test suite plus lint pass clean.

@gfargo-horizon-agent
gfargo-horizon-agent Bot merged commit e20219e into main Aug 2, 2026
11 checks passed
@gfargo-horizon-agent
gfargo-horizon-agent Bot deleted the agent/coco-1744-coco-1889-cmd-16-fix-cli-mutually-exclus branch August 2, 2026 14:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CMD-16: fix(cli): mutually exclusive flags are silently resolved by precedence instead of validated

0 participants