docs(field-types): give unique its real scope vocabulary - #14941
Merged
Conversation
The Universal Field Properties row typed `unique` as `boolean` with the
one-line description "Enforce uniqueness". The contract has been
`boolean | 'global' | 'organization'` since ADR-0120 D1, so the table hid
two of the three spellings — a reader who trusted it could not express
`'global'` at all, and was contradicted by this same page's own example
at :50, which passes `unique: 'organization'`.
The type cell now matches the generated reference
(`content/docs/references/data/field.mdx`) spelling for spelling, and the
description points at the `FieldSchema` doc block rather than restating a
rule that regenerates elsewhere — the convention the sibling `multiple`
row on this table already uses.
The default cell is deliberately left at `false`. Measured against this
tree: `FieldSchema.parse({ type: 'autonumber', ... })` with `unique`
omitted resolves to `false`, exactly as `text` does. The card asked for a
type-conditional default (`'organization'` on `autonumber`) predicated on
#13894, which has not landed. Documenting it would assert a default the
schema does not implement.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
This was referenced Sep 3, 2026
baozhoutao
marked this pull request as ready for review
September 3, 2026 11:39
baozhoutao
enabled auto-merge
September 3, 2026 11:39
baozhoutao
deleted the
claude/issue-14901-unique-row-scope-vocabulary
branch
September 3, 2026 12:12
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #14901 — this lands the type-vocabulary half only. The default half is deliberately not written; see "The default cell stays
false" below. The card stays open to carry it.The row
content/docs/data-modeling/field-types.mdx, Universal Field Properties:The contract has been
boolean | 'global' | 'organization'since ADR-0120 D1, so the table hid two of the three spellings: a reader who trusted it could not express'global'at all, and was contradicted by this same page's own worked example at:50, which passesunique: 'organization'.Two things kept the edit anchored rather than invented:
content/docs/references/data/field.mdx:64(generated fromFieldSchema, carries theAUTO-GENERATEDbanner) already renders`boolean \| 'global' \| 'organization'`. This hand-written row now agrees with it byte for byte in that cell, so the two pages cannot drift apart on the vocabulary.multiplerow ends the same way, for the same reason: a hand-written row that restates a rule which regenerates elsewhere is a second copy waiting to go stale.The default cell stays
false— measured, not assumedThe card asked for a type-conditional default (
falseeverywhere,'organization'onautonumber) predicated on #13894. That premise does not hold on this tree, so writing it would have asserted a default the schema does not implement.packages/specbuilt at4b8f5f39, thenFieldSchema.safeParseon the resolved output:An omitted
uniqueonautonumberresolves tofalse, exactly as ontext. Corroborating, in the source:unique: UniqueScopeSchema.default(false)is unconditional, and the only value-materializing hook onFieldSchema— the.overwrite()— deals exclusively withdeleteBehaviorand early-returns long before it could reachunique.superRefinecannot rewrite values. Independently, the generated reference page states the default asfalsewith no type condition. #13894 is still open and is referenced nowhere in the tree.All four accepted spellings parse and
'tenant'is refused, so the row's vocabulary is stated as measured.packages/spec/**was not touched — the spec is the correct side of this disagreement, and the docs move to it.Gates
Derived off the real change set from this worktree, not from a supplied list:
34 commands, all run at
4b8f5f39(the final commit — tree clean, nothing landed after the sweep). Every exit code captured before any pipe. 33 green, 1 NOT MEASURED, no findings.The one not measured is
scripts/check-test-completeness.mjs, which exits 3 with no argument and says so itself: it reads a saved test-run log, the derived family names it with no argument, and its own output instructs recording it as NOT MEASURED locally. It is not a red and there is nothing to fix.Three gates first came back as refusals to measure rather than findings —
check:doc-formula-expressionsandcheck:doc-security-posture(exit 3,PREREQUISITE NOT MET:@objectstack/formula/@objectstack/lintnot built) andcheck:skill-examples(exit 1, refusing to compute against aclient-reactdist with no declarations). I built those three packages and re-ran all three; each then measured green. They are counted green on the real reading, not on the refusal.Sample verdict lines, quoted from the gates themselves:
check-doc-anchorsis the one that matters for the new link: the/docs/references/data/fieldtarget resolves.No changeset: the only changed path is
content/docs/**, consumed byapps/docs(private: true), so nothing publishes from a package.skip-changesetapplied.Also filed
#14940 — the
autonumbercallout on this same page (:485,:493) promises "guarantees a unique, ever-increasing business identifier", which the measurement above shows is not what a bare autonumber gets. Not swept in: the correct wording is a consequence of how #13894 is decided, not a mechanical substitution. Filed unassigned withBlocked-by: #13894.🤖 Generated with Claude Code
https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
Generated by Claude Code