Skip to content

feat(plugin-map)!: retire the bare map node type key; object-map is the one spelling (objectui#10393) - #10540

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-10393-retire-bare-map-key
Sep 25, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-10393-retire-bare-map-key

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #10393
Clause-②: yes

Retires the bare map node type key registered by @object-ui/plugin-map (and with it the namespaced twin view:map), executing the objectui#8008 family ruling (record 5603555117, route 3: retire the bare registration, keep object-*). object-map is now the one spelling. The stored view type map (NamedListView.type, defaultViewType, CreateViewDialog) is not touched.

Implemented by the os-dev agent dispatched by the domain:ui seat 2 PM, session https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN. Head measured: ec6e667dd (code at c6d3f38b2; ec6e667dd is a prose-only rework on top). Base: 09ab32bff.

Guard (triage 5825154518, narrowed by the claim): does not fire

No non-test source resolves the renderer through map / view:map. Measured on the base tree, per site:

instrument hits in non-test source control
literal registry call on map / view:map (get / has / getConfig / register / registerLazy …) 2 registrations only: plugin-map/src/index.tsx and apps/console/src/register-plugins.ts (both removed here). InterfaceListPage's allowedSet.has('map') is a Set of view types, not the registry object-map fires on the same pattern (both registrations + the Registry.ts doc example); an absent name (zzmapabsent) stays silent (exit 1)
a view: key built by template or concatenation 2 hits, both data-objectstack metadata CACHE keys (view: + object + view name), not registry lookups the 2 hits are the matcher firing
a node emitted with literal type: 'map' 0 of 28 repo-wide hits are non-test object-map fires in 11 non-test files
a node type taken from a stored view type ObjectView and ListView both have a case 'map' that emits object-map; the other type: view.type sites build tab items, not nodes —

The 22 type: 'map' sites in 10 ObjectMap.*.test.tsx files all hand the node straight to the ObjectMap component's schema prop (JSX, not SchemaRenderer). That component does not read type for resolution, so they are left as they are. The only schema.type reads in ObjectMap.tsx are warning-memo keys. That file is held by objectui#10525 and is not edited.

Named refusal or plain unregistration: plain unregistration

In @object-ui/types the 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.ts plus its zod mirror. AnyComponentSchema has no map arm, so there is nothing to turn into a named refusal. This matches the gantt precedent and is recorded in the tombstone docblock in plugin-map/src/index.tsx.

What changed, and why each file

  • packages/plugin-map/src/index.tsx: removes ComponentRegistry.register('map', …, { namespace: 'view' }) and adds a tombstone docblock in the form of plugin-gantt's. The comment that said two inline lists were pinned identical now describes one list.
  • apps/console/src/register-plugins.ts: removes the lazy map / view:map stub. This is the same move objectui#8865 made for gantt. The lazy object-map stub stays.
  • packages/core/src/utils/record-source.ts: removes the 'view:map' and map rows from RECORD_SOURCE_DATA_ARM_BY_TYPE and updates the doc comment (the one-block-spelling note on ObjectMap.tsx, and the alias example now uses view:calendar / calendar). Two sentences said every one of these renderers is registered twice. That was already false for gantt and this change makes it false for map, so both now say "most".
  • packages/cli/src/utils/known-schema-types.ts: regenerated with node scripts/regenerate-known-schema-types.mjs. Bare map leaves the list. view:map stays, because the opt-in protocol placeholder registers it. That is the same outcome as view:gantt in objectui#8865, and the changeset says so.
  • .github/prompts/component.prompt.md: the list-view Keys: bullet taught view:map. After this change that key answers only the placeholder, so check:prompt-keys would go red. It now teaches object-map, and the prose names view:map among the retired namespaced spellings. scripts/__tests__/check-prompt-component-keys.test.ts follows: the keys-line pin, and view:map added to the prose-only tombstones.
  • packages/plugin-map/README.md: now says one registered type is object-map. It keeps the sentence that a stored map view compiles to object-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).
  • Pins that enumerated the retired keys drop them. They still pass on the object-map rows:
    • index.recordSourceInput-7470.test.tsx and index.recordSourceInputs-10394.test.tsx drop the view:map row. The 10394 "both keys publish identical lists" row is removed because only one list is left.
    • index.registration.test.tsx drops its has('map', 'view') non-vacuity line. has('object-map', 'plugin-map') stays.
    • ObjectMap.schemaDataShorthand.test.tsx 3d now asserts the renderer's key group is EXACTLY object-map and plugin-map:object-map.
    • SchemaRenderer.dataPropArm-9571.test.tsx and SchemaRenderer.legacyPropsDataStrip-9758.test.tsx (@object-ui/react, test-only) drop view:map and map from their object-arm lists. Those lists mirror the table rows removed above.
  • New pin packages/plugin-map/src/index.bareMapKeyRetired-10393.test.tsx:
    • row 1: map / view:map resolve nothing; the firing control is that object-map resolves.
    • row 2: both authoring faces now refuse a map node. The html tier (validateTree over the live registry) reports unknown-component, and the Zod face (safeValidateSchema) refuses it as before. The control is that the same content spelled object-map passes both.
    • row 3: the source registers exactly one key, read off disk with comments masked.
  • .changeset/10393-retire-bare-map-key.md: @object-ui/plugin-map minor, with a BREAKING body (never major, per AGENTS.md §9). @object-ui/core, @object-ui/cli and @object-ui/console are 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 that object-map, map and object-gantt gained data / staticData inputs 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 bare map registration has since been retired — objectui#10393". This is the form PR objectui#10533 used. A re-grep of every pending .changeset/*.md found no other present-tense claim that map is a registered key. 7470-designer-objectname-not-required.md names map in the past tense and stays true, so it is left as is.

Checked and left alone (the other packages objectui#8865 touched): runner and sdui-parser. In that PR their changes were the kanban manual-registration map and the kanban quick-add. Neither package references a map node 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 stored type: 'map' view emits an object-map node. Re-run green in this change.
  • plugin-map/src/ObjectMap.listViewMapConfigReach.test.tsx: a viewType: 'map' list view draws markers through object-map. Green.

Bare-array sentence carried from objectui#10392's ACCEPT (README and two docs pages)

The README claimed that staticData, a bare array under data and data: { provider: 'value', items } "all reach the same in-memory adapter". That has been false since objectui#8348 / #10394. The sentence now matches the registration's data description: a bare array under data is not a record source, the ladder falls through to staticData and then objectName, and inline rows belong under staticData. Two more phrases in the same README said "an inline data array" (the titleField row and the dataSource prop row). They now read data: { 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, the filter / sort paragraph. It used to say "staticData, a bare array under data, or data: { provider: 'value' }". It now says staticData or data: { provider: 'value', items }, and states that a bare array under data is not a record source.
  • content/docs/fields/location.mdx, the Note under the map example. It used to say "an objectName (or an explicit data array)". It now says inline rows go under staticData or a data: { provider: 'value', items } configuration, and that a bare array under data is 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.tsx was restored to base 09ab32bff with git checkout BASE -- path, after the fix was committed.

  • The mutation landed on disk: label: 'Map View' count went 0 → 1 and the tombstone count 1 → 0.
  • The restore was git checkout HEAD -- path under a trap. The blob hash after the restore equals the HEAD blob 58dd6164…, and git diff HEAD is empty.
pin on base on head c6d3f38b2
bareMapKeyRetired row 1 (map resolves nothing) ✗ expected true to be false ✓
row 1 FIRING CONTROL (object-map resolves) ✓ ✓
row 2 (both faces refuse map) ✗ expected [] to deeply equal [ 'unknown-component' ] ✓
row 2 CONTROL (object-map passes both) ✓ ✓
row 3 (source registers one key) ✗ expected [ 'object-map', 'map' ] to deeply equal [ 'object-map' ] ✓
schemaDataShorthand 3d (key group is exactly the two object-map keys) ✗ received map, object-map, plugin-map:object-map, view:map ✓

Base leg: Tests 4 failed | 11 passed (15). Head leg: Tests 15 passed (15).

Local verification (code and tests at c6d3f38b2; prose rework re-gated at ec6e667dd)

  • Tests: 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-6416 and check-lucide-icon-record-names. Result: Test Files 113 passed (113), Tests 1904 passed (1904).
  • More tests: the 10 apps/console tests that import register-plugins, packages/cli/, catalog-gallery-render, ObjectView.mapFlatten and check-doc-component-types. Result: Test Files 33 passed (33), Tests 1296 passed (1296).
  • Type check: type-check for @object-ui/plugin-map, core, react, cli and console. All Done, and the lock reported VERDICT command-exit 0. The test programs of plugin-map and react list the changed test files, counted with --listFilesOnly.
  • Gates that passed, each exit 0:
    • 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: OK
    • check:handler-key-reads: OK
    • check:new-line-citations: "VERDICT new-cross-file-line-citations: 0 new citation(s)"
    • check:control-bytes: OK
    • check-changeset-presence: "✅ 11 source file(s) of 5 released package(s) changed, and this change declares 1 changeset(s)"
    • check-changeset-no-major and check-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-shape and check-test-path-roots: ✅
  • Report-only: check:component-surface-parity (exit 0; the view:map rows 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.
  • ESLint on the 12 touched code files: 0 errors, 32 warnings, all on lines this change does not touch. This is a sanity run, not a proven narrowing; the full lint belongs to CI.
  • NOT MEASURED: check:sdui-registration-pins exited 2, a prerequisite: it needs apps/console/dist/assets, which only a console bundle build produces. Its derived key set (--list) holds no map or object-map key, so this diff moves nothing it pins.
  • Re-gated at ec6e667dd, after the prose-only rework (docs and changesets):
    • these gates exited 0: 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 (✅) and check:prompt-keys (OK);
    • check-changeset-overwrite exited 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-claims exited 0 and reported one new pending entry naming a touched file: 8221-retire-legacy-string-sort.md names plugin-map.mdx. The paragraph was re-read and still holds (the docs still teach sort as an array only);
    • tests: 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-carriage and check-prompt-component-keys gave Test Files 11 passed (11), Tests 532 passed (532), with the lock reporting VERDICT command-exit 0;
    • not run: check:doc-snippets. No fenced code block changed.

Acceptance notes

  • view:map survives as a placeholder key in the console (registerPlaceholders() → PROTOCOL_COMPONENTS) and on the generated CLI list. That is the same state objectui#8865 left view:gantt / view:kanban in. Removing it from PROTOCOL_COMPONENTS would be a separate decision and is out of scope here.
  • .changeset/10392-registration-record-source-inputs.md is 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 OWN MapRenderer as map under namespace: '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

…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
@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 9 pending changeset(s) describe a file this change touches

Their bodies publish verbatim into the CHANGELOG at the next release, so this is a request to re-read them against your diff — addressed here because you are the one seat that can answer it without re-deriving anything.

⛔ Nothing here blocks, and nothing here is a verdict on your change. This gate exits 0, is not a required context, and judges name resolution, never meaning: it asked whether a pending body names a file you touched. "Is this sentence still true?" is the one question it will not answer, and the one you are being asked to answer.

.changeset/6416-plugin-report-namespace.md

  • names packages/cli/src/utils/known-schema-types.ts → packages/cli/src/utils/known-schema-types.ts — edited by this change

    packages/cli/src/utils/known-schema-types.ts is regenerated from the registrations, dropping report:report, report:report-viewer and report:spec-report.

.changeset/7122-spec-17-3-0-parity-reconciliation.md

  • names known-schema-types.ts → packages/cli/src/utils/known-schema-types.ts — edited by this change

    user:profile is retired across all three sites. 17.3.0 dropped it from PageComponentType (measured: the enum went 34 → 32 options, lost set exactly ['user:profile', 'element:form'], gained set empty). objectui went on knowing it in three places, so all three moved together: the Studio palette exclusion ledger, PROTOCOL_COMPONENTS in renderers/placeholders.tsx, and the regenerated known-schema-types.ts the CLI checks schemas against. Nothing user-reachable went with it — neither type had a renderer, user:profile had only the dashed "Component Placeholder" scaffold, and the app shell's own profile affordance is a React slot, never this block type. A page schema still naming it now draws the loud "Unknown component type" panel rather than a silent grey box, which is this repo's standing treatment for a type outside the supported surface.

.changeset/7704-schema-registry-chatbot-keys.md

  • names packages/cli/src/utils/known-schema-types.ts → packages/cli/src/utils/known-schema-types.ts — edited by this change

    packages/plugin-chatbot/src/renderer.tsx registers three components — chatbot, chatbot-enhanced and chatbot-floating — and SchemaRegistry mapped one of them. Since ComponentType = keyof SchemaRegistry is the published union, a consumer discriminating on it was told two registered keys do not exist: an author narrowing a node by ComponentType, or writing a RecordANGLE-BRACKETS(ComponentType, …) table, had no arm for either. The asymmetry that showed which half was wrong is that packages/cli/src/utils/known-schema-types.ts keeps its own parallel list containing both keys, precisely because this map did not.

.changeset/8221-retire-legacy-string-sort.md

  • names content/docs/plugins/plugin-map.mdx → content/docs/plugins/plugin-map.mdx — edited by this change

    Director ruling, decision batch Release v0.2.1 with changeset automation #77 (2026-09-07), option B. Three faces disagreed about one key: @object-ui/core implemented the string clause on purpose (sort-query.ts, docblock and all), content/docs/plugins/plugin-map.mdx taught it as sort?: string | SortConfig[], and the html tier answered type-mismatch for it because all seven sort registrations publish type: 'array' alone — while @objectstack/spec refuses the string outright on element-record-picker. Option A (per-block string arms) was rejected by name: it would make one key mean different things on different blocks.

.changeset/8499-node-slot-registered-arms.md

  • names apps/console/src/register-plugins.ts → apps/console/src/register-plugins.ts — edited by this change

    line-chart is deliberately NOT armed. The card lists it among the eight as a live renderer; measured here it is not. apps/console/src/register-plugins.ts registers it as a lazy stub pointing at @object-ui/plugin-charts, and that package never registers the key, so it resolves to nothing at render time. Arming it would invent a capability rather than name one. __tests__/node-slot-registered-arms-8499.test.ts pins the absence together with its reason, so registering the key for real turns red.

.changeset/8760-unfulfilled-chart-stubs.md

  • names apps/console/src/register-plugins.ts → apps/console/src/register-plugins.ts — edited by this change

    What moved. The stub lists in apps/console/src/register-plugins.ts and apps/console/src/preview-gallery.tsx (both loops, because the doc gate's key universe is their union — retiring one alone would have changed nothing observable); the dashboard doc snippet plus a note on how chart families are actually spelled; the regenerated KNOWN_SCHEMA_TYPES snapshot (six entries, three bare and three namespaced); and the line-chart leg of node-slot-registered-arms-8499.test.ts, whose premise this retirement changed and which reads the stub list rather than the file so the ⛔ comment left behind cannot satisfy it.

.changeset/8802-8257-8008-kanban-gantt-family-retirement.md

  • names known-schema-types.ts → packages/cli/src/utils/known-schema-types.ts — edited by this change

    ⚠️ objectui check will NOT flag either namespaced spelling. The CLI's known-schema-types.ts is generated from the repository's real registration calls, and the placeholder registration is a real one — so view:kanban and view:gantt are still on that list and still validate green, while the node renders a placeholder rather than a board. The bare kanban / gantt entries DID leave the generated list; only the namespaced pair survives, and only because of the placeholder. Grep your documents for the namespaced spellings directly; do not rely on objectui check to find them.

.changeset/9473-record-source-data-arm-type.md

  • names record-source.ts → packages/core/src/utils/record-source.ts — edited by this change

    Two stale docblock passages in record-source.ts and record-source-config.behaviourNeutrality-7632.test.ts explained ObjectCalendar's retired 'data' in schema guard by the props union ObjectGridSchema | CalendarSchema, which objectui#8651 retired — ObjectCalendarComponentProps.schema is the published ObjectCalendarSchema today. Both now say that the clause is history rather than a reading anyone can re-derive from main.

.changeset/9533-dashboard-bare-key-converge-plugin-namespace.md

  • names register-plugins.ts → apps/console/src/register-plugins.ts — edited by this change

    What was wrong. apps/console declares the lazy stub for bare dashboard under plugin-dashboard — twice, in preview-gallery.tsx and register-plugins.ts — while this package registered the renderer as view:dashboard. Neither site passed skipFallback, so both also claimed the bare key (Registry.register and Registry.registerLazy share the meta?.namespace && !meta?.skipFallback branch, and registerLazy has no collision check at all, so nothing warned). Two consequences followed, and both were reproduced against a real Registry before anything here was written:

  • names packages/cli/src/utils/known-schema-types.ts → packages/cli/src/utils/known-schema-types.ts — edited by this change

    What changed. The renderer registers as plugin-dashboard:dashboard, the namespace every sibling plugin, both console stubs and the CLI whitelist already use, so the console stubs are cleared on load and all three claimants of the bare key name ONE full type. The retired view:dashboard key answers RetiredDashboardNodeTombstone — an inline refusal, plus a console.error carrying the same text — registered with skipFallback: true so it claims no bare key. Its spelling is withheld from the derived key universe by declaration in scripts/check-doc-component-types.mjs, the same disposition the RETIRED_FIELD_TYPES tombstones take, so objectui check names it rather than blessing it; packages/cli/src/utils/known-schema-types.ts regenerates and loses that one entry.

Read the paragraph, not the line: both false halves of the objectui#8617 claim sat in one paragraph, and correcting either alone would have left it asserting the same wrong thing.

If a claim did go false, correct the body. That is precedented and prose-only, frontmatter untouched; check-changeset-overwrite.mjs will report the correction as its own case 2 ("correcting a declaration on purpose … legitimate"), which is the intended shape — one gate asks for the read, the other records the write.

Not covered, stated so nobody reads this as more: a born-false claim that spells no line address at all (objectui#9495 coordinated one by ORDINAL — "a grep finds that member first" — and deciding that means reading what the sentence means), a claim spelled as a symbol or a package rather than a backticked file name, and a file named ambiguously.

Angle-bracketed names in the quoted prose above are rewritten as ANGLE-BRACKETS(name): GitHub deletes tag-shaped fragments from a stored body, and a quote that silently loses the identifier it is about is worse than a visible repair.

Compared the checked-out tree with 0cba1b73f (merge-base with origin/main): 16 file(s) changed outside .changeset/, read against 1435 pending declaration(s) that publish a body (2017 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3044.7 KB 3104.5 KB
Main entry chunk (gzip) 147.7 KB 350 KB
Entry file index-ncugmOeq.js —
Status PASS —

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.68KB 6.20KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 544.97KB 130.34KB
core (index.js) 9.22KB 3.71KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.86KB 62.25KB
fields (index.js) 257.02KB 65.07KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.38KB 14.60KB
plugin-charts (index.js) 74.95KB 20.88KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.59KB 35.39KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.25KB 61.40KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 146.50KB 37.51KB
plugin-gantt (index.js) 169.37KB 41.83KB
plugin-grid (index.js) 215.49KB 58.91KB
plugin-kanban (index.js) 49.30KB 15.39KB
plugin-list (index.js) 114.55KB 28.29KB
plugin-map (index.js) 22.42KB 7.38KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.64KB 8.94KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 88.00KB 21.97KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 114.58KB 37.60KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

…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
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3046.3 KB 3104.5 KB
Main entry chunk (gzip) 147.8 KB 350 KB
Entry file index-CoYeRkHi.js —
Status PASS —

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.68KB 6.20KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 544.97KB 130.34KB
core (index.js) 9.22KB 3.71KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.86KB 62.25KB
fields (index.js) 257.16KB 65.08KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.38KB 14.60KB
plugin-charts (index.js) 74.95KB 20.88KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.50KB 35.37KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.25KB 61.40KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 146.50KB 37.51KB
plugin-gantt (index.js) 169.64KB 41.92KB
plugin-grid (index.js) 215.37KB 58.92KB
plugin-kanban (index.js) 49.30KB 15.39KB
plugin-list (index.js) 114.55KB 28.29KB
plugin-map (index.js) 22.42KB 7.38KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.64KB 8.94KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 87.85KB 21.96KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 114.58KB 37.60KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: c6d3f38b2c4004268abdfd2bc44a718318c48146

① Derived judgments

Head and base. git rev-parse refs/review/pr-10540 = c6d3f38b2… (re-fetched at the end, unchanged). Merge base 09ab32bff; main is 7 commits ahead (82bdd3ac9), and git diff --stat 09ab32bff refs/review/main -- [the 15 files] is empty, so nothing drifted under the PR. 15 files, +284/−77, exactly the list the PR names.

The guard does not fire (own search on the head tree, non-test source).

  • Registry accessors with a computed first argument (get/has/getConfig/getMeta/register/registerLazy/hasLazy/loadLazy): action-bar.tsx:331 (an ActionComponent), page.tsx:480 (manifest over getKnownTypes()), form.tsx:569-3839 (field:${type}), DatasetReportRenderer.tsx:738-751 (called with 'chart'), LegacyReportRenderer.tsx:42 (schema.chart.type), placeholders.tsx:138 (PROTOCOL_COMPONENTS), SchemaRenderer.tsx:1879/1885 (evaluatedSchema.type, the generic path that is meant to stop resolving map). None derives map from a stored view type.
  • view: template or concatenation keys: 2 hits, both data-objectstack/src/index.ts:5121,5209 metadata cache keys. `object-${…}` template keys: 0.
  • Stored view type turned into a node: plugin-view/ObjectView.tsx:1881 case 'map' returns type: 'object-map'; plugin-list/ListView.tsx:3483 case 'map' likewise; app-shell/ObjectView.tsx:3285 type: view.type and plugin-view/ObjectView.tsx:1498 type: v.type build view-tab items, not nodes; InterfaceListPage.tsx:429 allowedSet.has('map') is a Set of view types and :460 feeds ListView's viewType. strict-authoring-face.ts:244 case 'map' is a Zod def kind.
  • type: 'map' in non-test TS across packages/apps/examples/skills/scripts: 0 (only the tombstone prose). Lit control: 'object-map' on 16 non-test source lines; type: 'object-map' in all 3 catalog schemas under examples/schema-catalog/src/schemas/plugin-map/.
  • No HTML-tag registration would re-claim the bare key: html-elements.tsx / semantic.tsx contain no 'map'.

Stored layer untouched. No file under packages/types, plugin-view, plugin-list, or ObjectMap.tsx is in the diff, so ViewTypeSchema, NamedListView.type, defaultViewType, both case 'map' branches and the map config block/input are byte-identical to base by construction. content/docs/api/schema-reference.md:906,1286 still teach "map" only as a stored view type, correctly.

Plain unregistration is the whole retirement. 'map' inside packages/types/src (non-test) appears only at objectql.ts:2006,2203 (stored view-type unions), objectql.ts:2583 (map?: config block), zod/objectql.zod.ts:527 (defaultViewType enum). Firing control: object-map literal at objectql.ts (1) and objectql.zod.ts (2). No AnyComponentSchema arm → no passthrough hazard, same reasoning as record 5603555117 and the 8802-8257-8008 changeset for gantt.

Surfaces that listed the bare key.

  • packages/cli/src/utils/known-schema-types.ts: exactly one line removed ('map'). Head still carries view:map (:657) and protocol-placeholder:view:map (:411), the same state as view:gantt/view:kanban (:653,:655,:408,:410). The regen script derives from registration calls and placeholders.tsx:48 still lists view:map, so the residue is correct.
  • apps/console/src/register-plugins.ts: the lazy map/view stub removed, object-map stub kept; the same move feat(types,plugin-kanban,plugin-gantt)!: retire the bare kanban / gantt node keys and the kanban-ui / kanban-enhanced registrations #8865 made.
  • .github/prompts/component.prompt.md:97 Keys line view:map → object-map; prose at :90 lists view:map among retired twins; :108 keeps map as a spec view type (correct). Pin check-prompt-component-keys.test.ts:572-592 follows. .github/prompts/ is not one of the five GOVERNED_SURFACES (check-governed-queue-guard.mjs:304-309).
  • Missed surfaces: none found. view:map and "type": "map" / type: 'map' in JSON/MD/MDX/YAML: 0 outside this PR's own prose. content/docs/plugins/plugin-map.mdx teaches object-map only (14 sites). runner, sdui-parser, scripts/check-doc-component-types.mjs, root README, packages/types/README.md: no bare key. skills/objectui/guides/architecture.md:73,77 registers a user's own MapRenderer as map in namespace plugin-map — an extension example on a governed surface, left alone and disclosed in the PR body; acceptable.

record-source.ts. The 'view:map' and map rows leave RECORD_SOURCE_DATA_ARM_BY_TYPE; object-map and plugin-map:object-map stay. The "every → most" doc edits are correct (already false for gantt). The replacement alias example view:calendar/calendar is a real pair (calendar is a registered key per the pending 10392 changeset; view:calendar passes the prompt-keys gate as a real renderer). The two react tests SchemaRenderer.dataPropArm-9571 and legacyPropsDataStrip-9758 drop exactly the same two rows from OBJECT_ARM_TYPES; no object-map row lost.

README. "registers one component type" — head index.tsx has one ComponentRegistry.register('object-map', …) call. The carried sentence is now true at source: record-source.ts:293-306 — a bare array is not on the view-data arm, the ladder falls to staticData (lifted to { provider: 'value', items }) then objectName; ObjectMap.tsx routes the value provider through ValueDataSource, so staticData and data: { provider: 'value', items } do share the in-memory adapter; ObjectMapSchema.staticData (objectql.ts:2829) carries no @deprecated. The titleField and dataSource rewordings are true (value provider issues no find/getObjectSchema, pinned by the schemaDataShorthand CONTROL rows).

Pins. index.bareMapKeyRetired-10393.test.tsx is red on base for the right reasons by construction: base registers map (row 1 has('map') true), a live-registry manifest containing map yields no unknown-component (row 2), and the on-disk regex finds two register('…' calls (row 3); controls fire on object-map either way. Base leg reported as 4 failed / 11 passed matches. maskComments helper (scripts/js-comment-mask.mjs) and @object-ui/sdui-parser dep (plugin-map/package.json:48) exist. Edited tests were narrowed only where they enumerated map/view:map; schemaDataShorthand 3d was strengthened from arrayContaining to exact equality. index.registration.test.tsx keeps has('object-map','plugin-map').

Serial constraint. ObjectMap.tsx not in the diff. PR #10525 has since merged (c5d715fc1 on main); no overlap with these 15 files.

② Semver level

  • @object-ui/plugin-map: minor with a BREAKING body: matches AGENTS.md §9 (line 271: breaking changes are marked minor, breaking semantics in the body; check-changeset-no-major is mechanical) and the precedent changeset 8802-8257-8008 (plugin-gantt: minor, cli/console: patch). The narrowing is real on the published surface: npm pack @object-ui/plugin-map@latest → 17.6.0, dist/index.js:584 contains a.register("map", O, … and its README:10-14 teaches the bare key.
  • core, cli, console: patch: each has published source in the diff; correct. @object-ui/react (17.6.0, not private) changed only under src/__tests__/; check-changeset-presence.mjs:162-164 asks only for an empty-frontmatter sentence for test-only changes, not a bump, so no react entry is owed. CI Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Overwrite Report all success.
  • Body sentences: "objectui check now flags [bare map]" is true (the line left the list); "view:map stays … placeholder" is true (placeholders.tsx:48); "retired the bare gantt and kanban keys" conflates record 5603555117 (gantt) with the sibling 8802 ruling it cites, which the 8802 changeset describes as one batch; acceptable.
  • Title feat(plugin-map)!:: no commitlint config and no PR-title workflow in the tree; 71 squash subjects on main carry !:, including the family precedent adf581278 feat(types,plugin-kanban,plugin-gantt)!: … (#8865). Nothing objects.

③ Boundary flags

Implemented-by: claude/issue-10393-retire-bare-map-key
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: ec6e667dd5e70cf560d21c80b79018431fc6a36f

① Derived judgments

Setup: git rev-parse refs/review/pr-10540 = ec6e667dd… (matches). Merge-base with refs/review/main (2ad36717b) is 09ab32bff. Round delta git diff --stat c6d3f38b2 refs/review/pr-10540: 1 commit, 4 files, 32+/12-, all .md/.mdx (.changeset/10392-…, .changeset/10393-…, content/docs/fields/location.mdx, content/docs/plugins/plugin-map.mdx). No code or test file touched. Full diff: 18 files, 315+/88-. git diff --stat 09ab32bff refs/review/main -- <PR-touched files> is empty: main has not moved under any touched file.

Item 1 — 10392 changeset. True as written. git diff --word-diff base..head shows exactly one bullet changed: "Both are now declared," → "At this change both were declared on all three," and "now draws" → "draws"; then a 9-line dated note. Every other sentence (header, calendar bullet, closing paragraph) is byte-identical, so no other meaning moved. Precedent check against #10533 (0cba1b73f, e.g. 6235-mergedsort-wrap.md, 8973-…, objectview-sort-sink-4869.md): the form is "At this change …" + blank line + "⚠️ Dated note, 2026-09-25 — X has since been retired — objectui#NNNN. Later in this same release … The rest of this entry is kept as the reading of this change; the objectui#NNNN entry states what ships." The 10392 note matches that skeleton with objectui#10393 (only difference: it is indented 2 spaces as a continuation paragraph of the bullet rather than at column 0; markdown-valid, not a defect). Frontmatter md5 2fac3c0624a8d77f734ac52b6f126f21 at base 09ab32bff, head, and refs/review/main — identical (3 packages patch). The note's factual claims hold: object-map at head declares data and staticData (packages/plugin-map/src/index.tsx lines 90–91), and unknown-component is what the PR's pin row 2 asserts.
Whole pending set re-grepped at head (2008 files) for `map`, view:map, "type": "map": the only entries naming the bare node key are 10392 (now dated), 10393 (this PR) and 7470. The other 15 hits (10002, 10370, 4730, 6530, 7544, 8170, 8193, 8345, 9340, 9950, 6343, 5042, map-marker-title-binding, 8980, 7779) are the map config block, the stored view type, a locale key or a zod wrapper kind — none claims map is a registered key or declares inputs. No present-tense claim remains. 7470 checked: "the registrations' designer-facing inputs still said objectName was required, on object-map, map and object-gantt" is past tense describing its pre-change bug; its present-tense "objectName is now declared without required" holds for the two surviving registrations. Stays true — the dev's statement is correct.

Item 2 — docs pages. True at source. packages/core/src/utils/record-source.ts at head: resolveRecordSourceConfig (lines 285–311) reads data only if authoredDataIsOnTheDeclaredArm, which for 'view-data' is !Array.isArray(authored) (line 176); a bare array fails rung 1 and falls to staticData (wrapped { provider: 'value', items }), then objectName. object-map / plugin-map:object-map are 'view-data' rows (lines 387–388). README lines 100–105 at head: "bare array under data is not a record source on this map (objectui#8348): the ladder falls through to staticData, then objectName, so inline rows belong under staticData" — plugin-map.mdx 124–126 is near-verbatim; location.mdx 134 uses the compact form "inline rows under staticData, or a data: { provider: 'value', items } configuration — a bare array under data is not a record source". Both also match the registration's own data description (index.tsx line 90). Fence parity by awk: plugin-map.mdx 122–128 at fence count 8 (prose); location.mdx 134 at fence count 8 (prose), and the example above it is "type": "object-map". No type: 'map' / view:map on either page (plugin-map.mdx line 32 plugins={['map']} is the PluginLoader plugin name, untouched). Version literals: the delta's added lines under content/docs/ contain no digits.digits at all; scripts/__tests__/doc-version-claims.test.ts CLAIM_RES all require FIRST_PARTY or TOOLCHAIN + VERSION, so nothing new can match. Citations apt: #8348 is the record-source data-arm ruling, #9571 the SchemaRenderer data-spread fix, #9061 inline filter/sort.
Other pending entries naming these pages: 8221 (plugin-map.mdx teaches sort?: SortConfig[] — line 112 at head, delta did not touch it) and 6127 (location.mdx fence split — fences intact; 6127 has empty frontmatter so it is outside the claim-re-read population, judged by hand). Both stay true.

10393 changeset. Names plugins/plugin-map.mdx and fields/location.mdx (lines 53–55) and the superseded 10392 entry with "reads as of its own change and carries a dated note naming this card" (57–62). Truthful against the delta.

② Semver level

Unchanged: @object-ui/plugin-map minor with BREAKING body, core/cli/console patch. 10393 frontmatter md5 02a248bf14464234d9665bad2c91931b at c6d3f38b2 and at head — identical. 10392 frontmatter identical to base and main (above). On head: Changeset Bump Policy, Changeset Declaration, Changeset Fixed Group Check, Changeset Overwrite Report all success.

③ Boundary flags

  • CI on ec6e667dd (last poll): 42 check-runs; 34 success, 3 skipped (Test coverage, coverage-shard matrix, dependabot), 5 in_progress: Test (shard 1/8), (2/8), (6/8), (7/8), Spec Main Shape Gate; 0 failures. Shards 3/4/5/8, Type Check, Lint, Build Docs, Doc Snippet Type Check, README Export Check, Bundle Analysis, all Doc gates and Changeset Claim Re-read are success. doc-version-claims.test.ts runs inside the Test shards, so its verdict on this head is among the 4 still running. Do not land before they conclude.
  • PR body replacement (judged from the seat's in-session description against the diff; the current /pulls/10540 body, 12626 chars, is unchanged and is the baseline): "Head measured ec6e667dd, prose-only rework on c6d3f38b2" — true. Docs paragraph matching README — true. 10392 described as "At this change both were declared on all three" + dated note, frontmatter byte-identical — true. 10393 bullet naming both pages and the 10392 entry — true. Dropping "left untouched" for 10392 — required and correct. The added re-gate list / "11 script test files / 532 tests" is the dev's own count, not re-run here; the corresponding CI gates are success on the head.
    ⚠️ Must fix in the body before applying: the round-1 body's final Acceptance-notes bullet says content/docs/plugins/plugin-map.mdx and content/docs/fields/location.mdx "are not in this claim's file surface and are reported for the seat, not fixed here." The seat's described edits do not name this bullet. Carried unchanged, it contradicts the diff at ec6e667dd (both pages are fixed) and would go into the squash commit message. Delete or rewrite it.
    Minor: the round-1 body says check-changeset-claims "flags 8 pending changesets"; the gate's own comment on this head (id 5828793771, refreshed 08:22:29Z after the 08:05:46Z head commit, "16 file(s) changed outside .changeset/") lists 9 (6416, 7122, 7704, 8221, 8499, 8760, 8802, 9473, 9533). If "8" is carried, it is off by one against the gate; not a blocker.
  • PR is still draft: true; mergeable_state read unknown at both polls (GitHub had not computed it). Seat should un-draft and confirm mergeability before merge.
  • Nothing in the code, tests, stored layer or changesets should stop landing.

Implemented-by: claude/issue-10393-retire-bare-map-key
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

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

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 25, 2026 08:35
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 25, 2026
Merged via the queue into main with commit 64563a9 Sep 25, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-10393-retire-bare-map-key branch September 25, 2026 08:48
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Sep 28, 2026
…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>
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Sep 28, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

1 participant