feat(types,plugin-kanban): execute the batch #70 ruling on the kanban arm - #8799
Conversation
…n` arm Retire `allowCollapse` / `cardTemplates` / `columnWidths` (declared on both faces, read by no registered board) and `titleField` (the legacy spelling of `cardTitle`; the `object-kanban` arm keeps its own) as named refusals on both faces. Declare `navigation`, which `ObjectKanban` reads and no face named, on the gantt precedent objectui#5903 — the `(schema as any)` cast goes. Move `objectFields` off the schema bag onto `KanbanRendererProps` as a real React prop: an internal channel from the one caller that fetched the object definition, not an authoring surface. Fixes #7742 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
✅ 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
|
…ectFields channel The batch #70 ruling moves the `kanban` arm's accept set in both directions in one change, so a file that pinned only one half would pass on a change that forgot the other. Every refusal is asserted at its own path with an undeclared key on the same parse as the control (a `.passthrough()` object accepts unknown keys, so "the document failed" proves nothing alone), `navigation` is asserted to JUDGE its value rather than merely admit it, and the sibling `object-kanban` arm is asserted to still accept `titleField`. The `objectFields` pin discriminates the two channels behaviourally through an outcome only the field catalogue can produce, with the prop leg asserted first as the firing control for the schema leg's negative. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
Contract review at
|
✅ 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
|
…ban-arm-ruling-batch70
…`kanban` arm The move takes `objectFields` off the schema READ PATH, and on the ruled `kanban` arm that closes the key outright — `ObjectKanbanRenderer` serves that type key and discards its rest-spread (`void _props;`). It does NOT make the key author-unreachable in general: `objectFields` is absent from `SchemaRenderer`'s stripped-metadata list, so on the schema-only `kanban-ui` entry an authored value survives the generic prop spread and lands on the very prop this change introduced. Measured through the real `SchemaRenderer` — a `kanban-ui` node with an authored `objectFields` paints the card; the same node without it, and the same document as `type: "kanban"`, do not. Three texts read as if the general claim held, and the changeset is the one that ships: a changeset is this PR's input to the release notes, so a measured-false claim there becomes a release note. All three now state the scope at the arm level rather than dropping the claim: - .changeset/7742-kanban-arm-batch70.md - the KanbanRendererProps docblock in packages/plugin-kanban/src/index.tsx - the pin file's docblock, and the pin file's NAME Renamed objectFieldsIsNotAuthorable-7742.test.tsx to objectFieldsIsAPropNotASchemaKey-7742.test.tsx. Its describes were always accurate; only the filename claimed the generality the measurement refutes, and a filename is the widest-reach text here — it prints in every vitest run and in every file listing. Nothing in the repo referenced the old path, the two imports are same-directory relative, and check-type-check-coverage enumerates test files from the package root, so coverage is re-derived from disk (42/42 packages). Also corrects the countsAreWindowed docblock, whose "same shape as objectFields above" pointer this PR's own move had inverted, and the stale "Absent on the schema-only kanban-ui entry" line that the new scope paragraph contradicted. Wording only: no behaviour, no refusal, no declaration, no pin assertion and no ablation leg is touched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
Contract review at
|
✅ 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
|
…ban-arm-ruling-batch70
✅ 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
|
Post-merge contract audit at
|
| hunk | what | judged |
|---|---|---|
.changeset/7742-kanban-arm-batch70.md:55-75 |
heading renamed; "never an authoring surface" sentence replaced; new arm-level scope paragraph. Frontmatter (minor × 2) untouched |
correct — closes F3's release-bound carrier |
packages/plugin-kanban/src/index.tsx:156-166 (countsAreWindowed TSDoc) |
fixes the "objectFields above" pointer the move had inverted; "Not an authorable input" → "Not MEANT to be"; says an author can write it on kanban-ui |
beyond F3's named lines but same class, accurate (KanbanRenderer reads schema.countsAreWindowed at :245), no behaviour |
index.tsx:176-207 (objectFields TSDoc) |
"AN INTERNAL CHANNEL" → "INTENDED AS…"; the false "only writer" sentence deleted; new "closes the kanban ARM, not the key" paragraph |
correct vs SchemaRenderer.tsx:1480/1576 (no objectFields in the strip list) and the registrations :252/:579/:589, ObjectKanban.tsx:219 void _props — one precision gap, F7 |
__tests__/objectFieldsIsNotAuthorable-7742.test.tsx → objectFieldsIsAPropNotASchemaKey-7742.test.tsx (+17/−1) |
rename + docblock :10, new Scope section :30-45 |
docblock-only (mechanically: zero non-comment changed lines); old path referenced nowhere |
Mechanically confirmed: every changed line in index.tsx and the test is inside a JSDoc block. complex.ts, complex.zod.ts, ObjectKanban.tsx, schema-reference.md, kanban-arm-batch70-7742.test.ts, the 7664 census pin: byte-identical to a218a937. The 25 other files that moved are main's own (#8798/#8794/#8795/#8804/#8796/#8744); none touches a PR file.
Published-face reading: unchanged. KanbanSchema.navigation?: ViewNavigationConfig (complex.ts:347, new); allowCollapse/cardTemplates/columnWidths/titleField ?: never (:380/:398/:422/:451); zod complex.zod.ts:220/226/231/237/238; KanbanRendererProps.objectFields?: unknown top-level (index.tsx:208, shifted from :193 by docblock growth). The only shipped text that moved is TSDoc on two .d.ts members; no shape moved, so the accept set did not move.
Open-items status
- F2 (type-level leg) — NOT ADDRESSED.
kanban-arm-batch70-7742.test.tshas no type-level construct; the 7664 file (:138-141) still pins onlydraggable/color/onColumnAdd/onCardAdd. Carded as objectui#8811 (11:14:11Z, before the clear;tests+finding, unassigned) — the "land as-is with a follow-up card" option the verdict of record allowed. FourAssert<RetiredIsNever<KanbanSchema['x']>>lines are owed onmain. - F3 — ADDRESSED.
index.tsx:176-207and changeset:55-75(above). - F5 — ADDRESSED. PR body line 18 is
`Clause-②: yes`; run through the seat's own parser (readClause2Lineinscripts/pm/check-clause2-carriers.mjs) →{kind:'declared', value:'yes'}. Body-only: the squash message carries the two commit messages +Fixes #7742, not the body. - F6 — ADDRESSED. Rename + docblock state the tests render
KanbanRendererdirectly and never exerciseSchemaRenderer; Type Check green on the head covers compilation of the renamed file. - F4 (html-tier
titleField) — UNCHANGED, as ruled.OBJECT_KANBAN_INPUTS(index.tsx:564) still declarestitleField(:570) and is spread into'object-kanban'(:586) and'kanban'(:598) — identical toa218a937modulo a +17 line shift; zod refusalcomplex.zod.ts:238unchanged. objectui#8802 is open but still unlabeled (noneeds-user-decision) ~3h after filing — the ruling it needs has no carrier yet. - The
kanban-uifollow-up the PR body committed the PM to: objectui#8818 filed 11:45:02Z (post-merge), open,plugin: kanban+finding.
Clause-② reading: yes — unchanged. Delta is wording-only; no key, member, export or registration moved in either direction, so no re-derivation. Post-merge --pair 8799 exits 2 (PR not open → pair not formable; not a reading).
Governed surface: none. 9 files; none under AGENTS.md/CLAUDE.md/.claude/**/skills/**/docs/adr/**. Governed Surface Queue Guard green on the head (re-ran 11:23:24Z at ready-for-review) and on the queue run.
CI
- Head
1ecfafae: 35 check-runs — 32 success / 3 skipped (Test (coverage), coverage shard,dependabot: by design, same skips as thea218a937reading). Type Check 11:14:36Z, Lint 11:11:10Z, lastTest (shard 1/4)11:22:01Z. Pushed ~11:05:41Z;547dade7pushed ~10:48:53Z (one minute after the 10:47:55Z verdict, which was correctly ona218a937). - Merge-queue run:
merge_groupongh-readonly-queue/main/pr-8799-b89583ba…,head_sha=dcbf0b2b— 18 workflows / 27 check-runs from 11:23:44Z, all success (2 by-design coverage skips), lastTest (shard 2/4)11:39:45Z → merged 11:40:18Z. So the "third window" the dev recorded as NOT MEASURED (b89583ba) was measured by the queue. - Post-merge push on
main: all success, including the four coverage shards (12:00:22Z); merge-queue head patrol ×2 success.
New findings
F7 — LOW, wording precision (non-blocking; pre-existing surface, not moved by this PR). The delta's "on the 'kanban' arm that closes the key outright — ObjectKanbanRenderer serves that type key" (changeset :64-67, index.tsx:191-193, test docblock :33-36) is true of the plugin's own ComponentRegistry.register('kanban', ObjectKanbanRenderer) (:589). But the shipped "Standard Export Protocol" map kanbanComponents (index.tsx:343-347, in the published .d.ts) maps 'kanban' → KanbanRenderer, and packages/plugin-kanban/README.md:63-71 ("Manual Integration") teaches registering every entry of that map by its key; packages/runner/src/plugin-integration.test.ts:11-50 pins the map. An app on that path registers 'kanban' → KanbanRenderer, so an authored objectFields on a type:'kanban' node arrives by the same spread as on kanban-ui. Present on main before the PR (5dee0146:311-312), untouched here, named by neither #8818 nor #8811. Disposition: a one-paragraph rider on #8818 ("closed via the plugin's own registration; the kanbanComponents manual-integration route sends 'kanban' to KanbanRenderer"), plus a note that kanbanComponents.kanban and the registry's 'kanban' are two different renderers — a pre-existing inconsistency outside batch #70's ruling.
F8 — process (no fix on main follows). The carrier was cleared on a head with no verdict: needs:contract-review removed card 11:23:12Z / PR 11:23:13Z by os-warren, ready-for-review 11:23:18Z, queued 11:23:24Z — 36 min after the PASS on a218a937, with two later heads (547dade7, 1ecfafae) and no delta verdict. contract-review.md:22 ("head 后移 … 才重挂") and the 10:47Z verdict's own note (a wording commit "would move the head and re-hang the label — a delta-scoped re-review") both called for this audit before the clear. Also contract-review.md:38: no provenance comment on either carrier at clearing time; the card's Release: comment (5601282563, 11:43:31Z, post-merge) cites the PASS retroactively. Outcome-wise benign — this audit finds the delta correct — but it is the discipline gap for the round report.
F9 — cosmetic, carried over. Changeset :10-11 still says "which is why the PR carries needs:contract-review" — process noise in release-bound text (flagged in verdict 1), and now also false of the merged PR. Fold into the next touch of that changeset or the release-notes pass; not a PR on its own.
Acceptance notes
- Scope of the delta is exactly F3/F5/F6 plus one adjacent, consistent docblock (
countsAreWindowed) and two clean merges ofmain; no behaviour, refusal, declaration, pin assertion or ablation leg moved. The dev report (5600650055) declares each of these, including the body-only F5 line as an addition "beyond the dispatch's enumeration". - The verdict of record's published-face and Clause-② readings stand verbatim on the merged head; the merged tree on
mainis the PR head's tree for all 9 files. - Debt on
mainafter this merge, all carded: finding(types): the fourKanbanSchemaretirement tombstones have no compile-time pin — the.d.tsface is unchecked while the zod face is proven #8811 (F2 type-level pins — four lines), finding(react,plugin-kanban): an AUTHOREDobjectFieldsstill reaches the predicate layer on thekanban-uientry — batch #70 closed thekanbanarm, not the key #8818 (kanban-uiauthor reachability; add F7's rider), finding(plugin-kanban,types): after batch #70 theview:kanbanregistryinputsstill declarestitleFieldwhile the zod arm refuses it — two published faces of one type key, opposite verdicts, and every pin stays green #8802 (F4 ruling — needs triage/needs-user-decision, still unlabeled), finding(types):ObjectKanbanSchema.allowCollapseis the SAME zero-read key one arm over — declared on both faces, read by no registered board, and outside batch #70's reach #8801 (sibling-armallowCollapse, unlabeled), decision(plugin-kanban):navigationis read off the kanban node and declared on neither arm — while the grid schema declares it (5 of 7, objectui#8327 class (a)) #8652 (open, unlabeled, re-asks thenavigationquestion batch Add cloud storage functionality with Vercel KV integration in studio #70 settled — triage should reconcile). - Not readable from this seat: job logs (same proxy limitation as the prior verdict); every CI reading above is from check-run/workflow-run conclusions pinned to the exact
head_sha.
Generated by Claude Code
Fixes #7742
Executes maintainer decision batch #70 (2026-09-07, verbatim 「同意」) row by row on the
'kanban'arm.⭐ Wording patch round — after the contract review PASSED
The contract review adopted the implementation whole (PASS, tier-verified, every leg re-derived). ⛔ Nothing about the retirement, the declaration, the refusals, the pins or the ablation is reopened, and no behaviour changed in this round — it is prose, one file rename, and this body. Seat session
session_01Jmxdo7bmeqCQHLSfmLVX9w.What the review found, and what this round did about it:
objectFieldshad become author-unreachable in general; the changeset is the one that matters, because a changeset is this PR's input to the release notesKanbanRendererPropsdocblock and the pin file's docblock now state the scope at the arm level; the pin file is renamed (see row 4 below)origin/mainhad movedorigin/mainmerged in (⛔ no rebase, no force-push) and the paragraph re-measured — see Base to main window checkClause-②: yesThat line is in the fixed spelling the seat protocol reads, so the carrier requirement is machine-legible and not merely described: this PR moves a declared accept set in both directions, therefore
needs:contract-reviewis hung on both the card and this PR and ⛔ stays hung — the PR remains draft and is ⛔ not enqueued.Most cards move one way. This one narrows and widens, and a reviewer must check each half separately.
allowCollapse,cardTemplates,columnWidths,titleField?: neveroncomplex.ts+ named refusal oncomplex.zod.tsnavigationcomplex.ts+complex.zod.ts, spec type by referenceNeither half is a drop.
BaseSchemais.passthrough(), so a dropped key is kept, not refused — the failure objectui#7664's own first cut shipped atonCardClick.Both directions are pinned in one file,
packages/types/src/__tests__/kanban-arm-batch70-7742.test.ts, because a file that pinned only one half would pass on a change that forgot the other.The four rows
1.
allowCollapse/cardTemplates/columnWidths— RETIRETombstone + named refusal on both faces. Each refusal message names the channel the capability actually lives on:
columns[].collapsedfor collapsing, thetemplatescomponent prop for card templates, theuseColumnWidthshook option for widths.CardTemplate/ColumnWidthConfigstay exported — the prop and the hook still consume them.⛔ A new refusal factory (
retiredZeroReadKanbanKey), not the existingretiredDeclarativeKanbanKey. That helper's message says the key "belonged to the retiredDeclarativeKanbanSchemadialect", which is true ofdraggableand false of these three: they were members of the plugin dialect objectui#7664 ruled authoritative. Sharing the message would hand the author a false history of their own document.2.
titleFieldon thekanbanarm — RETIRE THE FALLBACKObjectKanban.tsxreadsschema.cardTitle || schema.titleFieldand it is load-bearing for the sibling arm. What retires is this arm's acceptance of the legacy spelling. Pinned as a PAIR: two documents identical but fortype, opposite verdicts.3.
navigation— DECLAREObjectKanban.tsxreads it to choose the record-detail overlay and defaults it to a drawer; the(schema as any).navigationcast is gone. Member list is@objectstack/spec'sNavigationConfigby reference, not restated. The pin asserts the value is judged, not merely admitted — a badmodeand an undeclared member both draw refusals, which a test asserting only the good config would not have caught (it would have passed onmaintoo).4.
objectFields— off theschemabag, onto a React propMoved off the
schemabag ontoKanbanRendererPropsas a real React prop, a sibling ofschema, injected byObjectKanban— the one caller that fetches the object definition. Insideschemait was reachable by an author: on the schema-onlykanban-uientry, which has no object schema of its own to substitute, an authoredobjectFieldsreachedresolveConditionalFormattingverbatim while no schema face declared or judged it.kanbanarm is closed, the KEY is not. Measured through the realSchemaRenderer, and stated because the first cut of this PR read as if it were:objectFieldskanban-uikanban-uirgb(255, 0, 0)kanbanSchemaRendererstrips a fixed metadata list that does not includeobjectFields, then spreads the rest as React props — andKanbanRenderer, registered forkanban-ui, now reads that prop. So onkanban-uian authored value still arrives, on the very prop this change adds. The ruledkanbanarm is genuinely closed:ObjectKanbanRendererserves that type key and discards its rest-spread (void _props;), so an authoredobjectFieldsthere reaches nothing. ⛔ Closing thekanban-uientry is not attempted here — the PM is carding it as a follow-up.packages/plugin-kanban/src/__tests__/objectFieldsIsAPropNotASchemaKey-7742.test.tsxnow does, behaviourally: an expanded relation collapses to its stored id only when the field catalogue arrives, so the paint is a positive reading of the channel. The prop leg is asserted first as the firing control for the schema leg's negative, plus a third leg proving the paint is not unconditional. It rendersKanbanRendererdirectly, so it never exercisesSchemaRenderer: thekanban-uirows in the table above are measured, not pinned by this file, and its docblock says so.⭐ The file was renamed this round, from
objectFieldsIsNotAuthorable-7742.test.tsx. Itsdescribes were always accurate ("reaches the predicate layer as a PROP", "is no longer read off the schema bag"); only the name claimed the general unreachability the measurement refutes, and a filename is the widest-reach text this PR ships — it prints in every vitest run, ingit log --stat, and in this PR's own file list. Cost of the rename:git mvalone — nothing in the repo referenced the old path (git grep→ 0 hits outside the file), the two imports are same-directory relative and still resolve, andcheck-type-check-coverageenumerates test files from the package root rather than a list, so it re-derives coverage from disk (re-run: exit 0, 42/42 packages compile their tests).⭐ The re-derived objectui#8410 screen — and a correction to it
The screen recorded on the card (2026-09-07,
fff250f) reached the right answer through a wrong discrimination, and the dispatch repeats the error, so it is worth stating.The screen says
KanbanRenderer— which destructures onlyschema— is "this card'sKanbanSchema(type: 'kanban')", and thatObjectKanbanRenderer(which DOES rest-spread) is "theobject-kanbanregistration, not this card's".Measured on this branch, that is inverted.
KanbanRendereris registered forkanban-ui.ObjectKanbanRendereris registered for BOTHobject-kanbanANDkanban— so this card's type key IS served by the rest-spreading renderer.The zeros survive anyway, for a different reason: the spread terminates. Full channel, traced end to end —
SchemaRenderer.tsxspreads every non-metadata node key as a React prop;ObjectKanbanRenderertakes({ schema, ...props })and forwardspropstoObjectKanban;ObjectKanbantakes({ schema, dataSource, className, data, loading, onRowClick, onCardClick, ..._props })and the very next statement isvoid _props;— the rest is discarded, never spread onward;effectiveSchemaintoKanbanRenderer), butKanbanRendererdestructures onlyschemaand names each key it forwards toLazyKanban.⇒ no forwarded rest-spread reaches a sink for any of the three keys. This is a retirement, not a regression.
⭐ The screen's own lesson holds and is strengthened:
grep -lfor a spread is not the instrument, and neither is the render function's signature alone — you must also read which type key that function is REGISTERED for, and follow the spread to where it terminates.Read census — every zero paired with a firing control on the same instrument
git grep -n -w KEY -- packages/plugin-kanban/src, every file including tests:allowCollapsecardTemplatescolumnWidthsgroupBycardTitlecoverImageFieldcolumnWidthsis not zero (22 hits / 11 files) — every one is a different key of the same spelling on the grid surface (data-table.tsx,ObjectGrid.tsx,RecordPickerDialog.tsx), untouched here.The two faces the screen did NOT cover, measured here
titleField, re-measured from25907cd70: both reads are now cast-free (schema.cardTitle || schema.titleField,schema.cardTitle ?? schema.titleField) because PR fix(plugin-kanban): ObjectKanbanComponentProps.schema names both registered node types (objectui#7322 item ②) #8153 widened the prop toKanbanSchema | ObjectKanbanSchema.ObjectKanbanSchema(objectql.ts) declarestitleField?: stringand this PR does not touch it — theobject-kanbanarm still declares it, asserted at runtime by the sibling-arm pin.inputsface — ⛔ a divergence this PR creates and does NOT close.OBJECT_KANBAN_INPUTSis ONE shared array spread into both registrations and it declarestitleField; objectui#8201 pins that sharing structurally, andComponentPropsMaphas anobject-kanbanentry but nokanbanentry to compare a split list against. So atype: 'kanban'node namingtitleFieldis now accepted by the html tier and refused by the zod tier, and every pin stays green. Un-sharing the array would redden a landed pin and re-open a landed ruling, so it is reported, not done: filed as objectui#8802 with four options and a recommendation. This is the one thing on this PR a reviewer should rule on.Ablation — every named refusal, mutation proven on disk
Each leg: replace the arm with a permissive declaration, prove the mutation landed (anchor gone, injected text present, byte delta non-zero), run the pin, restore with
git checkout HEAD -- ABSOLUTE_PATH, prove restoration by blob-hash equality AND an emptygit diff HEAD.allowCollapsecardTemplatescolumnWidthstitleFieldnavigation(the WIDENING)Tests no testswith a non-zero exit. A collection error is NOT a red; it is NOT MEASURED, and reading it as a red would have credited the pins with a failure they never demonstrated. The table above is the second cut, which replaces each arm as a whole expression so the file still parses and real assertions fail.Base to main window check —⚠️ RE-MEASURED; the window is NO LONGER empty
The earlier reading is superseded, not repaired. It recorded
origin/mainat the same sha asBASEand the window at 0 files, and noted that an empty window means its own control cannot fire. That was true when written. It is now stale:origin/mainhas moved to0e3bca45dover three commits (#8795, #8794, #8798), andgit diff --name-only b6d07df4 origin/mainreturns 9 files.⛔ Do not carry over its conclusion that "no file any pin reads off disk can have moved" — two of the nine are exactly such files:
packages/plugin-kanban/src/KanbanImpl.tsxobjectFieldspin imports it at module scope (the lazy-chunk prepay)apps/console/src/__tests__/registry-inputs-spec-parity.test.tsSo
origin/mainis merged into this branch (ordinary merge commits; ⛔ no rebase, no force-push, the branch is only ever appended to), and every gate in the table below was re-run on the merged tree. The window control now fires: non-empty, and the pins were re-run against the moved files instead of being reasoned about.mainmoved a second time while this round was verifying, so there are TWO merges — and a stopping rule.origin/mainat0e3bca45d(3 commits: #8795, #8794, #8798)KanbanImpl.tsx+ the console registry-parity pin — both read by pins here5dee0146d(3 commits: #8804, #8796, #8744)git diff --name-onlyof that window, intersected with this PR's paths, is emptyHead is
1ecfafae5andgit merge-base --is-ancestor origin/main HEADexits 0: the branch is level withmainas of5dee0146d. ⛔ There is no third merge, and that is deliberate rather than an omission — a branch cannot be held permanently level with a movingmainfrom inside one round, and each merge costs a full re-verification. The stopping rule taken here: merge, re-verify, record the sha, and leave any later movement to the queue at land time. The second window is recorded above precisely so the next reader can see it moved and that it could not have touched this PR.Verification
Re-run on the MERGED tree — the wording round
Exit codes captured before any pipe (
cmd > file 2>&1; EXIT=$?), heavy runs serialised through the container's shared verify lock, and each verdict read off the gate's own printed line rather than a bare$?. ⭐ Every row below was re-run on the final head1ecfafae5— i.e. after both merges, not just the first — so no number here predates a merge.pnpm --filter @object-ui/types buildpnpm --filter '@object-ui/plugin-kanban^...' build(dep closure)turbo run type-check --filter types --filter plugin-kanbanpackages/types/+packages/plugin-kanban/ai-zero-read-members-retired-8178.test.ts, arriving frommain)apps/console/pnpm --filter '@object-ui/console^...' build+--filter @object-ui/console buildcheck:sdui-registration-pinscheck-changeset-presence.mjs5dee0146d, 9 files changed, 7 published source, 1 changesetcheck-changeset-no-major.mjsmajorcheck-type-check-coverage.mjstype-check; 43/43 packages compile their tests (this is the gate the rename had to keep resolving; it was 42/42 beforemainbroughtplugin-aia test project)check-unreferenced-sources.mjscheck-control-bytes.mjscheck-governed-queue-guard.mjs --testover the 9 diff pathspnpm --filter types --filter plugin-kanban run lintno-explicit-any/react-refresh, none on a line this round touchedgrep -naP) over the 3 edited fileseslint .warning counts above (271 + 179) are not comparable with the 63 quoted in the previous round's narrowed run: that run linted only the 7 diff files, these lint the whole of both packages. Both readings are exit 0 with 0 errors; only the population differs.First round — the implementation, on the pre-merge tree
pnpm --filter @object-ui/types buildpnpm --filter '@object-ui/plugin-kanban^...' build(dep closure)pnpm --filter '@object-ui/console^...' buildpnpm --filter @object-ui/console build--build-filter)pnpm --filter @object-ui/cli buildtype-check— types + plugin-kanbanpackages/types/+packages/plugin-kanban/apps/console/packages/cli/ scripts/ examples/schema-catalog/pnpm check(the per-PR gate)pnpm --filter @object-ui/types --filter @object-ui/plugin-kanban run lintcheck:control-bytescheck:spec-symbolscheck:doc-typescheck:doc-snippetscheck:doc-examplescheck:doc-fencescheck:doc-example-readerscheck:handler-key-readscheck:sdui-registration-pinscheck:element-data-source-declarationcheck:readme-exportscheck:unreferenced-sourcescheck:changeset-no-majorcheck-changeset-presence.mjscheck:lint-coveragecheck:governed-queue-guard --testover the 9 diff pathscheck:doc-snippets,check:doc-examples,check:sdui-registration-pinsandcheck:readme-exportseach returned PRECONDITION NOT MET on the first attempt, naming unbuilt packages in their own output. Those are not reds and are not recorded as passes either — the required builds were run and the gates re-run; the table shows the real verdicts, each with a population that fires (636 blocks, 518 chunks, 3321 symbols).The eslint narrowing, with its three readings
turbo run lintover the whole farm belongs to CI. The narrowed run is a measurement, not a skip:eslint . --no-inline-config --format jsonreports 4634 files linted.--format json: 7 — every.ts/.tsxin the diff, and all 7 are present in the 4634. Exit 0, 0 errors, 63 warnings, all pre-existingno-explicit-any/react-refreshon untouched lines.eslint.config.jsdeclares noparserOptions.projectand noprojectServiceanywhere, so type-aware linting is not enabled — this diff cannot move the verdict on any file it does not itself contain.For completeness the full run was executed: exit 1, 94 errors across 78 files, none of them mine (verified by set intersection against the 7). Those are pre-existing
react-hooks/static-components/no-consolefindings under bareeslint ., which is not this repo's gate —pnpm lintisturbo run lint, i.e. the per-package scripts, and both of mine exit 0.Changeset
.changeset/7742-kanban-arm-batch70.md—minorfor@object-ui/typesand@object-ui/plugin-kanban, spelling out the breaking semantics in the body. ⛔ Nevermajor: this repo's fixed group tracks@objectstack's major, andcheck-changeset-no-major.mjsenforces it.Found and deliberately NOT done
ObjectKanbanSchema.allowCollapseis the same zero-read key one arm over, declared on bothobjectqlfaces, read by nothing, and outside batch Add cloud storage functionality with Vercel KV integration in studio #70's reach. After this PR the two arms of one renderer disagree about the key.inputsdivergence described above.countsAreWindowed— noted, not filed. Same shape and same argument asobjectFields: internal, injected byObjectKanban, absent from the registryinputs, and still riding theschemabag where an author can reach it on thekanban-uientry. The ruling did not name it, so it stays. Its successor is named: theKanbanRendererPropsdocblock inindex.tsxrecords it in as many words, and objectui#8307 owns the key.navigationis read off the kanban node and declared on neither arm" — the same subject batch Add cloud storage functionality with Vercel KV integration in studio #70 already ruled. This PR executes the ruling and ⛔ does not close that card; a triage seat should reconcile the two, since a later card appears to re-ask a settled question.onColumnToggle/enableVirtualScrolling/virtualScrollThresholdare out of scope here — they belong to'kanban-enhanced', which has no arm, and objectui#8257 carries them. The root README example is out of scope here — objectui#8256 carries it. Thefilterkey is out of scope here — objectui#7712 carries it. None of those three is addressed by this change.维护者速读(草稿)
改了什么 — 按 batch #70 裁决逐行执行
kanban臂:退役allowCollapse/cardTemplates/columnWidths(两面声明、无渲染器读取)与titleField(cardTitle的旧拼法;兄弟臂object-kanban保留自己的),四者都是具名拒绝而非删除;声明navigation(板子一直在读、两面都没声明);把objectFields从 schema 袋子挪到真正的 React prop。为什么改 — ADR-0049 enforce-or-remove。作者写
allowCollapse: true今天校验通过、板子却永不折叠;写navigation则相反,板子会兑现而校验器看不见。两种都是「声明与实现脱节」,方向相反。风险与代价(含回滚) — accept set 双向移动:四个拼法从「通过」变成「按名拒绝」,一个键从「不可见」变成「被校验」。今天写了这四个键的板子会开始报错——这正是裁决要的响亮失败,但它是破坏性的。回滚成本低:整个改动是声明面加一个 prop 位置,⚠️ 一处残留分歧未关:
git revert一笔即可,无数据迁移、无存储格式变化。titleField在注册表inputs面仍对kanban声明着(共享数组,objectui#8201 结构性钉住),于是 html 层接受、zod 层拒绝,且所有钉子照常绿。拆开那个数组要动一条已落地的裁决,故已立卡 objectui#8802 报告、未擅动。objectFields只在被裁的kanban臂上关死了;在kanban-ui入口上,作者写的objectFields仍会经SchemaRenderer的通用 prop 展开抵达渲染器(实测:卡片会被上色)。合同评审 PASS 之后的这一轮只改文案口径,把三处「读起来像是彻底不可达」的措辞改成按臂陈述,不动任何行为、拒绝、声明、钉子或消融;关掉那个入口是 PM 另行立卡的后续。席位意见 — (待定稿)
你要做的 — 只需裁一件事:objectui#8802 里那处
titleField的注册表分歧,选 A(接受、另卡跟进)、B(拆共享数组)、C(不退役)还是 D(上游给 spec 加kanban条目)。本席建议 A 现在、D 是真答案。其余四行按裁决原样执行,无需选择。Generated by Claude Code