Skip to content

fix(cli-generator): disclose the flag name for every property in --schema - #17554

Merged
cadesark merged 2 commits into
mainfrom
cade/cli-schema-flag-disclosure
Aug 28, 2026
Merged

fix(cli-generator): disclose the flag name for every property in --schema#17554
cadesark merged 2 commits into
mainfrom
cade/cli-schema-flag-disclosure

Conversation

@cadesark

@cadesark cadesark commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Description

Linear ticket: Refs

Follow-up to #17545, which merged while this was in flight. Both regression agents that verified #17545 independently reported the same gap, and it left one operation whose advertised contract could not be followed at all.

--schema keys properties by wire name, and the flag diverges more often than it looks:

property actual flag why
Idempotency-Key --idempotency-key header wire-casing
query --query-param --query is the JMESPath global, so the spec param gets a -param suffix
any x-fern-parameter-name rename the renamed flag override

The query case made the contract literally unfollowable on conversational-ai knowledge-base searchinput.required named query, and no --query flag existed for it:

$ elevenlabs conversational-ai knowledge-base search --query x --dry-run
Required parameter 'query' is missing. Provide it via --query-param or --params

An agent had to reimplement the CLI's own sanitisation to get from a property name to a flag — and get the -param suffix and header kebab-casing right. It now reads the flag off the contract:

"query": {"type": "string", "location": "query",
           "description": "The search query text", "flag": "--query-param"}

Derived from the same resolve_param_flag_name the command builder uses, so the two cannot drift. Multipart fields get the same treatment.

flag is absent exactly when the builder registers no flag, which is itself the signal that the property is reachable via --params alone. That is two cases, both mirroring build_resource_command:

  • the name cannot be sanitised into a flag at all;
  • another wire name claimed the same flag first. The builder keeps the first in sorted order and skips the rest, so the help loop tracks the same flag_to_wire map over the same sorted iteration — otherwise the loser would advertise a flag that sets the winner's parameter.

This is the parameter-loop counterpart to the builtin-collision skip #17545's multipart commit added: one loop in build_operation_schema was honest about builtin collisions and the other was not.

input.required still uses wire names, so the --params route keeps working unchanged.

Changes Made

  • sdk/src/openapi/help.rs — emit flag per settable property (parameters and multipart fields); skip it for sdk-variable-bound params, which already advertise globalFlag, and for params whose flag was claimed by an earlier wire name.
  • sdk/src/openapi/commands.rs — new resolve_multipart_field_flag_name, called by both the command builder and the help loop. This also closed an existing drift: the builder skipped on is_reserved_flag_name (builtins plus a customer-configured userAgentSuffixFlag) while the help loop only checked BUILTIN_FLAG_NAMES, so a field colliding with the suffix flag was advertised with a flag that is never registered.
  • Changelog entry under generators/cli/changes/unreleased/.
  • Updated README.md generator (if applicable) — n/a

Testing

  • Unit tests added/updated
  • Manual testing completed

1945 lib tests pass. every_settable_property_discloses_its_flag covers the builtin-collision suffix, header kebab-casing, an ordinary name, a multipart field, and a cross-parameter collision (pageSize / page_size both resolving to --page-size: the winner discloses it, the loser has no flag), and asserts required still uses wire names.

Verified by regenerating and building the ElevenLabs CLI: 2364 settable properties across 337 operations, 0 missing a flag, and the previously-unfollowable operation now exits 0 from exactly what the contract advertises (--query-param xquery_params=[['query','x']]).

Also re-confirmed on the same build: 0 ancestor-prefix violations in any required list.

Link to Devin session: https://app.devin.ai/sessions/8d3395b13c124d728dc5d615e2149057
Open in Devin Desktop: https://app.devin.ai/desktop/session/8d3395b13c124d728dc5d615e2149057?variant=devin


Devin Review

…hema

Both regression agents independently reported the same gap, and it left one
operation whose advertised contract could not be followed at all.

`--schema` keys properties by wire name, and the flag diverges more often than
it looks. A header `Idempotency-Key` is registered as `--idempotency-key`; an
`x-fern-parameter-name` rename changes it outright; and a name colliding with a
builtin gets a `-param` suffix — so a spec parameter called `query` becomes
`--query-param`, because `--query` is the JMESPath global. On
`conversational-ai knowledge-base search` that meant `required` named `query`
while no `--query` flag existed for it:

    $ elevenlabs conversational-ai knowledge-base search --query x --dry-run
    Required parameter 'query' is missing. Provide it via --query-param or --params

An agent had to reimplement the CLI's own sanitisation to get from a property
name to a flag, and get the `-param` suffix and header kebab-casing right. It
now reads the flag off the contract. Derived from the same
`resolve_param_flag_name` the command builder uses, so the two cannot drift;
absent only when the name cannot be sanitised into a flag at all, which is
itself the useful signal that the parameter is reachable via `--params` alone.
Multipart fields get the same treatment.

This is the parameter-loop counterpart to the builtin-collision skip the
multipart commit added — one loop in `build_operation_schema` was honest about
builtin collisions and the other was not.

Verified on the ElevenLabs spec: 2364 settable properties across 337
operations, 0 missing a `flag`, and the previously-unfollowable operation now
exits 0 from exactly what the contract advertises.
@cadesark cadesark self-assigned this Aug 27, 2026

@nitpickybot nitpickybot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

AI Review Summary

Adds a flag field to each settable property in the --schema output, derived from resolve_param_flag_name for parameters and a local kebab for multipart fields. The refactor of the else if into else + a separate variable_reference.is_none() && required check preserves the original required-list semantics. Two issues: an unused closure parameter in the new test (compile warning), and the multipart flag being computed locally rather than via a shared resolver.

  • 🟡 2 warning(s)
  • 🔵 1 suggestion(s)

To request another review, comment /ai-review on this pull request.

Comment thread generators/cli/sdk/src/openapi/help.rs Outdated
Comment thread generators/cli/sdk/src/openapi/help.rs
Comment thread generators/cli/sdk/src/openapi/help.rs Outdated

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

Devin Review

Comment thread generators/cli/sdk/src/openapi/help.rs Outdated
Address review: dedup cross-parameter flag collisions the same way build_resource_command does, route multipart flags through a shared resolver, and trim the parameter-loop comment.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@cadesark
cadesark merged commit 302cf6c into main Aug 28, 2026
61 checks passed
@cadesark
cadesark deleted the cade/cli-schema-flag-disclosure branch August 28, 2026 14:02
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.

2 participants