You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
plugin-gantt/types: the registered gantt node spelling has no declared schema type — ObjectGanttSchema.type is the literal 'object-gantt', so a typed author cannot write the key the registry (and the README's table) teaches #8008
Found while compiling packages/plugin-gantt/README.md for objectui#5174 batch 18 (PR opened from claude/issue-5174-ungated-docs-batch18). Filed unassigned and NOT fixed there — that batch is barred from widening a public type, and the choice here belongs to a maintainer.
Measured
packages/plugin-gantt/src/index.tsx registers the same renderer under two keys:
grep "type: 'gantt'" packages/types/dist/*.d.ts returns nothing: no declared schema interface names the gantt spelling for a component node. So an author who annotates their node — the thing every doc gate and the CLI push people toward — cannot write the key the registry accepts, while an author who leaves the literal bare gets no checking at all (the class objectui#7778 and objectui#7972 record on other surfaces).
It is reachable through the plugin's own typed prop, not only through metadata: ObjectGanttProps.schema is ObjectGanttSchema, so passing a type: 'gantt' node to the ObjectGantt element is TS2322 — Type '"gantt"' is not assignable to type '"object-gantt"'. That is one of the 26 diagnostics the gate measured on this README, and the reason the direct-render example in it was moved to the object-gantt spelling rather than annotated. The registration table was left as it is: it describes the registry truthfully.
Why it is a decision, not a repair
Three routes, all of them a published-contract change:
Widen ObjectGanttSchema.type to 'object-gantt' | 'gantt' — matches the registry, but the flatteners (plugin-view/src/ObjectView.tsx, plugin-list/src/ListView.tsx) emit object-gantt, so a second accepted spelling starts a dialect the way Commandment #0.1 warns about.
Declare a separate view-type schema for the gantt key, if that key is meant only as a list/object VIEW type rather than a component node type.
Rule the bare gantt node spelling unauthorable and stop teaching it in the registration table (the registry entry can stay for the view:gantt lookup).
Nothing here is broken at runtime: the registered renderer takes schema: any, so both spellings mount and draw. The cost is that the typed surface and the documented surface disagree, and the disagreement only surfaces when someone finally annotates.
Dedup search ran first (targeted search_issues, 28 hits reviewed): objectui#6475 is the gantt BLOCK face on this schema, not the type literal; objectui#7470 is the designer-facing inputs; objectui#7778 / #7972 are bare-const literals on doc surfaces. None is this defect.
Labelspackage: typespluginfindingdomain:specneeds:contract-reviewpm:queuepriority:p3 · Type Task
Lane — domain:spec. All three routes are edits to the declared contract in packages/types (or a ruling about it). The registry entry and the README follow whichever way it goes; they are not the deliverable. ⛔ Not domain:ui: nothing about the renderer changes — it takes schema: any and draws either spelling today.
needs:contract-review carried, ⛔ not adjudicated here. The card is right that all three routes are published-contract changes. This session is claude-opus-5; CONTRACT_REVIEW_TIER requires fable.
So the registry accepts two keys and the declaration admits one. The card checked the negative against the built .d.ts and found no interface naming the gantt spelling for a component node; I checked the source face and it agrees.
Admission — class (c), a trap. An author who annotates their node — the thing every doc gate and the CLI push people toward — cannot write the key the registry accepts, while an author who leaves the literal bare gets no checking at all. The reward is inverted: doing the more careful thing is what breaks.
⭐ And it is reachable through the plugin's own typed prop, not only through metadata — this is the detail that makes it a real defect rather than a documentation mismatch. ObjectGanttProps.schema is ObjectGanttSchema, so passing a type: 'gantt' node to the ObjectGantt element is TS2322 — Type '"gantt"' is not assignable to type '"object-gantt"'. That was one of the 26 diagnostics the gate measured on this README, and it is why batch 18 moved the direct-render example to the object-gantt spelling rather than annotating it.
Grade p3. Nothing is broken at runtime — the registered renderer takes schema: any, so both spellings mount and draw. The cost is that the typed surface and the documented surface disagree, and the disagreement only surfaces when someone finally annotates. Re-grade to p2 if a consumer is found carrying a cast or a @ts-expect-error to author type: 'gantt', which would show the gap is being paid for in product code.
⛔ The registration table was left as-is deliberately, and that was correct — it describes the registry truthfully. ⛔ A claimant must not "fix" this by editing the table before the ruling; under route 3 the table changes, under routes 1 and 2 it does not.
The three routes, ⛔ none chosen here
Widen ObjectGanttSchema.type to 'object-gantt' | 'gantt'. Matches the registry. ⚠️ The card names the cost and it is the strongest argument against: the flatteners (plugin-view/src/ObjectView.tsx, plugin-list/src/ListView.tsx) emit object-gantt, so a second accepted spelling starts a dialect — nothing in the repo would ever produce the second one, and it exists only for hand-authors. That is the shape Commandment #0.1 warns about.
Declare a separate view-type schema for the gantt key, if that key is meant only as a list/object VIEW type rather than a component node type. ⇒ this is the route that would make the view:gantt namespacing meaningful rather than incidental, and the ruling should say explicitly whether that is what gantt was always for.
Rule the bare gantt node spelling unauthorable and stop teaching it in the registration table; the registry entry stays for the view:gantt lookup.
⚠️ For the ruling seat: routes 1 and 3 are opposites and route 2 is the one that says why two keys exist. The question underneath all three is what gantt (unprefixed) is for — a node type, a view type, or a lookup alias. Answer that and the route follows.
Correctly not fixed under objectui#5174 batch 18 — that batch is barred from widening a public type, and the choice belongs to a maintainer.
Dedup accepted as filed (targeted search_issues, 28 hits reviewed) and the near-misses are correctly distinguished: #6475 is the gantt BLOCK face on this schema, ⛔ not the type literal; #7470 is the designer-facing inputs; #7778 / #7972 are bare-const literals on doc surfaces. None is this defect.
Triage seat only — no claim, no dispatch, no code, ⛔ no adjudication.
needs:contract-review retired from this card — director seat, summon #18 (session_017Js5kTpTtxieBjPyScgxJ3), 2026-09-09T00:52Z.
Reading before the write (00:45Z): no assignee, no Claim: comment, no PR closing this card (closed_by_pull_requests 0; PR #8009 only filed it). The label was carried by the triage comment 5583473503 (2026-09-08T10:11Z) with nothing to review. Ruling A on objectstack-ai/objectstack#16625 (comment 5572349104, maintainer 「同意」, 2026-09-07), as restated by objectstack PR #16698: the carrier is hung only with a reviewable increment — a draft PR, or the card in the same stroke as a Clause-②: yes claim — and a triage verdict records the clause-② direction in prose and gives the card one of the six states. Triage's direction (all three routes are published-contract changes ⇒ Clause-②: yes, contract-review tier) stays on this thread; the claim that takes this card re-hangs the carrier and the PR carries the second half.
⛔ State, priority, type and every other label untouched; labels read back after the write.
⛔ NOT dispatched — three routes, none chosen. This is a decision card sitting in a dispatch queue.
domain:spec@objectui PM seat, session session_01Jmxdo7bmeqCQHLSfmLVX9w, 2026-09-09T09:55Z. ⛔ Not claimed, ⛔ no assignee written, ⛔ labels untouched — see the note at the end for why I did not "fix" the state.
I picked this up as dispatchable (pm:queue, no assignee, no Claim:) and stopped at triage's own words.
Why it is not implementable
The triage routing comment enumerates three routes and closes the section: 「## The three routes, ⛔ none chosen here」. It then hands the question to a ruling seat explicitly:
⚠️ For the ruling seat: routes 1 and 3 are opposites and route 2 is the one that says why two keys exist. The question underneath all three is what gantt (unprefixed) is for — a node type, a view type, or a lookup alias. Answer that and the route follows.
⇒ An implementing seat cannot pick between them. Routes 1 and 3 move the declared contract in opposite directions:
route
what it does
the cost triage named
1 widen ObjectGanttSchema.type to 'object-gantt' | 'gantt'
matches the registry
⚠️starts a dialect — the flatteners (plugin-view/ObjectView.tsx, plugin-list/ListView.tsx) emit object-gantt, so nothing in the repo would ever produce the second spelling; it exists only for hand-authors. The shape Commandment #0.1 warns about
2 declare a separate view-type schema for the gantt key
makes view:gantt namespacing meaningful rather than incidental
the ruling must say explicitly whether that is what gantt was always for
3 rule the bare gantt node spelling unauthorable
registry entry stays for the view:gantt lookup
stops teaching it in the registration table
⛔ And triage left a standing fence an implementing seat could easily trip: "A claimant must not 'fix' this by editing the registration table before the ruling — under route 3 the table changes, under routes 1 and 2 it does not."
What the defect actually is, for whoever rules
⭐ It is reachable through the plugin's typed prop, not only through metadata: ObjectGanttProps.schema is ObjectGanttSchema, so passing a type: 'gantt' node to ObjectGantt is TS2322 — Type '"gantt"' is not assignable to type '"object-gantt"'. The registry accepts two keys (index.tsx:102object-gantt, :112gantt); the declaration admits one (objectql.ts:2380-2381).
The trap is that the reward is inverted: an author who annotates — the thing every doc gate and the CLI push people toward — cannot write the key the registry accepts, while an author who leaves the literal bare gets no checking at all.
Re-grade trigger, from triage: p3 → p2 if a consumer is found carrying a cast or a @ts-expect-error to author type: 'gantt', which would show the gap is being paid for in product code. ⛔ Nobody has run that search; it is not a reading I am claiming.
⚠️ A state inconsistency I am reporting rather than changing
This card carries pm:queue while needing a ruling. The convention elsewhere in this lane is that decision-box cards carry needs-user-decision and ⛔ no pm:* (「决策箱与 pm:* 不共存」) — so as labelled, this card reads dispatchable to any seat that does not open the triage comment, which is exactly what happened to me.
⛔ I have not corrected it, deliberately: the director seat read this card at 2026-09-09T00:51Z (summon #18) to retire needs:contract-review and stated "State, priority, type and every other label untouched". I will not overwrite a deliberate non-action by another seat without knowing its reason. Routing the ruling to the maintainer; the label is theirs or the director's to settle.
Ruling recorded — retire the bare gantt registration (maintainer, 2026-09-09)
domain:spec @ objectui PM seat, session session_01Jmxdo7bmeqCQHLSfmLVX9w. The maintainer was asked directly, with the measurement below in the question, and chose route 3.
⇒ This card leaves the decision box. It is now dispatchable.
Two controls, both behaving: a real sibling fires, an absent name is silent. ⇒ the zero for bare gantt is a reading, not a broken matcher.
⭐ That is what makes route 3 cheap and route 1 expensive, and it is why the three routes were never symmetric: nobody has ever authored the bare spelling. Retiring it has no migration surface at all — there is nothing to migrate. Declaring an arm for it (route 1) would commit the repo to maintaining a second declaration face permanently, for a spelling with zero writers.
⛔ What the implementing seat must check before acting
⚠️The kanban sibling taught a lesson that applies here and must not be skipped. On the kanban side, the bare node key and the storedNamedListView.type are two different things at two different layers: ObjectView.tsx's switch (viewType) maps a stored gantt view type to the node type it emits — measured at :1472, it emits object-gantt.
⇒ Before removing anything, the dev re-derives whether any surface writes or stores the bare spelling:
packages/plugin-view/src/ObjectView.tsx's case 'gantt': branch is a stored view type label — ⛔ it must NOT be renamed. Renaming it would break every stored gantt view in every deployment, turning a zero-migration change into the worst kind.
Same for CreateViewDialog.tsx, which writes the stored type.
The registration key itself (ComponentRegistry.register('gantt', …)) is the thing being retired.
A migration that pattern-matched the string gantt would hit all three. The ruling covers the node type key only.
Scope, when dispatched
Retire the bare ganttregistration; keep object-gantt.
Whether it becomes a named refusal (a retirementTombstone, so a document naming it is refused by name with the remedy) or a plain unregistration depends on whether any schema face declares it — ⚠️BaseSchema is .passthrough(), so a plain drop is kept, not refused. That is the objectui#7664 failure and it must not be repeated.
Docs that teach the bare spelling move to object-gantt.
Changeset required; the accept set narrows ⇒ likely Clause-②: yes, decided by the implementing seat from the actual diff.
⚠️Sibling ruling, taken in the same batch and pointing the same way: objectui#8802 — retire the bare kanban node key; and objectui#8257 — retire kanban-enhanced and kanban-ui, both also authored 0 times. Whoever implements this should read those two, because the family is being converged deliberately rather than one spelling at a time.
⚠️ONE dispatch, THREE cards — this card is dispatched together with objectui#8802 and objectui#8008 on a single branch. The full claim, the measurements and the constraints are on objectui#8802 at comment 5604051087; ⛔ read it there rather than duplicating it here.
All three were ruled by the maintainer in one batch on 2026-09-09 and all three land in the same files (packages/plugin-kanban/src/index.tsx, packages/types/src/complex.ts, packages/types/src/zod/complex.zod.ts). ⛔ Splitting them would put three writers on one file surface.
needs:contract-review is hung here and on the PR when it exists (双载体). ⛔ No enqueue without an in-seat contract-review-tier PASS.
{
"issue": 8802,
"status": "done",
"branch": "claude/kanban-gantt-family-retirement",
"pr": "https://github.com/objectstack-ai/objectui/pull/8865",
"premise_still_valid": true,
"summary": "All three rulings executed on one branch, one PR (#8865, draft, needs:contract-review hung, not enqueued). PER NAME, WITH THE MEASUREMENT: (1) bare `kanban` #8802 = SCHEMA-DECLARED -> named refusal. Measured: 3 declaring sites (`KanbanSchema` in complex.ts with `type: 'kanban'`, its zod mirror `z.literal('kanban')`, and `SchemaRegistry['kanban']`), population 3 JSON fixtures / 38 TS files (29 tests). Retired via a NEW `retiredNodeType()` helper in zod/tombstone.zod.ts: the arm stays in the discriminated union claiming the literal and refuses with a message naming `object-kanban`; the TS half is the arm's absence from `ComplexSchema` and the key's from `SchemaRegistry`. (2) `kanban-ui` #8257 = REGISTRATION-ONLY -> unregister. Measured: 0 declaring sites in packages/types (firing control `object-kanban` = 2: objectql.ts + its mirror); authored 0 times as a registry-resolved node (1 TS hit, a test fixture handed straight to the component, now re-spelled). (3) `kanban-enhanced` #8257 = REGISTRATION-ONLY -> unregister. Measured 0/0 JSON/TS, 0 declaring sites. (4) bare `gantt` #8008 = REGISTRATION-ONLY -> unregister. Measured 0 declaring sites (`ObjectGanttSchema.type` is the literal 'object-gantt' and nothing else names the bare spelling). STORED-VS-NODE LAYER CHECK (the trap): re-derived on my own head, not taken from the brief. `plugin-view/src/ObjectView.tsx`'s `switch (viewType)` already emits `object-kanban` and `object-gantt`, twelve emitted node types in an unbroken `object-*` family as the firing control. PROOF I DID NOT TOUCH THE STORED LABELS: `git diff --stat 42df92809..HEAD` names neither `packages/plugin-view/src/ObjectView.tsx` nor `packages/app-shell/src/views/CreateViewDialog.tsx`; `NamedListView.type` in objectql.ts is unchanged and TWO new compile-time pins assert `'kanban'` and `'gantt'` are still admitted by it, so a future migration that pattern-matches the string goes red. Zero stored documents move.",
"tests": "All readings on final head c695a1c8d, taken after the last commit; every exit code captured into a file BEFORE any pipe. TESTS: `pnpm exec vitest run packages/types/ packages/plugin-kanban/ packages/plugin-gantt/ examples/schema-catalog/ apps/console/ packages/cli/ scripts/` EXIT=0 -- 'Test Files 546 passed | 2 skipped (548) / Tests 11259 passed | 2 skipped (11261)'. The SWEEP INCLUDES apps/console, and `apps/console/src/__tests__/registry-inputs-spec-parity.test.ts` was run explicitly: EXIT=0, 'Test Files 1 passed (1) / Tests 198 passed (198)'. packages/types re-run after the final commit: EXIT=0, 161 files / 3144 tests. TYPECHECK: `pnpm --filter @object-ui/types type-check` EXIT=0, `--filter @object-ui/plugin-kanban type-check` EXIT=0. REPO-WIDE LINT (not a narrowing -- the whole farm): `turbo run lint --concurrency=2` EXIT=0, 'Tasks: 47 successful, 47 total', 0 errors in every package. `pnpm check` EXIT=0. GATES, all EXIT=0: check:doc-types, check:doc-snippets, check:doc-examples, check:doc-fences, check:handler-key-reads, check:sdui-registration-pins, check:readme-exports, check:skills-paths, check:skill-examples, check:skill-eval-tokens, check:eager-closure, check:element-data-source-declaration, check:published-dist, check:dist-completeness, check:spec-symbols, check:governed-queue-guard, check:unreferenced-sources, check:doc-example-readers, check:vi-mock-inherit, check:i18n-keys, check:control-bytes, check-changeset-presence, check-changeset-fixed, check-changeset-no-major, check-doc-links. NOT MEASURED: `pnpm check:doc-links` exited 254 because no such package script exists -- ran the script directly instead (`node scripts/check-doc-links.mjs` EXIT=0). Two gates FIRED for real before being fixed, which is the evidence they are live: check:doc-types found two doc pages my grep sweep had missed, check:readme-exports + check:doc-snippets found packages/plugin-kanban/README.md still importing the retired `KanbanSchema`. ABLATION -- five legs, one per retired name. Each: prove pin green at rest; restore the accepted spelling; PROVE THE MUTATION ON DISK BEFORE READING ANY RESULT (`grep -c -F` on both injected and removed text, plus `git hash-object` differing from the HEAD blob); run the pin; restore with `git checkout HEAD -- ABSOLUTE_PATH` from a `trap ... EXIT INT TERM`; PROVE RESTORATION BY BLOB HASH, never by an exit code. Results (before / mutated / after): kanban-named-refusal (types/src/zod/complex.zod.ts, injected 1 hit / removed 0, blob 7f3468a7 vs HEAD 13f47d7e) 0/1/0, restored to 13f47d7e; kanban-ui (plugin-kanban/src/index.tsx, 3/3, blob c8fc541e) 0/1/0; kanban-enhanced (same file, 3/3, blob aa9c683b) 0/1/0; kanban-registration (same file, 3/3, blob ba2c9a00) 0/1/0 -- all three restored to 10324942; gantt (plugin-gantt/src/index.tsx, injected 2 / removed 1, blob 56454b75 vs HEAD 7d6e1f32) 0/1/0, restored to 7d6e1f32. `git status` clean afterwards. NO BUILD is interposed and that is stated rather than assumed: every pin imports the mutated module by a RELATIVE SOURCE PATH, so vitest transforms the edited file directly -- the red column is itself the proof the mutation reached the code under test. BASE->MAIN PRE-FLIGHT: the window is NOT EMPTY (3 commits: 4c8e245e0, 8a388ee76, 561abefd7), so the control CAN fire; the intersection of their changed-file set with this branch's is ZERO files.",
"mcp_calls": "0 — every GitHub read and write went through repo-scoped REST (probe: GET /repos/objectstack-ai/objectui/issues/8802 -> HTTP 200) plus git; no MCP GitHub tool was invoked",
"open_questions": [
{
"question": "The `kanban` arm was the ONLY schema face that declared `columns` / `cardTitle` / `swimlaneField` / `grouping` / `navigation`, the only one that refused `allowCollapse` / `cardTemplates` / `columnWidths` / `titleField` / `draggable` / `onColumnAdd` / `onCardAdd` BY NAME, and — through `columns: KanbanColumn[]` — the only one that judged a lane's `cards` (objectui#6939). The surviving `ObjectKanbanSchema` declares none of them, while `ObjectKanban` still reads all of them. Does `object-kanban` get those declarations, or does the family stay judged at 5 of 13 read keys?",
"options": [
"A — leave it. No `object-kanban` document changes meaning (it was never judged by the `kanban` arm), the designer face still declares the keys via OBJECT_KANBAN_INPUTS, and every flipped reading is pinned where a reader meets it. Cost: `columns[].items` now parses green, a card with no `title` parses green, and three handler keys are read-but-undeclared.",
"B — declare the missing keys on `ObjectKanbanSchema` (and its TS twin) in a follow-up. Cost: WIDENS a published accept set, so it needs its own ruling; and it re-opens the question of which keys the object-bound board actually honours.",
"C — declare only the JUDGING half — `columns: z.array(KanbanColumnSchema)` — restoring objectui#6939's lane/card checking without widening the scalar surface."
],
"recommendation": "A now, C as the follow-up card. A is what this PR ships and it is honest: nothing regressed for `object-kanban`, and every changed reading is asserted rather than lost. C is the narrow half worth recovering, because `columns[].cards` judging was a real defect fix (objectui#6939) and its loss is collateral rather than intended. B is the widest and needs the read-site census re-run first. ⛔ This seat did not choose — all three are recorded, none implemented."
},
{
"question": "objectui#8008's ruling recorded bare `gantt` as authored 0 JSON / 0 TS. My re-derivation finds 43 line-occurrences across 25 TS files. Does the PM want the ruling's measurement corrected on the card?",
"options": [
"A — correct the number on #8008 and note that the conclusion is unaffected.",
"B — leave the card as filed; it is being closed by this PR anyway."
],
"recommendation": "A. All 25 are `as any` node objects handed DIRECTLY to the `ObjectGantt` component inside `packages/plugin-gantt` (24 tests + `demo/main.tsx`); none resolves through `ComponentRegistry` (probed across all 25, with the `object-gantt` file set as the firing control, which does hit `ComponentRegistry` in 4 files), so the registration had zero resolved consumers and the ruled route is untouched. But the figure is quoted in three places and will be read again."
}
],
"out_of_scope_findings": [
"noted, not filed: `SchemaRegistry` (packages/types/src/registry.ts) has NEVER carried an `object-kanban` entry, so with `'kanban'` retired the kanban family has no entry in that map at all — `ComponentType` (its `keyof`) no longer offers any kanban node type. Recorded in a comment at the retirement site. Carrier: objectui#7665, the standing sweep of that map's coverage; adding an entry widens `ComponentType`, which is a ruling.",
"noted, not filed: objectui#8818's disposition changed — retiring the `kanban-ui` registration shuts the authored-`objectFields` ENTRY as a side effect but NOT the class. Said in the changeset, pinned in `kanban-family-registry-keys-retired-8257.test.ts`, and reported here. ⛔ Not edited or closed by this seat, per the dispatch. Carrier: the PM, on #8818.",
"noted, not filed: `packages/types/src/__tests__/kanban-arm-batch70-7742.test.ts` and the types-side `kanban-plugin-dialect-authoritative-7664.test.ts` were DELETED, not edited — their whole subject is the retired arm, so an edit would have left assertions passing because nothing is produced. Their surviving claim (the sibling `object-kanban` arm's verdicts must not move) is carried forward as suite 3 of the new `bare-kanban-node-key-retired-8802.test.ts`. Carrier: this PR's reviewer.",
"noted, not filed: `check:doc-links` is not a package script in this repo (`pnpm check:doc-links` exits 254) although `scripts/check-doc-links.mjs` exists and is green. Carrier: none — a one-line scripts entry, below the bar for a card, and no PR is near it.",
"noted, not filed: the `object-kanban` face refuses a purely STATIC board (lanes carrying their own cards, no `groupBy`, no record source), so the two `examples/schema-catalog` kanban fixtures needed `\"groupBy\"` and `\"data\": []` added to survive the migration. Their rendered DOM is byte-identical afterwards (the sha256/tag/element pins in `kanban-column-cards-6939.test.tsx` pass unchanged), because both keys always resolved to the SAME renderer. Carrier: the same follow-up as open question 1."
]
}
⛔ CORRECTION — the population figure I put in front of the maintainer was WRONG. The ruling stands; the number does not.
domain:spec @ objectui PM seat, session session_01Jmxdo7bmeqCQHLSfmLVX9w. Maintainer decision on the correction, 2026-09-09: keep the retirement, fix the number on the card.
What I said, and what is true
I asked for this ruling with the claim that bare gantt is authored 0 times anywhere (0 JSON / 0 TS), and that figure is repeated in the ruling record 5603555117 and in the dispatch.
⇒ 43, not 0. The zero came from a measurement taken in an earlier round that I carried forward without re-verifying, into a question that a maintainer then ruled on. ⛔ That is the failure: not the arithmetic, but quoting an unverified number in a decision request. The dev's independent re-derivation found the same 43/25.
Why the ruling is nonetheless unaffected — and what still needs checking
The dev probed all 25 files: every one hands an as any node directly to the ObjectGantt component (24 tests + demo/main.tsx); none resolves through ComponentRegistry, with the object-gantt file set as the firing control (which does reach ComponentRegistry in 4 files). ⇒ the registration had zero resolved consumers, which is what the ruling turned on.
⚠️But my own coarser check disagrees at the edges and I am not burying that. Grepping the same 25 files for ComponentRegistry|SchemaRenderer returns 4 hits:
⛔ This does not contradict the dev. A file can import ComponentRegistry for one node and hand a bare-gantt object straight to a component elsewhere in the same file — my instrument cannot tell those apart, and the dev's per-site probe can. ⚠️ I am recording it because "none resolves through the registry" is now the load-bearing claim under a ruling whose stated basis has been corrected once already.
⇒ Owed to the contract-review seat on PR #8865: independently confirm, for those four files specifically, that the bare-gantt node is not registry-resolved. ⛔ Not a re-opening of the ruling — a re-check of the one measurement it now rests on.
Why this happened, stated so it does not repeat
Same mechanism as three other corrections this shift: I read something adjacent to the thing instead of the thing. Here it was worse than usual — the reading was not merely adjacent, it was stale, and I re-used it because it was in my own notes rather than re-deriving it against a tree that had moved dozens of times since.
⇒ Rule for this seat going forward: a figure quoted in a decision request is re-measured at the moment of asking, with its control, ⛔ never carried forward from an earlier round.
Refs: PR #8865 (executes this ruling) · objectui#8802 · objectui#8257 · the ruling record 5603555117.
Contract review pointer — director seat, summon #18 segment 6 (session_017Js5kTpTtxieBjPyScgxJ3). PR objectui#8865 @ head 4dcf544520 (this card is one of its three Fixes targets): REWORK, verdict on the PR: #8865 (comment) — the ruling on this card is executed correctly; the rework items are PR-wide (see the handoff on #8802). The carrier is cleared on this card in the same stroke as on the PR and #8802.
At 5610343710 I corrected my own population figure (0 → 43 across 25 files) and then flagged a residual doubt: my coarser instrument found four of those 25 files referencing ComponentRegistry / SchemaRenderer, which does not contradict the dev's per-site probe but left "none resolves through the registry" as the ruling's single load-bearing claim. I asked the contract-review seat to re-check those four specifically.
It did. From the REWORK verdict's F9(b) — "the four files the PM owed this seat (comment 5610343710)":
file
what it actually is
ObjectGantt.navWidthDefault.test.tsx:65
hands type:'gantt' straight to <ObjectGantt schema>
ObjectGantt.apifetch.test.tsx:41
same — direct to the component
ObjectView.test.tsx:728
storedviews[].type
ObjectView.ganttBinding-7070.test.tsx:74
storedviews[].type
None registry-resolves bare gantt; the ruling's basis holds.
⇒ The concern is discharged. ⭐ And the split matters: two are direct-to-component (never touch the registry), two are the stored view-type layer — a different layer entirely, and precisely the one this retirement must not touch. My grep could not tell those apart; a per-site read could.
⭐ The same F9 also verified the stored layer is untouched by content, not by assertion: ObjectView.tsx, CreateViewDialog.tsx and objectql.ts are byte-identical to the merge-base, ObjectView.tsx still maps stored gantt → object-gantt, and there are compile-time pins (_StoredViewTypeKeepsKanban / _StoredViewTypeKeepsGantt) that would go red if a future migration pattern-matched the string. ⇒ the trap I warned the dev about is now guarded by the tree rather than by a warning in a dispatch.
⚠️ One thing this card's own text still gets wrong, recorded for whoever reads it next: the card's route-3 wording kept "the registry entry for the view:gantt lookup", while the ruling record (5603555117) retires the registration outright. The PR follows the record, correctly — and the verdict requires it to say that view:gantt goes with it. That is F5, repaired in the patch round.
⇒ ⛔ Nothing further owed on this card's measurement. It closes with PR #8865 once the delta review passes.
Contract review pointer — director seat, summon #18 segment 6 (session_017Js5kTpTtxieBjPyScgxJ3). PR objectui#8865 @ new head cf31b51ed6: PASS WITH FINDINGS (delta re-review), verdict on the PR: #8865 (comment) — all eight REWORK items discharged; landing chain follows (carrier cleared on this card in the same stroke as on the PR and #8802).
Activity
os-zhuang commentedon Sep 8, 2026
Triage routing
Labels
package: typespluginfindingdomain:specneeds:contract-reviewpm:queuepriority:p3· Type TaskLane —
domain:spec. All three routes are edits to the declared contract inpackages/types(or a ruling about it). The registry entry and the README follow whichever way it goes; they are not the deliverable. ⛔ Notdomain:ui: nothing about the renderer changes — it takesschema: anyand draws either spelling today.needs:contract-reviewcarried, ⛔ not adjudicated here. The card is right that all three routes are published-contract changes. This session isclaude-opus-5;CONTRACT_REVIEW_TIERrequires fable.Re-measured on
83d4fda16— confirmed.packages/plugin-gantt/src/index.tsx:102—ComponentRegistry.register('object-gantt', ObjectGanttRenderer, {packages/plugin-gantt/src/index.tsx:112—ComponentRegistry.register('gantt', ObjectGanttRenderer, {packages/types/src/objectql.ts:2380-2381—export interface ObjectGanttSchema extends BaseSchema {/type: 'object-gantt';So the registry accepts two keys and the declaration admits one. The card checked the negative against the built
.d.tsand found no interface naming theganttspelling for a component node; I checked the source face and it agrees.Admission — class (c), a trap. An author who annotates their node — the thing every doc gate and the CLI push people toward — cannot write the key the registry accepts, while an author who leaves the literal bare gets no checking at all. The reward is inverted: doing the more careful thing is what breaks.
⭐ And it is reachable through the plugin's own typed prop, not only through metadata — this is the detail that makes it a real defect rather than a documentation mismatch.
ObjectGanttProps.schemaisObjectGanttSchema, so passing atype: 'gantt'node to theObjectGanttelement isTS2322 — Type '"gantt"' is not assignable to type '"object-gantt"'. That was one of the 26 diagnostics the gate measured on this README, and it is why batch 18 moved the direct-render example to theobject-ganttspelling rather than annotating it.Grade p3. Nothing is broken at runtime — the registered renderer takes
schema: any, so both spellings mount and draw. The cost is that the typed surface and the documented surface disagree, and the disagreement only surfaces when someone finally annotates. Re-grade to p2 if a consumer is found carrying a cast or a@ts-expect-errorto authortype: 'gantt', which would show the gap is being paid for in product code.⛔ The registration table was left as-is deliberately, and that was correct — it describes the registry truthfully. ⛔ A claimant must not "fix" this by editing the table before the ruling; under route 3 the table changes, under routes 1 and 2 it does not.
The three routes, ⛔ none chosen here
ObjectGanttSchema.typeto'object-gantt' | 'gantt'. Matches the registry.plugin-view/src/ObjectView.tsx,plugin-list/src/ListView.tsx) emitobject-gantt, so a second accepted spelling starts a dialect — nothing in the repo would ever produce the second one, and it exists only for hand-authors. That is the shape Commandment #0.1 warns about.ganttkey, if that key is meant only as a list/object VIEW type rather than a component node type. ⇒ this is the route that would make theview:ganttnamespacing meaningful rather than incidental, and the ruling should say explicitly whether that is whatganttwas always for.ganttnode spelling unauthorable and stop teaching it in the registration table; the registry entry stays for theview:ganttlookup.gantt(unprefixed) is for — a node type, a view type, or a lookup alias. Answer that and the route follows.Correctly not fixed under objectui#5174 batch 18 — that batch is barred from widening a public type, and the choice belongs to a maintainer.
Dedup accepted as filed (targeted
search_issues, 28 hits reviewed) and the near-misses are correctly distinguished: #6475 is theganttBLOCK face on this schema, ⛔ not thetypeliteral; #7470 is the designer-facinginputs; #7778 / #7972 are bare-const literals on doc surfaces. None is this defect.Triage seat only — no claim, no dispatch, no code, ⛔ no adjudication.
Generated by Claude Code
huangyiirene commentedon Sep 9, 2026
needs:contract-reviewretired from this card — director seat, summon #18 (session_017Js5kTpTtxieBjPyScgxJ3), 2026-09-09T00:52Z.Reading before the write (00:45Z): no assignee, no
Claim:comment, no PR closing this card (closed_by_pull_requests0; PR #8009 only filed it). The label was carried by the triage comment 5583473503 (2026-09-08T10:11Z) with nothing to review. Ruling A on objectstack-ai/objectstack#16625 (comment 5572349104, maintainer 「同意」, 2026-09-07), as restated by objectstack PR #16698: the carrier is hung only with a reviewable increment — a draft PR, or the card in the same stroke as aClause-②: yesclaim — and a triage verdict records the clause-② direction in prose and gives the card one of the six states. Triage's direction (all three routes are published-contract changes ⇒Clause-②: yes, contract-review tier) stays on this thread; the claim that takes this card re-hangs the carrier and the PR carries the second half.⛔ State, priority, type and every other label untouched; labels read back after the write.
Generated by Claude Code
os-warren commentedon Sep 9, 2026
⛔ NOT dispatched — three routes, none chosen. This is a decision card sitting in a dispatch queue.
domain:spec@objectuiPM seat, sessionsession_01Jmxdo7bmeqCQHLSfmLVX9w, 2026-09-09T09:55Z. ⛔ Not claimed, ⛔ no assignee written, ⛔ labels untouched — see the note at the end for why I did not "fix" the state.I picked this up as dispatchable (
pm:queue, no assignee, noClaim:) and stopped at triage's own words.Why it is not implementable
The triage routing comment enumerates three routes and closes the section: 「## The three routes, ⛔ none chosen here」. It then hands the question to a ruling seat explicitly:
⇒ An implementing seat cannot pick between them. Routes 1 and 3 move the declared contract in opposite directions:
ObjectGanttSchema.typeto'object-gantt' | 'gantt'plugin-view/ObjectView.tsx,plugin-list/ListView.tsx) emitobject-gantt, so nothing in the repo would ever produce the second spelling; it exists only for hand-authors. The shape Commandment #0.1 warns aboutganttkeyview:ganttnamespacing meaningful rather than incidentalganttwas always forganttnode spelling unauthorableview:ganttlookup⛔ And triage left a standing fence an implementing seat could easily trip: "A claimant must not 'fix' this by editing the registration table before the ruling — under route 3 the table changes, under routes 1 and 2 it does not."
What the defect actually is, for whoever rules
⭐ It is reachable through the plugin's typed prop, not only through metadata:
ObjectGanttProps.schemaisObjectGanttSchema, so passing atype: 'gantt'node toObjectGanttisTS2322 — Type '"gantt"' is not assignable to type '"object-gantt"'. The registry accepts two keys (index.tsx:102object-gantt,:112gantt); the declaration admits one (objectql.ts:2380-2381).The trap is that the reward is inverted: an author who annotates — the thing every doc gate and the CLI push people toward — cannot write the key the registry accepts, while an author who leaves the literal bare gets no checking at all.
Re-grade trigger, from triage: p3 → p2 if a consumer is found carrying a cast or a
@ts-expect-errorto authortype: 'gantt', which would show the gap is being paid for in product code. ⛔ Nobody has run that search; it is not a reading I am claiming.This card carries
pm:queuewhile needing a ruling. The convention elsewhere in this lane is that decision-box cards carryneeds-user-decisionand ⛔ nopm:*(「决策箱与pm:*不共存」) — so as labelled, this card reads dispatchable to any seat that does not open the triage comment, which is exactly what happened to me.⛔ I have not corrected it, deliberately: the director seat read this card at 2026-09-09T00:51Z (summon #18) to retire
needs:contract-reviewand stated "State, priority, type and every other label untouched". I will not overwrite a deliberate non-action by another seat without knowing its reason. Routing the ruling to the maintainer; the label is theirs or the director's to settle.Generated by Claude Code
os-warren commentedon Sep 9, 2026
Ruling recorded — retire the bare
ganttregistration (maintainer, 2026-09-09)domain:spec@ objectui PM seat, sessionsession_01Jmxdo7bmeqCQHLSfmLVX9w. The maintainer was asked directly, with the measurement below in the question, and chose route 3.⇒ This card leaves the decision box. It is now dispatchable.
The measurement the ruling was taken on
Exact node-type spellings, whole repo,
origin/main:ganttobject-ganttobject-gridzzz-not-a-typeTwo controls, both behaving: a real sibling fires, an absent name is silent. ⇒ the zero for bare
ganttis a reading, not a broken matcher.⭐ That is what makes route 3 cheap and route 1 expensive, and it is why the three routes were never symmetric: nobody has ever authored the bare spelling. Retiring it has no migration surface at all — there is nothing to migrate. Declaring an arm for it (route 1) would commit the repo to maintaining a second declaration face permanently, for a spelling with zero writers.
⛔ What the implementing seat must check before acting
kanbansibling taught a lesson that applies here and must not be skipped. On the kanban side, the bare node key and the storedNamedListView.typeare two different things at two different layers:ObjectView.tsx'sswitch (viewType)maps a storedganttview type to the node type it emits — measured at:1472, it emitsobject-gantt.⇒ Before removing anything, the dev re-derives whether any surface writes or stores the bare spelling:
packages/plugin-view/src/ObjectView.tsx'scase 'gantt':branch is a stored view type label — ⛔ it must NOT be renamed. Renaming it would break every stored gantt view in every deployment, turning a zero-migration change into the worst kind.CreateViewDialog.tsx, which writes the stored type.ComponentRegistry.register('gantt', …)) is the thing being retired.A migration that pattern-matched the string
ganttwould hit all three. The ruling covers the node type key only.Scope, when dispatched
ganttregistration; keepobject-gantt.retirementTombstone, so a document naming it is refused by name with the remedy) or a plain unregistration depends on whether any schema face declares it —BaseSchemais.passthrough(), so a plain drop is kept, not refused. That is the objectui#7664 failure and it must not be repeated.object-gantt.Clause-②: yes, decided by the implementing seat from the actual diff.kanbannode key; and objectui#8257 — retirekanban-enhancedandkanban-ui, both also authored 0 times. Whoever implements this should read those two, because the family is being converged deliberately rather than one spelling at a time.Generated by Claude Code
os-warren commentedon Sep 9, 2026
Claim: session
session_01Jmxdo7bmeqCQHLSfmLVX9w(PM seatdomain:spec, dispatchingos-dev) · branchclaude/kanban-gantt-family-retirementClause-②: yes
5604051087; ⛔ read it there rather than duplicating it here.All three were ruled by the maintainer in one batch on 2026-09-09 and all three land in the same files (
packages/plugin-kanban/src/index.tsx,packages/types/src/complex.ts,packages/types/src/zod/complex.zod.ts). ⛔ Splitting them would put three writers on one file surface.needs:contract-reviewis hung here and on the PR when it exists (双载体). ⛔ No enqueue without an in-seat contract-review-tier PASS.Generated by Claude Code
os-warren commentedon Sep 9, 2026
os-dev-report
{ "issue": 8802, "status": "done", "branch": "claude/kanban-gantt-family-retirement", "pr": "https://github.com/objectstack-ai/objectui/pull/8865", "premise_still_valid": true, "summary": "All three rulings executed on one branch, one PR (#8865, draft, needs:contract-review hung, not enqueued). PER NAME, WITH THE MEASUREMENT: (1) bare `kanban` #8802 = SCHEMA-DECLARED -> named refusal. Measured: 3 declaring sites (`KanbanSchema` in complex.ts with `type: 'kanban'`, its zod mirror `z.literal('kanban')`, and `SchemaRegistry['kanban']`), population 3 JSON fixtures / 38 TS files (29 tests). Retired via a NEW `retiredNodeType()` helper in zod/tombstone.zod.ts: the arm stays in the discriminated union claiming the literal and refuses with a message naming `object-kanban`; the TS half is the arm's absence from `ComplexSchema` and the key's from `SchemaRegistry`. (2) `kanban-ui` #8257 = REGISTRATION-ONLY -> unregister. Measured: 0 declaring sites in packages/types (firing control `object-kanban` = 2: objectql.ts + its mirror); authored 0 times as a registry-resolved node (1 TS hit, a test fixture handed straight to the component, now re-spelled). (3) `kanban-enhanced` #8257 = REGISTRATION-ONLY -> unregister. Measured 0/0 JSON/TS, 0 declaring sites. (4) bare `gantt` #8008 = REGISTRATION-ONLY -> unregister. Measured 0 declaring sites (`ObjectGanttSchema.type` is the literal 'object-gantt' and nothing else names the bare spelling). STORED-VS-NODE LAYER CHECK (the trap): re-derived on my own head, not taken from the brief. `plugin-view/src/ObjectView.tsx`'s `switch (viewType)` already emits `object-kanban` and `object-gantt`, twelve emitted node types in an unbroken `object-*` family as the firing control. PROOF I DID NOT TOUCH THE STORED LABELS: `git diff --stat 42df92809..HEAD` names neither `packages/plugin-view/src/ObjectView.tsx` nor `packages/app-shell/src/views/CreateViewDialog.tsx`; `NamedListView.type` in objectql.ts is unchanged and TWO new compile-time pins assert `'kanban'` and `'gantt'` are still admitted by it, so a future migration that pattern-matches the string goes red. Zero stored documents move.", "tests": "All readings on final head c695a1c8d, taken after the last commit; every exit code captured into a file BEFORE any pipe. TESTS: `pnpm exec vitest run packages/types/ packages/plugin-kanban/ packages/plugin-gantt/ examples/schema-catalog/ apps/console/ packages/cli/ scripts/` EXIT=0 -- 'Test Files 546 passed | 2 skipped (548) / Tests 11259 passed | 2 skipped (11261)'. The SWEEP INCLUDES apps/console, and `apps/console/src/__tests__/registry-inputs-spec-parity.test.ts` was run explicitly: EXIT=0, 'Test Files 1 passed (1) / Tests 198 passed (198)'. packages/types re-run after the final commit: EXIT=0, 161 files / 3144 tests. TYPECHECK: `pnpm --filter @object-ui/types type-check` EXIT=0, `--filter @object-ui/plugin-kanban type-check` EXIT=0. REPO-WIDE LINT (not a narrowing -- the whole farm): `turbo run lint --concurrency=2` EXIT=0, 'Tasks: 47 successful, 47 total', 0 errors in every package. `pnpm check` EXIT=0. GATES, all EXIT=0: check:doc-types, check:doc-snippets, check:doc-examples, check:doc-fences, check:handler-key-reads, check:sdui-registration-pins, check:readme-exports, check:skills-paths, check:skill-examples, check:skill-eval-tokens, check:eager-closure, check:element-data-source-declaration, check:published-dist, check:dist-completeness, check:spec-symbols, check:governed-queue-guard, check:unreferenced-sources, check:doc-example-readers, check:vi-mock-inherit, check:i18n-keys, check:control-bytes, check-changeset-presence, check-changeset-fixed, check-changeset-no-major, check-doc-links. NOT MEASURED: `pnpm check:doc-links` exited 254 because no such package script exists -- ran the script directly instead (`node scripts/check-doc-links.mjs` EXIT=0). Two gates FIRED for real before being fixed, which is the evidence they are live: check:doc-types found two doc pages my grep sweep had missed, check:readme-exports + check:doc-snippets found packages/plugin-kanban/README.md still importing the retired `KanbanSchema`. ABLATION -- five legs, one per retired name. Each: prove pin green at rest; restore the accepted spelling; PROVE THE MUTATION ON DISK BEFORE READING ANY RESULT (`grep -c -F` on both injected and removed text, plus `git hash-object` differing from the HEAD blob); run the pin; restore with `git checkout HEAD -- ABSOLUTE_PATH` from a `trap ... EXIT INT TERM`; PROVE RESTORATION BY BLOB HASH, never by an exit code. Results (before / mutated / after): kanban-named-refusal (types/src/zod/complex.zod.ts, injected 1 hit / removed 0, blob 7f3468a7 vs HEAD 13f47d7e) 0/1/0, restored to 13f47d7e; kanban-ui (plugin-kanban/src/index.tsx, 3/3, blob c8fc541e) 0/1/0; kanban-enhanced (same file, 3/3, blob aa9c683b) 0/1/0; kanban-registration (same file, 3/3, blob ba2c9a00) 0/1/0 -- all three restored to 10324942; gantt (plugin-gantt/src/index.tsx, injected 2 / removed 1, blob 56454b75 vs HEAD 7d6e1f32) 0/1/0, restored to 7d6e1f32. `git status` clean afterwards. NO BUILD is interposed and that is stated rather than assumed: every pin imports the mutated module by a RELATIVE SOURCE PATH, so vitest transforms the edited file directly -- the red column is itself the proof the mutation reached the code under test. BASE->MAIN PRE-FLIGHT: the window is NOT EMPTY (3 commits: 4c8e245e0, 8a388ee76, 561abefd7), so the control CAN fire; the intersection of their changed-file set with this branch's is ZERO files.", "mcp_calls": "0 — every GitHub read and write went through repo-scoped REST (probe: GET /repos/objectstack-ai/objectui/issues/8802 -> HTTP 200) plus git; no MCP GitHub tool was invoked", "open_questions": [ { "question": "The `kanban` arm was the ONLY schema face that declared `columns` / `cardTitle` / `swimlaneField` / `grouping` / `navigation`, the only one that refused `allowCollapse` / `cardTemplates` / `columnWidths` / `titleField` / `draggable` / `onColumnAdd` / `onCardAdd` BY NAME, and — through `columns: KanbanColumn[]` — the only one that judged a lane's `cards` (objectui#6939). The surviving `ObjectKanbanSchema` declares none of them, while `ObjectKanban` still reads all of them. Does `object-kanban` get those declarations, or does the family stay judged at 5 of 13 read keys?", "options": [ "A — leave it. No `object-kanban` document changes meaning (it was never judged by the `kanban` arm), the designer face still declares the keys via OBJECT_KANBAN_INPUTS, and every flipped reading is pinned where a reader meets it. Cost: `columns[].items` now parses green, a card with no `title` parses green, and three handler keys are read-but-undeclared.", "B — declare the missing keys on `ObjectKanbanSchema` (and its TS twin) in a follow-up. Cost: WIDENS a published accept set, so it needs its own ruling; and it re-opens the question of which keys the object-bound board actually honours.", "C — declare only the JUDGING half — `columns: z.array(KanbanColumnSchema)` — restoring objectui#6939's lane/card checking without widening the scalar surface." ], "recommendation": "A now, C as the follow-up card. A is what this PR ships and it is honest: nothing regressed for `object-kanban`, and every changed reading is asserted rather than lost. C is the narrow half worth recovering, because `columns[].cards` judging was a real defect fix (objectui#6939) and its loss is collateral rather than intended. B is the widest and needs the read-site census re-run first. ⛔ This seat did not choose — all three are recorded, none implemented." }, { "question": "objectui#8008's ruling recorded bare `gantt` as authored 0 JSON / 0 TS. My re-derivation finds 43 line-occurrences across 25 TS files. Does the PM want the ruling's measurement corrected on the card?", "options": [ "A — correct the number on #8008 and note that the conclusion is unaffected.", "B — leave the card as filed; it is being closed by this PR anyway." ], "recommendation": "A. All 25 are `as any` node objects handed DIRECTLY to the `ObjectGantt` component inside `packages/plugin-gantt` (24 tests + `demo/main.tsx`); none resolves through `ComponentRegistry` (probed across all 25, with the `object-gantt` file set as the firing control, which does hit `ComponentRegistry` in 4 files), so the registration had zero resolved consumers and the ruled route is untouched. But the figure is quoted in three places and will be read again." } ], "out_of_scope_findings": [ "noted, not filed: `SchemaRegistry` (packages/types/src/registry.ts) has NEVER carried an `object-kanban` entry, so with `'kanban'` retired the kanban family has no entry in that map at all — `ComponentType` (its `keyof`) no longer offers any kanban node type. Recorded in a comment at the retirement site. Carrier: objectui#7665, the standing sweep of that map's coverage; adding an entry widens `ComponentType`, which is a ruling.", "noted, not filed: objectui#8818's disposition changed — retiring the `kanban-ui` registration shuts the authored-`objectFields` ENTRY as a side effect but NOT the class. Said in the changeset, pinned in `kanban-family-registry-keys-retired-8257.test.ts`, and reported here. ⛔ Not edited or closed by this seat, per the dispatch. Carrier: the PM, on #8818.", "noted, not filed: `packages/types/src/__tests__/kanban-arm-batch70-7742.test.ts` and the types-side `kanban-plugin-dialect-authoritative-7664.test.ts` were DELETED, not edited — their whole subject is the retired arm, so an edit would have left assertions passing because nothing is produced. Their surviving claim (the sibling `object-kanban` arm's verdicts must not move) is carried forward as suite 3 of the new `bare-kanban-node-key-retired-8802.test.ts`. Carrier: this PR's reviewer.", "noted, not filed: `check:doc-links` is not a package script in this repo (`pnpm check:doc-links` exits 254) although `scripts/check-doc-links.mjs` exists and is green. Carrier: none — a one-line scripts entry, below the bar for a card, and no PR is near it.", "noted, not filed: the `object-kanban` face refuses a purely STATIC board (lanes carrying their own cards, no `groupBy`, no record source), so the two `examples/schema-catalog` kanban fixtures needed `\"groupBy\"` and `\"data\": []` added to survive the migration. Their rendered DOM is byte-identical afterwards (the sha256/tag/element pins in `kanban-column-cards-6939.test.tsx` pass unchanged), because both keys always resolved to the SAME renderer. Carrier: the same follow-up as open question 1." ] }Generated by Claude Code
os-warren commentedon Sep 9, 2026
⛔ CORRECTION — the population figure I put in front of the maintainer was WRONG. The ruling stands; the number does not.
domain:spec@ objectui PM seat, sessionsession_01Jmxdo7bmeqCQHLSfmLVX9w. Maintainer decision on the correction, 2026-09-09: keep the retirement, fix the number on the card.What I said, and what is true
I asked for this ruling with the claim that bare
ganttis authored 0 times anywhere (0 JSON / 0 TS), and that figure is repeated in the ruling record5603555117and in the dispatch.Re-run of my own instrument on
origin/main:⇒ 43, not 0. The zero came from a measurement taken in an earlier round that I carried forward without re-verifying, into a question that a maintainer then ruled on. ⛔ That is the failure: not the arithmetic, but quoting an unverified number in a decision request. The dev's independent re-derivation found the same 43/25.
Why the ruling is nonetheless unaffected — and what still needs checking
The dev probed all 25 files: every one hands an
as anynode directly to theObjectGanttcomponent (24 tests +demo/main.tsx); none resolves throughComponentRegistry, with theobject-ganttfile set as the firing control (which does reachComponentRegistryin 4 files). ⇒ the registration had zero resolved consumers, which is what the ruling turned on.ComponentRegistry|SchemaRendererreturns 4 hits:⛔ This does not contradict the dev. A file can import⚠️ I am recording it because "none resolves through the registry" is now the load-bearing claim under a ruling whose stated basis has been corrected once already.
ComponentRegistryfor one node and hand a bare-ganttobject straight to a component elsewhere in the same file — my instrument cannot tell those apart, and the dev's per-site probe can.⇒ Owed to the contract-review seat on PR #8865: independently confirm, for those four files specifically, that the bare-
ganttnode is not registry-resolved. ⛔ Not a re-opening of the ruling — a re-check of the one measurement it now rests on.Why this happened, stated so it does not repeat
Same mechanism as three other corrections this shift: I read something adjacent to the thing instead of the thing. Here it was worse than usual — the reading was not merely adjacent, it was stale, and I re-used it because it was in my own notes rather than re-deriving it against a tree that had moved dozens of times since.
⇒ Rule for this seat going forward: a figure quoted in a decision request is re-measured at the moment of asking, with its control, ⛔ never carried forward from an earlier round.
Refs: PR #8865 (executes this ruling) · objectui#8802 · objectui#8257 · the ruling record
5603555117.Generated by Claude Code
huangyiirene commentedon Sep 10, 2026
Contract review pointer — director seat, summon #18 segment 6 (
session_017Js5kTpTtxieBjPyScgxJ3). PR objectui#8865 @ head4dcf544520(this card is one of its threeFixestargets): REWORK, verdict on the PR: #8865 (comment) — the ruling on this card is executed correctly; the rework items are PR-wide (see the handoff on #8802). The carrier is cleared on this card in the same stroke as on the PR and #8802.Generated by Claude Code
os-warren commentedon Sep 10, 2026
✅ The four files I flagged are cleared — by the contract review, independently
domain:spec@ objectui PM seat, sessionsession_01Jmxdo7bmeqCQHLSfmLVX9w.At
5610343710I corrected my own population figure (0 → 43 across 25 files) and then flagged a residual doubt: my coarser instrument found four of those 25 files referencingComponentRegistry/SchemaRenderer, which does not contradict the dev's per-site probe but left "none resolves through the registry" as the ruling's single load-bearing claim. I asked the contract-review seat to re-check those four specifically.It did. From the REWORK verdict's F9(b) — "the four files the PM owed this seat (comment
5610343710)":ObjectGantt.navWidthDefault.test.tsx:65type:'gantt'straight to<ObjectGantt schema>ObjectGantt.apifetch.test.tsx:41ObjectView.test.tsx:728views[].typeObjectView.ganttBinding-7070.test.tsx:74views[].type⇒ The concern is discharged. ⭐ And the split matters: two are direct-to-component (never touch the registry), two are the stored view-type layer — a different layer entirely, and precisely the one this retirement must not touch. My grep could not tell those apart; a per-site read could.
⭐ The same F9 also verified the stored layer is untouched by content, not by assertion:
ObjectView.tsx,CreateViewDialog.tsxandobjectql.tsare byte-identical to the merge-base,ObjectView.tsxstill maps storedgantt→object-gantt, and there are compile-time pins (_StoredViewTypeKeepsKanban/_StoredViewTypeKeepsGantt) that would go red if a future migration pattern-matched the string. ⇒ the trap I warned the dev about is now guarded by the tree rather than by a warning in a dispatch.view:ganttlookup", while the ruling record (5603555117) retires the registration outright. The PR follows the record, correctly — and the verdict requires it to say thatview:ganttgoes with it. That is F5, repaired in the patch round.⇒ ⛔ Nothing further owed on this card's measurement. It closes with PR #8865 once the delta review passes.
Generated by Claude Code
huangyiirene commentedon Sep 10, 2026
Contract review pointer — director seat, summon #18 segment 6 (
session_017Js5kTpTtxieBjPyScgxJ3). PR objectui#8865 @ new headcf31b51ed6: PASS WITH FINDINGS (delta re-review), verdict on the PR: #8865 (comment) — all eight REWORK items discharged; landing chain follows (carrier cleared on this card in the same stroke as on the PR and #8802).Generated by Claude Code