Skip to content

feat(spec): declare defaultCollapsed / icon / description on record:details sections; withhold title (alias fork) and headerColor (dead-in-practice) - #11902

Merged
os-warren merged 2 commits into
mainfrom
claude/issue-11661-record-details-five-keys
Aug 25, 2026
Merged

os-warren merged 2 commits into
mainfrom
claude/issue-11661-record-details-five-keys

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

Fixes #11661

Declares three more record:details section keys the renderer has honoured all along — defaultCollapsed, icon, description — under the inherited #11289 ruling (declare what the renderer honours; renderer unchanged), following the landed #11662 pattern: optional, NO schema defaults, renderer-derived-default prose in describe(). The other two keys the card names are deliberately not declared, each with its own measured disposition (below). Clause-②: yes (widens the accepted authoring set on a published face).

Step 0 — re-measurement on current origin/main (387e231) at the .objectui-sha pin (190fbd01)

The #11662 and #11743 landings absorbed none of the five: the section shape on origin/main declares name / label / columns / fields / hideEmpty / collapsible / showBorder only. All five read points re-derived at the pin:

Key Read point (objectui packages/plugin-detail, pin 190fbd01) Measured type Renderer default Disposition
defaultCollapsed src/DetailSection.tsx:139useState(section.defaultCollapsed ?? false); the collapse state is only consulted on the collapsible branch (:509 renders without it) boolean false (expanded) Declared
icon src/DetailSection.tsx:516/546section.icon gates a SectionIcon element with name={section.icon}; SectionIcon (:50) renders a LazyIcon for ASCII-identifier names (regex: caret, [a-z0-9], then [a-z0-9-]*, case-insensitive), literal text otherwise string (lucide name; text fallback) none Declared
description src/DetailSection.tsx:520-521/557-558 — rendered raw as a JSX child (no pickLocalized, unlike the heading) string (plain, untranslated) none Declared
title src/renderers/record-details.tsx:190s.title ?? s.label, then pickLocalized + sectionLabel i18n, identically to label I18nLabel (alias of label) Withheld — fork reported
headerColor src/DetailSection.tsx:513/542cn(..., section.headerColor && `bg-${section.headerColor}`) string Withheld — dead-in-practice; objectui finding filed

title (alias fork, reported for ruling): the renderer's title limb is a strict-priority second spelling of the heading slot label already declares, with byte-identical localization handling. Zero producers author it (showcase pages, sys_user platform page, and objectui demos all author label). This is the page:card body-vs-children shape, which #5775 CONVERGED (retire one spelling) rather than declared — and since title was never declared, "converge" here is purely an objectui-side edit (drop the ?? -limb) with nothing to migrate. Recommendation in the report: converge, do not declare. Held for the maintainer since any accept-set disposition on this published face is a ruling.

headerColor (dead-in-practice, not declared): Tailwind v4 (^4.3.3) with @source scanning and no safelist — objectui's own sdui-preview-page-source-styling.test.ts states "a utility class authored in real page metadata produces no CSS". The template-literal call site generates no CSS; an authored value works only when another source file uses the identical complete class literal (bg- plus the authored value). Zero producers at the pin. Declaring it would be the capability-illusion shape, so it stays refused; filed as objectstack-ai/objectui#6178 (finding, unassigned) with the two fix directions.

Reverse verification (fix committed first; restore via git restore --source, tree-only, no staging)

The subject resolves in-package (component.test.ts imports ./component.zod relative — no dist/ in the path), so the legs need no rebuild; the mutation was confirmed on disk by an anchored grep (the injected describe() text), 1 → 0 → 1 across mutate/restore, with porcelain showing a lone unstaged M and then clean.

Verification

All local, at head c5b4307 (working tree clean since that commit; every run below post-dates it except where noted — the fix itself was committed as f710739 and the four changeset-diff gates were additionally re-run at c5b4307):

  • pnpm --filter @objectstack/spec test: 424 passed (424) files / 11259 passed (11259) tests (run on the fixed tree at bytes identical to f710739); targeted re-run at head green (211 passed (211)).
  • pnpm --filter @objectstack/spec typecheck: exit 0 — "check:test-typecheck: OK — @objectstack/spec's test layer compiles under packages/spec/tsconfig.test.json".
  • pnpm --filter @objectstack/spec check:generated: "✓ All 14 generated artifacts are up to date." — exactly 2 proved stale on first run (gen:docs wrote content/docs/references/ui/component.mdx, riding this PR; gen:api-surface regenerated byte-identical after a rebuild — its staleness was the reverse-verification leg's restored-src mtime vs dist, not a surface change).
  • pnpm --filter @objectstack/lint test (downstream consumer of spec — the component-props gate consumes this map): 81 passed (81) files / 2294 passed (2294) tests.
  • @objectstack/example-showcase (downstream consumer): typecheck exit 0; test: 26 passed (26) files / 362 passed (362) tests.
  • Cross-package .d.ts probe from packages/lint (proves consumers read the REBUILT dist declarations): RED leg — one error, TS2322 on defaultCollapsed: 'yes'; GREEN leg — 0 errors with the three keys authored correctly. Probe file deleted.
  • node scripts/pm/dispatch-gates.mjs at final head, derivation line: "dispatch-gates: gate list derived from the tree of 'objectstack-ai/objectstack' at commit c5b4307 (/home/user/objectstack-11661)." — change set: the 4 committed paths. Every derived family was run locally and exited 0 with its own verdict line, including the docs families the regenerated reference page newly pulled in (doc-anchors, doc-authoring, doc-security-posture, docs-audit-scope, docs-redirects, published-readme-links, quick-reference-counts, react-page-adapter-contract, role-word, doc-frontmatter, section-landing-index), the spec liveness family (empty-state, liveness, strictness-ledger, variant-docs), the changeset family (adr-0087-registration: "adds no declared-breaking changeset", no-major, empty-changeset, objectui-changeset, gate-self-tests), the test-file convention family (query-options-erasure, type-check-coverage, type-check-debt "32 ledger entries re-measured … none above its recorded number", engine-double-contract, where-matcher, cross-package-test-inputs), and merge-driver / published-files / slot-lookup / spec-parsed-alias / test-source-alias / type-source-resolution / dev-prereqs / plugin-teardown-shape / doc-formula-expressions / docs-audit x2 / release-rehearsal self-test / nul-bytes.
  • Changeset: @objectstack/spec minor, non-breaking (no ADR-0087 marker owed — verified by the gate above).

Out of scope

objectstack-ai/objectui#6178 remains open (finding); #11661's title fork is reported in the os-dev report's open_questions, not resolved here.

Generated by Claude Code


Generated by Claude Code

claude added 2 commits August 24, 2026 22:45
…etails sections

Three more section keys the renderer honours and the strict shape refused
(#11661, inheriting the #11289 ruling: declare what the renderer honours;
renderer unchanged). Optional, no schema defaults — the describe() texts
name the renderer-derived defaults, measured at the .objectui-sha pin
(190fbd01: DetailSection.tsx:139/516/546/520/557).

The same measurement's title and headerColor stay deliberately refused,
pinned by test: title is a second spelling of the declared heading slot
(alias fork held for a ruling); headerColor only reaches the DOM as a
template-literal Tailwind class that generates no CSS (dead-in-practice,
filed as objectui#6178).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rxnd8cyFnoU8V5y21PaTsy
…keys

check:generated proved exactly two artifacts stale; gen:docs wrote this page,
gen:api-surface regenerated byte-identical (the staleness was the reverse-
verification leg's restored src mtime vs dist, not a surface change).

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, 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
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • 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 50369dc07d7948793c5d3680bd2242610ae2cccdpackageMentionDocs.

Which tree this was computed on

This run read content/docs from 1ddb12726694105f9ab0395d8710749038412869 — the merge of head c5b430701abe60b21c9200a94ecab5b1e5ac4228 into base 50369dc07d7948793c5d3680bd2242610ae2cccd, 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 1ddb12726694105f9ab0395d8710749038412869 && git checkout 1ddb12726694105f9ab0395d8710749038412869
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 50369dc07d7948793c5d3680bd2242610ae2cccd c5b430701abe60b21c9200a94ecab5b1e5ac4228 && git checkout -B drift-repro 50369dc07d7948793c5d3680bd2242610ae2cccd && git merge --no-ff c5b430701abe60b21c9200a94ecab5b1e5ac4228

node scripts/docs-audit/affected-docs.mjs --json 50369dc07d7948793c5d3680bd2242610ae2cccd

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

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

Labels

documentation Improvements or additions to documentation protocol:ui size/m tests tooling

Projects

None yet

2 participants