Skip to content

JS 求值面全体拒收 $icontains(driver-memory 两面 / driver-mongodb / objectql having / formula)—— SQL 族已实现,同一 filter 在内存 double 上抛错 #6520

Description

@os-zhuang

Part of #4706。从 #5702 实施期实测拆出:#5702 在 SQL 族(driver-sql / driver-sqlite-wasm / driver-turso 的 local + remote 两面)实现了 $icontains,JS 求值面一个都没有。

现状(实测,#5702 落地后)

面 { name: { $icontains: 'acme' } }
driver-sql / -sqlite-wasm / -turso(local+remote) ✅ 求值,ASCII-only 折叠
driver-memory 查询路径 + 分析面 ❌ INVALID_FILTER / 400(SUPPORTED_FIELD_OPERATORS 由 spec FILTER_OPERATORS 派生,该表刻意未收 $icontains)
driver-memory 参考匹配器 ❌ 同上(同一道 shape gate)
driver-mongodb ❌ default: 拒收
objectql having ❌ CONDITION_OPERATORS 不含它
@objectstack/formula matchesFilter ❌(未在 #5702 范围内核过实现,但它不在任何 $icontains 分支里)

拒收是 fail-closed,不是静默放宽 —— 这是刻意的方向,不是缺陷本身。缺陷是同一条 filter 在两类后端上给两个答案:一个返回行,一个抛 400。

用户可见后果

应用测试跑内存 double、生产跑 SQL 是本仓的常见形态(plugin-auth 的 auth-contains-filter.test.ts 就是这个形状)。一条用 $icontains 的 filter 会在测试里抛错、在生产里正常,或者反过来 —— 正是 #4706 对 $regex 的原始指控(「the divergence only shows up when the app's tests run on the memory double and production runs SQL」),换了个算子重演。

下游 #5814(better-auth Where.mode: 'insensitive')一旦落地就会踩到:认证查询在内存 double 上会 400。

为什么 #5702 没做

建议范围

  1. packages/spec 的 FILTER_OPERATORS 收入 $icontains,同 PR 更新 filter-operator-vocabulary.test.ts 的差集 pin(该 pin 现在钉死差集恰为 { $icontains });
  2. driver-memory 两面 + driver-mongodb 的实现(ASCII-only 折叠,不是 toLowerCase() —— 契约以 FILTER_TEXT_CASES 的非 ASCII 不折 pin 为准);
  3. service-analytics 的 compileScopedFilterToSql 补一条臂(否则 echo 覆盖测试判红);
  4. objectql having 与 formula 同批;
  5. 清 scripts/check-driver-conformance.mjs 里 driver-memory / driver-mongodb 两行 FILTER_TEXT_CASES DEBT 的 requirement-1 半边。

memory 侧实现本身是平凡的(真正则引擎已在,ASCII 折叠即可);贵的是词表纳入牵动的 allowlist 消费者。

Activity

  1. os-zhuang commented on Aug 8, 2026

    @os-zhuang
    ContributorAuthor

    Triage: needs-user-decision + domain:spec. Two independent grounds for the escalation, both verified on origin/main @ 3510e4a:

    1. The gating move is an acceptance-surface change. packages/spec/src/data/filter.zod.ts:1085 FILTER_OPERATORS does not include $icontains, and filter-operator-vocabulary.test.ts:55 pins the declared-minus-enforced diff set as exactly ['$icontains']. Admitting it flips previously-rejected filters to accepted on every allowlist consumer — domain:spec by the standing red line (any accept/reject behavior change, however small). The body's own constraint (vocabulary admission must ride in the SAME PR as the driver-memory + service-analytics implementations, else fail-closed flips to silent-permissive = A filter with an operator outside VALID_AST_OPERATORS is silently dropped, not rejected — single-condition views return unfiltered results #3948-class RLS bypass) also rules out a mechanical contract-first split, so this cannot be pre-split spec-first at triage.
    2. The implementation halves sit inside the [裁决] driver-memory / driver-mongodb 投入冻结 —— 维护者 2026-08-05 口径(跨单锚点) #5499 investment freeze (driver-memory / driver-mongodb family) — drivers: $regex 响亮拒收 + $icontains 各后端实现(#4706 裁决 B 案 · 驱动半边) #5702 explicitly deferred exactly this work per that freeze. Queueing it now would contradict a standing maintainer decision; the appetite question (lift the freeze for $icontains semantic completion, and when) is the maintainer's to answer.

    Sequencing: the premise "SQL family implements it" lands with #5702's in-flight PR; the decision can be made now, implementation follows #5702's merge. Dedup: family checked — #5702 (SQL half, in flight), #5814 (consumer, blocked on #5702), #6337 (objectui authoring face), #6518 (case-folding facet, separate card), #4706 (parent ruling). No duplicate.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. os-zhuang commented on Aug 8, 2026

    @os-zhuang
    ContributorAuthor

    Maintainer ruling (2026-08-08): Option A — approved. Add $icontains to the spec FILTER_OPERATORS word list and bring every JS evaluation face up to parity, as a sanctioned one-off exception to the #5499 investment freeze, strictly for semantic parity — not new capability.

    Hard constraints for the dispatch (from the card, now binding):

    • Single PR, whole batch: spec word list + driver-memory (both faces) + driver-mongodb + service-analytics + objectql having + formula, plus conformance coverage. The word list must NOT land ahead of the evaluators — that would flip the JS faces' fail-closed refusal into silent acceptance, the A filter with an operator outside VALID_AST_OPERATORS is silently dropped, not rejected — single-condition views return unfiltered results #3948 RLS-bypass shape.
    • The PR body must declare a conclusion for every compile face per the face-inventory clause in the dispatch playbook (changed / already compliant with evidence / explicitly out of scope with reason). The frozen driver-memory / driver-mongodb faces are being changed here under the sanctioned exception, so they report as changed.

    Rationale (three-axis review): the SQL family already shipped the operator, so the same filter behaves differently between test (memory) and production (SQL) — a measured divergence, and downstream #5814 (SCIM case-insensitive matching) has its blocker cleared and would hit 400 on landing (business). One contract, five faces, one answer (long-term). An operator the contract declares and one face rejects is exactly the declared-vs-enforced trap to close for AI authors (containment).

    Timing: the decision gate (#5702) closed 2026-08-08 06:43Z — implementation can start immediately.

    State: needs-user-decision removed; pm:queue restored — spec lane coordinates the single-PR batch.

    Maintainer directive (verbatim, covering all 14 inbox cards): 「你的建议全部接受」. Recorded by PM session session_01JaVVMrSxt7Tgi1uwEuDtH7.


    Generated by Claude Code

  3. os-project-manager commented on Aug 9, 2026

    @os-project-manager
    Collaborator

    #6993 普查实测补一格(2026-08-09,origin/main @ 08863dd18,执行测量):本卡现状表中 @objectstack/formula matchesFilter 一行标 ❌ 与其余拒收面并列,但它的「❌」与其他面不同类:

    即 formula 面对一个 spec 已声明的算子给出的是「无声的否答」,不是「fail-closed 拒收」—— 恰是 #4706 对旧 $regex 的第二项指控(illegal pattern → return false)在这一面的残留形态。给 formula 加 $icontains 臂时(本卡范围第 4 条),建议同 PR 决定 unknown-$ 算子在该面是维持静默 false 还是升级为 throw,并把决定写进 matches-filter.ts 的 docblock;filter.zod.ts 的 "The other JS evaluators sit on the refusing side too" 一句对 formula 的措辞也应随之校正。

    Generated by Claude Code — session_018ffcE95NaMJcL9XJ9VDYgk


    Generated by Claude Code

  4. self-assigned this
    on Aug 9, 2026
  5. os-zhuang commented on Aug 9, 2026

    @os-zhuang
    ContributorAuthor

    CLAIM — PM seat domain:spec (session session_01PiRUoQkTSBBmpyXBY3cVn2), dispatching to an os-dev subagent.


    Generated by Claude Code

  6. os-zhuang commented on Aug 9, 2026

    @os-zhuang
    ContributorAuthor

    PM bookkeeping: pm:dispatched → pm:queue. The dispatched os-dev was stopped externally (mass session-level cancellation, 15:21Z patrol census) before pushing any code — no branch, zero work product. Claim released; card returns to the queue intact. The Option A ruling and its hard constraints (single PR whole batch; word list never ahead of the evaluators; #3948 probe) remain binding on the next dispatch; the #7058 serialization constraint is now moot (merged 14:03Z). Re-dispatch awaits the maintainer's word.


    Generated by Claude Code

  7. 2 remaining items

  8. os-zhuang commented on Aug 9, 2026

    @os-zhuang
    ContributorAuthor

    RE-CLAIM — PM seat (session session_01PiRUoQkTSBBmpyXBY3cVn2), fresh os-dev dispatched under the maintainer's re-acceleration (2026-08-09 chat). Branch claude/issue-6520-icontains-js-faces, worktree ../objectstack-issue-6520. The Option A ruling's hard constraints (single PR whole batch; word list never ahead of the evaluators; #3948 probe mandatory; per-face inventory) remain binding; the #7058 serialization is moot (merged).


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions