Uh oh!
There was an error while loading. Please reload this page.
test(driver-sql): empty the dialect ledger, and promote the matrix-routing measurement to an invariant - #12302
Merged
os-warren merged 2 commits intoAug 25, 2026
Conversation
…atrix, and promote MATRIXED
Resolves the two seeded DIALECT_LEDGER rows and turns the census measurement
"every dialect-scored cell has at least one matrix-routed suite" into an
enforced invariant.
- sql-driver-comparand-type-conformance.test.ts now iterates DIALECT_CELLS.
Its case-set claims the six accepted comparand types "compile everywhere";
that sentence had only ever been measured on SQLite. Measured on live
PostgreSQL 16.13: all eight executed cases answer the case-set's row ids.
The door-refusal half stays declared once — parseFilterAST is upstream of
every driver and has no dialect in it.
- sql-driver-icontains-and-retired-operators.test.ts declares
dialectCell('sqlite'). Measured BY OPERATOR before marking: the seven text
operators it reaches are exactly the seven FILTER_TEXT_CASES reaches, and
sql-driver-text-case-conformance.test.ts runs that table once per cell.
- check-driver-conformance.mjs gains MATRIXED, the per-cell invariant, and
DIALECT_LEDGER is now empty. A ledger row deliberately does NOT clear
MATRIXED: it excuses an undeclared FILE, not a CELL with no matrix-routed
suite anywhere.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6HFzyH98W1YaQXhJUJt6o…the bind probe `parseFilterAST` is typed `FilterCondition | undefined`, which `tsc --noEmit` caught and the vitest run could not. Asserted rather than `!`-ed: a case that produced no condition would compile a bare select, and the binding assertions would then be measuring an empty statement rather than a bound comparand. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W6HFzyH98W1YaQXhJUJt6o
Contributor
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
os-warren
marked this pull request as ready for review
August 25, 2026 17:31
os-warren
enabled auto-merge
August 25, 2026 17:31
Uh oh!
There was an error while loading. Please reload this page.
os-warren
deleted the
claude/issue-12136-dialect-ledger-resolve-and-promote
branch
August 25, 2026 17:56
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 freeto 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#12136
The dialect axis #12134 added could see the third axis of ADR-0053 D-A3 but could not enforce it: it printed
8 of 9 dialect-scored cell(s) have a matrix-routed suiteand exited 0. This resolves the two seededDIALECT_LEDGERrows and promotes that sentence to an enforced invariant, in the order the card ruled.1.
FILTER_COMPARAND_TYPEnow runs the matrix — and "compile everywhere" is now measuredFILTER_COMPARAND_TYPE_CASESwas the only dialect-scored cell with no matrix-routed suite, while its ownCASE_SETSentry says the six accepted types "compile everywhere". That sentence had only ever been measured on SQLite.Measured on live PostgreSQL 16.13 (initdb'd for this change; the same version #11456's
42883divergence was found on):The delta is exactly the nine live-Postgres tests — the fixture control plus all eight executed cases. All eight answered the case-set's row ids, so on Postgres the claim now holds as a measurement rather than as prose. Two non-vacuity proofs, because a live cell that quietly did not run would read identically:
pg_stat_database.xact_commit = 326on the live database after the run — real traffic, not a skipped cell.OS_EXPECT_LIVE_DIALECT_MATRIX=1the run goes red on the MySQL cell by name (OS_TEST_MYSQL_URL is unset while OS_EXPECT_LIVE_DIALECT_MATRIX=1) while the other 52 pass — so the provisioned cell and the un-provisioned one are each what they claim.mysqldexits 1 with zero output in this container and no Docker daemon is reachable, so it is a declared skip — the same disclosuresql-driver-text-case-conformance.test.tsmakes about its own MySQL cell. It is covered instead by the new bind block below, and named in the suite's head note rather than left to be inferred.What is per-dialect, and what deliberately is not. The case-set has two directions and only one has a dialect.
matches/compilescases reach the driver, so they run once per cell.door-refusalcases assertparseFilterASTthrows before any driver runs — a pure platform function with no dialect in it. Running those once per cell would not measure three things, it would measure one thing three times and report the repetition as coverage, so they are declared once in a block that says so in its name.A final block asserts every accepted type binds into a statement on all three dialect clients. That is a real check rather than a restatement: this driver refuses an unbindable comparand from inside
applyFilters, which throws while the statement is being built, so a type it could not bind onpgormysql2fails there with no server involved. It is the only layer that can speak for the un-provisioned cell.2.
sql-driver-icontains-and-retired-operators.test.tsdeclaresdialectCell('sqlite')The card required this be verified by operator, not by file, since #6518 makes
FILTER_TEXT_CASESanswer differently per dialect and a wrong call would hide a real gap. Measured off the exported case data, not the file's prose:$contains$endsWith$icontains$notContains$options$regex$startsWith— plus$orFILTER_TEXT_CASES(walked from the casefilters)$contains$endsWith$icontains$notContains$options$regex$startsWithExactly the same seven. Reading the spec file instead would have overstated it — its prose also mentions
$ilikeand$not, which no case exercises; that is precisely why this was measured off the data.sql-driver-text-case-conformance.test.tsruns that whole table once per cell ofDIALECT_CELLS, so all seven are already answered on all three dialects. The extra$oris a combinator belonging toFILTER_LOGIC_CASES, matrix-routed bysql-driver-or-filter.test.ts.The coverage overlaps, so the marking hides nothing. What is left over is this file's own residue — the
GLOBmetacharacter class, thelower(name) GLOB lower(…)construct, and the assertion that$containsand$icontainsstop answering identically.GLOBis emitted on the SQLite dialects and nowhere else, so those blocks are about SQLite by construction: SQLite is the right cell, not merely a tolerable one. Option B ("convert both") remains rejected on the same measurement — converting this file adds no dialect coverage.3. MATRIXED — the measurement, promoted
DIALECT_LEDGERis now empty, andcheck-driver-conformance.mjsgains a fifth invariant:check:driver-conformance, before and after, and why the number moved. The covered-cell count is45on both trees and that is the point — no cell'sCONSUMEDanswer moves, because nothing about coverage was wrong. What moves is the dialect line:8 of 9 → 9 of 9,2 in the DIALECT ledger → 0,5 run the matrix → 6,0 declare named cell(s) → 1. Every one of those is this PR's own doing, and none of it is a pre-existing green.DIALECTED and MATRIXED are two different questions, and both are needed. DIALECTED is per FILE and asks for a stated stance. A tree where every suite honestly declares
dialectCell('sqlite')satisfies it completely while enforcingPostgres at minimumnowhere. MATRIXED is per CELL and asks that the stance somewhere on that cell actually be the matrix.⛔ A dialect-ledger row deliberately does NOT clear MATRIXED. The ledger excuses an undeclared file; MATRIXED is about a cell with no matrix-routed suite anywhere, which no row can supply. So the ledger now reaches exactly one shape: a second suite over a cell some sibling already routes through the matrix. That is a real narrowing of the escape hatch, it is stated in the ledger comment and in the error message itself, and it is pinned by a self-test — so nobody adds a row, watches it stay red, and concludes the gate is broken.
Ablation — the promoted invariant must be able to fail
Direction predicted in writing before the run, implementation committed first at
4fc5c3e792so the restore had somewhere to go (#12204). Mutation: the comparand suite stops routing through the matrix and declares a named cell instead — the exact shape the promotion exists for, since it keeps DIALECTED satisfied. Proven on disk by anchored single-line grep counts before any result was read (never a multi-line-cF, which counts lines): removed text0, injected text1, on both edited lines.9 of 9· 0 MATRIXEDFILTER_COMPARAND_TYPE_CASES· 0 DIALECTEDerrors.pushdeleted8 of 9--self-test, same deletionThe second row is the claim: DIALECTED stays silent — the suite declares honestly — and only MATRIXED catches it. The third row is what the promotion bought, and it is #12014's defect reproduced with a real mutation: the pre-promotion gate prints the shortfall and passes.
Restore was verified, not trusted:
trap … EXIT INT TERM,git checkout HEAD --with absolute paths (never--alone, which restores from the index the mutation wrote).git hash-objectof both working files equals the blob idgit rev-parsereports for the same path at HEAD, andgit status --porcelainis empty.Verification
Gate union derived, not recalled —
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(no paths; the script takes the changeset itself). All 21 families re-run ateee427ed4c, the final commit. Every exit code captured before any pipe (cmd > log 2>&1; ex=$?); each verdict below is the gate's own line, never$?.check:driver-conformanceOK — 45 covered cell(s), 0 in the DEBT ledger, 0 exempt.+ the dialect line abovecheck:driver-conformance --self-testOK self-test: …incl. the new MATRIXED directionscheck:agent-test-spelling0 violations — 380 file(s) · 3875 bare -- token(s) …check:cli-command-ids275 command-id literal(s) across 99 file(s) … all resolvecheck:cross-package-test-inputsOK: 16 package(s) read outside themselves, all declared …check:entry-guard164 scripts/ file(s) — every entry guard goes through invoked-as.mjs …check:parse-guard163 scripts/ file(s) — every TypeScript parse goes through ts-parse.mjs.check:pnpm-filter-targets136/173 --filter occurrence(s) across 28 file(s) resolve …check:published-files69 publishable package(s) of 78 … admits no test …check:slot-lookupratchet holds: 107 unswept site(s) in 25 file(s), none newcheck:test-source-aliasOK — 72 packages with tests scanned …check:type-source-resolutionOK — 93 tsc program(s) across 77 packages scannedcheck-ci-filter-parityOK: all 96 declared cross-package glob(s) (81 unique) are covered …check-cross-package-test-inputsOK: 16 package(s) read outside themselves, all declared …check-plugin-teardown-shape63 Plugin implementation(s) across 4713 source(s) …check-affected-docscheck:query-options-erasureratchet holds: 67 unswept non-test site(s) in 17 file(s), none newcheck:type-check-coverageOK — 65/78 workspace packages type-checked …check:engine-double-contract391 (file, verb) row(s) held by the RETAINED ledgercheck:where-matcher299 matcher(s) discovered, 299 answer … 0 silently-wrongcheck:nul-bytesOK (scanned 6802 text file(s) … no raw ASCII control bytes).The convention-triggered families the derivation named for "adds or edits a test file" are all in that list.
check:type-check-debtis the one it named that is not run here: it needs the whole workspace closure built, and its own refusal on an unbuilt worktree means NOT MEASURED rather than not-applicable — declared rather than quietly omitted, and it runs in CI.Package-level, under the shared verify lock:
pnpm --filter @objectstack/driver-sql typecheck→tsc --noEmitclean, and the full suite135 passed | 8 skipped (143) · 2061 passed | 125 skipped (2186). The typecheck is load-bearing here — it caught aFilterCondition | undefinedthe green vitest run could not see, which is the second commit.Changeset
None,
skip-changesetlabel. Two test files plus one repo gate script; nothing publishes. Measured rather than assumed:driver-sql'sfileswhitelist is["dist","README.md","CHANGELOG.md"], so neither test file ships, andscripts/is not a package.Graded against
check-empty-changeset's own ledger (--self-test:118 assertions over real temp git repos), which is one-directional on exactly this case: an empty changeset satisfies the same gate the label does, produces no CHANGELOG entry (measured), and is uniquely a real input tochangesets/action— thehasChangesets && !hasNonEmptyChangesetsbranch that silently stalled 17.0.0-rc.2 in #4898. It buys nothing the label does not and uniquely carries the risk.The label was applied through the whole-set fallback, declared rather than glossed:
POST /issues/12302/labels(the additive endpoint) returned HTTP 403 — "GitHub access is not enabled for this session", the known per-session outage (#12123), which also took the PR-creation call. So the current set was read (size/l), the union computed, and both written together. Read back after the write; see the report comment on #12136.Scope
Held to the declared surface — exactly the three files the card named, confirmed against
origin/main:PR #12287 (#11567) was re-checked at claim time: it touches
sql-driver.ts,sql-driver-schema.test.ts,sql-driver-string-maxlength-varchar.test.tsand others — no overlap with these three. Nothing was re-located by a line number quoted in an older card; every anchor here was found by symbol.Clause ② — no. Test coverage plus a gate invariant: no shipped behaviour, no public surface, no accept/reject door moves. It does change what two suites assert, which is what #12014 deliberately would not, and is why this was a separate card.
Generated by Claude Code