Skip to content

finding: 78 test-double WHERE matchers have no combinator branch at all — they read $or as a field name and silently drop the row #8582

Description

@os-zhuang

Filed unassigned by the domain:engine-core dev seat while building #8494's guard (PR #8581), session session_01RDTnVvsgA6cUZ4xFVtPZRy. ⛔ Not queued, not graded by me — triage grades it.

The fact

#8494 landed pnpm check:where-matcher (scripts/check-where-matcher-conformance.mjs), which lifts every in-memory WHERE matcher out of its test file and asks it four combinator questions. Measured across packages/**/*.test.ts on main:

verdictcount
discovered matchers168
conforming89 (4 of them by refusing — throwing on an unrecognised $-key)
silently wrong78
unjudged (could not be lifted and executed)1

The 78 are all failure shape (b) from #8494: an Object.entries(where).every(…) matcher with no combinator branch at all. It treats $or as an ordinary FIELD NAME, compares row.$or (undefined) against the array, gets no match, and silently excludes the row — so a suite that ever passes one asserts on an empty result set with nothing erroring.

They are grandfathered in scripts/where-matcher-conformance.baseline.json, which is the worklist: 75 files, shrink-only, reconciled in both directions. This issue is the ledger's other half — the sweep that empties it.

⭐ Note what is deliberately not in that ledger: failure shape (a), the $or early return. PR #8581 fixed the five live instances it found rather than baselining them, so shape (a) is enforced with an empty ledger. Only shape (b) is owed.

Dormant, and that is not a reason to close it

#8493 measured its three instances as never invoked with a combinator today, and the same is very likely true of most of these 78 — the liveness of the remaining 75 is unmeasured, and measuring it per file is part of the work, not a precondition for filing.

The card this came from already answered the "it's dormant" objection in its own words, and it applies unchanged here: "the next test that adds an $or inherits a double that lies."

The cheap fix is usually the right one

Two ways to clear an entry, and option 2 is one line:

  1. Make the matcher conjoin $or/$and with its sibling keys.
  2. Make it refuse what it does not implement — throw on an unrecognised $-prefixed key.

Option 2 is already the recorded practice in packages/objectql/src/engine-autonumber-*.test.ts, with its own rationale: "silently ignoring an unknown operator would let a bad query pass as a good one." A double that only ever sees scalar equality does not need a combinator implementation — it needs to stop lying. Those four files are exactly the "4 conforming by refusing" above, so the pattern is proven against the gate.

Suggested shape

Sweep per package as separate lanes rather than one 75-file PR (metadata-protocol, objectql, plugin-*, runtime, services/*, triggers/*), lowering the baseline counts in the same PR each time — the gate fails if a count falls without the ledger following it down. The 1 unjudged entry is its own small question: whether the extractor should reach it, or whether that matcher should be restructured.

⛔ Not a request to extract a shared matchesWhere — ruled NO on #7620, reasoning specific to independent test doubles, and not re-litigable here.

Refs: #8494 · PR #8581 · #7620 · PR #7846 · PR #8493 · #7619


Generated by Claude Code

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions