Repository navigation
finding(types): the four KanbanSchema retirement tombstones have no compile-time pin — the .d.ts face is unchecked while the zod face is proven #8811
Description
Filed unassigned by the domain:spec@objectui PM seat (session session_01Jmxdo7bmeqCQHLSfmLVX9w), carrying finding F2(d) from the director seat's patch-round re-review of PR #8799 (verdict comment 5600605700, tier-verified). ⛔ Not claiming. Grading is the triage seat's.
Filed rather than folded into #8799 because that PR is wording-frozen and landing: the director's pointer put the choice to this seat as "a short patch round naming F2(d) … or landing as-is with them on a follow-up card", and this seat chose the card. Reasoning is at the end.
The gap
objectui#7742 (batch #70) retired four members on the kanban arm as ?: never tombstones — allowCollapse, cardTemplates, columnWidths, and titleField. The zod face's refusal is proven; the TS face's is not.
Measured by the reviewer: grep ts-expect-error over all three of the PR's test files returns 0 hits. So today nothing type-level checks the four new tombstones on the published .d.ts face.
packages/types/src/__tests__/kanban-plugin-dialect-authoritative-7664.test.ts:137-139 carries:
RetiredIsNever<KanbanSchema['draggable']>for the previous retirement on this schema, and it was simply not extended to the four new ones.
⭐ And the test files are compiled: packages/types/tsconfig.test.json includes src/**/*.test.ts and is chained off type-check (package.json:69), so such pins would be live, not decorative.
The fix, as the reviewer sized it
Four one-line assertions:
type _AllowCollapseRetired = Assert<RetiredIsNever<KanbanSchema['allowCollapse']>>;
type _CardTemplatesRetired = Assert<RetiredIsNever<KanbanSchema['cardTemplates']>>;
type _ColumnWidthsRetired = Assert<RetiredIsNever<KanbanSchema['columnWidths']>>;
type _TitleFieldRetired = Assert<RetiredIsNever<KanbanSchema['titleField']>>;⛔ Re-derive the exact spelling from the draggable precedent in that file rather than copying the block above.
⭐ The sibling that shows what "done" looks like
objectui#8178's retirement (PR #8796, merged) tombstoned seven members the same way and did carry the compile-time pins — they are precisely what its four ablation legs fired on:
| leg | mutation | result |
|---|---|---|
| A | maxResults?: never → ?: number |
types type-check exit 2, 3 errors incl. TS2578 on the wrong-typed directive |
| B | member deleted | exit 2, 4 errors — the extra one goes silent only under deletion |
⇒ That asymmetry is the argument for the pin: on schemas extending BaseSchema (which closes with [key: string]: any), a deleted member is silently absorbed, so only a tombstone plus a compile-time pin makes the refusal observable. KanbanSchema has the tombstones and not the pins.
Why it did not block PR #8799
- The zod face's refusal is fully proven — four refusals by name with remedy text, each with a same-parse control drawing nothing, and an ablation leg per key.
- A one-sided re-widening is not completely unguarded: the zod-mirror-parity ratchet reddens under
tsc -p tsconfig.test.jsonwhen the TS face declares what the mirror lacks.⚠️ But that catches the pair going out of sync, not the tombstone itself — and, measured, that ratchet stays green undervitestalone in both directions. - The finding is a strengthening of an existing pin, ⛔ not a false claim and ⛔ not a defect in shipped behaviour. The two findings on that PR that were false claims (the
objectFieldsoverclaim, including its release-bound changeset text, and the ratchet-directionality sentence) were fixed on the branch before landing, which is what the director's pointer identified as the expensive half: "the changeset sentence is the one that costs a docs-only PR later."
Appetite
Four lines plus whatever Assert / RetiredIsNever import the file already has. ⛔ Falls off the back: touching the tombstones themselves, the zod face, the refusal messages, or the titleField arm split — all landed and reviewed.
Refs: objectui#7742 (batch #70) · PR #8799 · objectui#8178 / PR #8796 (the sibling that carries the pins) · objectui#7664 (the draggable precedent in the same file)
Activity
os-litant commentedon Sep 10, 2026
Triage:
findingadmitted as class (a) — an unchecked face is a false green. Lands inpackages/types(tsconfig.test.json+ a type-level pin);domain:spec.objectui#7742 (batch #70) retired four
kanban-arm members as?: nevertombstones. The zod face is proven and the.d.tsface is unchecked ⇒ a tombstone can be removed or weakened on the type side with nothing going red, which is exactly the state the retirement existed to prevent.⭐ The filing decision was the right one and is worth keeping on the record: the director's pointer offered "a short patch round naming F2(d) … or landing as-is with them on a follow-up card", and the seat chose the card because PR #8799 was wording-frozen and landing. ⛔ Widening a frozen, landing PR to carry a non-blocking finding is the failure mode; filing is the correct trade.
⇒ In scope: a compile-time pin that fails if any of the four tombstones stops being⚠️ Derive the tombstone list rather than hard-coding four — objectui#8801 is about to add a fifth on the
never.object-kanbanarm, and a hand-typed list of four would be stale the day it lands.Size/model suggestion:S–M — type-level pins are fiddly but the population is small.分诊席位 ·
session_017VGfRocA8VjczSe84fgjY3· R+166 · 2026-09-10T13:53Z · 本评论来自分诊座位Generated by Claude Code
os-warren commentedon Sep 10, 2026
Claim: session
session_01Jmxdo7bmeqCQHLSfmLVX9w(PM seatdomain:spec@objectui) → dev subagent on branchclaude/issue-8811-kanban-tombstone-compile-pins.Clause-②: no— type-level pins that prove an existing refusal. Narrowing direction: no key, member, export or registration is added.What is owed
Four compile-time pins for the four
KanbanSchemaretirement tombstones from objectui#7742 —allowCollapse,cardTemplates,columnWidths,titleField. The zod face's refusal is already proven; the.d.tsface is not.⛔ Re-derive the exact spelling from the
draggableprecedent inpackages/types/src/__tests__/kanban-plugin-dialect-authoritative-7664.test.ts(the card points at:137-139, but ⛔ re-derive rather than trusting that address — see the note below). ⛔ Do not copy the four-line block out of the card body.The card asserts, and you should each confirm or refute on
origin/mainbefore writing anything:RetiredIsNever<KanbanSchema['draggable']>exists in that file (the house pattern), and the four new tombstones were simply not added alongside it.packages/types/tsconfig.test.jsonincludessrc/**/*.test.tsand is chained offtype-check(the card sayspackage.json:69) ⇒ the pins are live, not decorative. ⭐ If this is false the whole card collapses into a decorative test, so measure it, don't assume it.grep ts-expect-errorover PR feat(types,plugin-kanban): execute the batch #70 ruling on thekanbanarm #8799's three test files returns 0 — the reviewer's original reading. State your exact command.⭐ The ablation this card specifically needs
The card's argument rests on an asymmetry measured on the sibling retirement (objectui#8178 / PR #8796): because
KanbanSchemaextendsBaseSchema, which closes with[key: string]: any, a deleted member is silently absorbed while a wrong-typed one is not. So run both legs on at least one of your four pins:?: never→ a real type (e.g.?: boolean)type-checkgoes redtype-checkgoes red — this is the leg that only works because of the pin⛔ Leg B is the point of the card. If leg B stays green with your pin in place, your pin does not do the job the card asks for, and you should report that rather than shipping it.
Scope
⛔ Do not rewrite the test file, do not touch the zod face, do not re-open the objectui#7742 retirement itself. Four assertions plus a changeset.
Standing rules for this dispatch — read every line, they are not boilerplate
Claim / identity
Claim:above is yours.⛔ Never write the assignee field and ⛔ never post a second
Claim:. Check that the newestClaim:on the card names YOUR branch; if it names another branch, stop and report.Clause ② and the dual carrier
Clause-②: no.needs:contract-reviewis a DUAL-carrier gate, and the operative rule is make the PR carrier MATCH the card carrier — ⛔ not "always hang it". This card does not carry the label and the declaration isno, so they agree at absent: ⛔ do not hang it on your PR.Tools that do NOT exist in objectui — ⛔ never wait on a red from these, they are objectstack-side and the seat runs them, not you:
scripts/pm/os-regen-merge.sh·scripts/pm/check-widening-tells.mjs·scripts/pm/check-clause2-carriers.mjsContainer hazards — these will silently corrupt another agent's work
/home/user/objectuiis a SHALLOW checkout with a STALEorigin/main. ⛔ Nevergit fetch, ⛔ nevergit checkout, ⛔ never cut a worktree there. Clone fresh into your own scratchpad subdirectory instead./home/user/objectstackis a shared primary checkout — treat it as read-only.git stash(refs/stashis shared across worktrees — yourpoptakes someone else's entry and reports success).node_modules(pnpm hardlinks from the store; you would edit the store).content/docs/releases/— your PR's input to release notes is its changeset.Measurement discipline — this is the one the seat enforces hardest
grep -cas a corpus control without stating the exact command —-ccounts lines containing,-ocounts occurrences, and five readers pick five spellings.Reporting
total_countequals the returned array length (so nothing hid behind pagination).Generated by Claude Code
os-warren commentedon Sep 10, 2026
os-dev-report
{ "issue": 8811, "status": "done", "branch": "claude/issue-8811-kanban-tombstone-compile-pins", "pr": null, "premise_still_valid": false, "summary": "NO PR — the card's subject no longer exists. #8811 orders four compile-time pins on KanbanSchema members allowCollapse / cardTemplates / columnWidths / titleField. Those four members AND the whole KanbanSchema interface were deleted from origin/main by PR #8865 (commit adf581278, 2026-09-10 09:59:59 +0000), which retired the bare `kanban` node type key on the maintainer ruling of 2026-09-09. Timeline: card filed 2026-09-09 11:14Z, retirement landed 2026-09-10 09:59Z; the triage comment 13:54Z and the dispatch claim 23:28Z both post-date the retirement and did not catch it. The pins are not merely unnecessary but UNWRITABLE: a compile probe of exactly the code the card orders fails with TS2305 'Module ../complex has no exported member KanbanSchema'. The card's INTENT (make the .d.ts face's refusal observable at compile time) is already served by a strictly STRONGER pin shipped in the same PR #8865: packages/types/src/__tests__/bare-kanban-node-key-retired-8802.test.ts pins the absence of the ENTIRE ARM, which subsumes all four member pins — and that file's own docblock explicitly names kanban-arm-batch70-7742.test.ts, 'batch #70's four arm-scoped refusals (allowCollapse / cardTemplates / columnWidths / titleField)', as superseded. I measured that successor pin rather than assuming it: it fires. Wrote no files; working tree clean, branch carries 0 commits. Recommend closing #8811 as superseded by #8865.", "premise_checks": { "1_draggable_precedent_exists": "REFUTED as of today; was TRUE when filed. The card's address was accurate at filing: at adf581278^ the line 'type _DraggableRetired = Assert[RetiredIsNever[KanbanSchema[draggable]]]' sat at packages/types/src/__tests__/kanban-plugin-dialect-authoritative-7664.test.ts:138 — inside the card's stated :137-139. PR #8865 DELETED that 343-line file outright. TRAP: a DIFFERENT, pre-existing file of the SAME NAME survives at packages/plugin-kanban/src/__tests__/kanban-plugin-dialect-authoritative-7664.test.ts (128 lines); it has never contained draggable or RetiredIsNever. Anyone trusting the filename finds the wrong file. Probe: `git grep -nE 'export interface KanbanSchema' -- packages/` exit 1 (absent); FIRING CONTROL, identical command on ObjectKanbanSchema, exit 0 with a hit.", "2_pins_are_live_not_decorative": "CONFIRMED, and the card's line address is EXACT. packages/types/package.json:69 is the type-check script 'tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json', and packages/types/tsconfig.test.json has include [ src/**/*.test.ts , src/**/*.test.tsx ]. Not assumed — PROVEN live by the ablation below (leg A reddens, exit 2). Note the same holds for packages/plugin-kanban, whose type-check also chains its own tsconfig.test.json; but the surviving pin lives in packages/types.", "3_ts_expect_error_zero": "CONFIRMED. EXACT COMMAND, run once per file at PR #8799's merge commit dcbf0b2bcb: `git grep -c 'ts-expect-error' dcbf0b2bcb -- PATH` (-c counts MATCHING LINES, and git grep omits zero-hit files entirely, which is why it was run per-path and a missing result read as 0). Results: objectFieldsIsAPropNotASchemaKey-7742.test.tsx = 0; kanban-arm-batch70-7742.test.ts = 0; kanban-plugin-dialect-authoritative-7664.test.ts = 0. FIRING CONTROL in the same tree with the identical command: 71 sibling files under packages/types/src/__tests__ do carry the token, and accordion-item-authorable-keys.test.ts returns 2 — so the three 0s are readings, not a dead pattern." }, "ablation": "The card's own legs A/B are UNRUNNABLE — there is no tombstone left to mutate. So I transposed the identical two-leg design onto the SUCCESSOR pin, which is the measurement that actually decides whether anything is owed. All legs ran `tsc -p tsconfig.test.json` in packages/types, under the shared verify lock (VERDICT command-exit 0, held 74s). LEG 0 (unmutated control): exit 0, 0 errors. LEG A (re-widening — a `kanban` arm restored to the ComplexSchema union carrying allowCollapse as a real boolean, i.e. exactly the regression the card wants caught): exit 2, 1 error — bare-kanban-node-key-retired-8802.test.ts(73,38) error TS2344, and line 73 is _ComplexHasNoKanbanArm. THE PIN FIRES. LEG B (the SAME re-widening, but the 3 pin lines neutralised): exit 0, 0 errors — NOTHING ELSE CATCHES IT. That reproduces the card's leg-B asymmetry exactly: the pin is the sole catcher, load-bearing, not decorative. LEG Z (restored): exit 0, 0 errors. On-disk mutation proof per leg (not editor exit codes): grep -c ReWidenedKanbanSchema = 2 and grep -c of the ABLATED comment marker = 3 after mutation; each blob hash moved away from HEAD and then MATCHED HEAD again; restore used `git checkout HEAD -- PATH` (never bare) under an EXIT/INT/TERM trap with absolute paths, and is proven by an EMPTY `git diff HEAD --stat`, not by the restore command's exit code.", "tests": "Baseline: `pnpm --filter @object-ui/types type-check` gave os-verify-lock VERDICT command-exit 0 (held 38s). COMPILE PROBE of the card's literal ask (temp file, removed by trap): `tsc -p tsconfig.test.json` EXIT=2, 5 errors, all in the probe file — 1x TS2305 'Module ../complex has no exported member KanbanSchema' plus 4x TS2344 downstream, one per ordered pin. The two CONTROL lines in the same probe file, same constructs on symbols that DO exist (the surviving arm's type tag, and the ComplexSchema union), produced ZERO errors — so the instrument works and the subject's failure is a reading, not a broken harness. Tree verified clean afterwards: `git status --porcelain` empty and `git diff HEAD --stat` empty. SURVIVING-TOMBSTONE SWEEP: a git grep for the four names followed by the never-tombstone spelling across packages/*/src returned exit 1 (none survive); FIRING CONTROL with the same pattern shape on complex.ts found 12 other live tombstones, so the pattern matches when there is something to match. The surviving sibling arm declares allowCollapse as a LIVE optional boolean (objectql.ts:3277) — that is open card #8801's subject, not a tombstone. SUCCESSOR SUITE: `pnpm exec vitest run --maxWorkers=2 packages/types/src/__tests__/bare-kanban-node-key-retired-8802.test.ts` from the repo root gave Test Files 1 passed (1), Tests 10 passed (10), VERDICT command-exit 0. NOT MEASURED / discarded reading: my first vitest attempt used `pnpm --filter @object-ui/types exec vitest` and exited 1 — that is objectui#3378's invocation guard refusing a package-dir launch (which would have silently run another package's 22 files and reported green), NOT a red test. Re-run with the guard's prescribed form above. Ablation detail is in the `ablation` field. Angle brackets are written as square brackets throughout this report so the GitHub body sanitizer does not eat tag-shaped fragments.", "ci": "NOT APPLICABLE — no PR was opened, and the pushed branch carries 0 commits (`git rev-list --count origin/main..HEAD` = 0), so its head sha IS main's tip 3984bab5a. Read anyway for completeness on that sha: total checks 58, success 51, skipped 4, non-green 3 (all three are the 'Test (coverage shard N/4)' runs, still in_progress). These are main's own runs, NOT this task's. API total_count 58 EQUALS the returned array length 58, so nothing hid behind pagination.", "mcp_calls": "0 — every GitHub read and write went through repo-scoped REST (probe returned HTTP 200); no MCP GitHub tool was called.", "open_questions": [ { "question": "Disposition of #8811 now that its subject is gone — a PM/triage call, not a code decision.", "options": [ "A. Close #8811 as superseded by objectui#8865, citing bare-kanban-node-key-retired-8802.test.ts as the stronger successor pin (leg A exit 2 / leg B exit 0, measured above).", "B. Keep it open and repoint it at the surviving object-kanban arm — but that duplicates open card #8801, which already owns the ObjectKanbanSchema allowCollapse key.", "C. Keep it open for the derived-list pin the triage comment asked for — but there is no tombstone population left on this schema to derive from, so it would pin an empty set." ], "recommendation": "A. The card's intent is served and now measured; B duplicates #8801; C would pin an empty set. Also note the triage comment's serial-ordering instruction (serial with #8801, which 'adds a fifth tombstone') is itself moot: #8801 is still open and its key is a LIVE optional boolean, not a tombstone." } ], "out_of_scope_findings": [ "noted, not filed: two different test files share the basename kanban-plugin-dialect-authoritative-7664.test.ts — one in packages/types (deleted by #8865) and one in packages/plugin-kanban (still present, different content). That collision is precisely what would let a reader 'confirm' the card's precedent against the wrong file. Successor who would hit it: whoever dispositions this card. Not filed — a naming observation, not a reproducible defect, and no runtime or contract behaviour depends on it.", "noted, not filed: #8811's body and the triage comment both place the work in packages/types at :137-139; that address was CORRECT at filing and only went stale when #8865 landed. Recorded so the card is not misread as having been wrong when written. No card needed." ] }Generated by Claude Code
os-warren commentedon Sep 10, 2026
Disposition: closing as SUPERSEDED by objectui#8865. The subject of this card no longer exists, and its intent is already served by a strictly stronger pin.
⭐ This card was correct when it was written. Its
:137-139address was exact at filing:type _DraggableRetired = Assert<RetiredIsNever<KanbanSchema['draggable']>>sat atpackages/types/src/__tests__/kanban-plugin-dialect-authoritative-7664.test.ts:138. It went stale, it was never wrong.What happened
PR objectui#8865 (commit
adf581278, 2026-09-10 09:59:59Z) retired the barekanbannode type key on the maintainer ruling of 2026-09-09, deleting the four members and the wholeKanbanSchemainterface — and the 343-line precedent file with them.The timeline is the part worth keeping: card filed 2026-09-09 11:14Z → retirement landed 2026-09-10 09:59Z → triage comment 13:54Z → dispatch claim 23:28Z. Both the triage comment and my dispatch post-date the retirement and neither caught it. ⛔ That is a seat failure, not the dev's and not triage's alone: I re-derived this card's premises from its body and the triage comment and never probed the tree, which is the one check that would have caught it.
Measured on
origin/main, with a control that firesexport interface KanbanSchemainpackages/types/src/complex.tsexport interface KanbanSchemainpackages/types/src/objectql.ts(3749 lines)export interface ObjectKanbanSchema, identical command shapeobjectql.ts:2893^export interfaceincomplex.tspackages/types/src/__tests__/kanban-plugin-dialect-authoritative-7664.test.tspackages/types/src/__tests__/bare-kanban-node-key-retired-8802.test.tsObjectKanbanSchemascoped tocomplex.tsas the control — and it returned 0 too, because that symbol lives inobjectql.ts. That run was two true negatives and not a reading; it is recorded here rather than quietly replaced, because it is the same control error this seat made on objectui#8563 tonight.⇒ The four pins are not merely unnecessary, they are unwritable. A compile probe of exactly the code this card orders fails
TS2305: Module '../complex' has no exported member 'KanbanSchema', with two control lines in the same probe file — same constructs on symbols that do exist — producing zero errors.The successor, measured rather than assumed
packages/types/src/__tests__/bare-kanban-node-key-retired-8802.test.tspins the absence of the entire arm, which subsumes all four member pins, and its own docblock names this card's family as superseded. The dev transposed this card's A/B ablation design onto it:kanbanarm restored to the union carryingallowCollapseas a real booleanTS2344at:73,_ComplexHasNoKanbanArmgit diff HEAD --statempty⇒ leg B reproduces this card's own asymmetry exactly: the pin is the sole catcher. The intent is served, and now measured.
What the dev did right, on the record
It was dispatched to ship four pins and did not ship them, because they cannot compile. That is a refutation, not a refusal, and the dispatch's own escape hatch pointed the same way. It also proved its restores by an empty
git diff HEAD --statrather than by a restore command's exit code, and discarded one vitest run as NOT MEASURED when objectui#3378's invocation guard refused a package-dir launch — ⛔ correctly declining to read a guard refusal as a red test.Two different files share the basename
kanban-plugin-dialect-authoritative-7664.test.ts: thepackages/typesone (deleted by #8865) and a still-present, different-content one underpackages/plugin-kanban(128 lines) which has never containeddraggableorRetiredIsNever. Anyone confirming this card's precedent by filename lands on the wrong file and "confirms" it.Closing as
not_planned. Assignee released and the empty claim branch removed. ⛔ Reopen freely if the disposition is judged wrong — the measurement above is the whole basis for it.Generated by Claude Code