Found by the os-dev seat while implementing the filter-builder group of #6939 (PR #7560). Out of that group's scope: the ruling in #6939 comment 5510084784 names the six value-family members for FilterFieldSchema.type and says nothing about the three gaps below, each of which is an accept-set change of its own. Grading and domain:* are the triage seat's.
PR #7560 pins all three with assertions rather than leaving them as absences, so they are recorded decisions; this card is where they get decided.
1. Seven live field types the mirror refuses
After #7560, FilterFieldSchema.type is text | number | boolean | date | datetime | time | select. The component buckets seven more spellings and draws a DISTINCT control for each — measured, one condition row per spelling, through the real SchemaRenderer:
type |
source of the bucket |
value control drawn |
currency, percent, rating |
numberLikeTypes |
input type="number" |
status |
selectLikeTypes |
no input — the option-driven Select |
lookup, master_detail, user |
lookupLikeTypes |
no input — the option Select, or the remote search picker without static options |
All seven were refused BEFORE #7560 as well, so this is a pre-existing gap and not a regression that PR introduces — but it is the same class as #6939 itself: the mirror refuses a document the renderer draws.
2. type is REQUIRED on the mirror and optional everywhere else
FilterBuilderProps.fields declares type?: string. valueFamilyForFieldType reads fieldType || "text" and operatorsForFieldType reads fieldType || "text", so an entry with no type is a text column. Measured: deleting type from every field of product-search still renders all three rows (76 elements, identical text; only the value inputs degrade from text/number/number to three text boxes). The mirror refuses { value, label }.
3. The published doc already declares the wider shape
content/docs/components/complex/filter-builder.mdx publishes
type?:
| 'text' | 'number' | 'currency' | 'percent' | 'rating'
| 'date' | 'datetime' | 'time'
| 'boolean'
| 'select' | 'status'
| 'lookup' | 'master_detail' | 'user';
— fourteen members and OPTIONAL. So the doc, the component and the mirror are three declarations of one authoring surface and the mirror is the odd one out on both counts. @object-ui/fields' deriveFilterFields, the producer that builds this list from a real object schema, passes ANY object field type through except thirteen it names non-filterable, which is the population the wide vocabulary exists for.
4. FilterBuilderConditionSchema omits id, which the component reads at four sites
The mirror declares a condition as { field, operator, value? }. The component's FilterBuilderCondition is { id, field, operator, value } and id is the identity every mutation matches on: removeCondition (c.id !== conditionId), updateCondition, changeOperator, changeField, plus the row's React key. All five catalog entries author it. Because a plain z.object STRIPS unknown keys, id is accepted-and-discarded today rather than refused — undeclared and unvalidated while being read, which is the shape #6150 closed for tree-view.title.
A row with no id still renders, but nothing on it can be removed or edited, so "required" is a defensible answer here in a way it is not for the GROUP's id (which has zero read sites — see #7560).
Reproduce
pnpm exec vitest run packages/types/src/__tests__/filter-builder-mirror-6939.test.ts
still refuses the live-but-unruled spelling ... (x7) and the gap is measured against the PUBLISHED doc, not against a private opinion are the measurements for 1-3.
Related
Generated by Claude Code
Found by the
os-devseat while implementing thefilter-buildergroup of #6939 (PR #7560). Out of that group's scope: the ruling in #6939 comment 5510084784 names the six value-family members forFilterFieldSchema.typeand says nothing about the three gaps below, each of which is an accept-set change of its own. Grading anddomain:*are the triage seat's.PR #7560 pins all three with assertions rather than leaving them as absences, so they are recorded decisions; this card is where they get decided.
1. Seven live field types the mirror refuses
After #7560,
FilterFieldSchema.typeistext | number | boolean | date | datetime | time | select. The component buckets seven more spellings and draws a DISTINCT control for each — measured, one condition row per spelling, through the realSchemaRenderer:typecurrency,percent,ratingnumberLikeTypesinput type="number"statusselectLikeTypeslookup,master_detail,userlookupLikeTypesAll seven were refused BEFORE #7560 as well, so this is a pre-existing gap and not a regression that PR introduces — but it is the same class as #6939 itself: the mirror refuses a document the renderer draws.
2.
typeis REQUIRED on the mirror and optional everywhere elseFilterBuilderProps.fieldsdeclarestype?: string.valueFamilyForFieldTypereadsfieldType || "text"andoperatorsForFieldTypereadsfieldType || "text", so an entry with notypeis a text column. Measured: deletingtypefrom every field ofproduct-searchstill renders all three rows (76 elements, identical text; only the value inputs degrade fromtext/number/numberto three text boxes). The mirror refuses{ value, label }.3. The published doc already declares the wider shape
content/docs/components/complex/filter-builder.mdxpublishes— fourteen members and OPTIONAL. So the doc, the component and the mirror are three declarations of one authoring surface and the mirror is the odd one out on both counts.
@object-ui/fields'deriveFilterFields, the producer that builds this list from a real object schema, passes ANY object field type through except thirteen it names non-filterable, which is the population the wide vocabulary exists for.4.
FilterBuilderConditionSchemaomitsid, which the component reads at four sitesThe mirror declares a condition as
{ field, operator, value? }. The component'sFilterBuilderConditionis{ id, field, operator, value }andidis the identity every mutation matches on:removeCondition(c.id !== conditionId),updateCondition,changeOperator,changeField, plus the row's Reactkey. All five catalog entries author it. Because a plainz.objectSTRIPS unknown keys,idis accepted-and-discarded today rather than refused — undeclared and unvalidated while being read, which is the shape #6150 closed fortree-view.title.A row with no
idstill renders, but nothing on it can be removed or edited, so "required" is a defensible answer here in a way it is not for the GROUP'sid(which has zero read sites — see #7560).Reproduce
still refuses the live-but-unruled spelling ...(x7) andthe gap is measured against the PUBLISHED doc, not against a private opinionare the measurements for 1-3.Related
safeValidateSchemabecause the Zod mirror names a key its renderer never reads (8 components) #6939 — the parent cardsafeValidateSchemabecause the Zod mirror names a key its renderer never reads (8 components) #6939's row does not reachGenerated by Claude Code