diff --git a/.changeset/20595-driver-sql-provenance-anchors.md b/.changeset/20595-driver-sql-provenance-anchors.md new file mode 100644 index 00000000000..4dc2efb4013 --- /dev/null +++ b/.changeset/20595-driver-sql-provenance-anchors.md @@ -0,0 +1,15 @@ +--- +'@objectstack/driver-sql': patch +--- + +Provenance comments in `@objectstack/driver-sql` cite the commits and ADR that decided them, not tracker numbers that no longer resolve + +Clause-②: no + +Docblocks and comments across the package cited issue-tracker numbers that now answer 404 on GitHub. +Each one now cites the commit in this repository's history that made the decision it describes, or the +ADR that records it (ADR-0104's 2026-09-05 addendum). Some of these docblocks sit on exported members, +so the reworded text appears in the published `index.d.ts` / `index.d.mts`, and comments that esbuild +keeps appear in the JavaScript output. + +Comment only: no export, type, error code, status, message text or runtime behaviour changes. diff --git a/packages/drivers/driver-sql/src/builtin-column-collision.ts b/packages/drivers/driver-sql/src/builtin-column-collision.ts index 288f3157502..86ebb324c1b 100644 --- a/packages/drivers/driver-sql/src/builtin-column-collision.ts +++ b/packages/drivers/driver-sql/src/builtin-column-collision.ts @@ -72,7 +72,7 @@ export type FieldKeyClass = 'storage' | 'presentation'; export const FIELD_KEY_STORAGE_CLASS: Readonly> = Object.freeze({ // ---- storage: the column's own shape ------------------------------------- type: 'storage', // `createColumn`: the column type itself - maxLength: 'storage', // `createColumn`: varchar(n) vs TEXT, and the #11374 keyable decision + maxLength: 'storage', // `createColumn`: varchar(n) vs TEXT, and commit d0e3a885b's keyable decision multiple: 'storage', // `createColumn`: a multi-value field is a JSON column precision: 'storage', // numeric column shape (this driver does not read it yet) scale: 'storage', // numeric column shape (this driver does not read it yet) diff --git a/packages/drivers/driver-sql/src/dialect-emission-refusal.ts b/packages/drivers/driver-sql/src/dialect-emission-refusal.ts index 00278ef8bf7..b87422bb1a1 100644 --- a/packages/drivers/driver-sql/src/dialect-emission-refusal.ts +++ b/packages/drivers/driver-sql/src/dialect-emission-refusal.ts @@ -63,7 +63,7 @@ * `engine.syncObjectSchema` → `SqlDriver.syncSchema` → the DDL gate, on a * server that is already serving HTTP. That was the exact test #8035 applied * when it UNregistered `MONGODB_MULTI_TENANT_UNSUPPORTED` for failing it — a - * removal #16649 reversed under the #16404 door-or-no-door rule, which takes + * removal that commit 613bfbd3d reversed under the #16404 door-or-no-door rule, which takes * registration out of that test's reach entirely: every `code` that ships in * `dist` carries a ledger row, and wire-reachability now decides only what a * door ANSWERS with. This one can be carried, so the door serves it under its diff --git a/packages/drivers/driver-sql/src/live-dialect-matrix.testkit.ts b/packages/drivers/driver-sql/src/live-dialect-matrix.testkit.ts index 612ba447ad3..33691dd1eb0 100644 --- a/packages/drivers/driver-sql/src/live-dialect-matrix.testkit.ts +++ b/packages/drivers/driver-sql/src/live-dialect-matrix.testkit.ts @@ -448,7 +448,7 @@ export function declareUnprovisionedCell(cell: DialectCell, matrix: string): voi * Nothing in the corridor (15_000, 600_000) is distinguishable by measurement, * so the value is fixed by this package's OWN existing answer for live-touching * sites: 60 explicit `60_000` budgets across 22 files — #13688 and its sweep - * #13902 put them on live test BODIES, #14213 and #14628 on the hooks that pay + * #13902 put them on live test BODIES, #14213 and commit 6392b9c2b on the hooks that pay * a live connect. Adopting it leaves the live matrix with ONE live budget * instead of two, so a red at 60_000 ms is unambiguous about which bound it hit. * diff --git a/packages/drivers/driver-sql/src/media-column-move.pin.test.ts b/packages/drivers/driver-sql/src/media-column-move.pin.test.ts index bc5c696187c..bf5f6a51429 100644 --- a/packages/drivers/driver-sql/src/media-column-move.pin.test.ts +++ b/packages/drivers/driver-sql/src/media-column-move.pin.test.ts @@ -11,7 +11,7 @@ * refusing. * * ⛔ The single most load-bearing assertion here is that the PostgreSQL retype - * arm's pre-check exists at all. The #15041 addendum prescribed the retype with + * arm's pre-check exists at all. The ADR-0104 2026-09-05 addendum prescribed the retype with * NO pre-check, and that form was measured on live PostgreSQL 16.13 to accept a * row holding an inline metadata blob and flatten it to its own literal text. * The director ruling (decision batch #120 item 1) replaced the clause; a pin diff --git a/packages/drivers/driver-sql/src/media-column-move.ts b/packages/drivers/driver-sql/src/media-column-move.ts index b63b7d804e1..fe0ce2c4d5b 100644 --- a/packages/drivers/driver-sql/src/media-column-move.ts +++ b/packages/drivers/driver-sql/src/media-column-move.ts @@ -6,7 +6,7 @@ * `sys_file` id, and the pre-check that ABORTS instead of destroying a row the * backfill never converted. * - * The ruling on #15041 gave this step one requirement in words — abort *"on + * The ruling in ADR-0104's 2026-09-05 addendum gave this step one requirement in words — abort *"on * the first cell that is not a JSON string"* — and one sketch in SQL beside * it. **The sketch does not implement the requirement, and that was measured * rather than argued** (director ruling, decision batch #120 item 1): on live diff --git a/packages/drivers/driver-sql/src/schema-drift.base-type-mismatch.test.ts b/packages/drivers/driver-sql/src/schema-drift.base-type-mismatch.test.ts index 86dd18df4f5..dc8eb8acfe9 100644 --- a/packages/drivers/driver-sql/src/schema-drift.base-type-mismatch.test.ts +++ b/packages/drivers/driver-sql/src/schema-drift.base-type-mismatch.test.ts @@ -364,7 +364,7 @@ describe('diffManagedTable — a SINGLE-VALUE JSON-class field over a stale text // The card's scope, asserted rather than described: the fork applies to // every single-value member of the writer's set. // - // ⚠️ [#15989] #15041 has since been ruled — option A, the file family's + // ⚠️ [#15989] ADR-0104's 2026-09-05 addendum has since ruled — option A, the file family's // column holds the bare `sys_file` id — so the family is no longer a member // of {@link JSON_COLUMN_FIELD_TYPES}: it is asked per deployment, and // `diffTags` omits `fileColumnsMoved`, i.e. every call here is about a diff --git a/packages/drivers/driver-sql/src/schema-drift.ts b/packages/drivers/driver-sql/src/schema-drift.ts index 86bed97c965..72e45a83968 100644 --- a/packages/drivers/driver-sql/src/schema-drift.ts +++ b/packages/drivers/driver-sql/src/schema-drift.ts @@ -417,7 +417,7 @@ export const HASH_SHADOW_SUFFIX = '__hash'; * orphan pass reports as `unmapped_column` with a `drop_column` op. Dropping it * would take the UNIQUE index it carries with it, silently returning the object * to "registered but its declared uniqueness unenforced" — the very state - * #11374/#11627 exist to end, reached this time through the migration tool + * #11627 and commit d0e3a885b exist to end, reached this time through the migration tool * rather than through a refused DDL. * * Matched by SUFFIX rather than by a registry of known names, deliberately: the @@ -448,7 +448,7 @@ export function isHashShadowColumn(name: string): boolean { * 64-character identifier limit. * * ⚠️ Lives HERE, beside {@link isHashShadowColumn}, rather than in the driver: - * #13015 was the price of the split. The ORPHAN-column pass knew the shadow + * The defect commit cd1348802 fixed was the price of the split. The ORPHAN-column pass knew the shadow * vocabulary and the INDEX differ did not, so a healthy shadow-carried UNIQUE * had its column protected from a drop while the index that column carries was * proposed for a destructive rebuild. Both passes now ask the same module the @@ -473,7 +473,7 @@ export function hashShadowColumnFor(indexName: string): string { /** * One key part a hash shadow hashes: the column identity, and whether the * generation expression folds it through the NULL-safe `COALESCE(col, ...)` - * form (ADR-0120 D3, carried into the shadow by #12998). + * form (ADR-0120 D3, carried into the shadow by commit df1c75c4b). */ export interface HashShadowKeyPart { column: string; @@ -482,16 +482,16 @@ export interface HashShadowKeyPart { /** * Read the DECLARED key parts back out of a hash shadow's stored - * `GENERATION_EXPRESSION` (#13015). + * `GENERATION_EXPRESSION` (commit cd1348802). * * This is what makes a shadow-carried key COMPARABLE rather than merely - * skippable. Since #12998 the expression carries the NULL-safe parts in their + * skippable. Since commit df1c75c4b the expression carries the NULL-safe parts in their * COALESCE spelling, so the FORM of the key — which columns, and which of them * are folded — survives the round trip, and the differ can ask the real * question ("does this shadow enforce what metadata declares?") instead of the * blind one ("is this a shadow at all?"). * - * ⛔ Why the blind question is not good enough: a shadow created BEFORE #12998 + * ⛔ Why the blind question is not good enough: a shadow created BEFORE commit df1c75c4b * hashes the RAW columns, so `CONCAT` returns NULL for every NULL-organization * row and the rows the COALESCE bucket exists to constrain are constrained by * nothing (#5030's shape). It is indistinguishable BY NAME from a healthy one. @@ -858,7 +858,7 @@ export function diffManagedTable(args: { columns: PhysicalColumn[]; dialect: SqlDialectName; /** - * Which columns an index KEYS ON (#11374), keyed by field name — the exact + * Which columns an index KEYS ON (commit d0e3a885b), keyed by field name — the exact * map {@link indexedKeyColumns} builds. Consulted ONLY by the varchar-length * branch below, through {@link varcharColumnChars}, to answer the same * question `createColumn` asks before it sizes a text-family column. @@ -1703,7 +1703,7 @@ export interface PhysicalIndex { /** * When this index is physically carried by a #11627 hash shadow, the * DECLARED key parts that shadow hashes, read back from the generation - * expression (#13015 via #12998) by `SqlDriver.introspectIndexes`. + * expression (commit cd1348802 via commit df1c75c4b) by `SqlDriver.introspectIndexes`. * * Absent both when the index is NOT shadow-carried and when it is but the * expression could not be read. {@link isHashShadowCarrier} tells those two @@ -2029,7 +2029,7 @@ export function diffUnbuildableIndexes(args: { * field-level `unique` through {@link uniqueIndexesFromFields}, object-level * `indexes[]` through {@link normalizeDeclaredIndex} — so "which columns end up * in a key" has ONE answer, shared by the index sync that creates them and by - * the DDL that has to make them keyable in the first place (#11374). + * the DDL that has to make them keyable in the first place (commit d0e3a885b). * * ⚠️ Deliberately NOT filtered by `physicalColumns`, unlike `expectedIndexes`: * its caller runs BEFORE the columns exist — deciding a column's TYPE is the @@ -2275,7 +2275,7 @@ function indexSignature( * Answerable from the index alone, by NAME: the shadow is derived from the * index name ({@link hashShadowColumnFor}), so a carrier is an index whose sole * key column is its own shadow. That is what makes this the FAIL-SAFE half of - * #13015 — it holds even when the generation expression cannot be read, and a + * commit cd1348802 — it holds even when the generation expression cannot be read, and a * carrier is never a thing this differ may propose destroying on a guess. */ export function isHashShadowCarrier(index: PhysicalIndex): boolean { @@ -2283,7 +2283,7 @@ export function isHashShadowCarrier(index: PhysicalIndex): boolean { } /** - * The key an index ENFORCES, which is not always the key it STORES (#13015). + * The key an index ENFORCES, which is not always the key it STORES (commit cd1348802). * * For an ordinary index the two are the same. For a #11627 shadow-carried * UNIQUE the stored key is one VARBINARY(32) generated column and the enforced @@ -2358,7 +2358,7 @@ export function diffManagedIndexes(args: { if (!p || p.primary || isRuntimeManagedIndex(p, runtimeCreated, tenantField)) return false; if (!p.unique || p.partial === true) return false; if ((p.expressions?.length ?? 0) > 0 || (p.nullSafeColumns?.length ?? 0) > 0) return false; - // #13015: nor is a hash-shadow carrier. Its stored key is one generated + // Commit cd1348802: nor is a hash-shadow carrier. Its stored key is one generated // column, so the identity comparison below already excludes it — stated // outright because the exclusion must survive that comparison changing, // and because `replace_unique_index` DROPS the legacy name. @@ -2428,7 +2428,7 @@ export function diffManagedIndexes(args: { // Same normalization on BOTH sides (#4884, ADR-0120 D3): column identity // AND key-part form, literal-agnostic on the COALESCE literal — asked of // the key the index ENFORCES, which for a #11627 shadow-carried UNIQUE is - // not the column it stores (#13015). + // not the column it stores (commit cd1348802). const pk = enforcedIndexKey(p); if ( p.unique === e.unique && @@ -2445,7 +2445,7 @@ export function diffManagedIndexes(args: { // (`recreate_index` → drop first) this differ cannot undo. Not ours to // reconcile (#4884). if (isRuntimeManagedIndex(p, runtimeCreated, tenantField)) continue; - // #13015, fail-safe half: a hash-shadow carrier whose generation + // Commit cd1348802, fail-safe half: a hash-shadow carrier whose generation // expression could NOT be read (`shadowKey` unresolved). We know by name // that the index is driver-owned and that its stored key is a digest, so // the identity comparison above is meaningless for it — but we do not know @@ -2455,7 +2455,7 @@ export function diffManagedIndexes(args: { // ⛔ The `!p.shadowKey` half is load-bearing, and was measured: without it // this guard swallows the RESOLVED carriers too, which silently demotes the // whole fix to the blind skip — every shadow-carried index unreportable, - // including a pre-#12998 one hashing the RAW columns whose constraint does + // including one from before commit df1c75c4b hashing the RAW columns whose constraint does // not cover NULL-organization rows at all. Green, quiet, and the exact // trade this fix exists to refuse. if (isHashShadowCarrier(p) && !p.shadowKey) continue; @@ -2471,7 +2471,7 @@ export function diffManagedIndexes(args: { // clean → recategorised `safe` (dev autoMigrate may apply); duplicates → // blocked with a row report, the old index left in place. // - // #13015: read through the ENFORCED key, so a pre-#12998 shadow — same + // Commit cd1348802: read through the ENFORCED key, so a shadow from before commit df1c75c4b — same // columns, hashed RAW instead of through the NULL-safe COALESCE — is // recognised as exactly this tightening and gets the same duplicate // pre-flight before anything is dropped. The explicit "physical side is @@ -2530,7 +2530,7 @@ export function diffManagedIndexes(args: { // (#4884 — the boot advised dropping `idx_sys_metadata_overlay_draft`, the // partial UNIQUE enforcing draft-overlay uniqueness, on a healthy fresh DB). if (isRuntimeManagedIndex(p, runtimeCreated, tenantField)) continue; - // #13015: an orphaned shadow carrier is still an orphan — its declaration + // Commit cd1348802: an orphaned shadow carrier is still an orphan — its declaration // is gone, and `drop_index` is the right remedy — but the report must name // the constraint it enforced, not the digest column it stored. const po = enforcedIndexKey(p); diff --git a/packages/drivers/driver-sql/src/sql-driver-11176-bulk-and-merge-updated-at.test.ts b/packages/drivers/driver-sql/src/sql-driver-11176-bulk-and-merge-updated-at.test.ts index 098aef7f709..88c2a09085e 100644 --- a/packages/drivers/driver-sql/src/sql-driver-11176-bulk-and-merge-updated-at.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-11176-bulk-and-merge-updated-at.test.ts @@ -4,9 +4,9 @@ * [#11176] The two write doors that did not advance `updated_at`: `updateMany()` * on every dialect, and `upsert()`'s merge branch on Postgres and MySQL. * - * ## Not #11067, and the difference is what this file is set up to show + * ## Not the defect commit 479fba50d fixed, and the difference is what this file is set up to show * - * #11067 is about `tablesWithTimestamps` being filled only by DDL, so a + * Commit 479fba50d is about `tablesWithTimestamps` being filled only by DDL, so a * `skipSchemaSync` deployment never stamped. These two are missing on EVERY * deployment — so almost every table here is built by the driver's own * `initObjects`, with `tablesWithTimestamps` correctly populated. That is the @@ -57,7 +57,7 @@ * ## §6 The narrowing, stated as a measurement rather than a claim * * The upsert stamp reads `observedUpdatedAtColumn` — DDL-observed, or settled - * `present` by a successful stamped UPDATE — and deliberately NOT #11067's + * `present` by a successful stamped UPDATE — and deliberately NOT commit 479fba50d's * `presumed` state. `presumed` exists so an UPDATE can speculate and then * RECOVER (`updateWithPresumedTimestamp`); the upsert door has no such recovery, * and a wrong presumption there would name a missing column in an INSERT column @@ -66,7 +66,7 @@ * would break first if the narrowing were ever widened without a recovery. * * `updateMany` has no such narrowing: it is an UPDATE door, so it reuses - * #11067's machinery whole (§7). + * commit 479fba50d's machinery whole (§7). * * ## Reverse verification (direction predicted before running) * @@ -87,7 +87,7 @@ const OPTS = { bypassTenantAudit: true } as any; /** * The instant a row is backdated to before the write under test. * - * A sentinel far in the past rather than a sleep, for #11067's reason: a stamp + * A sentinel far in the past rather than a sleep, for commit 479fba50d's reason: a stamp * taken a moment after an insert default can legitimately land on the same * stored value. Backdating removes the race without weakening the assertion — * the stamp either moved to ~now or did not move at all, and those are six @@ -338,7 +338,7 @@ function measure(cell: DialectCell): void { // ── §6 The declared narrowing, measured at the property that would break ── it('§6 still upserts a hand-migrated table that has NO `updated_at` column', async () => { - // The upsert stamp reads the OBSERVED answer, never #11067's presumption, + // The upsert stamp reads the OBSERVED answer, never commit 479fba50d's presumption, // because this door has no recovery to fall back on. If that narrowing is // ever widened without one, this is the call that stops working. const id = 'n1'; @@ -349,7 +349,7 @@ function measure(cell: DialectCell): void { expect(after.row.title).toBe('b'); }); - // ── §7 `updateMany` reuses #11067's machinery whole ────────────────────── + // ── §7 `updateMany` reuses commit 479fba50d's machinery whole ────────────────────── it('§7 stamps a `skipSchemaSync` table, and still updates one without the column', async () => { // The presumption and its recovery, exercised through the bulk door: it is @@ -363,7 +363,7 @@ function measure(cell: DialectCell): void { expect(presumed.updatedAt).toBeGreaterThan(BACKDATED_MS); expect(presumed.row.title).toBe('b'); - // The other half of #11067's pair: a table that genuinely lacks the column + // The other half of commit 479fba50d's pair: a table that genuinely lacks the column // must NOT gain a new rejection. await driver.create(NO_COL, { id: 'n2', title: 'a', status: 'bulk' }, OPTS); const touched = await driver.updateMany(NO_COL, { where: { status: 'bulk' } }, { title: 'b' }, OPTS); diff --git a/packages/drivers/driver-sql/src/sql-driver-11224-update-stamp-precision.test.ts b/packages/drivers/driver-sql/src/sql-driver-11224-update-stamp-precision.test.ts index 97d7c78d29f..e0413bd8825 100644 --- a/packages/drivers/driver-sql/src/sql-driver-11224-update-stamp-precision.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-11224-update-stamp-precision.test.ts @@ -38,7 +38,7 @@ * audit answer comparing the two, a "modified since creation?" badge, and above * all a millisecond-precision delta cursor (`updated_at > cursor`), which * SKIPS every row whose stamp was truncated back below it — the same - * silent-wrong-answer family as #11067 / #11176 / #11223, reached by a fourth + * silent-wrong-answer family as #11176 / #11223 / the one commit 479fba50d fixed, reached by a fourth * mechanism. §2 asserts that skip is gone by issuing the cursor comparison as * real SQL on the server rather than comparing numbers in JS. * diff --git a/packages/drivers/driver-sql/src/sql-driver-11627-hash-shadow-key.test.ts b/packages/drivers/driver-sql/src/sql-driver-11627-hash-shadow-key.test.ts index d0f8b294462..3b10199f92c 100644 --- a/packages/drivers/driver-sql/src/sql-driver-11627-hash-shadow-key.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-11627-hash-shadow-key.test.ts @@ -15,7 +15,7 @@ * * ## Why a shadow and not a prefix index * - * The maintainer's 2026-08-24 ruling on #11374 chose the hash route and + * The maintainer's 2026-08-24 ruling, landed as commit 107bb4ba4, chose the hash route and * rejected prefix-unique indexes, on measurement: `UNIQUE KEY (token(191))` * enforces uniqueness over the PREFIX, so two genuinely distinct tokens that * share their first 191 characters collide and the second is refused as diff --git a/packages/drivers/driver-sql/src/sql-driver-11635-boolean-aggregand-answers.test.ts b/packages/drivers/driver-sql/src/sql-driver-11635-boolean-aggregand-answers.test.ts index 2cd07a7e305..54f15d5d94b 100644 --- a/packages/drivers/driver-sql/src/sql-driver-11635-boolean-aggregand-answers.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-11635-boolean-aggregand-answers.test.ts @@ -6,7 +6,7 @@ * * ## The ruling this suite pins * - * #11152 (maintainer 2026-08-28, applied in its comment 5448627494, ruling + * #11152 (maintainer 2026-08-28, landed as commit f6fa22ce1, ruling * verbatim and untranslated: 「12745 A回,其他同意。」 — option A on that * card) adopted, superseding #11249's `false`/`true` for the order * statistics: @@ -16,7 +16,7 @@ * — the same numeric domain `sum` / `avg` answer in, so one column's five * aggregates answer in one domain rather than three-numbers-two-booleans. * - **`sum` / `avg` answer arithmetic** (`3` / `0.5` on the 3-true/3-false - * fixture) — the settled #11065 family shape, unchanged. + * fixture) — the settled commit 20950404c family shape, unchanged. * * ## The two measured gaps this suite exists to keep closed * diff --git a/packages/drivers/driver-sql/src/sql-driver-11782-boolean-row-read-presentation.test.ts b/packages/drivers/driver-sql/src/sql-driver-11782-boolean-row-read-presentation.test.ts index 9dd2f87cddb..389171a36f4 100644 --- a/packages/drivers/driver-sql/src/sql-driver-11782-boolean-row-read-presentation.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-11782-boolean-row-read-presentation.test.ts @@ -33,8 +33,8 @@ * * ## [#11152] The `min`/`max` CELLS are superseded — the row-read doors are NOT * - * The maintainer's 2026-08-28 ruling on #11152 (applied in that card's comment - * 5448627494, verbatim 「12745 A回,其他同意。」, superseding #11249) pins that + * The maintainer's 2026-08-28 ruling on #11152 (landed as commit + * f6fa22ce1, verbatim 「12745 A回,其他同意。」, superseding #11249) pins that * **booleans aggregate as numbers on every face**: `min(flag)`/`max(flag)` * answer the JSON NUMBERS `0`/`1`, so the aggregate-result boolean * presentation this suite once asserted is deliberately removed again. ⚠️ diff --git a/packages/drivers/driver-sql/src/sql-driver-11794-richtext-text-family.test.ts b/packages/drivers/driver-sql/src/sql-driver-11794-richtext-text-family.test.ts index e1519cb2dce..1ffc7107357 100644 --- a/packages/drivers/driver-sql/src/sql-driver-11794-richtext-text-family.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-11794-richtext-text-family.test.ts @@ -39,7 +39,7 @@ * their declared bound and the invariant above licenses their TEXT column. * The former "STILL-OPEN half" cases below are the same measurements in their * CLOSED shape: the data-URI that was refused `22001` / `ER_DATA_TOO_LONG` at - * varchar(255) is accepted and round-trips byte-identically, and the #11374 + * varchar(255) is accepted and round-trips byte-identically, and commit d0e3a885b's * keyed-and-bounded rule applies to them the way it applies to every other * text-family member (keyed + bounded ⇒ varchar(maxLength), physically * enforced at exactly the declared bound; otherwise TEXT, bound enforced at @@ -205,7 +205,7 @@ describe('richtext joins the TEXT family (#11794) — physical shape on SQLite', // from a width. expect(mirror({ type: 'richtext' })).toBeNull(); expect(mirror({ type: 'code', maxLength: 64 })).toBeNull(); - // Keyed and bounded: varchar(maxLength) — the #11374 rule, so a declared + // Keyed and bounded: varchar(maxLength) — commit d0e3a885b's rule, so a declared // index on a bounded code field still keys on MySQL. expect(mirror({ type: 'code', maxLength: 64 }, { unique: true })).toBe(64); // Keyed and unbounded: still TEXT — MySQL then refuses the key BY NAME @@ -321,17 +321,17 @@ for (const liveCell of [PG_CELL, MYSQL_CELL]) { // seam (record-validator, pinned in objectql), keyed columns enforce // it physically too. Both directions measured, boundary included. const KT = `${T}_keyed`; - // Hoisted (not an inline literal) the way #11374's `boundedObject()` + // Hoisted (not an inline literal) the way commit d0e3a885b's `boundedObject()` // is, exactly as the platform objects declare it. // // ⚠️ The second half of what this comment used to say has EXPIRED and // is kept here as a dated record rather than deleted: it read // "`indexes` rides through `initObjects` beyond its narrow parameter // type", and that was true — the signature declared no `indexes` and - // the driver read the key through an `as any` anyway. #16570 declared - // it and #16711 closed the class, so the hoist is no longer LOAD-BEARING + // the driver read the key through an `as any` anyway. Commit b72226f48 declared + // it and commit 7862fb711 closed the class, so the hoist is no longer LOAD-BEARING // here; an inline literal would compile today. It stays because - // mirroring #11374's authoring shape is why it was written that way in + // mirroring commit d0e3a885b's authoring shape is why it was written that way in // the first place, and because this suite is about column widths, not // about parameter types. The pin that must stay inline is // `sql-driver-16711-object-def-param-keys.test.ts`. diff --git a/packages/drivers/driver-sql/src/sql-driver-12380-json-roundtrip.test.ts b/packages/drivers/driver-sql/src/sql-driver-12380-json-roundtrip.test.ts index 10b23661d5c..f6d3fac0794 100644 --- a/packages/drivers/driver-sql/src/sql-driver-12380-json-roundtrip.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-12380-json-roundtrip.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * [#12380] A `Field.json` column round-trips FAITHFULLY — what you wrote is what + * [commit 4045b954d] A `Field.json` column round-trips FAITHFULLY — what you wrote is what * you read back — on every dialect this driver speaks. * * ## What was measured, and why the contract decides it @@ -297,7 +297,7 @@ describe(`[#12380] driver-sql — Field.json round-trips faithfully (${cell.labe * * This block used to assert that the DDL was UNCHANGED — that a `Field.json` * column was still declared `json`, still carried NUMERIC affinity, and that - * #12380's encoded form survived it. #12738 changed the DDL: the SQLite family + * commit 4045b954d's encoded form survived it. #12738 changed the DDL: the SQLite family * now declares `text`, so NUMERIC affinity is gone from NEW columns and the * exposure is closed at the root rather than encoded around. * @@ -308,7 +308,7 @@ describe(`[#12380] driver-sql — Field.json round-trips faithfully (${cell.labe * so a bare number-like value written by raw SQL is no longer destroyed. * - **§B a LEGACY column** — declared `json` by hand, which is what every * database created before #12738 holds. NUMERIC affinity is still in force - * there and #12380's encoding still defeats it. This is the half the #12738 + * there and commit 4045b954d's encoding still defeats it. This is the half the #12738 * ruling requires to stay green: existing columns keep their declared type, * so the codec that protects them stays load-bearing forever. * @@ -354,7 +354,7 @@ describe('[#12738] SQLite affinity: gone from new columns, still defeated on leg async (raw) => { const bare = `bare_${raw}`; const enc = `enc_${raw}`; - // ONE statement, one column, two bindings: the pre-#12380 form and the + // ONE statement, one column, two bindings: the form from before commit 4045b954d and the // form `formatInput` produces. Bound through raw SQL so nothing but // SQLite's own affinity rule can be responsible for the outcome. await driver.execute( @@ -388,7 +388,7 @@ describe('[#12738] SQLite affinity: gone from new columns, still defeated on leg expect(rows[0].val).toBe(4200); }); - // ─── §B the legacy column — #12380 still in force, and still needed ─────── + // ─── §B the legacy column — commit 4045b954d's encoding still in force, and still needed ─────── it('[#12738] a LEGACY column is still declared `json` — the fixture is real', async () => { expect(await catalogType(driver, cell, LEGACY_T)).toBe('json'); @@ -406,7 +406,7 @@ describe('[#12738] SQLite affinity: gone from new columns, still defeated on leg const bareDisk = await diskCell(driver, cell, LEGACY_T, bare); const encDisk = await diskCell(driver, cell, LEGACY_T, enc); // Unchanged from the pre-#12738 assertion, on purpose: this is the exact - // measurement #12380 made, still true, now correctly scoped to the + // measurement commit 4045b954d made, still true, now correctly scoped to the // columns it is still true OF. expect(['integer', 'real'], `bare ${raw} must be eaten by NUMERIC affinity`).toContain(bareDisk.t); expect(encDisk.t, `encoded ${raw} must stay TEXT`).toBe('text'); diff --git a/packages/drivers/driver-sql/src/sql-driver-12998-shadow-null-safe-key.test.ts b/packages/drivers/driver-sql/src/sql-driver-12998-shadow-null-safe-key.test.ts index 8e322631add..e11c8c6e4b0 100644 --- a/packages/drivers/driver-sql/src/sql-driver-12998-shadow-null-safe-key.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-12998-shadow-null-safe-key.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * #12998 — the hash shadow must carry the DECLARED key: NULL-safe organization + * Commit df1c75c4b — the hash shadow must carry the DECLARED key: NULL-safe organization * key parts (ADR-0120 D3) ride into the generation expression. * * ## The defect @@ -236,7 +236,7 @@ declareDialectCell(MYSQL_CELL, 'hash-shadow NULL-safe key (#12998)', (cell) => { /** * The OVERFLOW TAIL — the half of that message no test in this repo could * see. The report is assembled by the module-local `formatDuplicateGroups` - * (#14902), shared with the drift entry and both plain-unique logs, and it + * (commit 61821e54c), shared with the drift entry and both plain-unique logs, and it * renders at most FIVE groups before counting the rest. Every duplicate * fixture in this package conflicts a single group, so the tail had never * been rendered by a test at all, and the `Conflicting group(s):` @@ -263,7 +263,7 @@ declareDialectCell(MYSQL_CELL, 'hash-shadow NULL-safe key (#12998)', (cell) => { const bare = orgUniqueOn('os12998_tail'); // Bound to a variable, like the fixture above: `initObjects`' parameter // type does not declare `indexes`, and an inline literal would be - // rejected by tsc for a key the driver reads regardless (#16570). + // rejected by tsc for a key the driver reads regardless (the gap commit b72226f48 closed). const withoutIndex = { ...bare, indexes: [] }; await driver.initObjects([withoutIndex]); const knex = (driver as any).knex; diff --git a/packages/drivers/driver-sql/src/sql-driver-12999-stale-text-remedy.test.ts b/packages/drivers/driver-sql/src/sql-driver-12999-stale-text-remedy.test.ts index 7280baf352c..5bea023671d 100644 --- a/packages/drivers/driver-sql/src/sql-driver-12999-stale-text-remedy.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-12999-stale-text-remedy.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * #12999 — the refusal message for an unkeyable TEXT column has TWO causes, and + * Commit ebcc34e89 — the refusal message for an unkeyable TEXT column has TWO causes, and * for one of them the advice it gave was a no-op. * * ## The defect @@ -9,7 +9,7 @@ * `explainUnkeyableTextColumn` rendered every `ER_BLOB_KEY_WITHOUT_LENGTH` / * `ER_TOO_LONG_KEY` index refusal as "the field declares no `maxLength` … * declare `maxLength` on the field(s)". True at CREATE time. False on the - * UPGRADE path, in both halves: once a release adds the bound (#12978 did + * UPGRADE path, in both halves: once a release adds the bound (commit e4902d2b9 did * exactly that for five `sys_notification_*` objects), the field DOES declare * one — but the additive sync never rewrites a column's type, so the physical * column stays TEXT, the index is refused again on every boot, and the message @@ -59,7 +59,7 @@ const beforeTheBound = () => ({ fields: { token: { type: 'text' } }, }); -/** The release that adds it — the #12978 shape, and the one that must not lie. */ +/** The release that adds it — commit e4902d2b9's shape, and the one that must not lie. */ const afterTheBound = () => ({ name: TABLE, fields: { token: { type: 'text', maxLength: 64 } }, diff --git a/packages/drivers/driver-sql/src/sql-driver-13015-shadow-carried-index-drift.test.ts b/packages/drivers/driver-sql/src/sql-driver-13015-shadow-carried-index-drift.test.ts index 5f2a26ef141..81d2b145c9f 100644 --- a/packages/drivers/driver-sql/src/sql-driver-13015-shadow-carried-index-drift.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-13015-shadow-carried-index-drift.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * #13015 — a healthy #11627 hash-shadow UNIQUE is not drift, and the remedy the + * Commit cd1348802 — a healthy #11627 hash-shadow UNIQUE is not drift, and the remedy the * differ used to propose would have DROPPED the constraint it was reconciling. * * ## The defect @@ -44,8 +44,8 @@ import { describe, it, expect, afterEach } from 'vitest'; import { SqlDriver, diffManagedIndexes } from '../src/index.js'; // The shadow vocabulary stays INTERNAL to this package — nothing outside it -// consumes a hash shadow, so #13015 deliberately did not widen the published -// surface. Imported from the module, exactly as the #11627/#12998 suites do. +// consumes a hash shadow, so commit cd1348802 deliberately did not widen the published +// surface. Imported from the module, exactly as the #11627 and commit df1c75c4b suites do. import { enforcedIndexKey, hashShadowColumnFor, @@ -86,13 +86,13 @@ const carrier = (shadowKey?: PhysicalIndex['shadowKey']): PhysicalIndex => ({ ...(shadowKey ? { shadowKey } : {}), }); -/** What a HEALTHY (post-#12998) shadow hashes: the declared NULL-safe key. */ +/** What a HEALTHY (since commit df1c75c4b) shadow hashes: the declared NULL-safe key. */ const healthyKey = [ { column: 'organization_id', nullSafe: true }, { column: 'v', nullSafe: false }, ]; -/** What a PRE-#12998 shadow hashes: the same columns, RAW. */ +/** What a shadow from BEFORE commit df1c75c4b hashes: the same columns, RAW. */ const staleKey = [ { column: 'organization_id', nullSafe: false }, { column: 'v', nullSafe: false }, @@ -188,7 +188,7 @@ describe('shadow-carried UNIQUE is not index drift (#13015)', () => { it('resolves the enforced key from the stored generation expression', () => { // The spellings MySQL 8 stores, verbatim: single column, plain composite, - // and the NULL-safe composite #12998 introduced. + // and the NULL-safe composite commit df1c75c4b introduced. expect(parseHashShadowKeyParts('unhex(sha2(`v`,256))')).toEqual([ { column: 'v', nullSafe: false }, ]); @@ -350,7 +350,7 @@ declareDialectCell(MYSQL_CELL, 'shadow-carried index drift (#13015)', (cell) => /** * The direction a blind skip would have lost: a shadow hashing the RAW - * columns (what shipped before #12998) must still be reported AND must be + * columns (what shipped before commit df1c75c4b) must still be reported AND must be * repairable — the stale column is re-generated, not reused. */ it('reports and repairs a shadow that hashes the raw columns', async () => { @@ -361,7 +361,7 @@ declareDialectCell(MYSQL_CELL, 'shadow-carried index drift (#13015)', (cell) => const indexName = 'uniq_os13015_stale_org_v'; const shadow = hashShadowColumnFor(indexName); - // Reproduce the pre-#12998 physical state: raw CONCAT, no COALESCE. + // Reproduce the physical state from before commit df1c75c4b: raw CONCAT, no COALESCE. await knex.raw(`ALTER TABLE \`os13015_stale\` DROP INDEX \`${indexName}\``); await knex.raw(`ALTER TABLE \`os13015_stale\` DROP COLUMN \`${shadow}\``); await knex.raw( diff --git a/packages/drivers/driver-sql/src/sql-driver-13056-orphan-shadow-column-cleanup.test.ts b/packages/drivers/driver-sql/src/sql-driver-13056-orphan-shadow-column-cleanup.test.ts index 0c42eaa1cc6..538563981af 100644 --- a/packages/drivers/driver-sql/src/sql-driver-13056-orphan-shadow-column-cleanup.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-13056-orphan-shadow-column-cleanup.test.ts @@ -28,11 +28,11 @@ * - `drop_index` — TERMINAL. The declaration is gone; nothing will re-create * the name, so the shadow derived from it is dead. This is the leak. * - `recreate_index` — drops in order to re-create under the SAME name. Its - * shadow is deliberately kept: #13015's `reusable` branch re-keys the + * shadow is deliberately kept: commit cd1348802's `reusable` branch re-keys the * survivor in place instead of rebuilding the table around a regenerated * STORED column. A cleanup inside `dropIndexIfExists` would destroy exactly * that survivor on every rebuild. - * - `replace_unique_index` — cannot reach a shadow at all. #13015 already + * - `replace_unique_index` — cannot reach a shadow at all. Commit cd1348802 already * excludes `isHashShadowCarrier` from legacy detection, in `diffManagedIndexes`, * with a comment saying it does so *because* that op drops the legacy name. * A shadow-aware step there would be enforcement for a state the producer @@ -331,7 +331,7 @@ describe('the drop_index op collects the shadow it retires (#13056)', () => { }); /** - * #13015's `foreign` guard is the precedent, and this is the same refusal in + * Commit cd1348802's `foreign` guard is the precedent, and this is the same refusal in * the removal direction: a column of that name that is not generated may hold * user data and is not the driver's to drop. */ @@ -415,7 +415,7 @@ describe('the drop_index op collects the shadow it retires (#13056)', () => { /** * `recreate_index` drops and re-creates under the SAME name. Its shadow must - * SURVIVE, or #13015's `reusable` branch — which re-keys the survivor in + * SURVIVE, or commit cd1348802's `reusable` branch — which re-keys the survivor in * place rather than rebuilding the table around a regenerated STORED column — * can never be reached again. This is the assertion a cleanup moved into * `dropIndexIfExists` would fail. diff --git a/packages/drivers/driver-sql/src/sql-driver-13438-federated-missing-remote-envelope.test.ts b/packages/drivers/driver-sql/src/sql-driver-13438-federated-missing-remote-envelope.test.ts index 1b87f38cdfc..9e6034d59b0 100644 --- a/packages/drivers/driver-sql/src/sql-driver-13438-federated-missing-remote-envelope.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-13438-federated-missing-remote-envelope.test.ts @@ -4,7 +4,7 @@ * objectstack#13438 — the terminal backend-fault envelope DECLARES the table the * statement targeted, so a genuinely absent federated remote reads benign again. * - * ## The residual #13324 left behind + * ## The residual commit 4cda78c9b left behind * * `isMissingTableError(error, readObject)` refuses the benign "not provisioned * yet" verdict when the dialect phrase names a relation OTHER than the one the @@ -21,7 +21,7 @@ * * The driver declares the table it targeted on the envelope, and the predicate * prefers a declared name over the caller-supplied object name. The predicate's - * half — precedence, the dialect fixtures, the #13324 fence — is pinned in + * half — precedence, the dialect fixtures, commit 4cda78c9b's fence — is pinned in * `packages/types/src/driver-error-classification.targeted-table.test.ts`. This * suite pins the DRIVER's half, live, on every dialect it speaks: * @@ -181,11 +181,11 @@ for (const cell of DIALECT_CELLS) { } // ───────────────────────────────────────────────────────────────── -// SQLITE-ONLY — the #13324 fence, live, WITH the declaration present +// SQLITE-ONLY — commit 4cda78c9b's fence, live, WITH the declaration present // ───────────────────────────────────────────────────────────────── /** - * The defect #13324 closed, reproduced live: a VIEW whose base table is gone + * The defect commit 4cda78c9b closed, reproduced live: a VIEW whose base table is gone * raises `no such table: main.` — a phrase that answers the shape test * perfectly and names a relation the statement did NOT target. The envelope * now declares the view (what `getBuilder` targeted); the phrase names the diff --git a/packages/drivers/driver-sql/src/sql-driver-13973-canonical-iso-read-door.test.ts b/packages/drivers/driver-sql/src/sql-driver-13973-canonical-iso-read-door.test.ts index 3bda054b45f..c5a04ecfc95 100644 --- a/packages/drivers/driver-sql/src/sql-driver-13973-canonical-iso-read-door.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-13973-canonical-iso-read-door.test.ts @@ -18,7 +18,7 @@ * `Date` it is (pinned by `sql-driver-14078-invalid-date-materialisation.test.ts`; * never met here, because this fixture writes only valid instants). * `findWithWindowFunctions` used to be the one read door outside this list: it - * applied no read presentation of any kind. Since #16609 it routes each row + * applied no read presentation of any kind. Since commit 78bc4ad58 it routes each row * through the SAME `formatOutput` pass `find()` runs (minus the window-alias * columns), so it presents these two column classes exactly as the doors above * do — pinned by `sql-driver-window-function-output.test.ts`, whose live cells @@ -308,7 +308,7 @@ function measure(cell: DialectCell): void { for (const col of INSTANT_COLUMNS) expectCanonicalInstant(r[col], `bulkUpdate() return w${i} ${col}`); expect(r.closed_at, `bulkUpdate() return w${i} closed_at`).toBe(CLOSED_AT[i]); // A fresh stamp, in UTC — the same recency bound §A1 puts on `find()`. - // [#17690] `bulkUpdate()` publishes the contract's + // [commit be5c60291] `bulkUpdate()` publishes the contract's // `Record[]` now, so the stamp is typed before it is // parsed. const updatedAt = r.updated_at; @@ -347,7 +347,7 @@ function measure(cell: DialectCell): void { // the batch landed, its rows read back canonical through `find()`, and — // where the return carried a row — the return and the row agree value // for value, so the return door presents what the read door presents. - // [#17690] `find()` publishes `Record[]`, so every id + // [commit be5c60291] `find()` publishes `Record[]`, so every id // read off a returned row is narrowed before it is used as a key, and // `Array.prototype.find`'s absent arm is narrowed away rather than // asserted past. @@ -469,7 +469,7 @@ for (const cell of DIALECT_CELLS) { * not to `normalizeSqliteDatetimeOutput`. The two presenters differ on exactly * one input class, a NUMBER: the audit presenter passes it through (ADR-0074 * §3), the datetime fold turns it into ISO text. #13973's first cut routed the - * audit columns to the datetime fold, and the contract review of PR #16619 + * audit columns to the datetime fold, and the contract review recorded in commit 45cfa1b88 * reproduced the divergence that made: an author-declared `created_at: number` * read `1700000000000` off `find()` and `"2023-11-14T22:13:20.000Z"` off * `distinct()` and `max()`. Two reachable shapes carry a number there: diff --git a/packages/drivers/driver-sql/src/sql-driver-14902-plain-unique-duplicate-preflight.test.ts b/packages/drivers/driver-sql/src/sql-driver-14902-plain-unique-duplicate-preflight.test.ts index b042819a8bb..925f3451a03 100644 --- a/packages/drivers/driver-sql/src/sql-driver-14902-plain-unique-duplicate-preflight.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-14902-plain-unique-duplicate-preflight.test.ts @@ -4,7 +4,7 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { SqlDriver } from '../src/index.js'; /** - * #14902 — a PLAIN unique index over existing duplicate rows. + * Commit 61821e54c — a PLAIN unique index over existing duplicate rows. * * ## The defect, in the two shapes it was measured side by side * diff --git a/packages/drivers/driver-sql/src/sql-driver-15479-shadow-plain-unique-duplicates.test.ts b/packages/drivers/driver-sql/src/sql-driver-15479-shadow-plain-unique-duplicates.test.ts index bdf5363fb1f..80e5ddd499a 100644 --- a/packages/drivers/driver-sql/src/sql-driver-15479-shadow-plain-unique-duplicates.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-15479-shadow-plain-unique-duplicates.test.ts @@ -6,7 +6,7 @@ * * ## The defect * - * #14902 (PR #15477) brought the DIRECT arm to parity: a plain unique over + * Commit 61821e54c (PR #15477) brought the DIRECT arm to parity: a plain unique over * existing duplicates logs on the durability channel and lets the boot * continue, instead of throwing the database's raw error. The hash-shadow arm * — one branch above it in the SAME `catch`, taken when MySQL refuses to key @@ -53,7 +53,7 @@ const LONG = 'p'.repeat(900); * A tenancy-DISABLED object whose one long text field carries a PLAIN unique. * `maxLength: 1024` exceeds the keyable ceiling, so MySQL refuses the direct * index and the sync takes the shadow route; `tenancy: { enabled: false }` is - * one of the two shapes #14902 identified as reaching the plain path (the other + * one of the two shapes commit 61821e54c identified as reaching the plain path (the other * is an explicit `unique: 'global'`, exercised by `globalUniqueOn` below). */ const plainUniqueOn = (name: string) => ({ @@ -125,7 +125,7 @@ declareDialectCell(MYSQL_CELL, 'hash-shadow plain unique over duplicates (#15479 ): Promise<{ logs: string[]; err: unknown }> => { driver = new SqlDriver(cell.config()); const logs = spy(); - // #16711: the `as any` that used to be on both of these calls was a + // Commit 7862fb711: the `as any` that used to be on both of these calls was a // workaround for `initObjects` not declaring `indexes`. The signature // declares it now, so the cast is gone and these two calls are checked // like any other. @@ -183,7 +183,7 @@ declareDialectCell(MYSQL_CELL, 'hash-shadow plain unique over duplicates (#15479 /** * The same disposition on the OTHER shape that reaches the plain path — an * explicit `unique: 'global'` on a tenanted object. Two shapes because - * #14902 measured both, and a guard keyed on the wrong one would pass here + * commit 61821e54c measured both, and a guard keyed on the wrong one would pass here * and fail in production. */ it("survives the boot under an explicit unique: 'global' on a tenanted object", async () => { diff --git a/packages/drivers/driver-sql/src/sql-driver-15683-temporal-text-operator-gate.test.ts b/packages/drivers/driver-sql/src/sql-driver-15683-temporal-text-operator-gate.test.ts index acccfdce1f7..fafabab764c 100644 --- a/packages/drivers/driver-sql/src/sql-driver-15683-temporal-text-operator-gate.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-15683-temporal-text-operator-gate.test.ts @@ -339,7 +339,7 @@ describe('[#15683] the per-dialect construct, compiled', () => { const d = new MultiProbeDriver(DIALECTS[0][1]).declareMulti(); const membership = d.compileWhere({ milestones: { $contains: '2026-01-05T00:00:00.000Z' } }); expect(membership).not.toMatch(/1 = 0/); - // [#17590] This row's own title said "it is membership, not a substring + // [commit e04a0aff2] This row's own title said "it is membership, not a substring // test" while the assertion under it named the SUBSTRING construct — the // only one that existed when it was written. It is a membership construct // now (`json_each` on this SQLite cell), so the assertion says what the diff --git a/packages/drivers/driver-sql/src/sql-driver-15989-file-column-move.test.ts b/packages/drivers/driver-sql/src/sql-driver-15989-file-column-move.test.ts index 78f8d7331c6..e668256dada 100644 --- a/packages/drivers/driver-sql/src/sql-driver-15989-file-column-move.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-15989-file-column-move.test.ts @@ -2,7 +2,7 @@ /** * [#15989] The ADR-0104 file-family COLUMN step, executed — per dialect, for - * BOTH encodings, across the window. The ruling on #15041 step 2, and the + * BOTH encodings, across the window. Step 2 of ADR-0104's 2026-09-05 addendum, and the * director ruling on this card (decision batch #120 item 1). * * `sql-driver-15989-file-family-bare-id.test.ts` pins what the two ARMS store @@ -13,7 +13,7 @@ * * ## §1 is the measurement this card exists for * - * The #15041 addendum prescribed `ALTER … USING (col #>> '{}')` with nothing in + * The ADR-0104 2026-09-05 addendum prescribed `ALTER … USING (col #>> '{}')` with nothing in * front of it, and required the step to abort *"on the first cell that is not a * JSON string"*. Those two sentences contradict each other, and which one was * wrong was settled by running it: `#>> '{}'` extracts ANY json type as text, so diff --git a/packages/drivers/driver-sql/src/sql-driver-15989-file-family-bare-id.test.ts b/packages/drivers/driver-sql/src/sql-driver-15989-file-family-bare-id.test.ts index 1f97411fc7b..fb4e9519e0b 100644 --- a/packages/drivers/driver-sql/src/sql-driver-15989-file-family-bare-id.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-15989-file-family-bare-id.test.ts @@ -4,7 +4,7 @@ * [#15989] The file family is stored as the BARE `sys_file` id in a string * column — per dialect, and for BOTH encodings across the ADR-0104 window. * - * Maintainer ruling on #15041, verbatim: 「15041 应该改为实际 id 保存。选A,其他 + * Maintainer ruling (ADR-0104's 2026-09-05 addendum), verbatim: 「15041 应该改为实际 id 保存。选A,其他 * 同意」. The physical column for `file` / `image` / `avatar` / `video` / * `audio` holds the actual id, not a JSON-quoted id in a JSON column. The * generator already emits `VARCHAR(2048)` for the family and does not move; the diff --git a/packages/drivers/driver-sql/src/sql-driver-16570-init-objects-indexes-param.test.ts b/packages/drivers/driver-sql/src/sql-driver-16570-init-objects-indexes-param.test.ts index e19b3c6dbb0..c624d9fd056 100644 --- a/packages/drivers/driver-sql/src/sql-driver-16570-init-objects-indexes-param.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-16570-init-objects-indexes-param.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * [#16570] `initObjects` and `registerObjectMetadata` accept `indexes` — the + * [commit b72226f48] `initObjects` and `registerObjectMetadata` accept `indexes` — the * key they have always READ — spelled as a **fresh object literal**. * * ## The defect this pins diff --git a/packages/drivers/driver-sql/src/sql-driver-16657-operator-facing-cause-text.test.ts b/packages/drivers/driver-sql/src/sql-driver-16657-operator-facing-cause-text.test.ts index 5c843f0d32c..31d10e9d843 100644 --- a/packages/drivers/driver-sql/src/sql-driver-16657-operator-facing-cause-text.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-16657-operator-facing-cause-text.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * [#16657] The producer↔consumer pin for `operatorFacingErrorText`. + * [commit 5a95b0e93] The producer↔consumer pin for `operatorFacingErrorText`. * * `@objectstack/types` cannot import a driver — every driver depends on it — * so the helper that reads the raw-path envelope carries its own copy of the diff --git a/packages/drivers/driver-sql/src/sql-driver-16711-object-def-param-keys.test.ts b/packages/drivers/driver-sql/src/sql-driver-16711-object-def-param-keys.test.ts index 1865e815f3e..9dce94669df 100644 --- a/packages/drivers/driver-sql/src/sql-driver-16711-object-def-param-keys.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-16711-object-def-param-keys.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * [#16711] The object-definition parameters on `SqlDriver` accept every key + * [commit 7862fb711] The object-definition parameters on `SqlDriver` accept every key * they READ, spelled as a **fresh object literal** — and still refuse a key * that is genuinely not one of them. * @@ -10,7 +10,7 @@ * `SqlDriver` reads keys off caller objects through `(obj as any).` while * the parameter's own inline literal declares none of them. Three instances * were carded and fixed one at a time before anyone called it a class: - * `tenancy` (#4311), `indexes` (#16570), and `lifecycle` here. Each fix left + * `tenancy` (#4311), `indexes` (commit b72226f48), and `lifecycle` here. Each fix left * the next one standing, and each looked complete from inside its own card. * * The escape is silent by construction. TypeScript's excess-property check @@ -105,7 +105,7 @@ describe('SqlDriver object-definition parameters accept the keys they read (#167 // The PUBLIC entry point of the rotation chain, with a fresh literal // carrying both keys the chain's leaf (`ensureShardTable`) reads. Before - // #16711 this call did not compile: `rotateShards` declared neither key, + // commit 7862fb711 this call did not compile: `rotateShards` declared neither key, // and the two casts that read them sat three links down. const state = await driver.rotateShards({ ...bareObject(T), @@ -140,7 +140,7 @@ describe('SqlDriver object-definition parameters accept the keys they read (#167 }); /** - * ⭐ THE NEGATIVE CONTROL (#16711 验收口径 item 4). + * ⭐ THE NEGATIVE CONTROL (验收口径 item 4 of the card commit 7862fb711 closed). * * Widening is only a fix while the accept set still has a boundary. A parameter * relaxed to `any`, or given an index signature, makes every assertion above @@ -172,7 +172,7 @@ export async function refusesKeysThatAreNotDeclared(driver: SqlDriver): Promise< } /** - * The narrowing axis, unchanged from #16570 and re-pinned here because the + * The narrowing axis, unchanged from commit b72226f48 and re-pinned here because the * `lifecycle` widening touches the same literal: a variable-bound argument * bypasses the excess-property check and is judged by ordinary assignability, * so the declared TYPES still bind. Compile-time only. diff --git a/packages/drivers/driver-sql/src/sql-driver-17343-multi-valued-boolean-membership.test.ts b/packages/drivers/driver-sql/src/sql-driver-17343-multi-valued-boolean-membership.test.ts index 29e575d8890..ad430b27659 100644 --- a/packages/drivers/driver-sql/src/sql-driver-17343-multi-valued-boolean-membership.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-17343-multi-valued-boolean-membership.test.ts @@ -1,13 +1,13 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * [#17343 · retargeted by #17469] The declared-type gate never fires on a JSON + * [commit 82cb69fed · retargeted by #17469] The declared-type gate never fires on a JSON * column — and the population of JSON columns is now the one the protocol * declares. * * ## What this file was filed for, and what happened to it * - * #17343: a `multiple: true` BOOLEAN column lost its `$contains` MEMBERSHIP + * The defect commit 82cb69fed fixed: a `multiple: true` BOOLEAN column lost its `$contains` MEMBERSHIP * filter — the carve-out #14079's declared-type gate never received on its * boolean limb, while its numeric limb carried one from the first line it * shipped and its temporal limb gained one in #15683. `{ FIELD: { $contains: @@ -51,7 +51,7 @@ * ## Which cells executed * * - **sqlite** — always, embedded. - * - **live mysql / live postgres** — run when provisioned. #17590's ruling + * - **live mysql / live postgres** — run when provisioned. The ruling commit e04a0aff2 records * (2026-09-12) replaced the text lowering with a real MEMBERSHIP construct * compiled per dialect, so all three answer the same rows and this file * carries no per-dialect branch. @@ -59,11 +59,11 @@ * @see SqlDriver.isNonTextColumn — the predicate; its JSON carve-out is the invariant. * @see SqlDriver.isJsonField — the storage half of the #17469 ruling. * @see https://github.com/objectstack-ai/objectstack/issues/17469 (the ruling that retargeted this file) - * @see https://github.com/objectstack-ai/objectstack/issues/17343 + * @see commit 82cb69fed * @see https://github.com/objectstack-ai/objectstack/issues/14079 (the gate) * @see https://github.com/objectstack-ai/objectstack/issues/15683 (the temporal carve-out) * @see https://github.com/objectstack-ai/objectstack/issues/7398 (the membership spelling it protects) - * @see https://github.com/objectstack-ai/objectstack/issues/17590 (the membership construct) + * @see commit e04a0aff2 (the membership construct) */ import { describe, it, expect, beforeAll, afterAll } from 'vitest'; @@ -251,11 +251,11 @@ describe('[#17343] the per-dialect construct, compiled — the registerExternalO const typed = (config: SqlDriverConfig) => new CompilerProbeDriver(config).declareMulti(); /** - * [#17590] "A real predicate over the column" per dialect — the pattern + * [commit e04a0aff2] "A real predicate over the column" per dialect — the pattern * emitter OR the membership construct that replaced it for `$contains`. This * file's question is whether the declared-type gate fired, so it must accept * either; asserting one SHAPE here would make it red on the card that changes - * the other, which is exactly what #17590 did to its predecessor. [#20024] + * the other, which is exactly what commit e04a0aff2 did to its predecessor. [#20024] * On SQLite the substring emitter spells `contains` as `instr(` and `ends` as * `substr(CAST(`; only `starts` and the `$like` pair keep `GLOB`. */ @@ -274,7 +274,7 @@ describe('[#17343] the per-dialect construct, compiled — the registerExternalO // JSON column now; the rest of the family is REFUSED there (`400`), // ahead of both this card's declared-type gate and the emitter. Either // way the gate this file is about does not fire: a refusal is not the - // `1 = 0` constant, and the SHAPE per operator is owned by #17590's + // `1 = 0` constant, and the SHAPE per operator is owned by commit e04a0aff2's // and #21009's own files. if (op !== '$contains') { let refusal: (Error & { code?: string; status?: number }) | undefined; @@ -289,7 +289,7 @@ describe('[#17343] the per-dialect construct, compiled — the registerExternalO } const sql = d.compileWhere({ [field]: { [op]: 'x' } } as FilterCondition); expect(sql, `${op} over ${field}`).not.toMatch(/1 = 0|1 = 1/); - // [#17590] `$contains` compiles the MEMBERSHIP construct. What this + // [commit e04a0aff2] `$contains` compiles the MEMBERSHIP construct. What this // card is about is not the shape — it is that the declared-type gate // does not fire — so this row asks for "a real predicate over the // column". diff --git a/packages/drivers/driver-sql/src/sql-driver-17586-multi-valued-boolean-read-inversion.test.ts b/packages/drivers/driver-sql/src/sql-driver-17586-multi-valued-boolean-read-inversion.test.ts index d465a254c72..c4e816c5b7c 100644 --- a/packages/drivers/driver-sql/src/sql-driver-17586-multi-valued-boolean-read-inversion.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-17586-multi-valued-boolean-read-inversion.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * [#17586 · retargeted by #17469] A JSON column's stored array survives the + * [commit d46deba19 · retargeted by #17469] A JSON column's stored array survives the * read — and `booleanFields` never contains one. * * ## What this file was filed for @@ -67,8 +67,8 @@ * @see SqlDriver.isNonTextColumn — the reader that carves out at the reader. * @see SqlDriver.isJsonField — the storage half of the #17469 ruling. * @see https://github.com/objectstack-ai/objectstack/issues/17469 (the ruling that retargeted this file) - * @see https://github.com/objectstack-ai/objectstack/issues/17586 - * @see https://github.com/objectstack-ai/objectstack/issues/17343 (the filter half) + * @see commit d46deba19 + * @see commit 82cb69fed (the filter half) * @see https://github.com/objectstack-ai/objectstack/issues/11782 (the pass) * @see https://github.com/objectstack-ai/objectstack/issues/11635 (the PG cast) */ @@ -98,7 +98,7 @@ const BYPASS: DriverOptions = { bypassTenantAudit: true }; * statement is refused before any row is presented (`could not identify an * equality operator for type json`). A property of the COLUMN CLASS, measured * on live PostgreSQL 16.13 across both legs of the original card's change and - * unchanged by it — the mirror of the `LIKE`-over-`json` divergence #17590 owns + * unchanged by it — the mirror of the `LIKE`-over-`json` divergence commit e04a0aff2 ruled * on the filter side, reached through the read door instead. */ const distinctExecutes = (cell: DialectCell): boolean => cell.id !== 'pg'; @@ -259,11 +259,11 @@ function declareReadSweep(cell: DialectCell): void { } else { /** * The NAMED DIVERGENCE, pinned rather than skipped — the same posture - * #17343's suite takes for the filter-side half of this property. + * commit 82cb69fed's suite takes for the filter-side half of this property. * * PostgreSQL's `json` type defines no equality operator and * `SELECT DISTINCT` needs one, so this door is REFUSED there for every - * JSON column. It is pinned as the ADR-0112 envelope #17639 brought to + * JSON column. It is pinned as the ADR-0112 envelope commit 7c2c5aedd brought to * this door, and on the CLASS rather than on a bare throw: `picks` is a * multi-valued `select` and `tags_` an inherently-multi option type, and * both must fail the SAME way, so a future edit that broke one of them @@ -287,7 +287,7 @@ function declareReadSweep(cell: DialectCell): void { expect(err, `distinct() over ${column} must be refused on this backend`).toBeDefined(); expect(err!.code, `code for ${column}`).toBe('DATABASE_ERROR'); expect(err!.status, `status for ${column}`).toBe(500); - // [#17639] The raw SQLSTATE the caller used to receive is the CAUSE now. + // [commit 7c2c5aedd] The raw SQLSTATE the caller used to receive is the CAUSE now. expect((err as unknown as { cause?: { code?: string } }).cause?.code, `SQLSTATE for ${column}`) .toBe('42883'); } @@ -426,7 +426,7 @@ describe('[#17586] the `booleanFields` registry and its four readers', () => { * The two registry fills, side by side. The omission the card repairs was * present in BOTH, and they are separate code with no shared helper to make * that impossible — so a repair reaching only one leaves the defect live on - * the other, exactly as #17343's round found. + * the other, exactly as commit 82cb69fed's round found. */ it('BOTH registry fills agree — initObjects and registerExternalObject register the same columns', async () => { const ext = external(DIALECTS[0][1]); diff --git a/packages/drivers/driver-sql/src/sql-driver-17590-json-column-membership.test.ts b/packages/drivers/driver-sql/src/sql-driver-17590-json-column-membership.test.ts index 17ad08ac88c..4c69cc0e6c2 100644 --- a/packages/drivers/driver-sql/src/sql-driver-17590-json-column-membership.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-17590-json-column-membership.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * [#17590, director ruling 2026-09-12] `$contains` on a multi-valued / JSON + * [commit e04a0aff2, director ruling 2026-09-12] `$contains` on a multi-valued / JSON * column is a MEMBERSHIP test, and `driver-sql` compiles it PER DIALECT so * SQLite, MySQL and PostgreSQL answer the SAME ROWS. * @@ -52,7 +52,7 @@ * - **live postgres** — the cell that carried the defect. Runs when * provisioned; measured here on PostgreSQL 16.13. * - **live mysql** — measured DIRECTLY, which is what the ruling asked for: - * the card's MySQL row was a second-hand reading off #17343's CI job. + * the card's MySQL row was a second-hand reading off commit 82cb69fed's CI job. * Measured here on MySQL 8.0.46. * * The three cells assert the SAME literal row sets, which is what "answer the @@ -61,9 +61,9 @@ * @see SqlDriver.applyJsonMembership — the emitter and its two fall-through cases. * @see jsonMembershipPredicate — the per-dialect construct and its measured table. * @see jsonMembershipCandidates — why one string comparand denotes two JSON scalars. - * @see https://github.com/objectstack-ai/objectstack/issues/17590 + * @see commit e04a0aff2 * @see https://github.com/objectstack-ai/objectstack/issues/7398 (the membership spelling) - * @see https://github.com/objectstack-ai/objectstack/issues/17343 (the boolean cell this covers) + * @see commit 82cb69fed (the boolean cell this covers) * @see https://github.com/objectstack-ai/objectstack/issues/17469 (the population predicate, unwidened) */ diff --git a/packages/drivers/driver-sql/src/sql-driver-17639-distinct-fault-envelope.test.ts b/packages/drivers/driver-sql/src/sql-driver-17639-distinct-fault-envelope.test.ts index 61ada54547b..936cb188710 100644 --- a/packages/drivers/driver-sql/src/sql-driver-17639-distinct-fault-envelope.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-17639-distinct-fault-envelope.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * objectstack#17639 — `distinct()` joins the enveloped read exits. + * Commit 7c2c5aedd — `distinct()` joins the enveloped read exits. * * ## The measurement this suite is built from * @@ -41,12 +41,12 @@ * ⛔ It is NOT a request to make `distinct()` ANSWER over a JSON column. The * call fails either way; what changes is whether the failure is CLASSIFIED. * Whether a `json` column should support a distinct read at all belongs with - * #17590, which owns the sibling `LIKE`-over-`json` divergence on the filter + * the card commit e04a0aff2 closed, which owned the sibling `LIKE`-over-`json` divergence on the filter * side of the same columns. This is the same split #11455 made on the * `aggregate()` door while #11152 / #11249 still owned the answer question: the * envelope is the half that holds whichever way that card rules. * - * ⚠️ RETIREMENT CLAUSE for the Postgres-only block at the bottom: if #17590 + * ⚠️ RETIREMENT CLAUSE for the Postgres-only block at the bottom: if a card after commit e04a0aff2 * rules that a `json` column should ANSWER a distinct read, those cases stop * failing and that block is RETIRED by that card — exactly as #11635 retired * #11455's boolean-aggregand block. What the all-dialect sweep pins is @@ -62,14 +62,14 @@ * would not run this statement* — which is the only claim the signal supports. * ⛔ Never `INVALID_QUERY` / 400: that would say *"asking for the distinct * values of this column is your mistake"*, a verdict about the request that - * #17590 has not made. + * commit e04a0aff2 has not made. * * | card | door | before | after | * |--------|-----------------|--------------------------------|----------------------| * | #8790 | `count()` WHERE | raw dialect error | `INVALID_FILTER`/400 | * | #8931 | read exits | raw dialect error, no `status` | `DATABASE_ERROR`/500 | * | #11455 | `aggregate()` | raw dialect error, no `status` | `DATABASE_ERROR`/500 | - * | #17639 | `distinct()` | raw dialect error, no `status` | `DATABASE_ERROR`/500 | + * | commit 7c2c5aedd | `distinct()` | raw dialect error, no `status` | `DATABASE_ERROR`/500 | * * ⭐ The envelope comes from the EXIT, not from recognising `42883` or the * words *equality operator* — the #8926 lesson (a predicate arm matched by @@ -86,7 +86,7 @@ * compiled from `filters`. A blanket arm would tell the author of * `distinct(o, 'nosuchcol')` — who passed no filter at all — that their FILTER * was wrong. #11541 closed that gap for `aggregate()` with a clause-attributing - * classifier; #17857 has since closed the `distinct()` half the same way + * classifier; commit 9ccc4179e has since closed the `distinct()` half the same way * (`SqlDriver.distinctBackendFault`, pinned by * `sql-driver-17857-distinct-unresolvable-column-refusal.test.ts`). What this * suite pins was unchanged by that, exactly as this note anticipated: an error @@ -177,7 +177,7 @@ describe(`[#17639] driver-sql — distinct() takes the backend-fault envelope ($ // A table that was never provisioned raises a dialect error on all three // backends (`42P01` / `SQLITE_ERROR` / `ER_NO_SUCH_TABLE`), so this is the // envelope invariant measured on EVERY cell — and it involves no JSON column, - // so #17590's ruling cannot move it in either direction. + // so the ruling commit e04a0aff2 records cannot move it in either direction. it('an unclassified dialect fault on the distinct door carries a code AND a status', async () => { const err = await caught(() => driver.distinct(MISSING_TABLE, 'title')); expect(err.code, 'code').toBe('DATABASE_ERROR'); @@ -202,8 +202,8 @@ describe(`[#17639] driver-sql — distinct() takes the backend-fault envelope ($ }); // ⛔ THE FENCE: the envelope claims nothing about the request. Listing the - // distinct values of a column is not a malformed query, and #17590 — not this - // exit — owns whether the column supports the read. + // distinct values of a column is not a malformed query, and the card commit e04a0aff2 closed — not this + // exit — owned whether the column supports the read. it('the envelope makes NO claim about the query or the filter', async () => { const err = await caught(() => driver.distinct(MISSING_TABLE, 'title')); expect(err.code).not.toBe('INVALID_QUERY'); @@ -299,7 +299,7 @@ for (const cell of DIALECT_CELLS) { // ───────────────────────────────────────────────────────────────── // // ⚠️ RETIREMENT CLAUSE: this block pins that the JSON-column refusal the card -// measured is ENVELOPED, never that it refuses. If #17590 rules that a `json` +// measured is ENVELOPED, never that it refuses. If a card after commit e04a0aff2 rules that a `json` // column should ANSWER a distinct read, these cases stop failing and this block // is RETIRED by that card — the same clause #11455's boolean-aggregand block // carried and #11635 fired. diff --git a/packages/drivers/driver-sql/src/sql-driver-17857-distinct-unresolvable-column-refusal.test.ts b/packages/drivers/driver-sql/src/sql-driver-17857-distinct-unresolvable-column-refusal.test.ts index 9c1e9910ee6..c6520d71a58 100644 --- a/packages/drivers/driver-sql/src/sql-driver-17857-distinct-unresolvable-column-refusal.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-17857-distinct-unresolvable-column-refusal.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * objectstack#17857 — the #8790 unresolvable-column refusal reaches the LAST + * Commit 9ccc4179e — the #8790 unresolvable-column refusal reaches the LAST * read door, attributed to the clause the caller's own request names. * * ## The five-row probe this card was filed from @@ -25,9 +25,9 @@ * naming nothing they could act on — and a picklist-populating `distinct()` * sits beside the `find()` and `count()` of the same list view. * - * ## What #17639 landed, and why it stopped where it did + * ## What commit 7c2c5aedd landed, and why it stopped where it did * - * #17639 gave this door the TERMINAL envelope (`DATABASE_ERROR` / 500 instead + * Commit 7c2c5aedd gave this door the TERMINAL envelope (`DATABASE_ERROR` / 500 instead * of pg's raw `DatabaseError`) and deliberately left the ATTRIBUTION arm filed, * with the reason written at the door: a BLANKET `isUnresolvableColumnError` * arm answers *"Filter on 'x' names a column …"*, and this door names columns @@ -48,7 +48,7 @@ * compiled from `filters` and the tenant-scope predicate, both filters, so * #8790's `unresolvableFilterColumnRefusal` applies verbatim; * 3. `unresolvableColumnNameOf` answers `null` ⇒ no attribution is supportable, - * so the #17639 terminal envelope stands unchanged. + * so commit 7c2c5aedd's terminal envelope stands unchanged. * * ⭐ Arm 2 is the COMPLEMENT of arm 1, never a search of the `filters` AST for * the name — pinned below on `{ $or: [{ nosuchcol: 1 }] }`. `FilterCondition` @@ -72,9 +72,9 @@ * * ## What this suite deliberately does NOT decide * - * ⛔ Nothing here touches #17590 (whether a `json` column should ANSWER a + * ⛔ Nothing here touches the card commit e04a0aff2 closed (whether a `json` column should ANSWER a * distinct read). That is a different condition — a column that EXISTS whose - * type has no equality operator — and it stays on #17639's terminal envelope, + * type has no equality operator — and it stays on commit 7c2c5aedd's terminal envelope, * whose own pins in `sql-driver-17639-distinct-fault-envelope.test.ts` are * unchanged by this card: an error this classifier does not claim still leaves * as `DATABASE_ERROR` / 500. @@ -144,7 +144,7 @@ async function withLog( /** * The disclosure clause, applied to one caller-visible message — the same - * NEGATIVE-set-plus-positive-anchor shape the #8931, #11455, #11541 and #17639 + * NEGATIVE-set-plus-positive-anchor shape the #8931, #11455, #11541 and commit 7c2c5aedd * suites use, so an emptied message cannot satisfy it trivially. */ function expectNoStatementShape(message: string, object: string, half: string): void { @@ -347,7 +347,7 @@ describe(`[#17857] driver-sql — distinct() attributes an unresolvable column ( ).toEqual(['Design']); }); - // ⛔ #17639's terminal is NOT narrowed by this card. A table that was never + // ⛔ Commit 7c2c5aedd's terminal is NOT narrowed by this card. A table that was never // provisioned is not an unresolvable COLUMN, so nothing here claims it. it('CONTROL an unclassified dialect fault still leaves as the #17639 terminal', async () => { const err = await caught(() => driver.distinct(MISSING_TABLE, 'title')); diff --git a/packages/drivers/driver-sql/src/sql-driver-20987-json-membership-move.test.ts b/packages/drivers/driver-sql/src/sql-driver-20987-json-membership-move.test.ts index 70a06c8bfec..f2228702776 100644 --- a/packages/drivers/driver-sql/src/sql-driver-20987-json-membership-move.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-20987-json-membership-move.test.ts @@ -5,7 +5,7 @@ * MEMBERSHIP emitter: the statement and its bindings, per dialect and per * comparand shape, are the ones this driver emitted before the predicate moved. * - * `jsonMembershipCandidates` and the per-dialect construct (#17590) were + * `jsonMembershipCandidates` and the per-dialect construct (commit e04a0aff2) were * module-private here. They moved to `@objectstack/core` * (`utils/json-membership-sql.ts`) so the analytics read scope and the * analytics `where` ask the same question this driver asks, from one @@ -23,7 +23,7 @@ * string `Number()` would accept but the JSON grammar refuses (`'0x10'`). * * `'unknown'` is a knex client this driver does not model (here `mssql`): it - * keeps the pre-#17590 `LIKE`, unchanged by the move. The scalar column beside + * keeps the `LIKE` from before commit e04a0aff2, unchanged by the move. The scalar column beside * the JSON one is the control: `$contains` stays the substring test there. */ diff --git a/packages/drivers/driver-sql/src/sql-driver-backend-fault-envelope.test.ts b/packages/drivers/driver-sql/src/sql-driver-backend-fault-envelope.test.ts index ce3681226cd..584b5c8d0cb 100644 --- a/packages/drivers/driver-sql/src/sql-driver-backend-fault-envelope.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-backend-fault-envelope.test.ts @@ -74,7 +74,7 @@ * the platform ACCEPTS and is expressly outside this ruling. * * The predicate already follows `error.cause` four levels deep, with its own - * pins (`packages/types/src/driver-error-classification.test.ts` — #13279 + * pins (`packages/types/src/driver-error-classification.test.ts` — commit 6a180e42d * moved it there from `metadata/src/utils/schema-sync-errors.test.ts` — * "follows an error wrapped as `cause`"), because "drivers commonly re-throw * with the original attached as `cause`" is a case it was built for. So the diff --git a/packages/drivers/driver-sql/src/sql-driver-datetime-mysql-storage.test.ts b/packages/drivers/driver-sql/src/sql-driver-datetime-mysql-storage.test.ts index fdb30016a7a..cfbaa906837 100644 --- a/packages/drivers/driver-sql/src/sql-driver-datetime-mysql-storage.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-datetime-mysql-storage.test.ts @@ -54,7 +54,7 @@ describe.skipIf(!URL)('Field.datetime on MySQL (#3942)', () => { let driver: SqlDriver; let serverTimeZone = ''; - // ── Why this beforeAll carries an explicit 60_000 budget (#14628) ── + // ── Why this beforeAll carries an explicit 60_000 budget (commit 6392b9c2b) ── // The live cell is one indirection away: this hook builds no driver of its // own, it calls `rawDriver()` above — which hard-codes `MYSQL_CELL.config()`, // unconditionally LIVE, not a parametrised `cell.config()` that would be diff --git a/packages/drivers/driver-sql/src/sql-driver-deferred-ddl-lock-wait.test.ts b/packages/drivers/driver-sql/src/sql-driver-deferred-ddl-lock-wait.test.ts index 90ac919493b..2b54722a52e 100644 --- a/packages/drivers/driver-sql/src/sql-driver-deferred-ddl-lock-wait.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-deferred-ddl-lock-wait.test.ts @@ -16,7 +16,7 @@ * whose code comes from the closed vocabulary and names the lock wait. No retry * logic, no configurability. * - * # #9542 — the same bound on boot, deliberately without the refusal + * # Commit 8bbf45947 — the same bound on boot, deliberately without the refusal * * The seam above separates "bound the wait" from "escape the swallow", and * #9354 armed only the flush, leaving boot schema-sync on the one-year default @@ -110,7 +110,7 @@ class FakeMysqlDriver extends SqlDriver { issued: Issued[] = []; /** - * [#9542/#9609] Every log line the driver emitted, **with its level**. + * [commit 8bbf45947, #9609] Every log line the driver emitted, **with its level**. * * On the boot path this is the ONLY output a blocked widening produces — the * swallow eats the error itself — so "the bound fires and the operator is @@ -378,7 +378,7 @@ describe('[#9354/#9542] a blocked widening ALTER — bounded on both paths, refu }); // ─────────────────────────────────────────────────────────────── - // BOOT (#9542) — the same bound, the opposite answer when it fires + // BOOT (commit 8bbf45947) — the same bound, the opposite answer when it fires // ─────────────────────────────────────────────────────────────── it('bounds BOOT sync too, and still swallows — boot is not the flush', async () => { @@ -390,7 +390,7 @@ describe('[#9354/#9542] a blocked widening ALTER — bounded on both paths, refu // but off the deferred path. This pin used to read the other way round: // boot ran the ALTER through the pool at MySQL's one-year default, so a boot // behind another session's metadata lock stopped at schema-sync, printed - // nothing, and could not be told from a crash. #9542 arms the bound here. + // nothing, and could not be told from a crash. Commit 8bbf45947 arms the bound here. await expect(driver.initObjects([WIDGET])).resolves.toBeUndefined(); const set = setStatements(driver); @@ -452,7 +452,7 @@ describe('[#9354/#9542] a blocked widening ALTER — bounded on both paths, refu // while something it claims is persisted has not landed? Here: yes. Boot // completed, traffic is served, and the `error` limb names this exact case // — "DDL that was supposed to run did not". The swallow is unchanged and - // deliberately so (#9542); only the level moved. + // deliberately so (commit 8bbf45947); only the level moved. const line = driver.logs.find((w) => /widen MySQL datetime columns/.test(w.msg)); expect(line?.level).toBe('error'); // ⭐ Asserted as an ABSENCE too, because `find` above would happily return @@ -529,7 +529,7 @@ describe('[#9354/#9542] a blocked widening ALTER — bounded on both paths, refu await expect(driver.initObjects([WIDGET])).resolves.toBeUndefined(); // ⚠️ This pin guards FLAG HYGIENE, not boot policy — it only ever shared an // assertion with the pin above. It used to read `setStatements → 0`, which - // since #9542 says nothing about the flag: a clean boot arms the bound too, + // since commit 8bbf45947 says nothing about the flag: a clean boot arms the bound too, // so that count is 2 either way. The observable that still discriminates is // the swallow on the line above (a stuck flag makes this same lock wait // escape `initObjects` as a refusal), and this line keeps it from passing diff --git a/packages/drivers/driver-sql/src/sql-driver-diagnostic-value-probe.test.ts b/packages/drivers/driver-sql/src/sql-driver-diagnostic-value-probe.test.ts index c675f091a7f..faae129c460 100644 --- a/packages/drivers/driver-sql/src/sql-driver-diagnostic-value-probe.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-diagnostic-value-probe.test.ts @@ -1,14 +1,14 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * [#9160] The instrument #8823 did not have: raise each candidate diagnostic + * [#9160] The instrument commit 4dfa369a9 did not have: raise each candidate diagnostic * family against a LIVE server and record what the server actually printed. * * ## Why this file exists * * `redactStatementFromMessage` (`@objectstack/objectql`) keeps the database's * diagnostic after the statement cut, on the premise that a diagnostic names - * IDENTIFIERS. #8823 found one family where that is false — MySQL's + * IDENTIFIERS. Commit 4dfa369a9 found one family where that is false — MySQL's * `ER_DUP_ENTRY` inlines the conflicting VALUE — and redacted that one slot. * * The list it introduced had exactly one entry and **no way to notice a second @@ -236,7 +236,7 @@ const PG_CASES: readonly ProbeCase[] = [ raise: (db) => db(PG_TABLE).insert({ age: 99999999999 }), }, { - // #8823's coincidence, re-measured. The value is on `detail`, which + // Commit 4dfa369a9's coincidence, re-measured. The value is on `detail`, which // `ObjectLogger.write` does not serialize — so Postgres is saved here by a // fact about our Logger, not by the cut. family: 'unique_violation (23505)', diff --git a/packages/drivers/driver-sql/src/sql-driver-distinct-filter-narrowing.test.ts b/packages/drivers/driver-sql/src/sql-driver-distinct-filter-narrowing.test.ts index 0ff64bc0f68..54850fa8a6b 100644 --- a/packages/drivers/driver-sql/src/sql-driver-distinct-filter-narrowing.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-distinct-filter-narrowing.test.ts @@ -5,7 +5,7 @@ * `FilterCondition`, never `any`. * * `distinct` is not declared on `IDataDriver`, so #5181's narrowing and - * #6075's follow-through never reached it; it kept `filters?: any` while its + * commit d367f03d6's follow-through never reached it; it kept `filters?: any` while its * body said something much more specific — `applyFilters(builder, filters)` * takes the argument ITSELF, so what it wants is the value `find()` carries * under `query.where`, not a query envelope. This file holds the type to that @@ -52,8 +52,8 @@ import { SqlDriver } from './index.js'; type IsAny = 0 extends 1 & T ? true : false; /** - * [#17879] MEASURED — this door's `IsAny` half is a PHANTOM half, and the - * `ContainsAny` detector (#17876) does NOT close it here. On disk, against + * [commit eb9334915] MEASURED — this door's `IsAny` half is a PHANTOM half, and the + * `ContainsAny` detector (commit be5c60291) does NOT close it here. On disk, against * this file's own driver, with both directions predicted before running: * * door resolves to `FilterCondition | undefined` @@ -67,7 +67,7 @@ type IsAny = 0 extends 1 & T ? true : false; * assertion forms; and `ContainsAny` is already `true` — * the contract type is an open map (`[key: string]: any`), so the detector is * SATURATED on the correct door and cannot separate it from a regression. - * No swap was made. The two measured repairs are in the #17879 report. + * No swap was made. The two measured repairs are in commit eb9334915's message. */ describe('SqlDriver.distinct takes a bare FilterCondition (#6320)', () => { diff --git a/packages/drivers/driver-sql/src/sql-driver-doors-declared-types.test.ts b/packages/drivers/driver-sql/src/sql-driver-doors-declared-types.test.ts index e4cfb4c0d9b..23a638c2749 100644 --- a/packages/drivers/driver-sql/src/sql-driver-doors-declared-types.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-doors-declared-types.test.ts @@ -3,7 +3,7 @@ // #15267 — the five remaining `IDataDriver` doors on `SqlDriver` publish their // declared return type, not `any`. // -// #14438 (PR #15280) un-masked `update()` on this class and filed the census of +// Commit 2200f8ec8 (PR #15280) un-masked `update()` on this class and filed the census of // what was left: `findOne`, `create`, `bulkCreate`, `execute` and `explain` // each carried an EXPLICIT `Promise` while // `packages/spec/src/contracts/data-driver.ts` had already declared every one @@ -39,7 +39,7 @@ // against the very `any` being removed. `findOne`'s `null` arm is the one door // that also has a runtime face, and it is exercised below. // -// The typed-const form is `sql-driver-update-declared-null.test.ts`'s (#14438), +// The typed-const form is `sql-driver-update-declared-null.test.ts`'s (commit 2200f8ec8), // which is `memory-update-declared-null.test.ts`'s (#13878). `TursoDriver` // overrides four of these five doors and carries its own copy of the driver // half in its own tsc program (`turso-driver-doors-declared-types.test.ts`); @@ -64,7 +64,7 @@ // not whether the door owes its declared type: optionality governs whether the // member EXISTS, not what it returns once it does. // -// #17690 adds four MORE doors of the same family — `find`, `upsert`, +// Commit be5c60291 adds four MORE doors of the same family — `find`, `upsert`, // `bulkUpdate` and `temporalFilterValue` — and the reason they were not in // #15267's repaired set nor in its deliberately-excluded set is worth one // paragraph, because it is the transferable half of this card. #15267's census @@ -106,7 +106,7 @@ type Equals = (() => T extends A ? 1 : 2) extends (() => T extends B type Resolved = F extends (...args: never[]) => PromiseLike ? R : never; /** - * [#17690] `IsAny` answers about T ITSELF, which is honestly `false` for + * [commit be5c60291] `IsAny` answers about T ITSELF, which is honestly `false` for * `any[]` and for `Record` — and those are exactly the two shapes * every door on this card had regressed to. Used as the "is not `any`" half of * a nested-`any` door it is a PHANTOM CHECK: it evaluates, it is green, and it @@ -131,7 +131,7 @@ type Resolved = F extends (...args: never[]) => PromiseLike ? R : ne * `Record[]` / `Record` / `unknown` answer * `false`. * - * [#17970] Answering `boolean` is the ONE failure mode this detector has to + * [commit 47e6601c5] Answering `boolean` is the ONE failure mode this detector has to * stay out of, and staying out of it takes TWO guards, because `T` can reach a * distributive conditional for two unrelated reasons. Every leg below is * spelled `const x: ContainsAny = false`, and `false` is ASSIGNABLE to @@ -176,7 +176,7 @@ type ContainsAnyPerMember = IsAny extends true : false; /** - * [#17970] The collapse. `ContainsAnyPerMember` is distributive, so on a + * [commit 47e6601c5] The collapse. `ContainsAnyPerMember` is distributive, so on a * union door it answers a UNION of per-member verdicts; this reports `false` * only when that union is exactly `false`, turning any `boolean` into `true`. */ @@ -192,7 +192,7 @@ type ContractExecute = Resolved; type ContractExplain = Resolved>; // `aggregate` is optional too (`aggregate?(...)`), read the same way (#17277). type ContractAggregate = Resolved>; -// [#17690] Four more doors. `temporalFilterValue` is synchronous, so it is read +// [commit be5c60291] Four more doors. `temporalFilterValue` is synchronous, so it is read // through `ReturnType` — `Resolved` would answer `never` and pin nothing. type ContractFind = Resolved; type ContractUpsert = Resolved; @@ -349,7 +349,7 @@ describe('SqlDriver declared return types on the five remaining IDataDriver door expect(Number(cell)).toBe(1); }); - // [#17690] The four doors a literal-string census could not see. Both halves + // [commit be5c60291] The four doors a literal-string census could not see. Both halves // each: put any one annotation back and `ContainsAny` flips to `true` while // `Equals` flips to `false`, reding this file twice for that door — verified // by ablating all four, two errors apiece and nothing else. diff --git a/packages/drivers/driver-sql/src/sql-driver-external-remote-name.test.ts b/packages/drivers/driver-sql/src/sql-driver-external-remote-name.test.ts index e8df02f704b..b96b3242dc1 100644 --- a/packages/drivers/driver-sql/src/sql-driver-external-remote-name.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-external-remote-name.test.ts @@ -90,7 +90,7 @@ describe('SqlDriver external read path — remoteName resolution (ADR-0015)', () expect(rows).toHaveLength(2); const acme = rows.find((r) => r.name === 'Acme'); - // [#17690] `find()` publishes `Record[]`, so + // [commit be5c60291] `find()` publishes `Record[]`, so // `Array.prototype.find` answers `… | undefined` and the absent arm is // narrowed away before any field is read. Through the old // `Promise` the four reads below compiled against nothing. diff --git a/packages/drivers/driver-sql/src/sql-driver-json-backfill-depth-limit.test.ts b/packages/drivers/driver-sql/src/sql-driver-json-backfill-depth-limit.test.ts index 5144fdd8ce0..9512c6fd5f7 100644 --- a/packages/drivers/driver-sql/src/sql-driver-json-backfill-depth-limit.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-json-backfill-depth-limit.test.ts @@ -2,7 +2,7 @@ /** * [#19912] The local `Field.json` storage backfill - * (`SqlDriver.backfillCanonicalJsonEncoding`, #12380) lets the driver's own + * (`SqlDriver.backfillCanonicalJsonEncoding`, commit 4045b954d) lets the driver's own * codec decide which cell to rewrite. SQL only pre-filters. * * The defect: the backfill was one `UPDATE … set col = json_quote(col) where @@ -92,7 +92,7 @@ afterEach(async () => { for (const dir of dirs.splice(0)) rmSync(dir, { recursive: true, force: true }); }); -/** A cell the way a pre-#12380 door (or any raw writer) left it: bound as-is. */ +/** A cell the way a door before commit 4045b954d (or any raw writer) left it: bound as-is. */ async function plantRaw(d: SqlDriver, id: string, val: unknown): Promise { await d.execute(`insert into "${T}" ("id", "label", "val") values (?, ?, ?)`, [id, id, val as any]); } @@ -116,7 +116,7 @@ describe('[#19912] the local json backfill leaves JSON nested past SQLite\'s dep it('the card\'s reproduction: only `bare` is quoted; the 1001-level array reads as an array after a SECOND backfill', async () => { const d = await makeDriver(); await d.syncSchema(T, SCHEMA); // creates the table: no backfill - await plantRaw(d, 'bare', 'bare'); // the pre-#12380 form of the string 'bare' + await plantRaw(d, 'bare', 'bare'); // the form of the string 'bare' before commit 4045b954d await d.create(T, { id: 'deep', label: 'deep', val: deepArray(1001) }, { bypassTenantAudit: true }); await d.create(T, { id: 'pair', label: 'pair', val: [1, 2] }, { bypassTenantAudit: true }); diff --git a/packages/drivers/driver-sql/src/sql-driver-json-column-operator-refusal.test.ts b/packages/drivers/driver-sql/src/sql-driver-json-column-operator-refusal.test.ts index 3e83f6d78dd..7fb0f054aea 100644 --- a/packages/drivers/driver-sql/src/sql-driver-json-column-operator-refusal.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-json-column-operator-refusal.test.ts @@ -170,7 +170,7 @@ const REFUSED: ReadonlyArray = [ * predicates, which ask about the column's presence — a well-formed question * whatever the column holds. * - * ⚠️ [#17590] The sentence that stood here said the `LIKE` family "matches the + * ⚠️ [commit e04a0aff2] The sentence that stood here said the `LIKE` family "matches the * serialization as text (which is how `$contains` works at all)". That was an * accurate description of a mechanism that has since been replaced: it made * `$contains` a SUBSTRING test over the serialization — right across element diff --git a/packages/drivers/driver-sql/src/sql-driver-keyed-text-mysql.test.ts b/packages/drivers/driver-sql/src/sql-driver-keyed-text-mysql.test.ts index d00cc3d52d0..df3f2e8c250 100644 --- a/packages/drivers/driver-sql/src/sql-driver-keyed-text-mysql.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-keyed-text-mysql.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * #11374 — a text-family field that a declared index KEYS ON. + * Commit d0e3a885b — a text-family field that a declared index KEYS ON. * * ## The defect this pins, and why nothing caught it * @@ -101,7 +101,7 @@ const tooWideObject = () => ({ * ⚠️ Why this file grew these: the two objects above declare UNIQUE indexes, and * #11627 made a UNIQUE index over an unkeyable column expressible — it is now * carried on a hash-shadow column instead of being refused. That is a ruled - * behaviour change (maintainer, 2026-08-24 on #11374), so the assertions that + * behaviour change (maintainer, 2026-08-24; landed as commit 107bb4ba4), so the assertions that * pinned "unkeyable ⇒ refused" for those objects were pinning a branch that no * longer exists for them, and were rewritten rather than deleted or silenced. * The refusal itself is NOT gone — it is the disposition for a NON-UNIQUE diff --git a/packages/drivers/driver-sql/src/sql-driver-query-signature.test.ts b/packages/drivers/driver-sql/src/sql-driver-query-signature.test.ts index b7c249aba12..715cb54babd 100644 --- a/packages/drivers/driver-sql/src/sql-driver-query-signature.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-query-signature.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * The driver side of #5181's `DriverQuery` narrowing (#6075). + * The driver side of #5181's `DriverQuery` narrowing (commit d367f03d6). * * `packages/spec/src/contracts/data-driver.test.ts` already pins the CONTRACT: * `IDataDriver`'s six query-taking methods declare `DriverQuery`. That pin says @@ -9,7 +9,7 @@ * compared bivariantly, so an implementation declaring the wider `QueryAST` * satisfies the narrower contract and every gate stays green. That is exactly * how five drivers kept a stale signature through a full `pnpm typecheck` - * (125/125) after #6076 merged. + * (125/125) after commit 6513c1749 landed. * * The cost of the gap was a dormant lie rather than a live defect: a caller is * now free to omit `object`, so an implementation declaring `query: QueryAST` @@ -83,7 +83,7 @@ describe('SqlDriver query signatures follow the DriverQuery contract (#6075)', ( }); /** - * The two SQL-driver-OWN query doors — not on `IDataDriver`, so #5181/#6075 + * The two SQL-driver-OWN query doors — not on `IDataDriver`, so #5181 and commit d367f03d6 * never reached them and both kept `query: any` (#6212). * * `any` on a query parameter is not "unchecked object name"; it is every check diff --git a/packages/drivers/driver-sql/src/sql-driver-shard-tenancy-opt-out.test.ts b/packages/drivers/driver-sql/src/sql-driver-shard-tenancy-opt-out.test.ts index bf86f77a3b5..1c19ff7658c 100644 --- a/packages/drivers/driver-sql/src/sql-driver-shard-tenancy-opt-out.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-shard-tenancy-opt-out.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * [#16729] The shard path resolves its tenant column through the STICKY + * [commit 0f38ab084] The shard path resolves its tenant column through the STICKY * opt-out record, so a rotation-declared platform-global object scopes its * declared `unique: 'organization'` index identically on the base table and on * every shard. diff --git a/packages/drivers/driver-sql/src/sql-driver-tenant-scope.test.ts b/packages/drivers/driver-sql/src/sql-driver-tenant-scope.test.ts index f16af708162..2dd289621e4 100644 --- a/packages/drivers/driver-sql/src/sql-driver-tenant-scope.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-tenant-scope.test.ts @@ -516,7 +516,7 @@ describe('SqlDriver tenant scope (organization_id)', () => { }); /** - * [#8778] `tenancy.organizationField` is STAMP-ONLY — the driver's tenant + * [commit 7901b2dd2] `tenancy.organizationField` is STAMP-ONLY — the driver's tenant * scoping must be blind to it. The key exists for the audit writer alone * (which column says who a row is ABOUT); the wall keeps answering a different * question (what the object is WALLED by) from `enabled` / `tenantField` / @@ -585,7 +585,7 @@ describe('tenancy.organizationField is read-neutral in the driver (#8778)', () = it('the unwalled credential-table shape stays unwalled: reads unscoped, inserts uninjected', async () => { // Pre-#8287-shaped row: no organization at all. Under any wall reading // `active_organization_id` or resurrecting a scope, this row vanishes for - // its own owner — the defect #8287 removed and #8778 must not reintroduce. + // its own owner — the defect #8287 removed and commit 7901b2dd2's stamp-only key must not reintroduce. await driver.create('api_key_like', { id: 'k0', name: 'legacy', revoked: false }); await driver.create('api_key_like', { id: 'k1', name: 'ci', active_organization_id: 'org_b', revoked: false }); diff --git a/packages/drivers/driver-sql/src/sql-driver-timestamps-without-ddl.test.ts b/packages/drivers/driver-sql/src/sql-driver-timestamps-without-ddl.test.ts index e73ce4ec85c..b0126fb81a7 100644 --- a/packages/drivers/driver-sql/src/sql-driver-timestamps-without-ddl.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-timestamps-without-ddl.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * [#11067] `updated_at` must advance on a deployment that never runs the + * [commit 479fba50d] `updated_at` must advance on a deployment that never runs the * driver's DDL — and must keep working on a hand-migrated table that genuinely * has no `updated_at` column. * diff --git a/packages/drivers/driver-sql/src/sql-driver-ttl-onlywhen-null-dialects.test.ts b/packages/drivers/driver-sql/src/sql-driver-ttl-onlywhen-null-dialects.test.ts index 67d934cf6b0..c8f6615ca0f 100644 --- a/packages/drivers/driver-sql/src/sql-driver-ttl-onlywhen-null-dialects.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-ttl-onlywhen-null-dialects.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * [#10165] Dialect-compile measurement for `{ $null: true }` inside a + * [commit 801296050] Dialect-compile measurement for `{ $null: true }` inside a * lifecycle `onlyWhen` reap scope — the confidence gap named on the card: * the prior round (#7826) covered schema parsing and a fake engine, but not * the REAL driver compile path. @@ -27,7 +27,7 @@ * `$null: false` must render `is not null`, and the TTL cutoff must * remain a bound comparison on the same statement. * - * 3. **[#10836] Live pg + mysql**: the same where EXECUTED against real + * 3. **[commit 7ab286e44] Live pg + mysql**: the same where EXECUTED against real * servers, through `PG_CELL` / `MYSQL_CELL`. Measurement 2 proves the SQL * *text*; it cannot prove the *server* returns those rows, and it never * exercises the ttl cutoff against the column type each dialect actually @@ -170,7 +170,7 @@ describe('ttl onlyWhen {$null} — real driver compile path, three dialects (#10 }); // ───────────────────────────────────────────────────────────────── -// [#10836] MEASUREMENT 3 — the same where, EXECUTED on live servers +// [commit 7ab286e44] MEASUREMENT 3 — the same where, EXECUTED on live servers // ───────────────────────────────────────────────────────────────── /** Table this file owns on the live servers. The SCHEMA it lands in is derived diff --git a/packages/drivers/driver-sql/src/sql-driver-unique-violation-predicate.test.ts b/packages/drivers/driver-sql/src/sql-driver-unique-violation-predicate.test.ts index 17768d3632b..2514a67a84b 100644 --- a/packages/drivers/driver-sql/src/sql-driver-unique-violation-predicate.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-unique-violation-predicate.test.ts @@ -6,7 +6,7 @@ * (the constraint is logged as not-enforced and reported by the ADR-0120 D4 * drift pre-flight) instead of taking the process down. * - * ⚠️ Since #14902 that branch has two arms, not one: the NULL-safe organization + * ⚠️ Since commit 61821e54c that branch has two arms, not one: the NULL-safe organization * composite AND the plain unique (`tenancy: { enabled: false }`, or an explicit * `unique: 'global'`), which used to fall through to `throw e`. The * discriminator below is what BOTH arms judge with, so its blind spots are now @@ -235,13 +235,13 @@ describe('syncDeclaredIndexes unique-violation discriminator (#6543)', () => { // ── The site's own business logic, untouched by the migration ───────────── /** - * ⚠️ RETIRED PIN, re-authored — #14902. + * ⚠️ RETIRED PIN, re-authored — commit 61821e54c. * * This block used to assert the opposite: 「leaves the `nullSafe.size > 0` * guard intact — a plain unique still fails the sync」, on the reasoning that * absorbing it 「would silently ship an unenforced constraint **the drift * pre-flight was never told about**」. That reasoning was right, and its - * premise is exactly what #14902 removed: the ADR-0120 D4 pre-flight now + * premise is exactly what commit 61821e54c removed: the ADR-0120 D4 pre-flight now * probes the plain unique too, so the drift pass IS told, and `os migrate * plan` reports the blocked op `destructive` with the offending rows instead * of calling it `safe`. @@ -301,7 +301,7 @@ describe('syncDeclaredIndexes unique-violation discriminator (#6543)', () => { // A non-unique index exists for an ACCESS PATH — it cannot raise a // uniqueness violation, so a failure that reads as one while creating it is - // something else entirely. #14902's `unique` limb is what keeps that + // something else entirely. Commit 61821e54c's `unique` limb is what keeps that // failing loudly instead of being logged away as an unenforced constraint // that was never declared in the first place. const rejected: any = await sync([NON_UNIQUE_INDEX]).then( diff --git a/packages/drivers/driver-sql/src/sql-driver-update-declared-null.test.ts b/packages/drivers/driver-sql/src/sql-driver-update-declared-null.test.ts index e0257a673dc..1bdf1b4727d 100644 --- a/packages/drivers/driver-sql/src/sql-driver-update-declared-null.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-update-declared-null.test.ts @@ -1,6 +1,6 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. // -// #14438 — `SqlDriver.update()`'s declared return type is the contract's, not +// Commit 2200f8ec8 — `SqlDriver.update()`'s declared return type is the contract's, not // `any`, and it carries the not-found arm. // // `SqlDriver.update()` has always answered a missing id with `null` @@ -8,7 +8,7 @@ // the rotation path's `return null` once every shard has been probed), while // its signature was written out as an EXPLICIT `Promise`. // `IDataDriver.update()` declares `Promise | null>` -// (the arm landed with #13878 / PR #14434 under the maintainer's 2026-09-01 +// (the arm landed with #13878 / commit 93940d492 under the maintainer's 2026-09-01 // ruling), and an explicit `any` satisfies that structurally — so `tsc` said // nothing, the published `.d.ts` of `@objectstack/driver-sql` read // `Promise`, and no caller holding a `SqlDriver` (or a `SqliteWasmDriver`, @@ -51,8 +51,8 @@ import { SqlDriver } from './index.js'; type IsAny = 0 extends 1 & T ? true : false; /** - * [#17879] MEASURED — `sqlUpdateIsAny` below is a PHANTOM half against a - * NESTED regression, and `ContainsAny` (#17876) does NOT close it. On disk, + * [commit eb9334915] MEASURED — `sqlUpdateIsAny` below is a PHANTOM half against a + * NESTED regression, and `ContainsAny` (commit be5c60291) does NOT close it. On disk, * reverting only this door in `sql-driver.ts`: * * door resolves to `Record | null` @@ -66,7 +66,7 @@ type IsAny = 0 extends 1 & T ? true : false; * `Record | null` answers `boolean` (`true` for the record arm, * `false` for `null`) — and `const leg: boolean = false` compiles. Every * door carrying the not-found arm has this shape. No swap was made; the two - * measured repairs are in the #17879 report. + * measured repairs are in commit eb9334915's message. */ /** Exact (mutual, non-`any`) type equality. */ type Equals = (() => T extends A ? 1 : 2) extends (() => T extends B ? 1 : 2) ? true : false; diff --git a/packages/drivers/driver-sql/src/sql-driver-value-roundtrip-conformance.test.ts b/packages/drivers/driver-sql/src/sql-driver-value-roundtrip-conformance.test.ts index 5e01b23c74f..5833c3704d2 100644 --- a/packages/drivers/driver-sql/src/sql-driver-value-roundtrip-conformance.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-value-roundtrip-conformance.test.ts @@ -7,15 +7,15 @@ * * ## Why this file is matrix-routed and not SQLite-only * - * The defect that produced the table (#12380) was a *dialect* defect: SQLite's + * The defect that produced the table (fixed in commit 4045b954d) was a *dialect* defect: SQLite's * `Field.json` codec was not injective while Postgres and MySQL were faithful, * and the difference was invisible to a suite pinned to one client. So the * dialect axis is the whole point here, not a formality — this cell is the one * `MATRIXED` (#12136) exists to make real, and a SQLite-only version of this - * file would restate exactly the coverage that let #12380 survive. + * file would restate exactly the coverage that let the defect commit 4045b954d fixed survive. * * PG and MySQL are also the **regression control**: they were faithful before - * #12380's fix and must stay faithful after it. If a future change to the codec + * commit 4045b954d and must stay faithful after it. If a future change to the codec * moves the defect onto them instead of closing it, it goes red here first. * * ## Its relationship to `sql-driver-12380-json-roundtrip.test.ts` @@ -82,7 +82,7 @@ function declareRoundTrip(cell: DialectCell): void { } as DriverQuery)) as any[]; expect(rows).toHaveLength(1); const read = rows[0][c.column]; - // The type pin comes first: `'123'` read back as `123` is #12380's + // The type pin comes first: `'123'` read back as `123` is commit 4045b954d's // exact before-state, and it survives every value-only comparison. expect(typeof read, `typeof for ${c.name}`).toBe(typeof c.wrote); expect(read, `value for ${c.name}`).toStrictEqual(c.wrote); diff --git a/packages/drivers/driver-sql/src/sql-driver-window-function-output.test.ts b/packages/drivers/driver-sql/src/sql-driver-window-function-output.test.ts index e84553d1def..e1bc6cf88f9 100644 --- a/packages/drivers/driver-sql/src/sql-driver-window-function-output.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver-window-function-output.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * Rows leaving `findWithWindowFunctions()` (#16609). + * Rows leaving `findWithWindowFunctions()` (commit 78bc4ad58). * * It was the last read door that returned `await builder` with NO presentation: * no `formatOutput` (which every `find()` / `findOne()` row gets) and no @@ -188,15 +188,15 @@ describe('rows leaving findWithWindowFunctions() (#16609)', () => { expect(rows.map((r: any) => Number(r.rn))).toEqual([1, 2]); }); - // ── THE COLLISION RULING (#16609) ────────────────────────────────────── + // ── THE COLLISION RULING (commit 78bc4ad58) ────────────────────────────────────── // // An alias may be spelled the same as a declared field. SQL decides that // one before the driver sees it: `select *` plus ` as ok` projects // two columns named `ok`, and the row object keeps the LAST — so the // COMPUTED value wins the key and the declared column's value is not in the - // row at all. That was already true before #16609 and is unchanged by it. + // row at all. That was already true before commit 78bc4ad58 and is unchanged by it. // - // What #16609 rules is the second half: the winning value stays RAW. It is + // What commit 78bc4ad58 rules is the second half: the winning value stays RAW. It is // a computed number, so no declared field's presentation rule may touch it // — applying the `Field.boolean` rule here would fold ROW_NUMBER 1 and 2 // into `true` and `true` and destroy the value the caller asked for. diff --git a/packages/drivers/driver-sql/src/sql-driver.test.ts b/packages/drivers/driver-sql/src/sql-driver.test.ts index 9bb52f77aec..e6e1a75fd17 100644 --- a/packages/drivers/driver-sql/src/sql-driver.test.ts +++ b/packages/drivers/driver-sql/src/sql-driver.test.ts @@ -100,7 +100,7 @@ describe('SqlDriver (SQLite Integration)', () => { it('should update an object', async () => { const [bob] = await driver.find('users', { where: { name: 'Bob' } }); - // [#17690] `find()` publishes the contract's `Record[]` + // [commit be5c60291] `find()` publishes the contract's `Record[]` // now, so an id read off a row is narrowed before it is passed as one. assert( typeof bob.id === 'string' || typeof bob.id === 'number', diff --git a/packages/drivers/driver-sql/src/sql-driver.ts b/packages/drivers/driver-sql/src/sql-driver.ts index 64955b94308..cd55a4ca44f 100644 --- a/packages/drivers/driver-sql/src/sql-driver.ts +++ b/packages/drivers/driver-sql/src/sql-driver.ts @@ -333,7 +333,7 @@ const JSON_COLUMN_TYPES = new Set([ * * ⚠️ NOT a taste: it is the width `os generate migration --format sql` already * emits for the family (`VARCHAR(2048)` in `packages/cli/src/commands/ - * generate.ts`), and the maintainer ruling on #15041 is that the GENERATOR + * generate.ts`), and the maintainer ruling in ADR-0104's 2026-09-05 addendum is that the GENERATOR * states the ruled end-state and does not move — the driver is the side that * moves to meet it. A driver that created knex's default `varchar(255)` here * would open a fresh divergence between the two producers of the same column @@ -996,7 +996,7 @@ function unresolvableFilterColumnError(object: string, column: string | null): E * platform accepts — expressly outside this ruling. * * That predicate already follows `error.cause` up to four levels, with its own - * pins (`packages/types/src/driver-error-classification.test.ts` — #13279 + * pins (`packages/types/src/driver-error-classification.test.ts` — commit 6a180e42d * moved it there from `metadata/src/utils/schema-sync-errors.test.ts` — * "follows an error wrapped as `cause`"), because "drivers commonly re-throw * with the original attached as `cause`" is a case it was built for. So the @@ -1018,7 +1018,7 @@ function unresolvableFilterColumnError(object: string, column: string | null): E * object (ADR-0015) that is not the name in the statement: {@link * SqlDriver.getBuilder} targets `external.remoteName`. A genuinely absent * remote therefore raised a phrase naming `legacy_orders` against a caller - * naming `crm_order`, and the #13324 comparison read a real missing table as + * naming `crm_order`, and commit 4cda78c9b's comparison read a real missing table as * "about something else" — loud, for the one case the licence exists for. * Nothing at a call site can fold that away: the mapping lives on this * instance. @@ -1182,7 +1182,7 @@ function insertedRowsNotReadBackError(object: string, missingIds: unknown[], wri /** * [#9354] How long a widening ALTER waits for a metadata lock, in seconds. * - * Named for the seam it arrived on; since #9542 it governs BOTH callers of + * Named for the seam it arrived on; since commit 8bbf45947 it governs BOTH callers of * {@link SqlDriver.runWideningAlters} — the deferred-DDL flush and boot * schema-sync. * @@ -1217,7 +1217,7 @@ function insertedRowsNotReadBackError(object: string, missingIds: unknown[], wri * re-run once the blocker is gone — against an unbounded hang as the cost of * one that never fires. * - * # The same number on boot (#9542) + * # The same number on boot (commit 8bbf45947) * * Boot schema-sync arms this bound too, at the same value rather than a longer * one. Everything above is reasoning about how long a legitimate metadata-lock @@ -5239,7 +5239,7 @@ interface RowWidthContribution { * Render the duplicate groups a unique pre-flight probe found, for an operator * message: at most five groups, then a count of the rest. * - * Module-local on purpose (#14902). The two sites that report a blocked unique + * Module-local on purpose (commit 61821e54c). The two sites that report a blocked unique * — the drift entry and the boot-time durability log — must name the SAME rows * in the SAME shape, and a second hand-rolled `.slice(0, 5).join('; ')` is * exactly how the plain and the NULL-safe path drifted apart in the first @@ -5439,7 +5439,7 @@ export class SqlDriver implements IDataDriver { * registry: its readers present the stored form (SQLite INTEGER 0/1, MySQL * `tinyint(1)`) as one JS boolean. * - * ⚠️ [#17586] SCALAR only. A MULTI-VALUED boolean/toggle is deliberately + * ⚠️ [commit d46deba19] SCALAR only. A MULTI-VALUED boolean/toggle is deliberately * NOT here — the same carve-out {@link mediaFields} states just above, and * the one `numericFields` / `numericValueFields` carry in both fills. Its * value is a LIST of booleans in a JSON column, and "present this as ONE @@ -5469,7 +5469,7 @@ export class SqlDriver implements IDataDriver { * 3. `formatOutput`'s row pass — the [#11782] coercion this registry exists * for, and where the collapse was filed; * 4. {@link isNonTextColumn} — already carves multi-valued out AT THE READER - * (`&& !this.isJsonColumn(...)`, #17343), so its answer is UNCHANGED by + * (`&& !this.isJsonColumn(...)`, commit 82cb69fed), so its answer is UNCHANGED by * the narrowing rather than merely unharmed by it. */ protected booleanFields: Record = {}; @@ -5560,7 +5560,7 @@ export class SqlDriver implements IDataDriver { protected columnFieldByObject: Record> = {}; protected tablesWithTimestamps: Set = new Set(); /** - * [#11067] What is known about `updated_at` on a table this driver was told + * [commit 479fba50d] What is known about `updated_at` on a table this driver was told * about WITHOUT running DDL against it. * * ## Why {@link tablesWithTimestamps} could not answer this @@ -7114,7 +7114,7 @@ export class SqlDriver implements IDataDriver { // CRUD — IDataDriver core // =================================== - // [#17690] The return is the contract's own type. It was `Promise`, and the + // [commit be5c60291] The return is the contract's own type. It was `Promise`, and the // reason #15267's census never named this door is worth carrying: that census // matched the literal string `Promise`, and here the `any` is NESTED // inside a wider type, so the characters were not there to match. The door @@ -8298,7 +8298,7 @@ export class SqlDriver implements IDataDriver { * `updated_at` — as opposed to merely presuming it from the declared shape? * * The stronger of the two answers {@link stampsUpdatedAt} folds together, and - * the only one the INSERT door may act on. `presumed` (#11067) is deliberately + * the only one the INSERT door may act on. `presumed` (commit 479fba50d) is deliberately * excluded: that state exists so an UPDATE can speculate and then RECOVER * ({@link updateWithPresumedTimestamp}), and the upsert door has no such * recovery — a presumption that turned out wrong there would name a column @@ -8338,7 +8338,7 @@ export class SqlDriver implements IDataDriver { * ## Only an OBSERVED column is stamped * * {@link observedUpdatedAtColumn}, not {@link stampsUpdatedAt} — see there for - * why the #11067 presumption stops at the UPDATE door. + * why the commit 479fba50d presumption stops at the UPDATE door. */ protected stampUpsertUpdatedAt(object: string, formatted: Record): void { if (formatted.updated_at !== undefined && formatted.updated_at !== null) return; @@ -8411,7 +8411,7 @@ export class SqlDriver implements IDataDriver { } /** - * [#11067] Should an UPDATE to `object` refresh `updated_at`? + * [commit 479fba50d] Should an UPDATE to `object` refresh `updated_at`? * * `true` on the DDL-observed tables exactly as before, and now also on a * table whose declared shape says it has the column — see @@ -8426,7 +8426,7 @@ export class SqlDriver implements IDataDriver { } /** - * [#11067] Is that stamp still a PRESUMPTION — i.e. must this write carry the + * [commit 479fba50d] Is that stamp still a PRESUMPTION — i.e. must this write carry the * fallback? * * Only for a table in the `presumed` state and outside `tablesWithTimestamps`. @@ -8438,7 +8438,7 @@ export class SqlDriver implements IDataDriver { } /** - * [#11067] Ask the DATABASE whether `object`'s physical table carries + * [commit 479fba50d] Ask the DATABASE whether `object`'s physical table carries * `updated_at`. `null` when the question could not be answered. * * ⛔ This is deliberately not a read of the failure's message. Every dialect @@ -8482,7 +8482,7 @@ export class SqlDriver implements IDataDriver { } /** - * [#11067] Issue an UPDATE whose `updated_at` stamp is a PRESUMPTION, and + * [commit 479fba50d] Issue an UPDATE whose `updated_at` stamp is a PRESUMPTION, and * recover if the column turns out not to be there. * * ## Why this exists rather than option 1 on its own @@ -8583,7 +8583,7 @@ export class SqlDriver implements IDataDriver { } /** - * [#14438] Declared as `IDataDriver.update()` declares it: the updated + * [commit 2200f8ec8] Declared as `IDataDriver.update()` declares it: the updated * record, or `null` when no row carries `id` — the un-rotated path answers * `formatOutput(...) || null` and the rotation path answers `null` once * every shard has been probed. The annotation used to be an explicit @@ -9259,7 +9259,7 @@ export class SqlDriver implements IDataDriver { throw refuseUpsertConflictOutsideWrittenOrganization(object, mergeKeys); } - // [#17690] The return is the contract's own type. It was `Promise>`, and the + // [commit be5c60291] The return is the contract's own type. It was `Promise>`, and the // reason #15267's census never named this door is worth carrying: that census // matched the literal string `Promise`, and here the `any` is NESTED // inside a wider type, so the characters were not there to match. The door @@ -9877,7 +9877,7 @@ export class SqlDriver implements IDataDriver { * usable — the rollback decision for the caller's OWN work stays the * caller's, which is the half of `upsert`'s reasoning that does transfer. */ - // [#17690] The return is the contract's own type. It was `Promise[]>`, and the + // [commit be5c60291] The return is the contract's own type. It was `Promise[]>`, and the // reason #15267's census never named this door is worth carrying: that census // matched the literal string `Promise`, and here the `any` is NESTED // inside a wider type, so the characters were not there to match. The door @@ -9926,7 +9926,7 @@ export class SqlDriver implements IDataDriver { * `updated_at` (list-view sorts, delta/incremental sync, cache invalidation, * audit), without erroring. * - * The decision is #11067's, unchanged and shared rather than re-derived: + * The decision is commit 479fba50d's, unchanged and shared rather than re-derived: * {@link stampsUpdatedAt} answers whether to stamp, {@link keepSuppliedUpdatedAt} * honours an opt-in historical import, and {@link updateWithPresumedTimestamp} * carries the speculative case so a hand-migrated table that genuinely lacks @@ -10027,7 +10027,7 @@ export class SqlDriver implements IDataDriver { options?: DriverOptions, ): Promise | null> { const formatted = this.applyWriteColumnMap(object, this.formatInput(object, data)); - // [#11067] One definition of the decision, shared with {@link update}. No + // [commit 479fba50d] One definition of the decision, shared with {@link update}. No // fallback is threaded here, and that is a property of the path rather than // an omission: `rotationShardsOf` returns shards only once `ensureRotation` // has run, and `initObjects` records the stronger `tablesWithTimestamps` @@ -10460,7 +10460,7 @@ export class SqlDriver implements IDataDriver { * `any` here was not "the object name goes unchecked", it was every check off * on the members this body READS: `where`'s filter dialect, `groupBy`'s node * union, `aggregations`' node shape. #5181 narrowed the six methods it swept - * and #6075 followed through on five drivers; this door was reached by + * and commit d367f03d6 followed through on five drivers; this door was reached by * neither. * * [#17277] The sentence that used to close the paragraph above — @@ -11035,7 +11035,7 @@ export class SqlDriver implements IDataDriver { } builder.distinct(field); - // [#17639] The THIRD read door joins the other two. `find()` and `count()` + // [commit 7c2c5aedd] The THIRD read door joins the other two. `find()` and `count()` // have carried the terminal envelope since #8931 and `aggregate()` since // #11455; this one still executed BARE, so any dialect refusal the // statement raised left the driver as the backend's own object — a raw @@ -11057,7 +11057,7 @@ export class SqlDriver implements IDataDriver { // values of this column — was logged as an UNHANDLED server fault. // // ⛔ NOT a decision about whether a JSON column should ANSWER a distinct - // read; that question is #17590's, on the same columns. Whatever this door + // read; that question belonged to the card commit e04a0aff2 closed, on the same columns. Whatever this door // ends up doing, it must not leak the backend's own object — so the // envelope is the half that lands here, exactly as #11455 landed it for // `aggregate()` while its own answer question was still open. @@ -11068,11 +11068,11 @@ export class SqlDriver implements IDataDriver { // `sql-driver-17639-distinct-fault-envelope.test.ts` asserts that on a // route (a table that was never provisioned) that has no JSON column in it. // - // ⛔ And still NO BLANKET `isUnresolvableColumnError` arm — #17857 closed - // the gap #17639 left FILED, and it closed it the way #11541 closed the + // ⛔ And still NO BLANKET `isUnresolvableColumnError` arm — commit 9ccc4179e closed + // the gap commit 7c2c5aedd left FILED, and it closed it the way #11541 closed the // `aggregate()` one: by ATTRIBUTING the column to a clause of the caller's // own request first. A blanket arm remains forbidden for the reason stated - // here since #17639 — it would tell the author of `distinct(o, 'nosuchcol')`, + // here since commit 7c2c5aedd — it would tell the author of `distinct(o, 'nosuchcol')`, // who passed no filter at all, that their FILTER was wrong — and // {@link SqlDriver.distinctBackendFault} is what makes that claim // unnecessary rather than what makes it safe. @@ -11101,7 +11101,7 @@ export class SqlDriver implements IDataDriver { } /** - * [#17857] Which envelope a dialect error leaving {@link SqlDriver.distinct} + * [commit 9ccc4179e] Which envelope a dialect error leaving {@link SqlDriver.distinct} * deserves — the #8790 unresolvable-column refusal reaching the LAST read * door, without the attribution #8931 forbids. * @@ -11124,7 +11124,7 @@ export class SqlDriver implements IDataDriver { * distinct(t, 'nosuchcol') => DATABASE_ERROR 500 ← this card * ``` * - * A BLANKET `isUnresolvableColumnError` arm is still refused, and #17639 + * A BLANKET `isUnresolvableColumnError` arm is still refused, and commit 7c2c5aedd * wrote the reason at the door: this door names columns in TWO clauses — the * `field` being listed and the WHERE compiled from `filters` — so the WHERE * refusal's words ("Filter on 'x' names a column …") would tell the author of @@ -11148,7 +11148,7 @@ export class SqlDriver implements IDataDriver { * {@link SqlDriver.unresolvableFilterColumnRefusal} applies verbatim; * 3. {@link unresolvableColumnNameOf} answers `null` ⇒ the wording parsed by * nothing. With no name there is no request lookup, so NO attribution is - * supportable — the #17639 terminal envelope stands unchanged. ⛔ Reading + * supportable — commit 7c2c5aedd's terminal envelope stands unchanged. ⛔ Reading * `null` as license for the WHERE arm would attribute a clause on no * evidence, which is arm 2's own justification inverted. * @@ -11203,7 +11203,7 @@ export class SqlDriver implements IDataDriver { } /** - * [#17857] Compose the refusal for the LISTED field of a distinct read whose + * [commit 9ccc4179e] Compose the refusal for the LISTED field of a distinct read whose * column the backend could not resolve, writing the dialect's own message to * the SERVER LOG on the way — the same statement-to-log, name-to-caller split * {@link SqlDriver.unresolvableFilterColumnRefusal} performs for the WHERE @@ -11244,7 +11244,7 @@ export class SqlDriver implements IDataDriver { */ protected unresolvableDistinctColumnRefusal(object: string, column: string, error: unknown): Error { const detail = (error as { message?: unknown } | null | undefined)?.message; - // The withholding rule is #7929's and this door's arm is #17857's — ids kept + // The withholding rule is #7929's and this door's arm is commit 9ccc4179e's — ids kept // in the comment, never in the emitted string: a log line reaches operators // and generated surfaces, where `#NNNN` resolves against nothing // (`pnpm check:doc-authoring`). @@ -11328,7 +11328,7 @@ export class SqlDriver implements IDataDriver { const rows = await builder; if (!Array.isArray(rows)) return rows; - // [#16609] The last read door that returned STORAGE forms. It handed back + // [commit 78bc4ad58] The last read door that returned STORAGE forms. It handed back // `await builder` with no presentation at all, while `find()`/`findOne()` // run every row through `formatOutput` and `aggregate()`/`distinct()` got // `presentReadValue` under #3797/#3849 — so one driver answered one @@ -11771,10 +11771,10 @@ export class SqlDriver implements IDataDriver { /** Create/column-sync one physical shard table (mirrors the managed-table * branch of {@link initObjects}, scoped to a shard). * - * #16711: `indexes` and `tenancy` are DECLARED here, on {@link ensureRotation} + * Commit 7862fb711: `indexes` and `tenancy` are DECLARED here, on {@link ensureRotation} * and on {@link rotateShards}, because this leaf reads both off the object the * public entry point was handed — a shard carries the base table's declared - * indexes (#11374) and must scope a `unique: 'organization'` index the same + * indexes (commit d0e3a885b) and must scope a `unique: 'organization'` index the same * way on every shard (ADR-0120 D1). Declaring them only here would leave the * two links above still narrowing the same value, so a caller spelling * `indexes` in a fresh literal to `rotateShards` would still be refused by a @@ -11797,7 +11797,7 @@ export class SqlDriver implements IDataDriver { // ahead of either. this.warnBuiltinColumnCollisions(shardName, obj.fields, builtinColumns, 'shard'); const exists = await this.knex.schema.hasTable(shardName); - // #11374: a shard carries the base table's declared indexes (below), so its + // Commit d0e3a885b: a shard carries the base table's declared indexes (below), so its // columns need the same keyable-text decision the managed path makes. const keyedColumns = indexedKeyColumns({ table: shardName, @@ -11842,7 +11842,7 @@ export class SqlDriver implements IDataDriver { // (ADR-0120 D1) must scope identically on every shard of the base table, // so the answer has to come from the base. // - // [#16729] Keyed by `baseTable`, and through the RECORDING resolver, not + // [commit 0f38ab084] Keyed by `baseTable`, and through the RECORDING resolver, not // the bare `computeTenantField`. The bare one reads this call's schema // alone, so a shard synced from a partial re-registration — one carrying // no `tenancy` block — fell through to the implicit `organization_id` @@ -12024,7 +12024,7 @@ export class SqlDriver implements IDataDriver { // Unconditional, on BOTH arms — see {@link mediaFields}. The read-side // legacy-encoding repair runs on a deployment that has not moved too. if (!isMultiValuedColumn(type, field) && FILE_REFERENCE_TYPES.has(type)) mediaCols.push(name); - // [#17586] SCALAR only — the house spelling its three neighbours in + // [commit d46deba19] SCALAR only — the house spelling its three neighbours in // this block already carry, and this line was the single omission. See // {@link booleanFields}: every reader of this registry presents its // entry as ONE JS boolean, which for a multi-valued (JSON) column @@ -12128,7 +12128,7 @@ export class SqlDriver implements IDataDriver { // `toggle` shares boolean storage/affinity, so it needs the same // read coercion (stored 1/0 → JS true/false) or it leaks back as a // number/string instead of a boolean (#field-zoo). - // [#17586] SCALAR only, like the three neighbours below: a + // [commit d46deba19] SCALAR only, like the three neighbours below: a // `multiple: true` boolean/toggle is a JSON column, and the read // coercion this registry exists for presents ONE JS boolean — which // collapses the parsed array to `true` whatever it holds. See @@ -12180,7 +12180,7 @@ export class SqlDriver implements IDataDriver { this.fractionalNumericFields[tableName] = fractionalCols; this.autoNumberFields[tableName] = autoNumberCols; this.tenantFieldByTable[tableName] = tenantField; - // [#11067] The declared shape's answer to "does this table carry + // [commit 479fba50d] The declared shape's answer to "does this table carry // `updated_at`?", installed here because here is the one place a managed // object reaches the driver on EVERY boot posture — `initObjects` calls // this first, and a `skipSchemaSync` boot calls it and stops. Presumed @@ -12233,7 +12233,7 @@ export class SqlDriver implements IDataDriver { * it affordable on exactly the cold-start-sensitive boots `skipSchemaSync` * exists to protect: it costs one pass over the object list, in memory. * - * It is the same ruling #7737/#10629 already made for FEDERATED objects — + * It is the same ruling #7737 already made for FEDERATED objects (commit 199ec4712) — * `OS_SKIP_SCHEMA_SYNC` is about DDL, and a binding that is DDL-free must * not ride on it — extended to the managed ones. * @@ -12265,7 +12265,7 @@ export class SqlDriver implements IDataDriver { // correctness. `src/sql-driver-16570-init-objects-indexes-param.test.ts` // pins the fresh-literal form so it cannot silently go back. // - // `lifecycle` was the third instance, and the one that made #16711 file the + // `lifecycle` was the third instance, and the one that made the card behind commit 7862fb711 file the // CLASS rather than a fourth single-key card: the loop below reads // `obj.lifecycle?.storage` to decide whether a table is time-sharded, while // the sibling `rotateShards` on this same class had always declared the key. @@ -12372,7 +12372,7 @@ export class SqlDriver implements IDataDriver { // rejects CREATE TABLE with two columns of the same name). const builtinColumns = new Set(['id', 'created_at', 'updated_at']); - // #11374: which columns this object's indexes will KEY ON, resolved before + // Commit d0e3a885b: which columns this object's indexes will KEY ON, resolved before // any DDL runs. `createColumn` needs it to decide whether a bounded text // field takes `varchar(maxLength)` (keyable) or TEXT — a decision that is // only makeable at CREATE time, since no dialect turns a TEXT column into @@ -12488,7 +12488,7 @@ export class SqlDriver implements IDataDriver { await this.backfillCanonicalDatetimes(tableName, exists); // #3994: the `Field.time` twin of the line above. await this.backfillCanonicalTimes(tableName, exists); - // #12380: converge this table's `Field.json` columns on the injective + // Commit 4045b954d: converge this table's `Field.json` columns on the injective // JSON-text storage form the rewritten `formatInput` now writes. await this.backfillCanonicalJsonEncoding(tableName, exists); // #3942: the MySQL twin — widen legacy `TIMESTAMP` columns to `DATETIME(3)`. @@ -12670,7 +12670,7 @@ export class SqlDriver implements IDataDriver { /** * Converge one table's `Field.json` columns on the injective JSON-text - * storage form (#12380) — the `Field.json` twin of + * storage form (commit 4045b954d) — the `Field.json` twin of * {@link backfillCanonicalDatetimes}, built the same way for the same reasons. * * SQLite only. Postgres and MySQL never had the defect: their half of @@ -13031,7 +13031,7 @@ export class SqlDriver implements IDataDriver { * the lock behaviour of unrelated runtime work. The restore is best-effort: * it must never mask the refusal it runs alongside. * - * # Armed on BOTH callers; only the flush escapes the swallow (#9542) + * # Armed on BOTH callers; only the flush escapes the swallow (commit 8bbf45947) * * The bound is armed unconditionally, because the year-long default is no * better for boot than it is for an operator: a boot blocked on another @@ -13074,7 +13074,7 @@ export class SqlDriver implements IDataDriver { try { await run(s.sql, s.bindings); } catch (err) { - // #9542: the bound is armed on both callers, the ESCAPE is not. + // Commit 8bbf45947: the bound is armed on both callers, the ESCAPE is not. // Off the flush this rethrows the server's own error, which the // widening's catch reports and swallows — boot's policy // unchanged, now reached by a wait that ends. @@ -13216,7 +13216,7 @@ export class SqlDriver implements IDataDriver { * * # The level is `error`, and that is a separate question from the swallow (#9609) * - * Swallow-vs-throw was adjudicated (#9542) and is unchanged: boot must not go + * Swallow-vs-throw was adjudicated (commit 8bbf45947) and is unchanged: boot must not go * down over a migration. `warn`-vs-`error` was never separately decided, and * AGENTS.md → "Degradation log levels" decides it with one question — after * the degradation, does the system still look normal from the outside while @@ -13572,7 +13572,7 @@ export class SqlDriver implements IDataDriver { // #12732: `diffManagedTable`'s varchar-length branch asks `createColumn`'s // own read-only mirror (`varcharColumnChars`) whether it would even build // a varchar for a given field — and for the text family that answer needs - // keyedness (#11374), the same input `initObjects` / `ensureShardTable` + // keyedness (commit d0e3a885b), the same input `initObjects` / `ensureShardTable` // already resolve via `indexedKeyColumns` before any DDL. Resolved here // too so the DIFFER'S expectation, not only the DDL, agrees with keyed // columns. @@ -13658,7 +13658,7 @@ export class SqlDriver implements IDataDriver { * `safe` to blocked with the same row report — the CREATE could only * fail at apply time otherwise, with a raw driver error naming no rows. * - * ⚠️ #14902 — the second bullet covers the PLAIN unique too: an index with no + * ⚠️ Commit 61821e54c — the second bullet covers the PLAIN unique too: an index with no * organization key part at all, reached by an object with * `tenancy: { enabled: false }` or by any explicit `unique: 'global'`. The * `nullSafeColumns.length > 0` guard used to exclude it, so `os migrate plan` @@ -13691,7 +13691,7 @@ export class SqlDriver implements IDataDriver { // keeps its pre-ADR-0120 semantics untouched. if (op.type === 'recreate_index' && !tighten) continue; // …and a PLAIN unique has no tightening shape at all, so only its CREATE - // reaches the probe (#14902). + // reaches the probe (commit 61821e54c). if (!nullSafeKey && op.type !== 'create_index') continue; let duplicates: Array<{ key: string; rows: number }>; @@ -13730,7 +13730,7 @@ export class SqlDriver implements IDataDriver { `UNIQUE is NULL-distinct, so it never constrained rows without an organization): ` + `${report}. The op is BLOCKED: apply re-probes and refuses, and the existing index stays in place ` + `(ADR-0120 D4). Deduplicate the listed rows, then re-run "os migrate plan".` - : // #14902: the plain unique has no #5030 history behind it — nothing + : // Commit 61821e54c: the plain unique has no #5030 history behind it — nothing // ever admitted these rows, the constraint is simply newly declared // over data that does not satisfy it. So the message says what IS // true, and above all withdraws the `safe` claim: this op is not @@ -14129,7 +14129,7 @@ export class SqlDriver implements IDataDriver { // ⚠️ Deliberately HERE and not inside `dropIndexIfExists`. That helper // has two other callers and neither is terminal: `recreate_index` // drops in order to re-create under the SAME name — routing it through - // this cleanup would destroy the survivor #13015's `reusable` branch + // this cleanup would destroy the survivor commit cd1348802's `reusable` branch // exists to re-key in place, turning every rebuild into a full // regeneration of a STORED generated column — and // `replace_unique_index` cannot reach a shadow at all (its legacy @@ -14615,14 +14615,14 @@ export class SqlDriver implements IDataDriver { /** * Record, on each #11627 hash-shadow CARRIER, the declared key its shadow - * actually hashes (#13015). + * actually hashes (commit cd1348802). * * ## Why introspection and not the differ * * A carrier is recognisable by name alone — its sole key column is * {@link SqlDriver.hashShadowColumnFor} of its own index name — but a NAME * cannot say WHAT is hashed, and that difference decides between two - * opposite verdicts. A shadow written since #12998 hashes the declared key + * opposite verdicts. A shadow written since commit df1c75c4b hashes the declared key * with its NULL-safe `COALESCE(organization_id, '__global__')` parts intact * and is HEALTHY; one written before it hashes the RAW columns, so `CONCAT` * yields NULL for every NULL-organization row and the constraint those rows @@ -14818,7 +14818,7 @@ export class SqlDriver implements IDataDriver { * at `error` (a declared constraint is not enforced — the * durability-degradation rule) and surfaces as drift with a row report via * the ADR-0120 D4 pre-flight, instead of failing the whole boot. - * - #14902: a PLAIN unique — no organization key part, i.e. + * - Commit 61821e54c: a PLAIN unique — no organization key part, i.e. * `tenancy: { enabled: false }` or an explicit `unique: 'global'` — over * data that already violates it gets the SAME disposition, where it used to * throw the database's raw error and take the boot down naming no rows and @@ -14905,7 +14905,7 @@ export class SqlDriver implements IDataDriver { // `code` / `errno` / `message` / `cause`; see // `@objectstack/types`' `unique-violation.ts` for why it is the one // name for this question. - // #11374: MySQL's refusal of a TEXT key part names a column in a table + // Commit d0e3a885b: MySQL's refusal of a TEXT key part names a column in a table // that was just created successfully, which reads as an index quirk // rather than what it is — the object is now registered with its // declared uniqueness absent. Re-throw the SAME failure carrying the @@ -14933,7 +14933,7 @@ export class SqlDriver implements IDataDriver { // nothing. Those cases stay refused below, and stay tracked. if (unique) { try { - // #12998: the shadow must hash the DECLARED key, so the NULL-safe + // Commit df1c75c4b: the shadow must hash the DECLARED key, so the NULL-safe // organization key parts (ADR-0120 D3) ride along — without them // the generation expression hashed the RAW columns, `CONCAT` // returned NULL for every NULL-organization row, and the rows the @@ -14951,7 +14951,7 @@ export class SqlDriver implements IDataDriver { continue; } if (nullSafe.size > 0 && isUniqueViolationError(shadowErr)) { - // #12998: the shadow ALTER computes the generated column for the + // Commit df1c75c4b: the shadow ALTER computes the generated column for the // EXISTING rows, so a database that accumulated duplicates under // the NULL-safe key while the constraint was void fails here // with a uniqueness violation — the same data-dependent @@ -14999,7 +14999,7 @@ export class SqlDriver implements IDataDriver { // DIED carrying `ER_BLOB_KEY_WITHOUT_LENGTH` -- a message about // an unkeyable TEXT column, telling the operator to declare a // `maxLength` the field already declares, naming NO rows and NO - // remedy -- while the actual cause was duplicate rows. #14902 + // remedy -- while the actual cause was duplicate rows. Commit 61821e54c // fixed exactly that on the direct arm; the two arms of one // `catch` then disagreed about one question. // @@ -15064,7 +15064,7 @@ export class SqlDriver implements IDataDriver { continue; } if (unique && isUniqueViolationError(e)) { - // #14902 — the PLAIN unique: no organization key part at all, reached + // Commit 61821e54c — the PLAIN unique: no organization key part at all, reached // by `tenancy: { enabled: false }` or by an explicit // `unique: 'global'`. It used to fall through to `throw e`, so the // boot DIED carrying the database's own error, which names the index @@ -15779,7 +15779,7 @@ export class SqlDriver implements IDataDriver { * author declared non-temporal (`applySystemFields` lets the declaration * win; `AUDIT_FIELD_GOVERNANCE` forces only `readonly` / `system`, never * `type`) leaves `find()` as the number it is — routed to the datetime fold, - * both became ISO text at this door and nowhere else (the #16619 contract + * both became ISO text at this door and nowhere else (commit 45cfa1b88's contract * review's finding; `sql-driver-13973-canonical-iso-read-door.test.ts` §D * pins the agreement). * @@ -15842,7 +15842,7 @@ export class SqlDriver implements IDataDriver { * (`16`, `5`, `15`, `Infinity`) and this door as the string; that is * reachable through `create()` / `update()` on the driver's own DDL with an * author-declared non-temporal audit column, not only through a hand-made - * TEXT-affinity column (measured in the #16619 contract review). A decimal + * TEXT-affinity column (measured in the contract review commit 45cfa1b88 records). A decimal * or exponent spelling (`'1700000000000'`, `'1e3'`, `'.5'`) is folded to * INTEGER/REAL by the affinity before it is read back, and a number passes * both presenters untouched, so those agree. The B1 ruling did not decide @@ -16079,7 +16079,7 @@ export class SqlDriver implements IDataDriver { * caller flips its operator, which is exactly the ambiguity an * operator-sensitive rule avoids. */ - // [#17690] The contract declares this hook `unknown`-returning; the class + // [commit be5c60291] The contract declares this hook `unknown`-returning; the class // published a bare `any`, which is the same family as the promise-shaped // doors above and the one member of it that is synchronous. Pinned through // `ReturnType` rather than `Resolved` in @@ -16164,7 +16164,7 @@ export class SqlDriver implements IDataDriver { } /** - * [#14079/#15683/#17343] Is `localField` a column on `table` a text operator + * [#14079/#15683, commit 82cb69fed] Is `localField` a column on `table` a text operator * must not be aimed at — a SCALAR column DECLARED numeric, boolean or * temporal? * @@ -16222,13 +16222,13 @@ export class SqlDriver implements IDataDriver { * - **temporal** [#15683] — HERE. `dateFields` / `datetimeFields` / * `timeFields` serve the read-presentation seam, which DOES apply to a * multi-valued column, so narrowing them would break a seam that is right. - * - **boolean** [#17343] — HERE, and this limb had NEITHER until then. + * - **boolean** [commit 82cb69fed] — HERE, and this limb had NEITHER until then. * #14079 landed this predicate describing itself as "a declared numeric or * boolean SCALAR" and annotated the numeric registry as non-`multiple`, so * the omission was the gap between that stated scope and `booleanFields`' * silence. * - * ⚠️ [#17586] `booleanFields` was SINCE narrowed at both fills, for a + * ⚠️ [commit d46deba19] `booleanFields` was SINCE narrowed at both fills, for a * defect of its own (the read coercion collapsed a parsed array to a * single, inverted `true`). * @@ -16689,11 +16689,11 @@ export class SqlDriver implements IDataDriver { } /** - * [#17590, director ruling 2026-09-12] Emit the MEMBERSHIP reading of + * [commit e04a0aff2, director ruling 2026-09-12] Emit the MEMBERSHIP reading of * `$contains` / `$notContains` when the column they were aimed at is a JSON * column, and say whether it did. * - * Returns `false` — leaving the caller on the pre-#17590 substring emitter — + * Returns `false` — leaving the caller on the substring emitter from before commit e04a0aff2 — * in exactly two cases, and the two are different in kind: * * 1. **The column is not a JSON column.** On a scalar string column @@ -17175,7 +17175,7 @@ export class SqlDriver implements IDataDriver { ); break; } - // [#17590] The MEMBERSHIP reading first: on a JSON column this + // [commit e04a0aff2] The MEMBERSHIP reading first: on a JSON column this // operator asks whether the comparand is an ELEMENT of the stored // array, not whether it is a substring of the serialization. Falls // through to the substring emitter for every scalar string column, @@ -17195,7 +17195,7 @@ export class SqlDriver implements IDataDriver { this.applyLike(builder, method, field, opValue, 'contains', false, true); break; case '$notContains': - // [#17590] The exact complement of the arm above, on the same + // [commit e04a0aff2] The exact complement of the arm above, on the same // population and the same construct. It moves WITH `$contains` // and cannot be deferred: this operator is declared "the negation // of $contains, on the same comparand contract", so leaving it on @@ -18038,7 +18038,7 @@ export class SqlDriver implements IDataDriver { /** * The widest `varchar(n)` one utf8mb4 key part can hold on InnoDB: 3072 bytes - * of index key ÷ 4 bytes per character (#11374). + * of index key ÷ 4 bytes per character (commit d0e3a885b). * * Measured on MySQL 8.0.46 (DYNAMIC row format, the 8.0 default) rather than * read off a doc page: `varchar(768) UNIQUE` creates, `varchar(769) UNIQUE` @@ -18055,7 +18055,7 @@ export class SqlDriver implements IDataDriver { /** * The `varchar(n)` a KEYED text-family field should take, or `null` to leave - * the column TEXT (#11374). + * the column TEXT (commit d0e3a885b). * * `null` has exactly two causes, and both are deliberate non-events rather * than failures here: @@ -18139,7 +18139,7 @@ export class SqlDriver implements IDataDriver { * ⚠️ Deliberately mirrors {@link keyableTextLength} without sharing code * with it. The two families answer different questions — that one asks * "can this KEY?" and returns `null` for an unbounded field, this one asks - * "how wide is this column?" and returns 255 — and #11374's remaining half + * "how wide is this column?" and returns 255 — and the half commit d0e3a885b left open * may still reshape the text side. A shared helper would couple a settled * decision to an unsettled one. */ @@ -18163,7 +18163,7 @@ export class SqlDriver implements IDataDriver { /** * Turn MySQL's `ER_BLOB_KEY_WITHOUT_LENGTH` / `ER_TOO_LONG_KEY` into a message * that names the columns at fault and the remedy that actually applies - * (#11374, #12999). + * (commits d0e3a885b and ebcc34e89). * * ## TWO remedies, because the refusal has two causes * @@ -18172,7 +18172,7 @@ export class SqlDriver implements IDataDriver { * | **create** | the field declares no usable `maxLength` | declare one — the next create emits `varchar(n)` | * | **upgrade** | the field declares one; the COLUMN predates it and is still TEXT | convert the column by hand | * - * One message served both until #12999, and on the upgrade path both of its + * One message served both until commit ebcc34e89, and on the upgrade path both of its * halves were false: it diagnosed a missing declaration that was present, and * prescribed a declaration that was already made. That is not merely * imprecise — it fires once per boot on a production upgrade and reads as the @@ -18223,7 +18223,7 @@ export class SqlDriver implements IDataDriver { ? `Column(s) ${offenders.map((c) => `"${c}"`).join(', ')} are stored as TEXT` : 'One or more of its key columns is stored as TEXT'; - // #12999: the SAME refusal has two causes, and the message below is true of + // Commit ebcc34e89: the SAME refusal has two causes, and the message below is true of // only one of them. On CREATE the field really declares no usable bound, so // "declare `maxLength`" is the fix. On an UPGRADE the bound IS declared — // the additive sync never rewrites a column's type (#3728), so a column @@ -18325,7 +18325,7 @@ export class SqlDriver implements IDataDriver { * get different shadows. */ protected static hashShadowColumnFor(indexName: string): string { - // #13015: DELEGATES rather than re-deriving. The differ has to look for + // Commit cd1348802: DELEGATES rather than re-deriving. The differ has to look for // exactly the column the sync creates, and the two halves of that question // lived in different modules — which is how a healthy shadow-carried // UNIQUE came to be reported as destructive drift while the shadow column @@ -18374,7 +18374,7 @@ export class SqlDriver implements IDataDriver { * * - **Distinct values that share a long prefix both insert.** This is the * property that rules OUT the prefix-index alternative and the reason - * this route was chosen over it (maintainer ruling on #11374, + * this route was chosen over it (maintainer ruling recorded in commit 107bb4ba4, * 2026-08-24): measured, two distinct tokens sharing their first 191 * characters are BOTH accepted here, where `UNIQUE KEY (token(191))` * rejected the second as `ER_DUP_ENTRY` — a valid sign-in refused as a @@ -18402,7 +18402,7 @@ export class SqlDriver implements IDataDriver { * a crash. {@link explainHashShadowDuplicate} exists so the driver can tell * the two apart by reading the source columns back, and name whichever it is. * - * ## The NULL-safe organization key parts ride along (#12998) + * ## The NULL-safe organization key parts ride along (commit df1c75c4b) * * The shadow hashes the DECLARED key, not the raw columns. For a key part * `normalizeDeclaredIndex` marked NULL-safe (ADR-0120 D3 — the organization @@ -18427,7 +18427,7 @@ export class SqlDriver implements IDataDriver { if (!this.isMysql) return false; const shadow = SqlDriver.hashShadowColumnFor(indexName); const ref = (c: string) => `\`${c.replace(/`/g, '``')}\``; - // The DECLARED key part: NULL-safe parts in their COALESCE form (#12998), + // The DECLARED key part: NULL-safe parts in their COALESCE form (commit df1c75c4b), // plain parts as the bare column. const part = (c: string) => nullSafeColumns?.has(c) ? `COALESCE(${ref(c)}, '${GLOBAL_TENANT}')` : ref(c); @@ -18440,7 +18440,7 @@ export class SqlDriver implements IDataDriver { const addColumn = `ADD COLUMN ${ref(shadow)} VARBINARY(32) GENERATED ALWAYS AS (UNHEX(SHA2(${expr}, 256))) STORED`; const addKey = `ADD UNIQUE KEY ${ref(indexName)} (${ref(shadow)})`; - // #13015: the shadow column OUTLIVES the index it carries. Dropping a + // Commit cd1348802: the shadow column OUTLIVES the index it carries. Dropping a // UNIQUE key by name does not drop the generated column keyed by it, so // every path that drops and re-syncs — `recreate_index` above all — arrives // back here with the survivor still on the table. The unconditional @@ -18454,7 +18454,7 @@ export class SqlDriver implements IDataDriver { if (state === 'foreign') { // A real, non-generated column already owns the name. It is not ours to // drop — it may hold data — so this route is refused and the caller falls - // through to the named #11374 refusal, which is the honest outcome. + // through to the refusal commit d0e3a885b named, which is the honest outcome. this.logDurabilityFailure( `[sql-driver] cannot carry UNIQUE index '${indexName}' on "${tableName}" on a hash shadow — the ` + `column "${shadow}" already exists and is NOT a generated column, so it is not the driver's to ` + @@ -18468,7 +18468,7 @@ export class SqlDriver implements IDataDriver { await this.knex.raw(`ALTER TABLE ${ref(tableName)} ${addKey}`); } else if (state === 'stale') { // The survivor hashes a DIFFERENT key than metadata now declares — the - // pre-#12998 raw-column shadow is exactly this case. Reusing it would + // raw-column shadow from before commit df1c75c4b is exactly this case. Reusing it would // re-enforce the old constraint under the new name, which is the silent // wrong answer; the column carries no user data (it is derived), so it is // dropped and re-generated. Two statements rather than one ALTER: the @@ -18481,7 +18481,7 @@ export class SqlDriver implements IDataDriver { } // The boot log describes the key the shadow actually enforces — the // NULL-safe parts in their COALESCE spelling — so "carried" can be read - // literally (#12998). + // literally (commit df1c75c4b). const described = columns .map((c) => (nullSafeColumns?.has(c) ? `COALESCE(${c}, '${GLOBAL_TENANT}')` : c)) .join(', '); @@ -18501,13 +18501,13 @@ export class SqlDriver implements IDataDriver { } /** - * What is already sitting where this index's hash shadow goes (#13015)? + * What is already sitting where this index's hash shadow goes (commit cd1348802)? * * - `absent` — nothing; create the column and the key together. * - `reusable` — a generated column hashing EXACTLY the declared key. The * index that keyed it was dropped (a `recreate_index`, a * manual `DROP INDEX`, a half-applied migration); re-key it. - * - `stale` — a generated column hashing a DIFFERENT key. The pre-#12998 + * - `stale` — a generated column hashing a DIFFERENT key. The older (before commit df1c75c4b) * shadow over RAW columns is this case: reusing it would * re-enforce the OLD constraint under the new name — green, * silent, and wrong — so it is re-generated instead. @@ -18577,7 +18577,7 @@ export class SqlDriver implements IDataDriver { * * ## Ownership is ESTABLISHED, never assumed * - * Two refusals, in the shape of #13015's `foreign` guard — a column we have + * Two refusals, in the shape of commit cd1348802's `foreign` guard — a column we have * not proved is ours is left alone and named in the log, never dropped: * * - a column of this name that is **not generated** may hold user data; @@ -18715,7 +18715,7 @@ export class SqlDriver implements IDataDriver { if (sources.length === 0) return null; // Only the source columns the failing write actually supplied; a partial // update cannot be re-selected on columns it never mentioned. A NULL-safe - // key part (#12998) is exempt: the enforced key COALESCEs an absent or + // key part (commit df1c75c4b) is exempt: the enforced key COALESCEs an absent or // NULL value into the '__global__' bucket, so its key part is knowable // without the write mentioning the column. if ( @@ -18727,7 +18727,7 @@ export class SqlDriver implements IDataDriver { } let existing = 0; try { - // Re-select by the key the index ENFORCES, not by raw equality (#12998): + // Re-select by the key the index ENFORCES, not by raw equality (commit df1c75c4b): // a NULL-safe part compares through the same COALESCE the generation // expression carries (NULL and absent both land in the global bucket), // and plain parts use `<=>` — MySQL's NULL-safe equality; this whole @@ -18749,7 +18749,7 @@ export class SqlDriver implements IDataDriver { return null; } // Describe the key parts as enforced — NULL-safe parts in their COALESCE - // spelling — so the message names the actual constraint (#12998). + // spelling — so the message names the actual constraint (commit df1c75c4b). const described = sources .map((s) => (s.nullSafe ? `COALESCE(${s.column}, '${GLOBAL_TENANT}')` : s.column)) .join(', '); @@ -18809,7 +18809,7 @@ export class SqlDriver implements IDataDriver { * generation expression the server stores, so the disambiguating select * above filters on the same key the shadow was generated from. Per part: * the column identity, and whether the expression wraps it in the NULL-safe - * `COALESCE(col, …)` form (ADR-0120 D3 via #12998) — the read side must + * `COALESCE(col, …)` form (ADR-0120 D3 via commit df1c75c4b) — the read side must * compare through the same fold the enforced key applies. */ protected async hashShadowSourceColumns( @@ -18829,7 +18829,7 @@ export class SqlDriver implements IDataDriver { const expr = String(rows[0]?.GENERATION_EXPRESSION ?? rows[0]?.generation_expression ?? ''); // `unhex(sha2(`a`,256))`, `unhex(sha2(concat(`a`,0x1f,`b`),256))`, or with // a NULL-safe part: `…concat(coalesce(`org`,_utf8mb4'__global__'),0x1f,`b`)…` - // (#12998). #13015 moved the parse itself next to the shadow vocabulary + // (commit df1c75c4b). Commit cd1348802 moved the parse itself next to the shadow vocabulary // in `schema-drift.ts`: the differ reads the same expression to decide // whether a shadow-carried index enforces what metadata declares, and two // copies of this regex would be two answers to one question. @@ -19214,7 +19214,7 @@ export class SqlDriver implements IDataDriver { * is what SQLite's own JSON1 functions operate on, so this is the type the * database actually means. * - * That exposure is the one #12380 had to defeat on this driver's SQLite half + * That exposure is the one commit 4045b954d had to defeat on this driver's SQLite half * by making the `Field.json` codec injective. This change removes it at the * ROOT for new columns instead of encoding around it. * @@ -19245,7 +19245,7 @@ export class SqlDriver implements IDataDriver { * * It changes what NEW columns are declared as. It never rewrites an existing * column: the schema sync is additive, and a legacy `json` column keeps its - * declared type, its NUMERIC affinity, and the #12380 encoding that defeats + * declared type, its NUMERIC affinity, and the commit 4045b954d encoding that defeats * it. Nothing starts reporting drift over the difference either — the base- * type finding in `schema-drift.ts` is gated on * `multiValueColumnTypeIsLoadBearing(dialect)`, which is `postgres || mysql` @@ -19418,7 +19418,7 @@ export class SqlDriver implements IDataDriver { // is enforced at the write seam, and the data-URI refusal goes away. case 'signature': case 'qrcode': { - // #11374: a text-family column that some declared index KEYS ON is + // Commit d0e3a885b: a text-family column that some declared index KEYS ON is // emitted as `varchar(maxLength)` rather than TEXT, whenever the field // declared a bound this dialect can key on. // @@ -20152,7 +20152,7 @@ export class SqlDriver implements IDataDriver { } } - // ── JSON field serialisation: ONE encoding, every dialect (#12380) ────── + // ── JSON field serialisation: ONE encoding, every dialect (commit 4045b954d) ────── // // `JSON.stringify` unconditionally, which is what Postgres and MySQL have // always done here. This DELETES the SQLite branch rather than adding one, @@ -20266,7 +20266,7 @@ export class SqlDriver implements IDataDriver { } if (this.isSqlite) { - // The exact inverse of `formatInput`'s `JSON.stringify` (#12380). Postgres + // The exact inverse of `formatInput`'s `JSON.stringify` (commit 4045b954d). Postgres // and MySQL need no arm here because their clients already parse a native // `json`/`jsonb` column; SQLite hands back the stored TEXT, so the driver // parses it. One codec, three dialects, same answer. @@ -20280,7 +20280,7 @@ export class SqlDriver implements IDataDriver { // // Nothing this driver writes can reach it: every new value on disk is the // output of `JSON.stringify`, so it parses by construction. It is the - // READ-SIDE REPAIR for rows written before #12380 — a pre-fix plain string + // READ-SIDE REPAIR for rows written before commit 4045b954d — a pre-fix plain string // was stored raw (`America/New_York`), and re-quoting it is exactly what // {@link backfillCanonicalJsonEncoding} does on the next `syncSchema`. // Keeping it here is the same posture `backfillCanonicalDatetimes` takes: @@ -20295,7 +20295,7 @@ export class SqlDriver implements IDataDriver { // INTEGER/REAL cell (a number, a boolean, or a number-like string eaten by // NUMERIC affinity) are collisions already resolved on disk. Those rows // read exactly as they read before this change — the class stops growing; - // it is not retroactively repaired. See the ruling recorded on #12380. + // it is not retroactively repaired. See the decision recorded in commit 4045b954d's message. const jsonFields = this.jsonFields[object]; if (jsonFields && jsonFields.length > 0) { for (const field of jsonFields) { @@ -20303,7 +20303,7 @@ export class SqlDriver implements IDataDriver { try { data[field] = JSON.parse(data[field]); } catch { - // Pre-#12380 row: keep the raw string, which IS its value. + // Row from before commit 4045b954d: keep the raw string, which IS its value. } } }