Skip to content

tui: detect array-of-enum on items.enum alone#1752

Merged
cliffhall merged 2 commits into
v2/mainfrom
v2/1751-tui-array-enum-guard
Jul 23, 2026
Merged

tui: detect array-of-enum on items.enum alone#1752
cliffhall merged 2 commits into
v2/mainfrom
v2/1751-tui-array-enum-guard

Conversation

@cliffhall

@cliffhall cliffhall commented Jul 23, 2026

Copy link
Copy Markdown
Member

Closes #1751

Follow-up from the review of #1691 (PR #1750).

Problem

The TUI schemaToForm array-of-enum branch was nested under an outer if (property.enum) guard:

if (property.enum) {
  if (property.type === "array" && property.items?.enum) { /* select from items.enum */ }
  else { /* single select from property.enum */ }
}

So a standard array-of-enums schema — { type: "array", items: { enum: [...] } } with no top-level enum — never reached the array branch and fell through to a plain string input. To get a select the schema had to redundantly carry a top-level enum. The web SchemaForm already guards on items?.enum alone, so the two clients diverged on which array shapes they recognized.

Fix

Restructure the enum handling so the array-of-enums case keys on property.type === "array" && property.items?.enum independently of a top-level enum, falling back to the single-select property.enum branch otherwise. Matches the web guard. The enumNames labeling from #1691 applies unchanged.

if (property.type === "array" && property.items?.enum) { /* select from items.enum */ }
else if (property.enum) { /* single select */ }
else { /* type switch */ }

Tests

  • The array-of-enum cases now use the standard items.enum-only shape (dropping the redundant top-level enum the old nesting required), including the enumNames label + mismatched-length fallback cases.
  • Added a case asserting an array that also carries a redundant top-level enum still renders as a select (no regression).

schemaToForm.ts stays at 100% lines/functions/statements. npm run ci passes (validate → coverage → smoke → Storybook).

Verified via a standalone render of the real schemaToForm: a { type: "array", items: { enum, enumNames } } field with no top-level enum now yields Small=>s | Medium=>m | Large=>l instead of an empty/plain-string field.

Smoke test (screenshots)

Rendered the real ink-form built by the real schemaToForm for the #1751 case: a Sizes array-of-enums field (enum: [s,m,l] + enumNames: [Small,Medium,Large], default: "m", no top-level enum) plus a Priority single-enum control (has a top-level enum). Non-interactive capture of the initial frame — the "before" column reproduces the pre-#1751 nesting.

Before #1751 With the fix
Sizes falls through to a plain text field showing the raw default m — never a select, so enumNames can't apply. Sizes is now a select; the default m resolves to its title Medium.

The Priority control (top-level enum) shows High in both — unchanged, confirming the reorder only affects the intended items.enum-only case. More detail + images in pr-screenshots/README.md.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5

The TUI schemaToForm array-of-enum branch was nested under an outer
`if (property.enum)` guard, so a standard array-of-enums schema —
`{ type: "array", items: { enum: [...] } }` with no top-level `enum` — never
reached it and fell through to a plain string input. The web SchemaForm already
guards on `items?.enum` alone; this aligns the TUI with it.

Restructure the enum handling so the array-of-enums case keys on
`property.type === "array" && property.items?.enum` independently of a top-level
`enum`, falling back to the single-select `property.enum` branch otherwise. The
enumNames labeling from #1691 applies unchanged.

Tests: the array-of-enum cases now use the standard `items.enum`-only shape
(dropping the redundant top-level `enum` the old nesting required), plus a case
asserting a redundant top-level `enum` still works.

Closes #1751

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5
@cliffhall cliffhall added the v2 Issues and PRs for v2 label Jul 23, 2026
@cliffhall

Copy link
Copy Markdown
Member Author

@claude review

@claude

claude Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Claude finished @cliffhall's task in 34s —— View job


Review — tui: detect array-of-enum on items.enum alone

  • Read the changed source and tests
  • Verify parity with the web SchemaForm guard
  • Check branch ordering for behavior regressions
  • Post review

Verdict: LGTM. The restructuring is correct, matches the stated goal, and is well-tested.

Correctness

The new ordering keys the array-of-enums case on items.enum alone, which is exactly what the web client does — confirmed at clients/web/src/components/groups/SchemaForm/SchemaForm.tsx:185:

if (fieldSchema.type === "array" && fieldSchema.items?.enum) {  }

So the two clients now agree on which array shapes render as a select. Good fix for the divergence.

No behavioral regressions from reordering

I walked the four relevant input shapes to confirm flipping the branch order changes nothing except the intended case:

Schema Old path New path Same?
array + items.enum (no top-level enum) falls through to plain string ❌ array select ✅ fixed
array + items.enum + top-level enum enum→array select array select ✅ same
array + top-level enum, no items.enum enum→single select else if enum→single select ✅ same
non-array + enum single select else if enum→single select ✅ same

The only changed outcome is the first row — the intended fix. The added "redundant top-level enum" test locks in row 2.

Tests

Coverage is thorough: standard items.enum-only shape, the redundant-enum no-regression case, and both enumNames label + mismatched-length fallback cases for the array branch. This exercises all three top-level branches plus both toSelectOptions paths, so the claimed 100% on schemaToForm.ts holds up.

Minor (non-blocking) observations

  • Both this client and the web client render an array-of-enums as a single-select (ink-form has no multiselect; the web uses MultiSelect). That's a pre-existing UX limitation, faithfully commented, and out of scope here — just noting it since a user picking multiple tags can't in the TUI.
  • The as FormField casts are pre-existing and unchanged; not introduced by this PR.

Nothing to change. Clean, focused, matches web behavior.
· v2/1751-tui-array-enum-guard

@cliffhall

Copy link
Copy Markdown
Member Author

Thanks — LGTM noted, and the four-shape regression table matches my analysis. Both observations are pre-existing and out of scope: the single-select-for-array limitation is inherent to ink-form (no multiselect) and already commented; the as FormField casts predate this PR. No changes. Attaching a before/after smoke of the array-of-enum render next.

Before/after of the TUI tool form for an array-of-enums field with no top-level
`enum`: before the fix it renders as a plain text field (raw "m"); with the fix
it renders as a select resolving the enumNames title ("Medium").

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5
@cliffhall
cliffhall merged commit c2b8661 into v2/main Jul 23, 2026
3 checks passed
@cliffhall
cliffhall deleted the v2/1751-tui-array-enum-guard branch July 23, 2026 23:12
@cliffhall cliffhall assigned cliffhall and unassigned cliffhall Jul 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v2 Issues and PRs for v2

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant