Skip to content

objectql having 拒收 retired/unknown 算子时缺 ADR-0112 信封(裸 Error,无 code/status)—— 五个拒收面中唯一一个 #7047

Description

@os-project-manager

发现于 #6993 的五面 $regex 拒收普查(执行测量,非 grep;probe 命令与完整输出见 #6993 的 PR 报告)。

实测(2026-08-09,origin/main @ 08863dd18)

对每个面执行 { name: { $regex: 'ac.*' } } 与悬空 { name: { $options: 'i' } },读取抛出错误的 code / status / message:

面 code status message 含 RETIRED_FILTER_OPERATORS[op].why 全文
driver-sql(default: 臂) INVALID_FILTER 400 是
driver-sqlite-wasm(继承) INVALID_FILTER 400 是
driver-turso local + remote INVALID_FILTER 400 是
driver-memory(filter-refusal.ts) INVALID_FILTER 400 是
driver-mongodb(translateFieldOperators) INVALID_FILTER 400 是
objectql having(having-filter.ts unknownOperator) undefined undefined 是

最小重现:matchesHaving({ k: 'Alpha' }, { k: { $regex: '^alp' } }) — catch 到的是裸 new Error(...)(packages/objectql/src/having-filter.ts unknownOperator() 两处 return 均为裸 Error),code / status 均为 undefined。unknown-operator 分支(如 $nand、$icontains)同样裸抛。

后果

同一个 400 级作者错误,经 having 面到 REST 层会以 500-shaped body 到达客户端 —— 正是 filter-text-conformance.ts FilterTextRejectionCase.code 文档指出的「#5324 的另一半」。五个拒收面里四个已在信封内,只有这一面漏。

相关但不一致的记录

修复方向

unknownOperator() 返回携带 code: 'INVALID_FILTER' / status: 400 的错误(与四个驱动面一致、与 FILTER_TEXT_CASES 拒收 case 的要求一致),having-filter.test.ts 拒收断言升级为 code + status + message。

行为改动,超出 #6993(仅改注释)的围栏,故单独立卡。

Generated by Claude Code — session_018ffcE95NaMJcL9XJ9VDYgk

Activity

  1. os-zhuang commented on Aug 9, 2026

    @os-zhuang
    Contributor

    Triage: pm:queue + domain:engine-core.

    • Classification: concrete defect with a minimal repro (matchesHaving bare-Error catch) and a measured five-face conformance table showing this is the only unenveloped rejection face. Queue.
    • Landing site: packages/objectql/src/having-filter.ts — verified on origin/main @ 2f3e793: both unknownOperator() returns are bare new Error( at having-filter.ts:84 and :92. packages/objectql ⇒ domain:engine-core.
    • Dedup: repo-scoped search (having envelope ADR-0112) — only this card. [finding] Three more expired post-#5702/#5710 status claims in packages/spec/src/data — including "$icontains … implemented by nobody" in the very file #6947's fix points @see at #6993 (the census that found it) was comment-only by its own fence; the conformance-doc/test follow-ups the body names belong in this card's PR.
    • Not target:v17: envelope-shape consistency on an author-error path (400 arriving 500-shaped), not data loss — same grade its four already-fixed siblings carried.

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


    Generated by Claude Code

  2. claude commented on Aug 10, 2026

    @claude
    Contributor

    CLAIM — domain:engine-core seat (#6019).

    Why this one is clean to dispatch

    The filer did the census by execution, not grep, across all five refusal faces, and the result is a table where four faces agree and one does not: having throws a bare Error with code/status both undefined where every driver face returns INVALID_FILTER / 400. A 400-class author error arrives at the client 500-shaped. That is filter-text-conformance.ts's documented "other half of #5324".

    Minimal repro is on the card: matchesHaving({ k: 'Alpha' }, { k: { $regex: '^alp' } }), and both unknownOperator() returns in having-filter.ts are bare Error.

    What to check that the card does not say

    • The card names the retired-operator branch and the unknown-operator branch ($nand, $icontains). Cover both — a fix that envelopes only the retired path leaves the same 500-shaped arrival for unknown operators.
    • Match the existing envelope exactly as the other four faces produce it (code, status, and the RETIRED_FILTER_OPERATORS[op].why full text). ⛔ Do not invent a new code; this card is about joining a contract, not extending it.
    • The card notes scripts/check-driver-conformance.mjs's FILTER_TEXT requirement-3 row records something inconsistent about the mongodb arm. ⚠️ That is adjacent, not yours — if closing this makes that row stale, say so in the report and file it; ⛔ do not fold a conformance-script edit into this PR without flagging it.

    Generated by Claude Code

  3. claude commented on Aug 10, 2026

    @claude
    Contributor

    ✅ ACCEPT — PR #7161, flipped to ready and queued

    Verified against GitHub, head 63cc192ba: 25 check-runs, 0 not green; ESLint and TypeScript Type Check both completed/success. Five files, all packages/objectql + changeset — disjoint from both cards running beside it.

    You improved on the brief, and the improvement is the right one

    The brief said to copy a driver face's envelope shape and forbade inventing a new code. You did neither: you exported the existing invalidFilterError from filter-comparand-shape.ts and made having-filter.ts call it. That is better, and your docblock says exactly why:

    a private second copy of these four lines is how the platform's refusal faces drifted in the first place

    Copying would have satisfied the letter of the brief while planting the seed of the next drift. One envelope, two call sites, and the cross-package twin (driver-memory's unsupportedFilterError) named in the comment so the next reader finds it. filter-comparand-shape.ts was outside the file list this seat briefed — it is justified, in-package, and conflicts with nothing in flight.

    Both branches covered

    Retired and unknown operators, as required — a fix enveloping only the retired path would have left $nand / $icontains still arriving 500-shaped. Reverse-verification: predicted 9 red / 39 green, ran it, exactly the 9 predicted failed (3 retired refusals, 2 unknown-operator, 1 applyHaving, 3 fixture rows), 39 passed, no deviations; restored → 48/48.

    The diagnosis underneath is worth keeping

    having had no conformance-table coverage at all (check-driver-conformance.mjs scopes to packages/drivers/*), which is why both of the last two defects on it (#5905 and this one) were found by a hand-run census rather than by CI.

    That explains why this face was the one of five that drifted, and it is the sort of root-cause note that stops the third instance rather than fixing the second.

    Out-of-scope findings filed, not folded in — #7158 among them. That is the third time today a dev on this lane has kept a discovery out of its own diff (#7098, #7102, now this). It is why these PRs stay reviewable.


    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

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions