From 50278e31c039e6b73d6d71166f717f1f3ee17efe Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 28 Sep 2026 19:53:10 +0000 Subject: [PATCH 1/6] docs(spec): re-anchor the dead tracker citations in data/ to the commits that decided them (stage 3) Every comment and docblock site under packages/spec/src/data that cited a tracker number answering 404 now cites the commit in this repository's history that decided what the line describes, and says in words what that commit decided. The files an open PR or an in-flight claim holds are left out. Comment-only: every file keeps its line count and no code token or test string moves. Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx Co-authored-by: Claude --- ...aggregate-field-type-compatibility.test.ts | 4 +- .../aggregate-field-type-compatibility.ts | 6 +-- .../spec/src/data/aggregation-conformance.ts | 6 +-- packages/spec/src/data/api-derivation.test.ts | 2 +- packages/spec/src/data/api-derivation.ts | 2 +- .../api-methods-batch-conformance.test.ts | 2 +- .../datasource-credential-redaction.test.ts | 4 +- .../data/datasource-credential-redaction.ts | 16 +++---- packages/spec/src/data/datasource.zod.ts | 32 +++++++------- .../spec/src/data/date-range-presets.test.ts | 4 +- packages/spec/src/data/date-range-presets.ts | 2 +- packages/spec/src/data/driver-nosql.zod.ts | 2 +- packages/spec/src/data/driver/common.zod.ts | 20 ++++----- .../src/data/driver/config-registry.test.ts | 2 +- .../src/data/driver/config-registry.zod.ts | 28 ++++++------- .../driver/driver-credential-refusal.test.ts | 42 +++++++++---------- .../driver/driver-placeholder-refusal.test.ts | 4 +- packages/spec/src/data/driver/memory.zod.ts | 8 ++-- packages/spec/src/data/driver/mongo.test.ts | 2 +- packages/spec/src/data/driver/mongo.zod.ts | 6 +-- packages/spec/src/data/driver/mysql.zod.ts | 2 +- .../src/data/driver/pg-url-grammar.server.ts | 4 +- .../spec/src/data/driver/postgres.test.ts | 2 +- packages/spec/src/data/driver/postgres.zod.ts | 2 +- packages/spec/src/data/driver/turso.test.ts | 4 +- ...signature-deadline-keys-retirement.test.ts | 2 +- packages/spec/src/data/feed.test.ts | 2 +- packages/spec/src/data/feed.zod.ts | 10 ++--- packages/spec/src/data/field-value.zod.ts | 4 +- packages/spec/src/data/field.test.ts | 4 +- packages/spec/src/data/field.zod.ts | 6 +-- .../src/data/filter-comparand-shape.test.ts | 6 +-- .../spec/src/data/filter-comparand-shape.ts | 8 ++-- .../spec/src/data/filter-comparand-type.ts | 2 +- .../spec/src/data/filter-logic-conformance.ts | 8 ++-- .../data/filter-subtree-provenance.test.ts | 6 +-- packages/spec/src/data/filter.test.ts | 8 ++-- packages/spec/src/data/filter.zod.ts | 28 ++++++------- packages/spec/src/data/hook-api.ts | 2 +- packages/spec/src/data/hook.test.ts | 2 +- packages/spec/src/data/hook.zod.ts | 2 +- packages/spec/src/data/index.ts | 2 +- packages/spec/src/data/mapping.test.ts | 4 +- packages/spec/src/data/mapping.zod.ts | 10 ++--- .../data/object-strictness-batch20.test.ts | 4 +- packages/spec/src/data/object.form.ts | 2 +- packages/spec/src/data/object.test.ts | 22 +++++----- packages/spec/src/data/object.zod.ts | 34 +++++++-------- .../src/data/value-roundtrip-conformance.ts | 10 ++--- 49 files changed, 198 insertions(+), 198 deletions(-) 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..26859833709 100644 --- a/packages/spec/src/data/api-derivation.ts +++ b/packages/spec/src/data/api-derivation.ts @@ -160,7 +160,7 @@ export const API_METHOD_DERIVATION: Record = { * the resolver, treated as ungated (custom actions were never gated by * `apiMethods`). * - * [#6259] The `batch: 'bulk'` row was removed, and the line above no longer + * [#6259] The `batch: 'bulk'` row was removed by commit 6968885ef, and the line above no longer * calls `batch` a runtime `callData` action. 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 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/driver/turso.test.ts b/packages/spec/src/data/driver/turso.test.ts index 7fe99ebf9ef..cff66816223 100644 --- a/packages/spec/src/data/driver/turso.test.ts +++ b/packages/spec/src/data/driver/turso.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * The turso/libSQL config contract (#6345). + * The turso/libSQL config contract (commit e2798fab7). * * These assertions are what "`validateDriverConfig('turso')` flipped from * `{ known: false }` to `{ known: true }`" MEANS in practice: before this file @@ -55,7 +55,7 @@ describe('TursoConfigSchema', () => { }); // The exact failure this contract was written for: `token` is the plausible - // spelling, `authToken` is the real one, and before #6345 the misspelling was + // spelling, `authToken` is the real one, and before commit e2798fab7 the misspelling was // accepted in silence and the connection attempted unauthenticated. Until // #7990 the fix was a rename hint onto `authToken`; now that `authToken` is // itself unwritable the same spelling gets the credential refusal directly — 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-logic-conformance.ts b/packages/spec/src/data/filter-logic-conformance.ts index c9b32acbaa5..8df3399a18f 100644 --- a/packages/spec/src/data/filter-logic-conformance.ts +++ b/packages/spec/src/data/filter-logic-conformance.ts @@ -164,7 +164,7 @@ * - **`$exists` means "has a value"** (`!= null`), never key-presence — cell 2, * the leg of the 07:33Z ruling that was never in conflict with #5298 and had * already shipped in PR #5962 on the surfaces the ruling named. It stands — - * and since PR #13529 (#13195) moved the last three key-presence exits, it + * and since commit 9dac1ae01 (PR #13529) moved the last three key-presence exits, it * is enforced here too: enrolled in {@link FILTER_LOGIC_CASES} in BOTH * directions (#13531). * @@ -223,8 +223,8 @@ * path, its analytics face (a third divergent exit the earlier prose never * named; measured in PR #13420), and `driver-mongodb`'s `translateFilter`. * The #5499 investment freeze that once excused the lag dissolved on - * 2026-08-11 (recorded in `./aggregation-conformance.ts`), and PR #13529 - * (#13195) moved all three to has-value — the gap is closed, the stated + * 2026-08-11 (recorded in `./aggregation-conformance.ts`), and commit + * 9dac1ae01 (PR #13529) moved all three to has-value — the gap is closed, the stated * blocker on enrolment is gone with it, and the two `$exists` rows below are * enrolled in BOTH directions (#13531). * @@ -499,7 +499,7 @@ export const FILTER_LOGIC_CASES: readonly FilterLogicCase[] = [ }, // [#13531] The value-presence predicate, enrolled in BOTH directions once - // PR #13529 (#13195) moved the last three key-presence exits to has-value. + // commit 9dac1ae01 (PR #13529) moved the last three key-presence exits to has-value. // The stored-null seeding is what makes these rows discriminating: a // key-presence reading answers MATCH on rows 3-4 for `$exists: true` // precisely because every harness stores `d: null` with the key present — 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.form.ts b/packages/spec/src/data/object.form.ts index 60713e06f3a..d57ff6c0951 100644 --- a/packages/spec/src/data/object.form.ts +++ b/packages/spec/src/data/object.form.ts @@ -253,7 +253,7 @@ export const objectForm = defineForm({ // the option shape is strict and has never declared `icon`, so a // Lucide name typed there was an `unrecognized_keys` refusal at // publish — the author found out at the 422, the same - // offer-vs-door class #11410 and #12868 retired elsewhere. + // offer-vs-door class #11410 and commit c459da6bc retired elsewhere. // // Remove rather than declare, on a premise measured for THIS // surface rather than inherited from #5016's action-param reading: 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/object.zod.ts b/packages/spec/src/data/object.zod.ts index befde04ca8e..d11297eb353 100644 --- a/packages/spec/src/data/object.zod.ts +++ b/packages/spec/src/data/object.zod.ts @@ -815,7 +815,7 @@ const lifecycleDuration = (what: string) => z.string().regex(LIFECYCLE_DURATION_REGEX, `${what} must be a duration literal like '6h', '14d', '12w' or '7y'`); /** - * [#10165] The `onlyWhen` row-filter value union, shared by + * [commit 801296050] The `onlyWhen` row-filter value union, shared by * `retention.onlyWhen` and `ttl.onlyWhen` — ONE shape on purpose: the two * blocks are mirrors (maintainer ruling 2026-08-20, option A: give `ttl` an * `onlyWhen` mirroring `retention`'s), and the runtime enforces them through @@ -1002,8 +1002,8 @@ export const LifecycleSchema = lazySchema(() => strictObject({ message: `lifecycle.archive.after ('${lc.archive.after}') must equal retention.maxAge ('${lc.retention.maxAge}') — the hot window ends where the archive begins`, }); } - // [#10527] The retention + ttl + archive triple — the alignment above, one - // policy wider. Since [#10347] the Archiver selects the rows it moves by the + // [commit 5649efbf9] The retention + ttl + archive triple — the alignment above, one + // policy wider. Since [commit 530c1df65] the Archiver selects the rows it moves by the // ttl cutoff (`ttl.field` older than `ttl.expireAfter`) whenever `ttl` is // declared, and by `created_at`/`archive.after` only when it is not — so on // this triple the age bound (`retention.maxAge`, pinned equal to @@ -1033,20 +1033,20 @@ export const LifecycleSchema = lazySchema(() => strictObject({ message: 'lifecycle.retention.onlyWhen cannot be combined with archive — the Archiver moves rows by age alone and would archive rows the filter protects', }); } - // [#10165] ttl.onlyWhen mirrors both of retention.onlyWhen's conflicts, from + // [commit 801296050] ttl.onlyWhen mirrors both of retention.onlyWhen's conflicts, from // the Reaper's actual semantics rather than by symmetry alone: // - rotation: the Rotator DROPs whole physical shards; a shard is dropped by // age with no row read, so rows the filter protects go down with it. // - archive: `reapObject` returns into `archiveObject` before the ttl reap // ever runs, so with `archive` declared the filter guards a code path that - // is never executed (declared ≠ enforced). Since [#10347] the Archiver does + // is never executed (declared ≠ enforced). Since [commit 530c1df65] the Archiver does // apply the declared ttl window itself — it selects candidates by // `ttl.field` past `ttl.expireAfter` instead of `created_at` past // `archive.after` — but its candidate read is that cutoff and nothing else // (`where: { [ttl.field]: { $lt: cutoff } }`, no `onlyWhen` spread the way // `reap()` spreads it into its scope), so every due row is copied and // hot-deleted whether or not the filter names it. That is the whole of what - // [#10347] changed here: the WINDOW an author declares now carries over to + // [commit 530c1df65] changed here: the WINDOW an author declares now carries over to // the Archiver, the FILTER still does not — so the refusal stands, on a // narrower reason than the "moves rows by age alone" this bullet used to // give. Whether `onlyWhen` should become meaningful under `archive` (the @@ -1788,7 +1788,7 @@ const ObjectSchemaBase = strictObject( // `ui/view.zod.ts` declares its own `userActions` with a completely // disjoint vocabulary (sort/search/filter/refresh/rowHeight/group/ // addRecordForm/editInline/hideFields/rowColor/buttons — the last three - // adopted at #11195), so an author who learned that block writes these + // adopted by commit b37231883), so an author who learned that block writes these // here. `group`/`hideFields`/`rowColor` were refused by name but without // a curated pointer until #11459 gave them one too, mirroring the other // four. @@ -2186,7 +2186,7 @@ const ObjectSchemaBase = strictObject( * `recordFormNavigation.ts` branches on `editMode !== 'page'`, and * `AppContent`'s `handleEdit` dispatcher routes on it). * - * Declared here by the #11408 maintainer ruling (the measured residue of the + * Declared here by the maintainer ruling commit f11fc61c5 landed (the measured residue of the * #10144 declare-or-rule-out census): objectui had published the key to * authors (CHANGELOG + live runtime read) while this strict parse rejected * it. objectui's `ObjectSchemaClientExtensions.editMode` mirror retires in a @@ -2314,7 +2314,7 @@ const ObjectSchemaBase = strictObject( /** * Master switch — a STANDING policy held at every redemption, not a * mint-time check (#14033; the same shape as the `eligibility` predicate - * below, #13608). + * below, commit fc9ba76a5). * * When false (default), no share links can be issued for this object AND * no share link on it resolves: `resolveToken` re-reads this switch on @@ -2351,7 +2351,7 @@ const ObjectSchemaBase = strictObject( /** * Optional CEL predicate over the candidate record. It is a STANDING * policy about which records may be reached anonymously, and the platform - * holds it at BOTH points in a link's life (#13608): + * holds it at BOTH points in a link's life (commit fc9ba76a5): * * - **at mint** — `createLink` refuses with 422 when the predicate is * false (e.g. "draft records cannot be shared") and writes no link row; @@ -2363,7 +2363,7 @@ const ObjectSchemaBase = strictObject( * ⚠️ Tightening this policy therefore cuts off already-minted links, on * purpose — no revocation step, no grace period. That is the point of a * standing policy, and it is a behaviour change for deployments that - * shipped before #13608. + * shipped before commit fc9ba76a5. * * Fail-CLOSED at both points: a predicate that does not compile, that * faults on the record, or that answers anything other than `true` refuses @@ -2715,7 +2715,7 @@ function assertReferenceViaSiblingDeclared(objectName: unknown, fields: unknown) } /** - * [#9138 — #8772 maintainer ruling, Direction 2 / ADR-0055] Under + * [#9138 — commit 75b7c240a, maintainer ruling Direction 2 / ADR-0055] Under * `sharingModel: 'controlled_by_parent'` the builder FORCES `required: true` * on every `master_detail` reference, and REFUSES an explicit * `required: false` there, loudly. @@ -2728,9 +2728,9 @@ function assertReferenceViaSiblingDeclared(objectName: unknown, fields: unknown) * `masterFK IN (accessible master ids)` can never match null — the row is * invisible to everyone — and every later by-id write answers * `422 MISSING_REQUIRED_FIELD`. Today only the security gate - * (`assertControlledByParentWrite`) closes that shape, and #8772 measured that - * the declaration and the enforcement disagree. This makes the unsafe shape - * impossible to NEWLY declare: + * (`assertControlledByParentWrite`) closes that shape, and the ruling commit + * 75b7c240a landed measured that the declaration and the enforcement + * disagree. This makes the unsafe shape impossible to NEWLY declare: * * - omitted `required` → forced to `true` (the lint rule * `relationship/master-detail-required` already computes exactly this fix); @@ -2741,7 +2741,7 @@ function assertReferenceViaSiblingDeclared(objectName: unknown, fields: unknown) * Lives at `create()` — the authoring surface (ADR-0077) — beside * {@link assertSystemDataIsWritable}, and deliberately NOT in raw * `.parse()`/`.safeParse()`: metadata already at rest must keep loading. - * Runtime tolerance is the other half of the #8772 ruling — the security + * Runtime tolerance is the other half of commit 75b7c240a's ruling — the security * gate's fallbacks stay, and the lint rule stays `warning` until v18 — so * publish-time refuses new declarations while runtime tolerates old ones. * @@ -2907,7 +2907,7 @@ export const ObjectSchema = lazySchema(() => { // declared can never resolve — refuse at the authoring seam, beside its // sibling assertions, rather than one error per seeded row at load time. assertReferenceViaSiblingDeclared(cfg.name, cfg.fields); - // [#9138 — #8772 ruling, Direction 2] A `controlled_by_parent` object's + // [#9138 — commit 75b7c240a, ruling Direction 2] A `controlled_by_parent` object's // `master_detail` reference is forced `required: true` (an explicit // `required: false` throws, loudly) so the unsafe shape cannot be newly // declared. Raw `.parse()`/`.safeParse()` stay tolerant for metadata at 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. From 49ce4b82e2b9e1a00c1c40a2e4e1755db99a2d74 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 28 Sep 2026 20:08:13 +0000 Subject: [PATCH 2/6] docs(spec): regenerate the feed reference page and add the patch changeset (stage 3) check:docs proved content/docs/references/data/feed.mdx stale: two lines project from the rewritten feed.zod.ts docblock. check:generated --fix regenerated only that artifact. The rewritten docblocks ship in src/**/*.zod.ts and dist, so @objectstack/spec takes a patch changeset. Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx Co-authored-by: Claude --- .changeset/spec-data-provenance-anchors.md | 11 +++++++++++ content/docs/references/data/feed.mdx | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 .changeset/spec-data-provenance-anchors.md 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. From b93f08f8d0db6babf387249215838b8424f57ad7 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 28 Sep 2026 20:18:10 +0000 Subject: [PATCH 3/6] docs(spec): keep the test-read [#6259] marker line byte-identical and anchor the sentence one line down api-derivation.test.ts splits DATA_ACTION_TO_API_OPERATION's TSDoc on the literal `[#6259]`, so that marker stays. Writing the deciding commit onto the marker's own line made the diff-scoped check-issue-citations verdict read the kept number as an added citation; the commit now sits on the next line of the same sentence, which carries no number. Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx Co-authored-by: Claude --- packages/spec/src/data/api-derivation.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/spec/src/data/api-derivation.ts b/packages/spec/src/data/api-derivation.ts index 26859833709..e363ba62b6b 100644 --- a/packages/spec/src/data/api-derivation.ts +++ b/packages/spec/src/data/api-derivation.ts @@ -160,8 +160,8 @@ export const API_METHOD_DERIVATION: Record = { * the resolver, treated as ungated (custom actions were never gated by * `apiMethods`). * - * [#6259] The `batch: 'bulk'` row was removed by commit 6968885ef, and the line above no longer - * calls `batch` a runtime `callData` action. It was the one entry with no + * [#6259] The `batch: 'bulk'` row was removed, and the line above no longer + * 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 From 86463ed0a139ebfcc7f693e683a91806f15da8fd Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 28 Sep 2026 20:54:07 +0000 Subject: [PATCH 4/6] docs(spec): leave driver/turso.test.ts to the open PR that now holds it (stage 3) The #20437 PR opened after this stage's claim and edits packages/spec/src/data/driver/turso.test.ts, so that file joins the stage's exclusions beside driver/turso.zod.ts. Its two comment sites return to the base bytes (blob 7fe99ebf9) and wait for a later stage. Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx Co-authored-by: Claude --- packages/spec/src/data/driver/turso.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/spec/src/data/driver/turso.test.ts b/packages/spec/src/data/driver/turso.test.ts index cff66816223..7fe99ebf9ef 100644 --- a/packages/spec/src/data/driver/turso.test.ts +++ b/packages/spec/src/data/driver/turso.test.ts @@ -1,7 +1,7 @@ // Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. /** - * The turso/libSQL config contract (commit e2798fab7). + * The turso/libSQL config contract (#6345). * * These assertions are what "`validateDriverConfig('turso')` flipped from * `{ known: false }` to `{ known: true }`" MEANS in practice: before this file @@ -55,7 +55,7 @@ describe('TursoConfigSchema', () => { }); // The exact failure this contract was written for: `token` is the plausible - // spelling, `authToken` is the real one, and before commit e2798fab7 the misspelling was + // spelling, `authToken` is the real one, and before #6345 the misspelling was // accepted in silence and the connection attempted unauthenticated. Until // #7990 the fix was a rename hint onto `authToken`; now that `authToken` is // itself unwritable the same spelling gets the credential refusal directly — From 3479600dda6859497f932af7306975b09eb530c1 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 28 Sep 2026 22:03:59 +0000 Subject: [PATCH 5/6] docs(spec): leave data/object.form.ts to the open PR that now holds it (stage 3) The #20432 PR opened after this stage's claim and edits packages/spec/src/data/object.form.ts, so that file joins the stage's exclusions. Its one comment site returns to the base bytes (blob 60713e06f) and waits for a later stage. Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx Co-authored-by: Claude --- packages/spec/src/data/object.form.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/spec/src/data/object.form.ts b/packages/spec/src/data/object.form.ts index d57ff6c0951..60713e06f3a 100644 --- a/packages/spec/src/data/object.form.ts +++ b/packages/spec/src/data/object.form.ts @@ -253,7 +253,7 @@ export const objectForm = defineForm({ // the option shape is strict and has never declared `icon`, so a // Lucide name typed there was an `unrecognized_keys` refusal at // publish — the author found out at the 422, the same - // offer-vs-door class #11410 and commit c459da6bc retired elsewhere. + // offer-vs-door class #11410 and #12868 retired elsewhere. // // Remove rather than declare, on a premise measured for THIS // surface rather than inherited from #5016's action-param reading: From 96fd49caa2e88a14b1c4a598323b5cf245c79e20 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 28 Sep 2026 22:55:07 +0000 Subject: [PATCH 6/6] docs(spec): leave object.zod.ts and filter-logic-conformance.ts to the open PRs that now hold them (stage 3) The #20494 PR (object.zod.ts) and the #20444 PR (filter-logic-conformance.ts) opened after this stage's claim, so both files join the stage's exclusions. Their comment sites return to the base bytes (blobs befde04ca and c9b32acba) and wait for a later stage. Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx Co-authored-by: Claude --- .../spec/src/data/filter-logic-conformance.ts | 8 ++--- packages/spec/src/data/object.zod.ts | 34 +++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/spec/src/data/filter-logic-conformance.ts b/packages/spec/src/data/filter-logic-conformance.ts index 8df3399a18f..c9b32acbaa5 100644 --- a/packages/spec/src/data/filter-logic-conformance.ts +++ b/packages/spec/src/data/filter-logic-conformance.ts @@ -164,7 +164,7 @@ * - **`$exists` means "has a value"** (`!= null`), never key-presence — cell 2, * the leg of the 07:33Z ruling that was never in conflict with #5298 and had * already shipped in PR #5962 on the surfaces the ruling named. It stands — - * and since commit 9dac1ae01 (PR #13529) moved the last three key-presence exits, it + * and since PR #13529 (#13195) moved the last three key-presence exits, it * is enforced here too: enrolled in {@link FILTER_LOGIC_CASES} in BOTH * directions (#13531). * @@ -223,8 +223,8 @@ * path, its analytics face (a third divergent exit the earlier prose never * named; measured in PR #13420), and `driver-mongodb`'s `translateFilter`. * The #5499 investment freeze that once excused the lag dissolved on - * 2026-08-11 (recorded in `./aggregation-conformance.ts`), and commit - * 9dac1ae01 (PR #13529) moved all three to has-value — the gap is closed, the stated + * 2026-08-11 (recorded in `./aggregation-conformance.ts`), and PR #13529 + * (#13195) moved all three to has-value — the gap is closed, the stated * blocker on enrolment is gone with it, and the two `$exists` rows below are * enrolled in BOTH directions (#13531). * @@ -499,7 +499,7 @@ export const FILTER_LOGIC_CASES: readonly FilterLogicCase[] = [ }, // [#13531] The value-presence predicate, enrolled in BOTH directions once - // commit 9dac1ae01 (PR #13529) moved the last three key-presence exits to has-value. + // PR #13529 (#13195) moved the last three key-presence exits to has-value. // The stored-null seeding is what makes these rows discriminating: a // key-presence reading answers MATCH on rows 3-4 for `$exists: true` // precisely because every harness stores `d: null` with the key present — diff --git a/packages/spec/src/data/object.zod.ts b/packages/spec/src/data/object.zod.ts index d11297eb353..befde04ca8e 100644 --- a/packages/spec/src/data/object.zod.ts +++ b/packages/spec/src/data/object.zod.ts @@ -815,7 +815,7 @@ const lifecycleDuration = (what: string) => z.string().regex(LIFECYCLE_DURATION_REGEX, `${what} must be a duration literal like '6h', '14d', '12w' or '7y'`); /** - * [commit 801296050] The `onlyWhen` row-filter value union, shared by + * [#10165] The `onlyWhen` row-filter value union, shared by * `retention.onlyWhen` and `ttl.onlyWhen` — ONE shape on purpose: the two * blocks are mirrors (maintainer ruling 2026-08-20, option A: give `ttl` an * `onlyWhen` mirroring `retention`'s), and the runtime enforces them through @@ -1002,8 +1002,8 @@ export const LifecycleSchema = lazySchema(() => strictObject({ message: `lifecycle.archive.after ('${lc.archive.after}') must equal retention.maxAge ('${lc.retention.maxAge}') — the hot window ends where the archive begins`, }); } - // [commit 5649efbf9] The retention + ttl + archive triple — the alignment above, one - // policy wider. Since [commit 530c1df65] the Archiver selects the rows it moves by the + // [#10527] The retention + ttl + archive triple — the alignment above, one + // policy wider. Since [#10347] the Archiver selects the rows it moves by the // ttl cutoff (`ttl.field` older than `ttl.expireAfter`) whenever `ttl` is // declared, and by `created_at`/`archive.after` only when it is not — so on // this triple the age bound (`retention.maxAge`, pinned equal to @@ -1033,20 +1033,20 @@ export const LifecycleSchema = lazySchema(() => strictObject({ message: 'lifecycle.retention.onlyWhen cannot be combined with archive — the Archiver moves rows by age alone and would archive rows the filter protects', }); } - // [commit 801296050] ttl.onlyWhen mirrors both of retention.onlyWhen's conflicts, from + // [#10165] ttl.onlyWhen mirrors both of retention.onlyWhen's conflicts, from // the Reaper's actual semantics rather than by symmetry alone: // - rotation: the Rotator DROPs whole physical shards; a shard is dropped by // age with no row read, so rows the filter protects go down with it. // - archive: `reapObject` returns into `archiveObject` before the ttl reap // ever runs, so with `archive` declared the filter guards a code path that - // is never executed (declared ≠ enforced). Since [commit 530c1df65] the Archiver does + // is never executed (declared ≠ enforced). Since [#10347] the Archiver does // apply the declared ttl window itself — it selects candidates by // `ttl.field` past `ttl.expireAfter` instead of `created_at` past // `archive.after` — but its candidate read is that cutoff and nothing else // (`where: { [ttl.field]: { $lt: cutoff } }`, no `onlyWhen` spread the way // `reap()` spreads it into its scope), so every due row is copied and // hot-deleted whether or not the filter names it. That is the whole of what - // [commit 530c1df65] changed here: the WINDOW an author declares now carries over to + // [#10347] changed here: the WINDOW an author declares now carries over to // the Archiver, the FILTER still does not — so the refusal stands, on a // narrower reason than the "moves rows by age alone" this bullet used to // give. Whether `onlyWhen` should become meaningful under `archive` (the @@ -1788,7 +1788,7 @@ const ObjectSchemaBase = strictObject( // `ui/view.zod.ts` declares its own `userActions` with a completely // disjoint vocabulary (sort/search/filter/refresh/rowHeight/group/ // addRecordForm/editInline/hideFields/rowColor/buttons — the last three - // adopted by commit b37231883), so an author who learned that block writes these + // adopted at #11195), so an author who learned that block writes these // here. `group`/`hideFields`/`rowColor` were refused by name but without // a curated pointer until #11459 gave them one too, mirroring the other // four. @@ -2186,7 +2186,7 @@ const ObjectSchemaBase = strictObject( * `recordFormNavigation.ts` branches on `editMode !== 'page'`, and * `AppContent`'s `handleEdit` dispatcher routes on it). * - * Declared here by the maintainer ruling commit f11fc61c5 landed (the measured residue of the + * Declared here by the #11408 maintainer ruling (the measured residue of the * #10144 declare-or-rule-out census): objectui had published the key to * authors (CHANGELOG + live runtime read) while this strict parse rejected * it. objectui's `ObjectSchemaClientExtensions.editMode` mirror retires in a @@ -2314,7 +2314,7 @@ const ObjectSchemaBase = strictObject( /** * Master switch — a STANDING policy held at every redemption, not a * mint-time check (#14033; the same shape as the `eligibility` predicate - * below, commit fc9ba76a5). + * below, #13608). * * When false (default), no share links can be issued for this object AND * no share link on it resolves: `resolveToken` re-reads this switch on @@ -2351,7 +2351,7 @@ const ObjectSchemaBase = strictObject( /** * Optional CEL predicate over the candidate record. It is a STANDING * policy about which records may be reached anonymously, and the platform - * holds it at BOTH points in a link's life (commit fc9ba76a5): + * holds it at BOTH points in a link's life (#13608): * * - **at mint** — `createLink` refuses with 422 when the predicate is * false (e.g. "draft records cannot be shared") and writes no link row; @@ -2363,7 +2363,7 @@ const ObjectSchemaBase = strictObject( * ⚠️ Tightening this policy therefore cuts off already-minted links, on * purpose — no revocation step, no grace period. That is the point of a * standing policy, and it is a behaviour change for deployments that - * shipped before commit fc9ba76a5. + * shipped before #13608. * * Fail-CLOSED at both points: a predicate that does not compile, that * faults on the record, or that answers anything other than `true` refuses @@ -2715,7 +2715,7 @@ function assertReferenceViaSiblingDeclared(objectName: unknown, fields: unknown) } /** - * [#9138 — commit 75b7c240a, maintainer ruling Direction 2 / ADR-0055] Under + * [#9138 — #8772 maintainer ruling, Direction 2 / ADR-0055] Under * `sharingModel: 'controlled_by_parent'` the builder FORCES `required: true` * on every `master_detail` reference, and REFUSES an explicit * `required: false` there, loudly. @@ -2728,9 +2728,9 @@ function assertReferenceViaSiblingDeclared(objectName: unknown, fields: unknown) * `masterFK IN (accessible master ids)` can never match null — the row is * invisible to everyone — and every later by-id write answers * `422 MISSING_REQUIRED_FIELD`. Today only the security gate - * (`assertControlledByParentWrite`) closes that shape, and the ruling commit - * 75b7c240a landed measured that the declaration and the enforcement - * disagree. This makes the unsafe shape impossible to NEWLY declare: + * (`assertControlledByParentWrite`) closes that shape, and #8772 measured that + * the declaration and the enforcement disagree. This makes the unsafe shape + * impossible to NEWLY declare: * * - omitted `required` → forced to `true` (the lint rule * `relationship/master-detail-required` already computes exactly this fix); @@ -2741,7 +2741,7 @@ function assertReferenceViaSiblingDeclared(objectName: unknown, fields: unknown) * Lives at `create()` — the authoring surface (ADR-0077) — beside * {@link assertSystemDataIsWritable}, and deliberately NOT in raw * `.parse()`/`.safeParse()`: metadata already at rest must keep loading. - * Runtime tolerance is the other half of commit 75b7c240a's ruling — the security + * Runtime tolerance is the other half of the #8772 ruling — the security * gate's fallbacks stay, and the lint rule stays `warning` until v18 — so * publish-time refuses new declarations while runtime tolerates old ones. * @@ -2907,7 +2907,7 @@ export const ObjectSchema = lazySchema(() => { // declared can never resolve — refuse at the authoring seam, beside its // sibling assertions, rather than one error per seeded row at load time. assertReferenceViaSiblingDeclared(cfg.name, cfg.fields); - // [#9138 — commit 75b7c240a, ruling Direction 2] A `controlled_by_parent` object's + // [#9138 — #8772 ruling, Direction 2] A `controlled_by_parent` object's // `master_detail` reference is forced `required: true` (an explicit // `required: false` throws, loudly) so the unsafe shape cannot be newly // declared. Raw `.parse()`/`.safeParse()` stay tolerant for metadata at