Repository navigation
Commit 9cbe4db
Fixes #10485
Clause-②: yes
Declares `hideEmpty` on the `detail-section` node, following the
objectui#9529 ruling (comment `5724939941`) that the triage
(`5828292064`) reuses: declare a key the renderer already honours. The
registration in `packages/plugin-detail/src/index.tsx` gains `{ name:
'hideEmpty', type: 'boolean', description }`, and
`DETAIL_SECTION_NODE_INPUTS` gains `'hideEmpty'`, the same two edits PR
objectui#10484 made for `icon`. `DetailSection`'s hide logic is not
touched: it already reads `section.hideEmpty === true`.
## What changed
| File | Change |
|---|---|
| `packages/plugin-detail/src/index.tsx` | the `detail-section`
registration gains `hideEmpty` (boolean). Its description states the
node's omitted default. No `defaultValue`, since no other input of this
node carries one |
| `packages/plugin-detail/src/DetailSectionNode.tsx` |
`DETAIL_SECTION_NODE_INPUTS` gains `'hideEmpty'`, so the adapter folds
it into `section`. One comment phrase ("a ninth input") made count-free
|
|
`packages/plugin-detail/src/__tests__/detailSectionAuthoredHideEmpty-10485.test.tsx`
| the new pin, six rows |
| `.changeset/10485-detail-section-declares-hideempty.md` | `minor` on
`@object-ui/plugin-detail`. It names the one widened key and carries the
Clause-② line |
## The omitted default: one value for the node, so it is declared, not
escalated
The triage asked me to stop if the `record:details` / `detail-view`
difference turned out to be a real semantic split rather than a default
to declare. I measured all three paths on base `0d5d66aee`, each with a
sibling control section that must render (it rendered every time). The
node row comes from the new pin. The other two rows come from a
throwaway probe, which is not committed: `detail-view` through the real
`SchemaRenderer`, `record:details` through `RecordDetailsRenderer`.
| Surface | `hideEmpty` omitted | `true` | `false` |
|---|---|---|---|
| `detail-section` node, base | keeps heading + skeleton | keeps (warned
`unknown-prop`, dropped by the fold) | keeps (warned, dropped) |
| `detail-section` node, this PR | keeps | hides (no heading, no
skeleton) | keeps |
| `detail-view` sections | keeps | hides | keeps |
| `record:details` sections | hides (`hideEmpty: s.hideEmpty ?? true` on
its own authored sections) | hides | keeps |
`true` and `false` mean the same thing on all three surfaces. Only
`record:details` gives an omitted key a different meaning, and it does
that by applying a default to its own authored sections, which never go
through this node. On the node's own path nothing applies a default, and
`DetailSection` checks `=== true`, so an omitted key has one meaning
here: keep the section. That is exactly what `detail-view` does. The
description records that default, and nothing about the behaviour
changes.
## Premises, measured on base `0d5d66aee` before the edit
1. `DETAIL_SECTION_NODE_INPUTS` lives in `DetailSectionNode.tsx`, `icon`
is in it, and `hideEmpty` is not. **Held.**
2. The validator warns about an authored `hideEmpty`. I checked with a
manifest built the same way `page.tsx` builds it: the result was code
`unknown-prop`, message "DETAIL-SECTION has no prop hideEmpty" (the tag
name is spelled out to get past the body sanitizer). **Held, with one
correction to the mechanism.** The validator does not drop anything. It
only warns at authoring time. The drop happens in the fold:
`DetailSectionNode` sends an undeclared key to `hostProps` and not to
`section`, and `DetailSection` ignores that prop. So `hideEmpty: true`
on an all-empty section still drew its heading and skeleton (base: the
pin's `true` row is red with "expected span to be null").
3. Readers that enumerate node inputs need a new row, as PR
objectui#10484 did for `icon`. **Falsified: nothing to update, and PR
objectui#10484 did not update them either.** Each of the four console
readers (`registry-inputs-spec-parity`,
`ga-honoured-inputs-author-reach`, `public-contract`,
`component-input-union-specimens`) has 0 hits for `detail-section`, and
they stay green. `check:component-surface-parity` is report-only and has
no pin file. `check:sdui-registration-pins` pins registration KEYS
derived from the `sideEffects` array. The script never reads `inputs`,
and this diff adds no registration. The in-package readers stay green
without changes: the #8626 fold-parity row (sorted, both directions),
the #9529 pin and the #6955 pin.
4. Regeneration: none of `sdui.manifest.json`, `sdui-intrinsics.d.ts` or
`sdui-blocks.md` is checked in. `git ls-files` finds none of them; as a
control, the same listing does find `gen-manifest.ts`. `detail-section`
is also not in `public-blocks.ts` (0 hits), while `record:details` is (1
hit). **Nothing to regenerate.**
## The pin: `detailSectionAuthoredHideEmpty-10485.test.tsx`
| Row | Base `0d5d66aee` | Head `d0f0649d8` |
|---|---|---|
| no diagnostic for an authored boolean `hideEmpty`, in either polarity
| red: `unknown-prop` | green |
| judges `hideEmpty` as a declared BOOLEAN input (`'yes'` draws
`type-mismatch` naming `hideEmpty`) | red: `unknown-prop` | green |
| CONTROL: `name` still draws `unknown-prop` | green | green |
| `hideEmpty: true` hides an all-empty section. A sibling node,
identical except that it has no key, renders its heading and placeholder
in the same render | red | green |
| `hideEmpty: false` keeps the heading and the label skeleton | green |
green |
| an omitted `hideEmpty` keeps the all-empty section (the declared
default) | green | green |
Base: `Tests 3 failed | 3 passed (6)`. Head: `Tests 6 passed (6)`. The
last two rows are green on both sides on purpose. They pin the default
this PR declares, so a later change of that default shows up as a red
row and not as silence.
## Ablation: three legs, each proven on disk and restored by blob
Each mutation went through `node
../objectstack/scripts/ablation-replace.mjs --delete`. The anchor had to
hit exactly once, the blob had to change, and the restore is `git
checkout HEAD --` on an absolute path, verified by blob equality with
HEAD and an empty `git diff HEAD`. The wrapper has its own `trap ...
EXIT INT TERM`, which repeats that verification for both files. All legs
ran on the committed implementation `d0f0649d8`. There is no build
between mutation and assertion: the pin imports `../index` relatively,
and the root `vitest.config.mts` aliases `@object-ui/core`,
`@object-ui/react` and `@object-ui/sdui-parser` to their `src`. Expected
directions were written down before each run, and every leg matched
them.
| Leg | Mutation | Landed | Red | Stayed green | Restored |
|---|---|---|---|---|---|
| A | delete the `hideEmpty` entry from the registration's `inputs` |
anchor 1 to 0, blob `bd23f817f` to `04615591b` | both validate rows,
#8626 fold parity | the `name` control, all three render rows, the #9529
pin | blob `bd23f817f` equals HEAD, diff empty |
| B | delete `'hideEmpty'` from `DETAIL_SECTION_NODE_INPUTS` | anchor 1
to 0, blob `d41d873c3` to `637368d62` | the `true` render row, #8626
fold parity | both validate rows, the control, the `false` and omitted
rows | blob `d41d873c3` equals HEAD, diff empty |
| C (the dispatch's "warned and dropped") | both deletions together |
both as above | both validate rows, the `true` render row | the control,
#8626 fold parity (the two lists agree again), the `false` and omitted
rows | both blobs equal HEAD, diff empty |
Legs A and C read `Tests 3 failed | 15 passed (18)`. Leg B read `2
failed | 16 passed (18)`. All three ran over the #10485, #8626 and #9529
pins.
## Verification, at the final head `d0f0649d8`
| What | Command | Result |
|---|---|---|
| package + the four console readers | from the repo root: `pnpm exec
vitest run packages/plugin-detail/` plus the four
`apps/console/src/__tests__` readers | `Test Files 212 passed / 1
skipped (213)`, `Tests 2301 passed / 8 skipped (2309)`. The readers on
their own, verbose: `Test Files 4 passed (4)`, `Tests 240 passed (240)`,
each file named in the output |
| dependency closure | `pnpm exec turbo run build
--filter='@object-ui/plugin-detail^...' --concurrency=2` | `Tasks: 11
successful, 11 total` |
| types | `pnpm --filter @object-ui/plugin-detail type-check` | exit 0;
`tsc -p tsconfig.test.json --listFiles` lists the new pin (1 hit) |
| control bytes | `pnpm check:control-bytes` | `OK`, exit 0 |
| line citations | `pnpm check:new-line-citations` | `0 new citation(s),
enforcement report-only -> exit 0` |
| changeset | `node scripts/check-changeset-presence.mjs` | `3 source
file(s) of 1 released package(s) changed, and this change declares 1
changeset(s)` |
| no major | `node scripts/check-changeset-no-major.mjs` | `No changeset
declares a major bump` |
| changeset claims | `pnpm check:changeset-claims` | report-only, exit
0. It flags two pending changesets that name
`packages/plugin-detail/src/index.tsx`:
`9280-record-highlights-entry-icon-retired.md` (the `record:highlights`
`fields` description) and `record-alert-cta-label-i18n-4998.md`
(`record:alert`'s `action`). I read both paragraphs and both are still
true, because this diff touches neither registration |
| gates reading these files | `check:doc-types`,
`check:registry-bare-names`, `check:icon-record-names`,
`check:test-path-roots`, `check:pending-changeset-literals`,
`check:vi-mock-specifiers`, `check:handler-key-reads`,
`check:governed-queue-guard` | all exit 0. `--test` on the four paths
answers NOT GOVERNED |
| component-surface parity (report-only) | `pnpm
check:component-surface-parity` | exit 0. It lists
`detail-section.hideEmpty` as `input-outside-keyset`, next to eight
pre-existing `detail-section` inputs including `icon`, for the same
reason as those: the gate sees no named read through
`DetailSectionNode`, which folds by iterating the list |
| NOT MEASURED | `pnpm check:sdui-registration-pins` | exit 2, `No
console build to weigh` (it needs a full console build). Left to CI. It
reads registration keys, not `inputs` |
**Lint is narrowed, and I am declaring the narrowing.** `eslint
--no-inline-config --format json` over the three touched source files: 3
files linted, 0 errors. Per-rule counts on the two edited files are
equal to base (base read with `git show 0d5d66a:PATH` piped to `eslint
--stdin --stdin-filename PATH`): `DetailSectionNode.tsx` has 1
`react-refresh/only-export-components`; `index.tsx` has 29
`react-refresh/only-export-components` and 2
`@typescript-eslint/no-explicit-any`. The new pin has 0 messages. The
narrowing counts as a measurement because type-aware linting is not
enabled: `eslint --print-config` on `index.tsx` resolves an empty
`parserOptions` (no `project` / `projectService`), so this diff cannot
change the verdict on any file outside it. The repo-wide run belongs to
CI.
## Acceptance notes
- **The node's omitted default is stated, not reopened.** The #8603
design gave the node no default because it was "a section nobody could
write the key on". Now the key can be written on the node in both
polarities. Whether the node should therefore adopt `record:details`'
hide-by-default is a product question. This PR does not take it up: the
card forbids a behaviour change, and the spec's `describe()` is scoped
to `record:details`. The node matches `detail-view`, the other non-spec
surface.
- **Four texts outside this claim's file surface now describe the node
as unable to write the key.** Their conclusions still hold: the node
keeps its skeleton when the key is omitted, and `DetailSection` checks
`=== true`. Only their premise is out of date. I did not edit them,
because they are outside the claimed surface. Each one is quoted by
content:
- `DetailSection.tsx`, the comment above `hideAllEmptySection`: "the
`detail-section` node both synthesize one, and neither surface declares
`hideEmpty`". Fix: drop the node from that sentence, or say it declares
the key without a default.
- `renderers/record-details.tsx`, the comment on `hideEmpty: s.hideEmpty
?? true`: "Sections nobody can write it on stay out: the direct-`fields`
fallback body below and the `detail-section` node". Same fix.
- `renderers/__tests__/record-details.emptySectionDefault.test.tsx`, the
file header: "a section nobody could have written the key on — the
direct-`fields` fallback body, the `detail-section` node". Same fix.
- `packages/types/src/views.ts`, the `DetailViewSection.hideEmpty`
docblock: "This type is consumed by two authorable renderers". The node
is now a third one, and it behaves like `detail-view` (omitted keeps).
This is an incomplete list, not a wrong statement about behaviour, and
it ships in the `.d.ts`.
- **A pending changeset states an old count.**
`.changeset/8626-detail-section-authored-node.md` is unreleased and says
in present tense that the registration "declares eight FLAT inputs". PR
objectui#10484 already noted this when `icon` made the count nine, and
it is ten now. I did not edit it, because it is outside the claimed
surface.
- `packages/plugin-detail/README.md` and `content/docs` document no
input list for `detail-section`, so no doc line became false. I added
none, to stay inside the claimed surface.
Dispatched by the `domain:ui` seat #1; session
`https://claude.ai/code/session_01BA3nKVUwKQJf8DBxrSVtNC`.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01BA3nKVUwKQJf8DBxrSVtNC)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 1434bb4 commit 9cbe4db
8 files changed
Lines changed: 263 additions & 17 deletions
File tree
- .changeset
- packages/plugin-detail/src
- __tests__
- renderers
- __tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
270 | 270 | | |
271 | 271 | | |
272 | 272 | | |
273 | | - | |
274 | | - | |
275 | | - | |
276 | | - | |
277 | | - | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
278 | 280 | | |
279 | 281 | | |
280 | 282 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
83 | 83 | | |
84 | 84 | | |
85 | 85 | | |
86 | | - | |
| 86 | + | |
87 | 87 | | |
88 | 88 | | |
89 | 89 | | |
| |||
100 | 100 | | |
101 | 101 | | |
102 | 102 | | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
103 | 107 | | |
104 | 108 | | |
105 | 109 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
380 | 380 | | |
381 | 381 | | |
382 | 382 | | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
383 | 403 | | |
384 | 404 | | |
385 | 405 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
52 | 52 | | |
53 | 53 | | |
54 | 54 | | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
60 | 62 | | |
61 | 63 | | |
62 | 64 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
726 | 726 | | |
727 | 727 | | |
728 | 728 | | |
729 | | - | |
730 | | - | |
731 | | - | |
732 | | - | |
733 | | - | |
734 | | - | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
| 733 | + | |
| 734 | + | |
| 735 | + | |
| 736 | + | |
| 737 | + | |
735 | 738 | | |
736 | 739 | | |
737 | 740 | | |
| |||
0 commit comments