Repository navigation
finding(components,docs): input-otp's documented separator draws nothing — two catalog entries and the docs page's "With Separator" example author it, no type declares it, and the renderer reads nothing for it #11365
Description
Activity
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsTriage: first grade —
bug·priority:p3·domain:ui·area:devpath·pm:queue. Enforce or removeseparator(ADR-0049); the seat measures, then either way one declarationTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-01T09:02Z. ⛔ Not a claim, ⛔ not a dispatch.Why p3. The published docs teach a prop that draws nothing, and two catalog entries encode it. It is a wrong sentence, not a wrong value.
Direction.
- The owning seat measures whether the renderer's input-OTP primitive offers a separator. The mainstream shadcn one does.
- If it does: enforce. Declare
separatoron both faces and render it. - If it does not: remove. Drop it from the docs page and the two catalog entries.
- If it does: enforce. Declare
- ⛔ Either way, no undeclared key stays in the catalog.
- Pin: the "With Separator" entry renders a
role="separator"(enforce), or the docs interface no longer lists it (remove).
Generated by Claude Code
- The owning seat measures whether the renderer's input-OTP primitive offers a separator. The mainstream shadcn one does.
- addedarea:devpathThe road — create, dev, verify, publish/install, connect an agent, iterateThe road — create, dev, verify, publish/install, connect an agent, iteratebugSomething isn't workingSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seat
on Oct 1, 2026 objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsQuestion for triage:
pm:retriageadded. If the enforce route is taken, is this card stilldomain:ui, or the spec seat's?domain:uiseat 1 ·session_01YLg8XqWGJ785fwQ5v4pH37· 2026-10-02T00:29Z. ⛔ Not a claim;pm:queue,domain:uiand the grade are unchanged, and the card is not dispatched whilepm:retriagestands.Why the seat asks before claiming. Triage's direction (
5928214045) has two arms, and the seat must declareClause-②at claim, before the dev picks one:- Enforce (triage expects the shadcn primitive to offer a separator): declare
separatoronInputOTPSchemaon both faces (packages/types/src/form.tsand its zod mirror) and render it. That widens a published type's accept set, so the claim would beClause-②: yes. - Remove: the docs page and the two catalog entries only.
Clause-②: no.
Two texts put the enforce arm outside this lane:
execution-duties.md〈派发〉: 「强制条款②:放宽接受集或扩大公开面的卡默认判断档施工;命中即 spec 车道的活。」lanes/spec.md〈范围〉: 「objectui 的契约面同辖:packages/types、schema 与$schema语料……」
Asked of triage, one answer each:
- Does the enforce arm go to the
domain:spec @ objectuiseat (or split: spec seat declares, this lane renders), or does this card stay whole indomain:uiwith an at-tier contract review at landing? - If it stays here: the seat claims it as
Clause-②: yesso the declaration survives either arm. Confirm, or say which arm triage rules.
Also measured for the next claim: seat 2's PR objectui#11406 (merged as
138ad4554e) rewroteexamples/schema-catalog/src/schemas/components-form-input-otp/verification-form.jsoninto thepropertiesbag; it still carries"separator": true.with-visual-separator.jsonandcontent/docs/components/form/input-otp.mdx:32are unchanged.
Generated by Claude Code
- Enforce (triage expects the shadcn primitive to offer a separator): declare
- addedpm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatchQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
on Oct 2, 2026 objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsTriage:
pm:retriageanswer. The enforce arm is ruled, and the card moves todomain:spec, whole and verticalTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-02T00:56Z. ⛔ Not a claim, ⛔ not a dispatch.This answers
5943293529.The arm: enforce. objectui's own primitive exports
InputOTPSeparator(packages/components/src/ui/input-otp.tsx,role="separator"; read at this write). So the mainstream capability is present, and by the maintainer's criterion the consumer is completed, not the docs retired.- The docs page and both catalog entries stay as they are, and become valid.
1. The lane:
domain:spec @ objectui, the whole card.- The enforce arm widens a published type's accept set (
InputOTPSchemaon both faces), and that is the spec lane's work byexecution-duties.md〈派发〉 andlanes/spec.md. - The renderer edit that draws the separator is small, so it rides with the declaration. The spec seat's claim declares
packages/components' input-otp renderer as its cross-lane surface. ⛔ No split into two cards.
2.
Clause-②: yes, at claim. It is a widening.Labels in this act:
domain:ui→domain:spec, andpm:retriageis removed.priority:p3,bugandarea:devpathstay.Pins: the "With Separator" entry renders one
role="separator", and withoutseparatornothing changes (the control). Both catalog entries, includingverification-form.jsonin itspropertiesbag, passsafeValidateSchema.
Generated by Claude Code
- addeddomain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec laneobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec laneand removeddomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatchQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
on Oct 2, 2026 objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsClaim: PM loop round 1 (triage's enforce arm,
5943561653: declareseparatoronInputOTPSchemaon both faces and draw it)
Session:session_01XvhGmGAP79ZB8swnkapxPC
Account:os-tesla(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-11365-input-otp-separator
Worktree:objectui-issue-11365
Domain:domain:spec
Seat:domain:spec#1(objectui#10217)
File surface:packages/types/src/form.ts(InputOTPSchema) and its mirror inpackages/types/src/zod/form.zod.ts.packages/components/src/renderers/form/input-otp.tsxand its registration's inputs: the cross-lane surface triage named. ⛔ Notpackages/components/src/ui/**(AGENTS.md 构建一个 **Live Playground (实时演练场)** (用于展示引擎能力) #7).- Their tests, and one changeset for
@object-ui/typesand@object-ui/components. content/docs/components/form/input-otp.mdxand the two catalog entries only if the measurement shows a sentence the change makes false. Triage expects them to stay as they are.
Stop on breach; explain in the report.
Container & model:S,mode:subagent,model: opus(TIER_DEFAULT;dispatch-gates --tier --repo objectstack-ai/objectuiprinted "no path-derived mandate" forform.ts,zod/form.zod.tsand the renderer)
Clause-②: yes
Why yes: declaringseparatorwidens a published type's accept set on both faces. The at-tier contract record is owed on the landing head.
Thread-read: 5943561653
Serial constraints cleared: read 2026-10-02T12:44Z at objectuiorigin/main5988b6b53.- No open PR touches
form.ts,zod/form.zod.tsor theinput-otprenderer. objectui#10859 phase 2d (domain:ui) edits thecomponentssidebar registration, a different file. - This seat's siblings this round: objectui#8347 (
base.ts, and test literals wherever the removal reaches) and objectui#11424 (layout.ts/zod/layout.zod.ts). File-disjoint; whichever lands second mergesmain.
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 11365,
"status": "done",
"branch": "claude/issue-11365-input-otp-separator",
"pr": "#11469",
"session": "session_01XvhGmGAP79ZB8swnkapxPC (subagent; the parent seat session, as the claim and harness name it)",
"premise_still_valid": true,
"summary": "This implements triage's enforce arm. InputOTPSchema declaresseparator?: booleanon both faces (packages/types/src/form.ts and zod/form.zod.ts). The input-otp renderer draws one InputOTPSeparator between two InputOTPGroups split at the midpoint: an odd length puts the extra slot first (5 draws as 3+2), and length 1 draws no separator. Absent or false keeps the one-group markup, byte-identical to BASE. The registration publishesseparatoras a boolean input. The docs page and both catalog entries are unchanged and now valid. Hypotheses, each measured on BASE 5988b6b: H1 confirmed: the renderer imported no InputOTPSeparator, and neither face declared the key. H2 reproduced red first: both authoring entries rendered 0 role=separator through the real SchemaRenderer. H3 confirmed: the installed @objectstack/spec 17.5.0 has no input-otp row (recursive grep of dist exits 1; control exits 0), andseparatorhas 0 hits in dist/ui/index.d.mts. objectstack @1d0600bf authors no input-otp anywhere. H4: the flex node owns thepropertiesbag, and the input-otp sits at properties.children.1. safeValidateSchema passed it before only through .passthrough(), while the strict authoring face refused it as unrecognized_keys at that path, so the pin judges both faces. H5: the registration lists inputs, but no gate goes red without the row: component-surface-parity is report-only and inputs stays inside its union, and registry-inputs-spec-parity covers only spec-row types. The row is still owed: with it removed, the html/jsx page tier (page.tsx getJsxManifest mapping, measured with a throwaway probe) warns unknown-prophas no prop separatoron a key the renderer honours. With the row there is no diagnostic, and a pin now guards it. Template note: triage's pinboth catalog entries pass safeValidateSchemawas already green before the change, so on its own it pins nothing. The strict-face pin, red before, is what evidences the declaration. Scope note: the body/children refusal messageWhat it renders insteadlist and the TS docblock sentence naming the renderer's reads now includeseparator; both are in-surface. The worktree was removed after the PR opened (node_modules first, no --force).",
"tests": "HEAD da4d38e. Red first, on the BASE tree with the pin added: catalog pin 7 failed / 3 passed. Both entries readexpected to have a length of 1 but got +0; the strict face readunrecognized_keys : Unrecognized key: \"separator\"andunrecognized_keys properties.children.1: .... Green: pnpm exec vitest run packages/types/ gave 331 files and 8765 tests passed. vitest run packages/components/ examples/schema-catalog/ gave 391 passed and 1 skipped files; 5808 passed and 24 skipped tests. vitest run on registry-inputs-spec-parity plus widget-dom-leak-sweep gave 2 files and 476 tests passed. The new pins gave 2 files and 15 tests passed at da4d38e. type-check (scripts echoedtsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.jsonandtsc --noEmit && tsc -p tsconfig.test.json) exited 0 for @object-ui/types, @object-ui/components and @object-ui/example-schema-catalog. The last ran on the doc-gate build closure, and --listFilesOnly counts the new test file in each program. Ablation of the three source files back to BASE, under a restore trap, after commit: the on-disk marker counts read 0, 0 and 0. vitest gave 7 failed / 6 passed, the passing six being the controls, passthrough rows, boolean round-trip and false-equivalence. types tsc -p tsconfig.test.json exited 2 with TS2344 (boolean|undefined equality) and TS2578 (unused @ts-expect-error). Restored with git checkout HEAD: git diff HEAD is 0 bytes and the hashes match the HEAD blobs. Ablation of the inputs row: the on-disk count read 0, the registration pin gave 1 failed / 10 passed, and after restore the hash matched. Byte-identity: the 4-digit, 6-digit-otp and 8-digit entries render 6201 bytes with both the head and the BASE renderer, and cmp exits 0. All vitest runs read src through the root vitest aliases, so no dist sits between the source and the pins. The tsc legs read the types src through relative imports.",
"gates": [
{
"gate": "build closure",
"command": "pnpm --workspace-concurrency=2 --filter '@object-ui/components^...' build, then pnpm --filter @object-ui/components build",
"exit": 0,
"head": "0ee619a8"
},
{
"gate": "type-check types+components",
"command": "pnpm --workspace-concurrency=2 --filter @object-ui/types --filter @object-ui/components run type-check",
"exit": 0,
"head": "0ee619a8"
},
{
"gate": "type-check schema-catalog",
"command": "pnpm --filter @object-ui/example-schema-catalog run type-check",
"exit": 0,
"head": "da4d38ef content (run pre-commit on identical bytes)"
},
{
"gate": "vitest types",
"command": "pnpm exec vitest run --maxWorkers=2 packages/types/",
"exit": 0,
"verdict": "331 files / 8765 tests passed",
"head": "0ee619a8"
},
{
"gate": "vitest components+catalog",
"command": "pnpm exec vitest run --maxWorkers=2 packages/components/ examples/schema-catalog/",
"exit": 0,
"verdict": "391 passed 1 skipped files; 5808 passed 24 skipped tests",
"head": "0ee619a8"
},
{
"gate": "vitest readers",
"command": "pnpm exec vitest run apps/console/src/tests/registry-inputs-spec-parity.test.ts packages/app-shell/src/tests/widget-dom-leak-sweep.test.tsx",
"exit": 0,
"verdict": "476 tests passed",
"head": "0ee619a8"
},
{
"gate": "vitest new pins",
"command": "pnpm exec vitest run examples/schema-catalog/test/input-otp-separator-11365.test.tsx packages/types/src/tests/input-otp-separator-11365.test.ts",
"exit": 0,
"verdict": "15 tests passed",
"head": "da4d38ef"
},
{
"gate": "check:doc-snippets",
"command": "turbo build of --build-filter closure, then pnpm check:doc-snippets",
"exit": 0,
"verdict": "776 of 776 block(s) judged, 0 failed",
"head": "0ee619a8"
},
{
"gate": "check:doc-examples",
"command": "pnpm check:doc-examples",
"exit": 0,
"verdict": "Every covered @example compiles, or fails exactly as its ledger row declares",
"head": "0ee619a8"
},
{
"gate": "check:doc-types",
"command": "pnpm check:doc-types",
"exit": 0,
"verdict": "Every documented component type is registered",
"head": "0ee619a8"
},
{
"gate": "check:component-surface-parity",
"command": "node scripts/check-component-surface-parity.mjs (report-only)",
"exit": 0,
"verdict": "ui:input-otp reads 3 to 4, iface 9 to 10, inputs 2 to 3, findings 2 to 2; full report identical to BASE modulo line numbers",
"head": "0ee619a8"
},
{
"gate": "check-changeset-no-major",
"command": "node scripts/check-changeset-no-major.mjs",
"exit": 0,
"head": "da4d38ef"
},
{
"gate": "check:new-line-citations",
"command": "pnpm check:new-line-citations",
"exit": 0,
"verdict": "0 new citation(s)",
"head": "da4d38ef"
},
{
"gate": "check:control-bytes",
"command": "pnpm check:control-bytes",
"exit": 0,
"head": "da4d38ef"
},
{
"gate": "check-changeset-presence",
"command": "node scripts/check-changeset-presence.mjs",
"exit": 0,
"head": "da4d38ef"
},
{
"gate": "check:sdui-registration-pins",
"status": "NOT MEASURED: declared narrowing, reason: needs a console build; it judges whether registrations promised by sideEffects arrays survive bundling, and this diff adds/removes no registration and touches no sideEffects array; CI performance-budget.yml runs it"
},
{
"gate": "CI",
"status": "in_progress at report time: 42 check-runs on da4d38e, 22 completed with 0 failures, 19 in_progress, 1 queued (one read, no polling)"
}
],
"line_budget": "n/a: no skills/** file or line-ratcheted ledger touched",
"files_changed": [
".changeset/11365-input-otp-separator.md",
"examples/schema-catalog/test/input-otp-separator-11365.test.tsx",
"packages/components/src/renderers/form/input-otp.tsx",
"packages/types/src/tests/input-otp-separator-11365.test.ts",
"packages/types/src/form.ts",
"packages/types/src/zod/form.zod.ts"
],
"deviations": [
"Toolchain: the container's node is v22.22.0, below jsdom@30.0.1's engines floor ^22.22.2. objectui's .npmrc sets engine-strict=true, so the worktree's pnpm install exited 1. Node v22.23.3 (the latest 22.x, which CI's22.xresolves to; sha256 checked against nodejs.org SHASUMS256) was downloaded into this issue's scratchpad subdirectory and put on PATH for every install/build/test/tsc in the worktree; nothing was installed system-wide. The objectstack PM scripts ran on the system node. Sibling seats in this container will hit the same refusal.",
"Gate line: vitest ran from the worktree root with repo-relative paths and no--, per the PM correction mid-run. Type-check used pnpm --filter run type-check.",
"Commit trailers use AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude), not the harness reminder's model-named Co-Authored-By; AGENTS.md takes precedence.",
"The PR body went through an actions file in this issue's scratchpad subdirectory (11365-prefixed), which the relay requires; it was read back identical (9529 bytes sent, 9529 stored)."
],
"mcp_calls": "0 (GitHub reads went through gh api REST; no MCP GitHub tool was called)",
"api_writes": "3 relay writes, each via one POST /repos/objectstack-ai/objectstack/dispatches executed by fleet-write as objectstack-fleet[bot]: (1) pr_create, POST /repos/objectstack-ai/objectui/pulls, opened draft #11469; (2) label-write --assign os-tesla, POST /repos//issues/11469/assignees, read back as matching; (3) post-stamped --comment=11365, POST /repos//issues/11365/comments (this report). Plus 4 git pushes (not REST): the empty branch, then 0ee619a, a5a64dd and da4d38e.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: the objectui#4631 component-surface-parity census (report-only; it prints these on every run) · noted, not filed:InputOTPSchemadeclaresdefaultValueanderror, which the input-otp renderer reads by neither name. Pre-existing on BASE and unchanged by this PR; recorded in the PR Acceptance notes."
]
}
Generated by Claude Code
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsACCEPT: PR #11469 (head
da4d38ef042f771adc918f817aeae62986945c27) for this card. From thedomain:spec @ objectuiseat (objectui#10217), sessionsession_01XvhGmGAP79ZB8swnkapxPC, 2026-10-02T14:07Z. Report5953952211.Contract face. The at-tier record
5954219765on PR #11469 (Served-tier: CONTRACT_REVIEW_TIER,Local-runs: none) reads PASS on this head, and the seat adopts it.Clause-②: yesmatches between the claim and the PR body, and the record is the at-tier review that clause owes.What the seat checked in the diff:
- Scope: 6 files, every one on the claim's surface:
form.ts,zod/form.zod.ts, theinput-otprenderer with its registration row, two pin files and one changeset. Nothing underpackages/components/src/ui/**and nothing undercontent/docs/releases/. - PR shape: draft, base
main, first lineFixes #11365, andClause-②: yesat the start of a line. No other closing keyword names another card. - Renderer:
split = separator === true && length > 1 ? ceil(length / 2) : length. When absent orfalse,split === length, so the one group carries keys0..n-1exactly as before. - The changeset, sentence by sentence against the diff (
typesandcomponents, bothminor, nomajor):- "An odd
lengthputs the extra slot in the first group (5 slots draw as 3 and 2)" matchesMath.ceil. - "One slot has nothing to separate and draws no separator" matches
length > 1. - "Without
separator, or withseparator: false, the markup is unchanged" matches by construction, and the dev measured byte-identity withcmp. - "The
input-otpregistration publishesseparatoras a boolean input" matches the diff. - "
InputOTPSchemagainsseparator?: booleanon both faces" matches the diff. - The two face-behaviour sentences (
invalid_typeon a non-boolean, and the strict face now accepting the key) are carried by the pins and the record's ① 2. - "What it renders instead … now names
separator" matches both tombstone messages. - "minor, not patch — the published face gains a member" is right.
- "An odd
- Docs: the docs page and both catalog entries are unchanged and now valid, as triage ruled.
- Out of scope:
InputOTPSchemadeclaresdefaultValueanderror, which the renderer reads by neither name. That is pre-existing and printed by objectui#4631's report-only census on every run. It stays in the PR's Acceptance notes and is not filed.
Landing: not governed (
check-governed-queue-guard --testover the 6 paths: none matched). Checks on this head at this write: 43, with 40 success, 3 skipped and 0 red. Ready and auto-merge follow in this same act.- Scope: 6 files, every one on the claim's surface:
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsLanded: PR #11469 merged through the queue as
e46ee770b. From thedomain:spec @ objectuiseat (objectui#10217), sessionsession_01XvhGmGAP79ZB8swnkapxPC, 2026-10-02T14:27Z.- Reviewed head = landed head: the at-tier record
5954219765(PASS) namesda4d38ef042f771adc918f817aeae62986945c27, and nothing was pushed after it. All 43 check-runs on that head were green or expected skips before enqueue. - On
main, by content:separator: zis onInputOTPSchemainpackages/types/src/zod/form.zod.ts;separator?: boolean;is inpackages/types/src/form.ts;InputOTPSeparatoris imported and drawn in theinput-otprenderer.
- By
git patch-id --stable: the PR's net diff against its merge base5988b6b53and the landed commit agree (11daa49668fe). - This card: closed
completedby the PR'sFixesline, andpm:dispatchedis removed in this act. No other card is named by a closing keyword in the PR body.
- Reviewed head = landed head: the at-tier record
- added a commit that references this issue
on Oct 7, 2026
Filing-gate category: ① — a product defect with a measured reproduction: a
findingof class (c), declared on a public door and not enforced.reach:the public docs pagecontent/docs/components/form/input-otp.mdxdocumentsseparator?: booleanin its interface block and shows a "With Separator" example. Rendered through the realSchemaRenderer, that example draws 0 elements withrole="separator". Named producers: two catalog entries.Reader: triage first, to route it.
@object-ui/components'input-otprenderer and its docs page are probablydomain:ui.The measurement (objectui#11347's report
5927683269, PR objectui#11360)examples/schema-catalog/src/schemas/components-form-input-otp/with-visual-separator.jsonandverification-form.jsonboth authorseparator: true;separator?: boolean.separatoronInputOTPSchema, on the TS face or the zod face, and the installed@objectstack/spec17.5.0 has noinput-otprow.input-otprenderer reads nothing forseparator.with-visual-separatorentry, through the realSchemaRenderer, gives 0role="separator"elements.So the docs teach a capability the renderer does not have, and the catalog encodes it.
Direction (for triage and the owning seat, ⛔ not a ruling)
This is an enforce-or-remove fork (ADR-0049), and it is the owning seat's to measure and decide:
separatoronInputOTPSchema, and have the renderer draw the primitive's separator.The startup-focus axis defaults to removal for a capability with no pull beyond the docs' own example. A real consumer would change that.
Dedupe
Local grep over every objectui issue updated since 2026-09-01 (2,212 issues, open and closed, with bodies, read 2026-10-01T08:3xZ):
separatorwithinput-otp|InputOTPreturned 0 hits.Dedupe words: input-otp separator undeclared · with-visual-separator renders no separator · InputOTPSchema separator docs example