feat(plugin-map)!: retire the bare map node type key; object-map is the one spelling (objectui#10393) - #10540
Conversation
…is the one spelling
Unregister `ComponentRegistry.register('map', ...)` (and with it `view:map`)
from @object-ui/plugin-map, following the objectui#8008 family ruling that
retired the bare `gantt` / `kanban` keys. No schema face declares `map` as a
component node type, so unregistering is the whole retirement; the stored
`NamedListView.type` value `map` is untouched (ObjectView / ListView already
emit `object-map` for it).
- console: drop the lazy `map` stub.
- core: drop the `map` / `view:map` rows from RECORD_SOURCE_DATA_ARM_BY_TYPE.
- cli: regenerate known-schema-types (bare `map` leaves the list).
- prompts: teach `object-map` instead of `view:map` on the list-view Keys line.
- README: one registered type; a bare array under `data` is not a record source.
- tests: drop the retired rows from the registration / arm pins; add the
retirement pin with a firing control on `object-map`.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
|
changeset-claim-re-read
|
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
…t the bare-array claim on two docs pages
- .changeset/10392-registration-record-source-inputs.md: the sentence that the
`map` registration declares `data` / `staticData` now reads as of its own
change and carries a dated note naming the `map` retirement; frontmatter
byte-identical.
- content/docs/plugins/plugin-map.mdx and content/docs/fields/location.mdx: a
bare array under `data` is not a record source on the map; inline rows go
under `staticData` or a `data: { provider: 'value', items }` configuration,
matching the README wording.
- the retirement changeset names both.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract reviewServed-tier: ① Derived judgmentsHead and base. The guard does not fire (own search on the head tree, non-test source).
Stored layer untouched. No file under Plain unregistration is the whole retirement. Surfaces that listed the bare key.
README. "registers one component type" — head Pins. Serial constraint. ② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Contract reviewServed-tier: ① Derived judgmentsSetup: Item 1 — 10392 changeset. True as written. Item 2 — docs pages. True at source. 10393 changeset. Names ② Semver levelUnchanged: ③ Boundary flags
Implemented-by: VERDICT: PASS — conditioned on (a) the 4 Test shards and Spec Main Shape Gate concluding green, and (b) the seat removing or rewriting the "reported for the seat, not fixed here" docs-pages bullet in the replacement body before applying it. Generated by Claude Code |
…le (objectstack-ai#10447, objectstack-ai#10446) (objectstack-ai#10555) Fixes objectstack-ai#10447 Fixes objectstack-ai#10446 Clause-②: no — one renderer's private interpolation rebinds to the existing core authority, and core's empty test aligns with `recordDisplayValueAt`. No export, schema or accept set moves. Folded pair, one branch, executed from the triage on each card (comments 5827587063 and 5827564760) and the PM claims 5828659772 / 5828664602. objectui#10446's core fix lands first, then the H1 rebinds, so no commit on this branch renders `HT-2026-003 -` in the H1. ## Commits 1. `4c140cb9c` — objectui#10446, `@object-ui/core`. `formatTitleTemplate` judges each placeholder through the value half of `recordDisplayValueAt`, now the module-private `displayValueOf`: trim, then empty, with an expanded reference reduced to its display name. It used to test the raw value against null, undefined and the empty string on its own, so a whitespace-only value counted as resolved and the final whitespace collapse left its separator dangling. One implementation of the emptiness rule instead of two spellings of it. Exported signatures unchanged. 2. `ef988ac2f` — objectui#10447, `@object-ui/components`. The record page H1's `titleFormat` rung hands the declared template (string or Expression envelope) to core's `formatTitleTemplate` and takes its answer, including core's empty "nothing resolved"; the rung's separate unresolved-token test is gone. `cleanupTitleSeparators` had no other caller and is deleted with its test file. The rungs' order (objectui#9436) does not move. 3. `47536ebd8` — a typing fix inside the same helper: the declaration build refused a copy that re-widened to possibly-undefined (TS18048). 4. `b06b17020` — prose only: two pending changesets reconciled with the rung's one interpolator (see "Reconciled pending changesets" below), and this change's changeset names both. ## Measured before the change: the two interpolators, token by token Probe at base `6881e9e47`: the real `page:header` H1 and the header's own rung (its `interpolate` plus `cleanupTitleSeparators`, plus its "contains a brace, so decline" test) against core's `formatTitleTemplate`, same record. "declines" means the rung handed over to the unified resolver, which renders core's way. | Token rule | header rung | core | H1 before | H1 after | |---|---|---|---|---| | `{account} - {deal_no}`, `account` expanded to Acme | `Q3-042` | `Acme - Q3-042` | `Q3-042` | `Acme - Q3-042` | | `{a} - {b}`, `b` whitespace-only | `A` | `A -` | `A` | `A` | | `{a}, {b}`, `b` missing | `A,` | `A` | `A,` | `A` | | `Session — {a}`, `a` missing | `Session` | empty | `Session` | next rung | | `Static title` (no placeholder) | `Static title` | empty | `Static title` | next rung | | `{status}`, select `in_progress` | `In Progress` | `in_progress` | `In Progress` | `In Progress` (kept) | | `{{a}} - {{b}}` | declines | `A - B` | `A - B` | `A - B` | | `{ a } - { b }` | declines | `A - B` | `A - B` | `A - B` | | `{first-name} {a}` | declines | `Ada L` | `Ada L` | `Ada L` | | value containing a brace | declines | the value | the value | the value | | controls: dotted path, lookup with no display name, array, null, empty string, `0`, Expression envelope, the separators `·` `:` `–`, a 3-part template with its middle empty | equal | equal | equal | equal | After the change every row where core answers non-empty has H1 equal to core (select rows: equal to core over the labelled record). Where core answers empty, the H1 walks on to the next rung, as `getRecordDisplayName` walks from its template rung. ## Select option labels (H2): kept, without touching `formatTitleTemplate`'s signature - The header routed a top-level select token through `fieldOptionLabel`; core has no such hook. - `withOptionLabels` builds a copy of the record whose select values read as their option labels, and the rung renders core over that copy. `optionLabelFor` is the one label lookup in the file, shared with `interpolate`, so the two readers cannot drift. - Like-for-like with the dedupe: only top-level scalars are mapped (null, undefined, expanded references and arrays stay as they are), and a mapped value is never empty because the label falls back to the raw value. So the same placeholders resolve on the copy as on the raw record, and `record:details`, which renders the template over the raw record, still decides about the field the H1 shows. Pinned in plugin-detail (the Status-row cases below). - In-repo census, with a lit control. Instrument: `git grep -n -E "titleFormat['\"]?\s*[:=]" -- ':!*.test.*' ':!**/__tests__/**' ':!**/CHANGELOG.md'`. The one object declaration it finds outside tests is `content/docs/guide/building-crud-app.md`'s `titleFormat: '{title}'`, a text field, so no in-repo `titleFormat` names a select field. Control: the same pattern over test files is non-empty and includes `page-header-title.test.tsx`'s `{status}` select case. First-party producers outside this repo (objectstack platform objects, e.g. `sys_audit_log` with `{action} · {object_name}` and `sys_member` with `{user_id} ({role})`) do name select fields, and each of those also declares a `nameField`, so their template rung is reached only when that pointer is blank on a record. Re-derive with `git grep -n titleFormat origin/main -- 'packages/**/src/**'` in objectstack. ## `interpolate`'s callers (H3) It keeps two callers, both `page:header`'s own templates: `title` and `subtitle`. They are unchanged. The `titleFormat` rung was the third and no longer comes through it, so `interpolate` stays. Instrument: `grep -n "interpolate(" packages/components/src/renderers/layout/containers.tsx`. ## Who the objectstack-ai#10446 rule reaches (H4) - `formatTitleTemplate` is called by `getRecordDisplayName`'s template rung (core), `DetailView.resolveDisplayTitle` and the `record:details` H1 dedupe (plugin-detail), and now `page:header`. `@object-ui/app-shell` re-exports it as `formatRecordTitle`. - `getRecordDisplayName`, so every resolver-backed surface: app-shell `RecordDetailView`; components `page:header`; core `sort-values`; fields `index` and `LookupField`; plugin-calendar `ObjectCalendar`; plugin-detail `DetailView` and `RelatedList`; plugin-gantt `ObjectGantt`; plugin-kanban `ObjectKanban`; plugin-list `ObjectGallery`; plugin-map `ObjectMap`; plugin-tree `ObjectTree`. Instrument: `git grep -n "getRecordDisplayName(" -- 'packages/**/src/**' 'apps/**/src/**' ':!**/__tests__/**' ':!*.test.*'`. - The existing `record-title` suites stay green, and every other test file that mentions `titleFormat` was run too (Gates below). ## Pins, red on base then green on head New files: - `packages/core/src/utils/__tests__/record-title.whitespacePlaceholder-10446.test.ts` - `packages/components/src/__tests__/page-header-title.oneInterpolator-10447.test.tsx` - `packages/plugin-detail/src/renderers/__tests__/record-details.titleFormatOneInterpolator-10447.test.tsx` Red leg, from committed head `ef988ac2f`: both source files checked out at base `6881e9e47`, new tests kept. On-disk proof before the run: `displayValueOf` 0 hits and the old empty test 1 hit in `record-title.ts`; `withOptionLabels` 0 hits and `cleanupTitleSeparators` 2 hits in `containers.tsx`. Result `Tests 11 failed | 25 passed (36)`: - core: THE CARD `expected 'HT-2026-003 -' to be 'HT-2026-003'` at both doors; blank leading placeholder `expected '- Acme' to be 'Acme'`; agreement rows "spaces" `expected '- K' to be 'K'` and "no-break spaces". - `page:header`: THE CARD `expected 'Q3-042' to be 'Acme - Q3-042'`; whitespace `expected 'HT-2026-003' to be 'HT-2026-003 -'` (H1 against core); comma `expected 'Q3-042,' to be 'Q3-042'`; literal prefix `expected 'Session' not to be 'Session'`. - `page:header` beside `record:details`: THE CARD `expected 'Q3-042' to be 'Acme - Q3-042'`; objectstack-ai#10446 in the page `expected 'HT-2026-003' to be 'HT-2026-003 -'`. - The 25 that stay green are the controls, including every select option-label case: the H1's label behaviour is kept, not changed. Restore leg: `git checkout HEAD --` both paths inside an EXIT/INT/TERM trap, blob hashes equal to `ef988ac2f`'s (`d7e714385`, `86a980376`), `git diff HEAD` empty. `47536ebd8` differs from `ef988ac2f` only by the typing fix inside `withOptionLabels`, a file the red leg replaces with base, so the red and green numbers carry over. Green leg: the three files pass on head inside the package runs listed under Gates; the two render files alone read `Tests 21 passed (21)`. ## Gates Code, type and test gates ran at `47536ebd8`; the changeset gates were re-run on the prose commit `b06b17020` (no code or test file moved between the two). - `pnpm --workspace-concurrency=2 --filter "@object-ui/plugin-detail^..." build` (the dependency closure, includes core and components): exit 0. - `type-check` of `@object-ui/core`, `@object-ui/components`, `@object-ui/plugin-detail`: `Done` ×3, exit 0. Each package's test program lists its new test file (`tsc -p tsconfig.test.json --listFiles`, 1 match per package). - `pnpm exec vitest run packages/core/`: `Test Files 172 passed (172)`, `Tests 3501 passed (3501)`. - `pnpm exec vitest run packages/components/ packages/plugin-detail/`: `Test Files 507 passed | 2 skipped (509)`, `Tests 4976 passed | 25 skipped (5001)`. - Every other test file that mentions `titleFormat` (app-shell, fields, plugin-dashboard, plugin-grid, plugin-map, react, types): `Test Files 21 passed (21)`, `Tests 218 passed (218)`. - `pnpm --filter @object-ui/components run test:dist`: `Test Files 2 passed (2)`. - `check-changeset-presence` (at `b06b17020`): `✅ 7 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s): .changeset/10447-one-title-interpolator.md.` - `check:new-line-citations` (at `b06b17020`): `VERDICT new-cross-file-line-citations: 0 new citation(s)`. - `check:control-bytes` (at `b06b17020`): `✅ check-control-bytes: OK`. - At `b06b17020`: `check-changeset-no-major` `✅ No changeset declares a major bump.`; `check-changeset-fixed` `✅ All workspace packages are in the changeset fixed group.`; `check:pending-changeset-literals` `✅ No test source names a pending changeset.`; `check-changeset-overwrite` exit 0, report-only, listing exactly the two reconciled entries (`2 modified, 0 deleted`); `check:changeset-claims` exit 0, report-only, and the entries it still lists name the touched files for other aspects (reviewed; none describes the `titleFormat` rung's interpolation). - `check:test-path-roots`, `check-vi-mock-override-shape`: exit 0. - eslint on the six touched source and test files: 0 errors (warnings are the repo-wide `no-explicit-any` / `only-export-components` level). The package-wide `pnpm lint` is CI's run; not measured here. ## Reconciled pending changesets Two changesets still pending in the same release described the rung as rendered by the header's own interpolation. Each is reconciled in the form objectui#10533 / objectstack-ai#10540 used, with its frontmatter byte-identical (md5 of the frontmatter block equal before and after: `3d9e2a1e59bc11924bb99ffd9eea81e3` and `ce888ebde4729564f67bca69a7928743`): - `.changeset/9436-page-header-declared-pointer-order.md`: item 3 now reads "rendered at this change with the header's own interpolation", and "At this change, objects that declare a pointer and no `titleFormat`, and objects that declare a `titleFormat` and no pointer, render exactly as before". A dated note follows: "Dated note, 2026-09-25 — the record title's titleFormat rung now renders through core's formatTitleTemplate — objectui#10447". It states that option labels are still translated, via `withOptionLabels`, and that the order is unchanged. - `.changeset/9174-interpolate-fastpath-trim.md`: "At this change `page:header`'s `title`/`subtitle` (and the record-title `titleFormat`) all went through `interpolate()`". The same dated note follows, stating that `title` and `subtitle` still go through `interpolate()`, so that entry's fix holds for them unchanged. - `.changeset/10447-one-title-interpolator.md` names both entries. Every other pending `.changeset/*.md` was re-grepped for present-tense claims about the header's own `titleFormat` interpolation or `cleanupTitleSeparators`, and none has one. Instrument: `git grep -n -i -E "cleanupTitleSeparators|separator cleanup|interpolat|formatTitleTemplate|titleFormat" -- .changeset/`. The nearest hits are about `page:header`'s `title` (objectui#10117, objectstack-ai#8923) or other readers, and still hold. ## File surface - `packages/core/src/utils/record-title.ts`, and beside it the new `packages/core/src/utils/__tests__/record-title.whitespacePlaceholder-10446.test.ts`. - `packages/components/src/renderers/layout/containers.tsx`, and beside it the new `page-header-title.oneInterpolator-10447.test.tsx`, a docblock-only edit to `page-header-title.test.tsx`, and the deleted `cleanup-title-separators.test.ts` (its function had no other caller). - `packages/plugin-detail/src/renderers/__tests__/record-details.titleFormatOneInterpolator-10447.test.tsx`: new, test-only, outside the claims' listed surface ("tests beside each"). It is where `page:header` and `record:details` render together (the objectui#9436 and objectui#10360 agreement files live beside it), which the H5 pin needs. No plugin-detail source changes. - `.changeset/10447-one-title-interpolator.md` (patch on `@object-ui/core` and `@object-ui/components`), plus the two reconciled entries above. ## Scope - objectui#10499 is not addressed here. The H1's row source is unchanged (`ctx.data`, the raw served row). This neither helps nor hinders that fix beyond one fact: the rung and `record:details`' dedupe now call the same function and differ only in the row they pass it. - objectui#10462 (action-entry `params`, same file) is untouched. ## Acceptance notes - Behaviour change on the rung, by the one-authority rule: a template none of whose placeholders resolves no longer shows its literal text in the H1 (`Session — {user_id}` with no user used to read `Session`); the H1 walks on to the next rung. - A resolved placeholder now emits the trimmed string `recordDisplayValueAt` returns. After core's whitespace collapse this is visible only where a padded value sits against a non-space literal, for example `({code})`. - `DetailView.resolveDisplayTitle` still renders the template over the raw record, so on the non-synthesized detail header a select token reads as its raw value where `page:header` shows the label. That predates this change and is read from source only, not measured through a running page. --- _Generated by [Claude Code](https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
…r barrel reached (objectui#8932) (objectstack-ai#10588) Fixes objectstack-ai#8932 Clause-②: no `@object-ui/plugin-kanban`'s `exports` map has no `./KanbanEnhanced` entry. The published 17.6.0 map is `.` only; this release's in-repo map is `.` and `./style.css`. The entry point does not export the component by name, so the declared surface does not move (the ruling's ③). The changeset still names the file for anyone who deep-imported it. Executes ruling `5634037484`, ratified `5810258711` (maintainer: 「同意」): **A — delete `packages/plugin-kanban/src/KanbanEnhanced.tsx`** and its test references. PM claim `5830002817`. Rework round after contract review `5831016914`. Implemented by the `os-dev` run in session `https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN`. ## What changes - `packages/plugin-kanban/src/KanbanEnhanced.tsx` is deleted. - **Where the component was.** I read the published 17.6.0 tarball with `npm pack`: - `dist/index.js` and `dist/index.umd.cjs` carry the component inlined (a `KanbanEnhanced` export of the chunk, bound to the minified function `ct`). It was reachable through the `kanban-enhanced` registry key and the entry's `kanbanComponents` map. - `dist/index.d.ts` references `./KanbanEnhanced`. - The `exports` map is `.` only. The tarball contains no CSS file. - The component leaves the bundle in this release with the objectui#8257 / objectui#8802 key retirement (the pending `8802-8257-8008` entry), not with this PR. Building the base (`d22b37bd8`, after objectstack-ai#8865) and this branch, `dist/index.js` and `dist/index.umd.cjs` contain `KanbanEnhanced` 0 times on both sides (control: `KanbanImpl` is found in `dist/index.js`). - **What this PR removes from the published package**, measured by building `@object-ui/plugin-kanban` on the base and on the branch: - `dist/KanbanEnhanced.d.ts` and `dist/KanbanEnhanced.d.ts.map`. Nothing else leaves the `dist/` listing. - `dist/index.d.ts` is byte-identical (`cmp` exit 0). - `@tanstack/react-virtual` leaves the plugin's `dependencies`. The deleted module was the only file in the package that imported it, and `check-unused-dependencies` fails without this change. `pnpm-lock.yaml` loses just this importer's three lines. `@object-ui/plugin-grid` still declares the package, so no resolution moves. - **`style.css`.** The stylesheet first ships in this release (pending `4929-plugin-published-stylesheets.md`). Built after this deletion, it does not carry three utility rules that only the deleted module used: `border-border/50`, `text-yellow-500` and `shadow-xl`. Building with the module keeps 65 rules; building without it keeps 62. - **`@object-ui/types` JSDoc** (comment only). Four present-tense passages that named `KanbanEnhanced` as a live reader now name `KanbanImpl` as the one board. The two past counts are dated and name objectui#8932 rather than being deleted: - `KanbanCard`: "what `KanbanImpl` reads back". - `KanbanColumn.cards`: "measured at objectui#6939: 12 lines in `KanbanImpl`, and 8 in `KanbanEnhanced` until objectui#8932 deleted it". - `ObjectKanbanSchema.columns`, twice: "what `KanbanImpl` consumes", and "counted when there were two board implementations … `KanbanImpl` alone still reads all six members". I re-measured that last claim: `KanbanImpl.tsx` reads `id`, `title`, `cards`, `limit`, `className` and `collapsed` off the lane. - The past-tense passages on `KanbanColumn.collapsed`, on the retired `color` member, and on the retired lane-collapse key stay as they were. - `git diff -w` over the two files shows only lines starting with `*`. - Changesets: - `.changeset/8932-retire-kanban-enhanced.md` (`'@object-ui/plugin-kanban': patch`) states how the component left the bundle, what this change removes, the deep-import and dependency notes, and that `style.css` ships without the three rules. - `.changeset/8932-kanban-enhanced-types-jsdoc.md` (`'@object-ui/types': patch`) is one sentence on the JSDoc. ## Premise re-checked on the current `main` (`d22b37bd8`) **H1: confirmed for the source tree, with lit controls.** - The in-repo `exports` keys are `.` and `./style.css`. - The entry has no `import` or `export` line that names `KanbanEnhanced` (grep exit 1). Control: the same grep finds the `React.lazy` import of `./KanbanImpl`. - A module-specifier grep over the whole tree (`from` / `import(` / `require(` / `vi.mock(`) finds `KanbanEnhanced` twice, both times in tests. Non-test importers: 0. Control: the same pattern for `KanbanImpl` finds the production import in `packages/plugin-kanban/src/index.tsx`. -⚠️ Corrected in this rework: the first round built only the base and the branch, both after objectstack-ai#8865, and wrongly concluded the component was never bundled. The published 17.6.0 package does bundle it (see "Where the component was" above). - PR objectstack-ai#8865's registry-key retirement did not change the file's situation. `kanban-enhanced` is unregistered and pinned by `kanban-family-registry-keys-retired-8257.test.ts`. The file was still on disk at the base. **H2: falsified.** The module had two test references, not one: - `src/__tests__/kanban-handler-slots-7664.test.tsx`: a `vi.mock` target whose prop recorder no leg read. - `src/cardPredicateScope.test.tsx`: a direct import. This is the reference the `index.tsx` tombstone itself named. I handled both as the ruling's intent directs: each test is deleted, or rewritten to what it still pins. ## Test files: every assertion kept, removed or moved `kanban-handler-slots-7664.test.tsx`: the `KanbanEnhanced` prop recorder and its `enhanced` log are removed. All legs are kept verbatim, because every probe records the `KanbanImpl` chunk. The `log` parameter type narrows to `keyof typeof recorded`. A docblock paragraph records the removal. `cardPredicateScope.test.tsx`: - **Removed:** "KanbanEnhanced binds the ambient predicate scope alongside the card". Its `KanbanBoard` twin stays and pins the same verdict on the one registered board. - **Moved, subject `KanbanEnhanced` → `KanbanBoard`:** - "a scope-gated condition fails SOFT (no style) outside the provider". It is now the negative control for the live positive leg, which had none of its own. - "a bare-field condition no longer binds the card (objectui#5741)". Its canonical-spelling control moved with it. - **Kept unchanged:** "KanbanBoard (impl) binds the ambient predicate scope alongside the card" and the four relation-field legs (objectui#3501). ## Other files the deletion forced - `packages/plugin-kanban/src/index.tsx` (comment only). The `kanban-enhanced` tombstone said the file "stays". It now records the deletion, names the new pin, and says that through 17.6.0 the component was also bundled behind the `kanban-enhanced` key. - `scripts/__tests__/one-authority-per-exported-name-6273.test.ts`: in `KNOWN_COLLISIONS`, `ConditionalFormattingRule` drops its third site, the deleted file. This baseline is shrink-only and fails on a stale site. - Ablation on the committed tree: I put the three-site entry back with `ablation-replace` (anchor 1 → 0, blob `0a8b7933d98f` → `d610bed3eea6`). - Result: "declares no name twice outside the named, shrinking baseline" goes red with "KNOWN_COLLISIONS lists collisions that no longer exist", naming the deleted path. - Restore: blob equals HEAD, and `git diff HEAD` is empty. - `packages/plugin-kanban/package.json` and `pnpm-lock.yaml`: the dependency removal described above. ## Pending changesets (H4) Three pending changesets are reconciled in the objectui#10533 / objectstack-ai#10540 form: the sentence is scoped to "at this change" and a dated note is added. The frontmatter stays byte-identical: the md5 of each file's frontmatter block is equal on base and head, and `check-changeset-overwrite` prints the same "declared at base" and "declares now" lines. - `8802-8257-8008-kanban-gantt-family-retirement.md`: "`KanbanEnhanced.tsx` has zero non-test importers. ⛔ The file is deliberately left in place". - `6172-markdown-kanban-one-authority.md`: "both files now re-point at it". - `8913-object-kanban-columns-declared.md`: "exactly the members the two board implementations read" and "before either board implementation sees one". `check-changeset-claims` surfaced this entry once the branch edited `objectql.ts`. Read and left as they are: - `6939-kanban-column-cards.md`: a measurement anchored at `78a3cc238`. - `9628-kanban-column-collapsed-honoured.md`: describes the reader as it was before objectui#9628. - `6172-kanban-trio-rename.md`: it names the `kanban-enhanced` registry key among "four registered kanban renderers". That went stale with the objectui#8257 / objectstack-ai#8802 key retirement, not with this file deletion. - `6349-types-internal-name-collisions-batch-1.md`: flagged for naming the ratchet file. Its "43 entries → 40" is that change's own count, and this PR does not change the entry count. - The other pending entries flagged because they name `complex.ts` or `objectql.ts` describe members this PR does not touch. The edits in those files are comment-only on the four kanban passages above. ## `CardTemplate` / `ColumnWidthConfig`: report only, not deleted Neither type is in `KanbanEnhanced`'s condition. Both are exported through `.` (the entry's `export type` line from `./types`), and the deleted module named neither of them (0 hits). - **`CardTemplate` is consumed.** `CardTemplates.tsx` imports it for its `templates` prop and its `onSelect` signature, and `CardTemplates` is itself exported from the entry. Importers of the `CardTemplates` component in `apps/`, `packages/`, `examples/`, `content/` and `skills/`, other than its own file and the entry: 0. - **`ColumnWidthConfig` has no reader.** 5 non-test source files name it: its declaration in `packages/types/src/complex.ts`, its Zod mirror, and three re-exports. That matches the reading objectui#8522 recorded. - Control on the same query: `KanbanCard` is named by the consumers `KanbanImpl.tsx` and `useQuickAddReorder.ts`. ## Pin (H5): red on the base, green on the head `packages/plugin-kanban/src/__tests__/kanbanEnhancedRetired-8932.test.ts` follows the `chart-primitives-removed-7397` file-removal shape (`existsSync` with a lit control) and this package's `useColumnWidthsRetired-8522` naming. It pins two things: - The file is absent from `src/`. Control: `KanbanImpl.tsx` in the same directory reads present. - No file under `src/` names the module as a specifier (static or dynamic import, re-export, `require`, `vi.mock` / `vi.doMock`). Firing control: the same walk and pattern find the live board's lazy import in `index.tsx`. Red on `76265dfdc` (the base plus the pin alone): 2 failed. The first reported `{ retired: true, live: true }`. The second listed `__tests__/kanban-handler-slots-7664.test.tsx` and `cardPredicateScope.test.tsx`, after its control assertion passed. Green on the head. ## Verification (final head `313ca6321`) Test runs, all from the repo root under the shared verify lock: - `pnpm exec vitest run --maxWorkers=2 packages/types/ packages/plugin-kanban/`: 298 files and 5636 tests passed. - `pnpm exec vitest run --maxWorkers=2 scripts/` (the root-level suite, which is outside the package graph): 177 files passed, 2 skipped, 5303 tests passed. - At `2165ee472` (the deletion without the dependency removal) the same suite had 1 failure: `check-unused-dependencies.test.ts` › "is green: every gated declaration has a consumer", with `@tanstack/react-virtual` / `declared-without-consumer`. Type-check, lint and build: - `pnpm --filter @object-ui/types type-check` (`tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json`): exit 0 (at `abda7fc09`; the types source is unchanged since). - `pnpm --filter @object-ui/plugin-kanban type-check`: exit 0. `--listFiles` on the test program lists the pin and both rewritten tests. - `pnpm type-check:scripts`: exit 0. - `pnpm --filter @object-ui/plugin-kanban lint`: 0 errors. - `pnpm --filter @object-ui/plugin-kanban build`: exit 0. - These four ran in the first round. The plugin-kanban source changed since only in one `index.tsx` comment. Gates, all exit 0 at `313ca6321`: - Changesets: `check-changeset-presence` ("7 source file(s) of 2 released package(s) changed, and this change declares 2 changeset(s)"), `check-changeset-no-major`, `check-changeset-fixed`, `check:pending-changeset-literals`. - Report-only changeset gates: `check-changeset-claims` and `check-changeset-overwrite`. Their findings are addressed above. - Line citations and bytes: `check:new-line-citations` ("0 new citation(s)") and `check:control-bytes`. First-round gates, green at `a843148be` and not reached by the later edits: - Mocks and test paths: `check-vi-mock-specifiers`, `check-vi-mock-inherit`, `check-vi-mock-override-shape`, `check-test-path-roots`. - Source and dependency checks: `check:handler-key-reads`, `check:unreferenced-sources`, `check:unused-deps`, `check:phantom-deps`. - Lockfile: `check:lockfile-integrity` and `check:lockfile-dedupe`. - Governed surface: `check-governed-queue-guard --test` answers NOT GOVERNED. Merge check: `git merge-tree --write-tree origin/main HEAD` against `origin/main` `50e41f738` exits 0. `main` has not touched any file this branch changes since `d22b37bd8`, so no merge was needed. NOT MEASURED: - `check:sdui-registration-pins`: it exits 2 without a console build (a prerequisite, not a verdict), and this diff changes no registration. - `check:readme-exports`: it needs every package built. The entry's `dist/index.d.ts` is byte-identical. ## Surface The claim's file surface named the module, one test, a pin, any pending changesets, and one new changeset. Beyond that, this PR touches: - `cardPredicateScope.test.tsx`: it imported the module. - `index.tsx` (comment only). - `scripts/__tests__/one-authority-per-exported-name-6273.test.ts`: the ratchet. - `packages/plugin-kanban/package.json` and `pnpm-lock.yaml`: the unused-dependency gate. - The two `@object-ui/types` files, `packages/types/src/complex.ts` and `packages/types/src/objectql.ts` (JSDoc only), plus their changeset. This was asked for by the contract review. Serial overlaps: - objectui#10524 edits `ChatbotSchema.body` JSDoc in `complex.ts`, and objectui#10518 edits `ObjectChartSchema` in `objectql.ts`. Both are far from the four passages edited here. -⚠️ Draft PR objectstack-ai#8941 (the lucide-react bump) edits the adjacent `lucide-react` line in `packages/plugin-kanban/package.json` and the same importer block in `pnpm-lock.yaml`. Whichever PR lands second meets a textual conflict there, cleared by merging `main` and re-running `pnpm install`. ## Acceptance notes - Test docblocks still mention `KanbanEnhanced` as it was when they were written: `columnCollapsedHonoured-9628.test.tsx`, `examples/schema-catalog/test/kanban-column-cards-6939.test.tsx`, `object-kanban-columns-declared-8913.test.ts` and `one-authority-per-exported-name-6273.test.ts`. They are not shipped and are left alone. - Observation, not filed: the pending `8802-8257-8008` entry does not say that the entry's published `kanbanComponents` map narrows from four keys to `object-kanban` alone. In 17.6.0's typings the map included `'kanban-enhanced'`. That narrowing is objectui#8257 / objectstack-ai#8802's, not this PR's. --- _Generated by [Claude Code](https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #10393
Clause-②: yes
Retires the bare
mapnode type key registered by@object-ui/plugin-map(and with it the namespaced twinview:map), executing the objectui#8008 family ruling (record5603555117, route 3: retire the bare registration, keepobject-*).object-mapis now the one spelling. The stored view typemap(NamedListView.type,defaultViewType,CreateViewDialog) is not touched.Implemented by the
os-devagent dispatched by thedomain:uiseat 2 PM, sessionhttps://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN. Head measured:ec6e667dd(code atc6d3f38b2;ec6e667ddis a prose-only rework on top). Base:09ab32bff.Guard (triage
5825154518, narrowed by the claim): does not fireNo non-test source resolves the renderer through
map/view:map. Measured on the base tree, per site:map/view:map(get/has/getConfig/register/registerLazy…)plugin-map/src/index.tsxandapps/console/src/register-plugins.ts(both removed here).InterfaceListPage'sallowedSet.has('map')is a Set of view types, not the registryobject-mapfires on the same pattern (both registrations + theRegistry.tsdoc example); an absent name (zzmapabsent) stays silent (exit 1)view:key built by template or concatenationdata-objectstackmetadata CACHE keys (view:+ object + view name), not registry lookupstype: 'map'object-mapfires in 11 non-test filesObjectViewandListViewboth have acase 'map'that emitsobject-map; the othertype: view.typesites build tab items, not nodesThe 22
type: 'map'sites in 10ObjectMap.*.test.tsxfiles all hand the node straight to theObjectMapcomponent'sschemaprop (JSX, notSchemaRenderer). That component does not readtypefor resolution, so they are left as they are. The onlyschema.typereads inObjectMap.tsxare warning-memo keys. That file is held by objectui#10525 and is not edited.Named refusal or plain unregistration: plain unregistration
In
@object-ui/typesthe literal'map'appears only in the stored view-type unions (defaultViewType,NamedListView.type,ObjectViewSchema.defaultViewType's zod enum). It never appears as a component node type. The firing control is'object-map':objectql.tsplus its zod mirror.AnyComponentSchemahas nomaparm, so there is nothing to turn into a named refusal. This matches theganttprecedent and is recorded in the tombstone docblock inplugin-map/src/index.tsx.What changed, and why each file
packages/plugin-map/src/index.tsx: removesComponentRegistry.register('map', …, { namespace: 'view' })and adds a tombstone docblock in the form ofplugin-gantt's. The comment that said two inline lists were pinned identical now describes one list.apps/console/src/register-plugins.ts: removes the lazymap/view:mapstub. This is the same move objectui#8865 made forgantt. The lazyobject-mapstub stays.packages/core/src/utils/record-source.ts: removes the'view:map'andmaprows fromRECORD_SOURCE_DATA_ARM_BY_TYPEand updates the doc comment (the one-block-spelling note onObjectMap.tsx, and the alias example now usesview:calendar/calendar). Two sentences said every one of these renderers is registered twice. That was already false forganttand this change makes it false formap, so both now say "most".packages/cli/src/utils/known-schema-types.ts: regenerated withnode scripts/regenerate-known-schema-types.mjs. Baremapleaves the list.view:mapstays, because the opt-in protocol placeholder registers it. That is the same outcome asview:ganttin objectui#8865, and the changeset says so..github/prompts/component.prompt.md: the list-viewKeys:bullet taughtview:map. After this change that key answers only the placeholder, socheck:prompt-keyswould go red. It now teachesobject-map, and the prose namesview:mapamong the retired namespaced spellings.scripts/__tests__/check-prompt-component-keys.test.tsfollows: the keys-line pin, andview:mapadded to the prose-only tombstones.packages/plugin-map/README.md: now says one registered type isobject-map. It keeps the sentence that a storedmapview compiles toobject-map, and adds a retirement note that separates the two layers. The false bare-array sentence carried from objectui#10392's ACCEPT is corrected (see below).object-maprows:index.recordSourceInput-7470.test.tsxandindex.recordSourceInputs-10394.test.tsxdrop theview:maprow. The 10394 "both keys publish identical lists" row is removed because only one list is left.index.registration.test.tsxdrops itshas('map', 'view')non-vacuity line.has('object-map', 'plugin-map')stays.ObjectMap.schemaDataShorthand.test.tsx3d now asserts the renderer's key group is EXACTLYobject-mapandplugin-map:object-map.SchemaRenderer.dataPropArm-9571.test.tsxandSchemaRenderer.legacyPropsDataStrip-9758.test.tsx(@object-ui/react, test-only) dropview:mapandmapfrom their object-arm lists. Those lists mirror the table rows removed above.packages/plugin-map/src/index.bareMapKeyRetired-10393.test.tsx:map/view:mapresolve nothing; the firing control is thatobject-mapresolves.mapnode. The html tier (validateTreeover the live registry) reportsunknown-component, and the Zod face (safeValidateSchema) refuses it as before. The control is that the same content spelledobject-mappasses both..changeset/10393-retire-bare-map-key.md:@object-ui/plugin-mapminor, with a BREAKING body (nevermajor, per AGENTS.md §9).@object-ui/core,@object-ui/cliand@object-ui/consoleare patch. It names the two docs pages and the pending entry below..changeset/10392-registration-record-source-inputs.md(pending, same release; prose only, frontmatter byte-identical): its sentence thatobject-map,mapandobject-ganttgaineddata/staticDatainputs now reads as of its own change ("At this change both were declared on all three"). A dated note follows it, headed "Dated note, 2026-09-25 — the baremapregistration has since been retired — objectui#10393". This is the form PR objectui#10533 used. A re-grep of every pending.changeset/*.mdfound no other present-tense claim thatmapis a registered key.7470-designer-objectname-not-required.mdnamesmapin the past tense and stays true, so it is left as is.Checked and left alone (the other packages objectui#8865 touched):
runnerandsdui-parser. In that PR their changes were the kanban manual-registration map and the kanban quick-add. Neither package references amapnode key.The stored layer is untouched, and existing tests already pin it. Nothing is duplicated here:
plugin-view/src/__tests__/ObjectView.mapFlatten.test.tsx: a storedtype: 'map'view emits anobject-mapnode. Re-run green in this change.plugin-map/src/ObjectMap.listViewMapConfigReach.test.tsx: aviewType: 'map'list view draws markers throughobject-map. Green.Bare-array sentence carried from objectui#10392's ACCEPT (README and two docs pages)
The README claimed that
staticData, a bare array underdataanddata: { provider: 'value', items }"all reach the same in-memory adapter". That has been false since objectui#8348 / #10394. The sentence now matches the registration'sdatadescription: a bare array underdatais not a record source, the ladder falls through tostaticDataand thenobjectName, and inline rows belong understaticData. Two more phrases in the same README said "an inlinedataarray" (thetitleFieldrow and thedataSourceprop row). They now readdata: { provider: 'value', items }configuration. This is the same claim in the same file, fixed in place.Two public docs pages taught the same false sentence. The seat folded them into this PR, and both now match the README wording:
content/docs/plugins/plugin-map.mdx, thefilter/sortparagraph. It used to say "staticData, a bare array underdata, ordata: { provider: 'value' }". It now saysstaticDataordata: { provider: 'value', items }, and states that a bare array underdatais not a record source.content/docs/fields/location.mdx, theNoteunder the map example. It used to say "anobjectName(or an explicitdataarray)". It now says inline rows go understaticDataor adata: { provider: 'value', items }configuration, and that a bare array underdatais not a record source.Both edits are prose outside fenced code blocks.
Red then green, for every new or strengthened pin
Red leg:
packages/plugin-map/src/index.tsxwas restored to base09ab32bffwithgit checkout BASE -- path, after the fix was committed.label: 'Map View'count went 0 → 1 and the tombstone count 1 → 0.git checkout HEAD -- pathunder a trap. The blob hash after the restore equals the HEAD blob58dd6164…, andgit diff HEADis empty.c6d3f38b2mapresolves nothing)expected true to be falseobject-mapresolves)map)expected [] to deeply equal [ 'unknown-component' ]object-mappasses both)expected [ 'object-map', 'map' ] to deeply equal [ 'object-map' ]object-mapkeys)map,object-map,plugin-map:object-map,view:mapBase leg:
Tests 4 failed | 11 passed (15). Head leg:Tests 15 passed (15).Local verification (code and tests at
c6d3f38b2; prose rework re-gated atec6e667dd)pnpm exec vitest run packages/plugin-map/ packages/core/src/utils/__tests__/plus the two react files,check-prompt-component-keys,known-schema-types-derivation-5115,report-namespace-agreement-6416andcheck-lucide-icon-record-names. Result:Test Files 113 passed (113),Tests 1904 passed (1904).apps/consoletests that importregister-plugins,packages/cli/,catalog-gallery-render,ObjectView.mapFlattenandcheck-doc-component-types. Result:Test Files 33 passed (33),Tests 1296 passed (1296).type-checkfor@object-ui/plugin-map,core,react,cliandconsole. AllDone, and the lock reportedVERDICT command-exit 0. The test programs ofplugin-mapandreactlist the changed test files, counted with--listFilesOnly.check:prompt-keys: "OK Every key taught as available is answered by a real renderer …"check:doc-types: "✅ Every documented component type is registered."check:registry-bare-names: "✓ no new bare-name collision"check:element-data-source-declaration: OKcheck:handler-key-reads: OKcheck:new-line-citations: "VERDICT new-cross-file-line-citations: 0 new citation(s)"check:control-bytes: OKcheck-changeset-presence: "✅ 11 source file(s) of 5 released package(s) changed, and this change declares 1 changeset(s)"check-changeset-no-majorandcheck-changeset-overwrite: ✅check:pending-changeset-literals: ✅check-doc-links: "Links are valid across 17 scan roots."check:doc-fences: ✅check:readme-exports: "543 real, 0 wrong-path, 0 fabricated"check-vi-mock-specifiers,check-vi-mock-inherit,check-vi-mock-override-shapeandcheck-test-path-roots: ✅check:component-surface-parity(exit 0; theview:maprows leave its population).check-changeset-claims(exit 0) flags 8 pending changesets that name a touched file. Each paragraph was re-read and none is falsified; see Acceptance notes.check:sdui-registration-pinsexited 2, a prerequisite: it needsapps/console/dist/assets, which only a console bundle build produces. Its derived key set (--list) holds nomaporobject-mapkey, so this diff moves nothing it pins.ec6e667dd, after the prose-only rework (docs and changesets):check-doc-links("Links are valid across 17 scan roots."),check:doc-fences(✅),check:doc-types("✅ Every documented component type is registered."),check:pending-changeset-literals("✅ No test source names a pending changeset."),check-changeset-presence(✅),check-changeset-no-major(✅),check:new-line-citations("0 new citation(s)"),check:control-bytes(✅) andcheck:prompt-keys(OK);check-changeset-overwriteexited 0 and reported, as expected, the one intended modification:10392-registration-record-source-inputs.md, with the same declaration at base and now;check:changeset-claimsexited 0 and reported one new pending entry naming a touched file:8221-retire-legacy-string-sort.mdnamesplugin-map.mdx. The paragraph was re-read and still holds (the docs still teachsortas an array only);doc-version-claims,changeset-polarity-census,check-changeset-claims,check-pending-changeset-literals,check-changeset-presence,check-changeset-overwrite,check-changeset-fixed,check-doc-links,component-node-vocabulary-7434,check-doc-expression-carriageandcheck-prompt-component-keysgaveTest Files 11 passed (11),Tests 532 passed (532), with the lock reportingVERDICT command-exit 0;check:doc-snippets. No fenced code block changed.Acceptance notes
view:mapsurvives as a placeholder key in the console (registerPlaceholders()→PROTOCOL_COMPONENTS) and on the generated CLI list. That is the same state objectui#8865 leftview:gantt/view:kanbanin. Removing it fromPROTOCOL_COMPONENTSwould be a separate decision and is out of scope here..changeset/10392-registration-record-source-inputs.mdis handled above: it now reads as of its own change and carries the dated note, with frontmatter byte-identical.skills/objectui/guides/architecture.md("Pattern A: The Component Registry") shows a user registering their OWNMapRendererasmapundernamespace: 'plugin-map'. It is an extension example, not a claim about this plugin, and it stays true. It is also a governed surface, so it is left alone.Generated by Claude Code