Skip to content

feat(spec)!: narrow ObjectMasterDetailFormPropsSchema.formType to the measured simple | tabbed - #11963

Merged
os-support-ai merged 3 commits into
mainfrom
claude/issue-11873-masterdetail-formtype
Aug 25, 2026
Merged

feat(spec)!: narrow ObjectMasterDetailFormPropsSchema.formType to the measured simple | tabbed#11963
os-support-ai merged 3 commits into
mainfrom
claude/issue-11873-masterdetail-formtype

Conversation

@os-support-ai

Copy link
Copy Markdown
Collaborator

Fixes#11873

What

ObjectMasterDetailFormPropsSchema.formType narrows from bare z.string() to the measuredz.enum(['simple', 'tabbed']) — the spec half of objectui#5939, which already tightened the objectui registry declaration to the same pair on the same measurement. Direction per triage (comment 5403285119): not open; only the retirement mechanics were this card's work.

Census (triage constraint 2 — run BEFORE narrowing)

In-repo authored-value census over every formType on master-detail blocks (fixtures, examples, catalog entries, tests, docs — *.ts/tsx/js/json/md/mdx/yml/yaml, excluding node_modules/dist/.cache): 106 repo-wide formType mentions, all classified; zero out-of-vocabulary values on master-detail blocks. The three authored master-detail values are all 'simple':

  • examples/app-showcase/src/ui/pages/project-workspace.page.ts:45
  • packages/spec/src/ui/component.test.ts (showcase master-detail pin)
  • packages/lint/src/validate-component-props.test.ts (master-detail node)

Positive control: the same sweep resolves real authored out-of-simple|tabbed values on the sibling object-form block — 'wizard' (component.test.ts, validate-component-props.test.ts, new-project-wizard.page.ts) and 'drawer'/'modal' (task-desk / renewals-pipeline JSX pages) — proving it sees authored values, not just declarations. objectui's equivalent census (objectui#5939) also read zero. No premise fork.

Retirement mechanics — why no ADR-0087 conversion entry

Both precedents (#8762, #8010) registered a conversion + migration entry; this narrowing deliberately does not, and the reasoning is recorded in the schema comment:

  1. No gate requires one (PM mechanism assumption, verified): check:authorable-surface tracks keys (the key survives), and no gate diffs enum values against the registries. check-adr-0087-registration requires the question answered in writing — the changeset carries the adr-0087: not-required (no-migration-prescription) disposition, and the gate accepts it (green).
  2. Unlike both precedents — where the dropped values were the schema's own declared vocabulary (and, for chatter, its default) — these four names were never declared for this block: the key was bare z.string(). There is nothing spec-promised to rewrite, and both censuses read zero authored occurrences.
  3. The conversion route would force packages/spec/src/migrations/registry.ts (conversionIds list + semantic entry), which is HELD by a sibling family dispatch this wave — the dispatch constraint forbids touching it. Since (1) and (2) make the entry unnecessary rather than deferred, this is a completed shape, not a debt; if the maintainer wants a belt-and-braces conversion (rewriting the four names → 'simple') it can land as a follow-up once the hold lifts.

Tests

  • Acceptance pins: 'simple' and 'tabbed' parse green on object-master-detail-form.
  • Rejection pins assert the specific zod issue — code: 'invalid_value', path: ['formType'] — for 'wizzard' (plain enum refusal, no prescription) and for each of wizard/split/drawer/modal (per-value prescription naming the honoured pair and object-form).
  • type-alias-convention.pin.test.ts Iso844 (input ≡ infer) verified green without edits — an error-mapped enum with no default/transform stays isomorphic, so the pin file is deliberately not in the diff.

Gates

Full @objectstack/spec build + check:generated (→ gen:docs regenerated content/docs/references/ui/component.mdx, included; re-run green after fix) + full spec test suite: Test Files 424 passed (424) / Tests 11281 passed (11281) (lock verdict command-exit 0, run at 7b3020f; the one later commit 11294ac touches only the .changeset/ marker, no test input) + typecheck incl. the test layer, check:liveness, check:empty-state, check:strictness-ledger, check:variant-docs — all green at final head 11294ac (command-exit 0). check-adr-0087-registration green at 11294ac (disposition accepted). Reverse verification from the committed state: reverting the enum to z.string() (mutation disk-confirmed: injected=1 / removed-remaining=0) turns exactly the 5 rejection pins red (5 failed | 2 passed) with the 2 acceptance pins green, restore disk-confirmed (get-calls=1). Full gate readings quoted in the report comment on #11873. Gate family re-derived from the actual diff with node scripts/pm/dispatch-gates.mjs.

Breaking ships as minor per the launch-window convention (scripts/check-changeset-no-major.mjs); the changeset names the four newly-rejected values and points authors at the two honoured ones.

Generated by Claude Code


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to listnot a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • the SDK route bridge reached 45 of 222 client-bound route-ledger rows — the other 177 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 126 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 178f90c768155577dfacc04d5cf03aa4c5470e85packageMentionDocs.

Which tree this was computed on

This run read content/docs from 4a0ff18206cc7e9de5b6e5c1aa40e85187ab1dcc — the merge of head 11294ac5337c698ad7e178f55afa5be20847eb03 into base 178f90c768155577dfacc04d5cf03aa4c5470e85, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 4a0ff18206cc7e9de5b6e5c1aa40e85187ab1dcc && git checkout 4a0ff18206cc7e9de5b6e5c1aa40e85187ab1dcc
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 178f90c768155577dfacc04d5cf03aa4c5470e85 11294ac5337c698ad7e178f55afa5be20847eb03 && git checkout -B drift-repro 178f90c768155577dfacc04d5cf03aa4c5470e85 && git merge --no-ff 11294ac5337c698ad7e178f55afa5be20847eb03
node scripts/docs-audit/affected-docs.mjs --json 178f90c768155577dfacc04d5cf03aa4c5470e85

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

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

Labels

documentationImprovements or additions to documentationprotocol:uisize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ObjectMasterDetailFormPropsSchema.formType accepts any string — the spec half of objectui#5939, where the renderer honours exactly simple | tabbed

2 participants

@os-support-ai@claude