Repository navigation
feat(plugin-kanban,types): honour columns[].collapsed on the registered board (objectui#9628) - #9721
Conversation
…ered board `KanbanColumn.collapsed` was declared on both published faces of the `object-kanban` arm and read only by `KanbanEnhanced`, a module no production source imports. An authored `collapsed: true` parsed green and reached a board that did nothing with it — `KanbanImpl`'s only collapse is the swimlane row's, held in viewer state and never keyed to a lane's declared value. The repair is at the reader, not at the declaration: retiring the key would narrow a published accept set and strand objectui#8801's tombstone, which names this key as where lane collapse lives. Honouring it moves no accept set — both faces already declare the member and `@objectstack/spec` declares the lane element as `z.unknown()`. A lane authored `collapsed: true` now renders narrowed to a title spine with its cards withheld, on both of the board's layouts, and its heading is a disclosure the viewer can open. The authored value is the initial state; the viewer's own toggle wins after that and survives the data refreshes that rebuild the lane objects. A board that authors the key nowhere renders the DOM it rendered before. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UanLVj6xvbS6puBCewLr8L
|
changeset-claim-re-read
|
✅ 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
|
|
ACCEPT — card objectui#9628, reviewed by the ⛔ Nothing below is taken from the PR body or the dev's report. ①
|
| file | changed lines | NON-COMMENT | what the non-comment lines are |
|---|---|---|---|
packages/types/src/complex.ts |
17 | 0 | — |
packages/types/src/objectql.ts |
32 | 0 | — |
packages/types/src/zod/complex.zod.ts |
2 | 2 | a .describe() STRING; collapsed: z.boolean().optional() is byte-identical on both sides |
packages/types/src/zod/objectql.zod.ts |
9 | 9 | one .describe() string and the allowCollapse refusal MESSAGE; the lane's collapsed: z.boolean().optional() is byte-identical |
⇒ no member, requiredness or accept set moves on any of the four faces. The two .zod.ts files read as non-comment because zod carries its prose as a string argument, ⭐ which is exactly why "0 non-comment lines" is the wrong instrument there and the patch had to be read.
② The rewritten refusal message still carries every clause objectui#8801's ruling required
This is the sharpest risk in the diff: the PR rewrites a published refusal message that a prior ruling constrained. Read on the head:
KanbanColumn.collapsed— present- 「not a board-level authored toggle」 — present
- 「Delete the key」 — present
⭐ And ⛔ the pin that asserts all three was not touched. object-kanban-allow-collapse-retired-8801.test.ts:146/:147/:148 asserts each clause by regex; that file is untouched by this diff and present on the head (lit control). ⇒ the message was rewritten to satisfy a pin that was not moved to fit it.
⭐ Exactly one test file is in this diff, and it is the new one. Not one existing pin was edited — including laneCountHonesty-8307, which the dev's own report says went red on its first cut. The repair for that was an early return in LaneHeading, ⛔ not a change to the pin. That is the distinction between fixing the code and fixing the instrument, and it went the right way.
③ What the message correction actually says, and why it is a ROTTED repair done right
The old text said lane collapse 「is the VIEWER's rather than the author's」 on the board this arm renders.
- LANE collapse becomes the author's, honoured, naming objectui#9628
- SWIMLANE collapse stays the viewer's alone,
KanbanImplperswimlaneField
The same repair lands in the pending .changeset/8801-… entry — which publishes verbatim — and names objectui#9628 「in this same release」, so the two entries in one CHANGELOG do not contradict each other. Frontmatter untouched: the patch is a single hunk at line 50, so nothing above it moved. ⭐ This is a ROTTED repair, not a BORN-FALSE one: the old sentence was true when written, and it is re-stated with its falsifier named rather than silently overwritten.
④ The new pin has controls, including the no-regression leg
Nine rows. The ones that make its zeros readings:
- 「CONTROL — an undeclared lane key is dropped rather than kept」 ⇒ the two "both faces accept
collapsed" rows are about this key, ⛔ not about a passthrough face - the collapsed lane's own authored
classNamereaching the reader ⇒ the inert key was ⛔ not a plumbing failure of that lane - a sibling lane's authored member painting ⇒ the board renders authored lane members at all
- the swimlane layout asserted to be the one on screen before its rows are read
- 「a board that authors the key nowhere renders no disclosure at all」 ⇒ the no-regression leg, which is what lets every pre-existing pin stay green
⑤ Gates — job conclusions, ⛔ not the aggregate
All 36 check runs on head 4e2236eaf5 complete: 0 pending, 0 non-green. Named: Type Check, Lint, Test (shard 1..4/4), Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Overwrite Report, Changeset Claim Re-read, Doc Component Type Check, Doc Snippet Type Check, Doc Example Id Check, Doc Fence Language Check, Governed Surface Queue Guard — each completed: success.
The minor on @object-ui/types for a prose-only change is what Changeset Bump Policy and Changeset Fixed Group Check both accept — every workspace package is in the fixed group, so the level is set by plugin-kanban, which earns it.
⑥ One correction to the report, and one design call worth naming
packages/types/src/views.ts. It is not: PR objectui#9720 touches no source file at all — one test file and two changesets. The intersection conclusion (empty) is right; the premise was wrong, and this seat re-derived the intersections rather than adopting them. ⭐ Recorded because this board has already spent a round on a wrong-surface reading (objectstack#18755).
collapsed: true, so an omitted key and an authored collapsed: false stay indistinguishable. Declared, deliberate, and the right call for a no-regression change — ⛔ but it means collapsed: false is still an unhonoured spelling. ⛔ Not filed: it is one sentence in a docblock away from being a stated limit rather than a defect, and the changeset already states it.
Implemented-by: claude/issue-9628-kanban-column-collapsed
Reviewed-by: session_01UanLVj6xvbS6puBCewLr8L
VERDICT: ACCEPT. Turning ready and enqueueing; card objectui#9628 closes on merge.
Generated by Claude Code
Fixes #9628
Clause-②: no
What this is
KanbanColumn.collapsedwas declared on both published faces of theobject-kanbanarm — the lane element ofObjectKanbanSchema(packages/types/src/objectql.tsand its Zod mirror) and the runtime laneKanbanColumn(complex.tsand its mirror) — and read byKanbanEnhancedalone, a module no production source imports. An authored lane{ "id": "todo", "title": "To Do", "collapsed": true }parsed green on both faces and reached a board that did nothing with it.This PR repairs it at the reader (dispatch direction 2). It does not retire the key and does not reopen the PASS verdict this card originates from.
Direction-2 feasibility, measured before any repair
bucketCardsIntoColumnsspreads...colon both legs,localizeColumnreturns{ ...col, title }, andKanbanRendererforwards the whole lane objects ascolumns={processedColumns}@objectstack/specObjectKanbanPropsSchema.columnsisz.array(z.unknown()).optional(), so the lane element is unconstrained at the protocol level (measured on the installed@objectstack/spec17.4.0 in this worktree)The reproduction, with its firing controls in the same run
packages/plugin-kanban/src/__tests__/columnCollapsedHonoured-9628.test.tsx, run against the unrepaired reader:The four that passed in that same run are the controls:
collapsed: trueand keep the value (two legs), while an undeclared sibling key is accepted and dropped — so acceptance is a reading about this key, not about a passthrough;classNameauthored on the same lane reaches the DOM, so the inertcollapsedis not a plumbing failure of that lane;limitauthored on a sibling lane paints theFullbadge, so the board renders authored lane members at all.What an authored
collapsed: truenow doesThe lane renders narrowed to a title spine with its cards withheld, on both of the board's layouts — the flat lane and the column cells of every swimlane row — and its heading becomes a disclosure the viewer can open. That is the meaning
KanbanEnhancedalready gave the key, so the platform states one behaviour for it rather than two.The authored value is the lane's initial state. The board holds the viewer's overrides in a map and resolves them against the current prop, rather than copying the authored flags into state:
columnsis rebuilt on every data round-trip, so a state seeded from it would re-collapse a lane the viewer had opened on every refetch (AGENTS.md #8), and nothing here depends on the identity of a memoised value (#10).A board that authors the key nowhere is untouched. The disclosure appears only on a lane that declared
collapsed: true, so an omitted key and an authoredcollapsed: falsestay indistinguishable. The one place this had to be defended by construction:LaneHeadingreturns the bare title where the caller already owns an element, because wrapping it in one morespanunconditionally handslaneCountHonesty-8307's count-badge locator the title instead. That was measured, not predicted — the first cut failed those four rows, and the fix is the!collapsibleearly return rather than a change to that pin.Prose this change falsified, and repaired
Three sentences asserted that on the board this arm renders, collapse is the viewer's and not the author's. That was true when objectui#8801 retired
allowCollapse; this PR makes it false, so it is corrected where it is stated rather than left to publish:allowCollapsetombstone docblock inobjectql.tsand the author-facing refusal message on the Zod mirror — both keep every clause that ruling required of them (KanbanColumn.collapsed, "not a board-level authored toggle", "Delete the key"), and both now steer the author to a key that works. Swimlane-row collapse is still stated as the viewer's alone, because it still is;.changeset/8801-object-kanban-allow-collapse-retired.md, whose body publishes verbatim into the CHANGELOG at the next release — the same release this change lands in. Its "what replaces it: nothing you author" paragraph would have contradicted the entry beside it.pnpm check:changeset-claimsis the instrument that put this in front of this seat.The docblocks and
describetext on the declaration itself, the plugin README andcontent/docs/plugins/plugin-kanban.mdxstop pointing authors at the unregistered board.Evidence
Every command was run from the repo root in this worktree, through the shared verify lock, with the exit code captured before any pipe.
pnpm exec vitest run packages/plugin-kanban/Test Files 57 passed (57),Tests 365 passed (365)pnpm exec vitest run packages/types/Test Files 202 passed (202),Tests 4698 passed (4698)turbo run type-check(both packages, dependency builds included)Tasks: 15 successful, 15 totalturbo run lint(both packages)Tasks: 3 successful, 3 total, 0 errorsnode scripts/check-changeset-presence.mjspnpm check:control-bytespnpm check:new-line-citationspnpm check:changeset-claimspnpm check:pending-changeset-literalspnpm check:doc-fences/check:doc-types/check:i18n-keysnode scripts/check-governed-queue-guard.mjs --test(the six paths)NOT GOVERNEDpnpm check:doc-snippetsAblation of the pin this PR adds
From the committed tree, one leg each way, on
packages/plugin-kanban/src/KanbanImpl.tsx:The three that turned red are the flat lane, the viewer toggle and the swimlane layout; the four that stayed green are the two face legs, the undeclared-key control and the "no disclosure where nothing is authored" row — so the pin fails for the reason it exists and its controls do not move with it. Restored with
git checkout HEAD --on that file's absolute path, proven by blob equality withHEAD, an emptygit diff HEAD, a cleangit statusand the marker at zero occurrences. No ablation artefact remains in the tree.An earlier attempt at the same leg is recorded rather than hidden: its
perl -0pireplacement terminated at the/inside the injected comment, so the anchor went to zero while the intended marker never landed. The reading was voided and the leg re-run through a Python replacement that asserts the anchor count first — the numbers above are from that run.Acceptance notes
packages/types/src/complex.ts,objectql.ts, their two Zod mirrors,plugin-kanban/src/KanbanImpl.tsx, the plugin README, one docs page and two.changeset/files. Intersection with objectui#7924 (views.ts), objectui#9375 (base.ts) and objectui#9712 (data.ts,useViewData.ts) is empty. Against the queued.changeset/PRs measured on their own file lists: objectui#9714 holds8478-describe-line-addresses.mdand8478-zod-pins-form-layout.md, objectui#9715 holds8499-node-slot-registered-arms.md— none of the three is the file edited here.KanbanEnhancedis untouched. It keeps its owncollapsedread and its two test importers (cardPredicateScope.test.tsx,kanban-handler-slots-7664.test.tsx). Whether it should be revived or deleted is objectui#8257's question, not this one's.KanbanEnhanced's own collapse toggle is an icon-onlyButtonwith no accessible name, so on that board the control is unnamed to a screen reader. The repair here does not copy that shape — its disclosure is named by the lane title. No PR or person is queued on that file today, so the carrier is: none.packages/types/src/complex.tsintroducesKanbanColumnas "a lane of the registered'kanban'renderer", a node key retired by objectui#8802. Stale framing in one sentence, not a defect in a declaration.Generated by Claude Code