What
Sixteen test files spread over four packages build an in-memory driver / engine double whose WHERE matcher returns early on $or (and usually $and), discarding every sibling equality key in the same object:
const matchesWhere = (row, where) => {
if (!where || typeof where !== 'object') return true;
if (Array.isArray(where.$and)) return where.$and.every((w) => matchesWhere(row, w));
if (Array.isArray(where.$or)) return where.$or.some((w) => matchesWhere(row, w)); // <-- returns here
for (const [k, v] of Object.entries(where)) { /* the sibling keys, never reached */ }
};
A real driver conjoins them. So for a query shaped like SysMetadataRepository.listDrafts's —
{ state: 'draft', package_id: 'app.x', $or: [{ organization_id: ORG }, { organization_id: null }] }
— the double answers on the $or alone and hands back rows matching neither state nor package_id: active rows, other packages' rows, anything with a matching org. This is not a stricter or looser edge case; it is a different query.
Why it matters — measured, not theorised
Hit while measuring #7559. A probe driving the real publishPackageDrafts twice through one of these doubles had its second publish record no sys_metadata_commit row at all (commitId: undefined) while still promoting the draft — because listDrafts returned the active row alongside the draft one and the batch went down a different path. Correcting the matcher to conjoin (~8 lines) made the same scenario behave as the product does, and only then did the real defect reproduce.
The failure mode is the dangerous direction: the suite stays green while silently testing a scenario nobody wrote. Nothing distinguishes "this double is faithful here" from "this double quietly changed the fixture", and every one of these files reads as if the query were honoured.
Where
$or short-circuit, confirmed by grep on 2c1988c:
packages/objectql/src/ — protocol-recorded-by-null.test.ts, save-meta-response-conformance.test.ts, plugin.authoring-channel.test.ts, publish-meta-response-conformance.test.ts, protocol-save-meta-repo-path-real-engine.test.ts, protocol-registry-shadow.test.ts
packages/runtime/src/domains/share-links-enforcement-context.test.ts
packages/plugins/plugin-security/src/ — row-write-widener-composition.test.ts, vama-write-path-convergence.test.ts, authored-row-write-verdict.test.ts
packages/plugins/plugin-sharing/src/ — bulk-recompute.test.ts, boot-backfill.test.ts, authored-row-write-deferral.test.ts, sharing-rule.test.ts, system-write-skip-notice.test.ts, record-share-cascade.test.ts
Not audited per file: whether each one's suite actually sends a mixed $or + sibling-key query today. Several plausibly never do, in which case the looseness there is dormant rather than active — dormant is still worth closing, because the next test that adds an $or inherits a double that lies. Establishing which are live is the first step of this issue, not a precondition for filing it. sharing and security are the packages most likely to be live: org / owner / group $or predicates are their normal shape.
Suggested shape
Conjoin instead of returning early — the whole change per file:
for (const [k, v] of Object.entries(where)) {
if (k === '$and') { if (!v.every((w) => matchesWhere(row, w))) return false; continue; }
if (k === '$or') { if (!v.some((w) => matchesWhere(row, w))) return false; continue; }
if (k.startsWith('$')) continue;
/* equality compare */
}
Better than sixteen copies: one shared matchesWhere helper these doubles import, so the semantics are fixed once. That is a judgement for whoever picks this up — the copies differ slightly in what else they support, and consolidating them is a larger change than correcting them.
packages/objectql/src/protocol-revert-org-scope.test.ts (PR #7619) carries the corrected form with a comment saying why, and can be copied.
Not
Not a product defect — no shipped code path is affected, and no user hits this. It is a test-fixture fidelity gap, filed per Prime Directive #10 rather than fixed in #7619's PR, which is scoped to the revert. Adjacent but distinct from #7264 (objectql test driver doubles any-annotated at scale): that one is about the type channel over these same doubles, this one about their runtime semantics.
Found while measuring #7559.
Generated by Claude Code
Generated by Claude Code
What
Sixteen test files spread over four packages build an in-memory driver / engine double whose
WHEREmatcher returns early on$or(and usually$and), discarding every sibling equality key in the same object:A real driver conjoins them. So for a query shaped like
SysMetadataRepository.listDrafts's —— the double answers on the
$oralone and hands back rows matching neitherstatenorpackage_id: active rows, other packages' rows, anything with a matching org. This is not a stricter or looser edge case; it is a different query.Why it matters — measured, not theorised
Hit while measuring #7559. A probe driving the real
publishPackageDraftstwice through one of these doubles had its second publish record nosys_metadata_commitrow at all (commitId: undefined) while still promoting the draft — becauselistDraftsreturned the active row alongside the draft one and the batch went down a different path. Correcting the matcher to conjoin (~8 lines) made the same scenario behave as the product does, and only then did the real defect reproduce.The failure mode is the dangerous direction: the suite stays green while silently testing a scenario nobody wrote. Nothing distinguishes "this double is faithful here" from "this double quietly changed the fixture", and every one of these files reads as if the query were honoured.
Where
$orshort-circuit, confirmed by grep on2c1988c:packages/objectql/src/—protocol-recorded-by-null.test.ts,save-meta-response-conformance.test.ts,plugin.authoring-channel.test.ts,publish-meta-response-conformance.test.ts,protocol-save-meta-repo-path-real-engine.test.ts,protocol-registry-shadow.test.tspackages/runtime/src/domains/share-links-enforcement-context.test.tspackages/plugins/plugin-security/src/—row-write-widener-composition.test.ts,vama-write-path-convergence.test.ts,authored-row-write-verdict.test.tspackages/plugins/plugin-sharing/src/—bulk-recompute.test.ts,boot-backfill.test.ts,authored-row-write-deferral.test.ts,sharing-rule.test.ts,system-write-skip-notice.test.ts,record-share-cascade.test.tsNot audited per file: whether each one's suite actually sends a mixed
$or+ sibling-key query today. Several plausibly never do, in which case the looseness there is dormant rather than active — dormant is still worth closing, because the next test that adds an$orinherits a double that lies. Establishing which are live is the first step of this issue, not a precondition for filing it.sharingandsecurityare the packages most likely to be live: org / owner / group$orpredicates are their normal shape.Suggested shape
Conjoin instead of returning early — the whole change per file:
Better than sixteen copies: one shared
matchesWherehelper these doubles import, so the semantics are fixed once. That is a judgement for whoever picks this up — the copies differ slightly in what else they support, and consolidating them is a larger change than correcting them.packages/objectql/src/protocol-revert-org-scope.test.ts(PR #7619) carries the corrected form with a comment saying why, and can be copied.Not
Not a product defect — no shipped code path is affected, and no user hits this. It is a test-fixture fidelity gap, filed per Prime Directive #10 rather than fixed in #7619's PR, which is scoped to the revert. Adjacent but distinct from #7264 (objectql test driver doubles
any-annotated at scale): that one is about the type channel over these same doubles, this one about their runtime semantics.Found while measuring #7559.
Generated by Claude Code
Generated by Claude Code