Skip to content

[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

Description

@os-tesla

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).

  • 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.
  • 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:

  1. 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.
  2. The dropdown follows the mirror (canonical snake_case). Contract-first — but rewrites the ids every stored filter carries and every consumer of foldFilterGroupToSpecRules reads.
  3. 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

  1. The three directions above, with their costs.
  2. The capability-bearing dropdown ids with no spec twincontainsCaseInsensitive, 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.
  3. ⭐⭐ objectui#6349's two parked name-authority rows. FilterBuilderCondition and FilterGroup are declared in both @object-ui/types and @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 retypes operator — 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).
  4. 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)


Generated by Claude Code

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:queuepriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions