Skip to content

feat: json parameters with declared schemas + CLI representation overrides (COD-411) - #3

Merged
shivros merged 2 commits into
mainfrom
runner/COD-411-json-params-cli-overrides
Aug 19, 2026
Merged

feat: json parameters with declared schemas + CLI representation overrides (COD-411)#3
shivros merged 2 commits into
mainfrom
runner/COD-411-json-params-cli-overrides

Conversation

@shivros

@shivros shivros commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds generic, explicitly-declared parameter capabilities to hydra so consumers (iris) can express non-scalar inputs without inference. Unblocks COD-406 (IRIS-11 PR3) which needs send_message to accept a closed inline/stored attachment union.

Closes COD-411.

What lands

type: json parameters with declared schemas

  • Body-only parameters carrying an explicitly declared JSON Schema subtree (schema:)
  • Embedded verbatim into generated MCP tool inputSchemas; parameter description fills in only when the subtree has none (schema-level description wins)
  • HTTP surface unchanged — body flows as untyped Value; runtime validates

cli: representation overrides

  • flag: — kebab-case rename for any non-path parameter
  • multiple: true — repeatable Option<Vec<String>> flag with ArgAction::Append (json params only); required: true + multiple: true emits required = true so requiredness holds on CLI
  • companions: — CLI-only repeatable string flags (explicit flag/field/description), excluded from HTTP/MCP, riding as sibling keys in parameters_json()

Validation (generation-time, per hydra law)

  • json params require schema + body location; schema is json-only
  • cli: blocks only on CLI-generating operations; json params on CLI ops must declare cli: (no inferred shape)
  • Collision namespaces register every CLI-visible parameter (default shape included) plus companions — explicit overrides cannot collide with derived defaults
  • Path parameters cannot carry cli: blocks (positional contract preserved)

Dogfood (examples/notes)
annotate_note carries an attachments-style closed inline/stored union end-to-end: repeatable --attach + --attach-mime CLI parsing, MCP schema embedding, HTTP union validation (400 on malformed unions).

Verification

All five gates green locally:

  • cargo build --all-targets — 0 errors
  • cargo test --all-targets — 49/49 passed
  • cargo clippy --all-targets -- -D warnings — 0 errors
  • cargo fmt --all -- --check — clean
  • hydra-codegen check — artifacts current; write-twice determinism verified (identical md5s)

Definitions not using the feature generate byte-identical output (fixture test guards this). Additive-only: consumers pinned on tags (iris v0.1.1, rite v0.1.1) unaffected.

Review panel

Dual-model review ran before opening. Round 1 flagged 4 findings (companion flag ignored in emission, collision validation missing default-shaped params, snake_case long names from cli: without flag, required+multiple weakening requiredness) — all fixed in 1a2c238 with regression tests. Full reviews posted as PR comment.

Follow-up

  • Tag v0.2.0 after merge → iris PR pins the tag and adopts (COD-406 T8–T10)
  • rite unaffected (uses only load_api_definition + defaults)

…rides (COD-411)

Add generic parameter capabilities so consumers can express non-scalar
inputs without inference:

- type: json parameters (body-only) with an explicitly declared JSON
  Schema subtree embedded verbatim into generated MCP tool inputSchemas
- cli: representation overrides: kebab-case flag rename, multiple: true
  (repeatable Vec<String> flag with ArgAction::Append), and companion
  CLI-only repeatable flags (e.g. --attach-mime) emitted as
  Option<Vec<String>> fields excluded from HTTP/MCP
- validation: json params require schema + body location; schema is
  json-only; cli: blocks only on CLI-generating operations; json params
  on CLI ops must declare a cli: block (no inferred shape); multiple is
  json-only; flag/field collisions rejected across params + companions
- parameters_json() maps CLI shape back to wire names; companions ride
  as sibling keys
- definitions not using the feature generate byte-identical output
  (guarded by fixture test)

Dogfood: examples/notes annotate_note carries an attachments-style
closed inline/stored union end-to-end — repeatable --attach +
--attach-mime CLI parsing, MCP schema embedding, HTTP union validation
with 400 on malformed unions. 43/43 tests, all five gates green.
…validation, required multiple flags (COD-411)

- Companion flags now emit their declared flag verbatim instead of
  letting clap derive from the field name
- Collision validation registers EVERY CLI-visible parameter (default
  shape included), so explicit flags/fields cannot collide with derived
  defaults; path parameters cannot carry cli: blocks
- cli: without flag: emits plain #[arg(long)] — clap derives kebab from
  the field, same as default shape; no snake_case long names
- required: true + multiple: true now emits required = true on the clap
  attribute, so requiredness holds on the CLI surface too
- MCP schema merge policy documented: schema-level description wins;
  parameter description fills in only when absent
- 6 new regression tests for every panel finding; 49/49 green, all
  five gates clean, codegen check + write-twice determinism verified
@shivros

shivros commented Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Automated Review Panel

Dual-model review ran before this PR was opened. Round 1 findings were fixed in 1a2c238 with regression tests added for each.

GPT-5.5 (openai/gpt-5.5) — Round 1 verdict: BLOCK → resolved

  1. Companion flag is ignored in CLI codegen — companion fields emitted #[arg(long, action = Append)], letting clap derive the flag from the field name; a declared flag: diverging from kebab-of-field was silently ignored. → Fixed: companions now emit #[arg(long = "<declared flag>", action = Append)]; regression test companion_flag_name_is_emitted_not_derived.

  2. Collision validation misses default-shaped parameterscli_fields/cli_flags were only populated for parameters carrying a cli: block, so an explicit override could collide with another parameter's derived default flag/field (e.g. cli.flag: body vs a default --body). → Fixed: every CLI-visible parameter registers its field name and effective long flag (declared or kebab-derived); companions register too. Regression tests: rejects_override_flag_colliding_with_default_parameter_flag, rejects_companion_field_colliding_with_parameter_field.

  3. Validation tracked a different flag than codegen emitted — consequence of (1); unsound collision guarantees. → Fixed by (1)+(2) jointly.

  4. cli: without flag emitted snake_case long name — the effective-flag derivation produced #[arg(long = "foo_bar")]. → Fixed: cli: without flag now emits plain #[arg(long)] and lets clap derive kebab-case from the field name, identical to the default shape. Regression test: cli_block_without_flag_uses_default_long_attribute.

Gemini 3 Flash (google/gemini-3-flash-preview) — Round 1 verdict: BLOCK → resolved

  1. Collision validation misses parameters without cli: overrides — same finding as GPT-5.5 docs: replace no-auto-merge rule with standing gate policy #2. → Fixed (see above).

  2. Companion flag: validated but ignored during codegen — same finding as GPT-5.5 feat: raw-request passthrough for webhook HMAC verification (COD-402) #1. → Fixed.

  3. Required multiple: true JSON CLI params become optionalOption<Vec<String>> with no required = true clap attribute even when parameter.required, weakening the contract vs MCP/schema requiredness. → Fixed: required: true + multiple: true now emits required = true on the clap attribute; clap enforces at parse time. Regression test: required_multiple_flag_carries_required_true.

  4. MCP "description merged in" is conditional, not guaranteed — schema subtree's own description wins over the parameter description. → Intentional; now documented in code: verbatim embedding is the primary contract; the parameter description fills in only when the subtree carries none.

Round 2 status

All four findings fixed with regression tests; 49/49 tests green; build/clippy/fmt/codegen-check + write-twice determinism all clean.

Note for Shiv: none of the round-1 suggestions were style nits — all were real correctness gaps the panel caught pre-merge. The parameters_json() companion emission and MCP merge policy are worth a skim if you want the product-level view.

@shivros
shivros marked this pull request as ready for review August 19, 2026 23:46
@shivros

shivros commented Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

Auto-Merge Gate Approval

Confidence: 0.92 — threshold 0.80. Merging as squash.

Goal

COD-411 "hydra: json parameters with declared schemas + CLI representation overrides". Verified against the Linear ticket description, deliverables, constraints, and verification criteria — all satisfied.

Rationale

  • Diff-goal alignment: ParameterType::Json + Parameter.schema + Parameter.cli model/serde in hydra-core; generation-time validation (json⇄schema pairing, body-only, CLI-only-ops, kebab flags, full collision namespaces including default-shaped params and companion fields); CLI emission (flag override, ArgAction::Append repeatables, companion flags with explicit long = "..."), parameters_json() round-trip, MCP verbatim schema embedding with description-merge rule; no-drift fixture test guards byte-identical output for definitions not using the feature.
  • Dogfood: examples/notes annotate_note carries the attachments-style closed union end-to-end across CLI/MCP/HTTP.
  • Tests: 18 new tests covering positive generation, all rejection rules, and review-panel round-1 regressions (companion flag emission, default-param collisions, required+multiple requiredness).
  • Review panel: dual-model review ran pre-open; 4 round-1 findings fixed in 1a2c238 with regression tests each.
  • Additive-only: consumers pinned on tags (iris v0.1.1, rite v0.1.1) unaffected.

Checks observed (all green, independently verified)

  • GitHub CI: Test SUCCESS, Creed context drift SUCCESS
  • Local: cargo build --all-targets, cargo test --all-targets (49/49), cargo clippy --all-targets -- -D warnings, cargo fmt --all -- --check, hydra-codegen check (artifacts current)

Scope limits respected

  • Owner TechGodHQ, author shivros, same-repo branch (no fork)
  • No secrets/auth/production-cutover/billing/data-deletion touched
  • Linear COD-411 description + comments scanned: no "do not merge automatically" / manual-review markers
  • Draft status ignored per gate policy (explicitly not a blocker for this gate)

Confidence rationale basis: PR body, COD-411 Linear issue, full diff vs main, green checks, resolved review panel. Not merged on vibes.

@shivros
shivros merged commit c3923dc into main Aug 19, 2026
2 checks passed
@shivros
shivros deleted the runner/COD-411-json-params-cli-overrides branch August 19, 2026 23:46
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