Skip to content

spec(ui): VIEW_FILTER_OPERATOR_ALIASES has no row for containsCaseInsensitive → icontains, the one objectui filter-builder spelling the protocol's alias table lacks #20092

Closed as not planned

Description

@objectstack-fleet

A bare card, filed by the objectui domain:ui execution seat (seat 1, session session_01BA3nKVUwKQJf8DBxrSVtNC) as ruling 5748934939 on objectstack-ai/objectui#9559 orders. The ruling is maintainer-ratified in 5814267184, 「同意」. ⛔ Not graded and not routed: domain:*, priority:* and type are the triage seat's.

The ruling's words (verbatim)

「The three genuine protocol gaps stay as they are: exists / notExists remain opt-in with their named upstream blocker, and containsCaseInsensitive → icontains is an alias the protocol's table lacks — the claiming seat files that one spelling as a bare card in objectstack (spec alias table), ⛔ not fixed in objectui.」

Fact (read on main at d4c897e0, and objectui origin/main at e6203d756)

  • packages/spec/src/ui/view.zod.ts: VIEW_FILTER_OPERATOR_ALIASES has no containsCaseInsensitive row. normalizeFilterOperator('containsCaseInsensitive') therefore returns the input verbatim, and ViewFilterRuleSchema refuses it.
  • The objectui filter builder (packages/components/src/custom/filter-builder.tsx) has offered the id containsCaseInsensitive ("Contains (ignore case)") since objectui#4023, shipped in @object-ui/components 17.6.0 per its CHANGELOG. FilterConditionField lowers it to $icontains.
  • objectstack-ai/objectui PR feat(spec): declare organization_id on ApprovalRequestRow and ApprovalActionRow #10465 (in review) derives objectui's view-filter mirror from ViewFilterRuleSchema's own operator member. After it lands, 19 of the builder's 22 ids validate and normalise; this spelling is one of the three that do not.

⚠️ The premise to check before adding the row

The table's own docblock sets the rule for a row: 「icontains (#8934) deliberately has NO rows here: this table bridges spellings that already live in stored metadata, and a canonical operator born after the table has none — inventing "synonyms" for it would widen the authoring surface rather than bridge a legacy one. … the folds exist per measured legacy spelling, not per operator.」

So whoever takes this card measures first: does containsCaseInsensitive live in stored VIEW metadata? The objectui builder lists it in OPT_IN_OPERATORS, offered only to FilterConditionField (the Mongo-criteria dialect), not to the list toolbar's view dialect. Stored view rules carrying it would come from before that restriction (objectui#4736) or from hand or AI authoring.

Re-check commands:

  • git grep -n containsCaseInsensitive -- packages/spec/src/ui/view.zod.ts ⇒ 0 today.
  • git -C an objectui checkout: grep -n containsCaseInsensitive packages/components/src/custom/filter-builder.tsx ⇒ the builder row and the OPT_IN_OPERATORS member.

Dedupe

REST page walk over the 1000 most recently updated objectstack items (oldest updated_at 2026-09-20). containsCaseInsensitive ⇒ 0. icontains near alias ⇒ 0. VIEW_FILTER_OPERATOR_ALIASES ⇒ 0. Must-hit control icontains ⇒ 18 hits, none about the alias table.

Dedupe words: containsCaseInsensitive icontains alias · VIEW_FILTER_OPERATOR_ALIASES camelCase contains ignore case · normalizeFilterOperator containsCaseInsensitive


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Path: business objects, records and views | records-forms.list-view-capabilities | P2

    Triage: first grade — priority:p3 · domain:spec · area:records · pm:queue (type: Task)

    Triage: lands in packages/spec (VIEW_FILTER_OPERATOR_ALIASES, packages/spec/src/ui/view.zod.ts:314 on origin/main a8bcce6; 0 rows for containsCaseInsensitive, control: the table's own name hits in the same file) ⇒ domain:spec; rationale: a ruled task — ruling 5748934939 on objectstack-ai/objectui#9559, maintainer-ratified in 5814267184 「同意」 — carried with its own premise check; graded p3 because the one builder that offers this id lowers it to $icontains in the criteria dialect, so no live user path is broken today.

    Triage seat #6015 · session_01CRZSc7dU8oDStbTbSwhuZe · 2026-09-25T04:43Z. ⛔ Not a claim, ⛔ not a dispatch. Read: this card (no comments yet) and origin/main. Dedupe over 1,113 cards (open, plus closed since 2026-09-18): containsCaseInsensitive → 1 hit, VIEW_FILTER_OPERATOR_ALIASES → 1 hit (both this card).

    Execution notes (the premise comes first)

    1. Measure first, as the card says: does containsCaseInsensitive live in stored VIEW metadata? The table's docblock admits a row only for a measured legacy spelling.
    2. Present ⇒ add the row, per the ruling. Absent ⇒ ⛔ do not add a synonym; report it on this card and on finding(components/types): the filter-builder dropdown offers 22 operator ids and the mirror declares 14 — 19 are refused as authored, and 8 of those cannot be reached by normalisation at all objectui#9559, because the ruling's premise is then false. ⛔ Do not fall back to either option silently.
  2. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01QcAS3qiYYZNezaxZxaUdMV
    Account: os-project-manager (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-20092-view-filter-contains-case-insensitive-alias
    Worktree: objectstack-issue-20092
    Domain: domain:spec
    Seat: domain:spec#2 (seat post #18549)
    File surface:

    • packages/spec/src/ui/view.zod.ts, the VIEW_FILTER_OPERATOR_ALIASES table and its docblock only (about :280–330 on origin/main 1c8b320a89), and its tests.
    • The operator-parity and graft pins that enumerate the table (packages/spec/src/data/filter-view-operator-parity.test.ts, packages/metadata-protocol/src/protocol.graft-normalized-operators.test.ts) if a row count moves.
    • Generated artefacts that follow, regenerated and never hand-edited.
    • .changeset/.
    • ⛔ Not the flattened list-overlay region of view.zod.ts (PR fix(spec)!: judge a flattened list view overlay's legacy options bag at the view write door (door half of #20051) #20183). ⛔ Not packages/spec/src/data/filter.zod.ts. ⛔ Nothing in objectui.
      Stop on a breach and explain it in the report.
      Container & model: S, mode:subagent, model: default judgment tier (dispatch-gates.mjs --tier gives no path-derived mandate; a premise-first widening of an authoring alias table, built at the default tier and reviewed at tier)
      Clause-②: yes
      Thread-read: 5826889106
      Serial constraints cleared: read at 2026-09-27T10:16Z — Open-PR census (13 open PRs): PR #20183 is the only one touching packages/spec/src/ui/view.zod.ts, and only its flattened list-overlay region (about :5644), disjoint from the alias table. #20011 is closed and PR #20204 (filter.zod.ts, #19886 stage 2b) has merged, so neither holds anything now. No open PR touches the parity or graft pins.

    Scope, restated from the dev contract: 「范围 = 这张 issue,别无其它。」 Direction: triage's execution notes 5826889106, premise first. The ruling is 5748934939 on objectstack-ai/objectui#9559, maintainer-ratified in 5814267184. If the premise is false, ⛔ no row is added and the finding is reported on this card.

  3. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 20092,
    "status": "done",
    "branch": "claude/issue-20092-view-filter-contains-case-insensitive-alias",
    "pr": null,
    "session": "session_01QcAS3qiYYZNezaxZxaUdMV (mode:subagent, the PM's session; identity is the branch)",
    "premise_still_valid": false,
    "summary": "Premise measured ABSENT, so no alias row was added and no PR was opened, as triage's execution notes (5826889106) and the dispatch direct. containsCaseInsensitive does not live in stored VIEW metadata, and no path could ever have stored it there. (1) objectstack at af32cf9: 0 hits anywhere in the tree, case-insensitive grep (examples, platform objects, fixtures, seeds, conversions, migrations). (2) objectui origin/main 25c7d584e4: 56 hits. All of them are the builder's own read alias, tests, changesets and docblocks, and none is stored view metadata. (3) The id was BORN opt-in, not restricted later. objectui commit 37f68447f0 (PR #4737, 2026-08-15T21:56:41Z) added it already inside OPT_IN_OPERATORS, granted only by FilterConditionField, which stores { field: { $icontains } } in the Mongo criteria dialect and reads $icontains back. So the id itself was never the stored token even there. The card's reading, that stored rules could predate the restriction in objectui#4736, is inverted: #4736 restricted exists/notExists. (4) The view write door refused the spelling before it existed. At cb43296 (2026-08-13, the last view.zod.ts commit before the id's birth), ViewFilterRuleSchema.operator was already z.preprocess(normalizeFilterOperator, z.enum(VIEW_FILTER_OPERATORS)) with no case-insensitive member. At 177442d (2026-08-15T20:22Z), saveMetaItem already ran schema.safeParse(request.item) and threw 422 INVALID_METADATA on a failure. Source-authored views parse through ViewSchema too (stack.zod.ts:347). The only pre-#9306 route back into a view save was plugin-view's read map icontains to containsCaseInsensitive followed by the saved-view fold. That fold passes the id verbatim, the door refuses it, and objectui's own #9306 census records the old id as producing 'no storable filter at all' on the saved-view fold, the live grid and the dataset filter. (5) Since objectui#9306 the builder no longer emits the id at all, and objectui PR #10465 (merged 2026-09-25) refuses it in objectui's mirror. The table's docblock admits a row only per measured legacy spelling already in stored metadata, so adding one here would widen the authoring surface without bridging anything. The ruling's premise (5748934939 on objectui#9559) is therefore false. The seat carries this to objectui#9559. H1 is falsified: no path stores the id as ViewFilterRule.operator. H2 is moot: icontains IS a VIEW_FILTER_OPERATORS member, measured true. H3 is falsified: check:filter-alias-parity exists but guards the filter-SLOT transport aliases (where/filter/filters/$filter). It reads 0 occurrences of VIEW_FILTER_OPERATOR_ALIASES/normalizeFilterOperator (control: 5 of QUERY_TRANSPORT_ALIAS_SLOTS), and no script under scripts/ reads the view alias table.",
    "tests": "No code change, so no test suite was owed. Premise measurements, all at objectstack BASE af32cf9: [a] git grep -n -i containsCaseInsensitive returned exit 1 with 0 lines. Control leg, same subject and failure form: legacy camelCase spellings in operator position (notEquals/isNull/notIn/startsWith...) returned 14 hits across metadata-protocol, spec, rest, lint and service-analytics fixtures, and git grep -w icontains returned 1503 lines. [b] objectui git grep containsCaseInsensitive at refs/os-dev/issue-20092/objectui-main = 25c7d584e4 (fetched into an owned ref; the clone is shallow and no history claim rests on it) returned exit 0 with 56 lines, each classified above. [c] A tsx probe over packages/spec/src (scratch only, never committed), asking the SAME schema saveMetaItem validates a view write against, getMetadataTypeSchema('view'), printed: op=containsCaseInsensitive normalize=containsCaseInsensitive rule=REFUSED(invalid_value@operator) viewSave=REFUSED. Control legs from the same run: op=icontains ... rule=ok→icontains viewSave=ok, op=notContains normalize=not_contains rule=ok→not_contains viewSave=ok, op=contains ... viewSave=ok. It also printed VIEW_FILTER_OPERATORS has icontains: true and alias rows=36 containsCaseInsensitive row=false lowered row=false. [d] REST single reads, used because the local clones are shallow: objectui commit 37f68447f0 patch (OPT_IN_OPERATORS = new Set([containsCaseInsensitive]), FILTER_CONDITION_EXTRA_OPERATORS = [containsCaseInsensitive], case containsCaseInsensitive: return { [field]: { $icontains: value } }); objectstack view.zod.ts at cb43296 (line 594 carries the preprocess plus the enum, and 0 icontains/containsCaseInsensitive hits); protocol.ts at 177442d (schema.safeParse(request.item) at 12231 and the 422 throw at 12238); objectui issue/PR dates for 2945, 4023, 4736, 9306, 9559 and 10465. NOT MEASURED: live tenant sys_metadata rows in deployed environments, which are not reachable from this container. Only a direct DB write bypassing every door above could have put one there.",
    "mcp_calls": "0",
    "api_writes": "1 — POST /repos//issues/20092/comments (this os-dev-report, via scripts/pm/post-stamped.mjs). Not REST: one paced git push of the empty branch (write-route probe, contract rule 1).",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: the domain:ui seat carrying this premise finding to objectui#9559 · noted, not filed. objectui filter-builder.tsx (about :252-272 at 25c7d584e4) keeps a local UNFOLDED_LEGACY_OPERATOR_IDS row (containsCaseInsensitive to icontains). Its docblock names objectstack#20092 as the spec-side row whose landing deletes that map. It justifies the row with 'a filter the builder saved under that id (a sharing rule's FilterConditionField)', but FilterConditionField stored $icontains from birth and never the id. With this premise false, the map's exit condition names a row that will not be added. The objectui changesets 9302/9306 and the census test cite #20092 the same way. Dedupe words: UNFOLDED_LEGACY_OPERATOR_IDS containsCaseInsensitive · normalizeFilterBuilderOperator local alias · objectstack#20092 exit condition"
    ],
    "gates": "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands exited 2 with 'this branch changes nothing against origin/main (merge base af32cf9) — nothing to derive'. No families are owed, so there is nothing to reconcile with --ran. check:filter-alias-parity was not run: there is no diff, and it does not read the view alias table (see H3).",
    "line_budget": "n/a",
    "deviations": [
    "No PR, no changeset, no label-write and no PR assignee: the premise is false, so the dispatch's budget spends only the push and this comment.",
    "The empty branch claude/issue-20092-view-filter-contains-case-insensitive-alias stays on the remote at af32cf9, the claim's landing marker. Deleting it would be an unbudgeted write, so the seat may delete it.",
    "No comment was posted on objectui#9559. Per the dispatch, the seat carries the finding there.",
    "The PM hypothesis H3 did not hold: check:filter-alias-parity is not this table's gate."
    ],
    "files_changed": []
    }

  4. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Released and closed not_planned: the premise is falsified, so no alias row is owed · domain:spec seat 2 (session_01QcAS3qiYYZNezaxZxaUdMV) · 2026-09-27T10:29Z

    Triage's execution note 1 (5826889106) made the row conditional on a measurement: 「Present ⇒ add the row, per the ruling. Absent ⇒ ⛔ do not add a synonym; report it on this card and on objectstack-ai/objectui#9559, because the ruling's premise is then false.」 The dev measured absent (os-dev-report 5855023161, no PR):

    • objectstack at af32cf9a0e: git grep -i containsCaseInsensitive returns 0 lines. Control: the legacy camelCase operator spellings the table does bridge return 14 hits in fixtures.
    • objectui origin/main at 25c7d584e4: 56 hits, all of them the builder's own read alias, tests, changesets and docblocks. None is stored view metadata.
    • Never storable. The id was born opt-in (objectui PR spec 双源清账 C17:ActionLocationSchema(./studio ≠ ./ui)—— studio 侧 D9(a) 改名,1 条 #4737), and FilterConditionField stored { field: { $icontains } } from birth, not the id. Before the id existed, the view write door already validated ViewFilterRuleSchema.operator against VIEW_FILTER_OPERATORS with no such member, and saveMetaItem refused a failed parse with 422. A scratch probe of the same schema the view save uses answers REFUSED for containsCaseInsensitive, while icontains, notContains and contains save fine.
    • The table's own docblock admits a row only for a measured legacy spelling already in stored metadata, so a row here would widen the authoring surface without bridging anything.

    Disposition: the release cause is "premise falsified", so the card is closed not_planned and pm:dispatched is stripped in this act. The ruling on objectui#9559 (5748934939, ratified 5814267184) asked for this card to be filed, and its premise is what failed. The finding is carried to objectstack-ai/objectui#9559, where objectui's local UNFOLDED_LEGACY_OPERATOR_IDS row names this card as its exit condition. Reopening is free, and the maintainer can veto this closure.

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

    area:recordsBusiness objects, records, the views that show data, usable forms, searchdomain:specpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions