diff --git a/.changeset/org-scoped-unique-jsdoc-field-level.md b/.changeset/org-scoped-unique-jsdoc-field-level.md new file mode 100644 index 0000000000..93d63858f5 --- /dev/null +++ b/.changeset/org-scoped-unique-jsdoc-field-level.md @@ -0,0 +1,26 @@ +--- +"@objectstack/driver-sql": patch +--- + +docs(driver-sql): `isOrganizationScopedUnique` documents the FIELD-level spelling only + +The exported helper's JSDoc claimed it judged organization scope "on either +spelling (field-level `unique` or a declared index's `unique`)". It does not, +and never did: both of its call sites pass `field.unique`, while +`normalizeDeclaredIndex` scopes a declared index with a strict +`idx?.unique === 'organization'` — so a declared index's bare `unique: true` +is taken verbatim as global. + +That divergence is deliberate (the #4986 answer, ADR-0120 D1), but the comment +invited the tidy-up that would erase it — routing the declared-index branch +through the helper, which is option 1 of #8323 (⛔ rejected by the maintainer, +2026-08-13) and pre-empts the bare-spelling question parked on #5082. The +corrected JSDoc states what the helper actually judges, points at +`normalizeDeclaredIndex` and #5082, and records why unifying the two paths is +rejected: it would silently reinterpret every existing declared `unique: true` +on deployed databases as organization-scoped. + +Documentation only — no behaviour, signature or type change. Shipped as a patch +because the helper is a top-level export of the package entry point and +`declaration: true` with no `removeComments` puts this text in the published +`dist/index.d.ts` a consumer reads. diff --git a/packages/drivers/driver-sql/src/schema-drift.ts b/packages/drivers/driver-sql/src/schema-drift.ts index 01b23dbca5..3a16c6f5e2 100644 --- a/packages/drivers/driver-sql/src/schema-drift.ts +++ b/packages/drivers/driver-sql/src/schema-drift.ts @@ -75,9 +75,31 @@ export function isUniqueScopeDeclared(unique: unknown): boolean { } /** - * The organization-scoped spellings: field-level `true` (unchanged since - * #3696) and the explicit `'organization'` synonym (ADR-0120 D1) — on either - * spelling (field-level `unique` or a declared index's `unique`). + * The organization-scoped spellings of a FIELD-level `unique`: bare `true` + * (the positional synonym, unchanged since #3696) and the explicit + * `'organization'` word (ADR-0120 D1). Pass a field's `unique`; do NOT pass a + * declared index's. + * + * This is NOT the scope judgment for a declared index, and it must not be + * reached for there. {@link normalizeDeclaredIndex} decides with a strict + * `idx?.unique === 'organization'` instead, so a declared index's bare `true` + * is taken VERBATIM as global — the `'global'` arm. That the two paths judge + * the same token differently is the answer to #4986, not an oversight: the + * spellings were authored under different contracts, and both halves are + * pinned (`sql-driver-declared-index-organization-respelling.test.ts`). + * + * Routing the declared-index branch through this predicate so code and comment + * agree is REJECTED — maintainer ruling 2026-08-13, option 1 of #8323. It + * would silently reinterpret every existing declared `unique: true` on + * deployed databases as organization-scoped: an unannounced index migration, + * landing a release BEFORE #5082 refuses the bare spelling — the + * two-migrations-with-contradictory-meanings sequence that ruling exists to + * avoid. Whether a declared index's bare `true` should be refused at all is + * PARKED on #5082 (v18 D2: bare `true` → `'global'` plus a loud refusal). + * Until that lands the divergence stays, surfaced to authors rather than + * silently repaired: lint `unique/unscoped-declared-index` warns on it + * (`packages/lint/src/data-model-rules.ts`) and `IndexSchema.unique`'s + * `describe()` states it (`packages/spec/src/data/object.zod.ts`). */ export function isOrganizationScopedUnique(unique: unknown): boolean { return unique === true || unique === 'organization';