Skip to content

fix(cli): name projection flags only where the command declares them - #187

Open
ysyneu wants to merge 2 commits into
mainfrom
fix/projection-note-flag-accuracy
Open

fix(cli): name projection flags only where the command declares them#187
ysyneu wants to merge 2 commits into
mainfrom
fix/projection-note-flag-accuracy

Conversation

@ysyneu

@ysyneu ysyneu commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

What

A reduced structured list announces the reduction on stderr, and that note used to advise --fields / --limit unconditionally. Both flags are registered per verb, so a verb declaring neither was told to pass a flag it rejects (unknown flag: --limit) — and --fields is not always a projection control: monit rule-update-fields uses it to select which rule fields get written, so obeying the advice would change a write rather than the output.

The byte-bounding helper no longer composes prose. It returns the facts about what it removed (projectionBound), and the note is composed at the call site from the flags the command itself declares:

  • commands declare their narrowing flags with declareOutputNarrowing, next to the flag registrations it names;
  • declaredNarrowing drops a declaration whose flag the command no longer carries, so a renamed flag degrades to no advice rather than naming a stale one;
  • value shortening drops the rows flag — a smaller page cannot shrink the single row that already overflows;
  • a command that declares nothing gets a flag-neutral note, and the irreducible-row overflow error is wrapped the same way, so no flag-blind helper composes flag advice anywhere.

Generated verbs declare nothing deliberately: the OpenAPI spec carries no structured contract for whether a --limit actually bounds the emitted rows (safari knowledge-file-list documents its limit as ignored; monit rule-list-basic honours it only with --include-descendants), so any generator rule would be inference. The decision is documented at the generator site so it cannot drift silently.

Verification

  • make fmt, go build ./..., go vet ./..., go test -count=1 ./... — all green.
  • New end-to-end tests through execCommandSplit: declared projection+rows names both; projection-only never names --limit; the write-selector --fields gets no --fields; a no-declaration verb gets the flagless note; an ignored --limit is never offered; the shortening branch omits the rows flag; the overflow error path names only declared flags.
  • The truncation guarantee is unchanged (over-budget lists still reduced, every emitted value intact, announced on stderr exactly once) and the under-budget fast path stays byte-identical.

A reduced structured list announces the reduction on stderr with advice to
narrow --fields or lower --limit, but those flags are registered per verb,
not globally. The note was composed inside the byte-bounding helper, which
knows bytes and nothing about the running command, so a generated verb that
declares neither flag (e.g. channel silence-rule-list, whose only flags are
--channel-id and --data) was told to pass a flag it rejects (unknown flag:
--limit), costing a round trip that cannot be obeyed.

boundProjectedList now returns a projectionBound describing what it removed
(rows kept/total, or values shortened and in which fields) instead of prose.
noteProjectionBound composes the note against cmd's real flag set, naming
only the flags the verb declares and saying plainly when it declares none.
A --fields-only verb (channel escalate-rule-list) is no longer told to
lower --limit, and a --limit-only verb (insight incident-list, monit
rule-list-basic) is no longer told to narrow --fields.

Tests cover a verb with --fields and --limit (incident list, alert-event
list), one with --fields only, one with neither, and the --limit-only
generated verbs, asserting the reduction is still announced with every
emitted value intact.
The reduction note and the overflow error told the caller to narrow --fields
or lower --limit whenever those flags existed, but a flag's name does not
imply its effect: monit rule-update-fields spells a request-body write
selector --fields, so obeying the advice would change a write instead of the
output; safari knowledge-file-list documents its --limit as ignored; monit
rule-list-basic honors its own only alongside --include-descendants.

Commands now declare, in their own definition, the flags that narrow their
structured output — a projection flag that shrinks each row, and a rows flag
that requests fewer rows (declareOutputNarrowing, set next to the flag
registrations). The hand-written projection verbs declare theirs; generated
verbs declare none, because the spec has no structured contract for whether a
--limit bounds the response, so their note names no flag at all.

The byte-bounding helper composes no flag advice: it returns facts, and an
irreducible overflow comes back as a flag-neutral error the caller completes
(explainProjectionOverflow). The single-row shortening branch no longer
offers a rows flag, since requesting fewer rows cannot shrink the one row
that already overflows.

Tests drive real verbs end to end: a write-selector --fields gets no --fields
advice, an ignored or conditional --limit gets no --limit advice, the
shortening note offers only the projection flag, and the overflow error names
only flags the command declared.
@ysyneu
ysyneu changed the base branch from feat/ai-sre to main September 10, 2026 08:59
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.

1 participant