feat(types): FilterFieldSchema.type — the published doc's fourteen members, optional - #8766
Merged
os-bill merged 2 commits intoSep 9, 2026
Merged
Conversation
…teen members Of the three declarations of the `filter-builder` authoring surface — the published doc, the component, and this zod mirror — the doc is the authority (objectui#7562, director seat, decision batch #88, 2026-09-08). The component already follows it; the mirror was the outlier on both axes, offering seven `type` members where the doc offers fourteen and REQUIRING a key the doc marks optional. So a `fields` entry written against our own documentation, which the renderer draws correctly, was refused by our own validator. The ruling carried a precondition, measured before the enum moved: every one of the fourteen has a renderer branch, or it comes OUT of the doc instead. One condition row per member was driven through the real `FilterBuilder` and both the value control and the operator bucket were read. All fourteen have a branch, so nothing was withdrawn from the doc and the mdx is untouched by this change. `text` is the one member whose branch is by NAME rather than by a distinct control — it IS the unrecognised-word fallthrough target, so it measures identical to a nonsense spelling. The renderer names it at `valueFamilyForFieldType`'s `fieldType || "text"`, which is both why it stays and why `type` is safe to leave optional. `string`, named nowhere, stays refused. The mirror-test pins move with the accept set: the seven `still refuses the live-but-unruled spelling …` assertions become `accepts …, and the renderer draws it`, each paired with the literal bucket that carries it, and the doc-vs-mirror assertion's closing line inverts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012W3vMLTFY9SPr2LyxhSeYi
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
This was referenced Sep 9, 2026
os-bill
marked this pull request as ready for review
September 9, 2026 05:16
os-bill
deleted the
claude/issue-7562-filter-builder-fourteen-type-members
branch
September 9, 2026 05:33
This was referenced Sep 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #7562
⛔ DRAFT, deliberately — do not flip ready, do not enqueue, do not enable auto-merge.
Clause-②: yes(this widens the accept set of a published authoring surface), so underthe rule that landed 2026-09-08T02:17:47Z it is BUILT at this tier and REVIEWED at
CONTRACT_REVIEW_TIER. That ceiling review has not happened yet.needs:contract-reviewis on the PR.
The ruling this executes
objectui#7562, director seat, decision batch #88, 2026-09-08 (comment
5582379508):of the three declarations of the
filter-builderauthoring surface — the published doc,the component, and the zod mirror — the published doc is the authority. Items 1 and 2
land together:
FilterFieldSchema.typewidens to the doc's fourteen members and becomesoptional (
textwhen absent, as the renderer already reads it).The ruling carried a precondition measured before the mirror moved: every one of the
fourteen has a renderer branch. A member with none would have come OUT of the doc in this
same PR. All fourteen have one, so the doc is untouched by this change.
The measurement leg (the precondition)
Measured on
e3fb3b6with a throwaway harness that drives one condition row per memberthrough the real
FilterBuilder(not a parse-then-render path) and reads threethings: the value control, the number of comboboxes on the row, and the operator bucket
operatorsForFieldTypeanswers with. Three controls were probed alongside — a nonsensespelling,
string, and an absenttype— because "it draws something" is only a findingagainst something that draws the fallthrough. Read
input=[...]as thetypeattributeof each
inputelement on the row;comboscountsrole="combobox".custom/filter-builder.tsxtext:408(fieldType || "text"),:405,:431input=["text"], combos=2, 35 elsnumbernumberLikeTypes:931input=["number"], combos=2currencynumberLikeTypes:931input=["number"], combos=2percentnumberLikeTypes:931input=["number"], combos=2ratingnumberLikeTypes:931input=["number"], combos=2date:411,dateLikeTypes:933input=["date"], combos=2datetime:412,dateLikeTypes:933input=["datetime-local"], combos=2time:413,dateLikeTypes:933input=["time"], combos=2boolean:410equals/notEqualsonlyselectselectLikeTypes:935in/notIn)statusselectLikeTypes:935in/notIn)lookuplookupLikeTypes:947referenceToand no options, the remote picker (:1277)in/notIn)master_detaillookupLikeTypes:947lookupin/notIn)userlookupLikeTypes:947referenceTo(:1277names it)in/notIn)input=["text"], combos=2, 35 elsstringinput=["text"], combos=2, 35 els:408suppliestextinput=["text"], combos=2, 35 elsVerdict: fourteen of fourteen have a renderer branch. Nothing removed from the doc.
textis the one member the ruling's DOM criterion cannot decide, and why it is not a forktextis the unrecognised-word fallthrough target, so it measures byte-identical to anonsense spelling and to an absent
type— same 35 elements, same control, same operatorbucket, all three. That is exactly the reading that made
stringa phantom inobjectui#6939. I checked whether this is the Zone-1 fork condition and concluded it is
not, on two independent grounds that point the same way:
textwhen absent, as therenderer already reads it» — the ruling asserts the renderer reads
text. Item 2cannot land while
textis deleted, and items 1 and 2 "land together".textis accepted by the mirror today,is what the registration's
defaultPropsand all five catalog entries author, andrenders correctly. Withdrawing it is the published-promise retraction the ruling
refused.
The criterion that survives both is the one the mirror's own docblock already used to
separate
stringfromtext: is the spelling NAMED in the renderer?textis, atthree sites (
:408where an absenttypeacquires it, theFilterValueFamilyunion at:405, theFILTER_INPUT_TYPE_BY_FAMILYkey at:431).stringis named at none. Bothfacts are now pinned in a test rather than left as prose.
Reverse verification — both legs, from the committed state
Each leg: mutate → prove the bytes moved on disk (blob hash before/after, anchor
occurrence count) → run → restore via
git checkout HEAD -- path→ provegit diff HEADis empty and the blob is back.
trap ... EXIT INT TERMon absolute paths throughout.'currency'from theexpectTypeunion inthe mirror test. Blob
0be5ad6→743628b, anchor 1 → 0.pnpm --filter @object-ui/types type-check→ exit 2,filter-builder-mirror-6939.test.ts(149,12): error TS2344: Type 'false' does not satisfy the constraint 'true'.⇒tsconfig.test.jsonreally compiles this file, so thecompile-time half of the accept set is measured and not merely written down.
type. Dropped'user'from the zod enumonly, leaving the TS union at fourteen. Blob
489fe69→443504e, anchor 1 → 0.Same command → exit 2,
zod-mirror-parity.test.ts(2746,14): error TS2322: Type '"complex.zod.ts#FilterFieldSchema"' is not assignable to type 'never'.git statusclean.Leg B is also the answer to a premise this card was dispatched with. The dispatch expected
the widening to move a
FilterFieldSchemarow in the parity ledger. It did not, and theledger file is not in this diff. Leg B shows why: the ledger reddens the moment the two
faces disagree, and this change moved both faces symmetrically. The ledger's three existing
FilterFieldSchemarows are all aboutoperators, a pre-existing drift this card does nottouch.
What is still refused, on purpose
string— named nowhere in the renderer, reaches the text control only by thefallthrough. The phantom objectui#6939 removed; the published doc does not offer it
either, so the two faces agree.
{ value, label, type: 'zzz' }is still refused, pinned in the same test as the optional-
typeaccept, so this cannotbe read as "
typestopped being checked".Out of scope, and left alone
FilterBuilderConditionSchema.id— objectui#8415, split out at comment5575381230.id— stays optional. objectui#7560 measured zero read sites for it andthe condition-
idanswer does not transfer.packages/components/src/custom/filter-builder.tsx— the measurement subject,read-only for this card and not in the diff.
Verification
Commands and their own printed verdicts, all at final HEAD
0b92457(after theorigin/mainmerge, which pulled in a sibling'spackages/typeschanges and so invalidatedthe pre-merge readings):
pnpm --filter @object-ui/types type-check(tsc --noEmit+ examples + test projects)pnpm exec vitest run packages/types/—Test Files 154 passed (154),Tests 3065 passed (3065)pnpm exec eslint .inpackages/types—224files, 0 errors, 266 warningspnpm check:control-bytespnpm check:doc-typespnpm check:spec-symbolspnpm check:handler-key-readsnode scripts/check-changeset-presence.mjsRun pre-merge and unaffected by the merge (none of them reads a file the merge moved):
pnpm exec turbo run build $(node scripts/check-doc-snippet-types.mjs --build-filter) --concurrency=2(35/35), then
check:doc-examples·check:doc-snippets·check:readme-exports·check:dist-completeness·changeset:check— all 0.check:doc-fences·check:designer-field-key-parity·check:shell-escape-residue— all 0. The renderhalf plus the retired-field-type pin —
pnpm exec vitest run examples/schema-catalog/ packages/components/src/__tests__/filter-builder-retired-field-type.test.tsx—32 files / 2160 tests passed, exit 0.Lint narrowing, declared.
turbo run lintcovers 40 packages; this ran the one packagethe diff touches. The population is eslint's own —
224files, counted from the length ofits
--format jsonoutput, not estimated. The narrowing is sound because type-awarelinting is off in this repo:
eslint.config.jsextendstseslint.configs.recommended(not
recommendedTypeChecked) and sets noparserOptions.project/projectService, soevery file is judged from its own AST alone and this diff cannot move the verdict on a file
it does not touch. CI runs the full farm.
Acceptance notes
optionsrender a remote-search picker" istrue but under-specified: measured,
lookupandmaster_detailalso needreferenceTo(only
userdefaults its own). The doc listsreferenceToin the very next lines, sothis is a reading nit, not a false promise — noted, not filed.
scripts/measure-strict-authoring-face.mjsquotesFilterFieldSchema's enumillustratively with an elision; still accurate after this change and the script is not
wired into any workflow — noted, not filed.
pnpm --filter PKG exec vitest ...is refused by thisrepo's guard (objectui#3378) because it re-roots vitest at the package dir and silently
runs another package's suite. Run vitest from the repo root.
Generated by Claude Code