Uh oh!
There was an error while loading. Please reload this page.
fix(types): rename KanbanColumn.items to cards on both published halves - #7541
Conversation
`KanbanColumn` declared its card list as `items` in `complex.ts` and in the zod mirror `complex.zod.ts`, while every board reads `cards`. Measured on `origin/main` 78a3cc2: `KanbanImpl.tsx` reads `.cards` on 12 lines, `KanbanEnhanced.tsx` on 8, and `bucketCardsIntoColumns` twice more as `col.cards || []`; `.items` has zero read sites in either board, against a same-shaped `.title` control returning 8 and 3 on the same two files. The result was `declared !== enforced` on a published surface: both kanban catalog entries failed `safeValidateSchema` with ": Invalid input" while rendering perfectly — the type's seat in objectui#6318's bucket. Renaming the read sites instead was rejected by measurement, not preference: under the `items` spelling `col.cards || []` buckets every column to zero, and `basic-kanban-board` goes from 64 elements reading "To Do2 / Design new feature" to 45 elements reading "No cards / 3 columns / To Do0" — an empty board. The declaration was the wrong side. Pinned in `examples/schema-catalog/test/kanban-column-cards-6939.test.tsx`: both entries validate, the `items` spelling is now refused, the rendered DOM is byte-identical to the pre-change measurement (elements, tag census, visible text, SHA-256 of textContent), and the empty-board reading for `items` is pinned so the direction of the rename stays a measurement. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
✅ 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
|
…s false mechanism note The `reports a registered component type the bundled schemas do not model` case used a kanban board authoring `cards`, and its comment claimed `AnyComponentSchema` "has no member for" kanban. That claim was false, and was false before this branch touched anything: `ComplexSchema` has carried `KanbanSchema` throughout. The board landed in the bucket for a different reason — `KanbanColumnSchema` required `items` while every board, and that sample, writes `cards`. Renaming the mirror to `cards` made the board validate, so the case measured 0 candidates: the defect had been frozen into an assertion of expected behaviour. Repointed to `abbr`, chosen by measurement rather than memory. On this tree `KNOWN_SCHEMA_TYPES` carries 658 registered types while `AnyComponentSchema` declares 102 distinct literal `type` values, leaving 558 with no member; the probe was controlled in both directions (`kanban` / `text` / `tree-view` read as modelled, a nonsense type does not). Raw HTML primitives are the stable inhabitants of that bucket — registered in bulk by `html-elements.tsx`, never candidates for authorable component schemas — so the fixture does not carry a timer the way a plugin-ish type such as `dashboard-grid` would. The case also now asserts its own precondition, so if `abbr` is ever modelled this file reports why instead of a bare `expected +0 to be 1`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
✅ 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
|
✅ 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
|
os-project-manager
commented
Sep 3, 2026
合约审查裁断:FAIL 三条 → 补丁轮已全修 → 放行
转录档位核验:只提取 harness 生成的 三条 finding,逐条F1 — 本 diff 让一个受 CI 把关的测试变红,而 PR 的验证人群不含它所在的包。 F2 — clause 2 的叙述指错了物件,并漏掉了唯一直接相关的在先裁决。 F3(非阻塞)— 读点补记少了一处。 审查者核实通过的部分clause-2 的结构测量正确(公共 NARROWINGS(收口后由审查者自行转为实测的两项)被锁住的宽跑事后返回: 派发席处置:放行三条全修,逐条按内容核过:
合约面自被审 head 起未动(这是不重跑审查的依据): 自被审 head 起本分支只有一个自有提交 ⛔ clause 2 仍未实现,且仍在维护者手上本次放行的是 clause 1(
Generated by Claude Code |
os-project-manager
commented
Sep 3, 2026
CI 已在当前 head 转绿;放行动作剩两步,被 GitHub 配额挡住
CI 序列,按 |
Uh oh!
There was an error while loading. Please reload this page.
Part of #6939 — the
kanbanrow (group 3) only. Groups 4 (filter-builder) and 5 (chart) are not addressed here and remain open; group 2 (tree-view) is PR #7533.Clause-②: yes — this moves a published mirror's accept/reject behaviour AND renames a member on a published type. Draft +
needs:contract-review; not marked ready, no auto-merge.What changed
KanbanColumndeclared its card list asitemsin both halves of the published surface —packages/types/src/complex.tsand the zod mirrorpackages/types/src/zod/complex.zod.ts— while every board readscards. Renamed tocards.The ruling's own numbers, re-measured on this branch's base
The maintainer ruling (comment 5510084784) took its counts on
origin/mainec0a7b84. This branch is based on78a3cc238. All three reproduce:78a3cc238plugin-kanban/src/types.tsdeclarescards: KanbanCard[]complex.zod.tsdeclaresitems.cardsread lines inKanbanImpl.tsx.cardsread lines inKanbanEnhanced.tsx.itemsread lines in eitherControl for the two zeros: a same-shaped probe (
grep -c '\.title') on the same two files returns 8 and 3, so the zeros are readings and not a mis-shaped probe. A nonsense-member probe of the same shape returns 0 on both.Addendum the ruling did not count:
packages/plugin-kanban/src/index.tsxreadscol.cardsthree more times — lines 64 and 89 inbucketCardsIntoColumns, and line 327 in thekanban-enhancedregistration'suseMemo, which has the same shape. Each iscol.cards || [], and that|| []is the mechanism behind the discriminator below. (An earlier revision said "twice more (lines 64 and 89)", missing line 327; corrected on review.)Why the rename went toward
cards— measured, not assertedPer #6318's triage, a "correction" that renders identically proves the schema was the wrong side. This row clears the stronger, opposite-polarity bar: "correcting" the fixtures toward the declaration does not merely change the render, it empties the board. Measured through the harness in the new test file, on
78a3cc238, before either declaration was touched:cards(authored)items(the declared spelling)basic-kanban-boardTo Do2 … Design new feature …No cards3 columnsTo Do0In Progress0Done0advanced-kanban-with-badges-and-limitsBacklog2 … User Authentication …No cards4 columnsBacklog0 …Validator verdicts, same run: the authored
cardsdocuments failedsafeValidateSchema(: Invalid input) and theitemsdocuments passed. After this change, exactly reversed.What is pinned
examples/schema-catalog/test/kanban-column-cards-6939.test.tsx(11 tests):itemsspelling is now refused (the other half of the accept-set move — a mirror that merely widened would still accept it);cardsis a real declaration, not a passthrough hole — counter-probes use only the declared key, sinceBaseSchemais.passthrough()and an unknown key would prove nothing;textContent;itemsempty-board reading is itself pinned, so the direction of the rename stays a measurement.Controls are legal in both states. The render carriers never pass through the validator, and
bucketCardsIntoColumnsis untouched, so both the populated and the empty board render the same before and after. ThewaitForwitness is a column heading, which exists in both boards — it cannot settle for one spelling and time out for the other.Ablation (predicted before measured)
Restoring
itemsto both declarations, renderer untouched. Predicted red: the twovalidatescases, the twoitems-refused cases, and the passthrough counter-probe — 5 of 11, with the six render/anti-vacuity cases and all 12zod-mirror-paritycases staying green.Measured: exactly that —
Tests 5 failed | 18 passed (23), and the five names match the prediction one-for-one.The mutation was proven on disk before the run (anchored counts in both directions, plus blob hashes differing from HEAD), and the restore was proven by state, not by exit code: both worktree blob hashes equal their HEAD blobs,
git diff HEADempty,git status --porcelainempty. Both files resolve through the repo's vitest source alias (packages/types/src/...), so nodistrebuild participates in this ablation.Notes
packages/types/examples/zod-validation-example.tsauthored the old spelling in three places; renamed. It is covered bypackages/types'type-check(which runstsc -p tsconfig.examples.json).examples/schema-catalog/test/safe-validate-corpus-6318.test.tslisted this row as an open finding on the Zod union; that sentence is now stale and the kanban clause is removed. That file deliberately pins no bucket size, so nothing else there moves.@object-ui/plugin-kanbanis unchanged — it already declaredcards.@object-ui/plugin-kanban/KanbanImpl. That shape was removed: it is not in the package'sexportsmap and resolves only through the repo's vitest alias, which@object-ui/fieldsdeep subpaths resolve only through the repo vitest alias — itsexportsmap publishes none of them #4325 ruled out for@object-ui/fields. The test now settles the boundary withwaitFor, the waycatalog-gallery-render.test.tsxalready does for these same entries.Changeset
minor, notpatch— this is a rename on a published type, not a widening.@object-ui/typesonly. (majoris forbidden repo-wide byscripts/check-changeset-no-major.mjs; the breaking semantics are spelled out in the changeset body per repo convention.)Verification — all at merged head
fba1bfd78vitest run packages/types/ packages/plugin-kanban/ examples/schema-catalog/Test Files 140 passed (140),Tests 3727 passed (3727)zod-mirror-parity.test.ts(run explicitly)Tests 12 passed (12)turbo run type-check --filter=@object-ui/types --filter=@object-ui/plugin-kanbanTasks: 15 successful, 15 totalturbo run type-check --filter=@object-ui/example-schema-catalogTasks: 30 successful, 30 totaleslint . --format jsoncheck-changeset-presence.mjscheck-changeset-no-major.mjscheck-control-bytes.mjsBranch merged with
origin/main47547d01a(a merge, not a rebase, per AGENTS.md); the full suite above was re-run after the merge.Clause 2 of the ruling, and a PRIOR ruling on this exact pair
The 2026-09-02 ruling has two clauses. This PR implements clause 1 (the rename). Clause 2 — "
packages/plugin-kanban/src/types.tsstops carrying its ownKanbanColumndeclaration and re-uses the public one" — is NOT implemented here, and two things about it belong on the record. Neither asks this PR to decide anything.1. The public declaration is not structurally sufficient. Measured: the plugin's local
KanbanCardcarries four members the public@object-ui/typesKanbanCarddoes not declare —badges,cardSubtitle,cardFieldCells,coverImage— plus an index signature; the plugin'sKanbanColumncarriesclassNamewhere the public one hascolor. All are live reads (card.coverImage,card.cardFieldCells,card.cardSubtitle,card.badges,column.className, andcard[swimlaneField]atKanbanImpl.tsx449, 672 and 692). Deleting the local declaration would typecolumn.cardsas the publicKanbanCard[]and break every one of them, so clause 2 cannot be executed without widening the published type — which the dispatch explicitly forbids doing silently.2. Correction to an earlier revision of this PR, and the prior ruling it missed.
An earlier revision cited the comment at
KanbanImpl.tsx:52as "a landed decision pointing the other way", saying objectui#6172 / #6155 made./typesthe authority because the local card carried members the public one did not. That attribution was wrong. The comment comparesKanbanImpl.tsx's own former copies against the plugin's./types— an in-package drift — and says so in as many words: "the localKanbanCardcarriedcardSubtitle/cardFieldCells/coverImagethat./typesdid not". It says nothing about@object-ui/types.That card was explicit about not touching the cross-package question. Its changeset (
.changeset/6172-markdown-kanban-one-authority.md, landed inb3d562c02):So
:52is in-package convergence plus an explicit escalation, not a contrary decision.And that escalation was answered. objectui#6172 comment
5474954379(2026-08-31, maintainer verbatim 「其他同意」, director-seat batch #14) rules option A:Under that ruling the public
KanbanColumnis renamed away, so clause 2's "re-use the public one" would have nothing to re-use. It predates the 2026-09-02 ruling by two days, the two do not cite each other, and it is not yet executed (packages/types/src/complex.ts:28still declaresKanbanColumn). It also lists retiring the@object-ui/typestriplet outright as escalation option D — under which this PR's member rename would travel with the renamed type, or disappear with a retired one.⛔ I am not adjudicating which ruling governs. Both are recorded so the reviewer and the maintainer can see the conflict; the dispatching seat has escalated it as-is. Worth noting that #6172's own census agrees with this PR's measurement independently: it found the types-dialect
itemsspelling had zero non-demo, non-known-bad uses across the bound.Follow-up: CI red on
fba1bfd78, and the fix (a3fb15e5e)Test (shard 3/4)went red on one case —packages/cli/src/__tests__/check-validity-recogniser.test.ts, "reports a registered component type the bundled schemas do not model",expected +0 to be 1. It is a correct consequence of this repair, and it is fixed here rather than worked around.That case wrote a kanban board authoring
cardsand assertedobjectui checkwould report it as a registered type the bundled schemas cannot model. It only ever landed in that bucket because the mirror requireditems— the exact fork this PR repairs. Once the board validates, the count drops to 0. The defect was frozen into an assertion of expected behaviour.Reproduced end-to-end through the real
check, not inferred: a kanban board withcardsnow yieldscandidates=[]; the siblingtextfixture still yields a candidate (positive control); a foreignpackage.jsonstill yieldsSkipped 1and no candidate (negative control).The comment was also wrong, independently of this PR. It claimed
AnyComponentSchema"has no member for" kanban.ComplexSchemahas carriedKanbanSchemathroughout (complex.zod.ts:691-692, reached fromAnyComponentSchemavia theComplexSchemaarm). Corrected to the real mechanism — a required-key mismatch, not an absent member.The replacement type was chosen by measurement. Difference set, by introspecting every literal
typethe union declares: 658 registered types inKNOWN_SCHEMA_TYPES, 102 modelled, 558 with no member. Probe controlled in both directions —kanban,textandtree-viewall read as modelled (so it is not blind to real members), a nonsense type does not.Picked
abbr. Raw HTML primitives are the stable inhabitants of that bucket:packages/components/src/renderers/basic/html-elements.tsxregisters them in bulk and they are not candidates for authorable component schemas — unlike a plugin-ish type such asdashboard-grid, which also lands there today but is a plausible future modelling target. The fixture is a well-formedabbrdocument, which keeps the case's original point: the file is unmodelled, not broken.The timer is defused rather than passed on. The case now asserts its own precondition — that
abbris still refused bysafeValidateSchema— with a message naming what to do. If anyone ever modelsabbr, this file says why it went red instead of reporting a bareexpected +0 to be 1. A synthetic always-unmodelled type was considered and rejected: the registered-type universe is a generated, separately-pinned file (packages/cli/src/utils/known-schema-types.ts), so a synthetic type would have to be injected by mocking it — and that would fabricate the "registered" half, which is precisely the half this bucket is about.⛔ The test was not skipped, disabled, isolated or deleted, and the sample was not reverted to
items. The changeset level is unchanged (minor).Re-verified at merged head
a3fb15e5e(mergedorigin/mainf0f774b0d)vitest run packages/cli/ packages/types/ packages/plugin-kanban/ examples/schema-catalog/Test Files 156 passed (156),Tests 3973 passed (3973)check-validity-recogniser.test.tsaloneTests 10 passed (10)turbo run type-check --filter=@object-ui/cliTasks: 10 successful, 10 totaleslinton the changed filecheck-changeset-presence.mjscheck-changeset-no-major.mjscheck-control-bytes.mjs🤖 Generated with Claude Code
https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
Generated by Claude Code