Skip to content

Commit 57f54a0

Browse files
os-steveclaude
andauthored
Gate: an ObjectQL find double in a test must apply the caller's limit, or refuse it loudly (#12030)
* wip: objectql double-limit gate (rescued uncommitted work) Rescue commit of in-place worktree contents after a container restart. Verification has NOT been re-established at this point; a later commit carries the verified state. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx * fix(tooling): seat an ObjectQL find double only on positive control evidence The control probe granted a seat whenever it had not been disproven, rather than when it had been proven: `allCarry` answers `null` when NO returned row carries the probe's field, and `null !== false`. So a double answering both control probes with rows of its OWN -- constant stubs, and schema-signature fixtures that exist only to satisfy a parse() -- was seated and then graded limit-blind. That is debt with no possible remedy: there is no corpus to bound, and a shrink-only ledger carrying unfixable entries can never burn down. 19 of 294 seated candidates were in that state. They now take the same fallthrough every unseated candidate takes: 15 drop OUT OF SCOPE (the body never filters), 4 are DECLARED unjudged (it does, but the lift cannot drive it). Both directions are pinned by new --self-test fixtures. Baseline re-derived from the corrected measurement: 263 files / 186 blind / 50 unjudged -> 251 files / 167 blind / 54 unjudged. Shape breakers are unchanged at 32, and the header's SHAPED invariant -- which claimed no shape violation was grandfathered while 32 were -- now describes what the ledger holds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx * fix(tooling): record the bare-root verdict for the ObjectQL double-limit gate REFUSE-UNSPELLABLE: the gate walks packages/ but admits *.test.ts only (2696 of 5161, 52%). packages/** is the only spellable claim and covers 2465 non-test files the gate never opens; every narrower glob collapses to packages//.test.ts and covers 0 of 2696. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx * fix(tooling): re-measure the double-limit ledger against the merged tree The initial measurement was taken at merge base 311e207. Merging 42 commits of main moved the corpus underneath it: three new test files arrived carrying find doubles, and main's #11962 independently taught the bootstrap-declared-capabilities double to honour the bound. Re-measured: 251 -> 253 files, 167 -> 168 blind, 54 -> 55 unjudged, wrong unchanged at 32. Ratchet DOWN applied, three arrivals seated as pre-existing debt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx * test(plugin-auth): bound the audience-posture find double by presence, not truthiness The double read the caller's bound as `if (q.limit)`, so `limit: 0` -- a request for NOTHING -- returned every matched row. Arrived with #11767 after this branch's ledger was measured; fixed rather than seated, since it is one line in one file. Ledger unchanged: 253 files, 168 blind, 32 wrong, 55 unjudged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 8450eea commit 57f54a0

6 files changed

Lines changed: 2155 additions & 1 deletion

File tree

.github/workflows/lint.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2567,6 +2567,28 @@ jobs:
25672567
- name: WHERE-matcher conformance gate
25682568
run: pnpm check:where-matcher
25692569

2570+
# ObjectQL double `limit` gate (#11525, from #10978). The third member of
2571+
# the family above, and the one the other two leave uncovered: a
2572+
# `find(object, opts)` double that matches `where` correctly and then hands
2573+
# back EVERY matched row. It cannot tell a read bounded at 200 from the same
2574+
# read bounded at 1000, or from an unbounded one, so any limit change on
2575+
# that read is green by construction and the production symptom is a
2576+
# silently truncated result set rather than an error -- #10978's worked
2577+
# example truncates an RLS input. PR #11521 converted nine doubles on the
2578+
# authorization path and measured the population; #11525 is the observation
2579+
# one level up: nothing held those nine right. This lifts each discovered
2580+
# double out of its file and asks it a bounded question, encoding all three
2581+
# shape rules the nine settled -- presence not truthiness, bound after the
2582+
# filter, bound before any row-touching stage. Behavioural rather than
2583+
# syntactic on purpose: the bound is applied through inline slices, per-file
2584+
# helpers and shared helpers alike, and no pattern-match over source spans
2585+
# them. Pre-existing doubles are in a shrink-only measured baseline. Runs
2586+
# its own --self-test first: the detector can be broken while every double
2587+
# is fine, and a scan that quietly stops matching would report OK while
2588+
# reading nothing (#4868's family).
2589+
- name: ObjectQL double limit gate
2590+
run: pnpm check:objectql-double-limit
2591+
25702592
# Paired kernel-hook pin gate (#5282, from #5170 / #5257 / #5274). The two
25712593
# kernels — ObjectKernel (production) and LiteKernel (vitest / serverless /
25722594
# edge) — run the same plugin code and the same hook vocabulary, but do NOT

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@
126126
"check:driver-memory-census": "node scripts/check-driver-memory-census.mjs --self-test && node scripts/check-driver-memory-census.mjs",
127127
"check:engine-double-contract": "node scripts/check-engine-double-contract.mjs --self-test && node scripts/check-engine-double-contract.mjs",
128128
"check:where-matcher": "node scripts/check-where-matcher-conformance.mjs --self-test && node scripts/check-where-matcher-conformance.mjs",
129+
"check:objectql-double-limit": "node scripts/check-objectql-double-limit.mjs --self-test && node scripts/check-objectql-double-limit.mjs",
129130
"check:resume-authority-declared": "node scripts/check-resume-authority-declared.mjs --self-test && node scripts/check-resume-authority-declared.mjs",
130131
"check:spec-parsed-alias": "node scripts/check-spec-parsed-alias.mjs --self-test && node scripts/check-spec-parsed-alias.mjs",
131132
"check:tenant-chokepoint": "node scripts/check-tenant-chokepoint.mjs --self-test && node scripts/check-tenant-chokepoint.mjs",

packages/plugins/plugin-auth/src/audience-posture.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ const createMemoryEngine = () => {
8383
async find(name: string, q: any = {}) {
8484
let out = rows(name).filter((r) => matches(r, q.where));
8585
if (q.offset) out = out.slice(q.offset);
86-
if (q.limit) out = out.slice(0, q.limit);
86+
// Presence, not truthiness: `limit: 0` is a request for NOTHING, and `0`
87+
// is falsy — `if (q.limit)` answers it with every matched row.
88+
if (typeof q.limit === 'number') out = out.slice(0, q.limit);
8789
return out.map((r) => project(r, q.fields));
8890
},
8991
async count(name: string, q: any = {}) {

0 commit comments

Comments
 (0)