test(where-matcher): empty the combinator-blind ledger — 75 files, refuse unrecognised $-keys - #8618
Conversation
…rotocol, objectql, plugin-approvals, plugin-auth) Part of the #8582 sweep: 39 test-double matches(row, where) fakes across these four packages treated $or/$and as ordinary field names and silently dropped rows on any combinator query. Each now throws on an unrecognised $-prefixed key instead — the recorded practice already pinned in packages/objectql/src/engine-autonumber-*.test.ts ("silently ignoring an unknown operator would let a bad query pass as a good one"). Ledger (scripts/where-matcher-conformance.baseline.json) ratcheted down by the same 39 entries in this commit — pnpm check:where-matcher confirms 0 problems for these files. Refs #8582
…il, plugin-hono-server, plugin-reports, plugin-security, plugin-sharing, rest) Part of the #8582 sweep. 22 more combinator-blind matches(row, where) fakes now throw on an unrecognised $-prefixed key instead of treating it as a field name and silently dropping every row a combinator query touches. Two files needed more than the mechanical guard: - packages/plugins/plugin-sharing/src/business-unit-graph.test.ts already implements $or correctly (a real, exercised recursive conjunction) — added a refusal for any OTHER $-key ($and included) rather than growing a second combinator implementation nothing in the suite exercises. - packages/plugins/plugin-email/src/outbox-sweep.test.ts was the ledger's one UNJUDGED matcher. Its nested-operator throw message (`fakeEngine: unsupported operator ${op}`) coincidentally repeats its enclosing factory function's exact name, and the gate's identifiersIn() text-scanner reads that string-literal word as if it were a reference to the factory — pulling in the whole `fakeEngine(seed)` closure and its unsuppliable `seed` parameter, so the standalone extraction always threw `ReferenceError: seed is not defined`. Disposition: RESTRUCTURE (rename the message so it no longer shadows the enclosing scope) rather than extractor-reach — a one-line, zero-blast-radius fix versus changing the shared identifiersIn() text-scanner every one of the 168 discovered matchers depends on. The matcher now judges CONFORMING. Ledger ratcheted down by these 22 entries in this commit; 14 remain (services/*, triggers) for the next batch. Refs #8582
…alytics, service-automation, service-messaging, service-queue, service-settings, service-storage, trigger-schedule) Part of the #8582 sweep. Final 14 combinator-blind matches(row, where) fakes now throw on an unrecognised $-prefixed key. Two files already implement $and genuinely (a real, exercised recursive conjunction) — service-analytics/src/__tests__/objectql-daterange.test.ts and objectql-read-scope.test.ts. Both get a refusal for any OTHER $-key ($or included) rather than growing a second combinator implementation nothing in their suites exercises. Ledger (scripts/where-matcher-conformance.baseline.json) ratcheted to empty — its intended steady state (pnpm check:where-matcher reference: "78 (4 by refusing)" is now zero grandfathered, matching shape (a)'s empty ledger). pnpm check:where-matcher: 168 matcher(s) discovered, 168 conforming (83 by refusing), 0 silently wrong, 0 unjudged. Refs #8582
… matcher, not refuse it Batch 3 (e089b9f) added a blanket $-key refusal to this file's matches() double, on the (wrong) assumption that $or was dormant here like the rest of the sweep. It is not: the "keeps both operands when a bare equality meets an operator object" test drives query() through fieldLeaves' real NULL-safe $ne expansion (#5298), which emits `$and: [{ $or: [{field:null}, {field:{$ne}}] }]` — the double genuinely receives $or at runtime, and the new refusal turned a passing test red (`Error: test bridge: unhandled operator $or`). Per the issue's own guidance, option 1 (implement) applies where a suite genuinely exercises the combinator — $and was already handled this way in this file, $or now gets the same treatment (`.some()` over the branches), matching the file's existing recursive style. pnpm check:where-matcher confirms this matcher now CONFORMS by correctly answering the combinator battery, not by refusing. Refs #8582
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckNo hand-written docs reference the 0 changed package(s). ✅ |
|
PM review ( ⭐ Two things here are better than the card asked for, and both came from running the real suites rather than trusting the gate's synthetic battery.
Cross-domain conditions met: full file surface declared, 17 packages re-run green, ledger lowered in the same commit so the PR self-proves. Generated by Claude Code |
Closes #8582
What
Sweeps all 75 files (78 matchers + 1 unjudged) grandfathered in
scripts/where-matcher-conformance.baseline.json— test-doublematches(row, where)fakes with no combinator branch at all, which read$or/$andas an ordinary field name, comparedrow.$or(undefined) against the array, and silently dropped every row a combinator query touched.Default remedy (per the issue): make the double refuse —
throwon an unrecognised$-prefixed key, the practice already pinned inpackages/objectql/src/engine-autonumber-*.test.ts. Implemented the combinator instead only where a suite genuinely exercises it (see below).pnpm check:where-matcher(final state):The ledger is now
{}— empty, matching failure-shape-(a)'s steady state.Full file surface (cross-domain exception, per the claim comment)
75 test files across
metadata-protocol,objectql,plugin-approvals,plugin-auth,plugin-email,plugin-hono-server,plugin-reports,plugin-security,plugin-sharing,rest,service-analytics,service-automation,service-messaging,service-queue,service-settings,service-storage,trigger-schedule— test doubles only, zero production files. Disjoint from the two other in-flight production-file claims this batch (#8559packages/objectql/src/engine.ts, #8460packages/objectql/src/registry.ts) — neither touched here.Commits, per-package batches with the ledger lowered in the same commit each time (self-proving against the gate):
metadata-protocol,objectql,plugin-approvals,plugin-auth(39 files)plugin-email,plugin-hono-server,plugin-reports,plugin-security,plugin-sharing,rest(22 files)service-analytics,service-automation,service-messaging,service-queue,service-settings,service-storage,trigger-schedule(14 files)Two files needed more than the mechanical guard
packages/plugins/plugin-sharing/src/business-unit-graph.test.tsalready implements$orcorrectly (a real, exercised recursive conjunction). Added a refusal for any other$-key ($andincluded) rather than growing a second combinator implementation nothing in the suite exercises.packages/services/service-analytics/src/__tests__/objectql-daterange.test.ts— the blanket refusal I first landed (batch 3) turned a passing test red:query()drives throughfieldLeaves's real NULL-safe$neexpansion (非否定路径上的$ne/$nin/$notContains:driver-sql 排除 NULL 行,driver-memory / formula 返回它们(#5146 只裁定了$not) #5298), which emits$and: [{ $or: [{field:null},{field:{$ne}}] }]— this double genuinely receives$orat runtime, it is not dormant here. Per the issue's own guidance ("option 1 only where a suite genuinely exercises combinators"), implemented$orthe same way the file already implements$and(a.some()over the branches) instead of refusing it. Caught by running the full test suite before reporting, not by the gate (the gate only asks the four synthetic battery questions against the lifted function in isolation — it has no way to see what a real caller in this file actually hands the matcher).The 1 unjudged matcher
packages/plugins/plugin-email/src/outbox-sweep.test.ts. Its nested-operator throw message (`fakeEngine: unsupported operator ${op}`) coincidentally repeats its enclosing factory function's exact name (function fakeEngine(seed: RowSeed[])), and the gate'sidentifiersIn()text-scanner reads that string-literal word as if it were a reference to the factory — pulling the whole closure (and its unsuppliableseedparameter) into the standalone extraction, which then threwReferenceError: seed is not definedno matter how the retry/drop loop iterated.Disposition: RESTRUCTURE, not extractor-reach — renamed the message so it no longer shadows the enclosing scope (one line, zero blast radius) rather than changing the shared
identifiersIn()text-scanner every one of the 168 discovered matchers depends on. The matcher now judgesCONFORMING(refuses).Out-of-scope finding
Filed #8615: the gate's discovery heuristic requires a matcher to be a function with two or more identifier parameters, so a single-param
.filter((r) => Object.entries(where).every(...))closure that captureswhere/filterfrom an enclosing scope is structurally invisible to it — 17 such sites across 16 files carry the identical shape-(b) pattern, completely ungraded bycheck:where-matcher. Not fixed here (outside #8582's worklist, which is scoped to the ledger); left for triage.Verification
pnpm check:where-matcher— green (0 silently wrong, 0 unjudged), see above.pnpm testscoped to all 17 touched packages (metadata-protocol,objectql,plugin-approvals,plugin-auth,plugin-email,plugin-hono-server,plugin-reports,plugin-security,plugin-sharing,rest,service-analytics,service-automation,service-messaging,service-queue,service-settings,service-storage,trigger-schedule) — all green, real numbers:pnpm typecheckon every touched package that declares its owntypecheckscript — clean (service-analytics,service-automation,service-storagecarry notypecheckscript; pre-existing DEBT/EXEMPT-ledger packages, untouched by this test-only diff).node scripts/pm/dispatch-gates.mjs <changed paths>), all green:check:cross-package-test-inputs,check:durability-log-level,check:test-source-alias,check:type-source-resolution,check:engine-split-ratio,check:query-options-erasure,check:type-check-coverage(436 frozen DEBT / 1533 frozen TEST_DEBT — unchanged frommain, no regression),check:i18n(9 packages, all in sync),check:nul-bytes.Not in scope
matchesWhereextraction — ruled NO on finding: 16 test doubles short-circuit $or in their WHERE matcher, dropping sibling filters — the suite stays green while testing a different query #7620, not re-litigated.skip-changesetlabel applied).#8559/#8460's production files (packages/objectql/src/engine.ts,packages/objectql/src/registry.ts) — not touched.Generated by Claude Code