diff --git a/.changeset/spec-data-provenance-anchors.md b/.changeset/spec-data-provenance-anchors.md new file mode 100644 index 00000000000..56b4e045926 --- /dev/null +++ b/.changeset/spec-data-provenance-anchors.md @@ -0,0 +1,11 @@ +--- +'@objectstack/spec': patch +--- + +Provenance comments in `data/` were re-anchored + +Comment and docblock lines under `src/data` (all but the files other open work +holds) that cited tracker numbers which no longer resolve on GitHub now cite +the commit in this repository's history that decided the matter, and say in +their own words what was decided. Comments only: no type, schema, export or +runtime behaviour changes. diff --git a/content/docs/references/data/feed.mdx b/content/docs/references/data/feed.mdx index 6f7ea641cad..a599719fa00 100644 --- a/content/docs/references/data/feed.mdx +++ b/content/docs/references/data/feed.mdx @@ -15,10 +15,10 @@ enums here configure the record activity component (`RecordActivityProps` in `FeedItemType` is not backend-free: it has no backend *import*, yet it is the TARGET of the map UI consumers apply to the `sys_activity.type` column — a backend *coupling* — and that column's vocabulary is OPEN and -author-extensible (maintainer ruling 2026-08-24, #11507). `FeedItemType` is +author-extensible (maintainer ruling 2026-08-24, commit 88b9d749a). `FeedItemType` is therefore the built-in guidance half of that map, never the value domain of an authoring surface: `RecordActivityProps.types` accepts contributed kinds -beyond it (#11658), and consumers must map unknown `sys_activity.type` values +beyond it (commit 1a6a19c31), and consumers must map unknown `sys_activity.type` values to a fallback rather than drop them. `SYS_ACTIVITY_BUILTIN_TYPES` below is the published built-in vocabulary of the `sys_activity.type` column, co-located with `FeedItemType` because UI consumers map one onto the other. diff --git a/packages/spec/src/data/aggregate-field-type-compatibility.test.ts b/packages/spec/src/data/aggregate-field-type-compatibility.test.ts index 4570415d353..4d40eecbb5c 100644 --- a/packages/spec/src/data/aggregate-field-type-compatibility.test.ts +++ b/packages/spec/src/data/aggregate-field-type-compatibility.test.ts @@ -14,7 +14,7 @@ * conformance suite — every boolean case `AGGREGATION_CASES` requires a * backend to ANSWER (#11152) must be a pair this table accepts, so the two * tables in this package cannot contradict each other on the boolean axis - * (#16685) — the cross-pin reaches exactly as far as the `flag` cases. + * (commit ed7243d52) — the cross-pin reaches exactly as far as the `flag` cases. */ import { describe, it, expect } from 'vitest'; @@ -140,7 +140,7 @@ describe('isAggregateCompatibleWithFieldType — the pairs the card is about', ( // `AGGREGATION_ROWS.flag` is the boolean aggregand (declared `type: // 'boolean'` by every harness); each case over it is a pair #11152 pins // on six backends. A table refusing one of them would refuse a pair the - // spec elsewhere REQUIRES an answer to (#16685). + // spec elsewhere REQUIRES an answer to (commit ed7243d52). const booleanCases = AGGREGATION_CASES.filter((c) => c.field === 'flag'); expect(sorted(new Set(booleanCases.map((c) => c.function)))).toEqual(sorted(AggregationFunction.options)); for (const c of booleanCases) { diff --git a/packages/spec/src/data/aggregate-field-type-compatibility.ts b/packages/spec/src/data/aggregate-field-type-compatibility.ts index f318190d3fb..2c4004e03a2 100644 --- a/packages/spec/src/data/aggregate-field-type-compatibility.ts +++ b/packages/spec/src/data/aggregate-field-type-compatibility.ts @@ -4,7 +4,7 @@ * Aggregate × field-type compatibility — the ONE table saying which * `AggregationFunction` may be applied to a field of which `FieldType` * (#16353; director ruling, decision batch #59, 2026-09-06: "both legs, table - * in spec"; the boolean rows by decision batch #80, 2026-09-08, #16685 — see + * in spec"; the boolean rows by decision batch #80, 2026-09-08, commit ed7243d52 — see * below). A `DatasetMeasure` pairs an `aggregate` with a `field`; this * table is the contract both consumer legs execute — the compile-time refusal * in the dataset compiler (#16099) and the authoring-time lint rule — so the @@ -65,11 +65,11 @@ * aggregand to `int` on Postgres so that the one dialect storing a real * `boolean` column answers the same numbers (#11635). Batch #59's "every * other pair: refused" never named booleans — it was a blanket default — - * and the director ruling of decision batch #80 (2026-09-08, #16685, + * and the director ruling of decision batch #80 (2026-09-08, commit ed7243d52, * maintainer verbatim 「其他同意」, option A) holds that the specific ruling * #11152 stands over that default: the four rows carry both members and * nothing else moves. `avg(flag)` is the win-rate / SLA-violation-rate - * shape (#11065) — the reason `AGGREGATION_CASES` exists — so a table that + * shape (commit 20950404c) — the reason `AGGREGATION_CASES` exists — so a table that * refused it would refuse a pair every backend is REQUIRED to answer. * - **everything else** — the text family, option types, references, files, * structured JSON, `vector`, and the computed `formula` / `autonumber` — is diff --git a/packages/spec/src/data/aggregation-conformance.ts b/packages/spec/src/data/aggregation-conformance.ts index 886f7173e4a..5399f82c2e1 100644 --- a/packages/spec/src/data/aggregation-conformance.ts +++ b/packages/spec/src/data/aggregation-conformance.ts @@ -59,7 +59,7 @@ * verbatim 「12745 A回,其他同意。」, superseding #11249's `false`/`true`) * pins that **booleans aggregate as numbers on every face, with no * per-aggregate exception** — `min(flag)`/`max(flag)` answer `0`/`1`, the - * same numeric domain `sum`/`avg` already answer in (#11065). So a boolean + * same numeric domain `sum`/`avg` already answer in (commit 20950404c). So a boolean * aggregand takes NO boolean read-presentation on any face, and * {@link AggregationExpectation.value} stays a `number` for every case. * @@ -224,7 +224,7 @@ export interface AggregationRow { /** * [#11152] The non-null BOOLEAN aggregand — 3 true / 3 false, so `sum` and * `avg` cannot agree with a face that dropped the booleans (`0` / `null`, - * the #11065/#11151 defect) or that counted rows instead of trues. + * the commit 20950404c / #11151 defect) or that counted rows instead of trues. * * The distribution is the `FLAG_BY_ID` the #11635 suite landed, adopted here * verbatim so the two never disagree on grouped values: `west` holds @@ -383,7 +383,7 @@ export const AGGREGATION_CASES: readonly AggregationCase[] = [ // ── [#11152] the boolean aggregand: numbers on every face, by ruling ────── // // The whole vocabulary over `flag` (3 true / 3 false). Two rulings pin the - // values: #11065 settled `sum`/`avg` (a boolean is an aggregand worth 1 or + // values: commit 20950404c settled `sum`/`avg` (a boolean is an aggregand worth 1 or // 0 — driver-memory answered `0`/`null` while SQLite answered `2`/`0.4`, // found from an application because no conformance cell could see it), and // #11152 (maintainer 2026-08-28, superseding #11249's `false`/`true`) diff --git a/packages/spec/src/data/api-derivation.test.ts b/packages/spec/src/data/api-derivation.test.ts index bf0fae83571..007c164dc05 100644 --- a/packages/spec/src/data/api-derivation.test.ts +++ b/packages/spec/src/data/api-derivation.test.ts @@ -203,7 +203,7 @@ describe('api-derivation (#3391)', () => { expect(DATA_ACTION_TO_API_OPERATION.bulk).toBe('bulk'); }); - // [#6259] `batch: 'bulk'` was a producer-less row: `callData` has had no + // [commit 6968885ef] `batch: 'bulk'` was a producer-less row: `callData` has had no // `batch` arm since #5856, and REST gates `/batch` on the literal `'bulk'`. // Two pins, because the finding had two halves — the row AND the prose // that told readers `batch` was a live runtime action. diff --git a/packages/spec/src/data/api-derivation.ts b/packages/spec/src/data/api-derivation.ts index f1c6fe6959e..e363ba62b6b 100644 --- a/packages/spec/src/data/api-derivation.ts +++ b/packages/spec/src/data/api-derivation.ts @@ -161,7 +161,7 @@ export const API_METHOD_DERIVATION: Record = { * `apiMethods`). * * [#6259] The `batch: 'bulk'` row was removed, and the line above no longer - * calls `batch` a runtime `callData` action. It was the one entry with no + * calls `batch` a runtime `callData` action (both by commit 6968885ef). It was the one entry with no * producer on either side: `callData` branches on a closed set that has not * contained `batch` since that arm was retired (#5856), and every REST caller * of `apiAccessDenialFromEnable` passes a canonical literal — including the diff --git a/packages/spec/src/data/api-methods-batch-conformance.test.ts b/packages/spec/src/data/api-methods-batch-conformance.test.ts index 8d05ddbfcf1..45f8da41958 100644 --- a/packages/spec/src/data/api-methods-batch-conformance.test.ts +++ b/packages/spec/src/data/api-methods-batch-conformance.test.ts @@ -95,7 +95,7 @@ const SINGLE_RECORD_WRITE_ONLY: Record = { // `00d3f09c5` is the one that caught the previous record's OWN grid anchor as // wrong rather than merely shifted: `3790-3805` there is // `runBulkActionAggregate` and says nothing about selection. That is the - // #10274 class, and the reason a citation refresh re-READS instead of moving + // class commit d1ba685ec gates, and the reason a citation refresh re-READS instead of moving // numbers — arithmetic on a wrong anchor produces a fresh-looking span still // describing the wrong function. The second claim, // `hooks/useBulkExecutor.ts:298-303`, sits in a file that is byte-identical diff --git a/packages/spec/src/data/datasource-credential-redaction.test.ts b/packages/spec/src/data/datasource-credential-redaction.test.ts index cd590410013..74ab5bc992f 100644 --- a/packages/spec/src/data/datasource-credential-redaction.test.ts +++ b/packages/spec/src/data/datasource-credential-redaction.test.ts @@ -233,7 +233,7 @@ describe('write-door alignment: redactUrlPassword removes exactly what urlUserin // `urlUserinfoUsername` shares the password half's boundary parse by // construction; this pins the redactor to the same grammar from the other // side: stripping the password must never move or rewrite the username the - // #8696 injection path will read off the redacted/stored row. + // commit 90a12fb18's injection path will read off the redacted/stored row. for (const url of [...CARRYING, ...CREDENTIAL_FREE]) { expect(urlUserinfoUsername(redactUrlPassword(url)), url).toBe(urlUserinfoUsername(url)); } @@ -412,7 +412,7 @@ describe('passthrough secret redaction (#9040) — the nested spellings the key- keyVaultNamespace: 'encryption.__keyVault', kmsProviders: { // The identity halves the client also reads are NOT credential - // material (#8876's asymmetry) and stay served. + // material (commit d634e665b's asymmetry) and stay served. aws: { accessKeyId: 'AKIAFAKEFAKEFAKEFAKE' }, azure: { tenantId: 'tenant-id', clientId: 'client-id' }, gcp: { email: 'svc@example.iam.gserviceaccount.com' }, diff --git a/packages/spec/src/data/datasource-credential-redaction.ts b/packages/spec/src/data/datasource-credential-redaction.ts index 0bbfc19db00..43066bf2622 100644 --- a/packages/spec/src/data/datasource-credential-redaction.ts +++ b/packages/spec/src/data/datasource-credential-redaction.ts @@ -134,7 +134,7 @@ const STILL_WRITABLE_CREDENTIAL_KEYS: Record = { /** * Secret-bearing paths inside a driver's passthrough `config` slot — the - * FOURTH spelling of the stored credential (#9040). + * FOURTH spelling of the stored credential (commit 24206416a). * * Since the nested-position finding this table is a RESIDUE, not the nested * judgment: the credential-name scrub runs at every object depth (see @@ -150,16 +150,16 @@ const STILL_WRITABLE_CREDENTIAL_KEYS: Record = { * Only mongo declares a passthrough today (`options`, spread verbatim into * `MongoClientOptions`); postgres/mysql/turso/sqlite/memory have closed * strict-object contracts with no client-bound record slot (measured for - * #9040 — memory's `initialData` is seed DATA, deliberately not judged here: + * commit 24206416a — memory's `initialData` is seed DATA, deliberately not judged here: * redacting a seeded row's own `password` FIELD would corrupt data the driver * serves, which is not this module's question). Every path is measured against * `mongodb@7.5.0`, the client the driver spreads `options` into: * * - `options.auth.password` — resolved into `MongoCredentials`; the login * secret itself, and the one path the WRITE door also refuses - * (`MONGO_OPTIONS_CREDENTIAL_PATHS` in `driver/common.zod.ts`; #8696 + * (`MONGO_OPTIONS_CREDENTIAL_PATHS` in `driver/common.zod.ts`; commit 90a12fb18 * measured a bound secret outranking it at connect). `auth.username` is - * deliberately not here — a username is not credential material (#8876). + * deliberately not here — a username is not credential material (commit d634e665b). * - `options.proxyPassword` — SOCKS5 proxy password, honoured * (`c.options.proxyPassword`, measured). * - `options.tlsCertificateKeyFilePassword`, `options.key`, @@ -189,7 +189,7 @@ const STILL_WRITABLE_CREDENTIAL_KEYS: Record = { * one slot is the login password, the proxyPassword posture — but never * SERVED. The same families' identity halves (`aws.accessKeyId`, * `azure.tenantId` / `clientId`, `gcp.email`) are read by the client too - * but are not credential material (#8876's asymmetry), and the unmeasured + * but are not credential material (commit d634e665b's asymmetry), and the unmeasured * neighbours (`kmip.endpoint`, `keyVaultNamespace`, `schemaMap`) mirror no * credential spelling — deliberately not here: entries land on this table * with a measurement quoted, never by name-shape. @@ -218,7 +218,7 @@ const PASSTHROUGH_SECRET_PATHS: Readonly strictObject( // author's trust on a slot that cannot pay it back. reportDriverConfigIssues(ctx, ds.driver, ds.config, ['config']); - // #9041 (url branch) + #9147 (composed branch) — see + // commit d491625c1 (url branch) + #9147 (composed branch) — see // CREDENTIALS_REF_MONGO_URL_NO_USER_REFUSED and // CREDENTIALS_REF_MONGO_NO_USERNAME_REFUSED. Neither can live in // `MongoConfigSchema` (a config-level refinement sees only `config`; diff --git a/packages/spec/src/data/date-range-presets.test.ts b/packages/spec/src/data/date-range-presets.test.ts index 710cc0918d6..177299dcee1 100644 --- a/packages/spec/src/data/date-range-presets.test.ts +++ b/packages/spec/src/data/date-range-presets.test.ts @@ -70,7 +70,7 @@ describe('date-range preset vocabulary (#4614, re-homed by #8793)', () => { expect(message).toContain('{30_days_ago}'); // the spelling that works expect(message).toContain('2026-01-15'); // the ISO alternative // Attributable from the error alone by the customer-resolvable sentence — - // never by a tracker id (#13156's strip). + // never by a tracker id (commit fd289be45's strip). expect(message).toContain('Refused at authoring time so the error surfaces where the filter is written.'); expect(message).not.toMatch(/(? { * pin then asks whether `DATE_RANGE_PRESET_MACRO_WINDOWS` joins them, which is * the only question it exists to answer — and the question a membership check * over the token vocabulary cannot reach, because both a right and a wrong end - * token are perfectly good members (#17014). + * token are perfectly good members (commit 80aef8032). */ const REFERENCE_DAY = '2026-07-15'; // a Wednesday, mid-week / mid-month / mid-quarter diff --git a/packages/spec/src/data/date-range-presets.ts b/packages/spec/src/data/date-range-presets.ts index 5d3beeb6124..1d86a749413 100644 --- a/packages/spec/src/data/date-range-presets.ts +++ b/packages/spec/src/data/date-range-presets.ts @@ -68,7 +68,7 @@ export function isDateRangePresetName(value: unknown): value is DateRangePreset * The `{date-macro}` window each preset resolves to — `[start, end]`, in the * WRAPPED spelling a filter author writes. * - * ## The convention, binding on every entry (#17014) + * ## The convention, binding on every entry (commit 80aef8032) * * **`start` names the FIRST calendar day the window contains; `end` names its * LAST. Inclusive — never the day the window stops before.** diff --git a/packages/spec/src/data/driver-nosql.zod.ts b/packages/spec/src/data/driver-nosql.zod.ts index b95b445ac7b..92fab3d17d7 100644 --- a/packages/spec/src/data/driver-nosql.zod.ts +++ b/packages/spec/src/data/driver-nosql.zod.ts @@ -416,7 +416,7 @@ export const NoSQLIndexSchema = lazySchema(() => z.object({ * carried by `FieldSchema.unique` and `IndexSchema.unique`). * * This file is the raw NoSQL driver-configuration descriptor layer, not an - * organization-aware authoring surface. Measured for #11215: nothing in the + * organization-aware authoring surface. Measured for commit 42a117b88: nothing in the * repo parses `NoSQLIndexSchema` or materializes indexes from it (a leaf * schema — no runtime, kernel, or driver import), and the one NoSQL driver * that does create indexes (driver-mongodb's `syncCollectionSchema`) diff --git a/packages/spec/src/data/driver/common.zod.ts b/packages/spec/src/data/driver/common.zod.ts index 6c60193e79c..a2a81560dd8 100644 --- a/packages/spec/src/data/driver/common.zod.ts +++ b/packages/spec/src/data/driver/common.zod.ts @@ -222,7 +222,7 @@ export function urlUserinfoPassword(value: string): string | undefined { * The username component of a URL-ish string's userinfo, or `undefined` when * the string carries no userinfo at all — the other half of the grammar behind * {@link urlUserinfoPassword}, sharing {@link urlUserinfo}'s boundary parse so - * the two halves cannot drift (#8876; the #8082 ruling names a single + * the two halves cannot drift (commit d634e665b; the #8082 ruling names a single * value-level parse precisely so no second copy exists to disagree with this * one). * @@ -230,7 +230,7 @@ export function urlUserinfoPassword(value: string): string | undefined { * driver arm that binds a secret via `external.credentialsRef` against a DSN * (`MongoConfigSchema.url`'s declared contract) must hand its client the * username the URL already names — and reading it needs this grammar, because - * `new URL()` rejects the multi-host DSN form outright (#8696). Hence two + * `new URL()` rejects the multi-host DSN form outright (commit 90a12fb18). Hence two * deliberate asymmetries with the password half: * * - a `user:password@` URL is REFUSED at publish ({@link credentialFreeUrl}) @@ -341,14 +341,14 @@ export const URL_CREDENTIAL_QUERY_PARAM_REFUSED = (key: string, param: string): /** * Refusal prescription for a credential written into the MongoClient * passthrough (`config.options.auth.password`) — the FOURTH spelling of the - * same inline secret (#9040): #7990 refused the top-level key, #8082 the URL + * same inline secret (commit 24206416a): #7990 refused the top-level key, #8082 the URL * userinfo, #8337 the URL query parameter, and the `options` passthrough was * the next syntax over from all three, exactly as #8337 was one syntax over * from #8082. * * Same wording constraints as the sibling messages, plus one this message may * state that #8337's must not: the bound secret genuinely WINS over a - * passthrough `auth` block at connect — measured by #8696's pin + * passthrough `auth` block at connect — measured by commit 90a12fb18's pin * (`bound-secret-dsn-branches.test.ts`), which asserts the injected * `external.credentialsRef` secret outranks `options.auth`. So the "wins over" * reassurance is true here, unlike turso's query form where the URL token @@ -370,7 +370,7 @@ export const PASSTHROUGH_INLINE_CREDENTIAL_REFUSED = (path: string): string => /** * The paths inside mongo's `options` passthrough that resolve into a login * credential the client honours AND the secret binder can replace — the CLOSED - * refusal list behind {@link credentialFreeMongoOptions} (#9040). + * refusal list behind {@link credentialFreeMongoOptions} (commit 24206416a). * * Every entry is MEASURED against `mongodb@7.5.0`, the client * `@objectstack/driver-mongodb` pins and spreads `config.options` into @@ -380,10 +380,10 @@ export const PASSTHROUGH_INLINE_CREDENTIAL_REFUSED = (path: string): string => * - `auth.password` — `OPTIONS.auth` transforms `{ username, password }` into * `MongoCredentials`, so the passthrough password IS the login credential * (measured: `c.options.credentials.password` carries it verbatim). The - * binder replaces it exactly: #8696's pin measures a bound + * binder replaces it exactly: commit 90a12fb18's pin measures a bound * `external.credentialsRef` secret outranking this block at connect. Only a * NON-EMPTY STRING is refused — `auth.username` alone is not credential - * material (#8876's asymmetry, restated for this syntax), an empty password + * material (commit d634e665b's asymmetry, restated for this syntax), an empty password * is the passthrough twin of `user:@host` (accepted, #8082), and a * non-string value is not a secret the client accepts (its * `MongoCredentials` validation fails loudly at construction). @@ -488,7 +488,7 @@ function valueAtPath(value: unknown, path: readonly string[]): unknown { * - No `${…}` placeholder advice (measured broken escape, #8078/#8336) — * same as every sibling message. * - It must NOT promise the bound secret "wins over" this value: that claim - * is measured for `auth.password` only (#8696). For any other nested + * is measured for `auth.password` only (commit 90a12fb18). For any other nested * position nothing is measured to read the value at all — which is the * point the message makes instead: the value performs no function the * author can observe, while sitting cleartext at rest. @@ -506,7 +506,7 @@ export const PASSTHROUGH_NESTED_CREDENTIAL_REFUSED = (path: string): string => + 'at publish.'; /** - * Attach the #9040 passthrough-credential refusal to mongo's `options` slot. + * Attach commit 24206416a's passthrough-credential refusal to mongo's `options` slot. * * Composes with `placeholderFreeDeep` the same way `credentialFreeUrl` * composes with `placeholderFree` on the URL keys: both checks are @@ -518,7 +518,7 @@ export const PASSTHROUGH_NESTED_CREDENTIAL_REFUSED = (path: string): string => * * 1. The MEASURED paths ({@link MONGO_OPTIONS_CREDENTIAL_PATHS}) — positions * the client resolves into a login credential the binder substitutes - * (#9040's original walk, message unchanged). + * (commit 24206416a's original walk, message unchanged). * 2. The nested NAME judgment — a non-empty string under a key spelled like * a credential ({@link CREDENTIAL_KEY_SPELLINGS}), at ANY object depth of * the passthrough. Before this walk, `options.auth.password` was refused diff --git a/packages/spec/src/data/driver/config-registry.test.ts b/packages/spec/src/data/driver/config-registry.test.ts index 9ccafc3eedd..23707865c55 100644 --- a/packages/spec/src/data/driver/config-registry.test.ts +++ b/packages/spec/src/data/driver/config-registry.test.ts @@ -41,7 +41,7 @@ describe('driver config registry', () => { it('resolves case- and whitespace-insensitively', () => { expect(resolveDriverId(' PostgreSQL ')).toBe('postgres'); - // `mongodb`, not `mongo`, since #6345 renamed the canonical id. + // `mongodb`, not `mongo`, since commit e2798fab7 renamed the canonical id. expect(resolveDriverId('MongoDB')).toBe('mongodb'); expect(resolveDriverId(' Mongo ')).toBe('mongodb'); }); diff --git a/packages/spec/src/data/driver/config-registry.zod.ts b/packages/spec/src/data/driver/config-registry.zod.ts index bc36311d30f..de201036d8e 100644 --- a/packages/spec/src/data/driver/config-registry.zod.ts +++ b/packages/spec/src/data/driver/config-registry.zod.ts @@ -56,14 +56,14 @@ import { getTursoConfigJsonSchema, TursoConfigSchema } from './turso.zod'; * datasource against nothing while building it as postgres — so the factory now * imports {@link resolveDriverId} instead of keeping a second list. * - * ## The HOSTS read it too, since #6345 — and that is what closed the last fork + * ## The HOSTS read it too, since commit e2798fab7 — and that is what closed the last fork * * #4410 unified the two tables *inside* the metadata path. It did not reach the * two BOOT HOSTS, which kept answering the same question differently about the * same `OS_DATABASE_DRIVER`: the CLI (`packages/cli/src/utils/storage-driver.ts`) * hand-wrote its spellings into `if` arms, and the standalone stack * (`packages/runtime/src/standalone-stack.ts`) hand-wrote a zod enum of canonical - * spellings only. Measured on `main` before #6345: **10 of 21 spellings disagreed** + * spellings only. Measured on `main` before commit e2798fab7: **10 of 21 spellings disagreed** * — `OS_DATABASE_DRIVER=pg` booted under `os start` and was refused by * `os migrate`, and `libsql` was accepted by the CLI alone. Both hosts now resolve * through {@link resolveDatabaseDriverId}, so the vocabulary is this table and @@ -71,7 +71,7 @@ import { getTursoConfigJsonSchema, TursoConfigSchema } from './turso.zod'; * * ## Two faces, one table (the part a flat `Record` could not express) * - * A driver id answers three separate questions, and #6345's measurement is that + * A driver id answers three separate questions, and commit e2798fab7's measurement is that * they are NOT the same set: * * 1. **Selection** — may an operator write this spelling as @@ -87,7 +87,7 @@ import { getTursoConfigJsonSchema, TursoConfigSchema } from './turso.zod'; * `mongodb://localhost:27017/objectstack`; the standalone side handed all * of them a `file:` DSN). * - * The maintainer's #6345 ruling fixes the selection face as **the union of what + * The maintainer's ruling (commit e2798fab7) fixes the selection face as **the union of what * the two hosts accepted the day the ruling was written**. `sql` and `wasm` are * in it because the CLI accepted them; `sqlite3`, `better-sqlite3`, `mariadb` * and `inmemory` are NOT, because neither host did — they are @@ -96,7 +96,7 @@ import { getTursoConfigJsonSchema, TursoConfigSchema } from './turso.zod'; * before this table existed while refusing to widen a boot flag nobody asked to * widen. * - * ## `mongo` → `mongodb`, and turso becoming a real builtin (#6345) + * ## `mongo` → `mongodb`, and turso becoming a real builtin (commit e2798fab7) * * The old canon was `mongo` while both hosts, the npm package * (`@objectstack/driver-mongodb`) and every URL scheme said `mongodb`. The @@ -121,14 +121,14 @@ import { getTursoConfigJsonSchema, TursoConfigSchema } from './turso.zod'; export interface DriverVocabularyEntry { /** * The canonical id, for BOTH selection and config contract. `mongodb`, not - * `mongo` (#6345). + * `mongo` (commit e2798fab7). */ readonly id: string; /** * Spellings an operator or author may SELECT this driver by, matched * case-insensitively. Includes {@link id}. This is the face both boot hosts * accept, and the ruling fixes it as the union of what they accepted before - * #6345 — never widened by accident. + * commit e2798fab7 — never widened by accident. */ readonly aliases: readonly string[]; /** @@ -146,7 +146,7 @@ export interface DriverVocabularyEntry { * sqlite kinds fall back to `:memory:` / the unified default file). `false` * for every kind whose target is a server or an endpoint — there is nothing * truthful to guess, so both hosts refuse with a typed error naming what to - * set (#6345 fork 2). + * set (commit e2798fab7's fork 2). */ readonly hasLocalDefault: boolean; } @@ -155,7 +155,7 @@ export interface DriverVocabularyEntry { * THE table. Everything else in this module is a projection of it. * * Row order is the order {@link BUILTIN_DRIVER_IDS} publishes, which is the - * order the pre-#6345 tuple used, plus `turso` appended. + * order the tuple used before commit e2798fab7, plus `turso` appended. */ const DRIVER_VOCABULARY = [ { id: 'memory', aliases: ['memory', 'mingo', 'in-memory'], contractOnlyAliases: ['inmemory'], hasLocalDefault: true }, @@ -181,7 +181,7 @@ type VocabularyIds = { -readonly [ /** * Canonical driver ids the platform ships a config contract for — and, since - * #6345, exactly the ids both boot hosts dispatch. + * commit e2798fab7, exactly the ids both boot hosts dispatch. * * Projected through {@link VocabularyIds} rather than a plain `.map()` so the * published shape stays the same TUPLE it was before the table existed: a @@ -245,7 +245,7 @@ export const DATABASE_DRIVER_SELECTION_ALIASES: readonly string[] = Object.freez * That signal has consumers that are not plain-JS callers. The CLI's * `resolveStorageDriver` (`packages/cli/src/utils/storage-driver.ts`) refuses an * unclaimed operator selection with `if (driverType && !kind)`, so - * `OS_DATABASE_DRIVER=constructor` walked PAST the refusal #6345 fork 1 exists + * `OS_DATABASE_DRIVER=constructor` walked PAST the refusal commit e2798fab7's fork 1 exists * to be — a truthy `kind` that is not a driver id. {@link driverHasLocalDefault} * failed the same way from the other end: a truthy non-id indexed * `DRIVER_LOCAL_DEFAULT` to `undefined`, so a function DECLARED `boolean` @@ -303,7 +303,7 @@ const DATABASE_DRIVER_ALIASES: Readonly> = Objec * * Deliberately narrower than {@link resolveDriverId}: it refuses the * contract-only aliases ({@link DriverVocabularyEntry.contractOnlyAliases}), - * because neither host accepted `OS_DATABASE_DRIVER=sqlite3` before #6345 and + * because neither host accepted `OS_DATABASE_DRIVER=sqlite3` before commit e2798fab7 and * converging the two hosts is not a licence to widen the flag for both. */ export function resolveDatabaseDriverId(driver: unknown): BuiltinDriverId | undefined { @@ -318,7 +318,7 @@ export function resolveDatabaseDriverId(driver: unknown): BuiltinDriverId | unde * ## Which question this answers, and why it is not {@link BUILTIN_DRIVER_IDS} * * The two have equal contents today and answer different questions, which is the - * distinction #6345 was written to keep visible: + * distinction commit e2798fab7 was written to keep visible: * * - {@link BUILTIN_DRIVER_IDS} — "which ids does the platform ship a CONFIG * CONTRACT for". That is what {@link DRIVER_CONFIG_SCHEMAS} is keyed by, and @@ -345,7 +345,7 @@ export function resolveDatabaseDriverId(driver: unknown): BuiltinDriverId | unde * out, but because they never enter the array this reads. Deriving a boot flag from * {@link DRIVER_ID_ALIASES} instead would have offered all four, which is a * WIDENING of what both hosts accept, dressed as a refactor: neither host has ever - * accepted `--database-driver sqlite3`, and #6345's ruling fixes the selection face + * accepted `--database-driver sqlite3`, and commit e2798fab7's ruling fixes the selection face * as the union of what they accepted the day it was written. * * ## Order diff --git a/packages/spec/src/data/driver/driver-credential-refusal.test.ts b/packages/spec/src/data/driver/driver-credential-refusal.test.ts index fa3a3ee0985..d8a893a0221 100644 --- a/packages/spec/src/data/driver/driver-credential-refusal.test.ts +++ b/packages/spec/src/data/driver/driver-credential-refusal.test.ts @@ -596,7 +596,7 @@ describe('urlUserinfoUsername — the username half of the same grammar (#8876)' }); /** - * The passthrough spelling of the same secret (#9040) — the FOURTH: #7990 + * The passthrough spelling of the same secret (commit 24206416a) — the FOURTH: #7990 * refused the top-level key, #8082 the URL userinfo, #8337 the URL query * parameter, and `options.auth.password` was the next syntax over. Measured on * mongodb@7.5.0 (the client `@objectstack/driver-mongodb` spreads @@ -626,7 +626,7 @@ describe('mongo options passthrough — credential refusal (#9040)', () => { expect(issue!.message).toContain('sys_secret'); expect(issue!.message).toContain('secret binder'); // Unlike #8337's query form, the "wins over" reassurance is TRUE here and - // load-bearing: #8696's pin measures the bound secret outranking a + // load-bearing: commit 90a12fb18's pin measures the bound secret outranking a // passthrough `auth` block at connect. expect(issue!.message).toContain('wins over'); }); @@ -689,7 +689,7 @@ describe('mongo options passthrough — credential refusal (#9040)', () => { it('accepts the legitimate passthrough byte-identically (pin) — replicaSet, tls, timeouts', () => { // The dispatch fence: the refusal must not break what the passthrough is // FOR. Includes the redacted round-trip shape (`auth` with only a - // username) — what the #9040 read path serves for an affected legacy row, + // username) — what commit 24206416a's read path serves for an affected legacy row, // and what the Studio edit form PUTs back on an untouched "Save". for (const options of [ { replicaSet: 'rs0', tls: true, connectTimeoutMS: 5000, serverSelectionTimeoutMS: 3000 }, @@ -732,7 +732,7 @@ describe('mongo options passthrough — nested credential-SPELLED keys refused a expect(issue!.message).toContain('`options.auth.token`'); expect(issue!.message).toContain('cleartext at rest'); expect(issue!.message).toContain('external.credentialsRef'); - // The "wins over" reassurance is measured for `auth.password` ONLY (#8696) + // The "wins over" reassurance is measured for `auth.password` ONLY (commit 90a12fb18) // — this message must not inherit it for a position nothing reads. expect(issue!.message).not.toContain('wins over'); }); @@ -757,7 +757,7 @@ describe('mongo options passthrough — nested credential-SPELLED keys refused a const at = result.error!.issues.filter((i) => i.path.join('.') === 'options.auth.password'); expect(at.length).toBe(1); // The measured path keeps its own prescription — including the "wins over" - // reassurance that is TRUE for this position (#8696). + // reassurance that is TRUE for this position (commit 90a12fb18). expect(at[0]!.message).toContain('wins over'); }); @@ -786,8 +786,8 @@ describe('mongo options passthrough — nested credential-SPELLED keys refused a /** * The contradictory pair "`external.credentialsRef` bound + a mongo - * `config.url` naming no user" is refused at the datasource level (#9041) — - * the "absence must be loud" half of the #8696 family. The binding is a silent + * `config.url` naming no user" is refused at the datasource level (commit d491625c1) — + * the "absence must be loud" half of commit 90a12fb18's injection. The binding is a silent * no-op at connect (`buildMongoAuth` injects only when the URL's userinfo * names a user, because `MongoClient` credentials need a username the URL must * supply and fabricating an empty one is a measured handshake failure), so the @@ -859,7 +859,7 @@ describe('datasource — bound credentialsRef + user-less mongo url refused (#90 expect(result.success, JSON.stringify(result.error?.issues)).toBe(true); expect(result.data!.config).toEqual(ds.config); expect(result.data!.external!.credentialsRef).toBe(BOUND.credentialsRef); - // Multi-host too — the form `new URL()` cannot even parse (#8696). + // Multi-host too — the form `new URL()` cannot even parse (commit 90a12fb18). const multi = parse({ ...ds, config: { url: 'mongodb://app@h1:27017,h2:27017/app' }, @@ -894,9 +894,9 @@ describe('datasource — bound credentialsRef + user-less mongo url refused (#90 it('the COMPOSED branch is #9147\'s arm, never this one — a composed config reports neither #9041 nor a `config.url` path', () => { // With no `url` the discrete `username` is live and the factory - // interpolates the bound secret into the URI it composes (#8696's other - // branch), so a composed config that NAMES a user has no contradictory - // pair at all … + // interpolates the bound secret into the URI it composes (the branch + // beside commit 90a12fb18's DSN one), so a composed config that NAMES a + // user has no contradictory pair at all … const named = parse({ name: 'events', driver: 'mongodb', @@ -904,7 +904,7 @@ describe('datasource — bound credentialsRef + user-less mongo url refused (#90 external: { ...BOUND }, }); expect(named.success, JSON.stringify(named.error?.issues)).toBe(true); - // … and one that does not is judged by #9147's own message, with #9041's + // … and one that does not is judged by #9147's own message, with commit d491625c1's // URL prescription (which would name a fix this branch cannot take) kept // out. The two arms partition the input; they never both fire. const unnamed = parse({ @@ -928,9 +928,9 @@ describe('datasource — bound credentialsRef + user-less mongo url refused (#90 }); it('fence ① — the postgres arm is NOT assumed: a user-less pg DSN + binding stays accepted', () => { - // #8873 measured pg injecting on a user-less DSN (`pg` sends a password + // Commit 096106522 measured pg injecting on a user-less DSN (`pg` sends a password // only when the server asks), so the mongo mechanism does not transfer; - // the postgres equivalent is re-judged after #8873, never inherited. + // the postgres equivalent is re-judged after commit 096106522, never inherited. const result = parse({ name: 'warehouse', driver: 'postgres', @@ -950,14 +950,14 @@ describe('datasource — bound credentialsRef + user-less mongo url refused (#90 }); expect(result.success).toBe(false); // The driver-config parse reports the type error at the same path; the - // #9041 refusal stays silent rather than judging a value that has no + // commit d491625c1's refusal stays silent rather than judging a value that has no // userinfo to read. expect(result.error!.issues.some((i) => i.message.includes("the URL's own userinfo"))).toBe(false); }); it('composes with the #9040 passthrough refusal — one artefact, both findings, own paths', () => { - // The PM-mechanism composition pin: the datasource-level #9041 refinement - // and the config-level #9040 `credentialFreeMongoOptions` judge the same + // The PM-mechanism composition pin: the datasource-level commit d491625c1 refinement + // and the config-level commit 24206416a `credentialFreeMongoOptions` judge the same // artefact independently — an input violating both reports both. const result = parse({ name: 'events', @@ -1013,10 +1013,10 @@ describe('datasource — bound credentialsRef + user-less mongo url refused (#90 * bound while the mongo `config` authors no `url` and names no `username`. * * Same silent discard, one branch over, and the branches were measured to agree - * on this input before either was refused — so this inherits #9041's ruling + * on this input before either was refused — so this inherits commit d491625c1's ruling * rather than re-opening it. What does NOT carry over is the remedy: with no * `url` the discrete `config.username` is the live field, so the fix is - * `config.username`, and #9041's "add the username to the URL's userinfo" would + * `config.username`, and commit d491625c1's "add the username to the URL's userinfo" would * name a fix this branch cannot take. * * The mechanism, measured against `default-datasource-driver-factory.ts`: with @@ -1077,7 +1077,7 @@ describe('datasource — bound credentialsRef + composed mongo config naming no }); it('an EMPTY-STRING `username` is refused too — it is the same silent no-op, and the prescription must land somewhere enforced', () => { - // Deliberate asymmetry with #9041's present-but-empty carve-out: there + // Deliberate asymmetry with commit d491625c1's present-but-empty carve-out: there // `MongoClient` throws on the empty userinfo forms, so the shape is // already loud. Here nothing throws — `username: ''` is falsy at // `buildMongoUrl`'s `user ?` test, composes the same userinfo-free URI and @@ -1165,7 +1165,7 @@ describe('datasource — bound credentialsRef + composed mongo config naming no }); it('fence — the postgres arm is NOT widened to: a composed pg config with no username + binding stays accepted', () => { - // #8873 measured `pg` receiving the bound password regardless of the DSN + // Commit 096106522 measured `pg` receiving the bound password regardless of the DSN // naming a user, so the mongo mechanism does not transfer to it on this // branch any more than it did on the URL branch. const result = parse({ diff --git a/packages/spec/src/data/driver/driver-placeholder-refusal.test.ts b/packages/spec/src/data/driver/driver-placeholder-refusal.test.ts index 5cb926f468f..36d2b37a0c0 100644 --- a/packages/spec/src/data/driver/driver-placeholder-refusal.test.ts +++ b/packages/spec/src/data/driver/driver-placeholder-refusal.test.ts @@ -172,7 +172,7 @@ describe('mongo `options` passthrough — the deep judgement (#8336)', () => { }); /** - * #8495 — the #8336 shape one surface over: memory `persistence.path` (file + * Commit 4bfe1a539 — the #8336 shape one surface over: memory `persistence.path` (file * persistence and the `auto` override) and `persistence.key` (localStorage) * are config-material, not record data. A `${DATA_DIR}` written there is * resolved by nothing — the driver would create and write a literal @@ -236,7 +236,7 @@ describe('memory `initialData` stays UNJUDGED — the deliberate #8336 exclusion // The mother ruling's memory-driver exclusion was argued from exactly this: // `initialData` carries arbitrary record values, where `${…}` may be the // real payload (a template string a downstream renderer consumes). The - // #8495 refusal covers `persistence.path`/`persistence.key` ONLY. + // commit 4bfe1a539's refusal covers `persistence.path`/`persistence.key` ONLY. const config = { initialData: { templates: [{ id: '1', body: 'Hello ${name}, your order ${order_id} shipped.' }], diff --git a/packages/spec/src/data/driver/memory.zod.ts b/packages/spec/src/data/driver/memory.zod.ts index eb82214af9f..4e7b1c339cb 100644 --- a/packages/spec/src/data/driver/memory.zod.ts +++ b/packages/spec/src/data/driver/memory.zod.ts @@ -101,7 +101,7 @@ export const FilePersistenceConfigSchema = lazySchema(() => strictObject( /** * File path to persist data (JSON format). Defaults to `.objectstack/data/memory-driver.json`. * - * `${…}` placeholder syntax is refused (#8495, the #8336 shape one surface + * `${…}` placeholder syntax is refused (commit 4bfe1a539, the #8336 shape one surface * over): nothing resolves it, so the driver would create and write a * literal `./${DATA_DIR}/…` path — authored under a false belief. The * memory driver's `initialData` stays deliberately unjudged (record @@ -149,7 +149,7 @@ export const LocalStoragePersistenceConfigSchema = lazySchema(() => strictObject /** * localStorage key. Defaults to `objectstack:memory-db`. * - * `${…}` placeholder syntax is refused (#8495): nothing resolves it, so + * `${…}` placeholder syntax is refused (commit 4bfe1a539): nothing resolves it, so * the driver would write under the literal placeholder-bearing key. */ key: placeholderFree(z.string(), 'persistence.key').optional().describe('localStorage key for persisted data'), @@ -199,7 +199,7 @@ export const AutoPersistenceConfigSchema = lazySchema(() => strictObject( type: z.literal('auto'), /** * File path override when running in Node.js. - * `${…}` placeholder syntax is refused (#8495) — same judgment as the + * `${…}` placeholder syntax is refused (commit 4bfe1a539) — same judgment as the * `file` branch's `path`; the auto-detected file adapter resolves nothing. */ path: placeholderFree(z.string(), 'persistence.path').optional().describe('File path override for Node.js environments'), @@ -227,7 +227,7 @@ export const AutoPersistenceConfigSchema = lazySchema(() => strictObject( ), /** * localStorage key override when running in a browser. - * `${…}` placeholder syntax is refused (#8495) — same judgment as the + * `${…}` placeholder syntax is refused (commit 4bfe1a539) — same judgment as the * `local` branch's `key`. */ key: placeholderFree(z.string(), 'persistence.key').optional().describe('localStorage key override for browser environments'), diff --git a/packages/spec/src/data/driver/mongo.test.ts b/packages/spec/src/data/driver/mongo.test.ts index 45f7e4d333a..bdc32e1b4b5 100644 --- a/packages/spec/src/data/driver/mongo.test.ts +++ b/packages/spec/src/data/driver/mongo.test.ts @@ -98,7 +98,7 @@ describe('MongoConfigSchema', () => { }); describe('MongoDriverSpec', () => { - // `mongodb` since #6345: the canonical driver id was renamed to the spelling + // `mongodb` since commit e2798fab7: the canonical driver id was renamed to the spelling // both boot hosts and `@objectstack/driver-mongodb` already used, so driver // selection and config-contract selection are one string. `mongo` stays an // accepted ALIAS — pinned in `config-registry.test.ts`. diff --git a/packages/spec/src/data/driver/mongo.zod.ts b/packages/spec/src/data/driver/mongo.zod.ts index 95dc7db34d9..25b9c689165 100644 --- a/packages/spec/src/data/driver/mongo.zod.ts +++ b/packages/spec/src/data/driver/mongo.zod.ts @@ -124,13 +124,13 @@ export const MongoConfigSchema = lazySchema(() => strictObject( * (`replicaSet`, `tls`, timeouts, …). Placeholder-free since #8336, judged * DEEP: every nested string value reaches the client, and this passthrough * is exactly where a refusal on `url`/`host` would otherwise displace the - * placeholder to. Credential-free since #9040 — `auth.password` was the + * placeholder to. Credential-free since commit 24206416a — `auth.password` was the * FOURTH spelling of the inline secret (after the top-level key #7990, URL * userinfo #8082 and URL query params #8337): the client resolves the block * into `MongoCredentials`, so a passthrough password authenticated for real * while sitting cleartext in `sys_metadata`. A non-empty `auth.password` is * refused with the binder prescription; `auth.username` stays writable - * (#8876's asymmetry — a username is not credential material). The + * (commit d634e665b's asymmetry — a username is not credential material). The * nested-position finding widened the walk: a non-empty string under a * credential-SPELLED key (`password`, `authToken`, and the former aliases) * is refused at ANY object depth of the passthrough, so a nested position @@ -181,7 +181,7 @@ export const getMongoConfigJsonSchema = driverConfigJsonSchema(MongoConfigSchema * described. */ export const MongoDriverSpec = { - // `mongodb`, not `mongo`, since #6345: the canonical driver id was renamed to + // `mongodb`, not `mongo`, since commit e2798fab7: the canonical driver id was renamed to // the spelling both boot hosts, the `@objectstack/driver-mongodb` package and // every URL scheme already used, so driver selection and config-contract // selection are one string. `mongo` remains an accepted alias. diff --git a/packages/spec/src/data/driver/mysql.zod.ts b/packages/spec/src/data/driver/mysql.zod.ts index 9b7b22a4750..4c4aa0f54a9 100644 --- a/packages/spec/src/data/driver/mysql.zod.ts +++ b/packages/spec/src/data/driver/mysql.zod.ts @@ -115,7 +115,7 @@ export const MysqlConfigSchema = lazySchema(() => strictObject( /** * TLS on/off. `true` reaches `mysql2` as its own default TLS options * (`rejectUnauthorized: true`), not the bare boolean — mysql2 rejects a - * boolean outright (#8874). Certificates and verification live in the + * boolean outright (commit d70428ae7). Certificates and verification live in the * datasource-level `ssl` block. */ ssl: DriverSslToggleSchema.optional().meta({ title: 'Use SSL/TLS' }), diff --git a/packages/spec/src/data/driver/pg-url-grammar.server.ts b/packages/spec/src/data/driver/pg-url-grammar.server.ts index bb495f34c75..43671a071db 100644 --- a/packages/spec/src/data/driver/pg-url-grammar.server.ts +++ b/packages/spec/src/data/driver/pg-url-grammar.server.ts @@ -42,7 +42,7 @@ import { parse as parsePostgresUrl } from 'pg-connection-string'; * (`postgresql://[user@][host][:port][/dbname][?params]`) that nothing * enforced. The shared `credentialFreeUrl` / `placeholderFree` checks are * string-boundary scans by design — their refusal to parse is load-bearing - * for mongo's multi-host and `+srv` forms (#8696), so the parse question is + * for mongo's multi-host and `+srv` forms (commit 90a12fb18), so the parse question is * asked HERE, per-driver, of the postgres client's own grammar: `parse` from * `pg-connection-string@2.14.0`, the parser `pg@8.22.0` itself runs a * connection string through (`ConnectionParameters`). What that parser @@ -50,7 +50,7 @@ import { parse as parsePostgresUrl } from 'pg-connection-string'; * `ERR_INVALID_URL`; a non-numeric port; a malformed percent-escape) used to * parse green at publish and then fail at connect with a bare `Invalid URL` * whose own `input` field `pg` redacts — an error naming neither the value - * nor the datasource. Same posture as #8873's runtime arm: ask `pg`'s + * nor the datasource. Same posture as commit 096106522's runtime arm: ask `pg`'s * grammar, never re-model it. */ const PG_UNPARSEABLE_URL_REFUSED = (key: string, detail: string): string => diff --git a/packages/spec/src/data/driver/postgres.test.ts b/packages/spec/src/data/driver/postgres.test.ts index ece5d4993a3..80a0357bacb 100644 --- a/packages/spec/src/data/driver/postgres.test.ts +++ b/packages/spec/src/data/driver/postgres.test.ts @@ -201,7 +201,7 @@ describe('PostgresConfigSchema', () => { * The describe text always documented the postgres URL grammar; until #9091 * the value was only string-scanned (credentials #8082/#8337, placeholders * #8336) because the SHARED helper's refusal to parse is load-bearing for - * mongo's multi-host/`+srv` forms (#8696). The parse question is asked + * mongo's multi-host/`+srv` forms (commit 90a12fb18). The parse question is asked * per-driver, of `pg`'s own parser (`pg-connection-string`). * * Envelope note (the standing minimum for rejection pins): the zod issue's diff --git a/packages/spec/src/data/driver/postgres.zod.ts b/packages/spec/src/data/driver/postgres.zod.ts index ef8c798eabc..673d368b085 100644 --- a/packages/spec/src/data/driver/postgres.zod.ts +++ b/packages/spec/src/data/driver/postgres.zod.ts @@ -93,7 +93,7 @@ function pgFileReadingQueryParams(value: string): string[] { /** * Attach the #9091 pg-grammar refusal to the postgres `url` key — per-driver * by design (see `pg-url-grammar.server.ts`; the shared helpers must - * keep refusing to parse for mongo's sake, #8696). Composes with + * keep refusing to parse for mongo's sake, commit 90a12fb18). Composes with * `credentialFreeUrl` (#8082/#8337) and `placeholderFree` (#8336) the same * way those compose with each other: independent `superRefine`s judging one * value, each reporting its own finding. diff --git a/packages/spec/src/data/esignature-deadline-keys-retirement.test.ts b/packages/spec/src/data/esignature-deadline-keys-retirement.test.ts index a4eaca1c119..83cfcdf386e 100644 --- a/packages/spec/src/data/esignature-deadline-keys-retirement.test.ts +++ b/packages/spec/src/data/esignature-deadline-keys-retirement.test.ts @@ -33,7 +33,7 @@ import { MIGRATIONS_BY_MAJOR, RETIRED_KEYS_BY_MAJOR } from '../migrations/regist // `kernel/MetadataPluginConfig:additionalTypes` precedent) — the registration // is two `RETIRED_KEYS_BY_MAJOR[18]` entries plus one D3 semantic entry. // -// On the assertion set (the #8586 / #14676 / #14477 precedent): a schema +// On the assertion set (the #8586 / commit 13c48c2a5 / #14477 precedent): a schema // refusal raises a `ZodError` whose issues carry `code` and `path` but no // ADR-0112 `status` — that envelope belongs to the API error surface. So these // pins assert the strongest set this surface really has: refusal, the issue diff --git a/packages/spec/src/data/feed.test.ts b/packages/spec/src/data/feed.test.ts index 7a92b235ff0..0dca028af7e 100644 --- a/packages/spec/src/data/feed.test.ts +++ b/packages/spec/src/data/feed.test.ts @@ -42,7 +42,7 @@ describe('SYS_ACTIVITY_BUILTIN_TYPES (#11807)', () => { }); /** - * The vocabulary is OPEN (#11507): the published shape must not be able to + * The vocabulary is OPEN (commit 88b9d749a): the published shape must not be able to * reject anything. A plain readonly tuple has no parse/validate affordance; * a z.enum here would read as a value-domain validator and re-close the * vocabulary the day someone calls .parse() with it. diff --git a/packages/spec/src/data/feed.zod.ts b/packages/spec/src/data/feed.zod.ts index b62ac1a8598..e2483e8252e 100644 --- a/packages/spec/src/data/feed.zod.ts +++ b/packages/spec/src/data/feed.zod.ts @@ -12,10 +12,10 @@ import { z } from 'zod'; * `FeedItemType` is not backend-free: it has no backend *import*, yet it is the * TARGET of the map UI consumers apply to the `sys_activity.type` column — a * backend *coupling* — and that column's vocabulary is OPEN and - * author-extensible (maintainer ruling 2026-08-24, #11507). `FeedItemType` is + * author-extensible (maintainer ruling 2026-08-24, commit 88b9d749a). `FeedItemType` is * therefore the built-in guidance half of that map, never the value domain of * an authoring surface: `RecordActivityProps.types` accepts contributed kinds - * beyond it (#11658), and consumers must map unknown `sys_activity.type` values + * beyond it (commit 1a6a19c31), and consumers must map unknown `sys_activity.type` values * to a fallback rather than drop them. `SYS_ACTIVITY_BUILTIN_TYPES` below is * the published built-in vocabulary of the `sys_activity.type` column, * co-located with `FeedItemType` because UI consumers map one onto the other. @@ -66,7 +66,7 @@ export type FeedFilterMode = z.input; * ## Built-in set, NOT the column's value domain * * `sys_activity.type` is an OPEN, author-extensible vocabulary (maintainer - * ruling 2026-08-24, #11507): this list is the floor the platform itself writes + * ruling 2026-08-24, commit 88b9d749a): this list is the floor the platform itself writes * and offers in pickers/filters, never the ceiling of legal values. An app may * contribute its own values — the sanctioned authoring channel is * `activityMilestones[].type` (ADR-0052 §5b.2, `z.string()`, forwarded @@ -77,7 +77,7 @@ export type FeedFilterMode = z.input; * * - ⛔ Never use it to validate, reject, or filter OUT values. A row whose * `type` is not in this set is legitimate; render it (generic fallback), do - * not drop it. Every CLOSED map over this vocabulary is a bug (#11507). + * not drop it. Every CLOSED map over this vocabulary is a bug (commit 88b9d749a). * - It is deliberately a plain `as const` tuple rather than a `z.enum`: a Zod * schema here would read as a validator and quietly re-close the vocabulary. * - This is a different vocabulary from {@link FeedItemType}: `FeedItemType` is @@ -108,7 +108,7 @@ export const SYS_ACTIVITY_BUILTIN_TYPES = [ /** * One built-in `sys_activity.type` value — derived from * {@link SYS_ACTIVITY_BUILTIN_TYPES}. The column's runtime value domain is - * wider (`string`): the vocabulary is open and author-extensible (#11507), so + * wider (`string`): the vocabulary is open and author-extensible (commit 88b9d749a), so * code that READS rows must type the column as `string` and treat this union as * the known-built-in narrowing only. */ diff --git a/packages/spec/src/data/field-value.zod.ts b/packages/spec/src/data/field-value.zod.ts index 1502cc5c70c..0f93fd355c5 100644 --- a/packages/spec/src/data/field-value.zod.ts +++ b/packages/spec/src/data/field-value.zod.ts @@ -122,8 +122,8 @@ export const CLOCK_TIME_TYPES: ReadonlySet = new Set([ * Measured on #15683, so the gap is a number rather than a caveat: * `driver-memory` canonicalises a declared temporal write to ISO TEXT (#4047), * for a `Date` input and a string input alike, so a positive text operator - * MATCHES there — the exact complement of this set's answer (#17348, pinned as - * a named divergence in that driver's conformance suite). `formula`'s + * MATCHES there — the exact complement of this set's answer (commit 51efbf116 + * pinned it as a named divergence in that driver's conformance suite). `formula`'s * `matchesFilterCondition(record, filter)` takes a bare record and its own * docblock says it "has no schema to consult"; `having` filters AGGREGATED rows * whose columns carry no field declaration at all. Neither could key on the diff --git a/packages/spec/src/data/field.test.ts b/packages/spec/src/data/field.test.ts index b96dc9b7894..1db44884d9c 100644 --- a/packages/spec/src/data/field.test.ts +++ b/packages/spec/src/data/field.test.ts @@ -2017,7 +2017,7 @@ describe('ADR-0113 — required is a write contract; storage.notNull is the colu }); /** - * #16867 — the flattened column-constraint spellings must not be renamed onto + * Commit 0ee32edef — the flattened column-constraint spellings must not be renamed onto * `required`. * * The defect these pin against was not a silent one: the refusal fired, loudly, @@ -2604,7 +2604,7 @@ describe('Relationship target — `reference` required on lookup/master_detail ( }, ); - // [#16126] A whitespace-only target is the same hole a third way: it names + // [commit 859ded3ec] A whitespace-only target is the same hole a third way: it names // no object either (no whitespace-bearing string can match the declared // object-name grammar), and it is what a cleared target picker emits when // the value round-trips through an input. The notion of blank is `.trim()`, diff --git a/packages/spec/src/data/field.zod.ts b/packages/spec/src/data/field.zod.ts index ef1593846ab..a008f068bc0 100644 --- a/packages/spec/src/data/field.zod.ts +++ b/packages/spec/src/data/field.zod.ts @@ -971,7 +971,7 @@ export const InlineGridColumnSchema = lazySchema(() => strictObject({ * therefore a NESTED key, and `aliases` renames onto a flat one — the same * reason `currency` is answered in prose a few lines below. * - * ## Why it may not rename onto `required` (#16867) + * ## Why it may not rename onto `required` (commit 0ee32edef) * * It used to: `notNull: 'required'` sat in the alias table beside `isRequired` * and `mandatory`, and because `aliases` is consulted only AFTER this channel @@ -2075,7 +2075,7 @@ export const FieldSchema = lazySchema(() => { // `Field.masterDetail()` take the target as their first positional // argument, so helper-authored fields cannot miss it. // - // [#16126] The emptiness test is applied to the TRIMMED value, so a + // [commit 859ded3ec] The emptiness test is applied to the TRIMMED value, so a // whitespace-only `reference` joins `undefined` and `''` under this one // issue and this one message. It names no object either: the declared // grammar for an object name is `/^[a-z_][a-z0-9_]*$/` (`ObjectSchema`'s @@ -2356,7 +2356,7 @@ export const FieldSchema = lazySchema(() => { // rows to be KEPT and gets them DELETED — data loss relative to the declared // intent, silently, at the moment the parent goes away. Honoring it is ruled // out (a detail row whose master reference is nulled becomes an unreachable - // orphan — the outcome #8772/#9138 exist to prevent). `field.deleteBehavior` + // orphan — the outcome commit 75b7c240a (#9138) prevents). `field.deleteBehavior` // here is pre-`.overwrite`, so `undefined` means "not authored" — a bare // `master_detail` (the overwhelmingly common spelling) never fires this. if (field.type === 'master_detail' && field.deleteBehavior === 'set_null') { diff --git a/packages/spec/src/data/filter-comparand-shape.test.ts b/packages/spec/src/data/filter-comparand-shape.test.ts index 811c227c847..380c6bbc7be 100644 --- a/packages/spec/src/data/filter-comparand-shape.test.ts +++ b/packages/spec/src/data/filter-comparand-shape.test.ts @@ -261,7 +261,7 @@ describe('the list-comparand shape door (#5869) runs inside parseFilterAST (#922 // row, and a trim here would re-open the split in the other direction. ["[' ', 'M']", [' ', 'M']], // The 2026-08-11 `{ $field }` carve-out (#7596), reaching this door - // under #19377 — the same two-door question, one endpoint spelling over. + // by commit a60c913de — the same two-door question, one endpoint spelling over. ["[{ $field }, 'M']", [{ $field: 'a' }, 'M']], ["['A', { $field }]", ['A', { $field: 'b' }]], ['[{ $field }, { $field }]', [{ $field: 'a' }, { $field: 'b' }]], @@ -294,7 +294,7 @@ describe('the list-comparand shape door (#5869) runs inside parseFilterAST (#922 // The ruling is the oldest of the four and the last to reach this door: it // removed `FieldReferenceSchema` from both endpoint unions and published the // sentence "A { $field } reference is NOT an endpoint shape", while this door - // went on lowering such a range unchanged (#19377). + // went on lowering such a range unchanged (until commit a60c913de). it.each([ ['a reference as the MIN bound', { at: { $between: [{ $field: 'a' }, 'M'] } }], @@ -409,7 +409,7 @@ describe('the list-comparand shape door (#5869) runs inside parseFilterAST (#922 // ⚠️ `$in` / `$nin` MEMBERS carrying a `{ $field }` reference are the SAME // #7596 ruling one position over, published by `SET_MEMBER_DESCRIPTION`, and - // this door still lowers them unchanged — measured under #19377 and filed + // this door still lowers them unchanged — measured for commit a60c913de and filed // separately. ⛔ Deliberately NOT pinned here in either direction: pinning a // measured defect green reads as a ruling nobody made, and refusing it would // be a narrowing of a published face this card was never given. diff --git a/packages/spec/src/data/filter-comparand-shape.ts b/packages/spec/src/data/filter-comparand-shape.ts index b257aa9d037..a639231c230 100644 --- a/packages/spec/src/data/filter-comparand-shape.ts +++ b/packages/spec/src/data/filter-comparand-shape.ts @@ -159,7 +159,7 @@ * * ## Refused BY RULING, 2026-09-20: a BLANK `$between` ENDPOINT (#19071) * - * The runtime twin of the schema door's 2026-09-17 rule (#18012). That ruling + * The runtime twin of the schema door's 2026-09-17 rule (commit 176b03582). That ruling * wrote "BOTH are required NON-BLANK: an empty string, null and undefined are * refused, and the refusal names the blank side" into the PUBLISHED endpoint * contract (`RANGE_ENDPOINT_DESCRIPTION`, `./filter.zod.ts`) and enforced it at @@ -206,7 +206,7 @@ * `{ $between: [{ $field: 'a' }, 'M'] }` unchanged — one published sentence * with two truth values, decided by which door a caller came through, and the * door that passed it is the one an embedder reaches by handing a lowered - * filter straight to a driver. Measured again under #19377 before the change; + * filter straight to a driver. Measured again for commit a60c913de before the change; * closed here the way #19071 closed the blank spelling one endpoint over. * * The scope is the `$between` ENDPOINT position and nothing wider: @@ -641,7 +641,7 @@ function blankRangeBoundError( /** * A `$between` bound that is a `{ $field }` REFERENCE — refused BY RULING, - * 2026-08-11 (#7596), implemented at this door under #19377; see the module + * 2026-08-11 (#7596), implemented at this door by commit a60c913de; see the module * note's fourth "Refused BY RULING" section. * * Its own message rather than an arm of any of the three above: those @@ -902,7 +902,7 @@ function assertFieldListComparands( ); } // Then the `{ $field }` REFERENCE carve-out (2026-08-11 ruling, #7596, - // reaching this door under #19377) — LAST, so every pair that already + // reaching this door by commit a60c913de) — LAST, so every pair that already // carried a refusal keeps the message it had, and only a pair this door // accepts today can reach it. Shape, not value: `{ $field: 42 }` is the // shape the author wrote and is named as such, one step before the TYPE diff --git a/packages/spec/src/data/filter-comparand-type.ts b/packages/spec/src/data/filter-comparand-type.ts index 27af92752c1..c859a9cf3b9 100644 --- a/packages/spec/src/data/filter-comparand-type.ts +++ b/packages/spec/src/data/filter-comparand-type.ts @@ -268,7 +268,7 @@ const NOT_APPLIED = * and at `$gt` / `$gte` / `$lt` / `$lte` — or an `$in` / `$nin` / `$between` * member — "write null" produced exactly the null shapes refused one door over * (2026-08-31, 2026-09-01). Position-safe means following it never lands in a - * refusal, whatever position it was emitted at (#14426). + * refusal, whatever position it was emitted at (commit 40a44b91b). */ function undefinedComparandRefusal(context: string | undefined, path: string): Error { return invalidComparandError( diff --git a/packages/spec/src/data/filter-subtree-provenance.test.ts b/packages/spec/src/data/filter-subtree-provenance.test.ts index a01b339c2e5..87f8ae43e33 100644 --- a/packages/spec/src/data/filter-subtree-provenance.test.ts +++ b/packages/spec/src/data/filter-subtree-provenance.test.ts @@ -10,7 +10,7 @@ * tree, an aliased node under conflicting arms — must land on `null`, never on * `'author'`. * - * [#8836] The last block pins the one shape that does NOT land there — a + * [commit 1850ebbb0] The last block pins the one shape that does NOT land there — a * vouchable filter object reused across requests — and the caller-side * invariant that keeps it out of reach. Grep for "may outlive the request". */ @@ -164,7 +164,7 @@ describe('resolveFilterSubtreeProvenance', () => { }); /** - * [#8836, from the #8794 survey] The invariant the fail-closed direction + * [commit 1850ebbb0, from the survey it records] The invariant the fail-closed direction * silently depends on, made executable: * * > no filter object that can be vouched `'author'` may outlive the request @@ -182,7 +182,7 @@ describe('resolveFilterSubtreeProvenance', () => { * `options.where` itself or on the arms of a pure `$and` root). So the guard * this block can offer is that the consequence stays visible and stays * asserted: a future change that makes any expectation below go red is a - * change to the mark's mechanism, which #8794's ruling routes to a spec-seat + * change to the mark's mechanism, which the survey's ruling (commit 1850ebbb0) routes to a spec-seat * ruling BEFORE implementation — not something to fix by editing these * numbers. */ diff --git a/packages/spec/src/data/filter.test.ts b/packages/spec/src/data/filter.test.ts index b007454a56b..5dd40a1afe7 100644 --- a/packages/spec/src/data/filter.test.ts +++ b/packages/spec/src/data/filter.test.ts @@ -344,7 +344,7 @@ describe('RangeOperatorSchema', () => { }); // ========================================================================== - // #6571 — BOTH endpoints accept the STRING the platform itself produces. + // Commit 2f3e79351 — BOTH endpoints accept the STRING the platform itself produces. // // Before this was pinned each endpoint union was `number | Date | // FieldReference`, so every accepted shape below threw — including the shape @@ -463,7 +463,7 @@ describe('RangeOperatorSchema', () => { // REMOVE — declared = enforced (ADR-0049). // // The tests above this block asserted the ACCEPTANCE of exactly these shapes - // (#6571 pinned `['2026-01-01', { $field: 'contract.end_date' }]` and + // (commit 2f3e79351 pinned `['2026-01-01', { $field: 'contract.end_date' }]` and // `[{ $field: 'a.min' }, { $field: 'a.max' }]`); they are flipped here rather // than deleted, so the removal is pinned in the same place the declaration // was. @@ -583,7 +583,7 @@ describe('RangeOperatorSchema', () => { }); // ========================================================================== - // #18012 — a BLANK endpoint is ruled out, in both endpoint unions and in both + // Commit 176b03582 — a BLANK endpoint is ruled out, in both endpoint unions and in both // copies of the schema. Ruled 2026-09-17 (decision batch #146 item 5, letter // A): `$between` requires two endpoints that are present and non-empty. // @@ -1085,7 +1085,7 @@ describe('TypeScript Type System', () => { }); /** - * #6571 — the TYPED half of the range contract, the exact mirror of the + * Commit 2f3e79351 — the TYPED half of the range contract, the exact mirror of the * ordering block above. Checked by `pnpm typecheck`, NOT by the runtime * expectation below: vitest never typechecks, so reverting `filter.zod.ts` * leaves this test GREEN under vitest and RED under `tsc`. Measured on the diff --git a/packages/spec/src/data/filter.zod.ts b/packages/spec/src/data/filter.zod.ts index 0912c2776e5..23f9f8209ab 100644 --- a/packages/spec/src/data/filter.zod.ts +++ b/packages/spec/src/data/filter.zod.ts @@ -650,7 +650,7 @@ export const SetOperatorSchema = lazySchema(() => z.object({ })); /** - * The endpoint contract shared by both of `$between`'s bounds (#6571). + * The endpoint contract shared by both of `$between`'s bounds (commit 2f3e79351). * * Module-private on purpose, exactly like {@link ORDERING_COMPARAND_DESCRIPTION}: * it is documentation attached to a slot, not an authorable surface of its own, @@ -704,7 +704,7 @@ const RANGE_ENDPOINT_DESCRIPTION = * writes the two bounds separately (`{ $gte: { $field: 'a' }, $lte: { $field: 'b' } }`), * which every face already answers. * - * ## Why `string` is in BOTH endpoint unions (#6571) + * ## Why `string` is in BOTH endpoint unions (commit 2f3e79351) * * This is the same contradiction {@link ComparisonOperatorSchema} carried until * #5685, in the one slot where it bites hardest. Until this was written down @@ -740,7 +740,7 @@ const RANGE_ENDPOINT_DESCRIPTION = * reach for with the resolver's own output in hand, and the old declaration * told them that output was invalid. * - * ## Why a BARE string, and not an ISO-shaped refinement (#6571 rider ①) + * ## Why a BARE string, and not an ISO-shaped refinement (commit 2f3e79351, rider ①) * * Identical to {@link ComparisonOperatorSchema}'s finding, and re-measured for * the tuple: this schema is field-**agnostic** (it never sees which column the @@ -763,7 +763,7 @@ const RANGE_ENDPOINT_DESCRIPTION = * nothing, at every backend. */ /** - * [#18012] The author-facing refusal for a BLANK `$between` endpoint — the + * [commit 176b03582] The author-facing refusal for a BLANK `$between` endpoint — the * empty string and `undefined`, at either bound. Ruled 2026-09-17 (decision * batch #146 item 5, letter A): both endpoints present and non-empty. * @@ -799,7 +799,7 @@ function blankRangeBoundMessage(index: 0 | 1): string { /** * [#7596] One `$between` endpoint, with the `{ $field }` shape ruled out — and, - * since the 2026-09-17 ruling (#18012), the BLANK endpoint likewise. + * since the 2026-09-17 ruling (commit 176b03582), the BLANK endpoint likewise. * * ## Why the union's `error` carries three of the four refusals * @@ -818,7 +818,7 @@ function blankRangeBoundMessage(index: 0 | 1): string { * * `null`, `undefined` and `{ $field }` never passed the union; for all three * the ruling adds only a POINTED SENTENCE. `''` is a string and the union - * ACCEPTS it, so the empty-string arm is the one place where #18012 changes + * ACCEPTS it, so the empty-string arm is the one place where commit 176b03582 changes * what parses. It rides an ELEMENT-level `superRefine` — not the tuple-level * one the paragraph above rules out — which runs exactly when this endpoint * passed the union, i.e. precisely when there is an `''` to report. @@ -836,7 +836,7 @@ const rangeEndpointSchema = (index: 0 | 1) => // mechanism the `{ $field }` shape uses one line down. issue.input === null ? nullListComparandMemberMessage(`$between endpoint at index ${index}`) - // [#18012] `undefined` never passed it either — an absent bound is the + // [commit 176b03582] `undefined` never passed it either — an absent bound is the // same replace-only substitution, pointed at the side that is missing. : issue.input === undefined ? blankRangeBoundMessage(index) @@ -844,7 +844,7 @@ const rangeEndpointSchema = (index: 0 | 1) => ? listPositionFieldReferenceMessage(`$between endpoint at index ${index}`) : undefined, }).superRefine((endpoint, ctx) => { - // [#18012] The empty string is the one blank spelling the union accepts. + // [commit 176b03582] The empty string is the one blank spelling the union accepts. // ⛔ Not a trim and not a whitespace rule: the ruling is the empty string, // and widening it here would narrow a published face further than ruled. if (endpoint !== '') return; @@ -971,7 +971,7 @@ export const RangeOperatorSchema = lazySchema(() => z.object({ * driver-conformance ledger is empty. Read the open set from a run of that gate * rather than from this paragraph. * - * ### A JSON-stored column changes what `$contains` ASKS (#17590, maintainer ruling via the director seat, 2026-09-12) + * ### A JSON-stored column changes what `$contains` ASKS (commit e04a0aff2, maintainer ruling via the director seat, 2026-09-12) * * **On a `multiple: true` field or a `JSON_COLUMN_TYPES` member, `$contains: v` * is a MEMBERSHIP test — `v` is a member of the stored array — answered @@ -1018,11 +1018,11 @@ export const RangeOperatorSchema = lazySchema(() => z.object({ * `$contains: 'red'`, the same over-match the SQL family just lost) and * answers NOTHING at all for a `multiple: true` NUMBER, while its reference * matcher answers no array at all. That whole axis — every non-equality arm - * over a stored array, in both directions — is measured and owned by #17286, - * which recorded the semantics as undecided; this ruling is the decision it + * over a stored array, in both directions — was measured on a tracking card + * that recorded the semantics as undecided; this ruling is the decision it * was missing. ⚠️ So an application whose tests run on the in-memory double * and whose production runs SQL still gets two answers from one filter here. - * Read the open set from that card, ⛔ not from this paragraph. + * That card is gone: measure `driver-memory` for the open set, ⛔ not this text. * * The comparand stays a STRING on every column ({@link CONTAINS_DESCRIPTION}), * so a member that is stored as a JSON number or boolean is named by its text: @@ -1634,7 +1634,7 @@ export const FieldOperatorsSchema = lazySchema(() => z.object({ $in: setMembershipSchema('$in').optional().describe(SET_MEMBER_DESCRIPTION), $nin: setMembershipSchema('$nin').optional().describe(SET_MEMBER_DESCRIPTION), // Range. `string` is in BOTH endpoint unions for the reason - // {@link RangeOperatorSchema} gives at length (#6571): the date-macro resolver + // {@link RangeOperatorSchema} gives at length (commit 2f3e79351): the date-macro resolver // walks into arrays, so a token range resolves to two ISO/clock STRINGS, and // this package's own `temporal-conformance.ts` corpus spells that shape. // `FieldReferenceSchema` is NOT in them, for the reason the same docblock @@ -2182,7 +2182,7 @@ export type Filter = { $lte?: T[K] extends number ? number : T[K] extends Date | string ? T[K] | string : never; $in?: T[K][]; $nin?: T[K][]; - // Range (#6571). The TYPED half of what {@link RangeOperatorSchema} + // Range (commit 2f3e79351). The TYPED half of what {@link RangeOperatorSchema} // declares, and the exact mirror of the ordering guard above — a range // IS its two ordering bounds, so the two must agree slot for slot: // - a `Date` field also takes the ISO STRINGS the date-macro resolver diff --git a/packages/spec/src/data/hook-api.ts b/packages/spec/src/data/hook-api.ts index 09f44c5b794..9a97dbb4ade 100644 --- a/packages/spec/src/data/hook-api.ts +++ b/packages/spec/src/data/hook-api.ts @@ -311,7 +311,7 @@ export interface HookApi { * public declarations reference STRUCTURALLY, re-exported so they are nameable * from the entry that publishes them. * - * The governing text is the maintainer ruling of 2026-08-23 on #11350, recorded + * The governing text is the maintainer ruling of 2026-08-23 (commit ece4dad31), recorded * in `packages/spec/scripts/check-entry-nameability.ts` and chartered * 2026-08-25 on #11709: a type that appears structurally in an entry's public * declarations must be nameable from that same entry. diff --git a/packages/spec/src/data/hook.test.ts b/packages/spec/src/data/hook.test.ts index 355089b21e6..6a6e0f9c3f5 100644 --- a/packages/spec/src/data/hook.test.ts +++ b/packages/spec/src/data/hook.test.ts @@ -519,7 +519,7 @@ describe('HookContextSchema', () => { }); }); - // [#13644] The declared referential-cleanup marker. The parse legs matter + // [commit 34ce8e7db] The declared referential-cleanup marker. The parse legs matter // because this schema is non-strict in the STRIPPING sense: an UNDECLARED // key is silently dropped by `.parse()` (the `roles` tombstone above is the // history), so "the engine sets it" is worthless unless the schema declares diff --git a/packages/spec/src/data/hook.zod.ts b/packages/spec/src/data/hook.zod.ts index a6382d62c9e..7666b7c4a26 100644 --- a/packages/spec/src/data/hook.zod.ts +++ b/packages/spec/src/data/hook.zod.ts @@ -1063,7 +1063,7 @@ export const HookContextSchema = lazySchema(() => z.object({ * lookup removing the deleted member). Absent on every other dispatch; read * it as `ctx.referentialFieldClear === true`. * - * ## Why a declared key (#13644) + * ## Why a declared key (commit 34ce8e7db) * * The engine builds the cleanup write's context by INHERITING the caller's * envelope (`{ ...callerContext, transaction, __referentialFieldClear: true }`), diff --git a/packages/spec/src/data/index.ts b/packages/spec/src/data/index.ts index 1e26b4bd107..b0de45a0838 100644 --- a/packages/spec/src/data/index.ts +++ b/packages/spec/src/data/index.ts @@ -121,7 +121,7 @@ export * from './aggregation-conformance'; // wrote is what you read back", asserted on type as well as value, plus the // injectivity pairs a per-value check cannot see. The census's nine other // case-sets are all about WHICH ROWS come back; this is the one about what the -// values in them are, and its absence is why that family (#12380, #11535, +// values in them are, and its absence is why that family (commit 4045b954d, #11535, // #11782, #10995) kept arriving one card at a time. export * from './value-roundtrip-conformance'; export * from './date-macros.zod'; diff --git a/packages/spec/src/data/mapping.test.ts b/packages/spec/src/data/mapping.test.ts index cf0c9209c84..cdaa5398ba0 100644 --- a/packages/spec/src/data/mapping.test.ts +++ b/packages/spec/src/data/mapping.test.ts @@ -97,7 +97,7 @@ describe('ImportFieldMappingSchema', () => { expect(mapping.transform).toBe('lookup'); }); - // ── `params` lookup keys retired in the 17.x line (#10329, ADR-0049) ─────── + // ── `params` lookup keys retired in the 17.x line (commit 15d58dbf1, ADR-0049) ── // // `object` / `fromField` / `toField` / `autoCreate` declared a per-entry // reference-resolution dialect the import path never implemented: `lookup` @@ -445,7 +445,7 @@ describe('MappingSchema', () => { }, { // `lookup` is a pass-through: the import pipeline resolves the - // reference from the target field's own metadata (#10329). + // reference from the target field's own metadata (commit 15d58dbf1). source: 'account_name', target: 'account_id', transform: 'lookup' diff --git a/packages/spec/src/data/mapping.zod.ts b/packages/spec/src/data/mapping.zod.ts index 38f8c15902d..ed765ec46a3 100644 --- a/packages/spec/src/data/mapping.zod.ts +++ b/packages/spec/src/data/mapping.zod.ts @@ -77,7 +77,7 @@ const MAPPING_RETIRED_KEY_GUIDANCE: Readonly> = { }; /** - * `params`' lookup-steering keys, retired in the 17.x line (#10329, ADR-0049). + * `params`' lookup-steering keys, retired in the 17.x line (commit 15d58dbf1, ADR-0049). * * `object` / `fromField` / `toField` / `autoCreate` declared a per-entry * reference-resolution dialect that the import path never implemented: @@ -86,8 +86,8 @@ const MAPPING_RETIRED_KEY_GUIDANCE: Readonly> = { * `import-coerce.ts`, driven by the TARGET FIELD's own metadata — never by * these keys. Implementing them was considered and declined (a second * reference-resolution dialect on the import path; the code comment in - * `packages/rest/src/import-mapping.ts` declines it and the #10329 triage - * ruling confirms), so under ADR-0049 they go. + * `packages/rest/src/import-mapping.ts` declines it and the triage ruling + * commit 15d58dbf1 landed confirms), so under ADR-0049 they go. * * `autoCreate` is the one with teeth: it reads as "create the referenced * record when nothing matches", and what actually happens — with or without @@ -236,7 +236,7 @@ export const ImportFieldMappingSchema = lazySchema(() => strictObject({ // NOTE: `lookupObject` / `targetObject` / `match` / `matchOn` / // `matchField` / `keyField` / `returnField` / `valueField` / `create` / // `createIfMissing` / `upsert` were aliases onto the four lookup keys - // removed in the 17.x line (#10329). An alias pointing at a key that no + // removed in the 17.x line (commit 15d58dbf1). An alias pointing at a key that no // longer exists routes the author into a second rejection, so their // spellings fall through to the `guidance` prescriptions instead — // the 17.0.0 (#4509) treatment, one level down. @@ -249,7 +249,7 @@ export const ImportFieldMappingSchema = lazySchema(() => strictObject({ value: z.unknown().optional(), // `object` / `fromField` / `toField` / `autoCreate` — the `lookup` - // transform's steering keys — were removed in the 17.x line (#10329, + // transform's steering keys — were removed in the 17.x line (commit 15d58dbf1, // ADR-0049); see PARAMS_RETIRED_KEY_GUIDANCE above. The live mechanism: // `lookup` copies the cell through and the import pipeline resolves the // reference from the target field's own metadata (`import-coerce.ts`), diff --git a/packages/spec/src/data/object-strictness-batch20.test.ts b/packages/spec/src/data/object-strictness-batch20.test.ts index 822e4be2467..ef4454c63ac 100644 --- a/packages/spec/src/data/object-strictness-batch20.test.ts +++ b/packages/spec/src/data/object-strictness-batch20.test.ts @@ -411,7 +411,7 @@ describe('#4001 批 20 — curation is anchored to the sibling contract that mak it('`userActions.sort` names the VIEW block it belongs to — same key NAME, disjoint vocabulary', () => { // `ui/view.zod.ts`'s UserActionsConfigSchema declares sort/search/filter/ // refresh/rowHeight/group/addRecordForm/editInline/hideFields/rowColor/ - // buttons (group/hideFields/rowColor adopted at #11195, ruled A on + // buttons (group/hideFields/rowColor adopted by commit b37231883, ruled A on // objectui#5435); the object block declares create/import/edit/delete/ // exportCsv. Nothing overlaps, which is exactly why an author who // learned one writes it on the other. @@ -437,7 +437,7 @@ describe('#4001 批 20 — curation is anchored to the sibling contract that mak }); it('`userActions.group` / `.hideFields` / `.rowColor` name the VIEW block, same as `sort` (#11459)', () => { - // The three keys adopted onto the view's vocabulary at #11195 got only + // The three keys adopted onto the view's vocabulary by commit b37231883 got only // the generic unknown-key rejection on the object block — no curated // pointer — until this card added one, mirroring `sort`/`search`/ // `filter`/`editInline` above. diff --git a/packages/spec/src/data/object.test.ts b/packages/spec/src/data/object.test.ts index ece2371d359..c5489a20197 100644 --- a/packages/spec/src/data/object.test.ts +++ b/packages/spec/src/data/object.test.ts @@ -180,11 +180,11 @@ describe('LifecycleSchema (ADR-0057)', () => { expect(result.success).toBe(false); }); - // [#10527] The retention + ttl + archive triple. Since #10347 the Archiver - // selects rows by the ttl cutoff whenever `ttl` is declared, so a triple - // whose ttl diverges from the age bound leaves `retention.maxAge` declared - // but enforced by nothing — refused at parse time unless the ttl restates - // the age bound (same clock, same window). + // [commit 5649efbf9] The retention + ttl + archive triple. Since commit + // 530c1df65 the Archiver selects rows by the ttl cutoff whenever `ttl` is + // declared, so a triple whose ttl diverges from the age bound leaves + // `retention.maxAge` declared but enforced by nothing — refused at parse + // time unless the ttl restates the age bound (same clock, same window). describe('retention + ttl + archive triple (#10527)', () => { const messagesOf = (result: ReturnType) => result.success ? '' : result.error.issues.map((i) => i.message).join('\n'); @@ -220,7 +220,7 @@ describe('LifecycleSchema (ADR-0057)', () => { // Named values on both sides of the divergence … expect(msg).toContain("lifecycle.ttl ('30d' after 'expires_at')"); expect(msg).toContain("retention.maxAge ('90d' after created_at)"); - // … and the POST-#10347 runtime truth: the ttl cutoff selects, so the + // … and the runtime truth since commit 530c1df65: the ttl cutoff selects, so the // age bound is the one left inert (not "moves rows by age alone"). expect(msg).toContain('the Archiver moves rows by the ttl cutoff when ttl is declared'); expect(msg).toContain('no longer bounds the hot store'); @@ -310,7 +310,7 @@ describe('LifecycleSchema (ADR-0057)', () => { { revoked_at: { $null: 'yes' } }, // $null is z.boolean(), matching FieldOperatorsSchema { revoked_at: { $null: true, extra: 1 } }, // strict object: no extra keys { revoked_at: null }, // raw null is NOT the predicate — write {$null: true} - { revoked_at: { $nin: [null] } }, // unsupported operator, unchanged by #10165 + { revoked_at: { $nin: [null] } }, // unsupported operator, unchanged by commit 801296050 ]) { const result = LifecycleSchema.safeParse({ class: 'transient', @@ -1432,7 +1432,7 @@ describe('ObjectSchema.create()', () => { // ============================================================================ // controlled_by_parent × master_detail — the builder forces `required: true` -// (#9138 — #8772 maintainer ruling, Direction 2 / ADR-0055) +// (#9138 — commit 75b7c240a, maintainer ruling Direction 2 / ADR-0055) // ============================================================================ describe('ObjectSchema.create() forces a required master_detail under controlled_by_parent (#9138)', () => { @@ -1537,7 +1537,7 @@ describe('ObjectSchema.create() forces a required master_detail under controlled }); it('raw .parse()/.safeParse() stay TOLERANT of the old shape — metadata at rest keeps loading', () => { - // The other half of the #8772 ruling: the narrowing is authoring-time + // The other half of commit 75b7c240a's ruling: the narrowing is authoring-time // only. Stored metadata rehydrated through the schema (never through the // builder) must keep loading, UNREWRITTEN — runtime tolerance for existing // installs stays with the security gate, and the lint rule stays `warning` @@ -1726,7 +1726,7 @@ describe('ObjectSchema editMode (#11408 — declared by maintainer ruling, #1014 }); it('rejects a value outside the enum, as a VALUE error located at editMode — not unrecognized_keys', () => { - // Before #11408 the failure mode was `unrecognized_keys` at the top level + // Before commit f11fc61c5 the failure mode was `unrecognized_keys` at the top level // (the key itself was unknown). Declaring the key moves the judgment to // the VALUE: a bad spelling must now fail as an enum error at the // `editMode` path, proving the key is recognised and its value contract @@ -1934,7 +1934,7 @@ describe('TenancyConfigSchema — #2763 strategy/crossTenantAccess removal', () it('rejects the retired stamp-only `organizationField` with its prescription (#19054)', () => { // The shape this used to accept, verbatim — the one declaration the whole - // protocol ever carried (`sys_api_key`, #8778). The block is `.strict()`, + // protocol ever carried (`sys_api_key`, commit 7901b2dd2). The block is `.strict()`, // so the key is REFUSED with the guidance row rather than stripped: a // silent strip would swap one no-op for another, which is the class // ADR-0049 exists to end. diff --git a/packages/spec/src/data/value-roundtrip-conformance.ts b/packages/spec/src/data/value-roundtrip-conformance.ts index 36f4649a061..cbae80177b7 100644 --- a/packages/spec/src/data/value-roundtrip-conformance.ts +++ b/packages/spec/src/data/value-roundtrip-conformance.ts @@ -17,9 +17,9 @@ * * That is why this family kept arriving one card at a time: * - * | card | the same question, one instance at a time | + * | card or commit | the same question, one instance at a time | * |---|---| - * | #12380 | SQLite's `Field.json` codec was not injective — `'123'` read back as the number `123`; PG/MySQL disagreed | + * | commit 4045b954d | SQLite's `Field.json` codec was not injective — `'123'` read back as the number `123`; PG/MySQL disagreed | * | #11535 | a multi-value field read back as the string `'["x","y"]'` | * | #11782 | MySQL answered `1`/`0` for a declared boolean | * | #10995 | PG json values bound without `JSON.stringify` — the write half | @@ -82,7 +82,7 @@ * question from "the driver did not change it", and it already has two * tables. * - **Aggregated values** — `AGGREGATION_CASES` (#6409). `avg`/`sum` over a - * boolean (#11065 / #11151) is a value a driver *computes*, not one it + * boolean (commit 20950404c / #11151) is a value a driver *computes*, not one it * stored. * - **Which rows come back** — the filter, pagination and comparand tables. * @@ -154,7 +154,7 @@ export interface ValueRoundTripCase { /** * The cases. * - * The `v_json` block is #12380's measured boundary set: every string in it has + * The `v_json` block is commit 4045b954d's measured boundary set: every string in it has * content that is valid JSON or is number-like (or both) — the two classes the * pre-fix SQLite encoding destroyed — plus the ordinary strings that always * worked, kept as controls so a suite that goes red says *which* class broke. @@ -261,7 +261,7 @@ export const VALUE_ROUNDTRIP_ROWS: readonly Record[] = VALUE_RO * Pairs that must remain **distinguishable on read** — the injectivity half. * * Each pair is a string and the native value whose JSON encoding it looks like. - * Three of these collided on SQLite before #12380 (`'123'`/`123`, `'[]'`/`[]`, + * Three of these collided on SQLite before commit 4045b954d (`'123'`/`123`, `'[]'`/`[]`, * `'{"a":1}'`/`{a:1}`) and a fourth collided on read (`'null'`/`null`); all * were distinct on Postgres and MySQL, which is what made it a driver defect * rather than a platform decision.