Part of objectstack-ai/objectui#3567 (transferred to the main backlog by the triage seat — feature-level work lives in objectstack whatever repo the code lands in; see the multi-repo coordination rule "One main backlog").
Blocked-by: #5702
What is missing
packages/spec's FieldOperatorsSchema gained $icontains (case-insensitive contains, spec describe: "folds ASCII case") between 17.0.0-rc.2 and rc.5. objectui's FilterConditionField has no builder operator that can author it, so the capability is unreachable from the filter UI. The existing bidirectional parity test in packages/fields/src/widgets/__tests__/FilterConditionField.operators.test.ts ("every spec field operator is reachable from the builder", objectui#2942) went red on the rc.5 bump and was handled by objectui#3560 as an explicit, ratcheted KNOWN_UNREACHABLE entry rather than a silent exclusion.
Deliverable (lands in objectui)
- Add a case-insensitive contains operator to the
FilterConditionField builder vocabulary; condToMongo emits { field: { $icontains: value } }. kvToCondition reads it back — without the reverse mapping a saved filter view degrades on reopen.- Ten locale packs gain the label key (the
t() call-site key gate, objectui#3547). - Remove the
$icontains entry from KNOWN_UNREACHABLE in FilterConditionField.operators.test.ts; the parity assertion then takes over, and objectui#3560's ratchet simultaneously validates that the removal was legitimate.
Why this is blocked, and it is not the reason the filing issue gave
⚠️ The originating issue states the server already accepts $icontains. Half true, and the half that is false is the blocking half. Measured against objectstackorigin/main:
So the spec vocabulary accepts the token while no driver can execute it. Shipping the builder operator first would put a filter in users' hands that every backend fails — strictly worse than the capability being absent. This card returns to the queue when #5702 merges.
Open product question (default recorded, not blocking)
Should today's contains have been case-insensitive all along? If so, the better shape is to have contains emit $icontains and add an explicit containsCaseSensitive for $contains — but that changes the behaviour of stored filter views, so it needs the maintainer. Default for this card: add a new operator, leave contains semantics untouched. A dev implements the default; if the maintainer wants the flip, say so here first and this card is re-scoped (it would then move to the decision inbox).
Provenance
Measured during objectui#3560 (@objectstack/* family bump to 17.0.0-rc.5). Not a regression introduced by that bump — a capability gap it exposed. Original filing: objectstack-ai/objectui#3567.
Generated by Claude Code
Part of objectstack-ai/objectui#3567 (transferred to the main backlog by the triage seat — feature-level work lives in
objectstackwhatever repo the code lands in; see the multi-repo coordination rule "One main backlog").Blocked-by: #5702
What is missing
packages/spec'sFieldOperatorsSchemagained$icontains(case-insensitivecontains, spec describe: "folds ASCII case") between17.0.0-rc.2andrc.5.objectui'sFilterConditionFieldhas no builder operator that can author it, so the capability is unreachable from the filter UI. The existing bidirectional parity test inpackages/fields/src/widgets/__tests__/FilterConditionField.operators.test.ts("every spec field operator is reachable from the builder", objectui#2942) went red on the rc.5 bump and was handled by objectui#3560 as an explicit, ratchetedKNOWN_UNREACHABLEentry rather than a silent exclusion.Deliverable (lands in
objectui)FilterConditionFieldbuilder vocabulary;condToMongoemits{ field: { $icontains: value } }.kvToConditionreads it back — without the reverse mapping a saved filter view degrades on reopen.t()call-site key gate, objectui#3547).$icontainsentry fromKNOWN_UNREACHABLEinFilterConditionField.operators.test.ts; the parity assertion then takes over, and objectui#3560's ratchet simultaneously validates that the removal was legitimate.Why this is blocked, and it is not the reason the filing issue gave
$icontains. Half true, and the half that is false is the blocking half. Measured againstobjectstackorigin/main:$icontainsis present inpackages/speconly —filter.zod.ts,filter-operator-vocabulary.test.ts,filter-text-conformance.ts, the authorable-surface baselines and the CHANGELOG.git grep -l icontains origin/main -- packages/driversreturns nothing. No backend implements it.pm:queue+pm:blocked+domain:drivers), itself blocked by spec:$regex按 ADR-0049 退役 +$icontains入算子词表与 FILTER_LOGIC_CASES(#4706 裁决 B 案 · 契约半边,先行) #5701 (contract half: vocabulary +FILTER_LOGIC_CASES) and plugin-auth: better-auth 的contains被译成裸$regex,用户输入当正则求值 —— 且是$regex退役唯一挡路的生产者 #5710 (producer-first:plugin-auth'scontainsbranch).driver-memory/driver-mongodbimplementations sit inside the [裁决] driver-memory / driver-mongodb 投入冻结 —— 维护者 2026-08-05 口径(跨单锚点) #5499 freeze.So the spec vocabulary accepts the token while no driver can execute it. Shipping the builder operator first would put a filter in users' hands that every backend fails — strictly worse than the capability being absent. This card returns to the queue when #5702 merges.
Open product question (default recorded, not blocking)
Should today's
containshave been case-insensitive all along? If so, the better shape is to havecontainsemit$icontainsand add an explicitcontainsCaseSensitivefor$contains— but that changes the behaviour of stored filter views, so it needs the maintainer. Default for this card: add a new operator, leavecontainssemantics untouched. A dev implements the default; if the maintainer wants the flip, say so here first and this card is re-scoped (it would then move to the decision inbox).Provenance
Measured during objectui#3560 (
@objectstack/*family bump to17.0.0-rc.5). Not a regression introduced by that bump — a capability gap it exposed. Original filing: objectstack-ai/objectui#3567.Generated by Claude Code