feat(spec): declare defaultCollapsed / icon / description on record:details sections; withhold title (alias fork) and headerColor (dead-in-practice) - #11902
Conversation
…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
📓 Docs Drift Check1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not 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
Coarse fallback — 126 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # 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 |
Fixes #11661
Declares three more
record:detailssection 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 indescribe(). 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-shapin (190fbd01)The #11662 and #11743 landings absorbed none of the five: the section shape on
origin/maindeclaresname / label / columns / fields / hideEmpty / collapsible / showBorderonly. All five read points re-derived at the pin:packages/plugin-detail, pin190fbd01)defaultCollapsedsrc/DetailSection.tsx:139—useState(section.defaultCollapsed ?? false); the collapse state is only consulted on thecollapsiblebranch (:509renders without it)false(expanded)iconsrc/DetailSection.tsx:516/546—section.icongates aSectionIconelement withname={section.icon};SectionIcon(:50) renders aLazyIconfor ASCII-identifier names (regex: caret,[a-z0-9], then[a-z0-9-]*, case-insensitive), literal text otherwisedescriptionsrc/DetailSection.tsx:520-521/557-558— rendered raw as a JSX child (nopickLocalized, unlike the heading)titlesrc/renderers/record-details.tsx:190—s.title ?? s.label, thenpickLocalized+sectionLabeli18n, identically tolabellabel)headerColorsrc/DetailSection.tsx:513/542—cn(..., section.headerColor && `bg-${section.headerColor}`)title(alias fork, reported for ruling): the renderer'stitlelimb is a strict-priority second spelling of the heading slotlabelalready declares, with byte-identical localization handling. Zero producers author it (showcase pages,sys_userplatform page, and objectui demos all authorlabel). This is thepage:cardbody-vs-childrenshape, which #5775 CONVERGED (retire one spelling) rather than declared — and sincetitlewas 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@sourcescanning and no safelist — objectui's ownsdui-preview-page-source-styling.test.tsstates "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.tsimports./component.zodrelative — nodist/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 unstagedMand then clean.origin/main, new tests kept): exit 1 —3 failed | 208 passed (211). Red exactly the three pins that require the declaration: preserves the record:details sections: five more renderer-honoured keys remain undeclared by the strict section schema (title / defaultCollapsed / icon / description / headerColor) #11661 section keys verbatim, rejects wrongly-typed values (an undeclared key rejects as unrecognized, notinvalid_type), and still refuses unknown section keys after the record:details sections: five more renderer-honoured keys remain undeclared by the strict section schema (title / defaultCollapsed / icon / description / headerColor) #11661 widening (the arrow suggestiondefaultCollapse→defaultCollapsedrequires the declared key). Green as predicted: does not materialize (absence is byte-identical to today) and still refusestitle/headerColor(refused both sides — they pin the withholding, not the fix).211 passed (211); full spec suite424 passed (424)files /11259 passed (11259)tests.defaultCollapsedin the schema file still counts 2 atorigin/main— those belong torecord:chatter's own already-declared key, which is why the mutation marker is the anchored injected text, not the token.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 asf710739and the four changeset-diff gates were additionally re-run atc5b4307):pnpm --filter @objectstack/spec test:424 passed (424)files /11259 passed (11259)tests (run on the fixed tree at bytes identical tof710739); 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:docswrotecontent/docs/references/ui/component.mdx, riding this PR;gen:api-surfaceregenerated 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):typecheckexit 0;test:26 passed (26)files /362 passed (362)tests..d.tsprobe frompackages/lint(proves consumers read the REBUILT dist declarations): RED leg — one error,TS2322ondefaultCollapsed: 'yes'; GREEN leg — 0 errors with the three keys authored correctly. Probe file deleted.node scripts/pm/dispatch-gates.mjsat 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.@objectstack/specminor, non-breaking (no ADR-0087 marker owed — verified by the gate above).Out of scope
objectstack-ai/objectui#6178 remains open (
finding); #11661'stitlefork is reported in the os-dev report'sopen_questions, not resolved here.Generated by Claude Code
Generated by Claude Code