Skip to content

docs(spec): NoSQLIndexSchema.unique documents its deliberate scope-vocabulary omission - #11593

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-11215-nosql-unique-scope
Aug 24, 2026
Merged

docs(spec): NoSQLIndexSchema.unique documents its deliberate scope-vocabulary omission#11593
os-steve merged 1 commit into
mainfrom
claude/issue-11215-nosql-unique-scope

Conversation

@os-steve

Copy link
Copy Markdown
Collaborator

Fixes#11215

Branch decision: (b) — not tenant-aware / descriptor-layer. Measure-first card; the settling measurement (below) found no consumer that materializes NoSQLIndexSchema declared indexes at all, let alone against organization-aware collections, so the surface documents its deliberate omission of the ADR-0120 scope vocabulary instead of adopting it. Held as draft for the PM's contract review (clause-② path-limb, needs:contract-review hung at claim).

Measurement map

  1. Premise held at merge base 2dc0a770b6: packages/spec/src/data/driver-nosql.zod.ts:403 was still the bare boolean (unique: z.boolean().default(false)), while both sibling surfaces carry the vocabulary — UniqueScopeSchema at packages/spec/src/data/field.zod.ts:476 (used by FieldSchema.unique, field.zod.ts:822) and DeclaredIndexUniqueScopeSchema at packages/spec/src/data/object.zod.ts:431 (used by IndexSchema.unique, object.zod.ts:483, the UniqueScopeSchema's rejection message calls 'organization' "the explicit spelling of true" on BOTH surfaces — on a declared index bare true means 'global', so the message prescribes a silent scope change #10928 / PR fix(spec): give the declared-index unique surface its own rejection message #11213 per-surface message pattern).
  2. NoSQLIndexSchema consumer graph — empty. Repo-wide grep for NoSQLIndexSchema / NoSQLIndex hits only spec's own test (packages/spec/src/data/driver-nosql.test.ts), spec's generated artifacts, and the generated reference docs. No runtime, kernel, or driver import; it is a leaf schema — no other schema embeds it.
  3. The whole driver-nosql.zod.ts module is descriptor vocabulary with no live consumer: NoSQLQuerySchema, NoSQLConnectionSchema, AggregationPipelineSchema, NoSQLDatabaseTypeSchema, NoSQLTransactionOptionsSchema — zero imports outside packages/spec.
  4. isGlobalUnique / isOrganizationUnique reachability from the NoSQL side — none. The driver-facing helpers (packages/spec/src/data/field.zod.ts:490 / :518) are consumed by spec itself and packages/drivers/driver-sql/src/schema-drift.ts:68 only.
  5. The one NoSQL driver that creates indexes takes the other surface. driver-mongodb's syncCollectionSchema (packages/drivers/driver-mongodb/src/mongodb-schema.ts:79, called from mongodb-driver.ts:592) materializes the object-level indexes[] — the IndexSchema shape (mongodb-schema.ts:54-58), which already carries the vocabulary — and is explicitly single-tenant (settled by driver-mongodb 完全没有行级租户隔离:读不加谓词、写不打戳,多租户下跨租户可读写 #3724; mongodb-schema.ts:18-35 and :135-139): every scope materializes the listed columns verbatim, and no organization key part is injected anywhere on the NoSQL side.
  6. ADR-0120 mentions NoSQL zero times. D1's "both spellings" are the field-level and declared-index surfaces; NoSQLIndexSchema was never in the ADR's scope.

Conclusion: the 'organization'/'global' business boundary is stated on the authorable surfaces and resolved into physical key columns above this layer — by the time a descriptor of this shape would reach a NoSQL engine, an organization key part (when there is one) is already a listed field. A scope word here would be declarable-but-inert vocabulary (ADR-0078). The asymmetry the card observed is deliberate, not drift — the card's own stated right outcome for this branch.

Changes

  • packages/spec/src/data/driver-nosql.zod.ts — docblock + describe() on NoSQLIndexSchema.unique: states the layer, the measurement, why the vocabulary deliberately does not apply, and the condition under which UniqueScopeSchema should be adopted (import, never fork). The shape is untouched — still z.boolean().default(false), so no parse/materialization change (the FieldSchema accepts deleteBehavior: 'set_null' on a master_detail, and the engine silently resolves it to cascade #9689 parse-idempotency clause is branch-(a) material and does not arise).
  • content/docs/references/data/driver-nosql.mdx — regenerated (gen:docs).
  • .changeset/nosql-index-unique-scope-docs.md — patch on @objectstack/spec.

Verification

All dispatch-derived gates ran at commit 8db4595ecf (clean tree, exit codes captured before any pipe; wrapper verdict lines quoted):

  • pnpm --filter @objectstack/spec buildos-verify-lock: VERDICT command-exit 0 (runs gen:schema + gen:openapi + tsup + DTS; the authorable-surface roster is byte-unchanged — the key set did not move).
  • pnpm --filter @objectstack/spec check:generated — first run flagged exactly 1 of 14 artifacts stale (content/docs/references/**, the expected product of the describe change); --fix ran gen:docs only; re-verified VERDICT command-exit 0. The new describe text is present in the regenerated driver-nosql.mdx.
  • pnpm --filter @objectstack/spec testTest Files 420 passed (420), Tests 11213 passed (11213).
  • pnpm --filter @objectstack/spec typecheckVERDICT command-exit 0.
  • node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack at 8db4595ecf derived 33 families; all 33 green. Two (check:doc-formula-expressions, check:doc-security-posture) first failed on fresh-worktree missing dist (@objectstack/formula, @objectstack/lint) and went green after pnpm --filter "@objectstack/lint..." build — unbuilt-dependency artifacts, not this diff.
  • Declared narrowing (one gate): standalone node scripts/check-dev-prereqs.mjs asserts the entire workspace is built and reds in this fresh worktree because 63 of 67 packages have no dist/ — a build-state precondition CI provisions with a full pnpm build, independent of this diff (population: workspace build state; this diff cannot move it). The spec package's own prereq stamp — the one package this diff touches — was refreshed and verified during the spec build (✓ packages/spec/dist/.build-input-hash).
  • node scripts/check-nul-bytes.mjs — OK (6490 files scanned).

Generated by Claude Code

…ulary omission (#11215)
The bare boolean is deliberate: this schema is the raw NoSQL
driver-configuration descriptor layer below the tenancy seam. Nothing in
the repo materializes indexes from NoSQLIndexSchema (leaf schema, no
runtime/kernel/driver import), and the one NoSQL driver that creates
indexes (driver-mongodb syncCollectionSchema) consumes the object-level
indexes[] surface -- IndexSchema, which already carries the ADR-0120
vocabulary -- and is explicitly single-tenant (#3724). A scope word here
would be declarable-but-inert (ADR-0078). Docblock + describe() now say
so, and name the condition under which UniqueScopeSchema should be
adopted.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T9cDbY2NBiVJWYx3BpWfH2
@github-actionsgithub-actionsBot added size/s documentation Improvements or additions to documentation protocol:data tooling labels Aug 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ✅

What this run could not see
  • the SDK route bridge reached 45 of 222 client-bound route-ledger rows — the other 177 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run: node scripts/docs-audit/affected-docs.mjs --bridge-coverage

Coarse fallback — 126 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 2dc0a770b61fa0b140994ddcdbe692256eb53666packageMentionDocs.

Which tree this was computed on

This run read content/docs from 99201179e0045d29105fe6cc097260bf0688e741 — the merge of head 8db4595ecff4189b2c789c341d0d41a023efa784 into base 2dc0a770b61fa0b140994ddcdbe692256eb53666, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 99201179e0045d29105fe6cc097260bf0688e741 && git checkout 99201179e0045d29105fe6cc097260bf0688e741
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 2dc0a770b61fa0b140994ddcdbe692256eb53666 8db4595ecff4189b2c789c341d0d41a023efa784 && git checkout -B drift-repro 2dc0a770b61fa0b140994ddcdbe692256eb53666 && git merge --no-ff 8db4595ecff4189b2c789c341d0d41a023efa784
node scripts/docs-audit/affected-docs.mjs --json 2dc0a770b61fa0b140994ddcdbe692256eb53666

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationprotocol:datasize/stooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

NoSQLIndexSchema.unique is a bare boolean while both other unique surfaces carry the ADR-0120 scope vocabulary — is the omission deliberate?

2 participants

@os-steve@claude