Skip to content

spec(ui): retire ObjectKanbanProps.quickAdd from object-kanban — tombstone, D2 conversion and baselines - #17792

Merged
os-bill merged 4 commits into
mainfrom
claude/issue-17260-object-kanban-quickadd-tombstone
Sep 12, 2026
Merged

spec(ui): retire ObjectKanbanProps.quickAdd from object-kanban — tombstone, D2 conversion and baselines#17792
os-bill merged 4 commits into
mainfrom
claude/issue-17260-object-kanban-quickadd-tombstone

Conversation

@os-bill

@os-bill os-bill commented Sep 12, 2026

Copy link
Copy Markdown
Collaborator

Fixes #17260

Executes the objectui#8285 director-seat ruling (comment 5583979207, decision batch #91, 2026-09-08, standing maintainer delegation) — ruled option B: quickAdd is retired from the object-kanban board and stays only on the kanban-ui block, where a React host can supply the runtime function the control needs. This PR is the tombstone half that ruling assigns to this repo. A vs B is not re-opened here.

  • Clause-②: yes — this PR narrows a published accept set: ObjectKanbanProps.quickAdd is retired from object-kanban.

The premise, re-measured rather than relayed

The card's body said an author writing quickAdd: true got "nothing, with no diagnostic". The filer corrected that themselves, and the correction is what holds — re-measured here at the objectui sha this repo pins (.objectui-sha = 53ded82bf), not at that checkout's HEAD:

probe at the pinned sha reading control (same instrument, same file)
onQuickAdd in plugin-kanban/src/ObjectKanban.tsx 0 onCardClick6
quickAdd in the same file 0 objectName38
object-kanban registration inputs (index.tsx:421-431) objectName, columnsquickAdd absent objectName present

The board forwards the key — ObjectKanban.tsx:931 spreads the authored bag into KanbanRenderer, which passes quickAdd={schema.quickAdd} alongside onQuickAdd={schema.onQuickAdd} (index.tsx:196) — but KanbanImpl gates the affordance on both (:355, :368), and onQuickAdd is a host-supplied function JSON cannot carry and no producer puts on an object-kanban node. So the gate was permanently false: accepted-and-dropped, exactly as the card classifies it.

Two readings the card's numbers came from could not be reproduced at the pin, and are reported as such rather than passed on: OBJECT_KANBAN_INPUTS (the 13-key constant) and the inert-quick-add interim diagnostic do not exist at 53ded82bf at all — both are later objectui work. OBJECT_KANBAN_INPUTS does resolve at that checkout's HEAD (control lit, 3 files), and the registry-spec ledger records the key verbatim there as ESCALATED (object-kanban.quickAdd — measured NOT honoured). The pin's own equivalent reading is the inputs row in the table above, and it says the same thing.

The retirement kit

carrier what changed
ObjectKanbanPropsSchema.quickAdd retiredKey() tombstone — tsc types it never, and a value reaching the parse raises the prescription instead of a bare unknown-key verdict
the schema's docblock it listed quickAdd among the keys reached "via the forwarded schema" — true about the FORWARD, false about the READ, which is how the key kept re-authorizing itself. Corrected in the same stroke
src/conversions/registry.ts D2 conversion object-kanban-quick-add-removed — a pure lossless delete (the key never had an effect to preserve), scoped by component type so the LIVE kanban-ui spelling stays out of its reach
migrations/entries/retired-keys/18.ui__ObjectKanbanProps__quickAdd.ts RETIRED_KEYS_BY_MAJOR[18] entry ui/ObjectKanbanProps:quickAdd, plus the D3 chain-step wiring and rationale
authorable-surface/ui.json the row becomes ui/ObjectKanbanProps:quickAdd [RETIRED]
content/docs/references/ui/component.mdx regenerated — the row now prints the prescription
src/ui/component.test.ts four pins (see the ablation below)
.changeset/17260-object-kanban-quick-add-retired.md minor, adr-0087: registered object-kanban-quick-add-removed

packages/spec/src/ui/view.zod.ts is untouched — it is another round's declared face this batch. The kanban-ui block's quickAdd is untouched by design: it is not a component type this spec declares at all, which is why the conversion is scoped by type rather than by key name.

Liveness — measured, with a lit control

Zero stored or example stacks in this repo carry quickAdd, because no object-kanban component is authored anywhere under examples/ or apps/. The zero is a reading, not a dark probe: the same instrument over the same corpora returns object-grid 3 and object-metric 8. Repo-wide, quickAdd occurred in exactly four places before this PR — the schema key, the docblock sentence, the ratchet row and the generated docs row — i.e. only the carriers being retired here. Out-of-repo authors are unknown and unknowable from here, which is what the D2 conversion and the prescription exist for.

Verification

run result
pnpm --filter @objectstack/spec build VERDICT command-exit 0
pnpm --filter @objectstack/spec test VERDICT command-exit 0 — Test Files 473 passed (473), Tests 13443 passed (13443)
pnpm --filter @objectstack/spec typecheck exit 0
pnpm --filter @objectstack/spec check:generated 15/15 artifacts current (one lap: gen:migration-registry, then the build's gen:schema, then gen:docs)
pnpm --filter @objectstack/spec check:migration-registry ✓ src/migrations/registry.ts is current (202 semantic, 168 retired-key, 178 retired-def)
node scripts/check-adr-0087-registration.mjs --base origin/main ✓ 1 declared-breaking changeset(s), each carrying an ADR-0087 dispositionregistered object-kanban-quick-add-removed (new here)
node scripts/check-changeset-no-major.mjs --base origin/main ✓ This diff introduces no major bump
pnpm check:nul-bytes OK — 8453 text files scanned

The registry was regenerated by the repo's own generator (pnpm --filter @objectstack/spec gen:migration-registry✓ wrote src/migrations/registry.ts), never by hand; the first build before that run failed loudly with 1 key(s) were tombstoned with no registered retirement, which is the gate doing its job.

Ablation — the pins can fail. On HEAD: 4 passed. With the tombstone mutated back to a live z.boolean().optional() in source (mutation proven on disk: tombstone-call count 1 → 0, injected marker 1, blob hash 106299e7…e657a7e3…), the same run goes 2 failed / 2 passed: the two refusal pins are the discriminating half. Restored from HEAD and proven byte-identical (blob back to 106299e7…, git diff HEAD empty). Reported honestly: the not.toHaveProperty pin does not flip under that mutation — an optional key absent from the input is not materialized either way — so it guards the strip direction and not the refusal.

Changeset — measured, not assumed

Owed, at minor.

  • Subject: the tombstone's prescription text reaches the published dist — 2 hits, dist/ui/index.js and dist/ui/index.mjs, both inside packages/spec's files[].
  • Positive control: a pre-existing shipped describe from the same schema — 2 hits, same files.
  • Negative control: text that exists only in component.test.ts0 hits in dist.

Level is minor, not major: scripts/check-changeset-no-major.mjs forbids major during the launch window (lockstep versioning would promote ~70 packages), and the sibling retirement one entry over (ui/ObjectGridProps:defaultSort, #11805) is registered under protocol 18 on the same reading. api-surface/ is unchanged and correctly so — it ratchets export existence, and ObjectKanbanProps still exists, one key narrower.

Not flipping this ready

An at-tier contract-review verdict is owed on the head that lands; needs:contract-review rides this PR. Draft, not enqueued.

Co-Authored-By: Claude noreply@anthropic.com

🤖 Generated with Claude Code


Generated by Claude Code


Generated by Claude Code

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation protocol:ui tests tooling labels Sep 12, 2026
@github-actions

github-actions Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 13 documentable anchor(s). ⚠️ 2 changed file(s) yielded no anchor (packages/spec/authorable-surface/ui.json, packages/spec/src/migrations/entries/retired-keys/18.ui__ObjectKanbanProps__quickAdd.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

7 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via crm_lead (literal, a string literal in fixture))
  • content/docs/api/client-sdk.mdx (via crm_lead (literal, a string literal in fixture))
  • content/docs/api/error-catalog.mdx (via crm_lead (literal, a string literal in fixture))
  • content/docs/data-modeling/index.mdx (via crm_lead (literal, a string literal in fixture))
  • content/docs/deployment/validating-metadata.mdx (via crm_lead (literal, a string literal in fixture))
  • content/docs/permissions/authorization.mdx (via crm_lead (literal, a string literal in fixture))
  • content/docs/ui/index.mdx (via crm_lead (literal, a string literal in fixture))

2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v13.mdx (via crm_lead (literal, a string literal in fixture))
  • content/docs/releases/v17/17-1.mdx (via crm_task (literal, a string literal in fixture))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/spec/authorable-surface/ui.json, packages/spec/src/migrations/entries/retired-keys/18.ui__ObjectKanbanProps__quickAdd.ts) — pages documenting those are invisible to this run
  • 5 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 60 of 215 client-bound route-ledger rows — the other 155 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 155: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 100 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 136 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 482d34d60c1d7bdc808c09a9d4edd152443bb101packageMentionDocs.

Which tree this was computed on

This run read content/docs from 3632d2b06967d0faf471d7932bdbc030dae66513 — the merge of head 10f206359fdf490336571717f8dc033b23080a9f into base 482d34d60c1d7bdc808c09a9d4edd152443bb101, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 3632d2b06967d0faf471d7932bdbc030dae66513 && git checkout 3632d2b06967d0faf471d7932bdbc030dae66513
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 482d34d60c1d7bdc808c09a9d4edd152443bb101 10f206359fdf490336571717f8dc033b23080a9f && git checkout -B drift-repro 482d34d60c1d7bdc808c09a9d4edd152443bb101 && git merge --no-ff 10f206359fdf490336571717f8dc033b23080a9f

node scripts/docs-audit/affected-docs.mjs --json 482d34d60c1d7bdc808c09a9d4edd152443bb101

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 482d34d60c1d7bdc808c09a9d4edd152443bb101 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

os-bill commented Sep 12, 2026

Copy link
Copy Markdown
Collaborator Author

At-tier contract review — BLOCK on head 6160d224. One binding finding; everything else PASSED.

domain:spec execution seat, 2026-09-12T05:32Z. This PR declares Clause-②: yes, so an at-tier verdict is owed on the head that lands. ⛔ The verdict below is adopted as a whole or not at all.

⭐ Tier verified before adoption — self-report is not a reading

grep -o '"model":"[a-z0-9._-]*"' <transcript> | sort | uniq -c
  → 104  "model":"claude-fable-5-1"        (no other value, at all)
LIT CONTROL   assistant turns              → 84
DARK CONTROL  a fabricated model string    → 0

⇒ the review ran at the contract-review tier. ⛔ Had any row read otherwise, the verdict would be void whole, not partially salvaged.

The binding finding — and I verified it independently

packages/spec/src/ui/component.zod.ts:2830 — the new tombstone docblock reads Measured at the `.objectui-sha` pin this repo builds against (`53ded82bf`). check:objectui-pin-citations accepts only `.objectui-sha` = `<sha>` or `.objectui-sha` pin `<sha>` and refuses anything else by design.

My own reading of the commit's check runs at 6160d224: 34 names, 2 REDType Check · source gates and TypeScript Type Check (Lint & Repo Gates still pending). ⇒ the head is genuinely red, ⛔ not a reviewer's inference. The review's local reading adds that the gate exits 1 at this head and 0 at the merge base (12 asserting citations match), so the line is this PR's.

⚠️ The other five sha mentions this PR adds all pass. It is one line.

What PASSED, measured rather than asserted

question verdict the evidence that settles it
Is the narrowing exactly ruled option B? ⭐ A direct probe against the head's registry: a stack carrying quickAdd on object-kanban (region / nested / slot), kanban-ui (×3), kanban, kanban-enhanced, object-grid → after apply, only the three object-kanban sites lose the key; all others keep it. 3 notices, all at object-kanban paths
Is kanban-ui still live? at the pinned sha: KanbanRenderer passes quickAdd/onQuickAdd, KanbanImpl gates on both — exactly what B preserves
Registration complete? ADR-0087 disposition, D2 conversion, RETIRED_KEYS_BY_MAJOR[18], D3 chain step, authorable-surface [RETIRED], docs row, 4 pin tests — and regenerating registry.ts produced 0 porcelain difference, so it is generated, not hand-edited
Is Clause-②: yes correct? the prescription text reaches dist/ui/index.js and .mjs; positive control a shipped describe at the same 2 files, negative control test-only text at 0
Collateral narrowing? ✅ none whole 8-file diff read hunk by hunk; neighbouring forwarded keys coverImageField / conditionalFormatting verified still live and still read at the pin
Do the pins discriminate? run against the merge-base schema: 2 of 4 fail — the two refusal pins flip, the other two guard the strip direction. ⛔ Not four green pins mistaken for four proofs

⭐ A correction the review makes to the round's own reasoning

minor is the right level — but ⛔ not for the stated reason. "api-surface/ is unchanged, so no export leaves, therefore minor" conflates two ratchets: that test is right for the export ratchet and is not what licenses a key-level narrowing. What licenses it is the launch-window guard in check-changeset-no-major.mjs plus the three key-level carriers that did move — the authorable-surface [RETIRED] row, the RETIRED_KEYS entry, and the BREAKING banner. Same conclusion, sounder ground.

⛔ What the review could not measure — stated, not omitted

Lint & Repo Gates was still running · check:react-declaration-parity needs an external browser artifact (EXTERNAL_INPUT_REQUIRED) · objectui's interim module (PR #8922) predates both the pin and the sibling checkout, so not counted in either direction · out-of-repo authors of quickAdd, which the in-repo zero cannot speak to · the full 13,443-test suite was not re-run end to end (CI's shards all concluded success at this head).

Disposition: the fix is one line and has been sent back to the delivering round, which retains its full context. ⛔ The PR stays draft; no flip, no enqueue, no auto-merge. The next head owes a re-run of check:objectui-pin-citations and a re-confirmation that the four pins still discriminate — ⚠️ and this verdict does not carry over to it automatically: a moved head is a new head.

domain:spec execution seat · session_01MkQhmuuJAVDjmeWNixwDDH · verdict adopted 2026-09-12T05:32Z


Generated by Claude Code

os-bill commented Sep 12, 2026

Copy link
Copy Markdown
Collaborator Author

Scoped at-tier re-review of head 10f20635PASS_WITH_FINDINGS, no binding findings. Cleared to land.

domain:spec execution seat, 2026-09-12T06:05Z. The prior verdict on 6160d224 was BLOCK; ⛔ a moved head is a new head, so that verdict did not carry over — this is a fresh at-tier reading of the head that will land.

⭐ Tier verified before adoption

grep -o '"model":"[a-z0-9._-]*"' <transcript> | sort | uniq -c
  → 57  "model":"claude-fable-5-1"      (no other value)
LIT CONTROL   assistant turns          → 47
DARK CONTROL  a fabricated model       → 0

The binding finding is closed — with a control, not an assertion

check:objectui-pin-citations run in a scratch worktree at 10f20635 (⛔ not against the shared checkout, which is main and a different tree): exit 014 asserting citations match .objectui-sha (53ded82bf), 26 historical recorded and not checked, across 1389 spec sources.

The control is what makes that a reading: writing the old head's blob of component.zod.ts over the file makes the same gate exit 1, naming :2830 — then restored, blob back, git diff empty. ⇒ the gate can still come back the other way, so exit 0 means something.

The delta is comment-only — established three independent ways

  1. git diff --numstat → 6 files, 10+/9−; every changed .ts line, whitespace stripped, begins with * or //17 changed lines, 0 non-comment. Control: the same filter over this PR's real code hunk returns 133 non-comment lines.
  2. Comment-stripped, whitespace-stripped token stream of all five .ts files hashed at both heads — all five identical. That is the measure that actually settles it; a line-by-line eyeball would not have.
  3. check:generated 15/15 current after a full build, and gen:migration-registry rewrote registry.ts to a byte-identical file.

Nothing the first verdict relied on moved

re-measured because the fix touched it reading
the 4 pin tests still discriminate ablation on the tombstone: 2 failed / 2 passed — the two refusal pins flip, as before. Mutation proven on disk (quickAdd: retiredKey 1→0, marker 1, blob changed), restored byte-identical
registry.ts generator-current exit 0, regen byte-identical
diff scope vs merge base still exactly 8 files
⭐ the anchor correction, verified at the pin :930 is <KanbanRenderer schema={{, :931 is ...effectiveSchema,:931 is right. All 7 occurrences across 6 files now read :931; ObjectKanban.tsx:930 tree-wide → 0, with the :931 probe as its control

⇒ the round's decision to widen the fix from the one flagged site to all six was correct, and the widened anchor is right everywhere.

Relied on without re-deriving — and only because the delta is comment-only, which was measured rather than assumed: the type-scoped narrowing being exactly ruled option B, kanban-ui still live, registration completeness, the dist clause-② measurement, and minor as the level. ⛔ Had measure 1–3 come back otherwise, all five would be void.

Two NON-BINDING findings, neither blocking

  • ⚠️ Pre-existing anchor imprecision the fix did not introduce: the entries file, migrations/registry.ts and component.zod.ts:2834 cite index.tsx:196 for both quickAdd and onQuickAdd; at the pin :196 passes the first and :197 the second. The substantive claim (both are forwarded together) holds. Outside the gate's population, so nothing would flag it — worth :196-197 on a future touch.
  • --verify-anchors lists the new entries file with 2 unasserted anchors on its coverage worklist; that floor is a ratchet, not a requirement on new records. Informational.

⛔ What the review could not measure — stated, not omitted

CI's own run of the pin gate was still in progress (Lint & Repo Gates, three Test Core shards); every completed check on the head is success, and the gate reading above is a local run at the exact sha with a lit control, ⛔ not CI's. --verify-anchors verified 0 anchor-content assertions because no record in the tree carries a quoted first line, so anchor correctness rests on the direct reads quoted above. The full spec suite was not re-run — out of the bounded scope, the delta being comment-only.

Disposition: cleared. The landing sequence is armed under its own guards — flip, re-read ④ after the flip (it re-triggers Check Changeset and the queue guard), arm only on a fresh GREEN. ⛔ Nothing is armed on this verdict alone.

domain:spec execution seat · session_01MkQhmuuJAVDjmeWNixwDDH · verdict adopted 2026-09-12T06:05Z


Generated by Claude Code

os-bill commented Sep 12, 2026

Copy link
Copy Markdown
Collaborator Author

Carrier cleared — needs:contract-review stripped from both carriers, with the record cited

domain:spec execution seat, 2026-09-12T07:14Z.

What happened, and it was my error

This PR was flipped ready and armed at 06:19Z and entered the queue at 06:20:25Z while needs:contract-review was still hung on it. The Governed Surface Queue Guard leg of the merge-group run refused the group at 06:26:35Z with exit 6, and github-merge-queue[bot] removed this PR from the queue at 06:51:40Z:

#17792 — ⛔ CARRIES needs:contract-review — this pull request may not be in the queue.

The gate is right and it fired on the one thing I skipped. ⛔ The 34 checks on head 10f20635 were all green — this failure lives only in the merge_group context, so a green PR-head reading could never have caught it. The queue run is the reading; the PR-head run is not.

The clearance, on record before the strip — ⛔ not after it

The in-seat clause-② verdict this gate asks for already existed when I enqueued; what was missing was the strip, not the review:

  • PASS_WITH_FINDINGS on head 10f20635, posted 2026-09-12T06:05:57Z — spec(ui): retire ObjectKanbanProps.quickAdd from object-kanban — tombstone, D2 conversion and baselines #17792 (comment)
  • That head is still the head (10f20635, read 2026-09-12T07:11Z). ⭐ A moved head would have voided it; it has not moved.
  • The prior BLOCK on 6160d224 (06:05Z predecessor, comment 5643805167) was closed by a patch round and re-read fresh, ⛔ not carried over.
  • Tier verified from the transcript before adoption: 57 rows, every one claude-fable-5-1; lit control 47 assistant turns; dark control (a fabricated model string) 0.

⚠️ Recording the distinction the gate itself draws: this leg reads the LABEL, not the verdict. A carrier stripped with no PASS on record is indistinguishable here from one that was never hung. That is exactly why the citation above is a link to the verdict comment and not a claim that one exists.

Landing pre-checks, re-run on the FINAL file list at 07:12–07:13Z

# check reading
at-tier verdict (clause-② is yes) PASS_WITH_FINDINGS on the head that lands, linked above
check-clause2-carriers.mjs --pair 17792 exit 0 — "the clause-② declaration is readable in the fixed spelling and both carriers agree"
check-governed-merges.mjs --test over all 8 final diff paths exit 0 — "0 of 8 path(s) hit the register (5 surfaces)" · ⭐ lit control: the same predicate with AGENTS.md appended → exit 3, GOVERNED. So the 0 is a verdict, not a predicate that answers NOT-governed to anything
every check green, newest run per check NAME re-read after the strip, below

⭐ One correction to my own instrument while I was at it: I first ran ③ as --branch <name>, which this script does not accept — it silently fell through to the repo-wide governed-merge sweep and exited 2 for both the target and the control. Two identical answers from a probe that was not aimed at the question. The flag is --test <paths…>, and it was the dead control that said so.

What I am doing now

Stripping needs:contract-review from both carriers — this PR and card #17260 — in an additive-removal write with a read-back diff, then re-arming. ⛔ No second review is being invented and ⛔ no label is being stripped "to get past the check": the verdict predates the enqueue by 14 minutes.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec(ui): ObjectKanbanProps.quickAdd is accepted-and-dropped on object-kanban — objectui ruled it retired; the tombstone is this repo's half

1 participant