feat(types,plugin-kanban,plugin-gantt)!: retire the bare kanban / gantt node keys and the kanban-ui / kanban-enhanced registrations - #8865
Conversation
…gantt` node keys and the `kanban-ui` / `kanban-enhanced` registrations
Executes three maintainer rulings taken in one batch on 2026-09-09:
- objectui#8802 — retire the bare `kanban` node type key
- objectui#8257 — retire `kanban-enhanced` and `kanban-ui`
- objectui#8008 — retire the bare `gantt` registration
Three of the four are registration-only: no schema face ever declared
`kanban-ui`, `kanban-enhanced` or `gantt` as a component node type, so
unregistering is the whole retirement. The bare `kanban` key had a declared arm
on both faces, so it retires as a NAMED REFUSAL — a new `retiredNodeType()`
helper keeps an arm claiming the literal in `AnyComponentSchema`'s discriminated
union and answers a `{ "type": "kanban" }` document with a message naming
`object-kanban`, while the TypeScript half is the absence of the arm from
`ComplexSchema` and of the key from `SchemaRegistry`.
The STORED `NamedListView.type` values `"kanban"` and `"gantt"` are a different
layer and are untouched: `ObjectView` already maps a stored view type onto the
`object-*` node type it renders, so zero stored documents move. A compile-time
pin guards that layer against a future pattern-matching migration.
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
|
…ly retirement pins Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
…anban` arm 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
|
Landing plan — ⛔ I cannot merge this PR as it stands, and the maintainer ruled to split it
The blockerThis PR touches one governed-surface file: ⛔ This seat never merges a governed-surface PR and never submits an approving review on one, under any account. That is a hard constraint, not a judgement call — so as a single 50-file PR this cannot be landed by me at all, and it would sit as a draft waiting for a human while
Maintainer ruling, 2026-09-09: split the skills file out; the rest goes through the queue.
⇒ The 49 land on the normal path and release the held files; the governed one waits for a human without blocking them. ⛔ What the split must not do
Also recorded from this round
Generated by Claude Code |
`skills/objectui/guides/page-builder.md` is a governed surface (`GOVERNED_SURFACES` -> `skills-catalog`), so a PR containing it can only be merged by a human. Carrying it here held `packages/types/src/complex.ts`, `zod/complex.zod.ts` and `packages/plugin-kanban/**` against every other slice. Maintainer ruling, 2026-09-09: split the skills file out. This commit reverts that one file to its merge-base blob (6df1359), leaving this PR at 49 files on the normal queue path. The edit itself moves, unchanged, to its own PR for a human to merge. No content of the guide is changed here and nothing else on this branch is touched: the revert restores blob 6df1359 exactly, and the same two one-word type-literal corrections are reproduced byte-identically on the new branch (blob ac30a6e). History is NOT rewritten -- this is an added commit, no rebase, no amend, no force-push, because other seats read this branch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
…lder guide Split out of objectui#8865 (branch `claude/kanban-gantt-family-retirement`) under the maintainer's 2026-09-09 ruling, byte-identical. `skills/**` is a governed surface (`GOVERNED_SURFACES` -> `skills-catalog`), so a PR carrying this file can only be merged by a human. On #8865 it was 1 file of 50 and held the other 49 -- `packages/types/src/complex.ts`, `zod/complex.zod.ts`, `packages/plugin-kanban/**` -- against every other slice. Splitting it lets those 49 take the normal queue path while this one waits for a human. The change is the same two one-word type-literal corrections: the guide's two `<!-- os:check -->` JSON fences authored the bare `kanban` and `gantt` node keys, which #8865 retires (`kanban` becomes a named refusal, `gantt` loses its registration). They now author the surviving `object-kanban` and `object-gantt` spellings.⚠️ This is the NODE TYPE KEY layer only. The stored `NamedListView.type` value "kanban" is a different layer, is NOT retired, and is untouched here. Byte-identity with #8865: blob ac30a6e, equal to the blob this path carried on #8865's head c695a1c. Whole-file line count 331 before and after -- no ratchet spent. Refs objectui#8802 Refs objectui#8257 Refs objectui#8008 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
|
|
os-dev-report {
"issue": 8865,
"status": "done",
"branch": "49-file half: claude/kanban-gantt-family-retirement (head c695a1c8d -> 4dcf54452) | governed half: claude/split-8865-skills-page-builder (head 33c9234b2, based on origin/main 348725a7c)",
"pr": "49-file half: https://github.com/objectstack-ai/objectui/pull/8865 (draft, unchanged) | governed half: https://github.com/objectstack-ai/objectui/pull/8916 (new, draft)",
"premise_still_valid": true,
"summary": "The split is DONE and the two halves ARE separable: no gate reddened without the skills edit. objectui#8865 went 50 -> 49 files by ADDING commit 4dcf54452 that reverts skills/objectui/guides/page-builder.md to its merge-base blob; history was NOT rewritten (push was a fast-forward c695a1c8d..4dcf54452, and c695a1c8d is still an ancestor of the new head, exit 0). The same edit now rides objectui#8916 alone off origin/main, byte-identical. The change is two one-word type-literal corrections inside two os:check-marked JSON fences, bare kanban/gantt -> object-kanban/object-gantt. The gate that actually reads those fences is check:skill-examples -- NOT one of the three the brief named; it parses marked JSON fences and, per its own docblock, deliberately does NOT validate them against @object-ui/types schemas, which is the mechanism that makes the two halves separable at all. Neither PR was enqueued, flipped ready, or had needs:contract-review touched; objectui#8865 still carries that label and is still draft.",
"tests": "PRECONDITION: turbo run build --filter='!@object-ui/site' --concurrency=2 -> exit 0, 43/43 tasks. Run FIRST, because several of these gates exit 2 = precondition-not-met without dist. No gate below returned 2, so none is a NOT MEASURED.\nAll gate exit codes captured into a variable BEFORE any pipe (cmd > file 2>&1; e=$?), on 49-file head 4dcf54452.\nGATES ON THE 49-FILE HEAD, ALL GREEN, none reddened by removing the skills edit:\n check:doc-types = node scripts/check-doc-component-types.mjs -> exit 0 (brief named scripts/check-doc-types.mjs; that file does not exist). 188 doc files, 1107 code blocks, 892 type literals vs 650 registered keys.\n node scripts/check-readme-exports.mjs -> exit 0. 43 READMEs, 412 fenced blocks, 3324 export symbols from 37 of 40 packages, 0 fabricated, 0 stale omissions.\n check:doc-snippets = node scripts/check-doc-snippet-types.mjs -> exit 0. 245 documents, 637 of 637 blocks judged, 0 failed. Gate prints its OWN sentinel control: importing 'ThisNameIsDefinitelyNotExported' -> 1 diagnostic TS2305, positive 'BaseSchema' -> 0.\n node scripts/check-doc-links.mjs -> exit 0, links valid across 17 scan roots. CONFIRMED: pnpm check:doc-links -> exit 254, because no such package script exists; it is wired as docs:check-links. Ran the .mjs directly, as instructed.\n check:skill-examples -> exit 0. THE decisive gate: it is the only one that reads skills/** fences. 20 guides, 70 json fences parsed, 0 failed; marked floor 70 json / 13 ts, so the population did not shrink.\n check:doc-examples -> exit 0; check:skills-paths -> exit 0 (88/89 paths, 1 baselined); check:skill-eval-tokens -> exit 0; check:doc-fences -> exit 0.\nFIRING CONTROL for check:skill-examples -- three points on ONE instrument against the BUILT tree, so the zeros are measurements of THESE fences and not a skipped file: (1) bare kanban/gantt, this PR's pre-image -> exit 0, 70 parsed 0 failed. (2) object-kanban/object-gantt, the post-image blob ac30a6e6c -> exit 0, 70 parsed 0 failed. (3) deliberately invalid JSON at the same fence -> exit 1, naming 'skills/objectui/guides/page-builder.md:255', JSON phase 70 parsed 1 FAILED.\nABLATION DISCIPLINE: mutation proven ON DISK before any result was read (poison token count 0 -> 1; blob 6df13594d -> f98e4541d, differing from HEAD); restored via git checkout HEAD -- ABSOLUTE_PATH from a trap on EXIT INT TERM; restoration proven BY BLOB HASH (back to 6df13594d) and by 'git diff HEAD' being empty, never by an exit code. Worktree clean afterwards.\nCOVERAGE PROOF that the gate reaches the reverted lines: check-skill-examples.mjs --list shows 10 marked fences in page-builder.md, including :255 and :266 -- the exact two fences reverted -- each 'marked pass', out of 212 listed rows.\napps/console FULL SWEEP -> exit 0, 98 test files, 1132 tests passed.\napps/console/src/__tests__/registry-inputs-spec-parity.test.ts run EXPLICITLY and alone -> exit 0, 1 file, 198 TESTS (matches the 198 objectui#8865 reported).\nPATH CHECK (the sibling-worktree hazard): every vitest RUN line and every /home/user/objectui* path in both logs resolves to /home/user/objectui-pr8865-49. Zero paths from any sibling worktree.\nBRANCH B build-free gates: check:skills-paths 0, check:skill-eval-tokens 0, check:doc-fences 0, check-changeset-presence 0 ('no changeset is owed'). check:skill-examples on branch B's own tree is NOT MEASURED -- it exits 1 with ERR_MODULE_NOT_FOUND because that worktree has no node_modules, which is a harness miss, not a red gate; its content is covered by control point (2) above and CI measures it.",
"mcp_calls": "0 — every GitHub read and write went through the repo-scoped REST channel (probed first: GET repos/objectstack-ai/objectui -> 200). No MCP GitHub tool was invoked.",
"open_questions": [
{
"question": "objectui#8865 is 20 commits behind origin/main and 5 of its 49 files ALSO changed in that window. Who merges main in before it is enqueued? I did not, because the brief said change nothing else on either branch.",
"options": [
"A — the contract-review seat merges origin/main into the branch as part of its pass, re-running the gates on the merged tree",
"B — the PM merges main in now, before dispatching the contract review, so the review reads the tree that will actually land",
"C — leave it to the merge queue and let it reject if it conflicts"
],
"recommendation": "B, because the overlapping 5 files are exactly the load-bearing ones — packages/types/src/complex.ts, packages/types/src/zod/complex.zod.ts, packages/plugin-kanban/src/index.tsx, content/docs/api/schema-reference.md, content/docs/plugins/plugin-kanban.mdx — and objectui#8870 (refuse `actions` by name on the `page` node) landed in complex.ts and complex.zod.ts inside that window. A contract review of the accept set that reads the pre-merge tree is reviewing a tree that will not land. My gate readings above are likewise on the unmerged 49-file head, so they do not speak for the merged result."
},
{
"question": "The PM recorded on comment 5610353950 that CI's Governed Surface Queue Guard reported success on the 50-file head while the local --test probe reported GOVERNED. The split makes it moot for these two PRs, but the instrument disagreement is unresolved.",
"options": [
"A — file a card to reconcile the CI check's input with the --test probe's input",
"B — leave it; the split made it moot and it may not recur"
],
"recommendation": "A, but it is the PM's to file, not mine — the brief scoped me to the split and told me to touch nothing else. Recording it here so it is not lost. Note the guard's pull_request leg is documented as deliberately exit 0, so a CI green there is NOT evidence a PR is ungoverned; that alone may explain the disagreement without any defect."
}
],
"out_of_scope_findings": [
"noted, not filed: the brief's gate list named `scripts/check-doc-types.mjs`, which does not exist — the script is `scripts/check-doc-component-types.mjs` (package script `check:doc-types`). Successor who hits this: the contract-review seat on objectui#8865, and any seat copying the brief's gate list. Ran the real script; exit 0.",
"noted, not filed: `pnpm check:doc-links` exits 254 because there is no `check:doc-links` package script — it is wired as `docs:check-links`. The brief predicted this and it reproduced exactly. Successor: the same seats as above. Filing declined: it is a naming inconsistency, not a reproducible defect, a declared-contract violation, or a metadata trap.",
"noted, not filed: the three doc gates the brief flagged as the risk (check:doc-types, check:readme-exports, check:doc-snippets) structurally CANNOT be reddened by a skills/** edit — each excludes that surface by construction, as check-skill-examples.mjs's own header table records verbatim (DOCS_ROOT = 'content/docs', 'not skills/**'). The gate that does read skills/** fences is check:skill-examples, which the brief did not name. Successor: whoever writes the next split brief. Reported rather than silently relied on, because it is the reason the split is separable.",
"noted, not filed: objectui#8865's PR body still describes a 50-file change, a 3-commit base-to-main window and a skills line-budget table for a file the PR no longer carries. Successor: the contract-review seat, who reads that body next. I did NOT edit the body — the brief said change nothing else, and rewriting a governed-adjacent body to tidy it risks more than it fixes."
]
}Generated by Claude Code |
Split done — 50 → 49 files. ⛔ And my risk analysis named three gates that CANNOT see the surface in question
⛔ My error, firstI wrote that removing the All three structurally exclude The gate that does read
The split, verified
⭐ Byte-identity proven more strongly than I asked. I asked for the end-state blob to match. The dev proved both ends: post-image Firing control on Ledger (objectui#8614): 0 rows cite Governed reading, paired controls on one probe: the 49 paths → NOT GOVERNED (exit 0); the 1 skills path → GOVERNED (exit 3).
|
Brings the branch up to date so the contract review reads the tree that will actually land. Zero conflicts; both sides' intent verified present. Five files changed on both sides: content/docs/api/schema-reference.md content/docs/plugins/plugin-kanban.mdx packages/plugin-kanban/src/index.tsx packages/types/src/complex.ts packages/types/src/zod/complex.zod.ts Proven as a union rather than asserted: diff(old head -> merge) restricted to those five equals diff(merge-base -> main) restricted to them (53 lines), and diff(main -> merge) equals diff(merge-base -> old head) (936 lines). Control: the two expected diffs are not equal to each other, so the instrument discriminates. Correction to the dispatch premise, recorded rather than acted around: objectui#8870 (refuse `actions` by name on the `page` node) did NOT land in complex.ts / complex.zod.ts. It landed in packages/types/src/layout.ts and packages/types/src/zod/layout.zod.ts, which this branch does not touch; both are byte-identical to main in the merge result. What main actually contributed to the two contested schema files is the `refreshInterval` -> `refreshIntervalSeconds` rename on the Dashboard node (objectui#7783, @objectstack/spec 17.4.0), a different subject from the kanban arm. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
Contract review at
|
Contract review at
|
| # | Status | Evidence on head |
|---|---|---|
F1 CI red (runner reads kanbanComponents.kanban) |
Discharged | packages/runner/src/plugin-integration.test.ts:19,23,67-68 → kanbanComponents['object-kanban']; control :36-42 asserts Object.keys === ['object-kanban'] and the three retired spellings undefined; map is single-key at plugin-kanban/src/index.tsx:305-307. Type Check and Test (shard 2/4) green on head. |
F2 cross-repo Fixes defeats --pair |
Discharged | Body lines 1-3 Fixes #8802 / #8257 / #8008; --pair 8865 exit 0 (below). |
F3 README teaches type: 'kanban'; mdx imports non-exported symbol |
Discharged | plugin-kanban/README.md:8 and :43-58 (block now object-kanban + groupBy + data: [], with the stored-layer warning); plugin-kanban.mdx:443-444 split into @object-ui/types / @object-ui/plugin-kanban (barrel index.tsx:112 exports KanbanCard/KanbanColumn, never ObjectKanbanSchema). Residue sweep over docs/READMEs/apps/examples: no retired node-key literal remains except retirement notes, ViewSwitcher views[].type vocabulary (schema-reference.md:1200, view-switcher.mdx:23, plugin-view/README.md:270, view-switcher.json:16) and the parked skills guide (see R4). |
F4 false KanbanEnhanced subpath claim |
Discharged | Corrected at .changeset/…retirement.md:110-119 and index.tsx:333-344. Verified: plugin-kanban/package.json exports = ., ./style.css; barrel never exports it; tree-wide importers are only cardPredicateScope.test.tsx:22 (relative) and a vi.mock at kanban-handler-slots-7664.test.tsx:124. Decision card #8932 filed (open). File deliberately kept. |
F5 FROM→TO must name view:kanban/view:gantt |
Discharged | .changeset:75-108 (table + console-placeholder vs OBJUI-001 + objectui check trap); same in the body. Pins: kanban-family-registry-keys-retired-8257.test.ts:70-77 (getConfig('kanban','view') etc.), bare-gantt-node-key-retired-8008.test.ts (getConfig('gantt','view') falsy, object-gantt control). Verified placeholders.tsx:48 lists both keys; known-schema-types.ts:656,658 keep the namespaced pair, bare kanban/gantt absent. |
F6 changeset omits @object-ui/components |
Discharged | .changeset:5 '@object-ui/components': patch, :8 '@object-ui/runner': patch; 7 named = 7 touched published packages (all private unset/false). Levels per AGENTS.md:240 (breaking = minor). Changeset Declaration / Bump Policy / Fixed Group checks green. |
| F7 stale body readings | Discharged (two cosmetic residues) | Readings now stated per head (pre-merge / 616973214 / cf31b51ed); governed section reads NOT GOVERNED; "2 of 3 fixtures" with the third explained. Residue: body cites the #8818 disposition as comment 5611940366 (404) — the real one is 5611957855; merge-tree hash 33eb6e6d4 is 616973214's (seat's own finding B). Both body-only. |
F8 data/bind undocumented; onCardMove inside schema literals |
Discharged | schema-reference.md:949 blockquote (#baseschema anchor exists at :45); plugin-kanban.mdx:64-68, 81-83, 244-248, 403-415 — onCardMove split out of every literal, Properties row removed; no onCardMove: remains inside any schema object. Doc gates green on head. |
Clause-② + claim + --pair
- Reading unchanged:
yes(four registry keys + namespaced twins retire;KanbanSchemaleaves both barrels andplugin-kanban/src/types.tsre-export;kanbanComponentsshrinks to one key). ClaimClause-②: yeson PR and all three cards — matches. needs:contract-reviewpresent on PR and 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, types: does'kanban-enhanced'get a declared arm? — its registration readsonColumnToggle/enableVirtualScrolling/virtualScrollThresholdand no schema in@object-ui/typesdeclares the type at all (split from #7742) #8257, plugin-gantt/types: the registeredganttnode spelling has no declared schema type —ObjectGanttSchema.typeis the literal'object-gantt', so a typed author cannot write the key the registry (and the README's table) teaches #8008. Cards open,pm:dispatched+domain:spec, assigneeos-warren; newestClaim:on each (5604051087 / 5604054757 / 5604054957) namesclaude/kanban-gantt-family-retirement— unchanged.check-clause2-carriers.mjs --pair 8865→ exit 0, derives 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, types: does'kanban-enhanced'get a declared arm? — its registration readsonColumnToggle/enableVirtualScrolling/virtualScrollThresholdand no schema in@object-ui/typesdeclares the type at all (split from #7742) #8257, plugin-gantt/types: the registeredganttnode spelling has no declared schema type —ObjectGanttSchema.typeis the literal'object-gantt', so a typed author cannot write the key the registry (and the README's table) teaches #8008, "both carriers agree" on each. Control--pair 8916→ exit 2 (no card derivable), as expected.- The seat's in-seat delta review (5611963038) correctly self-classified as SELF-REVIEW; this director review is the independent one.
Governed
check-governed-queue-guard.mjs --test over all 50 changed paths → exit 0, NOT GOVERNED; 0 skills/** paths in the set (the governed half stays in #8916). Firing control skills/objectui/guides/page-builder.md → exit 3. CI "Governed Surface Queue Guard" green.
CI on head
33 check-runs on cf31b51ed6: 30 success · 3 skipped by design (coverage shard, coverage, dependabot) · 0 failure · 0 in progress. Lint, Type Check, Test shards 1-4, Build & E2E, Doc Snippet/Component/Fence checks, Changeset checks, README Export Check all green. Vercel status success.
#8917 overlap (ObjectKanban.tsx)
- fix(plugin-kanban): read the cardTitle/titleField pair once, through one resolver #8917 =
4fa0eb9c4, not in the PR's base (landed after merge-base60e1f80b2). git merge-tree --write-tree origin/main refs/pm-review/8865-r2→ exit 0 (clean, treecde55e58).- Composition checked in that tree:
ObjectKanban.tsxkeeps onlyresolveKanbanTitleField's two reads (:191-192), so fix(plugin-kanban): read the cardTitle/titleField pair once, through one resolver #8917's read-site census stays clean; the PR's narrowingschema: ObjectKanbanSchema(drops theKanbanSchemaarm,ObjectKanban.tsx:94) still satisfies fix(plugin-kanban): read the cardTitle/titleField pair once, through one resolver #8917's structuralKanbanTitleFieldSource; fix(plugin-kanban): read the cardTitle/titleField pair once, through one resolver #8917's test imports onlyObjectKanban/resolveKanbanTitleField— no retired symbol. No conflict, no foreseeable semantic break; the queue rebuild onmainis the final check.
Remaining findings (none blocking)
- R1 (minor, F3/F4 class, outside the delta): two untouched, ungated prose lists still name the retired
KanbanSchemasymbol —content/docs/api/index.md:22andpackages/types/src/zod/README.md:213. One-line edits; prose-only. - R2 (cosmetic, body-only): wrong finding(react,plugin-kanban): an AUTHORED
objectFieldsstill reaches the predicate layer on thekanban-uientry — batch #70 closed thekanbanarm, not the key #8818 comment ID (5611940366→5611957855); stale merge-tree hash; orphan**(self-reported). - R3 (note):
.github/prompts/component.prompt.md:71still listsview:kanban/view:ganttas View Layout keys. It describes the protocol placeholder taxonomy (both still registered as placeholders), so not copy-and-fails; a reader authoring from it gets a placeholder. Follow-up only. - R4 (sequencing):
skills/objectui/guides/page-builder.md:257,268still teaches"type": "kanban"/"gantt"— the governed half parked in docs(skills): authorobject-kanban/object-ganttin the page-builder guide — governed half split out of objectui#8865 #8916 (human merge). Until docs(skills): authorobject-kanban/object-ganttin the page-builder guide — governed half split out of objectui#8865 #8916 lands, the published skill teaches keys this PR refuses. - R5 (post-merge prose): fix(plugin-kanban): read the cardTitle/titleField pair once, through one resolver #8917's test comment (
ObjectKanban.titleFieldPrecedence-8308.test.tsx:119-121) references theKanbanSchemaarm; stale after merge, harmless. - R6 (process, recorded): the merge-round dev briefly added/removed
needs:contract-review(self-reported in 5611007175); net state unchanged; PM re-hung it later. No action.
Acceptance notes
- Implemented-by:
os-devsubagents (no own session) on branchclaude/kanban-gantt-family-retirement, dispatched fromsession_01Jmxdo7bmeqCQHLSfmLVX9w(os-warren seat) — merge round 5611007175, F1–F7 round 5611686320, F8 round 5611825425. - Reviewed-by:
session_017Js5kTpTtxieBjPyScgxJ3(director seat, this session). Distinct sessions ⇒ independent; not SELF-REVIEW. Fixes #8802 / #8257 / #8008— all correct. Each card's subject is fully removed on this head (barekanbanrefused by name + unregistered;kanban-ui/kanban-enhancedunregistered with the three keys confirmed surface-less; bareganttunregistered, docs onobject-gantt). The body's closing keywords touch no other open card (mechanical sweep: only those three#Nforms).- Landing: PASS stands on
cf31b51ed6. Clearneeds:contract-reviewon the PR and all three cards in one stroke with a provenance comment citing this PASS; flip ready and enqueue (all checks green, not a required subset; NOT GOVERNED). Prefer folding R1 into a docs-only follow-up rather than pushing a rider (a new head would need the carriers re-hung); if a rider is pushed anyway, it is prose-only and the seat's pre-committed condition (1)-(4) applies. After merge: the three cards auto-close viaFixes; unblock types(plugin-kanban): restore lane/card judging onobject-kanban— declarecolumns: KanbanColumn[], the one thing the retiredkanbanarm judged that nothing else does #8913 (pm:blockedon this PR); land docs(skills): authorobject-kanban/object-ganttin the page-builder guide — governed half split out of objectui#8865 #8916 promptly (R4); decision(plugin-kanban):KanbanEnhanced.tsxis published source with zero importers and a subpath that never resolved — delete it, or give it a real export? #8932 stays open as theKanbanEnhanced.tsxdecision.
Generated by Claude Code
|
Landing provenance — director seat, summon #18 segment 6 ( Generated by Claude Code |
Why the merge was rejected: ⛔ not a conflict — the merged tree goes red, and it will every time
The failure
⭐ That file does not exist on this PR's head. The mechanism. That test greps ⭐ PR #8922 anticipated this in its own commit message: "Interim by ruling: the contract-side refusal retires Attribution control — the failure is created by the merge, not pre-existing:
The conflict half is clean, and that is a reading
FIRING CONTROL, because an exit 0 from a conflict probe is nothing without one: a throwaway commit off the merge-base rewriting ⛔ Three suspects cleared, each with its own reading
Also green on the merged tree: ⇒ What has to changeThis is not a conflict to resolve. #8922's
A repair round is dispatched. ⛔ Not enqueued until it is green on a tree merged with current
|
…agnostic to the surviving object-kanban tag objectui#8922 landed `quickAddIsDiagnosedNotDropped-8285.test.ts` after this branch was cut. It re-derives its host-tag set from `plugin-kanban/src/index.tsx`'s `ComponentRegistry.register(…, ObjectKanbanRenderer)` calls and builds its manifest from the LIVE registry, so it is an importer of exactly what this branch retires — and it goes red only on the MERGED tree, which is what the queue builds and what a `mergeable_state` computed against a stale base cannot see. Measured on the merge of origin/main f4abff4: 4 failed / 5 passed, against 9/9 green on pure main and a file that does not exist on this branch's head. The subject stays pinned: the html tier NAMES the Quick Add pair instead of dropping the key. Only the set of tags it holds over shrinks. `QUICK_ADD_HOST_TYPES` drops `kanban`, and that is a measurement, not a convenience. `checkKanbanQuickAdd` has exactly one call site — `validate.ts`'s per-prop walk — and that walk runs only in the branch where `manifest.components[node.type]` resolved. A tag no registration produces is answered one level up by `unknown-component`, an error, and its props are never walked at all. On a manifest built from the live registry `<kanban quickAdd>` therefore draws `error/unknown-component` and nothing else, against a firing control on `<object-kanban quickAdd>` that draws `warning/inert-quick-add`. Nothing is silently dropped by the narrowing: the retired spelling is refused BY NAME at the tag, which is louder than this warning, and stacking both is the two-diagnostics-for-one-mistake shape `checkMemberTypes` already refuses. The diagnostic's remedy text is falsified by the same retirement and moves with it: it used to send authors to `<kanban-ui>`, a tag objectui#8257 retires, so a page written to the old advice draws `unknown-component`. It now names `KanbanRenderer` from `@object-ui/plugin-kanban`, which is still exported and still forwards both halves by identity. `content/docs/plugins/plugin-kanban.mdx` carried the same false sentence and is corrected the same way. The retired spellings keep a hand-built manifest row in `packages/sdui-parser` asserting they are NOT hosts — the discrimination a synthetic manifest can make and the live one cannot. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
|
| Step | Outcome |
|---|---|
| Build packages | success |
| Check console performance budget | failure |
Which half objected:
| Eager-closure half | Verdict |
|---|---|
| Aggregate closure ceiling | ✅ pass |
| Per-chunk ceilings | ✅ pass |
| Ceiling sensitivity (headroom) | ✅ pass |
| Ceiling freshness (checkout vs. base branch) |
⚠️ A superseded ceiling is neither a size regression nor a drifted gauge. The bundle was measured correctly and the ceilings on the base branch are correct — but this checkout predates a change to them, so the verdicts above were weighed against numbers that are no longer in force. Nothing grew. Update this branch onto the base branch and letBundle Analysisrun again; do not widen a ceiling to clear it. TheCheck console performance budgetstep log names each superseded constant with both values (objectui#6245).
Reason: The entry chunk measured 144.1 KB, but the eager-closure half of this gate returned no trustworthy VERDICT: the report could not be read, a ceiling has drifted out of range of the regression it must catch, or (objectui#6245) a ceiling was replaced on the base branch after this checkout was made. The step log says which. This is not a passing budget — and it is not a size regression either.
See the workflow run for details.
📦 Bundle Size Report
| Package | Size | Gzipped |
|---|---|---|
| app-shell (consoleActionDispatch.js) | 0.20KB | 0.19KB |
| app-shell (index.js) | 16.69KB | 6.21KB |
| app-shell (runtime-config.js) | 20.68KB | 7.36KB |
| app-shell (types.js) | 0.01KB | 0.04KB |
| app-shell (urlParams.js) | 10.06KB | 3.86KB |
| auth (ActiveOrganizationStorage.js) | 25.05KB | 9.16KB |
| auth (AuthContext.js) | 0.31KB | 0.24KB |
| auth (AuthGuard.js) | 2.07KB | 1.00KB |
| auth (AuthProvider.js) | 40.18KB | 10.59KB |
| auth (AuthShell.js) | 3.49KB | 1.40KB |
| auth (ForgotPasswordForm.js) | 12.21KB | 3.45KB |
| auth (LoginForm.js) | 18.15KB | 5.39KB |
| auth (PreviewBanner.js) | 0.90KB | 0.50KB |
| auth (RegisterForm.js) | 6.65KB | 2.22KB |
| auth (SocialSignInButtons.js) | 9.61KB | 3.89KB |
| auth (UserMenu.js) | 3.41KB | 1.23KB |
| auth (auth-gate-events.js) | 1.29KB | 0.66KB |
| auth (authStyles.js) | 5.04KB | 1.72KB |
| auth (createAuthClient.js) | 40.21KB | 10.80KB |
| auth (createAuthenticatedFetch.js) | 8.46KB | 3.43KB |
| auth (index.js) | 3.19KB | 1.44KB |
| auth (invitation-status.js) | 1.22KB | 0.70KB |
| auth (org-roles.js) | 6.66KB | 2.78KB |
| auth (phone-identifier.js) | 1.11KB | 0.66KB |
| auth (types.js) | 0.59KB | 0.35KB |
| auth (useAuth.js) | 5.30KB | 1.02KB |
| auth (useWorkspaceAdminStatus.js) | 11.08KB | 4.58KB |
| collaboration (CommentThread.js) | 26.08KB | 7.56KB |
| collaboration (LiveCursors.js) | 3.17KB | 1.27KB |
| collaboration (PresenceAvatars.js) | 6.49KB | 2.64KB |
| collaboration (PresenceProvider.js) | 2.79KB | 1.13KB |
| collaboration (index.js) | 1.68KB | 0.73KB |
| collaboration (useCollaborationTranslation.js) | 6.05KB | 2.52KB |
| collaboration (useCommentSearch.js) | 1.98KB | 0.88KB |
| collaboration (useConflictResolution.js) | 7.75KB | 1.86KB |
| collaboration (useMentionNotifications.js) | 1.81KB | 0.68KB |
| collaboration (usePresence.js) | 6.33KB | 1.84KB |
| collaboration (useRealtimeSubscription.js) | 7.91KB | 2.01KB |
| components (index.js) | 500.20KB | 114.67KB |
| core (index.js) | 7.48KB | 2.96KB |
| create-plugin (index.js) | 28.04KB | 9.46KB |
| data-objectstack (index.js) | 201.97KB | 56.22KB |
| fields (index.js) | 246.97KB | 62.30KB |
| i18n (LocalizationContext.js) | 1.76KB | 0.96KB |
| i18n (builtinAggregateLabels.js) | 0.86KB | 0.49KB |
| i18n (currency.js) | 1.22KB | 0.64KB |
| i18n (fallbackInterpolation.js) | 6.25KB | 2.77KB |
| i18n (i18n.js) | 6.57KB | 2.76KB |
| i18n (index.js) | 3.65KB | 1.47KB |
| i18n (pickLocalized.js) | 7.62KB | 3.26KB |
| i18n (provider.js) | 26.89KB | 9.04KB |
| i18n (useDisplayLocale.js) | 2.85KB | 1.45KB |
| i18n (useObjectLabel.js) | 34.34KB | 9.17KB |
| i18n (useSafeTranslation.js) | 5.60KB | 2.33KB |
| layout (index.js) | 38.84KB | 10.94KB |
| mobile (MobileProvider.js) | 0.92KB | 0.49KB |
| mobile (ResponsiveContainer.js) | 0.94KB | 0.38KB |
| mobile (breakpoints.js) | 1.51KB | 0.70KB |
| mobile (createOfflineDataSource.js) | 5.61KB | 1.75KB |
| mobile (index.js) | 1.99KB | 0.87KB |
| mobile (offlineQueue.js) | 3.91KB | 1.35KB |
| mobile (pwa.js) | 0.97KB | 0.49KB |
| mobile (serviceWorker.js) | 1.48KB | 0.62KB |
| mobile (serviceWorkerSource.js) | 3.41KB | 1.48KB |
| mobile (useBreakpoint.js) | 1.54KB | 0.65KB |
| mobile (useGesture.js) | 6.96KB | 1.98KB |
| mobile (useOfflineSync.js) | 1.99KB | 0.72KB |
| mobile (usePullToRefresh.js) | 2.53KB | 0.85KB |
| mobile (useResponsive.js) | 0.72KB | 0.42KB |
| mobile (useSpecGesture.js) | 4.39KB | 1.66KB |
| mobile (useTouchTarget.js) | 1.01KB | 0.54KB |
| permissions (MePermissionsProvider.js) | 13.52KB | 4.88KB |
| permissions (PermissionContext.js) | 0.31KB | 0.25KB |
| permissions (PermissionGuard.js) | 0.89KB | 0.45KB |
| permissions (PermissionProvider.js) | 6.24KB | 2.16KB |
| permissions (discardProofCache.js) | 1.04KB | 0.55KB |
| permissions (evaluator.js) | 8.39KB | 3.10KB |
| permissions (index.js) | 0.93KB | 0.41KB |
| permissions (store.js) | 0.91KB | 0.42KB |
| permissions (useFieldPermissions.js) | 1.28KB | 0.53KB |
| permissions (usePermissions.js) | 4.83KB | 2.27KB |
| plugin-ai (index.js) | 14.81KB | 3.63KB |
| plugin-calendar (index.js) | 49.03KB | 13.93KB |
| plugin-charts (index.js) | 71.39KB | 19.92KB |
| plugin-chatbot (index.js) | 194.54KB | 46.34KB |
| plugin-dashboard (index.js) | 132.41KB | 34.84KB |
| plugin-designer (index.js) | 215.68KB | 44.27KB |
| plugin-detail (index.js) | 253.19KB | 65.62KB |
| plugin-editor (index.js) | 2.23KB | 1.05KB |
| plugin-form (index.js) | 136.79KB | 34.19KB |
| plugin-gantt (index.js) | 166.65KB | 40.91KB |
| plugin-grid (index.js) | 210.86KB | 57.28KB |
| plugin-kanban (index.js) | 46.06KB | 14.30KB |
| plugin-list (index.js) | 112.54KB | 27.65KB |
| plugin-map (index.js) | 20.49KB | 6.83KB |
| plugin-markdown (index.js) | 13.88KB | 4.80KB |
| plugin-report (index.js) | 43.42KB | 11.92KB |
| plugin-timeline (index.js) | 30.10KB | 8.74KB |
| plugin-tree (index.js) | 9.55KB | 3.32KB |
| plugin-view (index.js) | 84.42KB | 20.80KB |
| providers (DataSourceProvider.js) | 0.75KB | 0.39KB |
| providers (MetadataProvider.js) | 1.37KB | 0.59KB |
| providers (ThemeProvider.js) | 1.90KB | 0.85KB |
| providers (UploadProvider.js) | 11.66KB | 3.50KB |
| providers (index.js) | 0.45KB | 0.23KB |
| providers (types.js) | 0.01KB | 0.04KB |
| react-runtime (index.js) | 5.62KB | 2.34KB |
| react (LazyPluginLoader.js) | 4.47KB | 1.63KB |
| react (SchemaRenderer.js) | 83.34KB | 27.61KB |
| react (data-invalidation.js) | 5.05KB | 2.08KB |
| react (index.js) | 4.63KB | 2.18KB |
| react (schema-input.js) | 2.32KB | 1.24KB |
| react (spec-input.js) | 0.20KB | 0.18KB |
| sdui-parser (codegen.js) | 6.58KB | 2.74KB |
| sdui-parser (dashboard-widget-options.js) | 3.08KB | 1.30KB |
| sdui-parser (index.js) | 5.66KB | 2.50KB |
| sdui-parser (input-type.js) | 2.84KB | 1.40KB |
| sdui-parser (kanban-quick-add.js) | 3.89KB | 1.87KB |
| sdui-parser (parse.js) | 20.57KB | 5.88KB |
| sdui-parser (provenance.js) | 3.66KB | 1.82KB |
| sdui-parser (types.js) | 0.28KB | 0.23KB |
| sdui-parser (validate.js) | 14.82KB | 4.99KB |
| types (ai.js) | 0.20KB | 0.17KB |
| types (api-types.js) | 0.20KB | 0.18KB |
| types (app.js) | 2.87KB | 1.00KB |
| types (base.js) | 0.20KB | 0.18KB |
| types (blocks.js) | 0.20KB | 0.18KB |
| types (complex.js) | 2.93KB | 1.49KB |
| types (crud.js) | 0.20KB | 0.18KB |
| types (dashboard-filter-alias.js) | 6.23KB | 2.74KB |
| types (data-display.js) | 3.75KB | 1.85KB |
| types (data-protocol.js) | 0.20KB | 0.19KB |
| types (data.js) | 0.20KB | 0.18KB |
| types (designer.js) | 1.85KB | 0.85KB |
| types (disclosure.js) | 0.20KB | 0.18KB |
| types (error-code.js) | 1.54KB | 0.88KB |
| types (expression.js) | 0.20KB | 0.18KB |
| types (feedback.js) | 0.20KB | 0.18KB |
| types (field-types.js) | 0.20KB | 0.18KB |
| types (form.js) | 0.20KB | 0.18KB |
| types (http-inflight.js) | 8.87KB | 3.73KB |
| types (http-retry.js) | 4.32KB | 2.02KB |
| types (icon-key-migration.js) | 4.26KB | 1.63KB |
| types (index.js) | 4.74KB | 2.25KB |
| types (layout.js) | 0.20KB | 0.18KB |
| types (managed-by.js) | 0.19KB | 0.18KB |
| types (mobile.js) | 4.73KB | 2.28KB |
| types (navigation.js) | 0.20KB | 0.18KB |
| types (objectql.js) | 0.20KB | 0.18KB |
| types (overlay.js) | 0.20KB | 0.18KB |
| types (permissions.js) | 0.20KB | 0.18KB |
| types (plugin-scope.js) | 0.20KB | 0.18KB |
| types (record-components.js) | 0.20KB | 0.19KB |
| types (record-semantics.js) | 1.28KB | 0.67KB |
| types (registry.js) | 0.20KB | 0.18KB |
| types (reports.js) | 0.20KB | 0.18KB |
| types (select-option.js) | 0.20KB | 0.19KB |
| types (spec-report.js) | 5.05KB | 1.93KB |
| types (spec-ui-namespace.js) | 0.20KB | 0.19KB |
| types (strict-authoring-face.js) | 14.27KB | 5.47KB |
| types (system-fields.js) | 3.33KB | 1.54KB |
| types (theme.js) | 6.28KB | 2.87KB |
| types (ui-action.js) | 8.11KB | 3.32KB |
| types (views.js) | 0.20KB | 0.18KB |
| types (widget.js) | 0.20KB | 0.18KB |
Size Limits
- ✅ Core packages should be < 50KB gzipped
- ✅ Component packages should be < 100KB gzipped
⚠️ Plugin packages should be < 150KB gzipped
✅ 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
|
Fixes #8802
Fixes #8257
Fixes #8008
Fixes #Non purpose. The cross-repoform (
Fixes owner/repo#N) is honoured by GitHub but NOT by this seat's landingpre-check, whose
closingKeywordReaccepts only the bare form — on the previoushead
check-clause2-carriers.mjs --pair 8865therefore exited 2 =UNJUDGED, no pair formed, and the gate could not answer at all. ⛔ Do not
"normalise" them back.
Executes three maintainer rulings taken in one batch on 2026-09-09. ⛔ Nothing about the direction is re-opened here.
Clause-②: yes— this narrows published accept sets. An in-seat contract-review-tier PASS is the PM's to run, and landing is the PM's and the maintainer's call.⛔ CORRECTION, 2026-09-10 — this paragraph previously said the PR "stays draft" and that "
needs:contract-reviewremains hung". Both are now FALSE and are corrected rather than reversed. At05:05:42Za maintainer flipped this PR out of draft and removed theneeds:contract-reviewlabel. Neither act was this seat's; ⛔ neither is reversed here — the label is not re-added and the draft state is not flipped back. What the removal did not change is the declaration itself:PM_SWEEP_REPO=objectstack-ai/objectui node scripts/pm/check-clause2-carriers.mjs --pair 8865exits 0 on the current head, agreeing on all three cards (#8802, #8257, #8008), with--self-test→ 0 (299 cases) as its control. That gate reads theClause-②declaration in both carriers, ⛔ not the label, which is why removing the label did not break the pair.🔁 Merge-repair round, 2026-09-10 — current
mainmerged in, and the semantic collision that exposedA maintainer's merge attempt was rejected. ⛔ Not a textual conflict —
git merge-tree --write-treeexits 0 and a realgit merge --no-ff origin/mainauto-merges with 0 unmerged paths. The failure is semantic and appears only on the MERGED tree, which is what the queue builds and what amergeable_statecomputed against a stale base cannot see.mainis now merged in, as merge commits — ⛔ no rebase, ⛔ no force-push, ⛔ no amend, because other agents and the reviewer's records reference this branch's existing commits. Two of them, becausemainmoved again during verification, which is the round's own lesson arriving twice:33874de20(mergingorigin/mainatf4abff499, the 30-commit window) and59489bcfc(merging8ea3beee4, three more).The window reading, corrected. This body said 13; the diagnosis comment said 29; measured immediately before taking the merge it was 30 —
mainmoved between the last two readings, which is the whole point of this round. After both merges:git rev-list --left-right --count origin/main...HEAD=0 10— zero behind.comm -12of the two three-dot diffs). Grep of all three commits' files for a retired key or the quick-add surface returned 3 hits, every one cleared with its own reading:packages/i18n/src/locales/{en,zh}.tsis a message about the timeline's unsupportedganttvariant, ⛔ not a node type;skills/objectui/guides/page-builder.md(PR #8916 — the governed file this PR deliberately split out, now landed onmainon its own) spellsganttasobject-gantt's nested config block key, ⛔ not a node type. That clearance was then converted from a grep into a run: the whole affected surface is re-run green on the second merged tree, below.The failure, reproduced before anything was changed
packages/plugin-kanban/src/__tests__/quickAddIsDiagnosedNotDropped-8285.test.ts— exit 1, 4 failed / 5 passed on the merged tree:⭐ That file does not exist on this branch's pre-merge head and is 9/9 green on pure
main. It landed with objectui#8922 (card objectui#8285) after this PR's base — and objectui#8922's own commit message anticipated the day: "the contract-side refusal retiresobject-kanban.quickAddfrom the spec'sComponentPropsMap, and this module goes with it. The live pin's spec row reddens on the day that lands."The fix, and why it is minimal
The retirement is ruled and ⛔ is not re-opened:
kanban,kanban-ui,kanban-enhancedandganttstay retired. ⛔ The test is not deleted, not skipped, not disabled. Its subject — the html tier NAMES the Quick Add pair instead of dropping the key — is still true and stays pinned; only the set of tags it holds over was wrong, and it shrinks to the one key that resolves.packages/sdui-parser/src/kanban-quick-add.tsQUICK_ADD_HOST_TYPES→{ 'object-kanban' }; the remedy sentence namesKanbanRendererinstead of the retiredkanban-uitagpackages/sdui-parser/src/__tests__/kanban-quick-add-8285.test.tskanban-uicontrol becomes anit.eachover both retired spellings, asserting neither is a hostpackages/plugin-kanban/src/__tests__/quickAddIsDiagnosedNotDropped-8285.test.tsKANBAN_TAGSlosesview:kanban; the registration row expects 1 key, andComponentRegistry.has('kanban-ui')flipstrue→false— kept as a reading, ⛔ not deletedcontent/docs/plugins/plugin-kanban.mdxkanban-uiblock.changeset/8802-…-retirement.md@object-ui/sdui-parser, and states the narrowing and the message move⭐ The
QUICK_ADD_HOST_TYPESdecision, and the measurement behind itDecision:
kanbanleaves the set. Not a judgement call in the end — a reading.checkKanbanQuickAddhas exactly one call site (whole-tree grep):validate.ts's per-prop walk. That walk lives in theelseofconst comp = manifest.components[node.type]; if (!comp) { … }— so for a tag the manifest did not resolve, the props are never walked at all. Measured on the merged tree against a manifest built from the live registry, the way the page compiler builds its own:["plugin-kanban:object-kanban", "object-kanban"]— nokanban, nokanban-uivalidateTreeon{ type: 'kanban', quickAdd: true }["error/unknown-component"]— and nothing elsevalidateTreeon{ type: 'object-kanban', quickAdd: true }["warning/inert-quick-add"]⇐ FIRING CONTROL, so the row above is a reading and not a broken probevalidateTreeon{ type: 'kanban-ui', quickAdd: true }["error/unknown-component"]checkKanbanQuickAdd('kanban', 'quickAdd', true)called directly'inert-quick-add'— the member is live in the function and unreachable through its only call site⇒ Keeping
kanbanwould be dead data. And ⛔ nothing is silently dropped by removing it: the retired spelling is refused by name at the tag, as anerror, which is a louder answer than thiswarning; stacking both would be the two-diagnostics-for-one-mistake shapecheckMemberTypesalready refuses (objectui#8067). The counter-argument — "the sdui tier parses, it does not resolve, so it may still want to diagnose the retired spelling" — is answered by the same measurement: this tier does gate the prop walk on manifest resolution, and the only way akanbanentry can still be reached is a hand-built manifest declaring a component of that name, which after the retirement is somebody else's component, about which this message's claims (ObjectKanbansupplies noonQuickAdd) would be false. ⛔ Nothing is widened; the set strictly shrinks to what resolves.Verifying the constant deliberately — the ablation
packages/sdui-parser's suite builds a synthetic manifest and would therefore not catch a change toQUICK_ADD_HOST_TYPES. That premise is half false, and the half that is true is now covered. The suite carries one row that hard-codes the set (the host set is …, and is not empty), deliberately, as anti-vacuity — every other row isit.each(HOST_TAGS)and re-derives itself from the constant, which is exactly the blind spot named.Predicted direction before running: turns red. Ablation — put
'kanban'back into the constant, source only, tests untouched:git hash-object57e10ca01→e0286b7f4; kept-spelling anchor 1→0, mutated-spelling anchor 0→1vitest.config.mts:500aliases@object-ui/sdui-parser→packages/sdui-parser/src, so the mutated source is what loadscontrol: kanban is NOT a hostrowgit checkout HEAD -- PATH, hash back to57e10ca01= the HEAD blob,git diff HEAD -- PATHempty.trap … EXIT INT TERMwith absolute paths⇒ the constant is now read by two rows inside
packages/sdui-parseritself, not one, so the next narrowing cannot pass silently there either.⭐ The importer census, re-taken against CURRENT
mainThis is the round's real lesson, and it is the same class as F1 — "a retirement's blast radius is its IMPORTERS, not its own package" — with one difference that F1 could not have taught: the importer did not exist when that census was taken. ⇒ a retirement that sits open across other merges must have its census re-taken against current
main, never against its own base.Re-taken on
origin/mainf4abff499. Query: every filemainhas changed or added since this PR's base60e1f80b2that spells a retired node key as a quoted literal.mainchanged since the basekanban/kanban-ui/kanban-enhanced/gantt)object-kanbanThe 9, each disposed of by inspection rather than by count:
packages/plugin-kanban/src/__tests__/quickAddIsDiagnosedNotDropped-8285.test.tsindex.tsx's registration calls. Fixed herepackages/sdui-parser/src/kanban-quick-add.tspackages/sdui-parser/src/__tests__/kanban-quick-add-8285.test.tscontent/docs/plugins/plugin-kanban.mdxkanban-uias a live block. Fixed herecontent/docs/api/schema-reference.mddefaultViewType: "kanban"), the layer this PR explicitly leaves alone, and this file already says so in its own notepackages/core/src/data-scope/__tests__/element-data-source.test.tstype: 'kanban'is a saved view, whose output field isviewType: 'kanban';packages/coreregisters no pluginpackages/types/src/__tests__/object-view-unmirrored-keys-7779.test.tsViewKindenum ('grid' | 'kanban' | … | 'gantt'), same stored-view layerpackages/types/src/layout.tspageType: 'kanban', same layerpackages/plugin-kanban/src/ObjectKanban.tsxTwo near-misses cleared with their own controls, because a name-match is not a reading: the plugin doc's
PluginLoader plugins={['kanban']}is the plugin package vocabulary (calendar,charts,chatbot, … — every plugin doc spells its own short name that way), ⛔ not a node type key; and the whole-tree symbol sweep forQUICK_ADD_HOST_TYPES/INERT_QUICK_ADD/checkKanbanQuickAddreturns only the three files fixed here.Blast radius of the fix itself, bounded the same way:
quickAddappears outside the two changed packages in exactly 6 files —packages/types(3),apps/console/src/__tests__/registry-inputs-spec-parity.test.ts, and the two docs — and every one of them is in the green re-runs below.Green on the MERGED tree — every exit code captured into a file BEFORE any pipe
vitest run packages/plugin-kanban/ packages/plugin-gantt/ packages/runner/vitest run packages/sdui-parser/vitest run packages/types/vitest run apps/console/src/__tests__/registry-inputs-spec-parity.test.tsturbo run type-check --concurrency=2(whole repo)error TSpnpm --filter @object-ui/sdui-parser lint> eslint .;0 errors, 7 warningspnpm --filter @object-ui/plugin-kanban lint> eslint .;0 errors, 172 warnings.errorstrings in either log are the0 errorssummary and two warnings whose message text begins "Error:"check-changeset-presence/-no-major/-overwrite/-fixedmajor, no pre-existing changeset modifiedcheck:doc-types·check:doc-fences·check:doc-snippets·check:doc-examples·check:doc-example-readerscheck:sdui-registration-pinspnpm --filter @object-ui/console buildfirstcheck:readme-exportspnpm buildfirst", all naming@object-ui/cli/@object-ui/plugin-ai, packages this diff does not touch. That is a PRECONDITION, not a red: after building those two it is 0 with 0 unbuiltcheck:control-bytes·check:handler-key-reads·check:unreferenced-sources·check:self-import·check:vi-mock-specifiers·check:eager-closureRe-run on the second merged tree (
59489bcfc), aftermainmoved three more commits:vitest run packages/plugin-kanban/ packages/plugin-gantt/ packages/runner/ packages/sdui-parser/vitest run packages/types/ apps/console/src/__tests__/registry-inputs-spec-parity.test.tscheck-changeset-presence/-no-major/-overwrite/-fixed·check:control-bytesturbo run type-checkand the doc/console gates were run on the first merged tree (9b36ffdb2) and ⛔ not re-run after the second merge: those three commits touchplugin-chatbot,data-objectstack,app-shell,i18nand twoscripts/, none of which this diff touches, and each was type-checked by CI on its own way intomain. Stated as a NARROWING, ⛔ not claimed green.apps/console,packages/components,packages/app-shell,packages/react,packages/coreand the otherpackages/plugin-*suites;examples/*;turbo run buildwhole-tree;turbo run lintwhole-farm; e2e. Those are CI's, and the narrowing above is what bounds the risk: the changed public face is one exportedSetand one message string, and the whole-tree sweep for readers of either returns only files that ARE in the runs above.🔁 Rework round — what the REWORK verdict asked for, and what changed
⛔ CORRECTION, 2026-09-10. This section previously said the verdict "returned
six findings" and tabulated F1–F6. That was wrong, and it was wrong because of
a PM error, not a reviewer one. The
contract-review-tier verdict (REWORK, PR comment
5610751162) returned ninenumbered items: eight findings, F1–F8, plus F9 "verified, no action" — of
which F1 and F2 were the blockers. The PM truncated the verdict at F6 when
dispatching the repair round, so F7 and F8 were never seen by that round and
this body inherited the truncated count. F7 was closed by the same round
incidentally (it asked for exactly the refresh that round performed); F8 needed
a round of its own, which is the commit
cf31b51edthis body now stands on.The verdict reopened nothing about the retirement itself. ⛔ The reasoning,
the measurements and the ablation record below are unchanged; what follows
was added, and the sections that carried stale numbers were corrected in place.
packages/runnerwas outside the previous round's sweep and outside both--filtertype-checks, sopackages/runner/src/plugin-integration.test.tsstill readkanbanComponents.kanbanafter this PR removed that keykanbanComponents['object-kanban']with a two-directional control; reproduce→green pair belowFixes #N(see the note at the top)type: 'kanban'in two places the gates structurally cannot seeplugin-gantt/README.md— see belowKanbanEnhanced@object-ui/componentsadded, and@object-ui/runnerwith itc695a1c8dwhile head was4dcf5445; the "Governed surface" section still claimed GOVERNED/1 path, false after the split; and "3 JSON fixtures" was stated where only 2 changed616973214, rewrote the governed-surface section to its post-split reading, and added the third-fixture paragraph naming the view-type vocabulary collisioncontent/docs/api/schema-reference.md'sObjectKanbanSchematable omitteddata/bindalthough the migration path this PR teaches depends on"data": []; andcontent/docs/plugins/plugin-kanban.mdxstill authoredonCardMoveinside typedObjectKanbanSchemadocument literals, contradicting the README this PR had just correctedcf31b51ed— see the F8 section belowtype:'kanban'read sites, and theretiredNodeType()mechanism deviation, all checked and all correct⇒ The lesson this round is paying for, stated so it is not re-learned: a
retirement's blast radius is its IMPORTERS, not its own package. The importer
census that now backs the sweep is in the Verification section.
⇒ And the second lesson, which is the PM's: a verdict is read to its last
finding before any of it is dispatched. Truncating at F6 cost this PR an extra
round and left this body contradicting the verdict it was reporting.
F3, widened by the same reasoning —
plugin-gantt/README.mdThe verdict named
plugin-kanban/README.md. Reading the other retiredfamily's README on the same instrument found the identical defect in a file
this PR already edits, so it is fixed here rather than filed:
const … = { type: 'gantt', … }literals (README lines 233, 260,445, 582 on the pre-fix head) still authored the retired key.
inside
```typescriptfences and the gates are green over them — anuntyped object literal compiles either way, which is the same blindness
that let the kanban README's "Automatic Registration" block through. ⛔ A gate
cannot find the rest of this class; only reading the file can.
type: 'gantt'"is a registered type, so thenode mounts and simply finds nothing to draw". This PR makes that false.
Re-pointed at
object-gantt, which is what the sentence is now true about.content/docs/plugins/plugin-kanban.mdximportedObjectKanbanSchemafrom@object-ui/plugin-kanban, which has never exported it (@object-ui/typesdoes,
@object-ui/types/zodcarries the mirror). Split into two imports.it: a
plaintextfence that opens with a comment is deliberately not counted bycheck:doc-fences, so writing the note inside would have driven that file'sKNOWN_UNHIGHLIGHTED_TS_FENCESrow to 0 and demanded a ledger edit — by hidingthe block from the detector rather than by highlighting it. Measured, not
reasoned: the first attempt did exactly that and the gate went exit 1 with
baselined at 1, now carries 0; moving the note out returned it to exit 0with the ledger reading 80 files / 89 blocks, unmoved. That red is also this
gate's firing control.
F4 — what was false, and what is true
Both the changeset and
plugin-kanban/src/index.tsxsaidKanbanEnhanced"isstill exported and still importable from
@object-ui/plugin-kanban/KanbanEnhanced".That subpath has never resolved. This package's
exportsmap has exactlytwo entries —
.and./style.css— and the barrel never re-exported thecomponent. After this PR
KanbanEnhanced.tsxhas zero non-test importers(
cardPredicateScope.test.tsxreaches it by relative path; onevi.mocknamesit).
KanbanRenderer, by contrast, is exported from the package entry, andonly its registry key is gone — the two were wrongly stated as one case.
⛔
KanbanEnhanced.tsxis deliberately left in place. Deleting published-but-unreachable source is a FURTHER narrowing and needs its own maintainer
ruling, which this PR does not carry. The reading is reported; it is not acted
on here. The verdict asked for a card for the dead module: it is
objectui#8932.
F6 — the changeset now names every released package whose source moved
check-changeset-presence.mjscounts 33 source files of 7 released packagesin this diff. The changeset named 6.
@object-ui/componentswas missing(
src/renderers/complex/README-KANBAN.md— the presence rule counts.mdunder
src/), and this round's own edit adds a seventh,@object-ui/runner.Both at
patch: neither package's published, executable bytes move, and thefixed group bumps them regardless. 7 named = 7 changed.
F8 — the two doc surfaces the truncated dispatch never reached
Closed on
cf31b51ed. ⛔ No schema face, behaviour or test changed anywhere—
git diff --cachedconfirms the commit touches exactly two files undercontent/docs/**and nothing underpackages/**.content/docs/api/schema-reference.md. TheObjectKanbanSchemasection(re-derived at
:913–:949on this head) omitteddataandbindwhile theretirement's own migration note requires
"data": []for a purely staticboard. One prose blockquote added after the existing
columnscollateral-classnote, in the same style, stating that both ride
BaseSchemarather than beingnarrowed on
ObjectKanbanSchema, and naming the renderer's own record-sourceladder.
around: on this head
bind(base.ts:248) anddata(base.ts:184) areboth named, JSDoc'd
BaseSchemamembers —bind's own doc already gives anobject-kanbanexample — rather than bare index-signature riders. objectui#6357landed that before this PR opened. The verdict's remedy (document in prose,
touch no schema face) is unaffected by the nuance and is what was done.
content/docs/plugins/plugin-kanban.mdx.onCardMovewas authored insidetyped
ObjectKanbanSchemaobject literals in five places (Basic Usage~
:44; the Schema API shape ~:79–:92and its Properties table ~:109–:115;Project Task Board ~
:196; Event Handling'suseBoardhook ~:396) — allre-derived on this head, and the dispatch's README anchor had moved too, from
:99–:104to:130. The real wiring was traced (ObjectKanban.tsx/index.tsx'sKanbanRenderer) to confirm that an authoredonCardMoveisnever read on the registered
object-kanbanpath — it is always overwritteninternally — which is what
packages/plugin-kanban/README.md's correctedsentence already says. Every occurrence now splits
onCardMoveinto its ownbinding with the README's disclaimer; the Properties-table row was removed
rather than kept-and-reworded, because a runtime prop does not belong in a
table of document properties.
"### Properties" table (~
:115–:120) omitstype,groupBy,dataandobjectName, all present in the Schema API shape immediately above it. That isan incomplete listing, not a copy-and-it-fails defect. Whoever next edits that
table is its natural owner.
⛔ The trap this change had to avoid, and the measurement that says it did
The strings
kanbanandganttname two different things at two different layers. Only one is retiring.NamedListView.type"kanban","gantt"CreateViewDialog, persisted in every tenant's databasekanban,ganttRe-derived on this head rather than taken from the brief:
packages/plugin-view/src/ObjectView.tsx'sswitch (viewType)is the seam, and it already emitsobject-kanbanandobject-gantt— twelve emitted node types, an unbrokenobject-*family, which is the firing control that makes those two readings rather than lone matches.⇒ Every kanban and gantt view any user ever created through the console already renders through the surviving spelling, so this retirement moves zero stored documents.
ObjectView.tsx'scase 'kanban':/case 'gantt':andCreateViewDialog.tsx's type values are unchanged —git diff --statnames neither file. Two compile-time pins guard the layer against a future migration that pattern-matches the string:Which case each of the four names was, with the measurement
Measured whole-repo on this head, PCRE with a lookbehind so
viewType:and friends cannot be counted as node keys. Controls on the same instrument:object-grid(2 JSON / 128 line-occurrences in TS) fires,zzz-not-a-type(0) is silent.kanbanKanbanSchemaincomplex.ts, its Zod mirror,SchemaRegistry['kanban']kanban-uikanban-enhancedganttganttas 0 JSON / 0 TS. On this head the node-key spelling appears in 43 line-occurrences across 25 TS files — 24 test files insidepackages/plugin-ganttplus itsdemo/main.tsx. Every one of them hands the node object directly to theObjectGanttcomponent with anas anycast; none resolves throughComponentRegistry(probed forSchemaRenderer/ComponentRegistry/validateTree/resolveComponentacross all 25, with theobject-ganttfile set as the firing control, which does hitComponentRegistryin 4 files). ⇒ The ruled route is untouched — the registration had zero resolved consumers — and the ruling's own follow-up paragraph anticipated exactly this by telling the implementing seat to re-derive the write/store surfaces. The count is wrong; the conclusion it priced is not.rather than left reading as three migrations.
examples/schema-catalog/src/schemas/components-complex-view-switcher/view-switcher.json:16carries
"type": "kanban"as a ViewSwitcherviews[].type— the view-typevocabulary (
ViewType,packages/types/src/views.ts), which is what aswitcher tab names, not a node type; the nested
schemain the same entry isits own document.
git diff --statover that path is empty. The samevocabulary collision is now declared, with its reason, in
scripts/check-doc-component-types.mjs'sDOC_TYPE_EXEMPTIONS— until thiscard the value passed by coincidence, the two vocabularies sharing one spelling.
⭐ The namespaced spellings retire too —
view:kanbanandview:ganttComponentRegistry.register(type, C, { namespace })stores both thenamespaced key and a bare-
typefallback (packages/core/src/registry/Registry.ts),so every retired key had a twin that goes with it. Neither the changeset nor
this body named them before this round:
kanbanview:kanbanobject-kanbanganttview:ganttobject-ganttkanban-uiplugin-kanban:kanban-uiobject-kanbankanban-enhancedplugin-kanban:kanban-enhancedobject-kanbanBoth spellings are pinned as gone — each against a firing control on the
surviving key — in
kanban-family-registry-keys-retired-8257.test.tsandbare-gantt-node-key-retired-8008.test.ts, measured throughgetConfig(key, ns)so that a retirement removing only one spelling would fail.
What an unmigrated
view:kanban/view:ganttnode now renders depends on thehost. In
apps/consoleit renders the protocol placeholder panel, not theOBJUI-001 "Unknown component type" error: the console calls the opt-in
registerPlaceholders()(@object-ui/components,renderers/placeholders.tsx)after its plugin registrations, both keys are in that file's
PROTOCOL_COMPONENTSlist, andregisterPlaceholderonly claims a key nothingelse has taken — which, until this change, the two plugins had. In every other
host, which does not call that bootstrap, the same node renders OBJUI-001.
objectui checkwill not flag eithernamespaced spelling. The CLI's
known-schema-types.tsis GENERATED from therepository's real registration calls, and the placeholder registration is a real
one — so
view:kanbanandview:ganttare still on that list and stillvalidate green, while the node renders a placeholder rather than a board.
The bare
kanban/ganttentries DID leave the generated list; only thenamespaced pair survives, and only because of the placeholder. ⇒ Grep documents
for the namespaced spellings directly; do not rely on
objectui checkto findthem. The migration note in the changeset says so.
The mechanism — why
kanbanis a refusal and the other three are deletionsBaseSchemacloses with[key: string]: anyandBaseSchemaCoreends.passthrough(), so a dropped member key is KEPT, not refused.AnyComponentSchemais a discriminated union (objectui#8498), so it selects one arm from the authored literal and an unclaimed literal matches nothing — deleting the arm would already refuse. What deleting would not do is name a remedy: the union answers a missed discriminator with its ownInvalid input.So a new
retiredNodeType()joins the three refusal helpers inzod/tombstone.zod.ts, and the arm survives to carry the message. Measured on zod 4.4.3, through the realsafeValidateSchema:⭐ objectui#8818 — the ENTRY is shut, ⛔ the CLASS is not
SchemaRendererstrips a fixed enumerated metadata list and spreads the rest as React props;objectFieldsis not on that list, andKanbanRenderer— the componentkanban-uiresolved to — declaresobjectFieldsas a real prop. Retiring the registration means no authored node can reach that component through the registry, which shuts that path.objectFieldsprop. That card's option (a), stripping at theSchemaRendererboundary, is what would shut the class. Said in the changeset and pinned inkanban-family-registry-keys-retired-8257.test.ts. ⛔ That card itself is not edited or touched by this PR; the PM updates it — done at objectui#8818 comment5611940366.kanbanarm took with it — the cost of this change, statedThat arm was the only schema face that ever declared
columns,cardTitle,swimlaneField,groupingandnavigation; the only one that refusedallowCollapse/cardTemplates/columnWidths/titleField/draggable/onColumnAdd/onCardAddby name; and — throughcolumns: KanbanColumn[]— the only one that judged a lane'scards(objectui#6939). The survivingObjectKanbanSchemadeclares none of them.⛔ No
object-kanbandocument changes meaning: it was never judged by thekanbanarm, so every one of those keys has always riddenBaseSchema's index signature there. What is gone is thekanbandocument that had them. Declaring them onObjectKanbanSchemawould WIDEN a published accept set, which is a ruling and not a repair — so each flipped reading is pinned where it can be seen instead of quietly disappearing:examples/schema-catalog/test/kanban-column-cards-6939.test.tsx—columns[].itemsnow parses green; four probes measured with a firing control (the surviving arm's owngroupFieldtombstone still refuses).packages/plugin-kanban/src/__tests__/kanban-handler-slots-7664.test.tsx—onCardMove/onCardClick/onQuickAddare still forwarded and are now declared on no face; firing controlgroupByreadstrueon the same shape lookup.packages/types/src/__tests__/handler-keys-json-refusal-6124.test.ts— the ledger shrinks 67 → 62, with the arm-vs-key distinction written where a future reader meets it.zod-mirror-parity.test.ts— KnownDrift 41 → 40 entries, 64 → 61 keys.packages/types/src/registry.ts— measured and reported:SchemaRegistryhas never carried anobject-kanbanentry, so the kanban family now has no entry in that map at all (objectui#7665's territory, not this card's).⇒ The restore-the-judging follow-up is filed as objectui#8913 and is
pm:blockedon this PR.Verification
round. The measurements below are NOT rewritten.
origin/main(60e1f80b2)was merged in as
6ed28c04f; the rework round landed616973214on top; the F8round landed
cf31b51ed, which is this PR's head. All are append-only —c695a1c8d,4dcf54452,6ed28c04fand616973214are all still ancestors —the merge resolved zero conflicts, and the push
616973214..cf31b51edwas aplain fast-forward with ⛔ no force-push.
Which head each reading stands on, stated rather than left to be inferred:
the pre-merge heads and stand as taken.
616973214.cf31b51edadds twocontent/docs/**files and nothing else — nopackages/**path moved — so everypackages/**reading above carries overunchanged. The readings that could move with a docs edit were re-taken on
cf31b51edand are listed in their own subsection below.Every exit code was captured into a file BEFORE any pipe (
cmd > file 2>&1; EXIT=$?), never off acmd | tailpipeline.Ablation — one leg per retired name, five legs
Each leg: prove the pin green at rest, restore the accepted spelling, prove the mutation on disk before reading any result (
grep -c -Fon both the injected and the removed text, plus agit hash-objectthat must differ from the HEAD blob), run the pin, restore withgit checkout HEAD -- ABSOLUTE_PATHfrom atrap … EXIT INT TERM, and prove restoration by blob hash — never by an exit code. Non-vacuity is the before/after pair.kanban(named refusal)types/src/zod/complex.zod.ts7f3468a7vs HEAD13f47d7e13f47d7ekanban-ui(registration)plugin-kanban/src/index.tsxc8fc541evs HEAD1032494210324942kanban-enhanced(registration)plugin-kanban/src/index.tsxaa9c683b10324942kanban(registration)plugin-kanban/src/index.tsxba2c9a0010324942gantt(registration)plugin-gantt/src/index.tsx56454b75vs HEAD7d6e1f327d6e1f32git statusis clean after the run, and the harness'straprestores on any exit — including the container's ten-minute foreground SIGTERM landing mid-mutation.Tests
vitest run packages/types/ packages/plugin-kanban/ packages/plugin-gantt/ examples/schema-catalog/ apps/console/ packages/cli/ scripts/packages/types/after the final commitapps/console/src/__tests__/registry-inputs-spec-parity.test.ts, run explicitlypnpm --filter @object-ui/types type-check,--filter @object-ui/plugin-kanban type-checkThe console sweep is in the first row;
registry-inputs-spec-parityis named separately because retiring registrations moves exactly what it checks.Gates
check:doc-types0 ·check:doc-snippets0 ·check:doc-examples0 ·check:doc-fences0 ·check:handler-key-reads0 ·check:sdui-registration-pins0 ·check:readme-exports0 ·check:skills-paths0 ·check:skill-examples0 ·check:skill-eval-tokens0 ·check:eager-closure0 ·check:element-data-source-declaration0 ·check:published-dist0 ·check:dist-completeness0 ·check:spec-symbols0 ·check:governed-queue-guard0 ·check:unreferenced-sources0 ·check:doc-example-readers0 ·check:vi-mock-inherit0 ·check:i18n-keys0 ·check:control-bytes0 ·check-changeset-presence0 ·check-changeset-fixed0 ·check-changeset-no-major0 ·check-doc-links0 ·pnpm check0 ·turbo run lint --concurrency=20 — 47/47 tasks, 0 errors across the whole farm.check:doc-typesfound two doc pages the grep sweep had missed, andcheck:readme-exports/check:doc-snippetsfoundpackages/plugin-kanban/README.mdstill importing the retiredKanbanSchema.Governed surface
node scripts/check-governed-queue-guard.mjs --testover all 50 changed paths (49 + the runner test the rework round re-keys), re-run on616973214: NOT GOVERNED — exit 0, 50 paths checked against 5 governed surfaces, none matched. Firing control on the same probe: the split-outskills/objectui/guides/page-builder.mdalone still reads GOVERNED, exit 3.cf31b51ed: that commit edits two files already inside the 50-path set (content/docs/api/schema-reference.md,content/docs/plugins/plugin-kanban.mdx), so the changed-path set neither grew nor gained askills/**entry.⇒ The governed-surface reason for parking is gone — that file left with PR #8916 and this PR carries no
skills/**path.⛔ CORRECTION, 2026-09-10. The rest of this paragraph said the PR "stays draft" and that "
needs:contract-reviewremains hung on this PR and on all three cards". Both were true when written and are false now: a maintainer flipped the PR out of draft and removed the label at05:05:42Z. ⛔ Neither is reversed here. What is unchanged: this seat does ⛔ not enqueue, ⛔ not enable auto-merge, ⛔ not merge and ⛔ not flip the draft state in either direction. Landing is the PM's and the maintainer's.objectui#8614 ledger hazard
scripts/check-doc-example-types.mjskeysUNGATED_EXAMPLESbyfile:line, and this diff shifts lines inpackages/types/src/complex.tsand — in the rework round — inpackages/plugin-kanban/src/index.tsx. Re-derived on616973214by importing the ledger rather than eyeballing it: 90 rows across 74 distinct files; the intersection with this diff's 50-file changed set is empty, so no row needs re-keying. Two controls on the same membership test: a file that IS in the ledger (packages/auth/src/AuthGuard.tsx) readstrue, and a file that cannot be (zzz/not-a-file.ts) readsfalse— so the empty intersection is a reading and not a parser that found nothing.check:doc-examplesexits 0.Re-derived again on
cf31b51edafter the F8 edit, by the same import-the-ledger method and the same two controls: 90 rows / 74 distinct files, changed set still 50 files (both edited files were already in it), intersection still empty.scripts/*.mjsforpath:line-shaped keys returns exactly one file, the one above.Base to main pre-flight
Re-taken on
616973214, and the earlier reading is superseded, not amended:mainhas moved again. The window is NOT empty — 13 commits, so the control can fire — and this time the intersection is NOT empty either:packages/plugin-kanban/src/ObjectKanban.tsxis in both sets. PR fix(plugin-kanban): read the cardTitle/titleField pair once, through one resolver #8917 (objectui#8308, thecardTitle/titleFieldsingle-resolver change) landed onmainas4fa0eb9c4; the contract review had flagged that overlap and asked for a stated order, and fix(plugin-kanban): read the cardTitle/titleField pair once, through one resolver #8917 went first.git merge-tree --write-tree HEAD origin/mainexits 0 (clean, tree33eb6e6d4), so there is no textual conflict to resolve. ⛔ No merge is taken here — the merge queue rebuilds onmainand would catch one. The reading is reported so the ordering is on the record.commis really comparing.⛔ SUPERSEDED, 2026-09-10 — every window figure in this subsection is a reading on an OLD head.
mainhas moved 30 commits since60e1f80b2, and currentmainis now merged into this branch; the authoritative window reading is in 🔁 Merge-repair round above. Kept as history, ⛔ not as a current reading.Re-derived on⚠️ ⛔ not a raw two-dot
cf31b51ed, with the instrument itself corrected: merge-base(HEAD,origin/main) =60e1f80b2(unchanged), window 13 commits (mainhas not moved further), and the true intersection computed via two..-scoped diffs from the real merge-base —HEAD..origin/maindiff, which conflates unrelated same-file edits and is what an earlier round reached for. Result: exactly 1 file,packages/plugin-kanban/src/ObjectKanban.tsx, already known and already accepted. Self-comparison control (the diff set against itself) = the full 50, confirming thecommmethodology.git merge-tree --write-tree HEAD origin/mainexit 0, same clean tree33eb6e6d4— no new conflict introduced.🔁 Rework round — F1's reproduce-then-green pair
⛔ A fix with no reproduced failure is a guess that compiled, so both legs were
run on this branch before and after the re-key. Every exit code captured into a
file BEFORE any pipe.
6ed28c04f)616973214)turbo run type-check --filter=@object-ui/runnerTS2339: Property 'kanban' does not exist on type '{ 'object-kanban': FC of ObjectKanbanComponentProps; }'atsrc/plugin-integration.test.ts(19,31),(23,42),(48,29),(49,76)vitest run packages/runner/src/plugin-integration.test.tsexpected 'undefined' to be 'function'at:24,expected undefined to be definedat:19and:53GitHub's body sanitiser eats angle-bracket-shaped fragments, backticks and code
fences included.
Shard membership is reported as NOT MEASURED, not as green. The verdict
placed the failing file in
Test (shard 2/4);vitest list --shard=2/4did notfinish inside this container's foreground window (killed at 480s, exit 124), so
that placement is the reviewer's reading and not one this round re-took. What
was re-taken is the failing file itself, red then green.
Ablation — the new control can fail, and only for its own reason. The
control asserts
Object.keys(kanbanComponents)is exactly['object-kanban']and that the three retired spellings resolve to nothing; asserting the survivor
alone would pass just as well against a map that had been renamed wholesale.
Mutation: re-introduce a bare
kanban: ObjectKanbanentry inplugin-kanban/src/index.tsx. Proven on disk before any result was read —injected line count 1, blob
6947d63avs HEAD blob4e8b86c8— and restoredfrom a
trap … EXIT INT TERMwithgit checkout HEAD -- ABSOLUTE_PATH, withrestoration proven by blob hash back to
4e8b86c8and an emptygit diff HEAD,never by an exit code. Result: exactly 1 test failed (the new control, on
[ 'object-kanban', 'kanban' ] to deeply equal [ 'object-kanban' ]), 6 passed.No build is interposed: the root Vitest config aliases
@object-ui/plugin-kanbanto
packages/plugin-kanban/src, so the mutated source is what ran.🔁 Rework round — importer census
The census, not the diff, defines the sweep. Every package that imports from
@object-ui/plugin-kanbanor@object-ui/plugin-gantt, derived two ways —package.jsondependency declarations, and a tracked-file grep for the importspecifiers — then confirmed present in the sweep:
packages/runnerimport { kanbanComponents }— the only consumer of that map anywhereapps/consoleregisterLazy(…, () => import(…))for both pluginsapps/siteimport()per plugin name inPluginLoaderexamples/console-startermain.tsxexamples/schema-catalogpackages/app-shellplugin-kanbansource off disk by pathpackages/clipackages/sdui-parserscripts/gen-manifest.tsTwo readings worth keeping:
kanbanComponentshas exactly one consumer in thewhole tree — the runner test F1 fixes — and
ganttComponentsdoes notexist (
plugin-ganttpublishes no components map; its README says so). Every'kanban'/'gantt'string literal in the importer packages was re-checkedand is stored-view-type layer,
allowedVisualizationsvocabulary, or —in
apps/site— that loader's own local union of package names, none of thema registry node type key.
🔁 Rework round — verification on
616973214Tests, run from the repo root with paths relative to it.
packages/app-shelldoes not fit this container's foreground window as one run (669 test files), so
it was run as four complete shards rather than narrowed: 168 + 167 + 167 +
167 = 669, which is exactly its tracked test-file count, so nothing was skipped.
vitest run packages/plugin-kanban/ packages/plugin-gantt/ packages/runner/vitest run packages/app-shell/ --shard=N/4, N = 1…4vitest run packages/sdui-parser/vitest run apps/console/ packages/cli/ examples/schema-catalog/ examples/console-starter/vitest run packages/types/ scripts/turbo run type-check --concurrency=2— whole repo, the CIType Checkjob's own commandcheck-type-check-coverage,tsc -p tsconfig.scripts.json,tsc -p tsconfig.vitest-setup.jsonturbo run build --filter=!@object-ui/site --concurrency=2turbo run lintover the three packages whose source changedGates, all on
616973214, each exit code captured before any pipe:check:doc-types0 ·check:doc-snippets0 ·check:doc-examples0 ·check:doc-fences0 ·check:readme-exports0 ·check-doc-links0 ·check:handler-key-reads0 ·check:sdui-registration-pins0 ·check:unreferenced-sources0 ·check:element-data-source-declaration0 ·check:side-effects-array0 ·check:doc-example-readers0 ·check:spec-symbols0 ·check:dist-completeness0 ·check:published-dist0 ·check:phantom-deps0 ·check:self-import0 ·check:vi-mock-specifiers0 ·check:vi-mock-inherit0 ·check:i18n-keys0 ·check:control-bytes0 ·check-changeset-presence0 ·check-changeset-fixed0 ·check-changeset-no-major0 ·check-governed-queue-guard --test0.reported as such rather than as passes**:
check:doc-examples,check:readme-exports(which additionally printed "the population COLLAPSED —this run proves nothing", 15 of 40 packages readable) and
check:sdui-registration-pins(no console bundle to weigh). All three werere-run after the builds they name and are the zeros listed above; the
readme-exportscensus then read 36 of 40 packages, 535 self-imports judgedreal, 0 fabricated.
check:doc-fencesfired for real this round — see the F3 section. A gatethat goes red on your own first attempt and green on the fix is the evidence it
is live.
🔁 F8 round — verification on
cf31b51edThe F8 commit touches two
content/docs/**files and nothing underpackages/**,so the doc gates are what can move. Build precondition satisfied first
(
turbo run build --filter='!@object-ui/site' --concurrency=2exit 0, 43/43,cache hit) so that none of these could return exit 2 = PRECONDITION NOT MET.
Every exit code captured into a file BEFORE any pipe.
check:doc-fencescheck:doc-typescheck:doc-snippetsBaseSchema's index signature)check:doc-examplescheck:readme-exportscheck-doc-links#baseschemaanchor.node scripts/check-doc-links.mjs: thepnpmwrapper's exit 254 is a known quirk of that script's runner, not a failurecheck:control-bytesFixesvsRefs, per cardAll three are
Fixes, each confirmed on this head rather than assumed:view:kanbanregistryinputsstill declarestitleFieldwhile the zod arm refuses it — two published faces of one type key, opposite verdicts, and every pin stays green #8802 — the barekanbannode key: 0 registrations, 0 schema faces, refused by name. Nothing of the card's subject is left.'kanban-enhanced'get a declared arm? — its registration readsonColumnToggle/enableVirtualScrolling/virtualScrollThresholdand no schema in@object-ui/typesdeclares the type at all (split from #7742) #8257 —kanban-enhancedandkanban-ui: both unregistered; and the card's own subject, the three keysonColumnToggle/enableVirtualScrolling/virtualScrollThreshold, is confirmed rather than assumed to have no authorable surface left anywhere (0 declared inputs, measured offindex.tsxwithgroupByas the firing control).ganttnode spelling has no declared schema type —ObjectGanttSchema.typeis the literal'object-gantt', so a typed author cannot write the key the registry (and the README's table) teaches #8008 — the bareganttregistration: gone, docs moved,object-ganttsole survivor.node scripts/check-clause2-carriers.mjs --pair 8865exits 0 on this head, with--pair 8916→ 2 and--self-test→ 0 (319 cases) as its controls.🤖 Generated with Claude Code
https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
Generated by Claude Code