From f2884adc328fd9544127feb3f8403a527d0187b1 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 2 Sep 2026 09:02:46 +0000 Subject: [PATCH] docs(drivers): scope the `reference_to` refusal docblocks to the authoring face MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SQL DDL door (#11567) and the Mongo schema-sync door (#13222) each assert that `reference_to` "is a REJECTED ALIAS, not a normalised one". After the `field-reference-to-alias` conversion landed, that is true on the AUTHORING face only: stored `sys_metadata` rehydration and `os migrate meta` now NORMALISE the key. Left as written, the next reader infers "a stored `reference_to` stays verbatim forever" from a driver comment, which is false. Both docblocks now scope the original assertion to the authoring face and quote the split the conversion's own registry docblock already states, rather than inventing a parallel formulation. Two prose measurements in driver-mongodb that a later spec tightening left behind are refreshed in the same stroke: `{ type: 'lookup' }` with no `reference` and `{ type: 'lookup', reference: '' }` no longer "parse successfully" — the superRefine added on the relationship types refuses both with `custom` on the `reference` path. The shape still reaches the join-index arm, because `syncSchema(object, schema: unknown)` casts and forwards verbatim with no Zod, so no assertion changes. Comments only: no behaviour, no assertion, no public type. Card #13851. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68 --- ...mongodb-13222-reference-to-refusal.test.ts | 11 +++--- .../driver-mongodb/src/mongodb-schema.ts | 34 ++++++++++++++----- packages/drivers/driver-sql/src/sql-driver.ts | 20 +++++++++-- 3 files changed, 50 insertions(+), 15 deletions(-) diff --git a/packages/drivers/driver-mongodb/src/mongodb-13222-reference-to-refusal.test.ts b/packages/drivers/driver-mongodb/src/mongodb-13222-reference-to-refusal.test.ts index 353fe98236..53f2f68644 100644 --- a/packages/drivers/driver-mongodb/src/mongodb-13222-reference-to-refusal.test.ts +++ b/packages/drivers/driver-mongodb/src/mongodb-13222-reference-to-refusal.test.ts @@ -228,10 +228,13 @@ describe('#13222 part (1) — driver-mongodb refuses `reference_to` at the schem it('does not index a `lookup` that declares no target — `reference` is read for truth, not presence', async () => { // Measured on `FieldSchema` built from this tree: `{ type: 'lookup' }` with - // no `reference`, and `{ type: 'lookup', reference: '' }`, BOTH parse - // successfully — the spec's prose calls `reference` required for these - // types, but the schema does not enforce it. So this is a shape an author - // can really publish, not a hypothetical, and the arm has to answer for it. + // no `reference`, and `{ type: 'lookup', reference: '' }`, are BOTH REFUSED + // (`custom` on the `reference` path) — #13927 made the "required for these + // types" in the spec's own prose enforced; both parsed successfully until + // then. The shape is still not hypothetical, because it does not arrive + // through Zod: `syncSchema(object, schema: unknown)` casts and forwards + // verbatim, which is the seam this test calls directly, so the arm still + // has to answer for it. // // It answers by declining: `idx_FIELD_lookup` exists to serve a join, and a // lookup with no declared target has no join to serve — the index would diff --git a/packages/drivers/driver-mongodb/src/mongodb-schema.ts b/packages/drivers/driver-mongodb/src/mongodb-schema.ts index a03821f443..6130f3f349 100644 --- a/packages/drivers/driver-mongodb/src/mongodb-schema.ts +++ b/packages/drivers/driver-mongodb/src/mongodb-schema.ts @@ -66,11 +66,14 @@ interface FieldDef { * ⚠️ Truthiness rather than `!== undefined`, and that difference is load * bearing rather than inherited. Measured on `FieldSchema` built from this * tree: `{ type: 'lookup' }` with NO `reference`, and `{ type: 'lookup', - * reference: '' }`, both parse SUCCESSFULLY — the "required for these types" - * in the spec's own prose is not enforced by the schema. So a lookup that - * points nowhere is a shape an author can really publish, and it must not - * get `idx_FIELD_lookup`: an index for a join whose target is undeclared - * costs writes and buys no read. Truthiness declines exactly that shape. + * reference: '' }`, are BOTH REFUSED — `custom` on the `reference` path, + * since #13927 made the "required for these types" in the spec's own prose + * enforced (it parsed successfully until then). The shape still reaches this + * arm regardless, because metadata gets here without meeting Zod at all — + * `syncSchema(object, schema: unknown)` casts and forwards verbatim — and it + * must not get `idx_FIELD_lookup`: an index for a join whose target is + * undeclared costs writes and buys no read. Truthiness declines exactly that + * shape. */ reference?: unknown; multiple?: boolean; @@ -110,9 +113,9 @@ interface ObjectDef { * * ## Why the DDL seam needs a door the schema already has * - * `reference` is the only relationship spelling the spec declares; - * `reference_to` is a REJECTED ALIAS, not a normalised one. Measured against - * `@objectstack/spec` built from this tree: + * `reference` is the only relationship spelling the spec declares. ON THE + * AUTHORING FACE `reference_to` is a REJECTED ALIAS, not a normalised one. + * Measured against `@objectstack/spec` built from this tree: * * ``` * FieldSchema.safeParse({ name:'company_id', type:'lookup', reference_to:'company' }) @@ -122,6 +125,21 @@ interface ObjectDef { * these were dropped silently ..." * ``` * + * ⚠️ That verdict is the authoring face and only the authoring face — do NOT + * read this door as "a stored `reference_to` stays verbatim forever". #13700 + * landed the `field-reference-to-alias` conversion (`toMajor: 18`, + * `retiredFromLoadPath: true`, in `packages/spec/src/conversions/registry.ts`), + * and its docblock already states the split this comment has to be read + * against: the entry "covers the two paths that serve or rewrite EXISTING data + * — stored rehydration and `os migrate meta`", where the key is NORMALISED to + * `reference` rather than refused, while "the DDL doors above keep guarding + * the third path (metadata handed straight to a driver, around both the gate + * and the stored pass); they are downstream of this entry, not replaced by + * it." This door is one of those DDL doors — and the third path is exactly the + * one this driver sits on, since `syncSchema(object, schema: unknown)` casts + * and forwards verbatim with no Zod and no stored pass. Refused when authored, + * rewritten when already at rest, refused again here. + * * Until this door, the driver read `reference_to` and ONLY `reference_to`, as * the gate on the field-level join index below. So one key had TWO doors with * opposite answers: the authoring door refused it, while this one silently diff --git a/packages/drivers/driver-sql/src/sql-driver.ts b/packages/drivers/driver-sql/src/sql-driver.ts index 0bb4e79267..296279babc 100644 --- a/packages/drivers/driver-sql/src/sql-driver.ts +++ b/packages/drivers/driver-sql/src/sql-driver.ts @@ -1488,9 +1488,9 @@ function refuseDateBucketedGroupBy(granularity: string, bucketedHere: string[], * * ## Why the DDL seam needs a door the schema already has * - * `reference` is the only relationship spelling the spec declares; - * `reference_to` is a REJECTED ALIAS, not a normalised one. Measured on - * `origin/main`: + * `reference` is the only relationship spelling the spec declares. ON THE + * AUTHORING FACE `reference_to` is a REJECTED ALIAS, not a normalised one. + * Measured on `origin/main`: * * ``` * FieldSchema.safeParse({ name:'parent', type:'lookup', reference_to:'p' }) @@ -1499,6 +1499,20 @@ function refuseDateBucketedGroupBy(granularity: string, bucketedHere: string[], * Did you mean `reference_to` → `reference`?" * ``` * + * ⚠️ That verdict is the authoring face and only the authoring face — do NOT + * read this door as "a stored `reference_to` stays verbatim forever". #13700 + * landed the `field-reference-to-alias` conversion (`toMajor: 18`, + * `retiredFromLoadPath: true`, in `packages/spec/src/conversions/registry.ts`), + * and its docblock already states the split this comment has to be read + * against: the entry "covers the two paths that serve or rewrite EXISTING data + * — stored rehydration and `os migrate meta`", where the key is NORMALISED to + * `reference` rather than refused, while "the DDL doors above keep guarding + * the third path (metadata handed straight to a driver, around both the gate + * and the stored pass); they are downstream of this entry, not replaced by + * it." This door is one of those DDL doors. Refused when authored, rewritten + * when already at rest, refused again here — three paths, and this comment + * speaks for the third. + * * Until #11567 this driver read `reference_to` and ONLY `reference_to`, as the * gate on a `table.foreign(name).references('id')`. So one key had TWO doors * with opposite answers: the authoring door refused it, while the DDL door