Skip to content

feat(spec): declare hideEmpty / collapsible / showBorder on record:details sections - #11662

Merged
os-warren merged 3 commits into
mainfrom
claude/issue-11289-record-details-section-keys
Aug 24, 2026
Merged

feat(spec): declare hideEmpty / collapsible / showBorder on record:details sections#11662
os-warren merged 3 commits into
mainfrom
claude/issue-11289-record-details-section-keys

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

Fixes#11289

Maintainer ruling 2026-08-23 (on-card, comment 5386670045), direction 1: declare the three keys on RecordDetailsProps sections with defaults matching current renderer behavior; direction 2 (renderer change) not approved — this PR touches no objectui code.

What changed

  • packages/spec/src/ui/component.zod.ts — the record:details section shape gains hideEmpty / collapsible / showBorder, each z.boolean().optional() with no schema default (the file's own maxVisible principle, stated at this schema's inlineEdit docblock: the fallbacks are the renderer's, and a schema default would turn "the author said nothing" into "the author asked for the default"). The sections describe() now enumerates the full object form.
  • packages/spec/src/ui/component.test.ts — four new tests: verbatim preservation of the three keys; no materialization on a clean parse; invalid_type at the right path for non-boolean values; strictness survives the widening and the new keys entered the "did you mean" candidate list (asserted in the arrow form `showBorders` → `showBorder` — a bare substring assertion is satisfied by the echoed offending key, which reverse verification actually measured).
  • content/docs/references/ui/component.mdx — regenerated (generator-owned; not hand-edited).
  • .changeset/record-details-section-presentation-keys.md@objectstack/specminor (accept-set widening; nothing removed or renamed, so no ADR-0087 disposition is required — the changeset is not declared-breaking).

Renderer defaults — measured, not transcribed

Measured at the .objectui-sha pin 190fbd01 AND the current objectui checkout e52fac05 (byte-identical logic at both), in objectui packages/plugin-detail:

  • hideEmptyrecord-details.tsx:210: hideEmpty: s.hideEmpty ?? true; with the default, DetailSection.tsx:450 returns null for an all-empty section (no heading, no skeleton). hideEmpty: false renders the empty rows and keeps the label skeleton on an all-empty record (its shouldAutoHideEmpty requires filledCount > 0, so nothing re-hides an all-empty section).
  • collapsibleDetailSection.tsx:509: read as bare truthiness; omitted → non-collapsible card. Default: off; when on, initially expanded.
  • showBorderrecord-details.tsx:204: s.showBorder ?? (translatedTitle ? true : false) — a derived default (titled → bordered card, untitled → borderless), which is also why a materialized .default() could not express it.

The objectstack validate warning this removes asserted the authored key "did nothing" — false for all three (and for collapsible the issue body had already measured it reaching DetailSection intact). Schema and runtime now declare the same key set for this surface.

Verification (all readings at head 9dd48377)

  • @objectstack/spec full suite: Test Files 420 passed (420) · Tests 11222 passed (11222) (the four new tests re-run post-amend in the targeted file: 203/203).
  • Reverse verification, from committed state (git restore --source=$BASE of the schema file only, restore verified by a discriminating text anchor 1/1/0 across worktree/HEAD/BASE): the preservation test and the invalid_type test go red on the pre-declaration schema with unrecognized-keys-shaped failures — and the run exposed that the original suggestion assertion stayed green there (vacuous substring), which is what the arrow-form assertion now pins.
  • @objectstack/spec typecheck (tsc + scripts + test-typecheck): green.
  • Consumer sweep, downstream direction (spec-lane duty): @objectstack/lint (the objectstack validate component-props path) — Test Files 81 passed · Tests 2266 passed | 5 skipped; its one hideEmpty fixture is record:reference_rail's, untouched. No fixture in the rule's consumption radius pins the old rejection.
  • pnpm --filter @objectstack/spec check:generated: all 14 artifacts up to date after regenerating the 2 it proved stale (api-surface/ — no content change after rebuild; content/docs/references/** — the one-row diff above). check:authorable-surface green.
  • node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (no paths — changed set derived by the script itself; derivation stderr confirmed repo/commit): 34 path-matched + convention gates for the test-file kind. 36/38 ran green locally; 2 environmental reds (check:dev-prereqs, lint check:doc-security-posture) are the fresh worktree's unbuilt-packages precondition, not this diff — full workspace build + re-run in flight, result recorded in the report on the issue.

Out of scope, filed as #11661 (which remains open, not addressed here): five more renderer-honoured section keys (title, defaultCollapsed, icon, description, headerColor) are still undeclared; each needs its own declare-vs-converge judgement beyond this card's ruled three-key scope.


Generated by Claude Code

hideEmpty / collapsible / showBorder are honoured by objectui's
DetailSection (spread through by RecordDetailsRenderer) but were rejected
by the strict section schema, so 'objectstack validate' warned that the
one key able to keep a section's label skeleton on an all-empty record
'did nothing'. Declared optional with NO schema default (the maxVisible
principle); describe() texts carry the renderer defaults measured at the
.objectui-sha pin.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rxnd8cyFnoU8V5y21PaTsy
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rxnd8cyFnoU8V5y21PaTsy
@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
  • 1 name(s) were too generic to anchor anything (single lowercase words)

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

Which tree this was computed on

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

⚠️ 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:uisize/mteststooling

Projects

None yet

3 participants

@os-warren@os-zhuang@claude