Repository navigation
finding(types,plugin-kanban): KanbanColumn.collapsed is declared on both faces and honoured only by KanbanEnhanced, which no production source imports — the key objectui#8801's tombstone points authors at #9628
Description
Activity
- addeddomain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec laneobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec laneand removed
on Sep 16, 2026 Claim:
Session:session_01UanLVj6xvbS6puBCewLr8L
Branch:claude/issue-9628-kanban-column-collapsed
Worktree:/home/user/wt/9628
Domain:domain:spec
Seat:domain:spec#1
File surface:packages/types/src/complex.tsand/orpackages/types/src/objectql.tswith their zod mirrors, plus theplugin-kanbanread site if the disposition needs it. A pin and a changeset as warranted.
Container & model: this container; dispatch seat servesclaude-opus-5, dev runs as anos-devsubagent in the same container.
Clause-②: no
Thread-read: read to the last page at the stamp below. The card carries no maintainer ruling; its origin is the closing paragraph of an isolated at-tier contract review of PR objectui#9621, whose PASS verdict this card adopts verbatim and ⛔ does not reopen.
Serial constraints cleared: intersected against every in-flight surface — objectui#7924 (packages/types/src/views.ts), objectui#9375 (packages/types/src/base.ts), objectui#9712 (packages/types/src/data.ts+packages/react/src/hooks/useViewData.ts), and the three queued PRs objectui#9714 / objectui#9715 (.changeset/) and objectui#9716 (scripts/). Intersection empty on every pair.⚠️ A sibling candidate was refused this slot on exactly this check. objectui#9549 was the higher-ranked pick until its surface was measured:ObjectTreeSchemalives inpackages/types/src/views.ts, which is objectui#7924's declared surface. ⛔ It waits rather than running concurrently — recorded so the next seat does not re-pick it while objectui#7924 is open.⛔ The stop condition, because this card has two directions with different costs
collapsedis declared on both faces and honoured by only one reader. That admits the same two repairs objectui#9701 faced:- Retire the key ⇒ narrows a published accept set ⇒ human floor, back to the maintainer.
- Honour it at the reader ⇒ the implementation catches up to a declaration both faces already make ⇒ no accept-set movement, no escalation.
⇒ The dispatch order sends the dev at 2, and makes its feasibility the first measurement. ⛔ If 2 is infeasible, the dev stops and hands back; the card then goes to the decision box as a narrowing question.
Clause-②: noabove is declared for direction 2 only.Read at 2026-09-17T19:11Z.
Generated by Claude Code
os-dev-report
{ "issue": 9628, "status": "done", "branch": "claude/issue-9628-kanban-column-collapsed", "pr": "https://github.com/objectstack-ai/objectui/pull/9721", "session": "session_01UanLVj6xvbS6puBCewLr8L", "head": "4e2236eaf", "base": "78a9c6744", "premise_still_valid": true, "direction": "2 — honoured at the reader. Feasibility measured FIRST and confirmed; no retirement, no accept-set movement, Clause-②: no unchanged.", "summary": "KanbanColumn.collapsed was declared on both published faces of the object-kanban arm and read only by KanbanEnhanced, which no production source imports. The registered board (KanbanImpl) now honours an authored columns[].collapsed on BOTH its layouts — the flat lane and the column cells of every swimlane row: the lane renders narrowed to a title spine with its cards withheld and its heading becomes a disclosure the viewer can open. The authored value is the INITIAL state; the board holds viewer overrides in a map and resolves them against the current prop, so a data refetch cannot re-collapse a lane the viewer opened. A board that authors the key nowhere renders the DOM it rendered before, which is why every existing pin stays green.", "direction_2_feasibility": { "plumbing": "CONFIRMED. bucketCardsIntoColumns spreads ...col on both legs; ObjectKanban's localizeColumn returns { ...col, title }; KanbanRenderer forwards whole lane objects as columns={processedColumns}. The authored member reaches KanbanImpl unchanged.", "accept_set": "UNMOVED in either direction. Both faces already declare the member, so nothing that parsed before parses differently; this is why the card did not need to go to the decision box.", "protocol": "NEITHER declares nor refuses it. @objectstack/spec 17.4.0 (installed in this worktree) declares ObjectKanbanPropsSchema.columns as z.array(z.unknown()).optional(), so the lane element is unconstrained at the protocol level. Honouring is protocol-compatible; this is the reading that separates this card from objectui#8801, where the spec's strictObject already refused the key by name.", "sibling_8801_read_not_assumed": "objectui#8801 went to letter A (retire) only through a director-seat class-1 ruling, on the ground that the spec refused allowCollapse by name and no registered board read it. Neither ground holds here: the spec is silent on the lane element, and the retirement's own tombstone NAMES this key as where lane collapse lives — retiring it would strand that text. 'The sibling was retired' is not a measurement, and it does not transfer." }, "reproduction": { "instrument": "packages/plugin-kanban/src/__tests__/columnCollapsedHonoured-9628.test.tsx, rendered through the real KanbanRenderer (the authored chain), run against the UNREPAIRED reader", "result": "exit 1 — Tests 2 failed | 4 passed (6)", "failing": "expected document not to contain the card 'Alpha' (the collapsed lane's cards were on screen); Unable to find an accessible element with role button and name 'To Do' (no disclosure existed)", "firing_controls_same_run": [ "both faces ACCEPT collapsed: true and keep the value — ObjectKanbanSchema lane element and KanbanColumnSchema, two legs, green", "an undeclared sibling key (collapsedd) is accepted and DROPPED — so the acceptance above is a reading about this key, not about a passthrough face", "className authored on the SAME lane reaches the DOM (toHaveClass) — the inert collapsed is not a plumbing failure of that lane", "limit authored on a sibling lane paints the Full badge — the board renders authored lane members at all" ] }, "tests": "All runs from the repo root of the worktree, through the shared verify lock, exit code captured to a variable before any pipe. (1) pnpm exec vitest run packages/plugin-kanban/ — VERDICT command-exit 0, Test Files 57 passed (57), Tests 365 passed (365). (2) pnpm exec vitest run packages/types/ — VERDICT command-exit 0, Test Files 202 passed (202), Tests 4698 passed (4698); re-run after the prose repairs, same result. (3) turbo run type-check --filter=@object-ui/plugin-kanban --filter=@object-ui/types — exit 0, 'Tasks: 15 successful, 15 total' (dependency builds included, so the .d.ts read is the rebuilt one). (4) turbo run lint, same two filters — exit 0, 'Tasks: 3 successful, 3 total', 0 errors (288 pre-existing no-explicit-any warnings). (5) node scripts/check-changeset-presence.mjs — exit 0, 5 published source files of 2 released packages, 1 changeset. (6) pnpm check:control-bytes — exit 0, 7818 tracked text files. (7) pnpm check:new-line-citations — exit 0, 0 new citations. (8) pnpm check:changeset-claims — exit 0 report-only, its finding answered (see prose_repairs). (9) pnpm check:pending-changeset-literals, check:doc-fences, check:doc-types, check:i18n-keys — all exit 0. (10) node scripts/check-governed-queue-guard.mjs --test on the six changed paths — exit 0, NOT GOVERNED. (11) pnpm check:doc-snippets — exit 2, which is its PREREQUISITE leg (it wants a scoped 34-package build), NOT MEASURED locally and declared to CI. ABLATION of the pin added, from the committed tree: anchor '?? col.collapsed === true,' 1 to 0, injected marker 0 to 1, bytes 64403 to 64400, mutated blob 3b3279af2 against HEAD blob 6ecfdd141 — ABLATED_TEST_EXIT=1, Tests 3 failed | 4 passed (7); the three reds are the flat lane, the viewer toggle and the swimlane layout, the four greens are the face legs, the undeclared-key control and the no-disclosure row. Restored with git checkout HEAD on that file's absolute path, proven by blob equality with HEAD, an empty git diff HEAD, a clean git status and the marker at zero occurrences. FIRST ATTEMPT VOIDED AND RECORDED: a perl -0pi replacement terminated at the slash inside the injected comment, so the anchor fell to zero while the intended marker never landed; the reading was discarded and the leg re-run through a Python replacement that asserts the anchor count first.", "files_touched": [ "packages/plugin-kanban/src/KanbanImpl.tsx — the reader: laneBox, LaneHeading, the override map, and the collapsed branch on both layouts", "packages/plugin-kanban/src/__tests__/columnCollapsedHonoured-9628.test.tsx — NEW, the reproduction turned pin (7 rows)", "packages/types/src/complex.ts — KanbanColumn.collapsed docblock (declaration unchanged)", "packages/types/src/zod/complex.zod.ts — its describe text", "packages/types/src/objectql.ts — the lane member's docblock, and the allowCollapse tombstone paragraph this change falsified", "packages/types/src/zod/objectql.zod.ts — the lane member's describe text, and the allowCollapse refusal MESSAGE this change falsified", "packages/plugin-kanban/README.md — the lane member row", "content/docs/plugins/plugin-kanban.mdx — the lane interface comment and the Lane Properties row", ".changeset/9628-kanban-column-collapsed-honoured.md — NEW, minor on plugin-kanban and types", ".changeset/8801-object-kanban-allow-collapse-retired.md — one paragraph, see prose_repairs" ], "prose_repairs": "Three sentences asserted that on the board this arm renders, collapse is the VIEWER's and not the author's. True when objectui#8801 landed, false as of this PR, so each was corrected where it is stated rather than left to publish false: the tombstone docblock in objectql.ts; the author-facing refusal MESSAGE on the zod mirror (every clause the 8801 ruling required of that text is preserved — KanbanColumn.collapsed, 'not a board-level authored toggle', 'Delete the key' — and the pin object-kanban-allow-collapse-retired-8801.test.ts, which asserts all three, is green); and the PENDING changeset .changeset/8801-object-kanban-allow-collapse-retired.md, whose body publishes verbatim into the CHANGELOG at the next release, the same release this lands in. pnpm check:changeset-claims is the instrument that put that third one in front of this seat. Serial check before touching it: objectui#9714 holds 8478-describe-line-addresses.md and 8478-zod-pins-form-layout.md, objectui#9715 holds 8499-node-slot-registered-arms.md — measured on their own file lists, none is this file.", "serial_constraints": "HELD, and measured rather than assumed. Intersection with objectui#7924 (packages/types/src/views.ts), objectui#9375 (base.ts) and objectui#9712 (data.ts, react/src/hooks/useViewData.ts) is EMPTY — none of those files is touched. The .changeset/ surface is shared with objectui#9714 and objectui#9715 at directory level and DISJOINT at file level (their file lists read from the API, above). Nothing here reached another seat's work, so no stop was triggered.", "labels": "NONE written. Clause-②: no, so needs:contract-review is not owed; the dispatch order named no other label for this PR and this seat does not invent one. The PR carries whatever the repo's own labelers apply.", "mcp_calls": "0 — no MCP GitHub tool was called, read or write. Every GitHub interaction went through the REST proxy with curl.", "api_writes": "2 — POST /repos/objectstack-ai/objectui/pulls (the draft PR) and POST /repos/objectstack-ai/objectui/issues/9628/comments (this report). Plus two git pushes: the empty branch as the write-route probe, and the one commit.", "open_questions": [], "out_of_scope_findings": [ "noted, not filed: KanbanEnhanced's own column-collapse toggle is an icon-only Button with no accessible name, so on that board the control is unnamed to a screen reader. The disclosure added here does not copy that shape (its name is the lane title). Carrier: none — no PR or person is queued on that file today, and whether KanbanEnhanced is revived or deleted is objectui#8257's question.", "noted, not filed: packages/types/src/complex.ts still introduces KanbanColumn as 'a lane of the registered kanban renderer', a node key objectui#8802 retired. Stale framing in one sentence of a docblock, not a defect in any declaration. Carrier: the next seat editing that interface.", "measured, not a finding: the card's correction that KanbanEnhanced is imported by two test files (cardPredicateScope.test.tsx, kanban-handler-slots-7664.test.tsx) is accurate on this tree and both suites are untouched by this PR — recorded because a seat told 'imported by nothing' could delete the module and break them." ] }
Generated by Claude Code
LANDED — PR objectui#9721 merged; verified on
origin/mainby symbol, read at 2026-09-17T20:19Z.⛔ Not read from
merged/merged_at.BEFORE=bbba09840,AFTER=dea17b469,--is-ancestorexit0; landing commitdea17b469 feat(plugin-kanban,types): honour columns[].collapsed on the registered board (objectui#9628) (#9721).The published refusal message — the riskiest thing in this diff
probe ( zod/objectql.zod.ts)before after it is the VIEWER's rather— the clause this PR falsified1 0 SWIMLANE collapse stays the VIEWER's— its replacement0 1 write it on the lane you want collapsed0 1 objectui#8801 required: KanbanColumn.collapsed1 1 objectui#8801 required: not a board-level authored toggle1 1 objectui#8801 required: Delete the key1 1 [LIT CTRL] there is no board-level replacement to rename it to1 1 [ABSENT CTRL] zzqNoSuchClause0 0 ⇒ all three clauses objectui#8801's ruling requires of that message survive the rewrite, and the one sentence this PR made false is gone and replaced by the distinction it was missing: lane collapse is the author's; swimlane collapse stays the viewer's.
The rest
probe file before after narrowed to a title spine with its cards withheldzod/objectql.zod.ts0 1 narrowed to a title spine with its cards withheldzod/complex.zod.ts0 1 honoured by the enhanced boardzod/complex.zod.ts1 0 col.collapsed === trueKanbanImpl.tsx0 2 — both layouts [LIT CTRL] collapsed: z.boolean().optional()both mirrors 1 1 ⭐ The last row is the one that certifies
Clause-②: noat the landing rather than at review time: the declaration is byte-steady on both mirrors while everything around it moved. No accept set went anywhere.⚠️ Two instrument faults, recorded rather than quietly re-drawnThe first draw returned 0 / 0 on
not a board-level authored toggle,SWIMLANE collapse stays the VIEWER'sandit is the VIEWER's rather— three phrases I had already read in the diff. ⛔ Zeros were not reported. Both faults were in the instrument:- JS string-concatenation glue. That message is built from
'…' +segments across lines, so a phrase spanning two segments carries' + 'through its middle and no literal match can see it. Joining lines is not enough; the glue has to go too. - ⭐
sed 's/[’‘]/x/g'is byte-oriented. A bracket class containing multibyte characters matches individual BYTES, so it mangles the text instead of normalising it — and the damage is silent. The file's apostrophe is U+2019 (342 200 231, confirmed byod -c); replacing the explicit byte sequence\xe2\x80\x99works where the class does not.
⇒ every reading in this comment is from the third draw, with a lit control inside the same message firing on both sides and an absent control at 0. ⭐ Two dark probes on one instrument were treated as a broken instrument, ⛔ never as absence — which is what they would have been read as, and the conclusion would have been that objectui#8801's clauses had been dropped.
What this card delivered
The key was declared on both published faces and read only by
KanbanEnhanced, which no production source imports — ADR-0049's declared-but-unhonoured shape. The repair is at the reader: retiring it would have narrowed a published accept set and stranded objectui#8801's tombstone, which namesKanbanColumn.collapsedas where lane collapse lives.⭐ And exactly one test file is in the diff — the new one. No existing pin was weakened,
laneCountHonesty-8307included, which went red on the implementer's first cut and was answered inLaneHeadingrather than in the pin.Dispatch state cleared in the same round:
pm:dispatchedremoved, assignee released. The card is closedcompletedby the merge.
Generated by Claude Code
- JS string-concatenation glue. That message is built from
- added a commit that references this issue
on Sep 28, 2026
Filed by the
domain:spec@ objectui execution seat (sessionsession_01VCpmqvacV4BypY48QdoxcE). Named by the isolated at-tier contract review of PR objectui#9621 (record: PR comment5699636620, VERDICT PASS), whose closing paragraph says of this key: "Outside this card and not held against this PR:columns[].collapsedon this arm is itself declared-but-unhonoured and warrants its own card." ⛔ That verdict is adopted verbatim and this card does not reopen it.⭐ This is the same shape, one member over, from the key objectui#8801 just retired — and it is the key that retirement's tombstone names as the migration target.
Measured by this seat on the tree at
163630bc9, ⛔ not quoted from the reviewpackages/types/src/complex.ts:151—collapsed?: boolean, docblock: "Whether the lane renders collapsed. Honoured byKanbanEnhanced"packages/types/src/objectql.ts:3680— same member, docblock "honoured by the enhanced board"column.collapsedpackages/plugin-kanban/src/KanbanEnhanced.tsxonly (:263, :273, :275, :277, :302, :313)KanbanImpl.tsx:579collapses a swimlane from viewer state underobjectui:kanban-collapsed:+swimlaneField. ⛔ It reads no lane's declaredcollapsed.The review says
KanbanEnhanced.tsx"is imported by nothing". Not quite — it is imported by two test files:packages/plugin-kanban/src/cardPredicateScope.test.tsx:22(a real import, used at :57 / :74 / :85 / :89)packages/plugin-kanban/src/__tests__/kanban-handler-slots-7664.test.tsx:145(avi.mockof the module path)Firing control, same walk:
ObjectKanbanis imported by production sources (app-shell/.../block-config.ts,i18n.ts, and more). ⇒ The accurate statement is "imported by no production source, only by two tests", and the zero is a real reading, not a dead query.⛔ This matters for whoever executes: a dev told "imported by nothing" may delete the file and break two live test suites.
The defect
An author who writes this on an
object-kanbannode:{ "type": "object-kanban", "columns": [{ "id": "todo", "title": "To Do", "collapsed": true }] }parses green on both faces, and gets nothing — the board that would have honoured it is registered nowhere. Same ADR-0049 enforce-or-remove shape the whole
ObjectKanbanarm has been worked through.⭐ And it is now load-bearing in a second way: objectui#8801's retirement tombstone (landing in PR objectui#9621) names
KanbanColumn.collapsedas where lane collapse lives. The review checked this and confirmed the tombstone does not strand the author — it also states that on this board collapse is the viewer's and prescribes deletion. But the ruling's required clause, taken alone, would have pointed at this dead key. ⇒ The platform is one careless docblock away from teaching a fiction.What is NOT measured here
columns[].collapsedtoday. This seat did not censuscontent/docs/**,examples/**,apps/**. That census is owed before any disposition, exactly as objectui#9263's ruling required for its own key.KanbanEnhancedshould be revived, deleted, or left as-is. That is a product question (objectui#8257 retired thekanban-enhancednode key), ⛔ not this seat's to decide.The ask
Measure first, then declare-or-remove. If the measurement leaves a genuine product question — is an authored, per-column collapse meant to exist on this platform at all? — file the four-facet block and move it to the decision inbox rather than picking a letter here.
pm:queue, unassigned. ⛔ No serial constraint today: PR objectui#9621 touches only theObjectKanbanSchemaregion and is already clear of contract review.session_号,账号hotlong只是会话被发到的 TOKEN,⛔ 不是作者。本班收尾那次check-half-states扫描点了本卡(同批还点了 #9641 / #9642),故由填卡席本人原地补入该号,补写于 2026-09-17T06:25Z;⛔ 正文其余部分一字未改,编辑历史 GitHub 自留。Generated by Claude Code