Skip to content

fix(spec): the object-kanban quickAdd retirement prescribes "delete the key", not the unregistered kanban-ui block - #21141

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-21017-quickadd-prescription
Oct 1, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-21017-quickadd-prescription

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #21017

Clause-②: no

The object-kanban quickAdd retirement told authors the control "is unchanged on the kanban-ui block". objectui retired that block (objectui#8257) and this spec never declared it as a component type, so a node written there saves clean and resolves no renderer. Every author-facing copy of the remedy now says one sentence: Delete the key; object-kanban offers no quick-add control. The refusal itself is unchanged: same key, same invalid_type code at path quickAdd, same accept set. Prose only.

The copies

Copy File Change
tombstone message (shown at the save door and in the generated reference) packages/spec/src/ui/component.zod.ts the sentence
D2 conversion object-kanban-quick-add-removed summary packages/spec/src/conversions/registry.ts the sentence
D3 entry object-kanban-quick-add-retired: replacement, reason, acceptanceCriteria (what os migrate meta prints) migrations/entries/semantic/18.object-kanban-quick-add-retired.ts, generated copy in migrations/registry.ts the sentence; the second way out "move the board to a host that renders the kanban-ui block" is dropped
step 18 rationale fragment (the paragraph the upgrade guide prints) migrations/registry.ts, the hand-written region outside the generated markers the sentence. A sixth copy the card did not list, found by the git grep of the order's H2
docblocks: two in component.zod.ts, two in conversions/registry.ts, the retired-key entry's header, the pin test's header same files the ruling is still quoted, now with what objectui did to the block it named
generated migrations/registry.ts (gen:migration-registry), content/docs/references/ui/component.mdx (gen:docs) regenerated, never hand-typed

Measured before writing

  • H1, is there a rendering route. objectui at the pin e420df310f5b and at main 770cc5ba4d15: 0 code lines naming kanban-ui under packages and apps outside tests and CHANGELOGs; 22 comment lines in 6 files, all recording the retirement. No register call carries the key. ObjectKanban passes neither half of the pair to KanbanBoardCore; objectui's own docblock there says "An object-kanban board draws no Quick Add control, whatever its document carries". The pair is read only by the exported KanbanRenderer React component, which a host mounts directly, so no metadata node reaches it. No route to name, so triage's sentence stands.
  • H2, every copy. Base git grep of kanban-ui under packages/spec/src lists the files in the order plus the step 18 rationale fragment above. After this PR no author-facing text names the block; the remaining mentions are docblocks and comments that explain why it is not named.
  • H3, what PR chore(objectui): bump the console pin to e420df310f5b (carries objectui#11163 and objectui#11199) #20990 already did. It added the "Re-read at e420df310" qualification to the second docblock. That qualification is kept, re-wrapped, with the same facts and citations. The sentence just above it still said the control "stays on kanban-ui", contradicting it; that sentence is the one rewritten.
  • The dead-node claim. Probe on this branch: PageComponentSchema.safeParse of a node with type kanban-ui succeeds (true), and ComponentPropsMap['object-kanban'].safeParse({ objectName: 'task', quickAdd: true }) fails with one issue, code invalid_type, path quickAdd.

Pin

ui/component.test.ts (ObjectKanbanPropsSchema quickAdd is retired): the existing refusal test now pins "Delete the key" in place of the block name, and one new test asserts that the refusal still carries invalid_type at quickAdd, that the tombstone carries the sentence, and that none of the tombstone, the D2 summary, the D3 entry's three texts and the step 18 rationale contains kanban-ui. The generated copies are held to their sources by check:migration-registry.

Ablation, each leg through scripts/ablation-replace.mjs in wrap mode against the committed fix: the tool printed anchor 1 to 0 and the blob hash changing before the run, and blob == HEAD with an empty git diff HEAD after it. Direction in every leg was red, exactly one failing test: leg 1 the tombstone sentence reverted (assertion on the sentence), leg 2 the D2 summary reverted, leg 3 the generated semantic copy reverted, leg 4 the step 18 rationale reverted (each of the last three on not.toContain('kanban-ui')). The test imports the sources by relative path, so no dist/ is in the resolution path and no rebuild is owed between legs.

Verified at head 83d99268a

  • pnpm --filter @objectstack/spec build exit 0; check:generated all 15 artifacts up to date, with check:migration-registry, check:spec-changes, check:upgrade-guide, check:docs, check:authorable-surface and check:api-surface green. check:docs was stale after the tombstone edit and was regenerated: the diff of content/docs/references/ui/component.mdx is the one sentence.
  • dispatch-gates.mjs --commands --repo objectstack-ai/objectstack over the actual diff: 112 commands. --ran verdict: "112 derived famil(ies) accounted for — 112 run, 0 NOT-MEASURED", every recorded exit 0. The four artifact-roster gates whose roster sits under the touched paths (check:meta-url-spelling, check:authz-resolver, check:error-code-casing, check:filter-alias-parity) also exit 0. The seven gates that first answered exit 3 (workspace not built) were re-run after turbo run build of the closure and exit 0.
  • pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/ui src/conversions src/migrations scripts/step18-rationale-merge.test.ts scripts/conversions-major18-merge.test.ts: 114 files, 4190 tests passed. pnpm --filter @objectstack/spec typecheck exit 0.
  • origin/main was merged with scripts/pm/os-regen-merge.sh (main at 8368f1c00); regenerating the migration registry afterwards changed nothing. The repo-wide lint union and the rest of the farm are CI's.

Acceptance notes

  • packages/spec/CHANGELOG.md, the 17.5.0 entry for the retirement (about lines 1580-1629), carries the same prescription ("It stays on the kanban-ui block ...", and "live kanban-ui spelling"). It is release-owned and published, so it is not touched here; it needs an amendment in a dedicated docs-only PR. Carrier: none.
  • .changeset/objectui-pin-citations-e420df310f5b.md already says the block was retired in objectui and is correct; untouched.
  • No governed surface is touched (no .claude/**, skills/**, ADR or AGENTS.md). The claim records that the at-tier contract review of the packages/spec/src/** face is owed before enqueue.
  • Changeset: patch for @objectstack/spec, Clause-②: no. The diff widens no accept set and no public surface.

Generated by Claude Code

claude added 4 commits October 1, 2026 06:41
… the unregistered kanban-ui block

Claude-Session: https://claude.ai/code/session_017VaLJnYwhPsanVCe9dMCJU
Co-authored-by: Claude <noreply@anthropic.com>
…e the key, not that the control stays on kanban-ui

Claude-Session: https://claude.ai/code/session_017VaLJnYwhPsanVCe9dMCJU
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added the size/m label Oct 1, 2026
@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:ui tests tooling labels Oct 1, 2026
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

3 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️ 1 changed file(s) yielded no anchor (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.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/src/migrations/entries/retired-keys/18.ui__ObjectKanbanProps__quickAdd.ts) — pages documenting those are invisible to this run
  • 6 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 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; 97 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.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 137 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 c6954d6d09080179d3af72c80a820c7c24cf6053 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 1a6e0400f3fb6aebcb6109f6532785091cc54b5c — the merge of head 83d99268a356566efa67280cd4007a14b2989ea2 into base c6954d6d09080179d3af72c80a820c7c24cf6053, 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 1a6e0400f3fb6aebcb6109f6532785091cc54b5c && git checkout 1a6e0400f3fb6aebcb6109f6532785091cc54b5c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin c6954d6d09080179d3af72c80a820c7c24cf6053 83d99268a356566efa67280cd4007a14b2989ea2 && git checkout -B drift-repro c6954d6d09080179d3af72c80a820c7c24cf6053 && git merge --no-ff 83d99268a356566efa67280cd4007a14b2989ea2

node scripts/docs-audit/affected-docs.mjs --json c6954d6d09080179d3af72c80a820c7c24cf6053

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 83d99268a356566efa67280cd4007a14b2989ea2
Local-runs: none

Rendered 2026-10-01T09:35Z by an isolated reviewer subagent from card #21017 (body and its four comments: the triage grade, the serial note, the claim, the os-dev-report), PR #21141 (body, its 8 files, the net diff against the merge-base 8368f1c00), the head's tree read with git show on a fetched ref, objectui main read over the REST contents API, and the head's check-runs, read last at 2026-10-01T09:33Z. Nothing built, run or re-run.

① Derived judgments

  1. The tombstone ObjectKanbanPropsSchema.quickAdd (component.zod.ts:4334-4341) — prose only, right. The only non-comment change is the guidance string handed to retiredKey(). At the head, shared/retired-key.ts:179 builds the schema as z.never() whose error callback returns guidance, then .optional() and .describe() with the [REMOVED] prefix on the same guidance: the accept set (absence accepted, every authored value refused), the issue code invalid_type and the path quickAdd all come from z.never(), not from the string. So the refusal's code, path and verdict are byte-identical and only its message changes. Clause-②: no is right.
  2. The [REMOVED] … describe text flows into the generated reference content/docs/references/ui/component.mdx:827; the diff regenerates it and the change is the one sentence. Right. At the head a whole-tree grep for kanban-ui (excluding node_modules, dist, lockfile and CHANGELOGs) finds no other generated artifact carrying the old sentence: spec-changes.json, the authorable-surface baseline and docs/protocol-upgrade-guide.md have no hit.
  3. D2 conversion object-kanban-quick-add-removed (conversions/registry.ts:8974-8977) — summary string only; apply(), its type scoping and its fixtures are untouched (the hunk at :9002-9005 is a comment inside the fixture). A published string changes, no behaviour. Right.
  4. D3 semantic entry 18.object-kanban-quick-add-retired.ts — replacement, reason and acceptanceCriteria change; id and surface do not. The second way out ("move the board to a host that renders the kanban-ui block") is dropped, which is what the triage sentence requires. Its generated copy at migrations/registry.ts:14117-14135 (inside the semantic:18 generated region, which opens at :6065) carries the identical text; check:migration-registry holds the two together and is CI's (Lint & Repo Gates, in progress at read time; the dev reports it green locally). Right.
  5. Retired-key entry 18.ui__ObjectKanbanProps__quickAdd.ts — header comment only, mirrored in the generated copy at migrations/registry.ts:23122-23144. Right.
  6. The sixth copy, the step-18 rationale fragment (migrations/registry.ts:5693-5697) — inside the family and the surface, right. It sits in the hand-written STEP18_RATIONALE table (declared at :5024, between the close of the semantic:17 generated region at :4973 and the open of the semantic:18 region at :6065), so it is edited by hand, not regenerated, and the dev did that. It is published: step18.rationale is joinRationale(STEP18_RATIONALE) at :6053, exported through MIGRATIONS_BY_MAJOR[18], and scripts/build-upgrade-guide.ts:89 prints step.rationale; the guide at the head covers protocol 16 to 17 only, so no guide regeneration was owed now (consistent with check:upgrade-guide up to date and no guide file in the diff), and the sentence would have reached the guide when 18 is cut. The claim's file surface names migrations/registry.ts, the sentence is the same false claim ("quickAdd stays LIVE on the kanban-ui block"), and triage's direction is that the remedy text stops steering to kanban-ui and "does not name a block objectui does not register". Fixing it here is in scope; flagging it as a deviation was the right handling.
  7. The docblocks — two in component.zod.ts (:4252-4259, :4315-4332), two in conversions/registry.ts (:8929-8936, :8950-8956), the retired-key entry header and the test header. Each keeps the ruling quoted (option B named kanban-ui) and adds what objectui did since (objectui#8257). The PR chore(objectui): bump the console pin to e420df310f5b (carries objectui#11163 and objectui#11199) #20990 "Re-read at e420df310" qualification is kept with its citations. After the diff, every remaining kanban-ui under packages/spec/src is one of these docblocks, a comment, or the test: none is author-facing at the save door, in os migrate meta output or in the generated reference. Right.
  8. The pin (component.test.ts:3630-3671) — the existing refusal test now matches Delete the key instead of the block name, and the new test asserts invalid_type at quickAdd, the shared sentence on the tombstone and the D3 replacement, and not.toContain('kanban-ui') over the six remedy texts (tombstone, D2 summary, D3 three texts, step-18 rationale through MIGRATIONS_BY_MAJOR[18].rationale). Triage's two pins are covered: the tombstone names no unregistered block (this test), and the generated copy matches its source (the existing generation check). Right.
  9. H1 holds — no metadata route renders quick-add, so triage's sentence is used and no route is named. objectui main at d0fba91aa (read over REST today; the dev measured the pin e420df310 and main 770cc5ba): packages/plugin-kanban/src/index.tsx carries one registration, ComponentRegistry.register('object-kanban', ObjectKanbanRenderer, …) at :678; KanbanRenderer (:344-350) is exported but registered under no key and is the only reader of the quickAdd/onQuickAdd pair; its docblock states ObjectKanban renders KanbanBoardCore directly and supplies neither half. Triage's own re-measure in the card (0 non-test hits under packages/*/src) agrees. Right.
  10. Public surface — no export added or removed, no schema shape changed, no key accepted or refused differently. The published values that change are strings only: the tombstone message and describe text, the D2 summary, the D3 three texts, the step-18 rationale. The dev reports check:api-surface "public API surface + factory signatures unchanged"; Build Core is green on the head.

② Semver level

.changeset/21017-quickadd-remedy.md declares '@objectstack/spec': patch and carries Clause-②: no; no ADR-0087 marker, and none is owed (nothing breaking). The diff publishes changed prose in a released package (17.5.0), so a changeset is owed and patch is the right level; skip-changeset would be wrong. Clause-②: no matches the diff: the accept set of object-kanban is unchanged (quickAdd absent accepted, any value refused with the same invalid_type at quickAdd), and the PR body's Clause-②: no line and triage's "prose only, so Clause-②: no" agree. Check Changeset is green on the head.

③ Boundary flags

  • Dev deviation 1, the sixth copy — answered in ① item 6: inside the card's family and the claimed surface; right.
  • Dev deviations 2 to 5 (background gate loops, a stray untracked /home/user/tmp deleted, two private refs in the sibling objectui checkout deleted at wrap-up with that checkout's origin/main ref advanced and no working tree changed, model-free trailers per the os-dev rule) — process notes with no trace in the diff; nothing to answer at contract level.
  • open_questions — empty; none to answer.
  • Out-of-scope finding 1, the released packages/spec/CHANGELOG.md 17.5.0 entry (:1580, :1586, :1616, :1629 at the head) still carries the old sentence — closing the card is right. packages/spec/package.json is 17.5.0, so the entry is released. AGENTS.md's Documentation Guardrails row for packages/*/CHANGELOG.md is explicit: release-owned, never edited in a code PR, and a factual error in a released entry is amended "in a dedicated docs-only PR, never a rider on code changes". Leaving it untouched here was required, not a choice. Triage's direction, the governing ruling on the card, enumerates the four texts, the two docblocks and the pins; the card body lists the same copies; neither names the CHANGELOG. This PR satisfies every item of that direction, so Fixes #21017 is right and the card closes on it. Escalated to the seat: the finding reads carrier: none. The CHANGELOG amendment is distinct work under a distinct rule, and it needs a carrier (the dedicated docs-only PR, or a point card) at or before the family close-out so the released sentence does not survive untracked. That is the seat's close-out item, not a block on this PR.
  • Out-of-scope finding 2 (objectui comment drift in plugin-kanban/src/index.tsx) — cross-repo, no consumer; noted, nothing owed here.
  • The claim's "assertion that pins the old sentence, if any" — there was one (the regex on kanban-ui); it is updated. Right.

Check-runs on the head, as read at 2026-10-01T09:33Z: 32 runs; 17 success (Auto Label, Build Core, Build Docs, Check Changeset, Check Documentation Links, Check PR Size, Dogfood Verify CLI, Flag docs affected by code changes, Governed Surface Queue Guard, No other open PR may claim the same issue, No other open PR may claim the same single-writer path, Part-of PR must not also close its card, Spec property liveness, The card this PR closes must claim this branch, Type Check · debt ledger, Type Check · source gates, filter); 2 skipped (Console Pin Gate, Packed-tarball smoke (opt-in)); 13 in progress (Dogfood Regression Gate 1/3, 2/3, 3/3; Lint & Repo Gates; Temporal Conformance (live PG + MySQL); Test Core 1/6 through 6/6; Type Check · consumer gates; Type Check · workspace). None failed. check:generated and check:migration-registry sit under Lint & Repo Gates, still in progress at read time; the seat re-reads every check before landing.

Implemented-by: claude/issue-21017-quickadd-prescription
Reviewed-by: session_017VaLJnYwhPsanVCe9dMCJU

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 1, 2026 09:49
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 1, 2026 09:49
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 1, 2026
Merged via the queue into main with commit 27c0cf3 Oct 1, 2026
37 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-21017-quickadd-prescription branch October 1, 2026 10:15
os-bill pushed a commit that referenced this pull request Oct 1, 2026
…jectui-pin-single-zod

component.zod.ts conflicted in two docblocks; both intents stack: main's
#21005 (action:button / action:icon refuse `endpoint`) and #21141 (the
quickAdd tombstone prescribes "delete the key") are kept verbatim, and the
pin bump's citation updates at 31971ff1e are re-applied on top
(`inputs` `:414-559` and the slice-2 `size` sentence; `e420df310` in the
kanban-ui registration count). Regeneration follows as its own commit.

Claude-Session: https://claude.ai/code/session_01MRdbfpy4sQT8bUjmMhxsN7
Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…ce in the vendored Console), add a single-zod canary to build-console.sh, and key the release console cache on the spec zod range (objectstack-ai#21149)

Fixes objectstack-ai#21108

Clause-②: no

Moves the console pin past objectstack-ai/objectui#11353, so the
vendored Console bundles one zod instance again. Adds a single-zod
canary to `scripts/build-console.sh`, and keys `release.yml`'s Console
dist cache on the spec's declared zod range.

## Target: `31971ff1e28f`, the minimal containing commit

`.objectui-sha` moves `e420df310f5b` ->
`31971ff1e28f89cfc45f0c19bc5b05e443f28b79`. That is the merge commit of
objectui#11353 itself, which closed objectui#11327. objectui `main` was
`2124d0411175`, 2 commits further on, when I chose the target.

Why not `main`:
- The two later commits are objectui#11293 (standalone `object-kanban` /
`object-calendar` honour `page` navigation through the host's record
navigator) and objectui#11165 (a docs example and its test). Neither is
needed by this card.
- objectui#11293 rewrites `plugin-kanban/src/index.tsx` and
`plugin-calendar/src/index.tsx`. Nine asserting pin-citation anchors in
`packages/spec/src` read those two files. Taking it would turn this
pin's citation re-measure into a navigation-claim change outside this
card.
- The procedure (`docs/releases-maintenance.md`) says the target is
decided in the objectstack issue. The card's unlock criterion is "a
commit that contains objectui#11353". The minimal containing commit
meets it and drags in the least.

Containment was measured on a private full clone of objectui (`rev-parse
--is-shallow-repository` = false):

| question | command | exit |
|---|---|---|
| pin is the objectui#11353 merge | `gh api …/pulls/11353` answers
`merged: true`, `merge_commit_sha: 31971ff1e28f…` | — |
| pin is on objectui `main` | `merge-base --is-ancestor 31971ff1e28f
origin/main` | 0 |
| old pin is inside the new range | `--is-ancestor e420df310f5b
31971ff1e28f` | 0 |
| negative: new pin is not inside the old one | `--is-ancestor
31971ff1e28f e420df310f5b` | 1 |
| control for that negative (deeper commit, same target) |
`--is-ancestor db11afd4967c e420df310f5b` | 0 |
| objectui#11293 is NOT in this pin | `--is-ancestor 2124d0411175
31971ff1e28f` | 1 |
| control for that negative | `--is-ancestor e420df310f5b 31971ff1e28f`
| 0 |

The range `e420df310f5b..31971ff1e28f` is 44 commits.

## What changed

- **`.objectui-sha` and `.changeset/console-31971ff1e28f.md`.** Written
by `scripts/bump-objectui.sh 31971ff1e28f --no-commit` against the
private clone, so the shared shallow `../objectui` was never deepened.
The range has 56 releasing changesets, 12 declared breaking, and 3
commits carry no changeset.
- objectui#11353, the fix this bump exists for, is one of those 3. So I
added one lead sentence naming it; the digest would otherwise list it
only under "declared nowhere".
- The bump script's `adr-0087: TODO` placeholder is answered
`not-required (no-migration-prescription)`. It is worded after the
maintainer's ruling on the previous bump (objectstack-ai#20949) and fitted to this
diff, and it names all 12 upstream entries. Three of them mirror
ObjectStack keys that are already retired and registered here:
`ui/ChartConfig:aria`, `ui/Page:assignedProfiles` and `ui/Action:aria`.
The `object-form` / `object-map` / `object-gantt` / `object-chart`
entries move objectui's validator onto the `properties` bag that the
spec rows already declare. `showFilters` was never an ObjectStack key on
`object-grid`. Please confirm this disposition; see the open question in
the report on objectstack-ai#21108.
- **`sdui.manifest.json` + `scripts/sdui-manifest.record.json`.**
Produced by `node scripts/gen-sdui-manifest-node.mjs` over the tree
`build-console.sh` built at the pin: 107 components, `@object-ui/core`
17.6.0. Eleven components changed their published inputs: `object-form`,
`object-kanban`, `object-calendar`, `object-gantt`, `object-timeline`,
`object-map`, `record:quick_actions`, `element:text`,
`element:definition-list`, `element:repeater` and `action:button`.
- **`packages/sdui-parser/objectui-lockstep.json`.** Re-recorded with
`pnpm gen:sdui-lockstep` against the pin's own checkout: 214 grammar
lines, 25 codes, containment predicate `76c18fb95d1f`.
`check:sdui-lockstep` exits 0 with no port owed.
- **`scripts/build-console.sh`: the single-zod canary.** Details in the
next section.
- **`.github/workflows/release.yml`: the Console dist key.** Details
below.
- **The 49 asserting pin citations in `packages/spec/src`.** Re-measured
at the new pin (section below). This also regenerates
`packages/spec/src/migrations/registry.ts` (`gen:migration-registry`)
and `content/docs/references/ui/view.mdx` (`check:generated --fix`, the
one artifact it proved stale), and adds
`.changeset/objectui-pin-citations-31971ff1e28f.md` (`@objectstack/spec`
patch). The `FormField.span` describe and six migration-entry
descriptions name the pin, so the text change publishes.
- **`origin/main` merged twice.**
- First at `19cbfeba7d`: no file overlapped this diff, and no generated
artifact moved.
- Then `fbcc05f400`, at merge commit `8435a830eb`, through
`scripts/pm/os-regen-merge.sh`. There was one textual conflict, in two
docblocks of `packages/spec/src/ui/component.zod.ts`. main's objectstack-ai#21005 and
objectstack-ai#21141 are kept verbatim, and the pin citations were re-applied and
re-measured at `31971ff1e`. No regeneration commit is owed:
`check:generated` reports 15/15. That merge also moved the spec's zod to
`^4.6.5`; the console still bundles exactly one zod, `4.6.5`.

## The single-zod canary

It sits beside the existing bundle canary.
- It counts zod version literals across
`packages/console/dist/assets/*.js`. Each zod 4 instance carries one
version object (the `version` export of `zod/v4/core/versions`),
minified as `{major:4,minor:6,patch:5}`.
- It requires **exactly one** occurrence. It refuses two different
versions (the shipped defect), one version found twice (two instances),
and zero (a blind matcher, since the console always bundles zod).
- The same function runs standalone as `scripts/build-console.sh
--check-single-zod ASSETS_DIR`, which builds and writes nothing. That
standalone mode is how both directions below were measured.

| fixture | result |
|---|---|
| the real dist this PR's build produced at `31971ff1e28f` (2450
`assets/*.js`) | exit 0: `exactly one zod version literal
{major:4,minor:6,patch:5} in vendor-objectstack-UrN_Az-7.js` |
| the real `db11afd4967c` dist (the published two-zod split) | exit 1:
`2 different zod versions` —
`vendor-objectstack-CfLtdaY1.js:{major:4,minor:6,patch:1}`,
`vendor-zod-CHLW1iCr.js:{major:4,minor:4,patch:3}` |
| the new dist plus one chunk carrying `{major:4,minor:4,patch:3}` |
exit 1, two different versions |
| the new dist plus a second `{major:4,minor:6,patch:5}` chunk | exit 1:
`2 copies of ONE zod version literal` |
| the new dist with its one literal rewritten (`major` -> `MAJOR`; `grep
-o` count 1 -> 0 before, 0 -> 1 after) | exit 1: the blind-matcher
refusal |
| no argument | exit 2, usage |

The fixtures are copies in a scratch directory. No tracked file was
mutated.

## `release.yml`: the key carries the spec's declared zod range, not the
whole `package.json`

The card suggested `hashFiles('packages/spec/package.json')`. I went
with a narrower input:
- A new step reads `dependencies.zod` out of
`packages/spec/package.json`. It fails rather than emitting an empty or
`undefined` range.
- The restore, the `CONSOLE_DIST_CACHE_KEY` env and the save step all
spell the same key, with `-zod-RANGE` appended (today `-zod-^4.6.5`,
after the merge of `fbcc05f400`).

Why not hash the whole file: its `version` field moves on every Version
Packages merge, and that merge is the push that drives the publish lane.
Hashing it would make every publish a cache miss and a cold console
rebuild. The comment above the key records that design as deliberate.
The declared range is the input the build actually judges:
objectui#11353's parity assertion refuses a console zod outside it.

`release.yml` is not a governed surface. `GOVERNED_SURFACES` in
`scripts/pm/check-governed-merges.mjs` lists `docs/adr/**`,
`.claude/**`, `skills/**`, `AGENTS.md`, `CLAUDE.md` and
`docs/NORTH-STAR.md`. No workflow was run or dispatched.

## Pin-citation re-measure

Each record was handled the same way:
- Its anchors were mapped through the `e420df310f5b..31971ff1e28f` diff
of each cited file and re-read at the new pin.
- Block hashes (`git hash-object`) and corpus counts (`git grep -o -F`
over the tracked tree, 9912 files) were taken again with the method each
record names. The method reproduces every `e420df310` count exactly at
the old pin: 9800 files, `objectstack` 15352, `@objectstack/spec` 6024,
`timeout` 1293.
- Earlier hops stay as history.
- `check:objectui-pin-citations --verify-anchors`, run against the pin's
checkout, reports 49 asserting citations matching `31971ff1e`, and 7
anchor content assertions verified.

Records whose CLAIMS moved, not only their numbers. These want a human
read:
- **`object-calendar` `navigation`.** objectui#8652 declared the key on
`ObjectCalendarSchema`. That removes the one `as any` cast objectui#8651
had left: the ledger comment `1008-1025` is replaced by a declaration
note, and the read `1026` -> `1018` now says `schema.navigation ?? {
mode: 'drawer' }`.
- **`object-kanban` `navigation`.** The read is unchanged. It now
compiles through a declared `ObjectKanbanSchema.navigation` member
rather than `BaseSchema`'s index signature.
- **`object-timeline`.** objectui#8654 declared `navigation`. The read
lost its cast (`753-756` -> `793-796`), and both registrations gained a
`navigation` input. The `schema.*` read set is unchanged, because
`navigation` was already read and is already on this row.
- **`action:button`.** objectui#11168 slice 2 makes the `size` input
publish the five values the row declares. The `inputs` range grew
`414-551` -> `414-559`.
- **`record:quick_actions`.** objectui#10224 rewrote the fail-closed
docblock: the bar is replaced by the notice, not hidden. The cited gate
lines moved by 6 with their text byte-identical.
- **`object-tree` optionality count.** By the record's own method,
`plugin-map/src/index.tsx` now reads 4 `ElementDataSourceGate` hits, not
3. objectui#10859 batch 5 added a comment naming the gate, while the
wiring is still three lines. The record now says so.
- **Wrong when written.** The `object-timeline` data door's
start/end-binding anchor `621-622` began one line early, on the
`rawData.map` head, at `e420df310` too. It is re-pointed to `657-658`
and the record says why.

No key, default, enum member or export moves.

## Acceptance at the new pin

**Build.** `scripts/build-console.sh` ran at `31971ff1e28f` with this
tree's client and spec injected, under the shared verify lock: `VERDICT
command-exit 0`.
- The single-zod canary passed (above). `assert-console-spec-injection`
passed: 21 of 21 injected-only descriptions are present and all 8
published-only ones are absent.
- The dist is 63032 KB.
- Environment, not a product change: objectui's `jsdom` requires Node
`^22.22.2` under engine-strict, and this container's Node is 22.22.0. A
verified Node 22.22.2 was prepended to `PATH` for the console build
only.
- The build ran before the manifest regeneration, as the procedure
orders it. So that local dist ships the previous manifest bytes, and the
build itself prints the re-run step. CI's Console Pin Gate builds fresh.

**Boot.** `pnpm dev -- --fresh --ui --no-watch -p 41633` started the
showcase with its own ephemeral DB and the seeded admin.
`check:console-sha` reads `objectui@31971ff1e28f`. I drove it in
headless Chromium (`/opt/pw-browsers/chromium`) from `localhost`:
- **Studio "New package (writable base)"** (`/_console/studio`): the
dialog renders **Display name (`name`)**, **Package ID (`id`)** and
**Object namespace (`namespace`)**, plus Version, Type and Description.
- I filled `Zod Probe` / `com.acme.zodprobe` / `zodprobe` and clicked
Create package. The request was **`POST /api/v1/packages` -> 201**.
- The Studio opened `/_console/studio/com.acme.zodprobe/data`. `GET
/api/v1/packages/com.acme.zodprobe` answers 200 with `writable: true`.
- **Dashboard inspector**
(`/_console/apps/showcase_app/metadata/dashboard/showcase_ops_dashboard`):
the spec-derived form renders Columns, Gap, Refresh Interval Seconds,
Header, Show Title, Show Description and Actions. "Spec schema
unavailable" appears 0 times.
- **Report inspector** (`…/metadata/report/showcase_task_overview`):
Label, Report type, Description and Blocks render, with 0 "Spec schema
unavailable".
- **Control for that absence:** the string is in the shipped bundle
(`i18n-D40RWXZZ.js`, 3 occurrences), so 0 on screen is a reading, not a
missing probe.
- **Errors:** 0 page errors and 0 console errors across the drive.
- **Non-2xx:**
  - `401 GET /api/v1/auth/get-session` before sign-in.
  - `404 GET /api/v1/usage/storage`, as at the previous pin.
  - `501 GET /api/v1/ai/usage`, with no AI provider configured.
- `404 GET /api/v1/meta/{dashboard,report}/NAME?state=draft`, the
no-draft probe of a code-loaded item.
- **Teardown:** only the 7 PIDs I recorded were stopped. The port is
free, and the `--fresh` tempdir went with the process.

## Gates and tests, at head `8435a830eb` (re-run in full after the
merge: 151 derived, 151 run, 0 NOT-MEASURED, 0 UNRUN)

- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 151 commands, all run sequentially
with exit codes captured before any pipe.
  - 150 exit 0.
- `pnpm check:pm-dispatch-gates` hit my 900 s runner timeout under
contention (exit 124). Re-run alone with no timeout, it passed: exit 0,
`dispatch-gates self-test: 1976 cases pass`, battery 1096.7 s.
- `dispatch-gates --ran` reconciliation: `151 derived, 151 run, 0
NOT-MEASURED, 0 UNRUN`.
- Highlights: `check:objectui-pin-citations` 49 matching,
`check-sdui-manifest` intact at the live pin, `check:sdui-lockstep` OK,
`check:console-sha`, `check:console-injection`,
`check-adr-0087-registration` (2 declaring changesets, each with a
disposition), `check:generated` (15 of 15 current), `check:docs` (227 in
sync), `check:migration-registry`, `check:bash32-floor` (32 shell
files), `check:nul-bytes`, `check-ci-filter-parity` and
`check:published-files`.
- `pnpm --filter @objectstack/spec test`: 591 files, 17368 passed and 1
todo, exit 0. `pnpm --filter @objectstack/spec typecheck`: exit 0.
- NOT MEASURED, by CI only: the Console Pin Gate job's own steps and the
workspace type-check lanes. The local equivalent of the Console Pin Gate
is the build above.

## Acceptance notes

- `scripts/check-console-injection.mjs`'s header still says
release.yml's key "hashes the pin and the build script". That sentence
is now incomplete, because the key also carries the spec's zod range.
The file is outside this card's file surface. It is a comment, and
nothing reads it.
- objectui#11353 landed with no objectui changeset, so objectui's own
CHANGELOG does not mention the zod fix. The console changeset here names
it.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01MRdbfpy4sQT8bUjmMhxsN7)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…ntrol to the retired kanban-ui block (objectstack-ai#21224)

Fixes objectstack-ai#21143

Clause-②: no

## What

`packages/spec/CHANGELOG.md`, inside the released `@objectstack/spec`
17.5.0 entry for 48203ff (the `object-kanban` `quickAdd` retirement),
told the reader four times that the quick-add control lives on
objectui's `kanban-ui` block:

- the ruling sentence: "the key leaves the board and stays only on the
`kanban-ui` block, where a React host can supply the runtime function
the control needs";
- the table row `kanban-ui` (objectui block), whose `after` cell read
**unchanged**;
- the "What to write instead" paragraph: "The control itself is not
withdrawn from the platform. It stays on the `kanban-ui` block, which a
React host renders directly and can hand the `onQuickAdd` slot to";
- the D2 conversion bullet: "scoped by component `type` so the live
`kanban-ui` spelling stays out of its reach".

objectui registers no `kanban-ui` block. objectui#8865 retired the
registration (objectui#8257) on 2026-09-10T10:17Z. 48203ff landed on
2026-09-12T07:24Z, so the retirement came first. The sentences were true
of the `.objectui-sha` pin the entry was measured at (`53ded82bf`). They
are false of the pin 17.5.0 was versioned at (`dd3f7e1be`) and of
today's pin (`31971ff1e`).

This PR amends that one entry in place. It uses the shape this file's
errata already use (`*Erratum, DATE — … (Corrected after publication,
#N.)*`, 7 instances on `main`). The nearest precedent is 576afc1, which
amended the 17.4.0 entry the same way: passages corrected in place, then
one dated erratum line at the end of the entry that quotes the old
phrasing.

1. The ruling sentence now reads "the key leaves the board. Delete the
key; `object-kanban` offers no quick-add control."
2. The table row keeps its `before` cell. Its `after` cell now reads
"**retired** in objectui (objectui#8257), not by this change: no block a
document can name offers the control". The row is corrected rather than
deleted, so a reader who greps `kanban-ui` lands on the corrected
answer.
3. The "What to write instead" paragraph now says that no block a
document can name offers the control. It also says the ruling kept the
control on `kanban-ui` and that objectui has since retired that block.
4. The D2 bullet now gives the scoping reason that the conversion's own
docblock on `main` gives: by component `type`, never by key name, so a
node of any other type that authors the same key is left alone.
5. One dated erratum line ends the entry. It quotes the four old
phrasings and says that the retirement, its tombstone and its D2
conversion are unchanged.

No other entry and no other file is touched. The corrected wording
matches what PR objectstack-ai#21141 put on every non-released copy: the tombstone
prescription, the D2 summary and both migration entries.

## Changeset: none, labelled `skip-changeset`

`CHANGELOG.md` is in `@objectstack/spec`'s `files[]`, so the next
tarball carries this text. A changeset is still the wrong instrument. It
would compile the correction into a new entry under a later version.
That is the erratum-in-a-later-entry shape that the Documentation
Guardrails row for `packages/*/CHANGELOG.md` rules out, and the PR
declares no package change. `changeset version` prepends to this file
and keeps the amended 17.5.0 entry. 105 pending changesets on `main`
already bump `@objectstack/spec`, so the next release ships the
corrected entry. This is the same disposition as 576afc1 (PR objectstack-ai#21014,
labelled `skip-changeset`).

## Evidence

| Reading | Result |
|---|---|
| `grep -c kanban-ui packages/spec/CHANGELOG.md` at the merge base
`ce4e205e` | 4, at lines 1580, 1586, 1616 and 1629 |
| npm tarball `@objectstack/spec` 17.5.0, `package/CHANGELOG.md` | the
same 4 lines |
| objectui registration-key lines (`'kanban-ui',` alone on a line, test
files excluded) at the current pin `31971ff1e`, the 17.5.0 pin
`dd3f7e1be` and the 48203ff pin `53ded82bf` | 0, 0 and 2
(`plugin-kanban/src/index.tsx:209` and
`cli/src/utils/known-schema-types.ts:225` at `53ded82bf`). The firing
control `'object-kanban',` on the same instrument reads 4, 4 and 3 |
| `.objectui-sha` at 48203ff, and at 8c87d26 (`chore: version
packages`, spec 17.5.0) | `53ded82bf`, then `dd3f7e1be` |
| Dates | objectui#8865 merged 2026-09-10T10:17Z; 48203ff committed
2026-09-12T07:24Z; spec 17.5.0 published 2026-09-29T08:09Z (npm `time`)
|
| Other released copies | `kanban-ui` occurs in no other
`*/CHANGELOG.md` and nowhere under `content/docs/releases/`. The 69
other CHANGELOGs that name 48203ff carry it only as `- Updated
dependencies [48203ff]` |
| After this PR | `grep -c kanban-ui packages/spec/CHANGELOG.md` reads
3: the corrected row, the corrected paragraph and the erratum line |

## Gates (on `ca8dffb4`, the final commit)

`node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands` derived 55 commands for this one path. 51 ran and exited 0.
They include `pnpm check:nul-bytes`, `pnpm check:published-files`, `pnpm
check:release-notes`, `pnpm check:release-page-status`, `pnpm
check:doc-authoring` and `node
scripts/check-release-section-coverage.mjs --strict`. The `--ran`
reconciliation reads: "55 derived famil(ies) accounted for — 51 run, 4
NOT-MEASURED (4 DERIVED from a recorded exit 3)", 0 UNRUN.

NOT MEASURED (4): `check:dts-closure`, `check:dual-build-cjs-loads`,
`check:lean-entry-closure` and `check:sourcemap-no-sources-content`.
Each exited 3 (PREREQUISITE NOT MET) because it reads built `dist/`
trees across the workspace, and this worktree built none. No build reads
`CHANGELOG.md`. CI runs these four on a fresh build.

Outside the derived set, two gates that read
`packages/spec/CHANGELOG.md` also ran and exited 0: `pnpm
check:future-spec-major` and `pnpm check:docs-image-tag`.

`check:issue-citations` is green, but it read 0 files: `**/CHANGELOG.md`
is one of its declared deferred surfaces. So the three citations this PR
adds were read by hand. objectstack-ai#21143 is this card, objectui#8865 is the merged
retirement PR and objectui#8257 is the card it answered.

## Acceptance notes

- PR objectstack-ai#21141 corrected the source copies to the same wording. This PR is
the released-text half that the Documentation Guardrails keep out of a
code PR.
- The pinned objectui still exports the `KanbanRenderer` React
component, which reads the `quickAdd` / `onQuickAdd` pair
(`plugin-kanban/src/index.tsx:345-346` at `31971ff1e`). The tombstone
docblock on `main` already records this as "not withdrawn from
objectui's React layer, but no metadata node reaches it". The amended
entry says only what holds for metadata and does not name that
component.

Written by the `domain:spec` dev session
`session_01UtnxvdiN376GF3sgXwAw4d`, dispatched by the PM loop on objectstack-ai#21143.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01UtnxvdiN376GF3sgXwAw4d)_

Co-authored-by: Claude <noreply@anthropic.com>
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

2 participants