chore(tests,tooling): rule the second driver-memory consumer and gate the census (#6664) - #6693
Merged
Merged
Conversation
… the census (#6664) Fixes #6664 Maintainer ruling 2026-08-08: Option A + Option C in one PR, Option B rejected. A — `packages/runtime/src/autonumber-seed-cross-side-parity.integration.test.ts` gets its own ruling block, inheriting #5704's Q2 = B: its schemaless arm is structural (InMemoryDriver declares `supports = {}`, so the engine's fallback seeding owns the counter; SqlDriver advertises the capability and answers from its own sequence bootstrap, which is the other arm of the same `toBe()`). The driver-split file's census is restated as two ruled consumers. C — `scripts/check-driver-memory-census.mjs` + `scripts/driver-memory-census.ledger.json` replace the prose census with a ledger and a gate: every module binding of `@objectstack/driver-memory` in a tracked TS file and every package.json dependency declaration must be ledgered, every ledger entry must still be live, each ruled file must still carry its ruling markers, and each must state the ruled COUNT verbatim — so the sentence and the set can no longer drift apart. The gate polices the retirement programme's bookkeeping only; nothing here invests in the driver (#5499 freeze). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K94yzy5CVgC2JtrqYAuDk2
|
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). ✅ |
qq9340100
marked this pull request as ready for review
August 8, 2026 13:05
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.
Fixes #6664
Maintainer ruling (2026-08-08, on the issue): Option A + Option C in one PR; Option B rejected. This PR is exactly that, and nothing else.
Premise re-verified on current
origin/main(d6d1a50)The issue's grep evidence was taken at e6025e9. Re-run before implementing — still true, with the file drifting one line:
PM assumption 1 also confirmed: PR #6665 already rewrote the ruled file's census sentence to point here, so this PR builds on the post-#6665 wording and does not restore the old "only here" claim.
A — the second consumer is ruled, not migrated
packages/runtime/src/autonumber-seed-cross-side-parity.integration.test.tsgets its own ruling block in the same style as the driver-split file's, stating why its arm is structural rather than a convenience:InMemoryDriverdeclaressupports = {}, so the driver has no autonumber of its own and the engine's fallback seeding owns the counter;SqlDriveradvertises the capability, so its own sequence bootstrap answers instead — that is the other arm of the sametoBe(), four functions down.Point the schemaless arm at sqlite
:memory:and both arms become the same implementation: the assertion then passes because nothing distinguishes them, not because the two seeders agree. That is the always-green shape this family has already paid for once (#5830 heldauth-contains-filter.test.tsback on exactly that measurement, and #5893 migrated it only after #5702 gave its SQL arm a real verdict again).The freeze half is quoted from the ruling rather than paraphrased — #5704, maintainer 2026-08-06: 「#5499 冻结令冻的是缺陷修复投入,不禁止作参照物使用」.
The driver-split file's census is restated as two ruled consumers, and one inherited inaccuracy is corrected while I was in the block: #5704's consequence reads 「runtime 的 driver-memory devDep 长期保留(仅此一个消费点)」, but the declaration is in
packages/runtime'sdependencies, notdevDependencies(besidedriver-sql/driver-sqlite-wasm, which this package declares for the same reason — the datasource factory resolves them by dynamic import), and it now serves two ruled consumers rather than one. The ruling is quoted verbatim; the ledger records what is actually there.C — the census becomes a ledger with a gate reading it
scripts/check-driver-memory-census.mjs+scripts/driver-memory-census.ledger.json, wired aspnpm check:driver-memory-censusand run inlint.yml's ESLint job (static AST, no build needed).Five invariants, each named in the failure text:
DISCOVEREDLEDGEREDpackage.jsondeclaration has a ledger entry whosekind/fieldmatches. An unledgered arrival is the #6664 defect itself.LIVERULEDruledConsumersentry is a static import whose file still contains itsrulingMarkers.CENSUSCENSUSis the one aimed straight at this card: the old census was prose with no second party, so it expired the first time the hand-edit step was skipped. Now changing the ruled set makes both files' sentences fail until they are rewritten.Two shape decisions worth flagging, both inside the "your call on the cleanest mechanical shape" the ruling delegates:
from '...'. It classifies every module-binding position — static import,export ... from,await import(),import()in type position,require(),vi.mock(). The dominant form for this package in this repo is the dynamic import (three shipped consumers use it), so a static-only scan would have had its hole exactly where the next arrival lands.vi.mockis included rather than silently skipped, because an unstated exclusion is the thing this gate replaces — andvi.mock('X', async (importOriginal) => ...)does reach the real module.packages/runtime/tsup.config.tscarries the specifier as a bare string in a bundler externals array; the ruled file's own block names the package five times. Positions decide this, so a comment and an externals entry are reported as mentions under--listand never as arrivals.The ledger holds 16 entries: 11 TS bindings (2 ruled + 9 others) and 5 manifest declarations. The 14 non-ruled ones are not exemptions: each carries a closed-vocabulary
axisand awhythe gate refuses to accept empty.product-consumerentries are the ones #5704 carved out of the migration surface by name (「⛔ 明确不在本单范围」) — their fate belongs to the package-retirement decision (#5499: A — 维持冻结、暂不退役), not to the test-backend programme.No
--fix/--updateflag, for the reasonengine-double-contract.baseline.jsonstates for itself: a generator would let a new arrival be admitted by "just run the update command".Verification
Reverse verification, direction predicted before running — red, twice, in the two places that matter:
RULED(the autonumber file mentioned neither driver-memory 测试面替代:项目内测试后端迁到 sqlite:memory:(#5499 重启条件 · memory 半边,维护者 2026-08-06 立项) #5704 nor driver-memory census inundeclared-field-write-driver-split.integration.test.tsis stale — a secondpackages/runtimetest consumer (#6468) is outside #5704's "in this one place" ruling #6664) and 2CENSUS(neither ruled file stated the count). Nothing else — the ledger and the scan reconciled from the first run.LEDGERED: packages/runtime/src/zz-probe-arrival.test.ts:1 binds @objectstack/driver-memory (import) and the ledger does not cover it, exit 1. Green again once removed.The self-test drives both sides of every decision the scanner and the reconciler make, including the direction that matters most here — growing the ruled set to 3 must invalidate every sentence still carrying "2".
Everything else run locally, gates enumerated from
.github/workflows/lint.ymlone by one rather than from memory:pnpm lintclean, and all 32check:*steps pass individually (including the new one).check:type-check-coverage,check:driver-conformance,check:stall-guard,check:skill-frame-sync,check:skill-compatibility— all pass.check:generated --reconcile-onlypasses unchanged: a rootcheck:script needs no entry inpackages/spec's ledger, which reads that package's ownpackage.json.pnpm --filter @objectstack/spec build && check:generatedreports all 10 generated artifacts up to date — the comment edits cause no artifact churn.packages/runtime:pnpm typecheckclean; full suite114 passed (114)files /1696 passed (1696)tests.check:nul-bytesclean, plus the wider self-scan the byte rule asks for when a change touches control-character territory. It earned its keep: a raw NUL had materialized in four template literals of the new script (${e.file}NUL${e.kind}), invisible to the gate because the file was still untracked at that point. Replaced with real spaces; re-scanned clean.No changeset
This releases nothing: two test-file comment blocks, an agent-tooling gate script, its ledger, a root
package.jsonscript entry and a workflow step. No package's published behaviour changes, so.changeset/gets nothing and the PR carriesskip-changeset.Generated by Claude Code