Skip to content

fix(plugin-detail,plugin-dashboard,plugin-grid): the I18nLabel family's batch 2 — record:path stage labels and four registrations accept a locale map (objectui#10993) - #11099

Merged
objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-10993-batch2-i18nlabel-reads
Sep 29, 2026
Merged

objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-10993-batch2-i18nlabel-reads

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #10993
Clause-②: yes

Batch 2 of the family card: the record:path raw read is repaired, the registry half is declared, and the docs face is corrected. RecordPathRenderer resolves each stages[].label locale map with pickLocalized against useObjectTranslation().language before the stages are translated, classified or drawn, so a spec-valid map no longer crashes the block. object-metric's label / description / title, object-grid's label and record:related_list's title declare type: ['string', 'object'], because their renderers already resolve the map, so validateTree no longer reports type-mismatch on a legal map. schema-reference.md stops calling fourteen of object-form's spec members, the five I18nLabel ones batch 1 repaired among them, "ObjectUI extensions with no spec backing". The census table below records every row's standing after this batch.

Held back, per the claim: object-grid's deprecated title (the caption fallback in ObjectGrid.tsx, serial behind objectui#11026, whose branch edits that file). The 9 never-read members and ai:chat_window are the seat's to route. Draft: a dispatched dev's PR (claim comment 5888090052, branch claude/issue-10993-batch2-i18nlabel-reads), so ready and landing belong to the seat. Measured at head a840a1094 on base e119f120c.

What changed

  • packages/plugin-detail/src/renderers/record-path.tsx: localizeStageLabels resolves each stage whose label is an object with pickLocalized against the UI language, inside a memo keyed on schema.stages and the language, before the picklist translation, classify() and the two rails read it. That covers three reads, not two: classify() probes value + label for its won/lost tokens, so a map left raw there probed [object Object] and a lost stage authored as a map stopped reading as lost. Only the map arm is touched; a string or any other value is handed on as authored, and the input array is returned untouched when no stage carries a map. The picklist translation of the status field still outranks the authored label, as it did for a string.
  • packages/plugin-dashboard/src/index.tsx: object-metric's label, description and title declare both arms, with descriptions that teach the map. MetricWidget resolves label (the tile heading) and description (the sub-caption); ObjectMetricWidget resolves title (the drill-down heading, falling back to label); all against the UI language.
  • packages/plugin-grid/src/index.tsx: GRID_QUERY_INPUTS' label declares both arms. Every read of it in ObjectGrid (the caption, the export title, the record-detail overlay heading) resolves with the spec's resolveI18nLabel against useDisplayLocale(), and the description says so. view:grid registers the same inputs, so its declaration carries the same arms; it is not in the public manifest, so no pin row reaches it through that door. ObjectGrid.tsx is not touched.
  • packages/plugin-detail/src/index.tsx: record:related_list's title declares both arms; RecordRelatedListRenderer resolves it with pickLocalized against the UI language before its object-label fallback. The same file's cross-file line addresses are re-addressed by content (objectui AGENTS.md commandment 11, "When you touch a file anyway, fix the cross-file line addresses inside it"; the seat applied the same rule to batch 1): the relationshipValueField / add reads in record-related-list.tsx, the picker.valueField / picker.labelField defaults in RelatedList.tsx (both addresses had drifted), the hideFields read in record-details.tsx, the DetailSection.tsx empty-section return, the spec's component.zod.ts bindings, and the applyFeedConfig call shape in record-activity.tsx. Comment text only.
  • content/docs/api/schema-reference.md: the ObjectFormSchema "Spec alignment & extension keys" bullet that called submitText, cancelText, nextText / prevText, successMessage and nine more keys "ObjectUI extensions ... with no spec backing yet" is replaced by what the installed spec says (measured below): a "Component-contract keys" class for the fourteen keys that are members of ComponentPropsMap['object-form'] but not of FormViewSchema, and an "Envelope key" class for className, which is on PageComponentSchema. The ObjectGridSchema table gains a label row typed string \| I18nLabel (the table had no label row at all).
  • Tests (new): record-path.stageLabelI18nLabel-10993.test.tsx and record-related-list.titleI18nLabel-10993.test.tsx (plugin-detail), ObjectMetric.i18nLabel-10993.test.tsx (plugin-dashboard), ObjectGrid.labelI18nLabel-10993.test.tsx (plugin-grid), i18nLabelInputsManifest-10993.test.ts (console). Every render pin mounts the { type, properties } document through the real SchemaRenderer and registry under zh and en, with every map listing en first. registry-inputs-spec-parity.test.ts gains five MEMBER_PINS rows (object-grid.label, object-metric.description / .label / .title, record:related_list.title), and the record:path.stages row names the new stage-label pin.
  • .changeset/10993-batch2-i18nlabel-reads.md: @object-ui/plugin-detail, @object-ui/plugin-dashboard and @object-ui/plugin-grid minor (each package's registration widens; plugin-detail also carries the renderer repair), with the Clause-②: yes line.

Premise checks

  • The spec rows. On the installed @objectstack/spec 17.4.0, ComponentPropsMap accepts a locale map and refuses a number (invalid_union) at record:path stages[].label, object-metric label / title / description, object-grid label and record:related_list title. view:grid has no row.
  • The red, before any edit. Through the real SchemaRenderer at base: { type: 'record:path', properties: { statusField, stages: [ { value, label: { en, 'zh-CN' } } ] } } rendered Component "record:path" failed to render with "Objects are not valid as a React child (found: object with keys {en, zh-CN})".
  • Each widened input's renderer resolves the map (the ComponentInput.type rule: teach the render site, then declare the arm), read at the sites named above and measured by the render pins, which are green on the base renderers too (they are the render half the arms rest on, not repairs). ObjectGrid's fourth label read, columnDiagnosticLabel, feeds a console diagnostic that keeps the label only when it is a string, so a map is dropped from the message rather than printed.
  • The registry half is complete. A one-off probe (not committed) walked every installed ComponentPropsMap row for top-level members that accept a locale map and a string and refuse a number and an array, and read each one's registered input: 29 members, 28 declare both arms at this head, and the 29th is object-grid.title, which is deliberately not declared (the ruled carve-out for the deprecated spelling).
  • Mechanism assumption 1, measured and not followed. The dispatch suggested routing stages[].label through the resolver the block's aria name uses (resolveI18nLabel against useDisplayLocale()). The stage label is resolved with pickLocalized against useObjectTranslation().language instead: it is the source every other visible record:* label in this package uses (record:related_list.title, record:alert.title, record:details sections), the card's direction, and the language t() composes the stage's accessible name in, so the name never mixes a regional-locale label with UI-language state words. The two resolvers agree limb for limb; only the locale source differs.
  • Serial check. claude/issue-11026-number-use-grouping (fetched into a private ref) edits neither plugin-grid/src/index.tsx nor plugin-detail/src/index.tsx; no serial conflict.

Evidence

All runs at a840a1094 unless a line says otherwise, through the shared verify lock on a shared box.

  • pnpm exec vitest run packages/plugin-detail/: Test Files 220 passed | 1 skipped (221), Tests 2159 passed | 8 skipped (2167).
  • packages/plugin-dashboard/: Test Files 152 passed (152), Tests 1385 passed | 6 skipped (1391).
  • packages/plugin-grid/: Test Files 166 passed (166), Tests 1543 passed (1543).
  • The registry-reading suites: the 16 console files that import register-plugins (the parity gate and the three I18nLabel manifest pins among them), cli registered-types-validate-ratchet-10859, spec-vocabulary-hint and validate-union-arm-selection, and schema-catalog catalog-gallery-render and safe-validate-corpus-6318: Test Files 21 passed (21), Tests 1022 passed (1022).
  • turbo run build over the three packages' dependency closure (14 tasks), then pnpm --filter type-check (tsc --noEmit && tsc -p tsconfig.test.json) for @object-ui/plugin-detail, @object-ui/plugin-dashboard and @object-ui/plugin-grid, joined by &&: exit 0. tsc -p tsconfig.test.json --listFiles in each package lists the new pins (2, 1 and 1), so the type-check covers them; each part exited 0.
  • Gates, each exit 0 at this head: check-changeset-presence, changeset:check, check:changeset-claims, check:pending-changeset-literals, check:control-bytes, check:new-line-citations (0 new), check:doc-types, check:doc-fences, check:registry-bare-names, check:prompt-keys, check:component-surface-parity, check:designer-field-key-parity, check:element-data-source-declaration, check:i18n-keys, check:test-path-roots, check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:handler-key-reads, check:unreferenced-sources, check:phantom-deps. check-governed-queue-guard --test over the 12 paths: NOT GOVERNED.
  • ESLint, narrowed and proven: the population is the config's own files: ['**/*.{ts,tsx}'] block over tseslint.configs.recommended; --format json over the 10 changed code files reports 10 files, 0 errors, 67 warnings, 1 of them on a line this branch adds: react-hooks/preserve-manual-memoization on the stages memo in record-path.tsx, whose type annotation this PR edits. Linting the base blob reports the same rule on the same memo. The config enables no type-aware linting (no parserOptions, projectService or TypeChecked in eslint.config.js) and no eslint-rules/ rule reads another file, so this diff cannot move a verdict on an untouched file.

Red on base, then green. The fix files were committed, then the four source files (plugin-dashboard, plugin-grid and plugin-detail src/index.tsx, and record-path.tsx) were replaced on disk by their e119f120c blobs inside the verify lock, under a trap-restore, at 2ef598299 (hashes equal to the base blobs; objectui#10993 mentions 0 in each index file, localizeStageLabels 0). Tests 16 failed | 225 passed (241) over the six pin files: the manifest pin's 5 arm rows (expected [ 'string' ] to deeply equal [ 'object', 'string' ]) and 5 map rows (a type-mismatch warning where none is expected) failed while its 3 reachability, 5 string and 5 number controls passed; the record:path pin's 5 map rows failed on Component "record:path" failed to render while its string control passed; the parity gate failed exactly every member pin names a key that is still array/object-armed on a covered block, naming the five new rows; and the object-metric, object-grid and record:related_list render pins stayed green, as predicted, because those renderers resolved the map already. Restored: hashes equal the HEAD blobs, git diff HEAD empty. The one later commit, a840a1094, rewrites localizeStageLabels' loop only, and the mutated tree is the base blobs either way.

The census, after batch 2

Every installed (17.4.0) ComponentPropsMap row's I18nLabel members, as batch 1 enumerated them in PR objectui#11050, with each row's standing now.

Row Member(s) Standing
object-form title, description, submitText, cancelText, nextText, prevText, successMessage done in batch 1 (PR objectui#11050)
object-master-detail-form title, submitText, cancelText already resolving (PR objectui#10986)
object-metric label, title, description already resolving; inputs declare both arms here
object-grid label already resolving; input declares both arms here
object-grid title held back: raw caption fallback in ObjectGrid.tsx, serial behind objectui#11026
element:text content already resolving
element:button label already resolving
element:text, element:button, element:image, element:number aria.ariaLabel done by objectui#11051 (PR objectui#11078)
element:text_input label, placeholder, description already resolving
element:record_picker label, placeholder, emptyText already resolving
element:text_input aria.ariaLabel not read: for the seat
element:record_picker, element:metadata_viewer aria.ariaLabel not read: for the seat
page:header title, subtitle already resolving
page:card title already resolving
page:tabs, page:accordion items[].label already resolving
page:header, page:card, page:tabs, page:accordion aria.ariaLabel not read: for the seat
record:alert title, body, action.label already resolving
record:related_list title, add.label already resolving; title input declares both arms here
record:details sections[].label already resolving
record:path stages[].label repaired here
record:activity, record:chatter, record:details, record:discussion, record:highlights, record:path, record:related_list aria.ariaLabel already resolving
record:chatter, record:discussion feed.aria.ariaLabel not read: for the seat
ai:chat_window aria.ariaLabel no renderer: for the seat

The rows' standings are readings at this head on spec 17.4.0; nothing in this PR re-derives the table.

Acceptance notes

  • NOT MEASURED: check:eager-closure / Bundle Analysis and check:sdui-registration-pins. Reason: both need a full console build. The five input descriptions add text to registrations the console loads eagerly.
  • NOT MEASURED: check:doc-snippets and check:doc-examples. Reason: not run; they need the full package set built. The doc edit adds a table row and bullet text, no fenced snippet.
  • NOT MEASURED: apps/console type-check. Reason: its dependency closure was not built here. The console change is two test files; the new one copies the shape of batch 1's objectFormI18nLabelManifest.test.ts. CI's type-check is the reading.
  • The TS face stays narrower than the spec, and this batch leaves it. In @object-ui/types (record-components.ts), RecordPathComponentProps.stages[].label, RecordRelatedListComponentProps.title and .add.label, and RecordDetailsComponentProps.sections[].label are typed string where the spec row says I18nLabel. The renderers resolve the map at runtime, so nothing crashes, but a TypeScript author who writes the spec's map gets a compile error. packages/types is outside this claim; named in the report for the seat as this card's TS face.
  • record:path's stages description promises what the renderer does not do. The registration says "Explicit stage definitions [{ value, label }] (else derived from field metadata)", echoing the spec's "(if not using field metadata)". A one-off probe (not committed): { type: 'record:path', properties: { statusField: 'stage' } } through SchemaRenderer, in a record context whose adapter declares the stage picklist, renders "record:path — no stages configured", with no list items and no getObjectSchema call. Only the synthesized default page derives stages. This PR does not edit that description (a different defect). Reported to the seat.
  • Cross-file line addresses: repaired in plugin-detail/src/index.tsx, the registration file this PR edits (see What changed). registry-inputs-spec-parity.test.ts, where this PR adds five rows as batch 1 added seven, carries 13 lines with such addresses in its prose and exemption reasons. They are left as batch 1 left them, because repairing them is outside this card; carrier: none.
  • view:grid shares GRID_QUERY_INPUTS, so its label declares both arms too. It is not in getPublicConfigs()'s manifest, so the manifest pin has no row for it.
  • main moved during the run by four commits (to 3e71a9825), none touching this PR's files. It is not merged in, because the queue rebuilds on main. objectui#11051's PR 11078 landed as a4b017eda, which is why the census marks the element:* aria.ariaLabel row done.

Generated by Claude Code

…(objectui#10993, batch 2)

`stages[].label` is an I18nLabel in the spec row; a locale map was read raw
as a React child and the block rendered `Component "record:path" failed to
render`. The renderer now resolves each map with pickLocalized against the
UI language before the stages are translated, classified or rendered.

Claude-Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec
Co-authored-by: Claude <noreply@anthropic.com>
…ccept an I18nLabel locale map (objectui#10993, batch 2)

object-metric label/description/title, object-grid label (and view:grid)
and record:related_list title declared 'string' only while their
renderers already resolve a per-locale map, so validateTree reported
type-mismatch on a spec-legal map. Each declares ['string', 'object'],
with a render pin per block and one manifest pin, the MEMBER_PINS rows,
the schema-reference docs face and a changeset. The related_list and
other plugin-detail registration comments cite by content, not line.

Claude-Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec
Co-authored-by: Claude <noreply@anthropic.com>
…w:grid, which the public manifest does not publish (objectui#10993)

Also types record:path's local stage shape's value as unknown rather than any.

Claude-Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec
Co-authored-by: Claude <noreply@anthropic.com>
… loop, so the narrowing of its copy is visible to the compiler (objectui#10993)

Claude-Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added documentation Improvements or additions to documentation plugin apps tests labels Sep 29, 2026
@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 18 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/3917-retire-action-condition-branch.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    • ActionCondition is removed from @object-ui/types (and from the barrel export). - ActionSchema.condition is retyped to the predicate the runtime actually honours: boolean | string | { dialect?: string; source: string } — the same three arms ActionRunner's own ActionDef.condition carries, and the same vocabulary visible and disabled use. - ActionConditionSchema is removed from @object-ui/types/zod (and from the zod barrel); the condition key now validates against that predicate union. - The two teaching sites (content/docs/core/enhanced-actions.mdx Conditional Execution, content/docs/api/schema-reference.md ActionSchema table) are rewritten to the live vocabulary: condition is a gate; a branch is expressed as separate actions with mutually exclusive conditions.

.changeset/6170-retire-timeline-dead-keys.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Also in this change: the in-repo example packages/types/examples/data-display-examples.json (its timeline node authored all three) is migrated to items / variant; the two content/docs/api/schema-reference.md snippets that authored events: [] now author items: []; and the plugin-timeline docs callout says retired rather than deprecated.

.changeset/6896-retire-chart-inline-data.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    ⚠️ One correction to the record the ruling rests on. The ruling states zero authorship of a populated series[].data outside tests across packages/ / apps/ / examples/. The re-measurement finds one such site inside those roots — packages/types/examples/data-display-examples.json (2 series) — plus four outside them, in documentation: content/docs/api/schema-reference.md (3) and content/docs/core/report-schema.mdx (1).

.changeset/6939-kanban-column-cards.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Breaking, deliberately. KanbanColumn declared its card list as items in complex.ts and in the zod mirror complex.zod.ts. Every board reads cards. Measured on origin/main 78a3cc238: KanbanImpl.tsx reads .cards on 12 lines, KanbanEnhanced.tsx on 8, and bucketCardsIntoColumns twice more as col.cards || []; .items had zero read sites in either board (a same-shaped .title control on the same two files returns 8 and 3, so those zeros are readings and not a mis-shaped probe). Both catalog entries, the plugin docs and content/docs/api/schema-reference.md all author cards.

.changeset/6951-tree-view-data-retired.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    What was measured, on this branch's base. TreeViewSchema declared two spellings for its one inline-nodes slot — nodes (read second) and data (read third: boundData || schema.nodes || schema.data || [] at renderers/data-display/tree-view.tsx:105), both declared by objectui#6150. data had been REQUIRED until 777e5c6f4 (PR fix(types): tree-view mirror stops requiring the limb it reads third (group 2 of objectui#6939) #7533) made it optional, so this retirement starts from a declared-and-optional member on both faces. The in-repo corpus at the retirement: seven tree-view nodes under examples/schema-catalog and packages/types/examples plus one content/docs fence — six on nodes, two on data (packages/types/examples/data-display-examples.json and content/docs/api/schema-reference.md), both rewritten; no package source authored either spelling.

.changeset/7664-kanban-arm-plugin-dialect.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Migration. Author boards in the plugin dialect — objectName + groupBy for an object-bound board, or columns[].cards[] with badges for a static one. Replace DeclarativeKanbanSchema imports with KanbanSchema (from @object-ui/types, or the Zod KanbanSchema from @object-ui/types/zod; @object-ui/plugin-kanban re-exports the same KanbanSchema type). Delete draggable (drag-and-drop is always on) and column color (style a lane through className). content/docs/api/schema-reference.md's kanban section now documents this dialect.

.changeset/7997-detail-view-related-retired.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Documentation. packages/plugin-detail/README.md and content/docs/api/schema-reference.md stop teaching the retired array and gain a migration block each.

.changeset/8067-component-input-member-kind.md

  • names apps/console/src/__tests__/registry-inputs-spec-parity.test.ts → apps/console/src/__tests__/registry-inputs-spec-parity.test.ts — edited by this change

    A registration's type: 'array' said a value was a list and stopped there, so a member that drifted from @objectstack/spec was invisible to every layer that reads a declaration. page:header.actions is the measured cost: the contract declares z.array(z.string()) ("Action IDs"), the renderer read the members as ActionDef objects, and the repo-wide parity gate in apps/console/src/__tests__/registry-inputs-spec-parity.test.ts stayed green for the whole life of the drift because both sides carried the key and neither could say what was inside it. What settled it was a maintainer ruling, not a test — and even after the fix, "these are ids" survived only as English in the registration's description.

.changeset/8268-testid-emitted-as-data-testid.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    The other direction — retiring the promise — was considered and declined. It is what c1fe272ad did for BaseSchema.hidden, but that key had a working behaviour to describe and zero named consumers, and the ruling's decline turned on exactly that. This promise already has carriers outside the type declaration: content/docs/api/schema-reference.md states it as a table row and authors testId in that page's own base-schema example, @object-ui/cli's OBJECTUI_STRUCTURAL_KEYS identifies a file as an ObjectUI schema node by this key, at this change ObjectGridSlotKey / ObjectFormSlotKey pin it, SchemaBuilder.testId() writes it, and ADR-0054 C4 — shipped — reads "the renderer emits data-testid … derived from metadata".

.changeset/8653-listview-title-retired-rowactiondefs-pinned.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    title — retired. ListView resolved its export filename through schema.label || (schema as any).title. @objectstack/spec/ui's ListViewSchema refuses title by name (unrecognized_keys: ['title']) while ObjectGridPropsSchema accepts it; packages/types mirrors the platform contract rather than ruling over it, so declaring title on ListViewSchema would have made this repo accept what the platform save gate rejects. That asymmetry is also why objectui#6639 could take the declare branch for ObjectGridSchema.title one package over and this site could not. A parse-based census of apps/ examples/ content/ and packages/ found zero list-view nodes authoring title, so the retirement costs no author a filename. Over that same corpus the instrument reports three object-grid nodes carrying the key: two authored ones, both in content/docs/api/schema-reference.md, plus one that is not authored at all — packages/plugin-view/src/ObjectView.tsx composes title: schema.table?.title onto a grid node it builds, so it is a producer writing the key rather than an author declaring it. ObjectGrid's own title reads are untouched — they remain declared, ruled and read.

.changeset/8801-object-kanban-allow-collapse-retired.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    • the declarations retired here — packages/types/src/objectql.ts and its mirror packages/types/src/zod/objectql.zod.ts; - the pins that assert the retirement — object-kanban-allow-collapse-retired-8801.test.ts and bare-kanban-node-key-retired-8802.test.ts; - a comment in packages/types/src/zod/complex.zod.ts, recording that the deleted retiredZeroReadKanbanKey helper once carried this spelling on the SIBLING arm; - one row of content/docs/api/schema-reference.md; - the .changeset/ release notes that discuss it — this one, the two historical entries covering the sibling arm's own spelling, and objectui#9629's note recording the correction to this paragraph.

.changeset/9187-record-highlights-layout-two-values.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    ⚠️ The census behind this narrowing covers this repository only, and it found no in-repo authoring to migrate: every in-tree layout: 'grid' belongs to a different component (detail-view in content/docs/api/schema-reference.md and phase2-schemas.test.ts, ai-recommendations in packages/plugin-ai/README.md), and the one in-repo consumer of this interface that writes a layout (p1-spec-alignment.test.ts) writes 'horizontal'. So no document in this repository stops type-checking. A TypeScript consumer outside this repo that wrote grid is not observable from here and gets a compile error (TS2322) naming the key — which is why the FROM/TO is spelled out above.

.changeset/9280-record-highlights-entry-icon-retired.md

  • names packages/plugin-detail/src/index.tsx → packages/plugin-detail/src/index.tsx — edited by this change

    • packages/types/src/record-components.ts — RecordHighlightsComponentProps.fields[]'s object arm: { name; label?; icon?; type?; readonly? } → { name; label?; type?; readonly? }. The key is removed, not tombstoned: the contract's arm is $strict, so the refusal an author needs already exists upstream and arrives named (invalid_union at the entry). A ?: never tombstone buys nothing here — it is the remedy for a non-strict mirror that would otherwise strip in silence, which is not this arm. - packages/plugin-detail/src/renderers/record-highlights.tsx — the entry normalizer stops copying icon: f?.icon into the normalized entry. That read was unreachable, not merely unused: no author could feed it past the $strict arm, and HeaderHighlight renders no .icon on the far side either, so the copy had no consumer in either direction. - packages/plugin-detail/src/index.tsx — the registry manifest's fields input description sketched the entry as {name,label?,icon?,type?,readonly?} → {name,label?,type?,readonly?}. The inputs ARE the published contract (gen-manifest.ts serializes them into sdui.manifest.json and sdui-intrinsics.d.ts), so leaving the sketch standing would have gone on teaching AI and human authors a key that gets the whole document refused at publish.

.changeset/calendar-view-schema-converge.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Runtime renderer behaviour is unchanged. @object-ui/plugin-calendar's README and content/docs/api/schema-reference.md are repaired to the converged surface in the same change, so no copy of the old contradiction survives.

.changeset/issue-5373-retire-crud-schema.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    crud had four declaration faces and no registered renderer, for the whole life of the key: the TS interface (packages/types/src/crud.ts), the zod mirror (packages/types/src/zod/crud.zod.ts), a dedicated branch in validateSchema that affirmatively PASSED it, and CRUDBuilder in @object-ui/core. A node spelling it painted the OBJUI-001 "Unknown component type" panel, and content/docs/api/schema-reference.md published it as reference material — so a reader (or an AI author) who copied the page got a red panel.

  • names api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Authoring crud is now REFUSED BY NAME rather than passed or silently ignored. validateSchema returns an error with code: 'RETIRED_TYPE' on schema.type — at any depth, since it is what validateChildren recurses with — so assertValidSchema throws and isValidSchema answers false. The message names the migration: object-grid for the record table with its toolbar, filters, pagination and row/batch actions, object-form for the create/edit form, and detail for the record view. api/schema-reference.md is rewritten around those shapes.

.changeset/object-view-unmirrored-keys-7779.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    listViews stays unmirrored, on the ruling's own fallback clause. The declaration's value is the local NamedListView — 64 declared top-level members (⚠️ re-taken at objectui#8980, which added the seventeen the protocol declares on this surface to the 47 this entry first measured), of which the renderer reads 21 off a named view. data is one of the 21 now: it used to reach the renderer through an as any cast on the named-view config in packages/plugin-view/src/ObjectView.tsx and be declared nowhere, and the objectui#8980 ruling declared it by name — objectui#7928's open half, answered. The spec's ViewSchema.listViews is a record of the STRICT ObjectListViewSchema, which requires columns and refuses options, ObjectQL tuple filters and default — that is, it refused the named views this package's own README and content/docs/api/schema-reference.md taught when this entry was written ({ label: 'All Users' } fails at columns; filter: [["owner", "=", "..."]] fails at filter.0), and objectui#8255 has since rewritten them in the spec shape. Mirroring the spec value would have lost documented behaviour; mirroring the local value would enforce 43 unread members (64 declared, minus the 21 that are both declared and read) into the contract — the very thing ruling B refused for the six local keys. The key therefore stays in the parity ledger with that measurement, pinned, until the maintainer decides its value type. It is not papered over with z.any().

.changeset/record-alert-cta-label-i18n-4998.md

.changeset/table-renderer-declared-column-contract-5350.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    What makes this site different from its three siblings is that the alias was not merely tolerated, it was published. content/docs/api/schema-reference.md §TableSchema shipped a copyable { "name": "id", "label": "#" } example and a property row reading "Column definitions with name, label, …", while packages/types declared the opposite pair. Docs and type disagreed about one type they both call TableColumn, each internally consistent. Retiring the alias without correcting the page would have turned a documented, working example into a silently broken one, so both halves land together: the page now authors accessorKey/header. The same row also advertised a render property that TableColumn has never declared — the renderer's hook is cell — and that claim is dropped rather than re-spelled.

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.

Compared the checked-out tree with 88fbd793d (merge-base with origin/main): 11 file(s) changed outside .changeset/, read against 1749 pending declaration(s) that publish a body (2353 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3109.4 KB 3149.4 KB
Main entry chunk (gzip) 149.3 KB 350 KB
Entry file index-D36H7Ls8.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.88KB 6.25KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
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.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
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) 558.81KB 133.93KB
core (index.js) 9.94KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 227.99KB 63.22KB
fields (index.js) 261.11KB 66.33KB
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 (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.35KB 9.18KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 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.58KB 4.90KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.25KB 2.17KB
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) 52.17KB 15.06KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 138.54KB 37.02KB
plugin-designer (index.js) 215.78KB 44.42KB
plugin-detail (index.js) 234.44KB 62.20KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 169.69KB 43.37KB
plugin-gantt (index.js) 171.88KB 42.61KB
plugin-grid (index.js) 228.99KB 62.89KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.92KB 28.65KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 31.07KB 9.15KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 90.43KB 22.76KB
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.81KB 3.58KB
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) 119.55KB 39.23KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
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) 8.61KB 3.45KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.17KB 2.73KB
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.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 22.61KB 7.40KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
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.26KB
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) 2.52KB 1.31KB
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) 19.30KB 6.99KB
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

Brings main's modalCloseButton row in schema-reference.md (objectui#11091),
which merged cleanly beside this branch's Spec alignment hunk.

Claude-Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec
Co-authored-by: Claude <noreply@anthropic.com>
…spec row without a version literal (objectui#10993)

doc-version-claims refused `@objectstack/spec` 17.4.0 on a scanned doc
surface. The sentence now points at the installed `ComponentPropsMap
['object-form']` row, so nothing in it can go stale.

Claude-Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3109.4 KB 3149.4 KB
Main entry chunk (gzip) 149.3 KB 350 KB
Entry file index-w5yZloWK.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.88KB 6.25KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
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.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
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) 558.81KB 133.93KB
core (index.js) 9.94KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 227.99KB 63.22KB
fields (index.js) 261.11KB 66.33KB
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 (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.35KB 9.18KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 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.58KB 4.90KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.25KB 2.17KB
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) 52.17KB 15.06KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 138.54KB 37.02KB
plugin-designer (index.js) 215.78KB 44.42KB
plugin-detail (index.js) 234.44KB 62.20KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 169.69KB 43.37KB
plugin-gantt (index.js) 171.88KB 42.61KB
plugin-grid (index.js) 228.99KB 62.89KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.92KB 28.65KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 31.07KB 9.15KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 90.43KB 22.76KB
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.81KB 3.58KB
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) 119.55KB 39.23KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
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) 8.61KB 3.45KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.17KB 2.73KB
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.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 22.61KB 7.40KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
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.26KB
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) 2.52KB 1.31KB
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) 19.30KB 6.99KB
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: f8f67e7140fbd1d90a9bcd8e5d04ade8b08bf8bb
Local-runs: none

PR objectui#11099, Part of card objectui#10993, batch 2. Scope judged: release 5885529746 ("What the next batch carries") as narrowed by claim 5888090052 — the record:path stages[].label read, the registry half (object-metric label / title / description, object-grid.label, record:related_list.title), the docs face; not object-grid's title caption in ObjectGrid.tsx, not the never-read members. Inputs: the card body and its ten comments (the batch-2 reports 5890598112 and 5890847567 included), the PR body, its 12-file list, the net diff against main at this head (compare: ahead 6, behind 0, merge-base 88fbd793d, 12 files, +760 / -21), and the head's check-runs. Spec rows were read at the @objectstack/spec@17.4.0 tag, the version the head's lockfile installs. Nothing was built, run or re-run; every "green" below is a check-run conclusion.

① Derived judgments

  1. record:path stages[].label — the raw read this batch repairs: RIGHT. The spec row types the member I18nLabelSchema. At the head localizeStageLabels resolves only the object arm, with pickLocalized against useObjectTranslation().language, in a memo that runs before translateOptions (the picklist translation still outranks the authored label, as for a string), before classify() (which probes value + label against LOST_TOKENS / WON_TOKENS), and before both rails' text node and the composed aria-label from stageAriaLabel. A string or any non-object value is handed on as authored; an array with no map is returned as-is. Read end to end at the head: no further raw read of the member remains. Caveat, not a finding: the won/lost heuristic now probes the entry chosen for the viewer's language, so a map whose entries carry a terminal token in one language only classifies per viewer; terminal is the authoritative override and the heuristic was already language-dependent for authored strings.

    • The memo chain (rawStages memoised, stages memoised on it) is consistent with objectui AGENTS.md commandment 10: both memos are pure, so a discarded cache recomputes the same content and no correctness rests on identity. The react-hooks/preserve-manual-memoization warning the dev reports sits on the pre-existing stages memo.
  2. The locale-source deviation: RIGHT, and the consistent choice. The dispatch suggested the rails' resolver (resolveInlineI18nLabel against useDisplayLocale(), which recordComponentAria.ts uses for an authored aria.ariaLabel); the dev resolved the stage label with pickLocalized against the UI language. Readings at the head: every other visible record:* label in plugin-detail resolves that way — record:related_list (pickLocalized(schema.title, language), pickLocalized(schema.add.label, language)), record:alert (title, body, action.label), record:details (section and field labels) — and so do MetricWidget (label, description) and ObjectMetricWidget (title), the exemplar the card body and triage 5872631146 name for exactly this source. The two resolvers agree limb for limb (exact tag, base language, region-qualified sibling, default, en, first string entry; own-property and string guards on both), differing only in how a miss is spelled, so the choice is purely the locale channel; useDisplayLocale() is locale || language || 'en', so the channels diverge only when a tenant has configured a regional display locale. The composed stage name is built by t() in the UI language, so the UI-language label keeps one name in one language. Observation for the seat, not against this PR: the family keeps two channels across packages — ObjectGrid resolves label against the display locale, plugin-detail and plugin-dashboard against the UI language — and each head registration description states its block's channel truthfully.

  3. The registry half — five inputs widen the accept set: RIGHT. object-metric label / description / title (plugin-dashboard), object-grid.label (GRID_QUERY_INPUTS, plugin-grid) and record:related_list.title (plugin-detail) go from 'string' to ['string', 'object']. Each arm is backed by a render site that resolves the map, the order the ComponentInput.type docblock prescribes (teach the render site, then declare the arm): the plugin-detail and plugin-dashboard sites above, and ObjectGrid's three resolveInlineI18nLabel(schema.label, displayLocale) reads (caption, export viewLabel, detail-overlay label). The spec rows agree: ObjectMetricPropsSchema label / description / title, ObjectGridPropsSchema label and RecordRelatedListProps title are all I18nLabelSchema at 17.4.0. A number still matches neither arm (the manifest pin's control row). ObjectGrid's fourth label read, columnDiagnosticLabel, feeds describeAddress, which keeps the label only when typeof label === 'string'; a map is dropped from the diagnostic text, not printed.

  4. view:grid carries the same two arms: RIGHT by construction. The head registers view:grid with inputs: GRID_QUERY_INPUTS.map(...) on the same ObjectGridRenderer, so the arm rests on the same resolving render site. The manifest pin has no view:grid row (not in getPublicConfigs()), and its reachability row keeps the three public blocks from passing vacuously.

  5. The held-back site is honoured: RIGHT. ObjectGrid.tsx is not in the file list; object-grid.title stays undeclared for the map, its caption fallback (caption: ... || schema.title) still reads a map raw, the grid pin authors no title, and the new MEMBER_PINS row and the changeset both say so. The nine never-read members and ai:chat_window are untouched.

  6. The docs face: RIGHT, and the reclassification is true by measurement. At the 17.4.0 tag all fourteen keys the new "Component-contract keys" bullet lists are top-level members of ObjectFormPropsSchema (showSubmit, submitText, showCancel, cancelText, showReset, nextText, prevText, successMessage, navigateOnSuccess, resetOnSuccess, modalCloseButton, initialValues, fields, customFields); the five named I18nLabel are I18nLabelSchema there; className is absent from that row while PageComponentSchema declares className: z.string().optional(), so the "Envelope key" class is right. The new ObjectGridSchema label row matches the TS face (label?: string | I18nLabel in packages/types' objectql.ts) and the renderer's channel, and the "read only when label is absent" clause matches || schema.title. The bullet says nothing re-derives its list — commandment 9 kept.

  7. Tests — the right shape for the claim's pins. Five new files mount the { type, properties } document through the real SchemaRenderer and registry under zh and en, every map en-first so an en or first-entry fallback fails the zh row, each with a plain-string control; the record:path pin adds the classification row and the accessible-name rows; the manifest pin carries reachability, arm, map, string and number rows per key. MEMBER_PINS gains the five rows and the record:path.stages row names the new pin; each new pin file names its block and key, which the parity gate's locator requires. The red-on-base numbers in the report are the dev's; the verdicts are the check-runs below.

  8. No other public surface moves. No export, type or zod change; packages/types untouched (see ③ item 4); ObjectGrid.tsx untouched.

  9. The merge and the round note: RIGHT. 0ac3ee656 merges origin/main 88fbd793d, which is the merge-base (branch behind 0), and the net diff is exactly the twelve claimed files with no stray path. PR objectui#11091's modalCloseButton row (merged as 559a2e207, an ancestor of the merge-base) sits in the ObjectFormSchema table two lines above the rewritten bullet at the head — both survive; the PR reads mergeable: true, mergeable_state: blocked (checks and draft, not dirty). The version literal that reddened a840a1094 on Test (shard 8/8) (doc-version-claims.test.ts) is gone: the bullet now names the installed row without a version, no KNOWN_CLAIMS entry was added, and no added line on a scanned surface (content/docs, packages/*/README.md, skills) carries a version-shaped token; Test (shard 8/8) is green on this head. Each of the six branch commits ends with the model-free trailer pair.

② Semver level

.changeset/10993-batch2-i18nlabel-reads.md: @object-ui/plugin-detail minor, @object-ui/plugin-dashboard minor, @object-ui/plugin-grid minor. MATCHES what the diff publishes: each package's registration widens the accept set validateTree applies through getPublicConfigs() and manifestFromConfigs (a locale map now passes where it was type-mismatch), which is a yes widening and takes at least minor under the Post-Task Checklist rule; plugin-detail also carries the record:path repair. Nothing accepted before is refused; no major. apps/console changes are test files only and content/docs is not a package; the fixed group carries the rest. Changeset Declaration, Changeset Bump Policy, Changeset Claim Re-read, Changeset Fixed Group Check and Changeset Overwrite Report are success on the head. The changeset body carries the clause line and no version literal.

Clause-②: yes — five published registry inputs widen from 'string' to ['string', 'object'] (and view:grid's shared label with them); the PR body's line, the claim's line and the changeset agree.

③ Boundary flags

Both batch-2 reports carry open_questions: []. Every deviation and out-of-scope finding, answered or escalated:

  1. Locale source (pickLocalized against the UI language, not useDisplayLocale()): ANSWERED in ① item 2 — accepted as the consistent choice and the card's direction.
  2. Docs reclassified by measurement (fourteen of fifteen keys; className an envelope key) where the order named four, plus the ObjectGridSchema label row: ANSWERED in ① item 6 — measured true against spec 17.4.0; rewriting the bullet while leaving nine knowingly false entries would have been the wrong edit. PR objectui#11091's row and the bullet coexist at the head; no conflict.
  3. Cross-file line addresses in plugin-detail/src/index.tsx re-addressed by content (objectui AGENTS.md commandment 11, as the seat applied it to batch 1 in 5884511871): ANSWERED — comment text only, and every replacement was re-read at the head and resolves: add.picker.valueField || 'id' and add?.picker?.labelField || 'name' (one hit each in RelatedList.tsx, after the merge that brought PR objectui#11089's edits to that file), schema.relationshipValueField || 'id' and the schema.add forward (record-related-list.tsx), visibleFields.length === 0 && emptyCount === section.fields.length (DetailSection.tsx), feed: RecordActivityProps.optional() (spec component.zod.ts), and applyFeedConfig(sourceItems, { types, showCompleted, unifiedTimeline }, ...) (record-activity.tsx). Line Citation Gate is success; the PR adds no line-address citation.
  4. The TS face is narrower than the spec (@object-ui/types record-components.ts: RecordPathComponentProps.stages[].label, RecordRelatedListComponentProps.title and .add.label, RecordDetailsComponentProps.sections[].label typed string): CONFIRMED at the head and ESCALATED to the seat as this card's family member. packages/types is outside claim 5888090052; the renderers resolve at runtime so no public door crashes; a TypeScript author writing the spec's map gets a compile error. Batch 1 widened ObjectFormSchema's TS face on this same card; the record-component props are the remaining half — for the seat to fold into the next batch or the closure note.
  5. Class-b finding — record:path with statusField and no stages: CONFIRMED by reading — the renderer builds rawStages from schema.stages alone and returns the "no stages configured" placeholder when it is empty, with no object-schema or picklist read, while the registration's stages description ("else derived from field metadata") and the spec's describe ("if not using field metadata") both promise derivation. Declared, not enforced: implement the derivation or trim both descriptions, a decision with its own card. ESCALATED to the seat to file. Leaving the description untouched in a file this PR edits is right, because trimming it is the other half of that decision.
  6. registry-inputs-spec-parity.test.ts keeps 13 lines of cross-file line addresses (carrier: none): ESCALATED to the seat. Ruling 5884511871 states commandment 11's general form ("when you touch a file anyway, fix the cross-file line addresses inside it") and applied it to two published descriptions; this PR touches the parity test (five rows added, one edited) and adds no new address, but re-addresses none of the 13 existing ones, on the same role-file scope reasoning the ruling overruled. Whether the ruling reaches test prose, and in which batch (the next one touches this file for the object-grid.title row), is the seat's to say. Not a correctness defect in this diff.
  7. view:grid widened through the shared inputs; the record:path stages description left as is: ANSWERED in ① item 4 and ③ item 5.

Check-runs on the head, filter=latest, read at 2026-09-29T13:15Z: 43 completed, 40 success, 3 skipped (Test (coverage), the coverage shard matrix entry, dependabot — path-filter skips), 0 failure, 0 in progress. Among the successes: Type Check, Lint, all eight Test shards and the Test roll-up, Test (dist pins), Build & E2E, Build Docs, Bundle Analysis, Doc Snippet Type Check, Doc Component Type Check, Line Citation Gate, Spec Main Shape Gate, Governed Surface Queue Guard (none of the twelve paths is a governed surface), and the five changeset checks named in ②. The PR is a draft with no auto-merge armed, as a dispatched dev's PR should be; ready and landing are the seat's.

Implemented-by: claude/issue-10993-batch2-i18nlabel-reads
Reviewed-by: session_011p7ikEivgXefNDaE5S5Uec

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 29, 2026 13:23
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 29, 2026
Merged via the queue into main with commit 27ae632 Sep 29, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-10993-batch2-i18nlabel-reads branch September 29, 2026 13:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

apps documentation Improvements or additions to documentation plugin tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants