test(driver-sql): name the dialects a local run did not exercise, under the counts - #18649
Conversation
… counts A local run with no servers ends on `178 passed | 11 skipped` and a round reads it as coverage. It is not: every live-Postgres and live-MySQL cell is inside the skipped counts, and 56 further files report PASSED with a live cell skipped inside them. Adds a declaration-only reporter, registered after `default` so it lands under the summary. It names which dialects did not run, the env var that would run each, and a measured in-container PostgreSQL recipe. It changes no behaviour, adds no gate and cannot fail a run. Claude-Session: https://claude.ai/code/session_01CqmCgU5RGDoJYhHUMVp2af Co-authored-by: Claude <noreply@anthropic.com>
…s missing Claude-Session: https://claude.ai/code/session_01CqmCgU5RGDoJYhHUMVp2af Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CqmCgU5RGDoJYhHUMVp2af Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check7 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. What this run could not see
Coarse fallback — 11 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 07ff3e0a4bb7a9732af44005bf5c8b4e7761b40e && git checkout 07ff3e0a4bb7a9732af44005bf5c8b4e7761b40e
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin cf39b83c090a5642c1ce260c6bc00972650b9ffa 7959bdc42fe54598460cb28823e77ce6dbf71cad && git checkout -B drift-repro cf39b83c090a5642c1ce260c6bc00972650b9ffa && git merge --no-ff 7959bdc42fe54598460cb28823e77ce6dbf71cad
node scripts/docs-audit/affected-docs.mjs --json cf39b83c090a5642c1ce260c6bc00972650b9ffa |
Fixes #18200
Clause-②: no
The trap, re-measured on this base (
32be735e5b)pnpm --filter @objectstack/driver-sql test, no servers present:The card's arithmetic was
11 of 188, taken 2026-09-14. Re-counted on this base: 11 of 189.The file total grew by one —
sql-driver-17231-multi-value-not-null.test.ts, landed this morningat
32be735e5b. The skipped-file count itself did not move.The 11 is the smaller half. On the same run, 168 tests are skipped across 67 of the 189
files: 11 that vitest reports as skipped, and 56 more that it reports as PASSED with a live
cell skipped inside them. A file with a green tick next to it is the least visible skip in the
output, and none of the three recorded instances of this card's failure mode had any way to see it.
The same suite with a live PostgreSQL attached (run in full, below) reports 3315 tests passed,
not 2627. The local green was blind to 688 tests — 21% of what the suite can run.
What this adds
packages/drivers/driver-sql/src/live-dialect-coverage.reporter.ts, registered invitest.config.tsafter'default'so it prints underneath the summary counts — the place around actually looks. It names, at the end of every run:
DIALECT_CELLS(this package's own source oftruth for the driver axis), never inferred from test names;
passed;
(shape 3 as a rider, 7 printed lines).
Declaration-only, as ruled. It reads a run that already happened and writes stdout. It adds no
gate, changes no test's mode, and every hook body is wrapped so a defect in the reporter cannot
redden a run.
Two mechanisms worth naming, both measured rather than assumed:
reporters:in the config is spelled conditionally onGITHUB_ACTIONS, because vitest appendsits
github-actionsreporter onlyif (!resolved.reporters.length)(vitest 4.1.11,dist/chunks/coverage.*.js). Naming any reporter would silently drop the annotations CI getstoday; re-adding it under vitest's own condition keeps CI output unchanged.
carries no
metaand no skip note (onlyctx.skip(note)sets one, and a skipped test neverreaches its body — probed against 4.1.11), so the only per-test channel left is the test's
name. Nine files here guard their live cells with a hand-rolled
skipIfwhose names thetestkit never wrote, so a name matcher would report a number smaller than the truth — which is
this card's own disease. The census is therefore reported as the raw skip count it is, and the
causal sentence is attached to the per-dialect lines, which are exact.
Proof: the two states, run end to end
A signal that fires identically with and without a backend is decoration, so both were run in full.
State A — no servers.
pnpm --filter @objectstack/driver-sql test, exit 0:followed by the recipe block and the three-way-zone-skew note.
State B — a live PostgreSQL 16.13, provisioned by the printed recipe.
initdb+pg_ctlonport 54988, server
timezone=Asia/Shanghaiagainst processTZ=America/New_York, exit 0:Here the PostgreSQL recipe is suppressed and the MySQL sentence takes its place: provisioning
MySQL means installing a server into the environment, which is not a step to print as a casual
next line.
State C —
OS_EXPECT_LIVE_DIALECT_MATRIX=1with no URLs, the shape CI runs. The testkitalready turns each missing cell into a named failure, so the block does not compete with the red
it would be shouting over:
The exit code in State C is the pre-existing testkit failure, not the reporter's: States A and B
exit 0 with the block printed, and the summary counts in State A are byte-identical to the same
command run at
32be735e5bbefore this change.The recipe was run before it was written down. Teardown was
pg_ctl -m fast stopplus removal ofthe data directory, verified by observing the port refuse connections rather than by reading an
exit code.
Verification
pnpm --filter @objectstack/driver-sql typecheck— exit 0.pnpm --filter @objectstack/driver-sql test— exit 0, twice (States A and B above).pnpm --filter '@objectstack/driver-sql^...' build— exit 0 (dependency closure).node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, run with exitcodes captured to disk before any pipe, then reconciled with
--ranon the final head7959bdc42f: 45 derived, 43 run, 2 NOT MEASURED, 0 UNRUN.check:dual-build-cjs-loadsandcheck:lean-entry-closure, bothexit 3
PREREQUISITE NOT MET— they read built output for the whole workspace, which is CI'srun, and neither reads anything this diff touches.
check:cross-package-test-inputsexits 1 onpackages/cli/test/init-created-files-summary.e2e.test.tsdescending
packages/spec/dist/. That is the known gate defect [finding]check:cross-package-test-inputsexits 0 or 1 on the same commit depending on whether adist/exists — and lint.yml runs it with no build ahead of it, so CI only ever sees the blind leg #18621, which fires wheneveranything creates
packages/spec/dist; this diff touches neitherpackages/clinorpackages/spec. Named, not chased, and not this PR's red.eslint . --no-inline-config --format json— 6814 files linted,0 findings, exit 0, with both changed files present in that population at 0 errors / 0
warnings. (No narrowing claim is needed, but for the record the config never enables type-aware
linting for any file, so no diff here can move an untouched file's verdict.)
check:nul-bytes: clean.Changeset: none, deliberately
Nothing published moves.
@objectstack/driver-sqlshipsfiles: ["dist", "README.md", "CHANGELOG.md"]and tsup's entry issrc/index.tsalone; the reporter is imported by neither.Measured after a real build rather than reasoned:
LiveDialectCoverageReporterhas zero hitsacross
dist/,README.mdandCHANGELOG.md, against a positive control(
UnsupportedDialectEmissionError, from a file that is in the published closure) that hitsdist/index.js,dist/index.mjsanddist/index.d.mts.vitest.config.tsis not publishedeither. Hence
skip-changeset.Acceptance notes
refuses to report green when live cells were skipped" to the maintainer's floor and asked for a
report if the work made a strong case. It does, and the new number is the argument: the card's
11 files were never the population — 67 files are, and 56 of them report PASSED. A reader
who learns to discount the "11 skipped" column still has no signal for the other 56, so a louder
declaration is doing more work here than the card assumed, and correspondingly more is still
riding on the reader choosing to look. Recorded for the seat to file to the decision box;
nothing conditional on it is built here, not even behind a flag.
OS_TEST_POSTGRES_URL/OS_TEST_MYSQL_URLappear in the testkit, the globalSetup andci.yml, and now in this block — butpackages/drivers/driver-sql/README.mddoes not mentioneither. Noted, not filed: the block now prints them at the end of every unprovisioned run, which
is a strictly better discovery path than a README section, so no card is warranted. Successor if
one is ever wanted: whoever next edits that README.
sql-driver-autonumber-cold-race.test.tsnames one of its suitesattemptWithoutPoisoning (skipped)by interpolating the cell's availability into the describetitle, so with no server the word "skipped" is part of the suite name rather than a state. It
reads as a quarantined test in the output and is not one. Noted, not filed: cosmetic, single
site, and the new block makes the real cause legible. Successor: the next PR to touch that file.
Generated by Claude Code