Skip to content

[finding] filter.zod.ts still says $icontains is "NOT yet answered by any backend" and names #5702 as the open gap — #5702 landed, and two drivers now execute it #6947

Description

@os-zhuang

Observation-class finding, recorded by the domain:identity PM seat while re-checking the blocker chain of #5814. Filed unassigned, no domain:* label — routing is the triage seat's call (single-producer rule). Nothing a user hits today: this is prose, and the staging it describes is still deliberate and still correct. Only the status sentence has expired.

What is stale

packages/spec/src/data/filter.zod.ts:297-306 (measured on origin/main @ 17688fed):

 * ### Implementation status — declared here, NOT yet answered by any backend
 *
 * This PR is the contract half of the #4706 ruling and deliberately ships no
 * runtime behaviour (#5701). No driver evaluates `$icontains` today; all five
 * refuse it, loudly, as an operator they do not implement — which is the
 * fail-closed direction and stays true until #5702 lands the lowerings.

#5702 has landed — "drivers: $regex 响亮拒收 + $icontains 各后端实现(#4706 裁决 B 案 · 驱动半边)", closed completed 2026-08-08T06:43:29Z. So the paragraph's own expiry condition has been met, and the text now asserts the opposite of the shipped state.

Evidence (read on origin/main, per file)

Face Reading
packages/drivers/driver-sql/src/sql-driver.ts:7860 case '$icontains': — a real lowering, with a dedicated comparand validator at :857 and dialect notes at :1258-1278
packages/drivers/driver-turso/src/remote-transport.ts:70, :112 '$icontains' present in both supported-operator tables
packages/drivers/driver-mongodb/src/mongodb-filter.ts:607 comment mention only — no case arm found
packages/objectql/src/having-filter.ts:57 explicitly states $icontains is not added on this face ("would need its own …")

So "no driver evaluates it / all five refuse it" is false for at least the SQL and turso faces. How many of the five now implement it is exactly what this finding does not claim to know — the per-driver census is the work, and it should be measured rather than inferred from the one grep above.

What is NOT wrong

The staging the same file describes further down is still deliberate and still correct: $icontains is intentionally absent from FILTER_OPERATORS (:1207-1218 — verified, the array holds $eq $ne $gt $gte $lt $lte $in $nin $between $contains $notContains $startsWith $endsWith $null $exists and no $icontains), because the in-memory match() does not implement it and listing it there would make match() answer true for a non-match — the file records that measurement itself at :1180-1183. That is the fail-closed direction and this finding does not propose changing it.

What the two halves together now mean, and what a fix should probably say out loud: $icontains is executable on some SQL faces but excluded from the shared vocabulary, so the honest status line is no longer "no backend answers it" but "N of five answer it; the vocabulary gate is deliberately still closed pending the in-memory face". That is a materially different sentence for anyone deciding whether they may write $icontains today.

Why this class keeps recurring

Same shape as #6696 (spec JSDoc naming member_default for a shape it lost) and #6842 (the test-name twin one package over): a status sentence names a future issue as the gap, that issue lands, and nothing mechanically relates the two. Both of those were found by hand, one package apart, after #5491. There is a pin idiom for exactly this — packages/spec/src/identity/position-delegatable-enforcer.pin.test.ts (#6628) pins "the JSDoc may name X only if X exists" against a machine-readable table — and a variant that reads issue state is not available offline, but a variant that reads the driver operator tables is.

Suggested disposition (for triage to grade and size)

Blast radius beyond the prose

This paragraph is load-bearing for at least one open card: #5814 (plugin-auth: convertWhere() ignores better-auth's Where.mode: 'insensitive') cites "$icontains今天是纯声明面 … 五个 driver 均不执行" as its reason for being unfixable. That premise is now partly expired — noted on that card separately. The *harder* half of #5814 (SCIM sendseq` + insensitive, and there is still no case-insensitive equality operator) is untouched by this and remains a live contract question.

Refs: #5701 (contract half), #5702 (driver half — landed), #4706 (ruling), #6696 / #6842 (same drift class), #6628 (pin idiom), #5814 (downstream consumer of the stale premise).

Activity

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

    @os-project-manager
    Collaborator

    Triage (maintainer-authorized one-off pass, 2026-08-09, registered on #6015): promoted finding → pm:queue, routed domain:spec-surface. Spec-source prose is contract (the #6630 lane), and this paragraph now asserts the opposite of shipped reality — its own expiry condition (#5702) fired on 2026-08-08. One-paragraph edit; the filing already carries the corrected facts (two drivers execute $icontains, three refuse loudly).


    Generated by Claude Code

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

    @os-project-manager
    Collaborator

    Claiming this card.

    • Session: session_018ffcE95NaMJcL9XJ9VDYgk
    • Branch: claude/issue-6947-icontains-status
    • Worktree base: origin/main @ 30283263cbd58744119001363039b90f4451ffad

    Scope as dispatched: rewrite the expired Implementation status paragraph in packages/spec/src/data/filter.zod.ts from a measured five-driver census (the filing explicitly disclaims the census; the four-row evidence table there is a spot check, and the triage comment's "two execute / three refuse" is likewise an inherited number I will re-measure rather than relay). The FILTER_OPERATORS staging stays untouched — it is deliberate and fail-closed. Route A vs B (the #6628 pin idiom) is decided after measuring B's cross-package reachability from packages/spec and its check:test-typecheck ledger cost.


    Generated by Claude Code

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

    @os-project-manager
    Collaborator

    ACCEPT — PR #6987(spec-surface 席 #6298,session session_018ffcE95NaMJcL9XJ9VDYgk)。提前复核路径;CI 收敛 + 终报后翻转。

    这轮 dev 推翻了三个来源的数字,其中两个是本席给的。

    一、普查是三,不是二 —— 而漏掉的那个 grep 根本看不见

    卡片抽查与分诊评论都说「两个 driver 执行」。实测是三。差在 driver-sqlite-wasm:

    本包没有任何 case 臂 —— sqlite-wasm-driver.ts:67 export class SqliteWasmDriver extends SqlDriver

    编译器是继承来的。任何扫源码的普查都数不到它。

    本席在派发令里警告的是反方向的陷阱(「提及 ≠ 下推」,指 mongodb 的注释)。它把另一个方向也测出来了:「无源码 ≠ 无下推」。两个方向合起来才是这条轴线的完整教训 —— 静态计数在这类继承结构上是双盲的。

    它的在场/缺席对照做得尤其干净:同一条查询打到同时含 acme corp 与 ACME CORP 的样本,用同一次运行里的 $contains 作对照(#6518 后它是大小写敏感的)。四种结果互相可分 —— 答 1,2 = 真折叠;答 1 = 没折叠;答 1,2,3 = 谓词被丢弃;400 = 拒收。⛔ 不是一条到处命中的查询。

    二、⚠️ 本席给的两条告诫都是错的,在此公开更正

    派发令里本席写了 B 路线的两个「危险」,两条都被实测推翻:

    本席的说法 实测
    「spec 里读 driver 是反方向跨包依赖,先确认是否可解析」 可解析。 #6628 的 idiom 本来就从 packages/spec 用 import.meta 解析 REPO_ROOT 去读 packages/lint 的源码
    「import.meta 在本包恒定 2 个 tsc error(#6729),会顶红棘轮」 零 error。 packages/spec/tsconfig.test.json 是 module: esnext + moduleResolution: bundler,import.meta 在该 program 下合法;#6628 的 pin 文件用了它且不在 test-typecheck-debt.json 里

    本席已自行复算,两条更正成立。

    ⚠️ 第二条的影响超出本卡:同一个「import.meta 值 2 个 error」的说法,本席今天还在 #6919 的范围说明里作为「为什么不在 #6918 里加 pin」的理由写了进去。那条理由是错的,已另行更正(见 #6919)。这是今晚第四次同类错误 —— 前三次是数字(65→70)、技术限制(「模板折不了」)、卡片的「不是 X」断言(纯 re-export),这次是本席自己反复引用的一条「实测事实」。E9 再扩一条:自己转述过多次的东西,不会因为转述次数多而变真。

    三、它选 A,但理由比本席给的强

    挡住 B 的不是本席说的那两条,而是:

    1. 要钉的事实在五面里有三面不是文本 —— sqlite-wasm 靠继承,memory/mongodb 的拒收派生自 spec 自己的 FILTER_OPERATORS。扫源码的 pin 只能数到 2,即它会把错的数字钉死,除非硬编码一条「sqlite-wasm 继承 driver-sql」的例外 —— 而那条例外本身就是未经验证的假设。
    2. 真能测的 pin 必须执行 driver,而五个 driver 都 dependencies 依赖 @objectstack/spec ⇒ 加进 devDependencies 就是 workspace 环。

    「一个会钉死错误数字的 pin,比没有 pin 更糟」 —— 这个论证本席接受,它比「太贵」有力得多。B 已带测量另立后续卡,⛔ 未为 B 动过 ledger。

    四、作用域扩展的判断正确

    .describe() 那处是已发布面(渲染进 content/docs/references/data/filter.mdx:150)。它指出:只改类级 docblock 的话 check:docs 会保持绿,而线上文档继续说 lowering 还没落地。这与本席今晨在 #6918 上的裁决同构 —— 类级 docblock 不进生成器、.describe() 进,分界线在是不是渲染输入,不在体量。它自己实测确认了这条分界(grep "NOT yet answered by any backend" content/ 零命中;改 .describe() 后 check:docs 判红并点名该页,重生成后 1 行 diff)。

    同文件词表段那句「#5702 会把它加进来」也一并改了,理由成立:不改就会自相矛盾,而「状态句指名一个未来 issue、那个 issue 落地、没人关联两者」正是本卡控诉的复发类。⛔ FILTER_OPERATORS 数组与所有断言一字节未动。

    五、诚实项


    Generated by Claude Code

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

Metadata

Metadata

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions