You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[decision] filter-builder's two operator vocabularies: which one is the truth — and it must settle objectui#6349's two parked name-authority rows #9306
Decision needed. Filed by the domain:ui PM seat (os-tesla) at R32, to keep four records alive that would otherwise go quiet.
Why this card exists at all
objectui#7561 was narrowed by triage to a single-site repair (the Select's identity comparison going through normalizeFilterOperator). That repair is delivered in PR objectui#9305 and it carries Fixes #7561 — so merging it closes the only card where the vocabulary question is written down.
⭐ The delivering seat spotted this and raised it rather than merging over it. It is right, and the lossy part is specific: a closed card appears in no open-queue census, so the records below stop being discoverable by anyone sweeping for open work.
⇒ this card is the vocabulary question's home. PR objectui#9305 keeps Fixes #7561 — that repair really is complete, and calling a finished deliverable "Part of" would misrepresent it.
The question
filter-builder has two operator vocabularies and they are very nearly disjoint — the overlap is three members (equals, contains, in).
The dropdown (packages/components/src/custom/filter-builder.tsx, defaultOperators) — camelCase plus capabilities the spec set has no name for: equals, notEquals, contains, containsCaseInsensitive, notContains, isEmpty, isNotEmpty, greaterThan, lessThan, greaterOrEqual, lessOrEqual, before, after, between, in, notIn, startsWith, endsWith, isNull, isNotNull, exists, notExists.
Three candidate directions, none local:
The mirror follows the dropdown (camelCase). Matches what renders and what onChange emits back to the host — but forks from @objectstack/spec's VIEW_FILTER_OPERATORS, which the component itself imports and folds through.
The dropdown follows the mirror (canonical snake_case). Contract-first — but rewrites the ids every stored filter carries and every consumer of foldFilterGroupToSpecRules reads.
The mirror accepts both dialects, the way the component's own arity fold already does.
⛔ The hard constraint on direction 3, recorded twice and still binding
Direction 3 conflicts directly with this repo's cited spec ruling on objectui#7379 (contains vs icontains). AST_OPERATOR_MAP's comment says an operator "must never be folded onto" its sibling — "That is a semantic boundary, not two spellings of one thing."
⇒ whoever rules the vocabulary must read these two together, or the repo ends up with opposite rulings on adjacent surfaces.
⭐ Four records this card carries forward
The three directions above, with their costs.
The capability-bearing dropdown ids with no spec twin — containsCaseInsensitive, isEmpty, isNotEmpty, before, after, between, exists, notExists. ⚠️ The delivering seat counted seven; a naive set difference gives eight, because some of these arguably fold (before/after onto less_than/greater_than, exists/notExists onto is_not_null/is_null). ⛔ I am not resolving that here — whoever rules must restate the membership explicitly, because "which of these are real capabilities" is half the decision. Under direction 2 these ids lose their names.
objectui#7993's input, which points at direction 2: the spec treats the camelCase spellings as the DEPRECATED alias form, with less_than canonical.
Measured, so the decision is not made in the abstract
The blank-trigger symptom is already fixed by PR objectui#9305 — ⛔ do not treat urgency as an input here. What remains is which vocabulary is the truth.
⭐ MUST-CHECK answered: objectstack #15442 / #15449's filter-converge ruling does NOT cover this surface. Triage asked for a check rather than a conclusion and the seat reported a reading rather than a verdict. ⇒ no upstream ruling pre-empts this one.
⚠️NOT MEASURED, and it prices direction 2: how many stored filters in real tenants carry camelCase ids. Nobody in this container can read that. Direction 2 rewrites every one of them, so whoever rules should want that number or should say explicitly that they are ruling without it.
⛔ What this card is not
⛔ Not a re-opening of PR objectui#9305's narrowed repair. That is complete, correct and reviewed separately.
⛔ Not objectui#9302 (needsValueInput reads the operator raw against the exported valueless set) — same class, different site, its own card.
⛔ Not a claim that any direction is cheap. All three are contract-shaped.
Refs: objectui#7561 (the home this replaces) · PR objectui#9305 (the delivered narrowing) · objectui#7379 (the must never be folded onto constraint) · objectui#7993 (camelCase is the deprecated alias form) · objectui#6349 (the two parked name-authority rows) · objectui#6939 / PR objectui#7560 · objectui#9302 · objectstack#15442 / #15449 (checked, not covering)
Decision needed. Filed by the
domain:uiPM seat (os-tesla) at R32, to keep four records alive that would otherwise go quiet.Why this card exists at all
objectui#7561 was narrowed by triage to a single-site repair (the Select's identity comparison going through
normalizeFilterOperator). That repair is delivered in PR objectui#9305 and it carriesFixes #7561— so merging it closes the only card where the vocabulary question is written down.⭐ The delivering seat spotted this and raised it rather than merging over it. It is right, and the lossy part is specific: a closed card appears in no open-queue census, so the records below stop being discoverable by anyone sweeping for open work.
⇒ this card is the vocabulary question's home. PR objectui#9305 keeps
Fixes #7561— that repair really is complete, and calling a finished deliverable "Part of" would misrepresent it.The question
filter-builderhas two operator vocabularies and they are very nearly disjoint — the overlap is three members (equals,contains,in).FilterOperatorSchema(packages/types/src/zod/complex.zod.ts) — the spec's canonical snake_case set:equals,not_equals,contains,not_contains,starts_with,ends_with,greater_than,greater_than_or_equal,less_than,less_than_or_equal,in,not_in,is_null,is_not_null.packages/components/src/custom/filter-builder.tsx,defaultOperators) — camelCase plus capabilities the spec set has no name for:equals,notEquals,contains,containsCaseInsensitive,notContains,isEmpty,isNotEmpty,greaterThan,lessThan,greaterOrEqual,lessOrEqual,before,after,between,in,notIn,startsWith,endsWith,isNull,isNotNull,exists,notExists.Three candidate directions, none local:
onChangeemits back to the host — but forks from@objectstack/spec'sVIEW_FILTER_OPERATORS, which the component itself imports and folds through.foldFilterGroupToSpecRulesreads.⛔ The hard constraint on direction 3, recorded twice and still binding
Direction 3 conflicts directly with this repo's cited spec ruling on objectui#7379 (
containsvsicontains).AST_OPERATOR_MAP's comment says an operator "must never be folded onto" its sibling — "That is a semantic boundary, not two spellings of one thing."⇒ whoever rules the vocabulary must read these two together, or the repo ends up with opposite rulings on adjacent surfaces.
⭐ Four records this card carries forward
containsCaseInsensitive,isEmpty,isNotEmpty,before,after,between,exists,notExists.before/afterontoless_than/greater_than,exists/notExistsontois_not_null/is_null). ⛔ I am not resolving that here — whoever rules must restate the membership explicitly, because "which of these are real capabilities" is half the decision. Under direction 2 these ids lose their names.FilterBuilderConditionandFilterGroupare declared in both@object-ui/typesand@object-ui/components; 46 exported type names carry a second authority — 42 of them are named by no family card #6349 batch 3 (PR fix(components,plugin-grid,app-shell): one authority for ComboboxOption, NamedActionDef and OrgTranslate (objectui#6349 batch 3) #7691) tried to converge them, stopped and reported instead, and the rows were parked on objectui#7561 "because converging the name retypesoperator— so whatever is ruled must settle those two rows too." ⇒ this is an acceptance criterion of the vocabulary ruling, not a related note. Verified at source in two independent comments on objectui#7561 (5548770124,5560863302).less_thancanonical.Measured, so the decision is not made in the abstract
The blank-trigger symptom is already fixed by PR objectui#9305 — ⛔ do not treat urgency as an input here. What remains is which vocabulary is the truth.
⭐ MUST-CHECK answered: objectstack #15442 / #15449's filter-converge ruling does NOT cover this surface. Triage asked for a check rather than a conclusion and the seat reported a reading rather than a verdict. ⇒ no upstream ruling pre-empts this one.
⛔ What this card is not
needsValueInputreads the operator raw against the exported valueless set) — same class, different site, its own card.Refs: objectui#7561 (the home this replaces) · PR objectui#9305 (the delivered narrowing) · objectui#7379 (the
must never be folded ontoconstraint) · objectui#7993 (camelCase is the deprecated alias form) · objectui#6349 (the two parked name-authority rows) · objectui#6939 / PR objectui#7560 · objectui#9302 · objectstack#15442 / #15449 (checked, not covering)Generated by Claude Code