test(objectql): conjoin $or/$and with sibling filters in six driver doubles - #7846
Merged
Merged
Conversation
…er doubles (part of #7620) Six in-memory driver doubles in `packages/objectql/src` returned early on `$and`/`$or`, discarding every sibling equality key in the same `where` object. A real driver ANDs them, so a query like { state: 'draft', package_id: 'app.x', $or: [{ organization_id: ORG }, { organization_id: null }] } was answered on the `$or` alone — a different query than the one written, with the suite still green. Fold `$and`/`$or` into the entries loop, matching the corrected form `protocol-revert-org-scope.test.ts` already carries from #7619. Each matcher keeps exactly the operator surface it had: `$eq` unwrapping, the `undefined`->`null` comparison normalisation, and the skip for any other `$`-prefixed key (which a non-array `$and`/`$or` still falls through to). Measured, not assumed: a probe in each matcher logging every `where` it was handed recorded 132 calls across the six suites and not one `$or` or `$and` — all six are dormant. The 132 plain-equality calls are the control proving the probe was live. No existing outcome changes; packages/objectql is 185 files / 3274 tests passing before and after. Deliberately not extracted into a shared helper: the repo's own rationale in `publish-meta-response-conformance.test.ts` keeps these harnesses self-contained so two gates can fail independently, and an objectql-local helper could not serve the ten remaining files in other packages anyway. Part of #7620 — this is the `packages/objectql` lane only; the plugin-sharing, plugin-security and runtime files are routed separately. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Da8i4RxJBSv73tgr92D9KB
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckNo hand-written docs reference the 0 changed package(s). ✅ |
This was referenced Aug 11, 2026
huangyiirene
marked this pull request as ready for review
August 11, 2026 21:20
huangyiirene
pushed a commit
that referenced
this pull request
Aug 11, 2026
… tier 1, part of #7620) CI red on PR #7857: `packages/objectql/src/protocol-commit-history.test.ts` had two org-scoped revert cases fail with COMMIT_NOT_FOUND. Measured rather than assumed: its `matchesWhere` was pure flat equality, so the widened lookup `{ id, $or: [{organization_id: <org>}, {organization_id: null}] }` compared `row['$or']` against the array and matched nothing. The double is the blind party, not the fix. Both failing rows carry the CALLER'S OWN org (`organization_id: 'org_a'`, request org `'org_a'`), so they match the FIRST `$or` branch outright — the same row the strict equality already accepted. No real behaviour changed, and neither case's subject (#6602's registry org-asymmetry) involves the commit lookup at all; it is merely the door they enter through. Conjoined with the sibling keys in the entries loop, matching the corrected form #7846 landed across six doubles in this package an hour earlier. Not the early-returning `if ($or) return …some(…)` shape those six carried before it: that discards sibling keys, so `{ id, $or: [...] }` would stop constraining `id` and could return some other commit whose org matched. This file was not among #7846's six because it had no operator handling to correct, so it is a new member of the #7620 lane rather than a regression of it. `undefined` normalises to `null` on comparison, same as the six, because a column a row never set reads as NULL out of a real driver. @objectstack/objectql: 185 files / 3274 tests passing (was 184/3272 with the two failures) — exactly the two cases restored, nothing else moved. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019hxiiv8qFCUmDThHU1k7HV
This was referenced Aug 12, 2026
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Aug 17, 2026
…ommit (objectstack-ai#7819 tier 1) (objectstack-ai#7857) * fix(metadata-protocol): let an org-scoped caller revert an env-wide commit (objectstack-ai#7819 tier 1) `revertCommit` and `rollbackToPackageCommit`'s target lookup each resolved their target commit with a strict `organization_id` equality, which matches no row whose column is NULL. An org-scoped caller therefore got COMMIT_NOT_FOUND (404) for any commit recorded env-wide — a row that demonstrably exists and that the same caller's `listCommits` hands back. Both lookups now accept org-scoped or env-wide rows, the same `$or` `deletePackage` (objectstack-ai#7705) and `listCommits` (objectstack-ai#7779) already carry. The `$or` was chosen over the two alternatives rather than copied. `where` is keyed on `id`, so the predicate reads like an authorization filter on a unique key; measured against the only door it is not one. Authorization is `requireManageMetadata`, checked before the call, and the `organizationId` that arrives is the session's active org selection from `resolveActiveOrganizationId` — a resolver whose body is entirely catch-wrapped and whose `undefined` omits the predicate, i.e. the widest reading. A boundary that fails open is not a boundary, which rules out "keep the check but distinguish 'not yours' from 'no such commit'". Dropping the predicate outright would newly let an org caller revert another organization's commit by id, a widening this card never asked for. The body already agreed with the `$or`: objectstack-ai#7559 made each item resolve its scope from the row, and since objectstack-ai#7814 `rollbackToPackageCommit` plans from `listCommits` (org + env-wide) and fed each id back into a lookup that refused half of them. The no-org branch is deliberately left un-narrowed, exactly as objectstack-ai#7705 and objectstack-ai#7779 left theirs. Pinned by a new real-engine/real-driver suite in packages/runtime (eight cases: the premise out of SQLite, the positive per site, both negative directions, and the no-org door per site; refusals asserted on code AND status per ADR-0112). The objectstack-ai#7814 handoff assertion that pinned this as known-incomplete now asserts the rollback succeeds. Reverse verification, direction predicted first: 3 failed | 11 passed, exactly the three positive cases. Tier 1 only — `duplicatePackage` and `reassignOrphanedMetadata` are untouched and objectstack-ai#7819 stays open to carry them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019hxiiv8qFCUmDThHU1k7HV * test(objectql): teach the commit-history double `$or`, conjoined (objectstack-ai#7819 tier 1, part of objectstack-ai#7620) CI red on PR objectstack-ai#7857: `packages/objectql/src/protocol-commit-history.test.ts` had two org-scoped revert cases fail with COMMIT_NOT_FOUND. Measured rather than assumed: its `matchesWhere` was pure flat equality, so the widened lookup `{ id, $or: [{organization_id: <org>}, {organization_id: null}] }` compared `row['$or']` against the array and matched nothing. The double is the blind party, not the fix. Both failing rows carry the CALLER'S OWN org (`organization_id: 'org_a'`, request org `'org_a'`), so they match the FIRST `$or` branch outright — the same row the strict equality already accepted. No real behaviour changed, and neither case's subject (objectstack-ai#6602's registry org-asymmetry) involves the commit lookup at all; it is merely the door they enter through. Conjoined with the sibling keys in the entries loop, matching the corrected form objectstack-ai#7846 landed across six doubles in this package an hour earlier. Not the early-returning `if ($or) return …some(…)` shape those six carried before it: that discards sibling keys, so `{ id, $or: [...] }` would stop constraining `id` and could return some other commit whose org matched. This file was not among objectstack-ai#7846's six because it had no operator handling to correct, so it is a new member of the objectstack-ai#7620 lane rather than a regression of it. `undefined` normalises to `null` on comparison, same as the six, because a column a row never set reads as NULL out of a real driver. @objectstack/objectql: 185 files / 3274 tests passing (was 184/3272 with the two failures) — exactly the two cases restored, nothing else moved. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019hxiiv8qFCUmDThHU1k7HV --------- Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Aug 17, 2026
…(part of objectstack-ai#7620) (objectstack-ai#8493) Twelve in-memory WHERE matchers across packages/plugins/plugin-sharing, packages/plugins/plugin-security and packages/runtime returned early on $or/$and, discarding every sibling equality key in the same filter object — a real driver ANDs them. Corrected to the same conjoin-with-siblings shape already used by packages/objectql's six (objectstack-ai#7846) and by several already-fixed siblings in these two packages. Measured live-vs-dormant per file via an fs.appendFileSync probe (with a positive control proving it would catch a live case): all twelve are dormant today, for two different reasons. plugin-sharing's six never receive $or/$and at all. plugin-security's five and the one runtime file do receive them, but always as the sole key in their filter object (no sibling ever present alongside), so early-return and conjoin produce identical results in every observed call. No test outcome changes. Re-grepped the issue's file enumeration at this branch's base ref rather than trusting it: plugin-sharing/src/sharing-rule.test.ts was already fixed, and three files this commit touches were never named in the issue (plugin-sharing/src/sharing-service.test.ts, plugin-security/src/check-only-write-scope.test.ts, plugin-security/src/select-only-write-visibility.test.ts). Claude-Session: https://claude.ai/code/session_01RDTnVvsgA6cUZ4xFVtPZRy Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #7620
Part of, notFixes, and deliberately so. #7620 names 16 files across four packages. This PR carries the six inpackages/objectql/src— thedomain:engine-corelane, per the claim comment on the card. The remaining ten (6plugin-sharing, 3plugin-security, 1runtime) belong to two other seats and are routed separately, so the card must survive this merge.What
Six in-memory driver doubles built a
WHEREmatcher that returned early on$and/$or, discarding every sibling equality key in the same object:A real driver ANDs them. So a query shaped like
SysMetadataRepository.listDrafts's —— would have been answered on the
$oralone, handing back rows matching neitherstatenorpackage_id. Not a stricter or looser edge case: a different query, with the suite staying green while testing it.The fix folds
$and/$orinto the entries loop so they compose with their siblings, matching the corrected formprotocol-revert-org-scope.test.tsalready carries from #7619 (re-verified intact on currentmain).$or/$andseenpublish-meta-response-conformance.test.tsprotocol-recorded-by-null.test.tsprotocol-save-meta-repo-path-real-engine.test.tsprotocol-registry-shadow.test.tssave-meta-response-conformance.test.tsplugin.authoring-channel.test.tsLive vs dormant — measured, not assumed
The card names establishing this as step one. A probe was installed in each of the six matchers, logging every
whereit was handed, and the suites run:132 matcher calls across the six suites, and not one
$oror$and. All six are dormant.The two zero-call files never invoke their matcher at all — those suites drive writes, not reads. The control that makes the silence evidence rather than a dead probe is the 132 plain-equality calls the same instrumentation did record.
Consequence: no existing test outcome changes, and none should. There was no correction-induced failure to report — the interesting result this card could have produced (a test that was passing against a query nobody wrote) did not materialise in this lane. The card's own note that
sharingandsecurityare the likeliest to be live is unaffected by this measurement and still worth the other seats' attention.Dormant is still worth closing: nothing distinguished "this double is faithful here" from "this double quietly changed the fixture", and the next test to add an
$orwould have inherited a matcher that lies.Capability preservation
Each matcher keeps exactly the operator surface it already had —
$equnwrapping, theundefined→nullcomparison normalisation, and the skip for any other$-prefixed key. A non-array$and/$orstill falls through to that skip, exactly as before. Measured first: the six proved semantically identical, differing only in indentation (one is 2-space) and an intermediaterowValvariable, so there was no lowest-common-denominator to flatten anything to.No shared helper — stated deliberately
A helper local to
packages/objectqlwould genuinely have reduced six to one. Rejected anyway, for three reasons:publish-meta-response-conformance.test.tscarries the repo's own written rationale against it: "a gate that imports its own substrate from another gate's file couples two tripwires that must be able to fail independently."plugin-sharing/plugin-security/runtime— a cross-package one is out of scope by construction — so it would add a second convention rather than consolidate to one.Verification
All run locally in a dedicated worktree at
b54aaab.packages/objectqlvitest (full)pnpm check:query-options-erasureb54aaab, no files addedpnpm check:type-check-debtpnpm check:durability-log-levelpnpm check:engine-double-contractnode scripts/check-engine-split-ratio.mjseslinton the sixGate list derived with
node scripts/pm/dispatch-gates.mjs <the six paths>, not enumerated by hand. Neither ratchet baseline was raised. Nothing here let me lower a ledger entry:packages/objectqlis fully type-checked and carries no entry, and the surplus the debt gate reports sits in unrelated packages.Not in this PR
any-annotated at scale — the retired-capability tombstone has no tsc channel in 64 files #7264 (these same doubles areany-annotated at scale) — that is the type channel over the same fixtures; this is their runtime semantics. Different fix, different round.Generated by Claude Code