Skip to content

docs(fields): attribute option description to objectui, not to the spec - #7994

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-7537-lookup-option-description-attribution
Sep 6, 2026
Merged

docs(fields): attribute option description to objectui, not to the spec#7994
baozhoutao merged 2 commits into
mainfrom
claude/issue-7537-lookup-option-description-attribution

Conversation

@baozhoutao

@baozhoutao baozhoutao commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Fixes #7537

Docs and changeset prose only. No type, schema, export or runtime path moves.

Half 1 — content/docs/fields/lookup.mdx

Before (:68 on 36fc746):

It is a declared SelectOptionMetadata member (declared for exactly that consumption — objectui#6153 — and aligned with @objectstack/spec's SelectOptionSchema.description).

After — the same attribution PR objectui#7510 gave the published JSDoc on SelectOptionMetadata.description (as shipped after the patch round below, which removed the version literal):

It is a declared SelectOptionMetadata member, declared for exactly that consumption (objectui#6153), but it is an objectui-side read-model extension, not a spec key. Measured on the installed @objectstack/spec, SelectOptionSchema is strict over exactly {label, value, color, default, visibleWhen} and refuses description by name (unrecognized_keys), with the same option minus the key accepted as the control. FieldSchema routes a field's options through that schema, so authoring description on an option in an object document fails the whole field with a 422 — the key lives on the runtime read model the widget consumes and must never reach authored object metadata (objectui#7014).

The key stays declared and stays consumed — LookupField really does search it. Only the attribution was wrong, and it was wrong in the direction that costs the reader the most.

The reading behind that sentence (re-measured today, not carried from the card)

Package resolved to node_modules/.pnpm/@objectstack+spec@17.2.0_ai@7.0.65_zod@4.4.3_/node_modules/@objectstack/spec, entry dist/data/index.js. The version belongs here, in a dated pull-request body, rather than in the page — see the patch round.

SelectOptionSchema declared keys : color, default, label, value, visibleWhen
CONTROL clean option             : ACCEPT
NEG-CONTROL 1-char value         : REJECT too_small@value
option + description             : REJECT unrecognized_keys(description) at [root]
FIELD control (clean option)     : ACCEPT
FIELD option + description       : REJECT unrecognized_keys(description) at [options.0]

The negative control uses a value short enough to trip too_small, which is what makes the clean-option ACCEPT a reading rather than a coincidence.

Half 2 — .changeset/6140-field-metadata-rows-option-description.md, corrected in place

The card and its triage both recorded a mechanism assumption: that scripts/check-changeset-overwrite.mjs "fails any change that modifies a pre-existing changeset". That is not what the shipped gate does, and its own header is the source:

Report-only is the DEFAULT and the shipped behaviour

12 commits MODIFY a pre-existing changeset (19 files), and all 19 are legitimate: bump-level corrections …, factual corrections to prose ("eleven" to "ten" locale packs; a typo'd package name …) …

So the premise "a PR modifying a pre-existing changeset is almost always a mistake" is NOT true of this repository's history — 19 for 19 against — and a blocking gate would have failed every one of those pull requests.

and, in the report it prints on this very diff, case 2 of the three shapes it names:

  1. You are CORRECTING a declaration on purpose — a bump level after review, a wrong package name, prose that no longer matches the change. Legitimate, and the reason this gate reports instead of failing.

.github/workflows/changeset-guard.yml runs it with no OS_CHANGESET_OVERWRITE_ENFORCE, so report-only is CI's configuration, not a local relaxation. So this half needed no decision-box escalation and no additive correction changeset — an additive one would in any case have left the false sentence in the CHANGELOG and merely printed a correction beside it, which is the opposite of what the card asked for.

Gate output on this diff (exit 0):

Compared the working tree with 36fc74629 (merge-base with origin/main): 0 changeset(s) added, 1 modified, 0 deleted.
      M  .changeset/6140-field-metadata-rows-option-description.md
             declared at base: @object-ui/types: minor, @object-ui/fields: patch
             declares now:     @object-ui/types: minor, @object-ui/fields: patch

No GONE from the declaration line: every package name the declaration carried at base is still there. That is the narrower signal the gate's header says a future blocking rule would be scoped to, and this change passes it too. Under the non-shipped OS_CHANGESET_OVERWRITE_ENFORCE=1 flip the same run exits 1 on the same finding — recorded for honesty; that flag is set nowhere in this repository.

Bounded in-place fix, declared

That changeset declares two keys, and both carried the same false spec attribution — rows as well as option description. The card named only :18. Correcting one and leaving its sibling would ship a release note that reads as if the rows claim had been verified, and would cost a second deliberate modification of a pre-existing changeset later. So both were corrected in one stroke, both backed by today's reading:

textarea  CONTROL (no rows): ACCEPT   | + rows: 4 : REJECT unrecognized_keys(rows)
markdown  CONTROL (no rows): ACCEPT   | + rows: 4 : REJECT unrecognized_keys(rows)
html      CONTROL (no rows): ACCEPT   | + rows: 4 : REJECT unrecognized_keys(rows)
richtext  CONTROL (no rows): ACCEPT   | + rows: 4 : REJECT unrecognized_keys(rows)

The bumps (@object-ui/types: minor, @object-ui/fields: patch) and the declared behaviour are untouched.

Root cause worth one line

scripts/check-spec-symbol-derivation.mjs cannot see prose in content/docs, which is why PR objectui#7510 corrected three TypeScript JSDoc claims and this documentation copy survived. Widening that gate's scan surface is a separate card (same family as the check-readme-exports scan-surface point recorded in objectui#7417) and is deliberately not attempted here.

Reverse check

The gate family cannot see prose, so the reverse check for this change is textual, on the page and on the changeset:

measurement before after
lookup.mdx — the false attribution string 1 0
lookup.mdxrefuses (the new attribution's key word) 0 1
changeset — aligned with 2 0
changeset — NOT a (spec key) 0 2

Repo-wide sweep after the change, git grep -n "SelectOptionSchema.description" -- content/docs .changeset packages/*/README.md: the only surviving hit is objectui#7014's own correction changeset, which quotes the old claim in order to correct it.

Gates

Run on f5154f2 and re-run on 1809407 (the patch head); exit codes captured by redirect, never through a pipe.

gate exit verdict line
vitest scripts/__tests__/doc-version-claims.test.ts 0 Test Files 1 passed (1) · Tests 29 passed (29) — red before the patch, see below
pnpm check:doc-snippets 0 Semantic phase: 495 of 495 block(s) judged, 0 failed.
pnpm check:doc-fences 0 every TypeScript block in 227 document(s) correctly fenced
node scripts/check-doc-links.mjs 0 Links are valid across 17 scan roots.
pnpm check:doc-types 0 Every documented component type is registered.
pnpm check:control-bytes 0 OK (scanned 6435 tracked text file(s); skipped 85 binary)
pnpm check:docs-route-closure 0 clean
pnpm check:doc-example-readers 0 no @example hand-spells one
node scripts/check-changeset-presence.mjs 0 no changeset is owed (nothing this change touches is published source)
node scripts/check-changeset-overwrite.mjs 0 report-only finding above, no declaration lost
pnpm changeset:check 0 fixed group OK, no major
node scripts/check-governed-queue-guard.mjs --test 0 NOT GOVERNED — 1 path(s) checked
turbo run build --filter=./packages/* 0 39 successful, 39 total (run before the doc-snippet gate)
vitest run scripts/__tests__/check-doc-links.test.ts 0 121 passed

TURBO_SCM_BASE=36fc746 turbo ls --affected reports 0 packages, so no package test / typecheck is owed. Repo-wide eslint --format json on both changed files reports 2 files, errors=0, each File ignored because no matching configuration was supplied. — the repo-wide lint has no judgement to make about .mdx / .md, so nothing was narrowed away.

Readers of the page (git grep -l 'fields/lookup.mdx' over scripts/ packages/ .github/ examples/ apps/): scripts/check-doc-links.mjs and its test (both green above), packages/plugin-grid/src/relationalMetaKeys.ts (a comment citing the page for the snake-case / camelCase dialect — untouched, and the lookupFilters spelling question belongs to objectui#7021), and examples/console-starter/README.md (a link, still valid).

Patch round (R45)

Removed from the merge queue at 07:49:20Z. Merge-group run 34019566751, job 101449510557, Test (shard 4/4):

FAIL  scripts/__tests__/doc-version-claims.test.ts:2271
AssertionError: These doc surfaces state a version and nothing in this repository can tell whether it is still true:
  - content/docs/fields/lookup.mdx:69  "@objectstack/spec` 17.2.0"
Prefer DELETING the literal and pointing at the truth instead …

Fix (1809407, one line): the sentence now reads "Measured on the installed @objectstack/spec, …". The literal is deleted, not added to KNOWN_CLAIMS — the pin's own message prefers deletion, and objectui#3645 is the reason: a spec range frozen in 36 READMEs survived thirteen majors because no review step ever asked whether it was still true. The measured version stays in this body and on the card, where it is dated. Every other byte of the sentence is unchanged.

The changeset keeps its literal, deliberately. The pin's population is SCAN_ROOTS = ['content/docs', 'packages/*/README.md', 'skills'], so .changeset/** is outside it — and a changeset is a dated release note about a measurement already taken, the one place a version literal cannot go stale.

Reproduced red before, green after, both on this branch:

before   AssertionError … content/docs/fields/lookup.mdx:69  "@objectstack/spec` 17.2.0"
         Test Files  1 failed (1)
after    Test Files  1 passed (1)
         Tests  29 passed (29)

Why this PR's own Test (shard 4/4) was green at 07:34Z

Not shard assignment. In the PR run 34019282906 all four shards report Run tests (shard N/4) with conclusion skipped and the job still concludes success: the Decide whether this change needs a full run step took the no-full-run path, because this diff is entirely content/docs/** and .changeset/**, both on that step's exclusion list. The whole run finished in 48 seconds. So the PR-run shard 4 was green because it executed zero tests, not because the pin passed there. The merge-group build does not take that path — its four shards each ran 12 to 13 minutes over ef6d7d4a (this commit merged onto main at 15789d5), and shard 4 is where doc-version-claims.test.ts actually executed and failed.

The consequence, stated rather than filed: on a docs-only pull request the first real execution of the repo-wide doc pins is inside the merge queue. That is the documented design of the path filter, and the queue did catch it — but it is why a docs-only PR's green shards are not evidence that the doc pins passed.

CI note

Live E2E (informational) is red on every branch today for an upstream reason (objectui#7990 / objectstack#16186). It is not this diff's.

Session, as a code span so it survives a body rewrite: https://claude.ai/code/session_01MM7kaS4dPpYHV5BsMyu4tQ

🤖 Generated with Claude Code

https://claude.ai/code/session_01MM7kaS4dPpYHV5BsMyu4tQ

…spec

`content/docs/fields/lookup.mdx` told readers that `SelectOptionMetadata`'s
option `description` is "aligned with `@objectstack/spec`'s
`SelectOptionSchema.description`". Re-measured today against the installed
`@objectstack/spec` 17.2.0, that schema declares exactly
`color, default, label, value, visibleWhen` and refuses `description` by name
with `unrecognized_keys`; `FieldSchema` routes a field's `options` through it,
so the sentence was inviting a write that fails the whole field with a 422.

The page now carries the same attribution PR #7510 gave the published JSDoc on
`SelectOptionMetadata.description`: an objectui-side read-model extension the
spec refuses by name, declared because `LookupField` consumes it, and never to
be written into an authored object document. The key itself stays declared and
stays consumed.

The still-unconsumed `.changeset/6140-field-metadata-rows-option-description.md`
carried the same false claim on both keys it declares, scheduled to ship into
the CHANGELOG. Corrected in place as a deliberate prose correction, which
`scripts/check-changeset-overwrite.mjs` documents as its case 2 and reports
without failing; every package name the declaration carried at base is
unchanged, so nothing is lost from it.

Docs and changeset prose only — no type, schema, export or runtime path moves.
Settles objectui#7537.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MM7kaS4dPpYHV5BsMyu4tQ
…note

`scripts/__tests__/doc-version-claims.test.ts` refuses a version literal on the
surfaces it scans, and the sentence added by the previous commit carried one:
"Measured on the installed `@objectstack/spec` 17.2.0". The pin is right — a
version frozen in prose is the objectui#3645 shape, where a spec range sat in
36 READMEs across thirteen majors because no review step ever asked whether it
was still true.

The literal is deleted rather than added to KNOWN_CLAIMS, which is what the
pin's own message asks for. The sentence now reads "Measured on the installed
`@objectstack/spec`"; the version of that reading lives in the pull request
body and on the card, where it is dated and cannot silently go stale. Every
other byte of the sentence is unchanged, so the attribution the card asked for
is intact.

The changeset corrected in the previous commit keeps its literal: the pin's
SCAN_ROOTS are content/docs, packages/*/README.md and skills, so `.changeset`
is outside its population — and a changeset is a dated release note about a
measurement already taken, which is the one place a version literal cannot rot.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MM7kaS4dPpYHV5BsMyu4tQ
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 6, 2026
Merged via the queue into main with commit e1545cf Sep 6, 2026
31 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-7537-lookup-option-description-attribution branch September 6, 2026 08:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs(fields): lookup.mdx still claims option description is aligned with @objectstack/spec — the spec refuses it by name

2 participants