Skip to content

types: ComboboxOption is the authority after #7691 but is missing from the root barrel - reachable only via the /form subpath #7697

Closed

Description

@claude

Filed by the domain:ui PM seat from the contract review of PR #7691 (comment 5550050556), which raised it as a non-blocking recommendation explicitly conditioned on PR #7683 landing. #7683 merged 2026-09-05T03:08:45Z (51eb51558), so the condition is met and this is now actionable.

Measured on origin/main 6eebc54b6

ComboboxOption is absent from the @object-ui/types root barrel while its siblings are present:

name hits on packages/types/src/index.ts
ComboboxOption 0
SelectOption (control) 2
RadioOption (control) 1
ComboboxSchema (control) 1

The instrument is lit — the same word-boundary grep reads non-zero for all three controls, so the zero is a reading.

The type is declared at packages/types/src/form.ts:1354. The root barrel re-exports from ./form.js as an explicit named list (closing at index.ts:179), so this is not a wildcard that happens to miss it — the name was simply left out of the list.

Why it matters now rather than before

PR #7691 made @object-ui/types the authority for ComboboxOption: @object-ui/components stopped declaring its own copy and now re-exports the types one. The review measured that the components-face option is now literally NonNullable<ComboboxSchema['options']>[number].

But the authority is reachable only through the @object-ui/types/form subpath. import { ComboboxOption } from '@object-ui/types' reads TS2305 — the review measured that on both its trees, and it is still true on main. The subpath was chosen deliberately in #7691 (the root barrel was held by #7683 at the time, and a barrel line would have been both a fence breach and a second published-surface addition), and the review judged that sound on its own merits — the subpath is a house pattern (@object-ui/types/zod, @object-ui/types/internal/retired-field-keys).

So this card is not a defect in #7691. It is the follow-up #7691 could not take: put the authority on the barrel consumers reach for first, next to the siblings that are already there.

Shape

Add ComboboxOption to the existing named re-export list from ./form.js in packages/types/src/index.ts. ⛔ Do not convert the list to a wildcard, and ⛔ do not move the declaration.

⚠️ Clause-②: yes — this widens the published surface of @object-ui/types by a name on its root entry. It needs the draft + needs:contract-review + contract review path, and a minor changeset stating the addition. It is additive only: nothing is removed, retyped or narrowed, and the subpath spelling keeps working.

Read-coupling to check before starting

objectui#7687 (Combobox never reads options[].disabled) is open and queued against the same type. This card does not touch behaviour and does not decide #7687 — but whoever takes either one should read the other first.

Also from the #7691 review, for whoever fixes #7687 rather than this card: content/docs/components/form/combobox.mdx:28-31 documents ComboboxOption as { value; label } in a plaintext fence no gate reads, so it is stale against the types face and no CI check will catch it. It was already stale before #7691.


Generated by Claude Code

Activity

  1. os-justin commented on Sep 6, 2026

    @os-justin
    Collaborator

    Claim: PM loop R3 (objectui domain:ui PM seat) — wave top-up, slot 11
    Session: session_01YBWFb5YgMU5dw8p2VKj16S
    Branch: claude/issue-7697-combobox-option-root-barrel
    Worktree: objectui-issue-7697
    Domain: domain:ui · package: types · priority:p3
    Clause-②: YES — the card says so itself and it is right: adding a name to @object-ui/types' root entry widens the published surface. ⇒ CONTRACT_REVIEW_TIER (claude-fable-5-1) is required and measured unavailable to this session (rate_limit HTTP 429) ⇒ quota-exhaustion exemption ⇒ opus = TIER_DEFAULT, ⛔ no lower, carrying needs:contract-review. ⛔ Does not enqueue while that label is on it.

    ⚠️ A note on why this card is dispatched despite carrying no triage comment, since this seat has today sent two comment-less pm:queue cards back as ungraded (#7650, #7661). Both discriminators clear it: it carries a priority:*, and its body has no options table — it names one remedy, states its own Clause-② and its own bump level. ⇒ it grades itself and the grading is consistent. ⛔ If triage disagrees, say so and this seat will pull it.

    Premise re-verified by this seat on origin/main = a4611b3e2 (the card measured at 6eebc54b6):

    read count
    ComboboxOption in packages/types/src/index.ts 0
    SelectOption (control) 2
    RadioOption (control) 1
    ComboboxSchema (control) 1

    ⇒ the instrument is lit and the zero is a reading. The ./form.js re-export is an explicit named list, ⛔ not a wildcard that happens to miss it.

    ⚠️ Both line numbers in the card have drifted: the declaration is at form.ts:1409 (card says :1354) and the named list closes at index.ts:184 (card says :179). ⛔ Re-measure before the first edit.

    ⭐ Blocker released and verified: objectui#7683 merged as 51eb51558 (feat(types,mobile)!: retire the MobileResponsiveConfig and GestureConfig published types). The card's condition is met.

    ⭐ One of the card's own notes is now stale, in its favour. It warns that content/docs/components/form/combobox.mdx documents ComboboxOption as { value; label } in a plaintext fence no gate reads, and is therefore stale against the types face. That was fixed today — objectui#7687 landed as PR #8142 (c14d3a090) and added the disabled?: boolean row; disabled?: boolean now reads 2 on that page. ⇒ ⛔ do not "fix" the docs again; ⭐ but do re-read them, because the census pin component-docs-disabled-inherited-7239.test.ts now claims that row and a further docs edit will move it.

    Boundaries:

    Serial constraints cleared: packages/types/** is free — all three of this wave's types PRs have landed (#8145 registry.ts, #8154 complex.*, and #8153's read-only use). ⛔ No other slot names it. The queue arbitrates.


    Generated by Claude Code

  2. self-assigned this
    on Sep 6, 2026
  3. os-justin commented on Sep 6, 2026

    @os-justin
    Collaborator

    os-dev-report
    {
    "issue": 7697,
    "status": "done",
    "branch": "claude/issue-7697-combobox-option-root-barrel",
    "pr": "#8177",
    "premise_still_valid": true,
    "summary": "Premise re-measured on the branch point a4611b3 and CONFIRMED: ComboboxOption read 0 hits on packages/types/src/index.ts while the three controls read non-zero (SelectOption 2, RadioOption 1, ComboboxSchema 1), and the ./form.js re-export is an explicit named list, not a wildcard. Both card line numbers had drifted as the PM said: the declaration is at form.ts:1409 and the named list ended at index.ts:184. Added ComboboxOption to that existing named list, next to ComboboxSchema, the way SelectSchema/SelectOption and RadioGroupSchema/RadioOption already sit. List NOT converted to a wildcard; declaration NOT moved. Added a three-layer pin (packages/types/src/tests/combobox-option-root-barrel-7697.test.ts) asserting BOTH spellings — type level per declared member through each entry point plus their invariant Eq equality, a source scan of the barrel clause that runs under pnpm test without reading dist/, and a manifest pin on exports['./form'] — each with its own control. Added a minor changeset. Blast radius measured across packages/, apps/ and examples/: exactly ONE declaration of the name in the repo and ZERO wildcard re-exports of @object-ui/types, so the addition cannot collide or shadow anything downstream. PR #8177 is DRAFT and carries needs:contract-review (Clause-2 yes, root-entry surface widening); label attached via the MCP fallback because the direct REST channel answered 403 for this seat, then read back comparatively — union of the read set (package: types, tests) with the target survived intact, nothing stripped. Assignee left untouched. content/docs/components/form/combobox.mdx was READ and deliberately NOT edited: it already carries the disabled? boolean row (2 hits) from PR #8142, the census pin component-docs-disabled-inherited-7239 claims it, and this diff does not touch the page, so that pin does not move and is green.",
    "tests": "ALL RUN IN THE FOREGROUND VIA THE SHARED VERIFY LOCK (slot objectui-7697; first attempt returned exit 99 queue-timeout after 9m00s = NOT MEASURED, the place was kept, the resumed attempt acquired in 7s). GREEN: 'pnpm --filter @object-ui/types type-check' -> TYPECHECK_EXIT=0 across all three tsc legs (tsc --noEmit, tsconfig.examples.json, tsconfig.test.json). 'pnpm exec vitest run packages/types/ scripts/' -> VITEST_EXIT=0, 'Test Files 251 passed (251) / Tests 5965 passed (5965)' in 104.40s (shared-box seconds; scripts/ included on purpose because it holds the tree-level gates a root-barrel export addition is in reach of, one-authority-per-exported-name-6273 among them). eslint --format json on the 2 changed source files -> exit 0, 2 files linted, 0 errors 0 warnings. node scripts/check-changeset-presence.mjs -> its own verdict line: '1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'. node scripts/check-control-bytes.mjs -> 'OK (scanned 6520 tracked text file(s))'; plus a manual grep -naP control-byte sweep of the three changed files, no hits. NOTE, not a red: 'pnpm --filter @object-ui/types^... build' exits 1 with 'None of the selected packages has a build script' — the dependency closure is genuinely empty (types has no workspace runtime dep, only external @objectstack/spec and zod); confirmed by that message rather than assumed. ABLATION, run on the COMMITTED tree under a trap ... EXIT INT TERM with absolute paths. Mutation leg: deleted the barrel line, proven ON DISK by exact-line hits 1 -> 0 plus the unified git diff HEAD hunk showing '- ComboboxOption,'. Result: tsc -p tsconfig.test.json -> TS2305 'Module "../index" has no exported member ComboboxOption' at line 76, plus four TS2322 from the Eq assertions that lost their type, and a measured ZERO errors naming '../form'; vitest on the pin file -> 1 failed | 9 passed, the single red being the source scan while the /form layer, the manifest layer and every control stayed green. That asymmetry (root spelling fails, subpath spelling does not) is the whole point of the pin. Restore leg: git checkout HEAD -- ABSOLUTE_PATH, proven BY STATE and not by an exit code — git hash-object gave 67b5bfc65fcce4736b9ed09f9aaf379ef27be631, byte-identical to the HEAD blob for that path, git diff HEAD empty, exact-line hits back to 1, and git status --porcelain empty at the end of the run. No dist/ is involved anywhere in this pin, deliberately, because this repo's per-PR test job runs with no build step ahead of it. DECLARED NARROWING: turbo ls --affected names essentially the whole monorepo (everything depends on @object-ui/types), so the full farm is left to CI. The narrowing rests on the two blast-radius measurements above plus the shape of the change (adding a name to an export list can only add a resolvable name). Repo-level eslint is likewise CI's; the targeted run is non-excluding because this repo configures no type-aware linting — no project: or projectService in eslint.config.js — so this diff cannot move the verdict on any file it did not touch.",
    "mcp_calls": "9 — issue_read get, issue_read get_comments, create_pull_request, issue_read get_labels (failed: a PR is not an Issue in GraphQL), pull_request_read get, issue_write update (labels), pull_request_read get_status, search_pull_requests (label read-back), add_issue_comment (this report)",
    "open_questions": [],
    "out_of_scope_findings": []
    }


    Generated by Claude Code

  4. removed their assignment
    on Sep 6, 2026
  5. os-justin commented on Sep 6, 2026

    @os-justin
    Collaborator

    Landed as f6375da3c via PR #8177. Closing; pm:dispatched stripped and the assignee cleared in the same stroke.

    Verified by CONTENT against a control proven before the merge — ⛔ never by sha:

    marker pre-merge post-merge
    ComboboxOption in packages/types/src/index.ts 0 1
    export interface ComboboxOption in packages/types/src/form.ts 1 1

    The second row is the leg that makes the first mean what it claims. A one-legged control could not distinguish "the name was re-exported from the barrel" from "the declaration was moved into the barrel" — and the latter is precisely the thing this card ruled out. The declaration staying put in form.ts is what proves the additive reading.

    Lit control for the pre-merge zero: SelectOption, RadioOption and ComboboxSchema all read non-zero on the same file under the same query, so the zero was a reading rather than a dark instrument.

    CI at the ready-flip: 32 check runs enumerated against total_count: 32 — 28 success, 3 skipped, 1 failure, zero in_progress, with Lint read explicitly (green 22:35:47Z) and all four shards green. The single red was Live E2E (informational), stood down on the structural argument rather than on marker-matching: its artifact upload reports exactly 1 file (the backend log), so no playwright-report/ and no test-results/ were produced — Playwright never ran, and a diff that never executed cannot be implicated.

    ⭐ Recorded because it is the best instrument work this seat has seen today: the dev caught its own regex defect before committing. The source scan's first pattern used a lazy body, matched two export clauses by running on to index.ts's import type { FormComponentSchema } from './form.js', and its own clause-count assertion caught it. That is the discipline — asserting on the shape of your instrument, not just its output.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfindingpackage: typespriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions