feat(types)!: retire sections[].collapsed from RecordDetailsComponentProps (objectui#8583) - #8636
Conversation
…ntProps (objectui#8583) `@objectstack/spec` `RecordDetailsProps.sections[]` refuses `collapsed` by name (`unrecognized_keys`) and declares `defaultCollapsed` for that state — the spelling `DetailSection` has always read. So this type declared the one collapse key an author could write in TypeScript and the contract rejects, while the key the contract accepts did not compile until item 1 landed it. Retired outright, no transition window: director seat decision batch #101 applies standing ruling batch #87 (this repo does not declare a key the spec refuses by name) and the 2026-08-27 retirement-pacing ruling. Zero readers and zero writers of this member in the repository, re-measured on this head with controls, so the removal is rename-free rather than a migration. The pin is reached by the leg that TYPE-CHECKS. `vitest` strips types and this package's build project excludes tests, so `tsc -p tsconfig.test.json` — the third leg of `type-check` — is the only invocation that reads the pin file. Its `@ts-expect-error` directives are the assertion: re-declare the member and they go unused (TS2578). The one in-repo writer the ruling's census reported as zero was real — `p1-spec-alignment.test.ts` wrote `collapsed: true` on a `RecordDetailsComponentProps` literal — and is renamed to the surviving spelling. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
…ngeset The changeset closed with "the six keys this type used to omit landed in the previous release." Item 1's changeset, `.changeset/8583-record-details-section-members.md`, is still PENDING on this PR's own base `c4326fe` (`git show` on it exits 0), and the `17.6.0` section of `packages/types/CHANGELOG.md` carries zero occurrences of `8583` — control: 17 occurrences of `objectui#` in that same section, so the zero is a reading. Both items ship in the SAME release. A changeset is this repository's input to release notes, so a wrong sentence there is wrong in the release notes, read by people who were never in the thread. Prose only: the `**BREAKING**` banner and the FROM/TO stay byte-identical, and no other file moves. Found by the ceiling-tier contract review of PR #8636 (defect 2), verified here rather than taken on report. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
Contract review (
|
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract review (
|
Fixes #8583
Item 2 of the card, executed on the director seat's ruling
5588930745(decision batch #101, option A), which applies standing ruling batch #87 (this repo does not declare a key@objectstack/specrefuses by name) and the 2026-08-27 retirement-pacing ruling 「项目在创业阶段,用户也很少,短期不考虑渐进。」. Item 1 — the six keys this type used to omit — landed in #8601.What changed
RecordDetailsComponentProps.sections[]no longer declarescollapsed.defaultCollapsedis the surviving spelling: it is what@objectstack/specRecordDetailsProps.sections[]declares, and whatDetailSectionhas always read (DetailSection.tsx:142). Retired outright, no transition window and no "refused upstream" annotation (option B, refused by the ruling).packages/types/src/record-components.ts-1line: thecollapsed?: boolean;declarationpackages/types/src/__tests__/record-details-section-members-8583.test.tspackages/types/src/__tests__/p1-spec-alignment.test.tscollapsed: truerenamed todefaultCollapsed: trueon itsRecordDetailsComponentPropsfixture.changeset/retire-record-details-section-collapsed.md@object-ui/typesminor, BREAKING banner, FROM/TO:120and:100)". Re-located on this head, and cross-read against the census SHA9f5de79dand against154fe2a(the merge base of #8601): line:120is right, butcollapsedhas never had a docblock at any of those three points — line:100isdefaultCollapsed's docblock, added by item 1, which this ruling explicitly keeps. Deleting it would have hit the one thing the ruling forbids ("no other member touched"), so it was not deleted. The deletion is one line.The pin is reached by the leg that TYPE-CHECKS
The card's own lesson:
viteststrips types, and this package's build project excludes tests, so a pin that runs only under vitest measures nothing. The instrument istsc -p packages/types/tsconfig.test.json— the third leg of@object-ui/types'stype-checkscript. Proven, not assumed:tsc --listFileson that project emits 614 files and the pin file is one of them (packages/types/src/__tests__/record-details-section-members-8583.test.ts, andp1-spec-alignment.test.tsalongside it).Four legs, run from the committed implementation, each with on-disk mutation proof (anchor grep counts plus a
git hash-objectblob comparison against the HEAD blob) and restored withgit checkout HEAD -- pathverified by an emptygit diff HEAD:tscexit0@ts-expect-errorabovecollapsed: true2error TS2353: Object literal may only specify known properties, and 'collapsed' does not exist in type '{ name?: …; showBorder?: …; defaultCollapsed?: …; headerColor?: … }'— the error names the key, and the member set it prints carriesdefaultCollapsedand notcollapsedcollapsed?: boolean;to the interface, directive still stripped2grep -c TS2353= 0). The only error left isTS2578: Unused '@ts-expect-error' directiveon the type-level pin at:1622TS2578twice, at:162and:184— both item-2 directives go unused, i.e. re-declaring the member makes this file red rather than quietly passingLeg 1 vs leg 2 is the direction proof the ruling asked for: with the member deleted the
collapsedliteral produces a realtscerror naming the key, and re-adding the member makes exactly that error disappear. Legs 3 shows the committed pin's own failure mode.The runtime half is a two-control read on the INSTALLED spec, not a restatement:
defaultCollapsedis in the section object's key list andcollapsedis not (the control that makes the zero a reading), and the canonical section pluscollapseddrawsunrecognized_keysnaming exactlycollapsed— while the same canonical section parses clean in control A above, so the refusal is attributable to the name.The census, re-run on this head with controls — and one item of it FALSIFIED
git ls-files -- 'packages/*/src'returns 0 files ('packages/*/src/*'returns 3954;-- packages apps examplesreturns 5110), because a git pathspec has to match a FILE path and*already spans/. In that form the control could not have fired, so "exit 1, zero lines" was not a reading. ⛔ The evidence is not deleted and the conclusion is not restated without a measurement under it — the commands below are the ones actually run on this head, with their real output. The finding is unchanged: no reader or writer of this member survives in this repository.1. The decisive reading — nobody who knows the type reads the key.
Five files know the type at all; not one of them reads
.collapsed.record-details.tsxis the only non-test importer — it declares its schema as the props type intersected with an open string-keyed record, so an undeclared key would still reach it at runtime, and it never mentions the key.2.
.collapsedproperty reads tree-wide — 39 hits, every one on a different type.app-shell/src/console/ai/__tests__/useCollapsibleChatsList.test.tsx(12)plugin-kanban/src/KanbanEnhanced.tsx(6)plugin-list/src/ObjectGallery.tsx(3),plugin-grid/src/useGroupedData.ts(2),plugin-grid/src/ObjectGrid.tsx,plugin-grid/src/__tests__/groupingNullEntry-7217.test.tsx,components/src/custom/grouping-editor.tsxplugin-form/src/ObjectForm.tsx(3),plugin-form/src/DrawerForm.tsx(3),components/src/renderers/form/form.tsx,app-shell/src/views/metadata-admin/SchemaForm.tsx,plugin-designer/src/__tests__/__mocks__/plugin-form.tsx,apps/console/src/components/FormPage.tsxapp-shell/src/views/studio-design/ObjectGroupInspector.tsxcomponents/src/renderers/layout/containers.tsxdata-objectstack/README.md⭐ The load-bearing row is the one that is empty:
packages/plugin-detail/**, the only renderer ofrecord:details, contributes zero of the 39 (grep -c plugin-detailover that output = 0, exit 1). Its collapse state is read asdefaultCollapsed—DetailSection.tsx:142,SectionGroup.tsx:50, both inside the control's 11.3. JSON writers — a true zero, with a live control on the same pathspec.
The control's four hits are
content/docs/components/disclosure/meta.json, twoexamples/schema-catalogschemas andpackages/components/shadcn-components.json— so the zero above is a reading, not a broken search.4. Literal
collapsed:KEY writes tree-wide — 90 sites, all accounted for.Only three of the 90 sit anywhere near
record:details, and each was opened:packages/types/src/__tests__/p1-spec-alignment.test.ts:293is anObjectFormSection;packages/plugin-detail/src/synth/__tests__/buildDefaultPageSchema.test.ts:973is afieldGroupsINPUT entry, whose converter emitsdefaultCollapsedon the way out (buildDefaultPageSchema.ts:722);content/docs/plugins/plugin-detail.mdx:156is English prose ("collapsible and default-collapsed:"), not a key — that doc'srecord:detailsschema block writesdefaultCollapsed. Two more are this PR's own pin literals. The remaining 85 are grouping fields, kanban lanes and columns, form sections, wizard steps, accordion items, sidebar state, the chats-list hook, and gate scripts' own fixtures.⛔ And the one census line of the ruling that is FALSE. The ruling reported "zero
collapsed:in any record-details test".packages/types/src/__tests__/p1-spec-alignment.test.ts:501wrote{ label: 'Address', fields: [...], collapsed: true }insideconst props: RecordDetailsComponentProps = {at:496, andtsconfig.test.jsoncompiles that file. Left alone, the deletion turns the package'stype-checkred on the first push. Renamed todefaultCollapsed— fixture triage of the "it used the retired spelling" kind, not a fixture the pin replaced: the test's own assertions arecolumns,sections.lengthandlayout, none of which reads the key.@object-ui/typesthat wrotecollapsedis not observable from here — it gets TS2353 naming the key, which is why the changeset spells out the FROM/TO.Gates run locally, exit code captured before any pipe
pnpm --filter @object-ui/types type-check(baseline, before any edit)0pnpm --filter @object-ui/types type-check(after)0pnpm --filter @object-ui/types build(tsc+check-dist-completeness)0pnpm --filter '@object-ui/plugin-detail^...' build(consumer closure)0pnpm --filter @object-ui/plugin-detail type-check(the one non-test consumer)0pnpm exec vitest runon the two changed test files (repo root, no--)0— 2 files, 45 testsnode scripts/check-changeset-presence.mjs0node scripts/check-changeset-no-major.mjs0node scripts/check-changeset-fixed.mjs0node scripts/check-control-bytes.mjs0node scripts/check-type-check-coverage.mjs0node scripts/check-governed-queue-guard.mjs --teston the four paths0— NOT GOVERNEDpnpm exec eslinton the three changed sources0(2 pre-existingno-explicit-anywarnings, neither on a changed line)plugin-detail type-checkread2on its first run —Cannot find module '@object-ui/core', an unbuilt dependency closure, i.e. NOT MEASURED rather than red. It reads0after the closure build; that row is the second run.Not measured locally, left to CI: the repo-wide
pnpm testandpnpm lint,pnpm check, the docs-snippet and doc-type gates, and every othercheck:*inlint.yml.Correction round after the ceiling-tier contract review (verdict: PASS)
The review at
5590429735passed all five axes, reproduced the four ablation legs with matching results, and confirmed both refusals this PR made against the ruling. Two of its three defects are fixed here; the third was declined by the seat.git show c4326fe:.changeset/8583-record-details-section-members.mdexits 0 — item 1's changeset is still pending on this PR's own base — and the17.6.0section ofpackages/types/CHANGELOG.mdcontains zero occurrences of8583(control: 17 occurrences ofobjectui#in the same section, so the zero is a reading). Both items ship in the same release. A changeset is this repository's input to release notes, so a wrong sentence there is wrong in the release notes. ⛔ Nothing else in that file moved: the**BREAKING**banner and the FROM/TO are byte-identical.check-changeset-overwritegreen.验收备注
hideEmptystays out, per the objectui#7129 ruling — not touched here.collapsiblestays; only the initial-state key moved.record-components.tsis a single deleted line.packages/types/src/zod/**orpackages/types/package.jsonwas touched (RecordDetailsComponentPropshas no zod mirror;git grep collapsed packages/types/src/zod/hits only navigation, complex and views, none of them this member), andzod-mirror-parity.test.tsis untouched — the hot-file fence held.collapsible?: booleanis now the only member of this entry with no docblock, while its six siblings each carry one. Cosmetic, inside the file this PR edits; the seat that next documents this entry is the carrier. Filing it would be a documentation nit, which is not one of the three fileable classes.Clause-②: yes— the accept set of a published type face narrows.needs:contract-reviewis hung on this PR as well as on the card (双载体); ⛔ only the PM clears it, on a transcript-verifiedCONTRACT_REVIEW_TIERPASS.Drafted by the
domain:spec@ objectui dev seat in sessionsession_01Jmxdo7bmeqCQHLSfmLVX9w; the branch isclaude/issue-8583-retire-sections-collapsed.Generated by Claude Code
Generated by Claude Code