…t's divider
`SimpleObjectForm`'s grouped branch — the layout a section-carrying
`object-form` gets when it declares no `formType` — rebuilds each section key
by key into a virtual `section-divider` row. The rebuild copied `label`, the
ADR-0089 predicate, the membership claim and the collapse pair, and not
`description`, so the key was dropped on the path an author reaches by
default while `SectionDivider` (the component that row renders as) has always
drawn a blurb and the `tabbed` / `wizard` / `split` / `modal` rebuilds copied
one. The sibling `label` on the same member arrived, which is what made the
miss invisible.
The `if (label)` gate is deliberately not widened with it: that gate also
decides the predicate row and the membership claim, so a member carrying a
`description` and neither `name` nor `label` still draws no divider. Pinned
as behaviour beside the fix.
`objectFormSectionMembers-8071` row 6 pinned the drop; it is rewritten onto
the new behaviour in this same change, reading the blurb off the divider
itself rather than off the form's `textContent`, and a new row pins the
boundary above. The member-pin ledger entry that described the old row in
prose is corrected with it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UanLVj6xvbS6puBCewLr8L
Fixes objectui#9779
Clause-②: no
The key is already declared on both sides (
ObjectFormSection.descriptionin this repo's own@object-ui/types,FormSection.descriptionin@objectstack/spec) and is already rendered by other layout arms, andSectionDividerhas always drawn one. Making the default layout read it relaxes no accepted set and enlarges no public surface. Re-declared here by me, as the seat asked; I reached the same reading it did, on the declarations above rather than on its say-so.What was wrong
SimpleObjectForm's grouped branch — the layout a section-carryingobject-formgets when it declares noformType— rebuilds every section key by key into a virtualsection-dividerrow. That rebuild copiedlabel, the ADR-0089 predicate, the objectui#6236 membership claim and the collapse pair, and notdescription. So the blurb never reached the divider, while the siblinglabelon the same member did — which is what made the miss invisible to an author.One key copied onto that push. Nothing else in the renderer moved.
My own measurement of the arms — the card's table is NOT what I measured
label+description) through the realObjectFormfor every arm, plusDrawerFormmounted directly, and readdocument.body.textContent. Probe deleted after measuring; it is not in this diff.formType) /simpleSimpleObjectForm'ssection-dividerpush — the fixtabbedTabbedForm'stabGroups, out asfieldTabs[].description, rendered by the form renderer's field-tabs group headerwizardFormSectionContainer's owndescriptionpropsplitpaneFields' divider push, gated `section.labelmodaldrawerDrawerForm's own divider push drops it again⇒ it was two arms missing the key, not one.
DrawerFormis deliberately untouched here: the seat's dispatch says not to widen into the other arms,DrawerForm's divider push is a separate site rather than a shared path, and an arm the card asserts is healthy turning out not to be is a finding for the seat to file, not something to fold into a one-arm repair. Handed back in my report.The pin moved WITH the fix, and it is shown failing
objectFormSectionMembers-8071's sixth row pinned the drop as behaviour (objectui#8071 slice 11 wrote pins only and handed the drop back as a finding). It is rewritten in this same change onto the new behaviour, with its comment saying what it used to pin and which card changed it. It was ⛔ not weakened and ⛔ not deleted: it is still the only assertion watching this key, and it now reads the blurb off the divider row itself rather than off the form's wholetextContent— an arrival assertion againsttextContentwould be satisfied by the string rendering anywhere at all.Ablation, run from the committed state so both legs are byte-provable, with a
traprestoring on any exit:83a8fbd13c44626aeb5097b97847d41ab3ea1b9b101ec9230ec6b175c4fd1318a9cfda82fbdfd164, different from HEAD's.Tests 1 failed | 6 passed (7), and the one red is exactly row 6 —AssertionError: … expected [] to deeply equal [ 'Totals as invoiced' ]. Every other row, the boundary row included, stayed green.git checkout HEAD --on the file (⛔ not a baregit checkout --, which restores from the polluted index). Restored blob83a8fbd13c44626aeb5097b97847d41ab3ea1b9b— equal to HEAD's —git diff HEADempty, anchor count back to 1, and the pin green again atTests 7 passed (7).No build was needed for either leg and none was used:
*.test.tsxruns under the root vitest projects, whose alias map redirects every@object-ui/*specifier to that package'ssrc, so the mutation was read directly. (The dist-pin family is the deliberate exception, and this file is not one of them.)What else asserts the old behaviour — swept, two hits, both moved
packages/plugin-formsuite at the fixed tree —Test Files 96 passed (96),Tests 920 passed | 1 skipped (921). Nothing else in the package went red, so nothing else in it was asserting the drop.descriptionwith drop/never-reaches/silently wording: exactly two carriers. Row 6 itself, and the member-pin ledger entry forobject-form.sectionsinregistry-inputs-spec-parity, whosepinsprose spelled out the LIMIT in words. The ledger prose is not compared mechanically (only the pin file's existence and whether it names the block and the key are), so it would have gone on describing a behaviour that no longer exists — commandment 完善设计器的每一个细节 #9's failure mode. Corrected in the same change; that test still passes,Tests 199 passed (199).plugin-formthat mount anobject-formand mentiondescriptionwere read individually: all of them are about a FIELD's description or a form-level one, none asserts a section blurb's absence.⇒ ⛔ row 6 was not the only carrier, but it was the only assertion.
Changeset grade — measured from a build, not assumed
A changeset is owed and it is a real bump, ⛔ not an empty-frontmatter declaration.
@object-ui/plugin-formpatch.Measured on the corpus that package's
filesarray actually publishes (distplus the three markdown/licence entries), built on this branch after building the dependency closure:SimpleObjectFormdivider push indist/index.js— the one carryingcolSpan: 4, the collapse pair and theonToggleclosure — now carriesdescription: r.description. The source spelling does not survive minification, so the property on the compiled push is the marker, ⛔ not a grep for the source line.section-divideris present indist/index.js,dist/index.umd.cjsand two.d.tsfiles — the corpus is one this grep can see into.Totals as invoicedappears nowhere indist— the grep can return zero, so the hit above is not a grep that hits everything.descriptionproperty at all — so the bundler is not inventing the key, and the property on the fixed site got there from the source change.The boundary this PR deliberately did NOT move
The divider row exists only for a member that yields a heading — a
nameor alabel— which is the "untitled trailing bucket renders flat" rule the file's own comment states and row 3 pins. A member carrying adescriptionand neither of those therefore still draws no divider and still drops its blurb, whereassplit/modal/wizard/tabbedrender one for it.I did not widen that gate, because it decides more than this key: it also gates the ADR-0089 predicate row and the objectui#6236 membership claim, and the collapse pair hangs off the same condition — so a headingless section that gained a divider would newly have its predicate gate its whole group, and a
collapsed: trueon it would newly take its fields out of the DOM. Those are rulings about other keys. The shape is pinned as behaviour in a new row beside row 6 — the same way row 6 recorded the drop — and handed back as a finding.Tests
pnpm exec vitest run packages/plugin-form/src/__tests__/objectFormSectionMembers-8071.test.tsx—Tests 7 passed (7).pnpm exec vitest run packages/plugin-form/—Test Files 96 passed (96),Tests 920 passed | 1 skipped (921).pnpm exec vitest run apps/console/src/__tests__/registry-inputs-spec-parity.test.ts—Tests 199 passed (199).pnpm --filter @object-ui/plugin-form type-check— exit 0; it runstsc --noEmitANDtsc -p tsconfig.test.json, so the rewritten pin is type-checked too. The copied key needs no cast:ObjectFormSection.descriptionis declared, so a rename would be caught by the compiler.pnpm --filter '@object-ui/plugin-form^...' buildthenpnpm --filter @object-ui/plugin-form build— both exit 0 (the closure first; the package cannot typecheck its own build against unbuilt dependencies).check:changeset-presence(red before the changeset, green after),check:control-bytes,check:new-line-citations(0 new),check:changeset-claims,check:pending-changeset-literals— all exit 0, each captured to a file BEFORE any pipe.eslint --no-inline-config --format jsonover the three changed files: 0 errors. The 58 + 2 warnings it reports are pre-existingno-explicit-anyhits in helpers this change does not touch; the diff adds none.VERDICT command-exitline, ⛔ never a bare exit status read through a pipe.Acceptance notes
packages/plugin-form/src. Re-taken, ⛔ not inherited from the claim comment.apps/console, for the reason in the sweep section. It is one string literal inside a test; no published behaviour of@object-ui/consolechanges.Refs, not the closing keyword, on purpose: the card's own repro and its judgement are discharged, but the card states the drop in general terms and the headingless shape above still drops the key. The seat owns whether that shape is this card or a new one; ⛔ I did not close the card from here, and ⛔ I filed nothing.drawerarm drops the same key atDrawerForm's own divider push (reproducible, measured above); the headingless-section boundary; and the card's own arm table, which says five arms render the key when four do.🤖 Generated with Claude Code
https://claude.ai/code/session_01UanLVj6xvbS6puBCewLr8L
Generated by Claude Code
Generated by Claude Code