Uh oh!
There was an error while loading. Please reload this page.
test(where-matcher): discover single-param filter callbacks that capture their where - #8661
Conversation
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). ✅ |
qq9340100
commented
Aug 14, 2026
PM review — ACCEPT on substance, held unmerged on #8654Reviewed at Per-job conclusions — all 29, not an aggregate readingOne failure: Everything else green, including the six that were still running when the dev reported and which it honestly declined to claim: What I verified independently of the PR body
Where my dispatch was wrong, recorded so it does not propagate
On the numbersThe card's "17" was a count of a shape, and the dev did not report shapes as defects: 64 structural candidates proposed, 62 seated by the behavioural probe, of which 58 combinator-blind (fixed) and 4 already correct. Discovery 169 → 231, ledger stays Mutation matrix: four mutants, predictions written before each run, every one verified to actually mutate ( Why it is not merged⛔ Not armed into a required check that cannot currently pass — auto-merge on a blocked PR waits silently and surfaces no reason. #8654 is a property of Generated by Claude Code |
Fixes#8615
check-where-matcher-conformancecould only see a matcher that took its filter as itsown second parameter. A single-parameter
.filter()callback that closes overwherefrom an enclosing scope was invisible to discovery — the arity was never the defect, it was
only what discovery happened to key on. A gate that cannot see a class of instance cannot
grade it green or red, and "not in the ledger" read as "conforming" for every one of them.
The change — discovery arm 2
discoverInSourcestill looks for the filter as the function's own second parameter first.Failing that, it now looks for an access path the body reads as a filter whose root
resolves in an enclosing scope — a same-file declaration (the
visibleDeclarationswalkalready built for extraction) or a parameter bound by an enclosing function. For extraction
the captured path is re-bound as a synthetic second parameter, so the battery drives
(row, where)uniformly whatever arity the source was written at.Why a path, not a bare identifier
The card proposed matching a captured identifier. Measured, that is not wide enough — and
the failure is not "misses some", it is mis-binding. Roughly half the corpus spells the
capture indirectly (
opts.where,query?.where,q.where,options.where,query.filter), and keying on the identifier alone reads the root (opts) as thefilter object. That produced 21 matchers judged UNJUDGED with a
TypeErrorout of thecontrol probe — this gate treats "could not run" as an error, so the narrower rule would
have manufactured 21 failures while claiming to close a blind spot.
Paths reduce only through plain dotted access, with optional chaining and a
?? {}/|| {}tail unwrapped. A call or a computed key yields no path and no candidate, because the
battery could not synthesise a binding for it. Requiring the root to resolve is a real scope
walk, not a name test: a root binding nowhere same-file is an import or a global, and this
gate does not grade code it cannot see.
The width is filtered behaviourally, as designed
Arm 2 deliberately proposes more structural candidates than it seats. Measured on the corpus:
Measured by building two harnesses from the real gate source differing in
discoverInSourceonly, the "before" harness verified byte-identical to
HEADbygit hash-object.Discovery: 169 to 231 matchers
Of the 62 newly seated:
$orread as an ordinary field name) and arefixed here
"single-param callback" implies "defective"
58 fixed, 0 grandfathered — and the zero is structural
Every one of the 58 now refuses an unrecognised
$-key, the recorded practice frompackages/objectql/src/engine-autonumber-*.test.tsand the shape #8618's sweep used for its75 files.
Nothing is grandfathered, and nothing could have been.#8618 ratcheted
where-matcher-conformance.baseline.jsonto empty, and the gate's MONOTONIC invariant errorson any key absent at the merge base. Verified rather than assumed — a throwaway entry was
added and the gate rejected it in both directions at once:
So the fix-versus-ledger split for this card was forced to 100% fix. The ledger stays
{},and this PR carries no first-measurement entries because there are none to carry.
Liveness: the shape count is not a defect count
The card was explicit that liveness was unmeasured. It is measured now, by the mechanism
#8618's own last commit demonstrated — a refusal turns a live combinator red immediately.
All 14 affected packages run green: 695 test files, 11,257 tests, 0 failures. So all 58
are dormant — reachable, genuinely engine-shaped, judged by the gate, and not currently
handed a
$or/$andby any suite. Dormant is not harmless: the day a test adds one, thesuite would have asserted on an empty result set with nothing erroring. That is exactly what
the refusal now prevents.
The residual the control probe drops, kept as a known limit
The 2 dropped candidates are inverted survivor filters inside a
deletedouble —(r) => !Object.entries(opts.where).every(...). They carry the same shape (b) defect (a$ormatches nothing, so the row is not deleted) but answer the control probebackwards, so they are correctly not row-selecting predicates by this gate's definition.
Teaching the probe to recognise a negated predicate means guessing at intent — the
naming-based reasoning this gate exists to avoid. Both known instances are fixed here anyway;
the gate still cannot grade them. Pinned as a self-test fixture so the limit stays known
rather than drifting into an accidental one, and filed separately.
Self-test fixtures, mutation-verified
Six fixtures added. Every mutant was verified to actually mutate (
git hash-objectdiffersfrom base, restored to base after) and to fail by assertion, not by crashing:
accessPathto identifier-onlyPredictions were written before each run.
Verification
check:where-matcherOK — 231 discovered, 231 conforming (140 refuse), 0 silently wrong,0 unjudged, 0 grandfathered files;
--self-testOKtestgreen (11,257 tests),typecheckgreencheck:type-check-coverage --re-measureEXIT=0, nothing above its ceiling. The onesurplus reported is
@objectstack/lint's pre-existing-1, deliberately left alonescripts/pm/dispatch-gates.mjsand all run green:
check:nul-bytes,check:cross-package-test-inputs,check:durability-log-level,check:error-code-casing,check:kernel-hook-pairs,check:test-source-alias,check:type-source-resolution,check:query-options-erasure,check:i18nTests and one root
scripts/gate only — no package source, nothing publishable changes, sothis carries
skip-changesetrather than a changeset.check-changeset-no-major --self-testis expected red on this branch for a repo-widepost-release reason tracked in #8654 (
.changeset/pre.jsonabsent, no major-declaringchangeset). Not this PR's defect and deliberately not worked around here.
Generated by Claude Code