[RESCUED — evidence complete] test(objectql): collapse the twelve ./registry module-mocks into one factory (#10551) - #10634
Conversation
…e factory
`vi.mock('./registry', …)` was hand-copied into twelve test files in
`packages/objectql/src`, and the copies had drifted: eleven declared twelve
members, `engine-count-read-filter.test.ts` declared eleven and omitted
`getAllObjects` — the #9002 shape, inert only because no path its suite drives
reaches one of the thirteen `getAllObjects` call sites in this package.
The deciding evidence for collapsing rather than adding the missing line is the
two lesson comments themselves: the #9002 explanation lived in exactly one copy
and the #9154 explanation in nine others, and neither could reach the rest
because there was no shared factory to write them in. Both now live in
`registry-module-mock.ts`, which every call site inherits.
`engine.test.ts` keeps its stateful in-memory registry as per-member overrides
over the shared member set, so it too fails when the shared factory loses a
member. The `async` factory form is what makes importing the shared module legal
under `vi.mock` hoisting.
Test infrastructure only — no production source is touched.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019yDEhPBC3tcGkW9bkce1HM
…gistry-mock-factory
📓 Docs Drift CheckThis PR changes 1 package(s): 4 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 14 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 884572b6430af1884160589ab74e99545b46333e && git checkout 884572b6430af1884160589ab74e99545b46333e
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 9185ff0213c1590bf3808d5955cddd2becf924ea 3a8999ea07a54649e3143aaa97dd438b965c4c80 && git checkout -B drift-repro 9185ff0213c1590bf3808d5955cddd2becf924ea && git merge --no-ff 3a8999ea07a54649e3143aaa97dd438b965c4c80
node scripts/docs-audit/affected-docs.mjs --json 9185ff0213c1590bf3808d5955cddd2becf924ea
|
…gistry-mock-factory
./registry module-mocks into one factory (#10551)./registry module-mocks into one factory (#10551)
Fixes #10551
✅ The rescue is now complete — all three structural proofs are in, and the rescued work stands
This PR carries the killed dev's 2 commits unmodified. Nothing was re-implemented, and no
line of the collapse was rewritten. The commits added since are the
origin/mainmerge only.Measured merge-base anchored against
9185ff0213: 13 files, +288 / −419 — unchanged fromthe rescued measurement.
Evidence head:
3a8999ea07. Every number below was produced at that commit.⭐ Check 0 — the #9002 comment survived, and gained a sibling
Before the collapse the #9002 lesson lived in exactly one of the twelve copies
(
engine-middleware-operation-vocabulary.test.ts). It is now in the shared factory,packages/objectql/src/registry-module-mock.ts, verbatim except for its closing sentence,which was generalised from one suite to all of them. Quoted as it now stands:
The only edit is the last sentence. Before, it read "Empty is the right body here: this suite
pins the middleware operation VOCABULARY and registers no relations, so 'no object references
the deleted one' is the truthful answer rather than an invented one." — suite-specific, and
untrue of the other eleven inheritors. The generalisation is correct for a shared factory.
The dev also carried in the #9154 lesson, which at merge base was spread across ten files,
and added a note recording why both now live in one place:
The cleanup did not delete the reason for the cleanup.
⭐ Proof 1 — the share proof: one deleted line reddens ten files
Deleted
getAllObjects: vi.fn(() => [])(line 158) from the shared factory's default memberset, ran the whole package, then restored.
Ten of the twelve call sites reddened from a single deleted line — a half-done refactor that
had left copies hand-rolled could not do this:
engine-autonumber-default-format.test.tsengine-autonumber-defer.test.tsengine-autonumber-resync.test.tsengine-autonumber-seed-outage.test.tsengine-autonumber-seed-scan.test.tsengine-autonumber-seed-suffix.test.tsengine-filter-tokens.test.tsengine-middleware-operation-vocabulary.test.tsengine-multivalue-normalize.test.tsengine-validation-locale.test.tsengine-count-read-filter.test.tsengine.test.ts⭐ The two that stayed green are exactly the two the model predicts, and each confirms a
separate claim.
engine-count-read-filter.test.tsis the outlier that never declaredgetAllObjectsat all — its staying green is an independent re-confirmation of the filer'sinertness measurement.
engine.test.tsoverridesgetAllObjectswith a real in-memorybody, so the default's absence cannot reach it — which proves the override path works and that
an overriding suite still inherits the other eleven members.
The failure text, and note where it lands:
That is the #9154 call site — the roll-up summary index read — failing hard rather than
silently answering "no objects". The lesson comment's own claim ("With the optional call gone
the omission is a hard
TypeError") is therefore not just documented here but measured.Restore proved on disk, not by an editor's exit code:
No rebuild was needed for this ablation and none is claimed: the twelve suites reach the factory
through the relative specifier
./registry-module-mock.jsinside the same package, whichvitest transforms from source. Nothing resolves through this package's
exportstodist/, sothere is no stale-artifact path for the measurement to hide in — independently corroborated by
check:test-source-alias, which registers the package's dist-resolving imports and did notgrow one for this module.
Proof 2 — the superset proof
Member sets read by AST (TypeScript compiler API) from each pre-collapse file's
const instanceobject literal, at merge base.Before — eleven files, 12 members each:
Before — the outlier
engine-count-read-filter.test.ts, 11 members:—
getAllObjectsabsent, exactly as the card states.After — the shared factory's default set, 12 members plus an overrides spread:
The after set equals the union of all twelve before-sets, and is therefore a superset of
every one of them individually — a strict superset of the outlier's eleven.
RegistryDoubleInstanceadditionally carries an index signature, so a suite may add a member without a type edit.
Two things checked beyond the member names, because a matching name set can still hide a
behaviour change:
computeFQN/parseFQN/RESERVED_NAMESPACES, and those are exactly the factory's defaults.engine.test.tsdeclared a namespace-aware pair, and it passes them through the factory's
computeFQN/parseFQNoptions — preserved, not defaulted away.engine.test.tsis the only suite with stateful bodies. The four membersit now inherits rather than declares (
registerNamespace,registerKind,registerItem,registerApp) were barevi.fn()in the pre-collapse file, so inheritance isbehaviour-identical. Verified by reading the merge-base source, not assumed.
Also confirmed: twelve is the complete population. A merge-base scan for
vi.mock('./registry'underpackages/objectql/src/returns exactly 12 files, and they areexactly the 12 the collapse touched. No thirteenth copy was left hand-rolled.
Proof 3 — suite numbers and the gate union
Suite, whole package, at
3a8999ea07:(The filer's pre-change baseline was 224 / 3956; the growth is landings merged from
main,not this branch — this branch adds no test case, it removes duplicated fixture code.)
Typecheck:
pnpm --filter @objectstack/objectql typecheck→ exit 0, with the script nameechoed in the output so this is not a zero-match silent pass. Note this also exercises the new
module:
registry-module-mock.tsis deliberately not named*.test.ts, so unlike the twelvecopies it replaces it is inside the program
tsc --noEmitreads.Gate union — derived by
node scripts/pm/dispatch-gates.mjswith no path arguments, afterthe final commit, at
3a8999ea07. Each row quotes the gate's own verdict line.check:durability-log-level✓ durability-degradation log levels: 30 durability-critical catch seam(s), all loud, rethrowing or propagating to the callercheck:slot-lookup✓ slot-lookup ratchet holds: 107 unswept site(s) in 25 file(s), none newcheck:test-source-aliascheck-test-source-alias OK — 72 packages with tests scanned; 61 registered as still resolving a workspace dep through dist/check:type-source-resolutioncheck-type-source-resolution OK — 76 packages with a tsconfig.json scannedcheck-engine-split-ratio.mjsratio: 97.5%(a reported metric, no threshold)check-affected-docs.mjscheck:query-options-erasure✓ query-options-erasure ratchet holds: 67 unswept non-test site(s) in 17 file(s), none new·test surface: 240 site(s) in 47 file(s) — at the ceilingcheck:type-check-coveragecheck-type-check-coverage: OK — 64/77 workspace packages type-checked (plus the root), 13 in the DEBT ledgercheck:type-check-debtcheck-type-check-coverage --re-measure: OK — 33 ledger entr(ies) re-measured in 222.5s, 1912 raw tsc error(s) total, none above its recorded numbercheck:engine-double-contractcheck-engine-double-contract: OK — 371 pinned, 133 in the DEBT ledger, 2 exemptcheck:where-matcher✓ where-matcher conformance holds: 271 matcher(s) discovered, 271 answer the combinator battery correctly or refuse it loudlycheck:nul-bytescheck-nul-bytes: OK (scanned 6232 text file(s) ... no raw ASCII control bytes)check:type-check-debtrequires a built workspace closure, so the full farm build was run first(
turbo run buildover./packages/*and./packages/*/*): 70 successful, 70 total, 5m17s.It was run with nothing else concurrent and a 4 GB heap cap, given what killed the previous
three dispatches on this box.
@objectstack/objectql's ledger entries did not move in eitherdirection, and no ledger file appears in this diff.
Changeset: deliberately none, plus
skip-changesetReasoning, stated rather than omitted. This is test-only and publishes nothing:
*.test.ts.packages/objectql/src/registry-module-mock.ts, is not reachablefrom either
tsupentry (src/index.ts,src/core.ts). Verified two ways: no import of itexists anywhere outside the twelve test files, and a grep of the freshly built
packages/objectql/dist/forcreateRegistryModuleMockandregistry-module-mockreturnszero occurrences.
release that does not exist.
The repo's mechanism for that declaration is the
skip-changesetlabel — the author's explicitopt-out read by
changeset-checkinpr-automation.yml— so the label is applied here as partof this PR rather than left to CI.
What I deliberately did NOT do
work is correct; there was nothing to rewrite, and rewriting it unprompted was out of scope.
vi.mockhoisting directly. This lane was bitten once by aprobe that pre-empted hoisting and turned two unrelated tests red. No probe is needed: the
async-factory form is confirmed by the whole suite running green through it, and the ablation
proves the shared module is genuinely what all ten reddened suites receive at runtime. A
synthetic probe would only add artefact risk to an already-answered question.
@objectstack/plugin-auth's TEST_DEBT surplus.check:type-check-debtreports its ledger records 109 where tsc now measures 97, and offers
--lower. That gap ispre-existing on
main, belongs to another author's package, and is already tracked upstream bythe standing issue the gate's own message names. Lowering it here would be unrelated scope.
Generated by Claude Code