Skip to content

fix(spec): name reference, not the rejected referenceTo alias, in the public-picker prose - #13198

Merged
os-elon merged 4 commits into
mainfrom
claude/issue-13138-picker-reference-key
Aug 29, 2026
Merged

fix(spec): name reference, not the rejected referenceTo alias, in the public-picker prose#13198
os-elon merged 4 commits into
mainfrom
claude/issue-13138-picker-reference-key

Conversation

@os-trump

Copy link
Copy Markdown
Collaborator

Fixes#13138

FormFieldPublicPickerSchema.object stated twice — once in the docblock, once in the published .describe() — that the picker target resolves from referenceTo on the parent object's field definition. referenceTo is not a key FieldSchema accepts. It is a rejected alias, listed in the field schema's alias map (packages/spec/src/data/field.zod.ts) only so strictUnknownKeyError can offer a rename hint when a parse fails on it. An author who followed the sentence got their whole object metadata refused at parse — a failure, not a degraded render.

Premise re-verified on this branch

Re-verified before editing, against the builtpackages/spec/dist/data/index.mjs, with the card's own three-level control. A two-level control cannot separate "rejected" from "not measured"; the negative control is what proves the harness can see a refusal at all. Verbatim readings:

spec dist entry: /home/user/objectstack-issue-13138/packages/spec/dist/data/index.mjs
LEVEL 1 positive control reference: ACCEPT
parsed.reference = "sys_user"
parsed.referenceTo = undefined
LEVEL 2 negative control zzz_not_a_key: REFUSE
code=unrecognized_keys path=[]
keys=["zzz_not_a_key"]
message="Unrecognized key(s) on this field: `zzz_not_a_key`. Until this shape was closed
these were dropped silently — the field was still created, minus whatever the key was
meant to constrain, protect or compute."
LEVEL 3 the claim referenceTo: REFUSE
code=unrecognized_keys path=[]
keys=["referenceTo"]
message="Unrecognized key(s) on this field: `referenceTo`. Did you mean `referenceTo` →
`reference`? Until this shape was closed these were dropped silently — the field was
still created, minus whatever the key was meant to constrain, protect or compute."

The premise reproduces exactly: reference accepted, an invented key refused without a rename hint, referenceTo refused with one.

The published half really did ship

The .describe() is the sharper site because it is published. gen:docs proved it: the false sentence rendered twice on content/docs/references/ui/view.mdx — the picker schema is emitted both nested under FormField and standalone as FormFieldPublicPicker. Both are corrected.

Generated artifacts

check:generated proved exactly one artifact stale, verdict quoted before and after.

Before, on the edited source:

 ✗ check:docs content/docs/references/**
✗ 1 of 14 artifact(s) stale:
content/docs/references/**
pnpm --filter @objectstack/spec gen:docs

Regenerated with the repo's own tooling — pnpm --filter @objectstack/spec gen:docs, never by hand. After, on the final head:

GATE 14 PASS exit=0 :: pnpm --filter @objectstack/spec run check:generated

Sibling sweep (this card's lane only)

Swept packages/spec for other prose naming referenceTo as an accepted key. This was the only instance. The other mentions are all correct and were left alone — they describe it as a rejected alias, which is what it is:

  • data/field.zod.ts — the alias-map entry itself (referenceTo: 'reference'), the declaration that makes the rename hint possible
  • ui/action.zod.tsACTION_PARAM_KEY_ALIASES, folding the spelling onto reference
  • shared/alias-probe.ts, shared/suggestions.test.ts, ui/action.test.ts — probe/suggestion machinery and its pins

A wider check for other rejected FieldSchema aliases named in spec prose (relatedTo, targetObject, lookupObject, isRequired, mandatory, notNull, and others) found only correct usages, each explicitly labelled an "alias spelling". packages/rest is #13137's lane and is untouched.

Scope

Prose only. No schema shape, no accept/reject movement, no key added or removed — every previously-valid input parses byte-identically. Runtime resolution is untouched.

This takes no position on #12920 (whether the REST route's legacy-spelling fallback chain survives). The sentence was wrong under either outcome, because a conformant authored field carries reference in both worlds — and the route already reads def?.referencefirst, before any fallback. That independence is why this card lands alone.

Verification

Gate family derived mechanically with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (no hand-fed paths; three-dot semantics against the merge base). Union re-run after the final commit, at cac171864, exit codes captured before any pipe.

49 of 53 derived gates PASS, including every load-bearing one for this card:

GATE 01 PASS exit=0 :: pnpm --filter @objectstack/spec run check:authorable-surface
GATE 05 PASS exit=0 :: pnpm check:doc-authoring
GATE 08 PASS exit=0 :: pnpm --filter @objectstack/spec run check:docs
GATE 14 PASS exit=0 :: pnpm --filter @objectstack/spec run check:generated

Plus the convention-triggered gate and the affected package, same head:

PASS exit=0 :: pnpm check:nul-bytes
PASS exit=0 :: pnpm check:dispatcher-error-vocabulary
PASS exit=0 :: pnpm --filter @objectstack/spec typecheck

pnpm --filter @objectstack/spec test443 test files, 11752 tests, all passed. That run was at 70b067251; git diff 70b067251 cac171864 -- packages/ is empty, so the test inputs are byte-identical at the final head.

4 gates are NOT MEASURED locally, none of them a red — each refuses by its own verdict text rather than returning a false green, and each is a container/invocation limit, not a finding:

gateits own verdict
check:dual-build-cjs-loadsPREREQUISITE NOT MET — this gate reads built output (needs a full repo pnpm build)
check-dev-prereqs.mjs66 of 67 packages have no dist/; needs a full repo pnpm build
check-test-completeness.mjs"That list names this script with NO argument … the local reading for this gate is NOT MEASURED. ⛔ It is not a red"
check-half-states.mjsPREREQUISITE NOT MET — the token in the environment is not a valid GitHub credential

Three further gates initially reported PREREQUISITE NOT MET and were converted into real measurements by building what they read (@objectstack/formula, @objectstack/lint, @objectstack/client-react, @objectstack/client), after which check:doc-formula-expressions, check:doc-security-posture and check:skill-examples all pass.

Declared narrowing: gen:docs was run outside the shared heavy-verify lock after ~18 minutes at the head of its queue with no progress. The heavy half (the spec build) was already complete and dist verified current for the edit, so the remaining step was a docs render, not a build. CI runs the full farm regardless.

Changeset

@objectstack/spec: patch. A published .describe() string does ship, and in-repo precedent for exactly this shape — a describe correction that regenerates a content/docs/references/** page — carries one: mongo-options-describe-boundary and visiblewhen-describe-bound-roots are both @objectstack/spec: patch. The skip-changeset closed list (docs/adr/**, .claude/**, scripts/pm/**, tests/workflow, comments) does not cover this diff.

Landing posture

needs:contract-review is attached (clause-② PATH leg: the diff touches packages/spec/src/**). The CONTENT leg does not apply — nothing about what parses changes.

This PR stays DRAFT. It is not to be marked ready, auto-merged or enqueued: the PM seat's tier fuse is down and contract review is parked for the review chain. A PR waiting in draft is the correct end state here, not a failure.


Generated by Claude Code

… the public-picker prose
`FormFieldPublicPickerSchema.object` told authors the picker target resolves
from `referenceTo` on the parent object's field definition. `referenceTo` is
not a key `FieldSchema` accepts — it is a rejected alias listed only so
`strictUnknownKeyError` can offer a rename hint, so an author who followed the
sentence had their whole object metadata refused at parse.
The `.describe()` half is the sharper one: it is published into the generated
JSON Schema and the reference docs, so it reached authors (and AI authors
reading the generated schema as ground truth) who never open this file.
Purely textual. Nothing about the runtime resolution changes, and this takes no
position on the REST route's legacy-spelling chain — the sentence is wrong under
either outcome, because a conformant authored field carries `reference` in both
worlds.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4
… prose, add changeset
`check:generated` proved exactly one artifact stale after the describe change:
`content/docs/references/ui/view.mdx`. Regenerated with the repo's own
`pnpm --filter @objectstack/spec gen:docs`, never by hand.
The false sentence rendered TWICE on that page — the picker schema is emitted
both nested under `FormField` and standalone as `FormFieldPublicPicker` — which
is the published half of the defect the card names.
Changeset is a `@objectstack/spec` patch, matching in-repo precedent for
describe-string corrections that regenerate a references page.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4
@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, so this run has nothing to listnot a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

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 c25bfb33271b49b2c77e8a25da0ad7f96b9eed49packageMentionDocs.

Which tree this was computed on

This run read content/docs from ade189fc7a09fefc37fe086e30ad5bbe0360396b — the merge of head cac1718642fcf705f0b180206552253943ea1259 into base c25bfb33271b49b2c77e8a25da0ad7f96b9eed49, 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 ade189fc7a09fefc37fe086e30ad5bbe0360396b && git checkout ade189fc7a09fefc37fe086e30ad5bbe0360396b
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin c25bfb33271b49b2c77e8a25da0ad7f96b9eed49 cac1718642fcf705f0b180206552253943ea1259 && git checkout -B drift-repro c25bfb33271b49b2c77e8a25da0ad7f96b9eed49 && git merge --no-ff cac1718642fcf705f0b180206552253943ea1259
node scripts/docs-audit/affected-docs.mjs --json c25bfb33271b49b2c77e8a25da0ad7f96b9eed49

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

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation protocol:ui tooling labels Aug 29, 2026
@os-trumpos-trump added needs:contract-review and removed documentation Improvements or additions to documentation tooling protocol:ui labels Aug 29, 2026 — with Claude
@os-elon
os-elon marked this pull request as ready for review August 29, 2026 09:49
@os-elon
os-elon enabled auto-merge August 29, 2026 09:49
@os-elon
os-elon added this pull request to the merge queueAug 29, 2026
Merged via the queue into main with commit 2d2e6f0Aug 29, 2026
50 checks passed
@os-elon
os-elon deleted the claude/issue-13138-picker-reference-key branch August 29, 2026 10:33
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

3 participants

@os-trump@os-elon@claude