fix: address Copilot review — const precedence, TUI variant model, decline both-keyword unions

- rootUnion: decline a schema carrying BOTH oneOf and anyOf rather than
  reading one and silently dropping the other's constraints.
- SchemaForm: handle `const` before every type dispatch and for every
  constant type, so a numeric/boolean constant, or a schema carrying both
  `const` and `enum`, can no longer offer a forbidden value.
- collectSchemaDefaults / TUI initialValue: `const` outranks `default`,
  which is an annotation a schema may set to something its const rejects.
- convertToolParameters: resolve from the first branch's merged schema, so a
  branch's specialization of a root-declared property is what types the arg.
- TUI: ink-form keys values by field name across the whole form, so two
  branches' `kind` were one field and the later section's initial value
  decided what the earlier submitted. Branch fields now render under prefixed
  names behind a Variant select, and decodeFormValues translates back on
  submit, dropping every branch but the chosen one.

Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-26 01:01:54 -04:00
commit 2782ed75d1

View file

@ -171,12 +171,19 @@ v2/main/
│ │ # SchemaForm (the Variant picker + the branch-change
│ │ # value pruning), TUI schemaToForm (a section per
│ │ # branch, its fields forced OPTIONAL since only one
│ │ # alternative applies), and convertToolParameters
│ │ # (which branch's schema types a CLI --tool-arg).
│ │ # Declines a union whose members are not ALL
│ │ # field-carrying objects rather than offering a
│ │ # picker with options that render nothing, and does
│ │ # not interpret `not` at all — #2123;
│ │ # alternative applies — and rendered under PREFIXED
│ │ # names behind a variant select, because ink-form
│ │ # keys values by field name across the WHOLE form,
│ │ # so two branches' `kind` would otherwise be one
│ │ # field; schemaToForm.decodeFormValues translates
│ │ # back on submit), and convertToolParameters (which
│ │ # branch's schema types a CLI --tool-arg).
│ │ # DECLINES rather than half-reads: a union whose
│ │ # members are not ALL field-carrying objects, and a
│ │ # schema carrying BOTH oneOf and anyOf (independent
│ │ # keywords, satisfied together — picking one drops
│ │ # real constraints). Does not interpret `not` at
│ │ # all — #2123;
│ │ # schemaLint.ts: tool-schema PORTABILITY lint —
│ │ # constructs that are legal JSON Schema and are
│ │ # refused or mishandled by real MCP clients (a bare