Skip to content

feat(plugin-tree,plugin-map,types): object-tree and object-map are judged and publish the keys their renderers honour (objectui#11168 slice 3) - #11335

Merged
objectstack-fleet[bot] merged 15 commits into
mainfrom
claude/issue-11168-lazy-blocks-form-layout
Oct 1, 2026
Merged

objectstack-fleet[bot] merged 15 commits into
mainfrom
claude/issue-11168-lazy-blocks-form-layout

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Refs #11168

Clause-②: yes (narrowing). It widens: object-tree publishes data, staticData, filter and navigation; object-map publishes mapStyle, navigation and enableClustering; ObjectTreeSchema.objectName goes optional; UIActionSchema.size admits default and icon. It narrows: on ObjectTreeSchema the keys data (now ViewData), tree, navigation and staticData used to ride the node's passthrough unjudged, and each is now judged on both faces. The fixed group ships it as minor.

objectui#11168 slice 3, part 1 of 2. Decision 3 = B of objectui#11111 applies (record 5902351047): every key is decided by its own measurement through the real SchemaRenderer, and only what the renderer honours is declared. Part 2 is the object-form.layout / objectui#7759 Group C narrowing, opened as a separate PR at the PM seat's split boundary.

What this PR takes

Block / booking Outcome
object-tree Loaded into the parity guard and judged. Four keys declared; objectName no longer required; the ObjectTreeSchema mirror aligned on both faces (pointer 5909523766, and the tree DECLARE verdict owed in the 8655 header)
object-map Loaded and judged. Three keys declared
UIActionSchema.size (booking 5923506054 item 1) Takes the action:button row's vocabulary by reference
Calendar pin text (booking item 2) allDayField is a spec key since 17.5.0; the text now says five keys
Timeline navigation description (record 5924396134) Both wording nits corrected, and the same in the docs table

Handed back as a worklist, at a block boundary, to keep this reviewable: object-gantt and object-timeline. They stay booked OWED in UNJUDGED_SPEC_BLOCKS. The object-timeline.navigation MEMBER_PINS line waits for the timeline's slice, because the guard refuses a pin for a block it does not judge. The pin file timelineNavigationMembers-8654.test.tsx is ready.

Measurements (before any edit, on the unchanged renderer)

object-tree (pointer 5909523766):

  • objectui validate / safeValidateSchema on a staticData-only tree with no objectName gave invalid_type at objectName. The spec row accepted the same tree. Through SchemaRenderer it drew all three rows. The page validator raised missing-required-prop (error) on objectName and unknown-prop on staticData.
  • filter narrowed the inline rows.
  • navigation: { mode: 'drawer' } opened the record overlay. With the key absent, nothing opened.
  • All 33 behavioural rows of the new pin passed on the unchanged renderer, and only the 4 registration rows were red.

object-map:

  • mapStyle replaces the demo tiles. (Its precedence against map.style is ruled and changed in round 2; see the section below.)
  • enableClustering: true clusters 2 nearby markers into one cluster. Absent, 2 markers stay 2. false keeps 120 markers unclustered, where absent clusters them.
  • navigation: drawer, modal and popover open the record. new_window and openNewTab call window.open('/store/record/m1', '_blank'). split, none and an absent key open nothing. page and a mode-less block open the record page through the record navigator the host publishes (openRecord('store', 'm1')). They open nothing under a host that publishes no navigator, or on a map that names no objectName (round 4, measured after objectui#11293). none outranks openNewTab.
  • All 29 behavioural rows passed on the unchanged renderer, and only the 2 registration rows were red.

UIActionSchema.size: a group MEMBER and an action:button draw identical classes for each size: default / md give h-10 px-4, sm gives h-9 px-3, lg gives h-11 px-8, and icon gives h-10 w-10. The old type refused default and icon (TS2322).

The ObjectTreeSchema mirror (rides here, by measurement)

The pointer's measurement is a public-door reach. It is in the claimed file surface and the same family, so the mirror moves in this PR:

  • objectName is optional, behind the record-source refinement the map and gantt mirrors carry.
  • data is typed ViewData.
  • staticData is declared.
  • tree and navigation take the spec's TreeConfigSchema / NavigationConfigSchema by reference.

⚠️ The node's dataSource binding is not counted as a record source here. The tree's registration is not gate-wrapped, so a binding reaches nothing. requireRecordSource gained a binding: 'not-read' option for exactly this, and its message then names only the three rungs. ObjectTree now reads navigation without a cast. The ledgers that re-derive this moved with it: SPEC_DERIVED_PAIRS +1 and the imported-defaults IMPORTED list +1. The 9550 barrel pin row for objectName is string | undefined. The 8655 typed-schema pin's two "stays undeclared" rows flipped to declared, as its own 17.5.0 note predicted.

Ledger arithmetic, by census of the edited guard

Figure 95bd23c90 this PR
unjudgedBlocks cap 4 2 (object-gantt, object-timeline)
offSpecInputs / unpublishedKeys / refusedArms / memberPins 0 / 3 / 1 / 0 unchanged
owner count objectui#11168 8 6
census judged / propless / ledgered 35 / 7 / 13 37 / 7 / 11 (sum 55)
MEMBER_PIN_EXEMPTION_CEILING 0 0 (all 12 new structured inputs answered with pins)

Member pins (12, registered in MEMBER_PINS)

  • object-tree: tree, data, staticData, filter, navigation. All are in one new file, packages/plugin-tree/src/__tests__/objectTreeInputs-11168.test.tsx.
  • object-map: map, data, staticData, filter, sort, navigation are in one new file, packages/plugin-map/src/__tests__/objectMapInputs-11168.test.tsx. dataSource PROMOTES ObjectMap.elementDataSource.test.tsx, after reading it end to end (binding object, view filter/sort, AND-ing, the unresolvable view, the unmapped limit, ascending default).

Ablations (predictions written first; every leg restored, blob == HEAD, git diff HEAD empty)

Leg Mutation Predicted Observed
A delete the tree's navigation input 4 tree-pin + 2 guard rows red exactly those 6; 276 green
B ObjectTreeSchema.objectName required again 3 authoring-door rows red exactly those 3; 39 green
C ObjectMap hands navigation: undefined to the hook 5 navigation rows red exactly those 5; 27 green
D UIActionSchema.size back to 'sm' / 'md' / 'lg' tsc: TS2344 + TS2322 x2 in the size pin exactly those 3 errors, exit 2
G (round 2) getMapConfig block path back to config.style || style only "both written: mapStyle draws" red exactly that 1; 34 green

Vitest aliases @object-ui/* to source, so no dist sits between mutation and assertion for A to C. D is a tsc leg over source.

Gates (at 17f1664ec unless noted)

  • Union, test runs:
    • packages/types: 308 files, 7758 tests passed.
    • plugin-tree + plugin-map + plugin-timeline + the guard + components/src/renderers/action + every outside test reader of object-tree, object-map or ObjectTreeSchema (core, list, view, react, sdui-parser, cli, schema-catalog, scripts): 158 files, 2852 passed, 34 skipped (pre-existing dateOnlyZone skips).
  • type-check: types, plugin-tree, plugin-map, plugin-timeline, components and console all exit 0, with the script name echoed. Each ran after building its dependency closure (console: turbo build --filter=@object-ui/console^..., 34 tasks).
  • check gates, all exit 0:
    • spec-symbols, designer-field-key-parity, component-surface-parity
    • phantom-deps (plugin-tree now declares its @object-ui/sdui-parser devDependency), unused-deps, self-import, esm-specifiers
    • new-line-citations (0 new), control-bytes, installed-pin-claims, i18n-keys
    • doc-types, registry-bare-names, prompt-keys, doc-fences, doc-example-ids
    • element-data-source-declaration, comment-mask-corpus, lockfile-integrity, lockfile-dedupe
    • test-path-roots, vi-mock-specifiers / inherit / override-shape, shell-escape-residue
    • unreferenced-sources, doc-example-readers, handler-key-reads, action-forward-parity
    • doc-snippets (698 of 698 blocks judged), doc-examples, readme-exports (after the scoped doc build)
    • changeset-claims, pending-changeset-literals, type-check:coverage, lint:coverage
    • check-changeset-presence / no-major / fixed / overwrite (report: 1 modified, the dated note below)
  • check-governed-queue-guard --test on the 28 changed paths: NOT GOVERNED. The control AGENTS.md answers exit 3.
  • Lint (a declared narrowing): eslint --format json over the 16 touched TS files gives 0 errors. --print-config shows 118 rules in effect, and parserOptions.project / projectService are null, so the linting is not type-aware and untouched files cannot move. One added line carries a no-explicit-any warning: ObjectTreeSchema.staticData?: any[], spelled as all five sibling mirrors spell it, paired with z.array(z.any()).
  • NOT MEASURED:
    • Spec Main Shape Gate. Reason: it builds @objectstack/spec from objectstack main and injects it into the install. The gate is red repo-wide on objectui#11330 (the PM seat's heads-up), and this diff touches none of its five sites. A source read of objectstack main (dff98c1f) shows the same action:button size vocabulary and the same TreeConfigSchema the new type pins rely on.
    • check-spec-range-floors and check:sdui-registration-pins. Reason: no package.json floor or sideEffects array moved.

Changesets

  • New: 11168-object-tree-inputs (plugin-tree minor), 11168-types-object-tree-mirror (types minor), 11168-object-map-inputs (plugin-map minor), 11168-ui-action-size (types minor), 11168-timeline-navigation-wording (plugin-timeline patch) and, in round 2, 11168-map-style-precedence (plugin-map patch).
  • 8655-object-tree-props-schema-typed.md ships in the same release and says navigation / tree stay undeclared. This slice falsifies that, so the file gets a dated, append-only note; its frontmatter is byte-identical.

object-map.mapStyle precedence: ruled A, the renderer follows the spec (round 2)

The installed 17.5.0 describe reads 「MapLibre style URL or spec, overriding the public demo tiles. Read before map.style; NOT the base node style, which is an inline CSS record」. The renderer read a style inside the map block first (getMapConfig returned style: config.style || style), so a map writing both drew map.style. The seat ruled option A (record 5926309662, under 协议为基准).

  • getMapConfig's declared-block path now returns the same mapStyle || map.style the flat path does.
  • The registration's mapStyle description is the spec's describe verbatim, and a pin row holds it equal to the installed row's.
  • Pinned both ways through the real SchemaRenderer: both written ⇒ mapStyle draws; only map.style ⇒ it draws; only mapStyle ⇒ it draws; neither ⇒ the demo tiles (the control).
  • Ablation G (prediction written first): reverting the one expression turned exactly "both written" red, with 34 green, and the file was restored to the HEAD blob.
  • Census: outside tests, mapStyle is written only by the ListView / ObjectView flatteners, on the path with no map block. objectstack carries it only in the spec and its docs. So no authored producer writes both, and no stored document changes what it draws.
  • Changeset 11168-map-style-precedence (plugin-map patch) states the behaviour change. README, plugin-map.mdx and the guard's object-map.map pin claim now teach the spec's order.

Round 2 head 94db25d50, on merge 289df609b (origin/main 770cc5ba4 merged in, clean, picking up the Spec Main Shape Gate repair 47e3ce008). Re-run there:

  • type-check types, plugin-map and console (exit 0, after turbo build --filter=@object-ui/console^...);
  • packages/plugin-map + the guard: 38 files, 488 passed;
  • changeset presence / no-major / overwrite, control-bytes, new-line-citations, changeset-claims, pending-changeset-literals, doc-fences, designer-field-key-parity and component-surface-parity, all exit 0;
  • lint: 0 findings on round-2 lines.

The round-1 union figures above are at 17f1664ec.

Acceptance notes (noted, not filed)

  • The tree's flat parentField / labelField / fields / defaultExpandedDepth are still declared on ObjectTreeSchema and still outrank the tree block's members, while the 17.5.0 row refuses them flat. That is the host-composed handoff. Retiring them is a separate decision, so the README now teaches the tree block. Carrier: 承接者:无.
  • The object-kanban / object-calendar navigation descriptions (objectui#8652, landed) carry the same "openNewTab outranks the mode" wording, which is untrue for none. They are outside this claim. Carrier: 承接者:无.

Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT (dispatched by the domain:spec @ objectui seat).


Generated by Claude Code

claude added 4 commits October 1, 2026 04:43
…s its renderer honours, and ObjectTreeSchema mirrors the 17.5.0 row (objectui#11168 slice 3)

The repo-wide registry parity guard now loads `@object-ui/plugin-tree`, so
`object-tree` leaves `UNJUDGED_SPEC_BLOCKS` (where the 17.5.0 bump booked it
OWED) and is judged in both directions. Every key was measured through the
real SchemaRenderer and this package's registration first (objectui#11111
decision 3 = B):

- `data`, `staticData`, `filter` and `navigation` are honoured, so the
  registration declares them on both tags; `objectName` is no longer
  required (a tree on inline rows never reads it, and the page validator
  raised missing-required-prop on a tree that draws).
- `ObjectTreeSchema` (TS and zod) gains `data`, `staticData`, `tree` and
  `navigation` (the last two the spec's own schemas, by reference), and
  `objectName` goes optional behind the record-source refinement. The
  pointer's measurement: `objectui validate` refused a staticData-only tree
  with invalid_type at objectName. The binding does not count on this block:
  its registration is not gate-wrapped.
- Member pins for tree / data / staticData / filter / navigation in one file,
  registered in MEMBER_PINS. unjudgedBlocks 4 -> 3, owner 8 -> 7, census
  judged 35 -> 36 / ledgered 13 -> 12.

Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT
Co-authored-by: Claude <noreply@anthropic.com>
…bleClustering are published, and every structured key is member-pinned (objectui#11168 slice 3)

The registry parity guard now loads `@object-ui/plugin-map`, so `object-map`
leaves `UNJUDGED_SPEC_BLOCKS` and is judged in both directions. The judgment
found three spec keys the registration did not publish; each was measured
through the real SchemaRenderer first and all three are honoured, so they are
declared (objectui#11111 decision 3 = B). Descriptions state what the renderer
does, including where it departs from the spec row's prose: a `style` inside
the `map` block wins over `mapStyle` (reported, not resolved here).

Member pins: map / data / staticData / filter / sort / navigation in one new
file; dataSource promotes ObjectMap.elementDataSource.test.tsx after reading
it end to end. unjudgedBlocks 3 -> 2, owner 7 -> 6, census judged 36 -> 37 /
ledgered 12 -> 11.

Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT
Co-authored-by: Claude <noreply@anthropic.com>
…UIActionSchema.size, the calendar pin text, the timeline navigation wording (objectui#11168 slice 3)

- `UIActionSchema.size` takes the `action:button` row's vocabulary by
  reference (`ActionButtonProps['size']`): `default` and `icon` were TS2322
  while the row, the registration and the Button primitive all take them.
  Measured through the real SchemaRenderer: a group MEMBER draws each of the
  five sizes exactly as an action:button does. Pinned in
  ui-action-size-11168.test.ts.
- The guard's `object-calendar.calendar` pin text called `allDayField`
  objectui-local and the spec block four keys; 17.5.0 declares all five.
- The timeline `navigation` description (record 5924396134's two nits):
  `split`'s reason no longer attaches to `page` / `none`, and `openNewTab`
  outranks every mode EXCEPT `none`, which is read first. The docs table
  says the same. The map description written in this slice had copied the
  first nit; corrected with it.

Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT
Co-authored-by: Claude <noreply@anthropic.com>
…is slice falsifies; type the map pin without any (objectui#11168 slice 3)

- `.changeset/8655-object-tree-props-schema-typed.md` ships in the same
  release and says `navigation` stays undeclared with its cast, and that
  declaring `tree` on the node would fork. objectui#11168 slice 3 declared
  both by reference to the 17.5.0 row, so the entry gets a dated,
  append-only note; its frontmatter is byte-identical.
- The new map pin used `any` in three places; they are `unknown` now
  (lint: 0 warnings on the file).

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

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 57 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/10393-retire-bare-map-key.md

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

    The @object-ui/plugin-map README now describes one registered type, and its sentence claiming a bare array under data reaches the in-memory adapter is corrected: a bare array under data is not a record source on the map (objectui#8348), so inline rows belong under staticData. The same false claim is corrected on the two docs pages that carried it, plugins/plugin-map.mdx and fields/location.mdx.

.changeset/10872-container-children-channel.md

  • names objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    Also in this change, with no behaviour change: objectql.zod.ts's two public-block arms (object-metric, object-master-detail-form) build their properties member with the same propsBag helper as the other public-block arms, instead of a byte copy of it. The member's description text is unchanged.

.changeset/5793-spec-range-floors.md

  • names pnpm-lock.yaml → pnpm-lock.yaml — edited by this change

    Nothing a consumer installs today changes: normal resolution already picks the newest 17.x, and pnpm-lock.yaml still resolves 17.2.0 on this edge after the bump. The change is to the declared floor only, which is why it is scored patch rather than minor — the same reasoning objectui#5753 used for the other direction on this dependency.

.changeset/5903-objectgantt-declared-keys.md

  • names packages/types/src/objectql.ts → packages/types/src/objectql.ts — edited by this change

    Both halves move together. The TS declaration (packages/types/src/objectql.ts) and its zod mirror (src/zod/objectql.zod.ts) gain the same ten keys at the same requiredness — all optional — and no KnownDrift entry is added. navigation is taken from @objectstack/spec's NavigationConfigSchema by reference rather than restated, matching ObjectGridSchema.navigation.

  • names src/zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    Both halves move together. The TS declaration (packages/types/src/objectql.ts) and its zod mirror (src/zod/objectql.zod.ts) gain the same ten keys at the same requiredness — all optional — and no KnownDrift entry is added. navigation is taken from @objectstack/spec's NavigationConfigSchema by reference rather than restated, matching ObjectGridSchema.navigation.

  • names packages/types/src/__tests__/zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    ⚠️ The JUSTIFICATION for that parity clause was retired (objectui#9743); the clause itself stands and did not move. As first written it credited the zod-mirror-parity ratchet with a zero-drift reading for this pair — but at that time the ratchet measured three directions and was structurally blind to the MIRRORED-but-undeclared one (objectui#9711), so a zero from it recorded that it had not looked in that direction, not that nothing was there. objectui#9725 landed the fourth direction, and it covers this pair BY NAME: packages/types/src/__tests__/zod-mirror-parity.test.ts registers objectql.zod.ts#ObjectGanttSchema in both its mirror map and its declaration map; assertionMirroredUndeclaredMatchesLedger requires every registered pair's mirrored-but-undeclared key set to equal that pair's MirroredUndeclared ledger entry — never for a pair the ledger does not name — and assertionNoVacuousMirroredUndeclaredMeasurement refuses a measurement that has degenerated to any. ⛔ Read this pair's verdict off that reconciliation, which re-derives it on every run, rather than off any figure written here; when this paragraph was authored, on 2026-09-18, it required no MirroredUndeclared entry for the pair.

.changeset/5928-classname-style-props-rename.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    Where the non-pair is recorded now. zod-mirror-parity.test.ts keys its existing EXCLUSIONS entry — the mechanism that accounts for every exported const with no TypeScript declaration to mirror, each with its stated reason — to ClassNameStylePropsSchema. Named for its own two keys, the const leaves no like-named declaration for a name-derived pairing to reach for.

.changeset/6051-gantt-flat-config-declared-keys.md

  • names packages/types/src/__tests__/zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    ⚠️ The JUSTIFICATION for that parity clause was retired (objectui#9743) — the same retirement objectui#5903's entry carries, for the same clause and the same reason; the clause itself stands and did not move. As first written it credited the zod-mirror-parity ratchet with a zero-drift reading for this pair, at a time when that ratchet measured three directions and was structurally blind to the MIRRORED-but-undeclared one (objectui#9711), so a zero from it recorded that it had not looked in that direction. objectui#9725 landed the fourth direction, and it covers this pair BY NAME: packages/types/src/__tests__/zod-mirror-parity.test.ts registers objectql.zod.ts#ObjectGanttSchema in both its mirror map and its declaration map; assertionMirroredUndeclaredMatchesLedger reconciles every registered pair's mirrored-but-undeclared key set against that pair's MirroredUndeclared ledger entry — never for a pair the ledger does not name — and assertionNoVacuousMirroredUndeclaredMeasurement refuses a measurement that has degenerated to any. ⛔ Read this pair's verdict off that reconciliation, which re-derives it on every run, rather than off any figure written here; when this paragraph was authored, on 2026-09-18, it required no MirroredUndeclared entry for the pair.

.changeset/6150-undeclared-but-consumed-keys.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    ⭐ AMENDED, and the amendment ships in this same release. The TreeViewSchema slice (604476d97) gave the key a zod arm after all — a NAMED REFUSAL (handlerKeyRefusal(key, 'runtime-slot', label)), never a shape — because "no mirror entry" is not neutral under BaseSchema.passthrough(): it meant an authored { "type": "tree-view", "onNodeClick": { "action": "toast" } } parsed GREEN, survived the parse, and reached a call site that expects a function. ⇒ the three clauses this bullet used to carry are no longer true of the code shipping beside it. The key is now a MEMBER of TreeViewSchema.shape and an authored value is refused BY NAME at path onNodeClick; it has LEFT zod-mirror-parity.test.ts's RuntimeOnlyDeclared for that file's KnownDrift; and it is no longer "the first pair to sit there without also sitting in UnmirroredDeclared" — draining it emptied that difference, so RuntimeOnlyDeclared is now a SUBSET of UnmirroredDeclared and the union of the two equals UnmirroredDeclared itself. ⛔ objectui#6152's ruling is untouched by any of this: what the key still does not have, and never will, is a z.function() shape — no serialized document could satisfy one.

.changeset/6152-object-form-unmirrored-members.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    ObjectFormSchema in @object-ui/types (objectql.ts) declared a set of members that its zod mirror in @object-ui/types/zod had never heard of. Every one of them is read by the object-form renderer (ObjectForm in @object-ui/plugin-form). The two published faces answered differently:

.changeset/6175-column-state-persistence.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    Nothing is retired. Both spellings remain declared on DataTableSchema; onColumnReorder stays declared and stays unwired, exactly as the RuntimeOnlyDeclared ledger in zod-mirror-parity.test.ts records it. Which of the two survives is a declared-surface ruling that stays open and is deliberately not settled here.

.changeset/6310-designer-formula-key-retired.md

  • names packages/app-shell/src/views/metadata-admin/i18n.ts → packages/app-shell/src/views/metadata-admin/i18n.ts — edited by this change

    Not touched: designer.field.formula ('Formula (CEL)') in packages/app-shell/src/views/metadata-admin/i18n.ts, a different and live key belonging to metadata-admin's ObjectFieldInspector — the surface that still authors formula expressions.

.changeset/6349-types-internal-name-collisions-batch-1.md

  • names ui-action.ts → packages/types/src/ui-action.ts — edited by this change

    ActionSchema — renamed, because the two shapes are unrelated. crud.ts and ui-action.ts both declared it. Measured member-by-member they share 9 keys out of 28 each: crud.ts extends BaseSchema and pins type: 'action' (a UI node — a button in a component tree), ui-action.ts extends nothing and types type as ActionType (a spec-v2 action definition, with name, locations, params, target). Re-pointing either at the other would silently hand a consumer a different type, so this took the rename branch (objectui#5044 is the precedent for choosing the surviving name). ui-action.ts's declaration is now spelled UIActionSchema — the name src/index.ts has always PUBLISHED it under, via export type { ActionSchema as UIActionSchema }, which is now a plain re-export. The package's public surface is unchanged: ActionSchema still means crud.ts's legacy shape and UIActionSchema still means ui-action.ts's, exactly as before. Nothing outside ui-action.ts imported the old spelling — there is no ./ui-action subpath in exports, so the old name was never reachable from outside the package.

.changeset/6361-spec-floor-17-2-0.md

  • names pnpm-lock.yaml → pnpm-lock.yaml — edited by this change

    Nothing a consumer installs today changes: normal resolution already picks the newest 17.x, and pnpm-lock.yaml still resolves 17.2.0 on both edges after the bump — only the recorded specifier: moves. No source and no behaviour changes, which is why this is scored patch, on the reasoning 111741454 used for the same remediation on @object-ui/plugin-detail.

.changeset/6639-objectgrid-title-mirrored.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    The gain is the typed refusal: the mirror's .passthrough() base was already admitting any title unexamined, and it now enforces the declared string. zod-mirror-parity.test.ts's UnmirroredDeclared ledger records the key as worked off — the ledger's first shrink by repair (97 + 1 mirrored + 23 reclassified is what the seeded "121" now means).

.changeset/6939-objectql-record-source-refinement.md

  • names plugin-map/src/ObjectMap.tsx → packages/plugin-map/src/ObjectMap.tsx — edited by this change

    Both renderers resolve their records from one of three keys, in this order — getDataConfig in plugin-map/src/ObjectMap.tsx and plugin-gantt/src/ObjectGantt.tsx: data, then staticData, then objectName. Both mirrors required objectName alone, so a document authored on staticData drew correctly and was refused by safeValidateSchema — six catalog entries, three per component.

.changeset/6940-rowactions-boolean-mirror.md

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    The list view's same-named rowActions in zod/objectql.zod.ts — z.array(z.string()), the legacy bare-name action list on ObjectGridSchema — is a different key that is correct as it stands, is in parity with its own TS twin (rowActions?: string[]), and is not touched.

.changeset/7113-chart-data-model.md

  • names objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    .extend() with a NEW key still works and preserves the fold and the refinement; .optional(), z.discriminatedUnion, z.toJSONSchema and safeValidateSchema are all unaffected. Nothing in this repository calls the throwing combinators on either const, and the published surface already ships refined mirrors (objectql.zod.ts, complex.zod.ts, form.zod.ts, app.zod.ts), so the class is not new — but it is a real behaviour change on a published export and it belongs in the release note rather than in a reviewer's file.

.changeset/7122-objectstack-family-17-3-0.md

  • names pnpm-lock.yaml → pnpm-lock.yaml — edited by this change

    @objectstack/client, core, formula and lint each pin @objectstack/spec EXACTLY, so resolving the spec alone to 17.3.0 left the console bundling TWO copies of it. Moving the family with it in pnpm-lock.yaml collapses the duplicate; every declared range already admitted 17.3.0, so no manifest moved.

.changeset/7125-dashboard-empty-state-keys-retired.md

  • names packages/app-shell/src/views/metadata-admin/i18n.ts → packages/app-shell/src/views/metadata-admin/i18n.ts — edited by this change

    Not touched: table.noRows ('No rows to display') and engine.form.noRows (packages/app-shell/src/views/metadata-admin/i18n.ts, read at widgets.tsx) — two different, same-named keys in different namespaces. Nor the comments in WidgetEmptyState.tsx, DatasetWidget.tsx, ObjectDataTable.tsx and PivotTable.tsx that record WHY three widgets with three strings became one shared empty state; the packs' own comment keeps that rationale and now names the retirement instead of a row that is gone.

.changeset/7200-object-form-section-style-keys-undeclared.md

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    The authored-metadata type now agrees with @objectstack/spec, whose FormSectionSchema is a strict object declaring neither key, and with the ruling's rationale (maintainer 2026-09-01, verbatim): "retire the reads … Declaring the keys was weighed and not adopted: it would formally invite free Tailwind strings into authored metadata, the exact class the boundary exists to keep out." A ?: never tombstone was not used: ObjectFormSection has no zod mirror (ObjectFormSchema in zod/objectql.zod.ts does not declare sections), so there is no parse door to refuse at, and a tombstone is still a declaration in completion and in the published .d.ts.

.changeset/7265-types-user-filter-field-derives.md

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    zod/objectql.zod.ts declared two schemas under names @objectstack/spec/ui already exports. They were triaged separately, by reading their sites, and went different ways.

.changeset/7322-object-kanban-group-by-limit.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    Breaking for authored metadata: ObjectKanbanSchema.groupField is RETIRED (objectui#7322, ADR-0049 enforce-or-remove), and the two keys the object-kanban renderer actually reads — groupBy and limit — are now DECLARED and validated on both published faces: the TypeScript interface in objectql.ts and the Zod mirror in zod/objectql.zod.ts.

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    Breaking for authored metadata: ObjectKanbanSchema.groupField is RETIRED (objectui#7322, ADR-0049 enforce-or-remove), and the two keys the object-kanban renderer actually reads — groupBy and limit — are now DECLARED and validated on both published faces: the TypeScript interface in objectql.ts and the Zod mirror in zod/objectql.zod.ts.

.changeset/7344-handler-string-any-mirrors.md

.changeset/7352-drill-down-config-mirror.md

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    DrillDownConfigSchema is the zod mirror of DrillDownConfig, and both declarations that carry drillDown reference it — ChartSchema (zod/data-display.zod.ts) and ObjectDataTableSchema (zod/objectql.zod.ts) — so the published validator under @object-ui/types/zod reads the key for the first time (objectui#7352).

.changeset/7363-objectql-union-arms.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    ObjectGallerySchema and ObjectDataTableSchema are members of ObjectQLComponentSchema on both faces — the TS union in objectql.ts and the zod union in zod/objectql.zod.ts — so AnyComponentSchema, and with it validateSchema / safeValidateSchema / objectui validate, has an arm for object-gallery and object-data-table nodes (objectui#7363).

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    ObjectGallerySchema and ObjectDataTableSchema are members of ObjectQLComponentSchema on both faces — the TS union in objectql.ts and the zod union in zod/objectql.zod.ts — so AnyComponentSchema, and with it validateSchema / safeValidateSchema / objectui validate, has an arm for object-gallery and object-data-table nodes (objectui#7363).

.changeset/7632-shared-record-source-config.md

  • names packages/types/src/objectql.ts → packages/types/src/objectql.ts — edited by this change

    That ladder is published contract on both faces — packages/types/src/objectql.ts and its zod mirror both ship .describe() strings naming getDataConfig's order (77cb489b4, maintainer ruling 2026-09-02), pinned by objectql-record-source-refinement-6939.test.ts — and it was hand-copied into five plugin components with no gate holding them together. A change to the ruled order had five edit sites and nothing that noticed a missed one; that is the AGENTS.md #0.1 drift class.

.changeset/7654-floating-chatbot-trigger-icon-tombstone.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    Every other tombstone in this package pairs ?: never with a retirementTombstone() refusal on the Zod twin. There is no twin here to carry one: FloatingChatbotConfig has no Zod mirror at all, and floatingConfig sits in the UnmirroredDeclared ledger (zod-mirror-parity.test.ts, complex.zod.ts#ChatbotSchema). BaseSchema is .passthrough(), so the whole floatingConfig object rides through unvalidated — before this change and after it. Minting a mirror to host a refusal would be the declared-but-UNMIRRORED axis (objectui#6152), a different defect: a key can be mirrored and inert, or unmirrored and live, and fixing one says nothing about the other. This change does not widen into it.

.changeset/7804-objectql-handler-key-arms.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    The four plain objectql.ts node faces declare the nine handler keys their registered renderers read (the objectql.ts slice): ObjectFormSchema.onCancel / .onError / .onOpenChange / .onStepChange / .onSuccess, ObjectGallerySchema.onCardClick / .onRowClick, ObjectGridSchema.onNavigate and ObjectViewSchema.onNavigate.

.changeset/7804-tree-view-handler-slot.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    'runtime-slot' and not 'retired', measured at this key's own channel. 'retired' publishes "no renderer reads this key, so nothing could ever run it" — true of the two siblings already tombstoned on this arm (onSelectChange, onExpandChange) and flatly false here, since the read is live and INVOKED. ⚠️ No in-repo host builds a tree-view node carrying the key: the channel is wired end to end and only the supplier is absent, which is the same shape as ObjectFormSchema.onStepChange in this card's objectql.ts slice and is not evidence of a dead read. The TypeScript declaration is unchanged and still callable, so a programmatic host supplies it exactly as before.

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    The pair moves from zod-mirror-parity.test.ts's RuntimeOnlyDeclared to its KnownDrift, which empties the former of the one entry the latter did not also hold — so the two unmirrored ledgers are now in a containment relation, and the cross-ledger figure that recorded their difference states the containment instead.

.changeset/7917-export-breadcrumb-object-tree-zod-schemas.md

  • names objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    AnyComponentSchema declares 107 node component types. 105 of them could be named on the ./zod barrel — ButtonSchema.safeParse(node), which is what a designer, a form builder or a targeted test needs. The arms declaring type: 'breadcrumb' (navigation.zod.ts) and type: 'object-tree' (objectql.zod.ts) could not: both were already export const in their own module, but index.zod.ts — the package's only zod entry point — did not re-export them, so the schemas existed, were maintained, and were applied by the union while no consumer could name them.

.changeset/7963-alert-dialog-footer-keys-retired.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    Nothing else moves. These spellings are overloaded across the tree and every other owner is a live key on a different declaration — FormSchema.cancelLabel, objectql.ts's confirmLabel, plugin-designer's ConfirmDialog React props, plugin-grid's def.confirmLabel, and plugin-form's ModalForm / DrawerForm, which build a local cancelLabel from schema.cancelText. None is an AlertDialogSchema; none is touched, and a pin asserts it. No fixture, catalog schema, example app or doc fence authored any of the three on an alert-dialog node, so no shipped document is stranded.

.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/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.

  • names packages/plugin-view/README.md → packages/plugin-view/README.md — edited by this change

    Docs teach the array only: content/docs/plugins/plugin-map.mdx, content/docs/plugins/plugin-view.mdx and packages/plugin-view/README.md.

.changeset/8338-retire-toast-action.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    The parity ledgers drain with it, every figure re-derived by zod-mirror-parity.test.ts's own AST and mirror instruments rather than stepped by hand: KnownDrift 42 entries / 64 keys → 41 / 63 (the entry's whole content, so the entry went too — the ledger's first loss by RETIRING a key rather than by moving either face toward the other), and WiderThanDeclared 23 / 36 / 47 arms, split 6 / 30 / 0 / 11 → 22 / 35 / 45, split 6 / 29 / 0 / 10. The pair itself stays registered, so EXPECTED_MIRROR_PAIRS does not move.

.changeset/8478-describe-line-addresses.md

.changeset/8572-chatbot-body-retired.md

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    Why this key and not another. It was the ONE place in this vocabulary where body did not mean "what goes inside this component": zod-mirror-parity.test.ts carried the pair under KnownDrift as "two different meanings of one key", and the same collision was the whole reason chatbot was the single arm of the component union whose output was not assignable to SchemaNode. Both ledger rows move with this change, and the two pins that recorded the old state are INVERTED rather than deleted (see below).

.changeset/8632-malformed-picklist-option-loud.md

  • names views/metadata-admin/i18n.ts → packages/app-shell/src/views/metadata-admin/i18n.ts — edited by this change

    Two new strings land in the designer's own en / zh tables — the metadata-admin console owns its strings in views/metadata-admin/i18n.ts and is deliberately outside the ten locale packs (packages/i18n/README.md, "Scope — the engine.* carve-out").

.changeset/8735-objectql-mirror-docblocks-not-defaulted.md

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    Correct four zod/objectql.zod.ts docblocks that described the behaviour objectui#8317 removed. Since that change the zod mirrors strip imported @objectstack/spec defaults at this package's import boundary, but the docblocks on HttpRequestSchema, ListColumnSchema, SelectionConfigSchema and PaginationConfigSchema still said, in the present tense, that method, prefix.type, type and pageSize are defaulted on parse — the opposite of what each export does. Each now says the key is declared and accepted but NOT defaulted on parse.

.changeset/8767-object-grid-refuses-string-sort.md

  • names packages/types/src/objectql.ts → packages/types/src/objectql.ts — edited by this change

    Migration. Write the array: sort: [{ field: 'name', order: 'desc' }]. Both keys are required. SortConfig.order carries no ? in @object-ui/types (packages/types/src/objectql.ts) and no .optional() in its zod mirror, and the protocol's own reusable SortItemSchema requires order as well — measured: that schema refuses [{ field: 'name' }] with invalid_value at 0.order. Do not omit it: this block's array arm interpolates whatever is present, so an omitted order lowers to $orderby: 'name undefined' today. That is pre-existing behaviour on the arm this change does not touch, and it is filed as a successor card rather than widened into here.

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

  • names packages/types/src/objectql.ts → packages/types/src/objectql.ts — 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.
  • names packages/types/src/zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — 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/8885-object-chart-drilldown-title-compareto.md

  • names packages/types/src/objectql.ts → packages/types/src/objectql.ts — edited by this change

    ObjectChart.tsx reads all three off schema, and until now neither published copy declared any of them: not the TS interface (packages/types/src/objectql.ts) and not the zod mirror (packages/types/src/zod/objectql.zod.ts). They rode BaseSchema's index signature / .passthrough() and arrived unvalidated. drillDown was the sharpest case — this component's registry inputs advertise it to the designer palette, and @objectstack/spec publishes ChartDrillDownSchema for exactly this carrier, so an author was offered a key that neither published shape mentioned.

  • names packages/types/src/zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    ObjectChart.tsx reads all three off schema, and until now neither published copy declared any of them: not the TS interface (packages/types/src/objectql.ts) and not the zod mirror (packages/types/src/zod/objectql.zod.ts). They rode BaseSchema's index signature / .passthrough() and arrived unvalidated. drillDown was the sharpest case — this component's registry inputs advertise it to the designer palette, and @objectstack/spec publishes ChartDrillDownSchema for exactly this carrier, so an author was offered a key that neither published shape mentioned.

.changeset/8913-object-kanban-columns-declared.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    What moved. ObjectKanbanSchema gains columns on both halves that move together — the TypeScript interface (objectql.ts) and its Zod mirror (zod/objectql.zod.ts). Retiring the bare kanban node type key (objectui#8802) removed the only face that judged a lane, and object-kanban had never declared the key, so it rode BaseSchema's [key: string]: any / .passthrough(): read by the renderer at three sites, named by no published face.

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    What moved. ObjectKanbanSchema gains columns on both halves that move together — the TypeScript interface (objectql.ts) and its Zod mirror (zod/objectql.zod.ts). Retiring the bare kanban node type key (objectui#8802) removed the only face that judged a lane, and object-kanban had never declared the key, so it rode BaseSchema's [key: string]: any / .passthrough(): read by the renderer at three sites, named by no published face.

.changeset/8990-object-kanban-groupby-optional.md

  • names packages/types/src/objectql.ts → packages/types/src/objectql.ts — edited by this change

    @objectstack/spec declares the key optional — groupBy: z.string().optional() on ObjectKanbanPropsSchema — while this package required it on the TypeScript declaration (packages/types/src/objectql.ts) and on the Zod mirror (packages/types/src/zod/objectql.zod.ts). objectui was therefore narrower than the protocol on a published key: ObjectKanbanSchema.safeParse and safeValidateSchema refused an object-kanban node the protocol accepts, and such a node could not be annotated with its own type.

  • names packages/types/src/zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    @objectstack/spec declares the key optional — groupBy: z.string().optional() on ObjectKanbanPropsSchema — while this package required it on the TypeScript declaration (packages/types/src/objectql.ts) and on the Zod mirror (packages/types/src/zod/objectql.zod.ts). objectui was therefore narrower than the protocol on a published key: ObjectKanbanSchema.safeParse and safeValidateSchema refused an object-kanban node the protocol accepts, and such a node could not be annotated with its own type.

.changeset/8992-user-actions-collapse-and-docblock.md

  • names objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    objectql.zod.ts's UserActionsSchema read stripImportedDefaults(Spec).extend({ group, hideFields, rowColor }), an extension that existed only because @objectstack/spec did not declare those three keys while normalizeListViewSchema folded objectui's legacy showGroup / showHideFields / showColor onto them. The protocol adopted all three in 17.3.0 (objectui#5435's ruling), so the extension is now a second local copy of a protocol declaration — the shape two faces start drifting from — and it collapses into the plain by-reference re-export its own note always said it would become.

.changeset/9092-inline-locale-declared-face.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    • AppComponentSchema.label (app.ts) - ObjectGridSchema.label and .description (objectql.ts) - PageNodeSchema.aria.ariaLabel (layout.ts)

.changeset/9309-object-gallery-filter-destination-typed.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    ObjectGallerySchema.filter is typed as the destination its own docblock names — QueryParams['$filter'] — on both faces, the TS interface in objectql.ts and the zod mirror in zod/objectql.zod.ts (objectui#9309).

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    ObjectGallerySchema.filter is typed as the destination its own docblock names — QueryParams['$filter'] — on both faces, the TS interface in objectql.ts and the zod mirror in zod/objectql.zod.ts (objectui#9309).

.changeset/9511-record-id-is-a-string.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    The three authorable keys, each on BOTH faces. ObjectFormSchema.recordId (objectql.ts + zod/objectql.zod.ts), DetailViewSchema.resourceId (views.ts + zod/views.zod.ts) and DetailSchema.resourceId (crud.ts + zod/crud.zod.ts). ⚠️ The crud pair is DetailSchema, not DetailViewSchema, and it reaches the same renderer — not by symbol but by data flow: plugin-detail registers the 'detail' node type onto DetailView. A read that follows TypeScript symbols alone finds two keys and is incomplete.

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    The three authorable keys, each on BOTH faces. ObjectFormSchema.recordId (objectql.ts + zod/objectql.zod.ts), DetailViewSchema.resourceId (views.ts + zod/views.zod.ts) and DetailSchema.resourceId (crud.ts + zod/crud.zod.ts). ⚠️ The crud pair is DetailSchema, not DetailViewSchema, and it reaches the same renderer — not by symbol but by data flow: plugin-detail registers the 'detail' node type onto DetailView. A read that follows TypeScript symbols alone finds two keys and is incomplete.

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    ⭐ The declaration alone would not have been enough, and this is the reusable part. A TypeScript declaration does not run at parse time. The hand-written zod mirror is the only face in this repository that can refuse an authored number, so narrowing the declaration without the mirror would have shipped declared !== enforced on a published surface — and zod-mirror-parity.test.ts would have stayed GREEN through it, because that instrument asserts a mirror accepts everything its declaration declares and a mirror left WIDER passes. Both faces moved together for that reason.

.changeset/9549-tree-filter-declared.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    ObjectTreeSchema.filter is declared on both faces, in the shape objectui#9309 settled for ObjectGallerySchema.filter: QueryParams['$filter'] by indexed access on the TS interface in objectql.ts, and the same two-arm union (array first) on the zod mirror in zod/objectql.zod.ts (objectui#9549).

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    ObjectTreeSchema.filter is declared on both faces, in the shape objectui#9309 settled for ObjectGallerySchema.filter: QueryParams['$filter'] by indexed access on the TS interface in objectql.ts, and the same two-arm union (array first) on the zod mirror in zod/objectql.zod.ts (objectui#9549).

.changeset/9550-object-tree-root-barrel.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    ObjectQLComponentSchema declares the node types an ObjectQL block may be. Every one of its arms was a named export of this package's root barrel except ObjectTreeSchema, which was declared in objectql.ts, applied by the union, and re-exported by the ./zod barrel (objectui#7917) — while no TypeScript consumer could name it. There is no ./objectql subpath to reach around the barrel: the package's exports map is pinned by packages/types/src/__tests__/package-exports-manifest.test.ts, and the root barrel was the only route to this type.

  • names packages/types/src/__tests__/object-tree-root-barrel-9550.test.ts → packages/types/src/__tests__/object-tree-root-barrel-9550.test.ts — edited by this change

    The change is one name added to an existing explicit named re-export list. Nothing is removed, retyped or narrowed: Extract off the union still resolves, and packages/types/src/__tests__/object-tree-root-barrel-9550.test.ts pins that the imported name and that Extract are the same declaration, that the list stays explicit rather than becoming a wildcard, and that the declaration itself stays in objectql.ts.

.changeset/9550-plugin-tree-import-object-tree-schema.md

  • names ObjectTree.schemaTyped-8655.test.ts → packages/plugin-tree/src/ObjectTree.schemaTyped-8655.test.ts — edited by this change

    Nothing about the type changes — the imported name and the narrowing resolve to the same declaration, which ObjectTree.schemaTyped-8655.test.ts asserts invariantly and independently through ObjectTreeProps['schema']. What changes is that the renderer no longer carries a derived restatement of a published type. A type nobody can import mints one of those per consumer, and each one is correct on the day it is written — that is the second-authority shape objectui#6349 is burning down, and this was one of its instances rather than a tidy-up.

.changeset/9606-object-kanban-card-title.md

  • names zod/objectql.zod.ts → packages/types/src/zod/objectql.zod.ts — edited by this change

    Both published faces of the object-kanban arm now name the key: the zod mirror ObjectKanbanSchema in zod/objectql.zod.ts and its TypeScript twin, the ObjectKanbanSchema interface in objectql.ts. Both declare it OPTIONAL, at the same requiredness the other face uses, so the two faces accept and refuse the same documents. (Located and cited by SYMBOL: line addresses in zod/objectql.zod.ts have drifted before, and this change is itself about a drifted mirror.)

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    Both published faces of the object-kanban arm now name the key: the zod mirror ObjectKanbanSchema in zod/objectql.zod.ts and its TypeScript twin, the ObjectKanbanSchema interface in objectql.ts. Both declare it OPTIONAL, at the same requiredness the other face uses, so the two faces accept and refuse the same documents. (Located and cited by SYMBOL: line addresses in zod/objectql.zod.ts have drifted before, and this change is itself about a drifted mirror.)

.changeset/9618-record-source-ladder-attribution.md

  • names ObjectMap.tsx → packages/plugin-map/src/ObjectMap.tsx — edited by this change

    Their zod .describe strings and TS docs said the data → staticData → objectName ladder is resolved by getDataConfig. Neither renderer has had a function by that name since the ladder moved into @object-ui/core's shared resolveRecordSourceConfig (ce2aaefe1), so the text now names that function. The ladder order is unchanged, and so is every accepted document. The ObjectMapSchema text still says getDataConfig, which is true: ObjectMap.tsx keeps a local wrapper by that name that delegates to the shared ladder. The resolveRecordSourceConfig docblock in @object-ui/core now says that the text it quotes is the map faces' text.

.changeset/9628-kanban-column-collapsed-honoured.md

  • names objectql.ts → packages/types/src/objectql.ts — edited by this change

    The key was declared on both published faces of the object-kanban arm — the lane element of ObjectKanbanSchema (objectql.ts and its Zod mirror) and the runtime lane KanbanColumn (complex.ts and its mirror) — and read by KanbanEnhanced alone, a module no production source imports. An authored { "id": "todo", "title": "To Do", "collapsed": true } therefore parsed green on both faces and reached a board that did nothing with it: KanbanImpl's only collapse is the SWIMLANE row's, held in viewer state under objectui:kanban-collapsed:ANGLE-BRACKETS(swimlaneField) and never keyed to a lane's declared value. That is the ADR-0049 declared-but-unhonoured shape.

.changeset/action-callback-retired-7068.md

  • names ui-action.ts → packages/types/src/ui-action.ts — edited by this change

    Where the live meaning lives. Post-success navigation is the spec's onSuccess block, { navigate, openIn }, declared on UIActionSchema (ui-action.ts) and forwarded to the runner (objectui#5934). A success or failure notice is successMessage / errorMessage — adjacent keys on the same legacy ActionSchema, NOT retired, and still accepted on both faces.

.changeset/i18nlabel-inverted-pin-5612.md

  • names ui-action.ts → packages/types/src/ui-action.ts — edited by this change

    The I18nLabel "inverted pin" now watches the premise it claims to watch, and ui-action.ts no longer imports a symbol it never uses (objectui#5612, objectui#5613).

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

  • names zod-mirror-parity.test.ts → packages/types/src/__tests__/zod-mirror-parity.test.ts — edited by this change

    Who is NOT affected: every correctly typed document, and every document that never wrote these keys — absent stays valid on all nine. No renderer changed. The parity ledger (zod-mirror-parity.test.ts) records the move: UnmirroredDeclared 14 entries / 96 keys to 14 / 87, the ObjectViewSchema entry re-derived into the SPEC-DERIVED half because the mirror now references the spec in code.

.changeset/param-dialog-title-fallback-4282.md

  • names ui-action.ts → packages/types/src/ui-action.ts — edited by this change

    title is declared on no action surface in the ecosystem: it is absent from @objectstack/spec's ActionSchema (44 keys walked at spec 17.0.0), from @object-ui/core's ActionDef and its pinned ACTION_DEF_KEYS / SPEC_ACTION_KEYS inventories, and from @object-ui/types' renderer view (ui-action.ts) and crud.ts ActionSchema / BaseSchema. None of the four action renderers — action:button, action:icon, action:group, action:menu — forwards it either. So the right-hand side of that || could not be reached by authored metadata: a fallback that cannot fire, which is the "declared is not enforced" shape objectstack#4075 exists to reduce. Nothing a user hits changes; the line now reads exactly one key, matching the description line directly below it.

.changeset/record-detail-param-dialog-title-5610.md

  • names ui-action.ts → packages/types/src/ui-action.ts — edited by this change

    title is declared on no action surface in the ecosystem: it is absent from @objectstack/spec's ActionSchema (44 keys walked at spec 17.0.0), from @object-ui/core's ActionDef and its pinned ACTION_DEF_KEYS / SPEC_ACTION_KEYS inventories, and from @object-ui/types' renderer view (ui-action.ts) and crud.ts ActionSchema / BaseSchema. None of the four action renderers — action:button, action:icon, action:group, action:menu — forwards it either. So the right-hand side of that || could not be reached by authored metadata: a fallback that cannot fire, which is the "declared is not enforced" shape objectstack#4075 exists to reduce. Nothing a user hits changes; the line now reads exactly one key, matching the description line directly below it.

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 c82ff391f (merge-base with origin/main): 29 file(s) changed outside .changeset/, read against 1917 pending declaration(s) that publish a body (2535 pending in total). · run

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3585.8 KB 3607.4 KB
Main entry chunk (gzip) 150.4 KB 350 KB
Entry file index-B5i0qf_O.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) 571.28KB 136.83KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 229.96KB 63.80KB
fields (index.js) 261.05KB 66.23KB
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.49KB 9.23KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 40.95KB 11.48KB
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.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
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.98KB 15.39KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.34KB 37.30KB
plugin-designer (index.js) 216.45KB 44.60KB
plugin-detail (index.js) 244.79KB 64.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.70KB 44.63KB
plugin-gantt (index.js) 173.11KB 43.10KB
plugin-grid (index.js) 231.53KB 63.58KB
plugin-kanban (index.js) 49.32KB 15.48KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 25.23KB 8.50KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 33.96KB 10.03KB
plugin-tree (index.js) 14.10KB 5.02KB
plugin-view (index.js) 90.32KB 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.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
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) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
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

claude added 2 commits October 1, 2026 06:56
…jectui#11168 slice 3, round 2)

Picks up the Spec Main Shape Gate repair (PR #11343, 47e3ce0) and
objectui#11299 (770cc5b). Merged cleanly: no conflicting hunk, so nothing
was resolved by hand.

Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT
Co-authored-by: Claude <noreply@anthropic.com>
…pec row says — the seat's ruling A (objectui#11168 slice 3, round 2)

The installed `@objectstack/spec` 17.5.0 describe for `object-map.mapStyle`
reads "Read before `map.style`". `getMapConfig` computed that order but the
declared-block path handed `config.style` back first, so a map writing both
drew `map.style`. The seat ruled the renderer follows the spec (option A,
record 5926309662): the block path now returns the same `mapStyle ||
map.style` the flat path does.

- The registration's `mapStyle` description is the spec's describe, verbatim,
  and a pin row holds it equal to the installed row's.
- The precedence is pinned both ways through the real SchemaRenderer: both
  written draws mapStyle, either alone draws itself, neither draws the demo
  tiles (the control). The "measured, not endorsed" row is gone.
- README, plugin-map.mdx and the guard's object-map.map pin claim follow; the
  slice's own pending changeset is corrected and a patch changeset states the
  behaviour change. No authored producer writes both (census in the
  changeset): the flatteners write mapStyle on the block-less path only.

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

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3585.6 KB 3607.4 KB
Main entry chunk (gzip) 150.4 KB 350 KB
Entry file index-BZBt2qER.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) 571.28KB 136.83KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 229.96KB 63.80KB
fields (index.js) 261.05KB 66.23KB
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.49KB 9.23KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 40.55KB 11.41KB
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.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
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.98KB 15.39KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.34KB 37.30KB
plugin-designer (index.js) 216.54KB 44.64KB
plugin-detail (index.js) 244.79KB 64.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.70KB 44.63KB
plugin-gantt (index.js) 173.11KB 43.10KB
plugin-grid (index.js) 231.53KB 63.58KB
plugin-kanban (index.js) 49.32KB 15.48KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 25.21KB 8.49KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 33.96KB 10.03KB
plugin-tree (index.js) 14.10KB 5.02KB
plugin-view (index.js) 90.32KB 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.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
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) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
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

…s; the object-form columns label says what columns does (objectui#11168 slice 3, round 2 addendum)

Two text nits carried from PR B's at-tier record:

- plugin-view README "### View tabs" (and its docs page) listed
  `form.layout` as `vertical | horizontal | inline | grid`; it now lists
  `vertical | horizontal`. The docs-page line is byte-identical to PR B's
  own edit, so the later merge is clean there.
- The designer label for `object-form.columns` read "Columns (grid
  layout)" / 「列数(网格布局)」, pointing at the retired `grid` layout.
  Measured through the real SchemaRenderer, `columns` is live under
  either layout: 1 stacks, 2-4 set the field grid's column count, absent
  infers from the field count (the spec row: "Number of field columns
  (multi-column forms), honoured under either `layout`"). The labels now
  read "Field columns (1–4)" / 「字段列数(1–4)」; the en pin follows.

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

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3585.7 KB 3607.4 KB
Main entry chunk (gzip) 150.4 KB 350 KB
Entry file index-DY_RItRX.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) 571.28KB 136.83KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 229.96KB 63.80KB
fields (index.js) 261.05KB 66.23KB
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.49KB 9.23KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 40.55KB 11.41KB
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.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
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.98KB 15.39KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.34KB 37.30KB
plugin-designer (index.js) 216.54KB 44.64KB
plugin-detail (index.js) 244.79KB 64.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.70KB 44.63KB
plugin-gantt (index.js) 173.11KB 43.10KB
plugin-grid (index.js) 231.53KB 63.58KB
plugin-kanban (index.js) 49.32KB 15.48KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 25.21KB 8.49KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 33.96KB 10.03KB
plugin-tree (index.js) 14.10KB 5.02KB
plugin-view (index.js) 90.32KB 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.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
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) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
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

…jectui#11168 slice 3, round 3)

origin/main db0beb2: PR B (#11351, 17dc167), objectui#11331 (bef9f20)
and #11337 (db0beb2). Two conflicts, both resolved by measurement:

- apps/console registry-inputs-spec-parity.test.ts: the per-owner total.
  Census of the merged ledgers: unjudgedBlocks 2 (object-gantt,
  object-timeline), offSpecInputs 0, unpublishedKeys 3, refusedArms 0
  (B emptied the map), memberPins 0, so objectui#11168 owns 5; 8652 and
  8649 stay 0. Neither side's 6 or 7.
- packages/app-shell i18n.ts (en and zh): B's deletions of
  option.layout.inline / grid kept, with this branch's reworded
  object-form.columns row.

Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT
Co-authored-by: Claude <noreply@anthropic.com>
…jectui#11168 slice 3, round 3, second merge)

origin/main advanced to 2124d04 after the first round-3 merge
(25901fc), bringing #11329 (fd6f5da) and seven more. No conflict.
The guard auto-merged: main's side is MEMBER_PINS prose only
(record:quick_actions.requiredPermissions, object-calendar.navigation,
object-kanban.navigation) and one comment, kept as main has them. The
census of the merged ledgers is unchanged: unjudgedBlocks 2,
offSpecInputs 0, unpublishedKeys 3, refusedArms 0, memberPins 0;
objectui#11168 owns 5, 8652 and 8649 own 0.

Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT
Co-authored-by: Claude <noreply@anthropic.com>
…jectui#11168 slice 3, round 3, third merge)

origin/main advanced to 5638529 while the round-3 gates ran: #11317
(docs), #11352 (types, fields) and #11350 (plugin-grid, core). No
conflict. #11352 also edits packages/types objectql.ts, objectql.zod.ts
and zod-mirror-parity.test.ts, which this branch edits too; it
auto-merged, and the gates are re-run at this head. The guard is
untouched by this merge, so its census still reads as at 2817b5f:
objectui#11168 5, unjudgedBlocks 2, refusedArms 0.

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

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3552.5 KB 3574.6 KB
Main entry chunk (gzip) 150.4 KB 350 KB
Entry file index-CbIwvgRV.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) 571.28KB 136.83KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 229.96KB 63.80KB
fields (index.js) 261.05KB 66.23KB
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.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.49KB 9.23KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.93KB 11.29KB
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.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
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) 53.15KB 15.45KB
plugin-charts (index.js) 84.07KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.34KB 37.30KB
plugin-designer (index.js) 216.54KB 44.64KB
plugin-detail (index.js) 245.45KB 64.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.25KB 44.58KB
plugin-gantt (index.js) 173.11KB 43.10KB
plugin-grid (index.js) 231.53KB 63.59KB
plugin-kanban (index.js) 49.49KB 15.54KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 25.21KB 8.49KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 33.96KB 10.03KB
plugin-tree (index.js) 14.10KB 5.02KB
plugin-view (index.js) 90.32KB 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.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
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) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
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: 24c5172f97adf1a6ef37068656a6132733e73e3a
Local-runs: none

Read-only, as briefed: card objectui#11168 (body and all 19 comments), PR #11335 (body, 35 files, the diff against its merge-base 5638529e6), the installed spec's source at objectstack 0f6dcac5e9, objectui AGENTS.md at origin/main, files at the head and at origin/main through git show, and the 45 check-runs on the head, read last at 2026-10-01T09:40Z: 42 success, 3 skipped by design (dependabot, the two coverage shards), none in progress, none red. The PM seat's addendum (the changeset-claim-re-read list, 58 entries) is folded into ②.

① Derived judgments

object-tree registration — RIGHT, except one clause. Both tags (object-tree, view:tree) share treeInputs, which now declares objectName (no required), tree, data, staticData, filter and navigation — the six keys ObjectTreePropsSchema declares at 0f6dcac5e9, where objectName is optional too. Each description names what the new pin measured through the real SchemaRenderer (the ladder order; the api and schema providers drawing no rows; a record whose parent the filter removed drawn as a root). The one wrong clause is the navigation paragraph below.

ObjectTreeSchema, both faces — RIGHT. TS: objectName?, data?: ViewData, staticData?: any[], tree?: SpecTreeConfig, navigation?: ViewNavigationConfig (the spec's NavigationConfig). Zod: the same five, tree and navigation by reference through stripImportedDefaults(SpecTreeConfigSchema) and stripImportedDefaults(SpecNavigationConfigSchema), the member ending in requireRecordSource('object-tree', RECORD_SOURCE_KEYS, 'node', 'not-read'). The ledgers moved with it: SPEC_DERIVED_PAIRS +1, the 8317 IMPORTED list +1, the 8655 pin's two "stays undeclared" rows flipped and its LEDGERED_UNDECLARED is empty, the 9550 barrel row reads string | undefined. The narrowing on the zod face is real and stated: a bare array under data, a misspelled tree member and an unknown navigation.mode are refused, each pinned in the tree pin's authoring-door block.

requireRecordSource binding: 'not-read' — RIGHT. The fourth parameter defaults to 'counts'; under it the message (ending "or name the object in dataSource.object") and the dataSourceSuppliesObject short-circuit are what they were. I read every call at the head — object-grid, list-view, object-map ×2, object-gantt ×2, object-calendar, object-kanban — and none passes the new arm, so every other arm's behaviour is unchanged. Under 'not-read' a binding-only tree is refused once at the root with RECORD_SOURCE_REQUIRED, which is exactly the node that draws nothing: ObjectTreeRenderer hands the context adapter down as dataSource and no gate lands dataSource.object on objectName (the spec row's own docblock measures the same zero). The message then names only the three rungs. It accepts nothing the renderer cannot draw.

@object-ui/sdui-parser devDependency — RIGHT. devDependencies is none of the eight publish-contract fields; the lockfile gains the one workspace link; the new pin imports manifestFromConfigs and validateTree from it.

object-map registration — RIGHT, except one clause. mapStyle (string), navigation (object) and enableClustering (boolean) are the three keys the row declares and the registration lacked. The mapStyle description equals the spec describe byte for byte (compared mechanically against ObjectMapPropsSchema.mapStyle at 0f6dcac5e9), and a pin row holds it equal to the installed row's own describe. enableClustering's description matches the row's ("Absent, the renderer clusters only above 100 markers") and is pinned at 2 and at 120 markers.

getMapConfig precedence, ruling A — RIGHT. The declared-block path returns { ...config, style } where style is schema.mapStyle || schema.map?.style, so mapStyle is read first on both paths. The pin is an it.each of exactly the four cases — both written draws mapStyle; only map.style; only mapStyle; neither draws the demo tiles — through the real SchemaRenderer. The map description ("style … read after the node's mapStyle"), the README and plugin-map.mdx agree. 11168-map-style-precedence states the behaviour change truthfully: both-written used to draw map.style, one or neither draws what it drew, no producer writes both.

UIActionSchema.size — RIGHT. SpecActionButtonProps['size'] is the row's z.enum([...BUTTON_PRIMITIVE_SIZES, 'md']), that is default / sm / lg / icon / md. The pin asserts type identity with the row, accepts default and icon, refuses xl at tsc, and reads the installed row at runtime. A widening, on the leaf and on the action:group / action:menu / action:bar member type alike. The changeset's sentence that a group member draws each of the five exactly as a leaf does is not re-derived by any pin in this diff — ③ item 8.

Wording — RIGHT. The calendar pin text now says five keys, and CalendarConfigSchema at 0f6dcac5e9 is a strict object of exactly startDateField, endDateField, titleField, colorField and allDayField. The timeline's two nits are corrected as described — the split reason sits on split alone, and openNewTab outranks every mode except none, which is the hook's order (none and preventNavigation are checked before new_window and openNewTab) — the docs table follows, and the map copies the corrected wording.

The guard — RIGHT. Eager imports of @object-ui/plugin-tree and @object-ui/plugin-map; MINOR_17_5_LOADED_BLOCKS (object-map, object-tree) joins EXPECTED_COVERED and the inputs-non-empty loop; the two OWED rows are struck. Census by static count of the file at the head: UNJUDGED_SPEC_BLOCKS 2 (object-gantt, object-timeline); OFF_SPEC_EXEMPTIONS is {}, 0; UNPUBLISHED_EXEMPTIONS 3, the three owedEntries spreads (action:button endpoint, action:button undoable, action:icon endpoint); OFF_SPEC_ARM_EXEMPTIONS 0; MEMBER_PIN_EXEMPTIONS 0 with MEMBER_PIN_EXEMPTION_CEILING 0. So the caps 2 / 0 / 3 / 0 / 0, the owner count objectui#11168 5 with objectui#8652 0 and objectui#8649 0, and the census 37 + 7 + 11 = 55 are census-true. The 12 new MEMBER_PINS rows each name a file at the head whose rows assert the claim: object-map.map (locationField in three forms, titleField and descriptionField, zoom / center against bounds, style with mapStyle four ways), .data, .staticData, .filter (not_equals, $filter unchanged, the no-filter control), .sort ($orderby in authored order, an omitted order ascending, the control), .dataSource (the pre-existing ObjectMap.elementDataSource.test.tsx has one test per claimed member: the object, the view's filter and sort, AND-ing, the unresolvable view, limit never at $top, a map with no binding unchanged, an omitted order), .navigation; object-tree.tree, .data (api and schema draw nothing), .staticData, .filter, .navigation. One clause of the two .navigation rows is wrong — below.

The merges — RIGHT. The merge-base of the head with origin/main is 5638529e6, the PR base. A diff-of-diffs (U0, index and hunk headers stripped) between round 2, 770cc5ba4..f7424b7f1 (36 files), and round 3, 5638529e6..24c5172f9 (35 files), differs in exactly two places: the owner-count line (round 2 moved it 8 to 6; round 3 moves it 7 to 5, the census the seat asked for once PR B landed owner 7, and 5 is what my own count reads), and content/docs/plugins/plugin-view.mdx dropping out because PR B (17dc16793) landed the identical line. No other changed line moved.

Changesets — RIGHT as bodies, with one false sentence in two of them (below). 11168-object-tree-inputs (plugin-tree minor), 11168-types-object-tree-mirror (types minor, the narrowing stated), 11168-object-map-inputs (plugin-map minor), 11168-ui-action-size (types minor), 11168-timeline-navigation-wording (plugin-timeline patch), 11168-map-style-precedence (plugin-map patch), 11168-view-tabs-layout-values (plugin-view patch — the README line only; a *.md in files needs no changeset by the presence rule, so this is a voluntary and truthful entry, and "the docs page carries the same line" is true on the merged tree), 11168-object-form-columns-label (app-shell patch, matching the two i18n.ts rows and the en pin). 8655-object-tree-props-schema-typed.md: origin/main's 3238 bytes are a byte-prefix of the head's file, the frontmatter is untouched, the note is dated and append-only, and main's copy has not moved since the PR base.

navigation on object-tree, object-map and object-timeline — WRONG at this head, and the ground for the verdict. The three descriptions (two new, the timeline's edited by this PR) say "page and none open nothing here" and that a block which omits mode opens nothing "on a tree / map no parent view navigates for". That held at the slice's measurement base 95bd23c90 (2026-10-01T03:54Z). objectui#11293 (2124d0411, landed 2026-10-01T07:35Z, after round 2) rewrote useNavigationOverlay's page branch: with no onNavigate supplied it now calls openRecord(objectName, recordId) from the record navigator the host publishes on RelatedRecordActionsContext, and the console publishes one on its custom pages, record pages and list views (the 11293-app-shell-page-record-navigator changeset). That commit entered this PR through the round-3 merge 2817b5f4e; the hook at the head differs from the hook at 95bd23c90 by +56 / −3 lines. ObjectTree, ObjectMap and ObjectTimeline each call the hook with navigation, objectName and onRowClick and no onNavigate, and none wraps the context. So at the head, a standalone tree, map or timeline that names its object and authors navigation: { mode: 'page' }, or a block without mode such as { size: 'lg' }, OPENS the record page under the console host. The ABSENT-key clause stays true: the hook reads the navigator only on an authored block that resolves to page. The pins stay green — the tree row "mode: "page", and a block without mode, open nothing on a tree no parent view navigates for" and the map's twin — only because the harness publishes no navigator: they pin the absence of the host, not the renderer under the product. The same sentence publishes verbatim from 11168-object-tree-inputs ("an absent navigation, page or a block without mode on a standalone tree" draw nothing) and 11168-object-map-inputs ("a click opens nothing in these cases: the key is absent, the mode is page, none or split, or the block has no mode"), and it sits in both READMEs, the new plugin-map.mdx section, the plugin-timeline.mdx callout, and the two MEMBER_PINS rows ("The rows that make the description true: an ABSENT key, page, and a block without mode open nothing"). At this same head the kanban and calendar descriptions already carry the 11293 wording ("page opens the record page through the record navigator the host publishes …; under a host that publishes none … the click opens nothing"). Under objectui#11111 decision 3 = B a description says what the renderer does with the key; these deny a behaviour the renderer delivers on the product, on three published inputs and in two CHANGELOG bodies.

Clause-②: yes (narrowing) — RIGHT arm. Widens: seven published inputs, objectName optional on the registration and on both faces, size admitting default and icon on UIActionSchema and so on the group / menu / bar member type. Narrows: the zod ObjectTreeSchema now judges data (a bare array refused), tree (strict) and navigation (mode enum), and the TS face types the same keys. Both halves are named on the line. No major.

② Semver level

  • @object-ui/plugin-tree minor, @object-ui/types minor ×2, @object-ui/plugin-map minor: widened and narrowed published faces, minor under the fixed-group rule (AGENTS.md 版本号策略). @object-ui/plugin-map patch for the precedence: a behaviour change toward the row's own describe, with no producer writing both, stated as a behaviour change — defensible as patch, and the group ships minor regardless. plugin-timeline, plugin-view and app-shell patch: wording, a README line, two label strings. apps/console: test-only, and Changeset Declaration is green on the head. The Clause-② arm is right (①).
  • Addendum — the pending bodies whose sentences describe what this PR's own changed lines change. 10393-retire-bare-map-key: still true — the README and plugin-map.mdx edits add a node-level table, a navigation section and style's precedence; the bare-array correction stands and the data description still says a bare array is not the key's shape. 5903-objectgantt-declared-keys: gantt-only sentences; the requireRecordSource arm it relies on is unchanged for the gantt and zod-mirror-parity's new SPEC_DERIVED_PAIRS row is the tree's — still true. 8655: this PR appends the dated note, so the sentences it falsifies are dated. 9950-map-style-reaches-flat-renderer ("mapStyle … is the first spelling getMapConfig reads"): true on every path now. 10002, 10859-object-map-properties-bag ("ObjectMapSchema … unchanged in shape"; "navigation is not judged member by member there"), 10859-plugin-map-properties-bag-docs, 10456, the three 8348 entries, 9549, the two 9550 entries, 8841, 7917, 6939, 7632, 9618, 11117, 10392, 7470, 8654 (its click paragraph does not carry the "outranks the mode" wording), 11168-element-family-inputs, 10064, 9136, the two 7804 entries, 8067, 8221, 10872, 5793, and the ui-action.ts entries (7068, 5612, 4282, 5610, with 8648): none names size or a sentence this diff moves. Two stale adjectives, not falsified claims: 8253-export-tree-view-config ends "the authored node remains the flat ObjectTreeSchema", and this PR dropped "flat" from the tree README for the same reason, so a dated note there would be consistent; 8348-record-source-data-arm-per-spec-row's "(which declares objectName required and no data)" is scoped by "When it landed". Neither blocks. The two pending sentences that ARE false at this head are this PR's own two inputs changesets, in ①.

③ Boundary flags

  1. object-map.mapStyle precedence (report 5926262979, open question 1): ruled A by the seat in 5926309662, implemented in round 2, pinned four ways, changeset truthful. Answered.
  2. The worklist handed back (object-gantt, object-timeline, booking c): booked by the seat as slices 4 and 5. Answered.
  3. The split into two PRs, PR B landed 17dc16793: the round-3 merges reconciled the owner count to 5 by census (verified by my own count of the file at the head), and i18n.ts at the head shows only the two columns label rows changed, B's option deletions kept. Answered.
  4. Bounded in-place additions — the not-read arm, the devDependency, ObjectTree.test.tsx dropping a bare-array schema.data that objectui#8348 had already made inert, the 9550 row reading string | undefined: each judged right in ①. Answered.
  5. The round-2 addendum (plugin-view README vertical | horizontal; the object-form.columns label en and zh with the en pin): judged from the diff; the columns liveness reading is the dev's measurement and agrees with the spec describe the changeset quotes. Answered.
  6. Out of scope, the kanban / calendar "outranks the mode" wording: booked to slice 5 by the seat; still open at the head beside objectui#11293's rewrite of those descriptions. Carried.
  7. ObjectTreeSchema's flat parentField / labelField / fields / defaultExpandedDepth: the seat noted, not booked. The renderer reads them (getTreeConfig, flat first), so declaring them is not a decision-3 = B breach; it is the mirror being wider than the row, which refuses them by name with OBJECT_TREE_FLAT_CONFIG_GUIDANCE. Escalated to the seat to book with a dedupe rather than leave at 承接者:无.
  8. Not re-derived (the AGENTS.md 完善设计器的每一个细节 #9 shape): the 11168-ui-action-size sentence that a group MEMBER draws all five sizes as a leaf does. Slice 2's action-group-menu-inputs-11168 pins a member's lg outranking the group's and the group's lg / icon reaching members, not the five-way equality. One row in that file closes it. Not the verdict's ground.
  9. Blocking — the page / mode-less navigation clause (①). The remedy stays inside this PR's files: (a) re-measure page and the mode-less block under a published navigator — one row each in the tree and map pins, mounting under a RelatedRecordActionsContext provider as the kanban's and calendar's NavigationMembers-8652 pins do; (b) reword the tree, map and timeline navigation descriptions, both READMEs, plugin-map.mdx, the plugin-timeline.mdx callout and table, and the two MEMBER_PINS rows to the 11293 form, keeping the ABSENT-key clause as it is; (c) correct the two inputs changesets' sentences, prose only with the frontmatter untouched (check-changeset-overwrite reports that as its legitimate case). Then a new record on the new head.

Implemented-by: claude/issue-11168-lazy-blocks-form-layout
Reviewed-by: session_01VhxTqosz7wn54ahqyxgERT

VERDICT: FAIL

claude added 4 commits October 1, 2026 09:42
…jectui#11168 slice 3, round 4)

origin/main d0fba91 (#11346, #11359, #11361, #11367). It touches
pnpm-lock.yaml, which this branch also edits (the plugin-tree
devDependency link); no conflict.

Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT
Co-authored-by: Claude <noreply@anthropic.com>
…avigation block open the record page through the host's navigator, as measured since objectui#11293 (objectui#11168 slice 3, round 4)

The at-tier record on PR #11335 (5928833509) found the `navigation`
descriptions this PR publishes on object-tree and object-map, and edits on
object-timeline, saying `page` and a block without `mode` open nothing.
objectui#11293 (2124d04, in this branch since 2817b5f) hands a `page`
click with no `onNavigate` to the record navigator the host publishes on
RelatedRecordActionsContext, and the three renderers pass none.

Measured first through the real SchemaRenderer, with the host's navigator
mounted and without (40 rows, predictions written before the run, all as
predicted): under the navigator `page` and `{ size: 'lg' }` call
openRecord(objectName, id) once, with no overlay and no tab, on all three
blocks; the ABSENT key, `none` and (map, timeline) `split` open nothing and
do not navigate; a tree or map that names no objectName opens nothing; with
no navigator `page` and the mode-less block open nothing.

- The three registration descriptions, both READMEs and the timeline
  README, plugin-map.mdx, the plugin-timeline.mdx table and callout, and the
  two MEMBER_PINS rows now say that, in the kanban/calendar 11293 wording.
  The absent-key clause is kept.
- The tree and map pins gain a navigator-mounted row set through the real
  SchemaRenderer; timelineNavigationMembers-8654 gains one and its no-host
  rows are renamed to say so.
- The prose of 11168-object-tree-inputs, 11168-object-map-inputs and
  11168-timeline-navigation-wording follows; frontmatter untouched.
- 8253-export-tree-view-config gains a dated, append-only note on "the flat
  ObjectTreeSchema"; the earlier 4323 bytes are byte-identical (cmp).
- action-group-menu-inputs-11168 gains the row the UIActionSchema.size
  changeset's claim was owed: a group member at each of the five sizes draws
  the same class list as an action:button at that size, with its control.

Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT
Co-authored-by: Claude <noreply@anthropic.com>
…ry, whose `page` sentence objectui#11293 and this slice's description overtake (objectui#11168 slice 3, round 4)

The pending .changeset/8654-timeline-navigation.md says `page` and a
mode-less block open nothing on a standalone timeline and that the input
description says so. Both stopped holding in this release. Append-only:
the earlier 1484 bytes and the frontmatter are byte-identical to
d0fba91 (cmp -n 1484 exit 0).

Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT
Co-authored-by: Claude <noreply@anthropic.com>
…a node that names no object (objectui#11168 slice 3, round 4)

`{ objectName = OBJECT } = {}` takes its default for an explicit
`objectName: undefined`, so the row meant to mount a tree / map on inline
rows alone still named the object and saw `openRecord` called. The helper
now takes `namesObject`; the probe that measured this case before the pin
omitted the key and read nothing opened.

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

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3552.4 KB 3574.6 KB
Main entry chunk (gzip) 150.4 KB 350 KB
Entry file index-BUADrVtH.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) 571.28KB 136.83KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 229.96KB 63.80KB
fields (index.js) 260.89KB 66.19KB
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.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.49KB 9.23KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.93KB 11.29KB
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.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
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) 53.15KB 15.45KB
plugin-charts (index.js) 84.07KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.07KB 37.18KB
plugin-designer (index.js) 216.54KB 44.64KB
plugin-detail (index.js) 245.45KB 64.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.16KB 44.58KB
plugin-gantt (index.js) 173.11KB 43.10KB
plugin-grid (index.js) 231.53KB 63.59KB
plugin-kanban (index.js) 49.49KB 15.54KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 25.60KB 8.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 34.29KB 10.13KB
plugin-tree (index.js) 14.50KB 5.14KB
plugin-view (index.js) 90.32KB 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.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
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) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
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: 821c810e99563e418a86fedaab476dced7a586f6
Local-runs: none

Delta record on round 4, after 5928833509 (FAIL on 24c5172f9). Everything that record judged RIGHT carries forward unchanged; this record judges what round 4 moved. Read-only, as briefed: the round-4 os-dev-report 5929353293 and the pointer 5929010995 on objectui#11168, the PR body (line 30 included), the PR's 40-file list, the merge 556bc5d65 against d0fba91aa, round 4's own diff 556bc5d65..821c810e9 in full, the hook and the resolver at origin/main through git show, and the check-runs on the head, read last. At that read (2026-10-01T10:25Z) the head carries 44 check-runs: 32 success, 3 skipped by design, and 9 still in progress, recorded as such and not as a verdict: Spec Main Shape Gate, Test (shard 1/8), Test (shard 6/8), Test (shard 5/8), Test (shard 2/8), Test (shard 7/8), Test (shard 3/8), Test (shard 4/8), Type Check; none red. The verdict below rests on ① and does not wait on them.

① Derived judgments

The merge 556bc5d65 — RIGHT. Its merge-base with d0fba91aa is d0fba91aa, and the head's merge-base with origin/main is d0fba91aa too. A diff-of-diffs (U0, index and hunk headers stripped) between round 3's net diff 5638529e6..24c5172f9 and the post-merge net diff d0fba91aa..556bc5d65 is IDENTICAL by changed lines. The only file main moved that this PR also edits was pnpm-lock.yaml, with no conflict. useNavigationOverlay is byte-identical at 24c5172f9, at the head and at origin/main (1a88ce22f), so the objectui#11293 behaviour 5928833509 judged against is what this head runs. origin/main has since gained three merges that share no file with the PR (the overlap is empty), hence behind and nothing more.

Round 4's own lines — read in full. Three commits (e5683313c, b90fb31e0, 821c810e9), 18 files, +377 / −47, every one inside the remedy's scope or one of the two extras named below.

The FAIL item of 5928833509 — closed on object-map and object-timeline; closed on object-tree except one clause. Reworded to the objectui#11293 wording family: the three registration navigation descriptions and the comment above the timeline's; the tree, map and timeline READMEs; plugin-map.mdx; the plugin-timeline.mdx table (a new page row) and callout; the guard's object-map.navigation and object-tree.navigation MEMBER_PINS rows; the prose of 11168-object-tree-inputs, 11168-object-map-inputs and 11168-timeline-navigation-wording, frontmatter untouched; and the PR body's map paragraph, which now states the measured matrix.

The new wording against the hook at this head. With the key ABSENT a click opens nothing: the hook's no-config branch calls onNavigate or returns, never the navigator, and none of the three renderers supplies one or defaults an absent key to a drawer — RIGHT. none and preventNavigation are read first; new_window / openNewTab next; then page hands the click to onNavigate, else to openRecord(objectName, recordId) when the hook holds an objectName and the host publishes a navigator, else nothing — RIGHT for page and the mode-less block under a navigator, and for "nothing under a host that publishes none". split draws beside on the tree and nothing on the map and timeline — RIGHT, unchanged. For the map and the timeline the hook's objectName is schema.objectName, so the map's "on a map that names no objectName … opens nothing" is exact, and the timeline, whose registration still requires objectName (slice 5's), makes no such claim — RIGHT.

object-tree: the no-objectName clause — WRONG. ObjectTree hands the hook resolveRecordSourceObjectName(schema, dataConfig) ?? schema.objectName, and that resolver (packages/core record-source.ts) returns dataConfig.object whenever the provider is object, else schema.objectName. So a tree authored on data: { provider: 'object', object } with no objectName key — the shape the spec row's own objectName describe endorses ("data can name the object itself") — holds that object in the hook, and under a navigator host page and a mode-less block OPEN its record page. The new tree description, the tree README, 11168-object-tree-inputs and the object-tree.navigation guard row all say that on a tree that names no objectName there is no record page to open and the click opens nothing; the phrase "the record page of the tree's objectName" is also wrong when both are written, because the rows' object wins — the rule the renderer's own comment states ("The record-page URL names the object the ROWS came from, not the block's bare top-level key"). The round's no-objectName measurement covered inline rows alone, and the pin's row mounts only that shape, while the same file's object-tree.data row already mounts data: { provider: 'object', object } with no objectName — adding navigation: { mode: 'page' } to it under the host is the row that would have shown openRecord called. A description that says "opens nothing" where the renderer opens the record page is the class 5928833509 failed on, now narrowed to one authoring shape on one block. The map's twin clause is right, so this is the tree's sentence alone.

The pins — RIGHT. The tree and map pins each gain a navigator-mounted describe through the real SchemaRenderer under a RelatedRecordActionsProvider whose openRecord is a spy: drawer as the lit control under the same host (dialog, no openRecord, no tab), page and the mode-less block calling openRecord(OBJECT, id) exactly once with no dialog and no tab, absent and none (and split on the map) opening nothing and not navigating, and the inline-rows no-objectName row. The old no-host rows are renamed to say so. timelineNavigationMembers-8654 gains the same describe around ObjectTimeline mounted directly, with the drawer control, page, the mode-less block, a parent onRowClick outranking the navigator, and absent / none / split; its header is rewritten to the pinned-both-ways reading. action-group-menu-inputs-11168 gains an it.each over default / sm / lg / icon / md comparing a group member's class list with an action:button leaf's at the same size with the variant held at default, plus a no-size control where the two differ (member sm, leaf default) — that closes 5928833509 ③ item 8 and makes the 11168-ui-action-size sentence re-derived.

The appended notes — RIGHT. 8253-export-tree-view-config.md: origin/main's and d0fba91aa's 4323 bytes are a byte-prefix of the head's 4964, frontmatter unchanged, and the note is true (the authored tree writes the four keys under tree; the flat spellings are the host's form; TreeViewConfig and objectui#5321 untouched) — closes ② of 5928833509's first stale adjective. 8654-timeline-navigation.md: 1484 bytes prefix-identical, frontmatter unchanged, the note true against the hook ("An absent key and split still open nothing") — this one the round added on its own and rightly, since its last paragraph says page opens nothing and that the description says so. 8655: 3238 bytes, unchanged this round.

The three changeset proses. 11168-object-map-inputs — RIGHT. 11168-timeline-navigation-wording — RIGHT: three wording errors named, "Behaviour is unchanged" true, the README and docs table and callout named. 11168-object-tree-inputs — carries the tree clause above, WRONG in that one sentence.

Semver and Clause-② — unchanged from 5928833509. No frontmatter moved this round; the three modified pending changesets are prose-only appends.

② Semver level

Carried from 5928833509: plugin-tree / types ×2 / plugin-map minor, the rest patch, no major, Clause-②: yes (narrowing) the right arm. Round 4 adds no changeset and changes no bump; 8253, 8654 and 8655 are dated, append-only, frontmatter untouched. The refreshed changeset-claim-re-read list reads 57 (8253 left it, now being this PR's own edit); nothing new entered it. The one pending sentence false at this head is this PR's own, in 11168-object-tree-inputs (①).

③ Boundary flags

  1. Blocking, one sentence on one block. The tree's navigation clause "on a tree that names no objectName … opens nothing" and "the record page of the tree's objectName" (①). Remedy, tree files only: in the registration description, the tree README, 11168-object-tree-inputs (prose, frontmatter untouched) and the object-tree.navigation guard row, say that page opens the record page of the object the rows came from (data.object, else objectName), and that the click opens nothing under a host that publishes none or on a tree whose rows name no object (inline rows with no objectName); and add one navigator-mounted pin row on data: { provider: 'object', object } with no objectName and mode: 'page', asserting openRecord(object, id) once. The map, timeline, docs pages, the other two changesets and the PR body stand. Then a delta record on that head.
  2. 5928833509 ③ item 8 (the group-member size claim not re-derived) — closed by the new it.each and its control.
  3. 5928833509 ② first stale adjective (8253) — closed by the dated note. The second (8348-record-source-data-arm-per-spec-row, time-scoped) stays optional.
  4. The round's extra 8654 note — right and needed; recorded.
  5. Round-4 out-of-scope findings, read as claims: plugin-view.mdx's sentence that a row click under mode: 'page' opens nothing without onNavigate may be overtaken by objectui#11293 for an object-view under the console (承接者:无, not measured — for the seat to carry); the ObjectTimelineProps.navigation JSDoc names the wrong mechanism for an absent key (booked to slice 5). Carried.
  6. Items 1 to 7 of 5928833509 ③ carry forward as recorded there.

Implemented-by: claude/issue-11168-lazy-blocks-form-layout
Reviewed-by: session_01VhxTqosz7wn54ahqyxgERT

VERDICT: FAIL

…'s rows came from — `data.object` on the object provider, else `objectName` (objectui#11168 slice 3, round 5)

The delta record on PR #11335 (5929481787) found the tree's round-4 clause
too narrow: ObjectTree hands the hook
`resolveRecordSourceObjectName(schema, data) ?? objectName`, which reads
`data.object` whenever `data` is the object provider.

Measured first through the real SchemaRenderer under the host's record
navigator, predictions written before the run (6 rows, all as predicted):
`data: { provider: 'object', object: X }` with no objectName opens X's
record page on `page` and on a mode-less block; with `objectName: Y` beside
it X opens and X is the object queried; a value provider beside
`objectName: Y` opens Y; inline rows with no objectName, and a value
provider with none, open nothing.

- The registration description, the tree README, the
  11168-object-tree-inputs prose and the guard's object-tree.navigation
  MEMBER_PINS row now say that.
- The tree pin gains two navigator-mounted rows on the object-provider
  shape: no objectName, and both written.

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

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3552.3 KB 3574.6 KB
Main entry chunk (gzip) 150.4 KB 350 KB
Entry file index-3A1fYnVB.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) 570.62KB 136.73KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 229.96KB 63.80KB
fields (index.js) 260.89KB 66.19KB
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.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.49KB 9.23KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.93KB 11.29KB
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.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
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) 53.15KB 15.45KB
plugin-charts (index.js) 84.07KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.07KB 37.18KB
plugin-designer (index.js) 216.54KB 44.64KB
plugin-detail (index.js) 245.45KB 64.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.16KB 44.58KB
plugin-gantt (index.js) 173.11KB 43.10KB
plugin-grid (index.js) 231.53KB 63.59KB
plugin-kanban (index.js) 49.49KB 15.54KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 25.60KB 8.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 34.29KB 10.13KB
plugin-tree (index.js) 14.58KB 5.17KB
plugin-view (index.js) 90.32KB 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.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
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) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
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: e1860d8f3bc5348853e8a367f1962ba5a3fdbf1c
Local-runs: none

Delta record on round 5, after 5929481787 (FAIL on 821c810e9, one clause on object-tree) and 5928833509 (FAIL on 24c5172f9). Everything those two records judged RIGHT carries forward unchanged; this record judges what round 5 moved. Read-only, as briefed: the round-5 os-dev-report 5929624408 on objectui#11168, the PR body, the PR's 40-file list, round 5's own diff 821c810e9..e1860d8f3 in full, the hook and the resolver at origin/main through git show, and the check-runs on the head, read last. At that read (2026-10-01T10:51Z), after a bounded poll, the head carries 45 check-runs: 42 success, 3 skipped by design, none in progress, none red.

① Derived judgments

Round 5's shape — RIGHT. One commit, e1860d8f3, whose parent is 821c810e9; no merge. The head's merge-base with origin/main (c82ff391f) is still d0fba91aa, and the files main has moved since share nothing with the PR (the overlap is empty), hence behind and nothing more. The diff is 5 files, +60 / −9, every one a tree carrier: the object-tree registration description in plugin-tree/src/index.tsx, the tree README, the tree pin objectTreeInputs-11168.test.tsx, the prose of .changeset/11168-object-tree-inputs.md, and the guard's object-tree.navigation MEMBER_PINS row. useNavigationOverlay and record-source.ts are byte-identical at the head and at origin/main, so the hook and the resolver 5929481787 judged against are what this head runs.

The 5929481787 clause — closed in all four places, and nowhere else stated. The description, the README, the changeset prose and the guard row now say that page, and a block without mode, open the record page of data.object when data is the object provider — so it wins when both are written — and of the tree's objectName otherwise, through the record navigator the host publishes; and that under a host that publishes none, or on a tree that names neither (inline rows with no objectName), there is no record page to open and the click opens nothing. The absent-key and no-host clauses are unchanged. The one names no objectName phrase left in the guard belongs to the object-map.navigation row, which is right for the map (it hands the hook schema.objectName). There is no plugin-tree.mdx on main. The PR body's tree section states neither the page case nor the no-objectName case — its measurement line says only that mode: 'drawer' opened the overlay and an absent key opened nothing, both still true — and line 30 is the map's paragraph, corrected in round 4. Confirmed: no body edit was owed.

The new wording against the resolver and the hook — RIGHT. resolveRecordSourceObjectName(schema, dataConfig) returns dataConfig.object when the provider is object, else schema.objectName; the tree's dataConfig is resolveRecordSourceConfig(schema, 'view-data'), whose first rung is data, so data.object is the name when both are written. objectName alone folds to { provider: 'object', object: objectName } and yields the same name; staticData and the value provider fall through to schema.objectName; with neither, the hook's page branch holds no objectName and returns without calling openRecord. Each sentence above is true of the code at this head.

The two pin rows — RIGHT. Both mount through the real SchemaRenderer under the same recordNavigatorHost spy as the round-4 describe, on the object-provider shape the file's object-tree.data rows already mount, and each first asserts the queried root was drawn, so the click lands on a real row. (a) data: { provider: 'object', object } with no objectName, mode: 'page': openRecord(OBJECT, 'q1') exactly once, no dialog, no tab. (b) objectName: 'org_chart' beside the same data: openRecord(OBJECT, 'q1'), and find to OBJECT, never to org_chart. Both are positive assertions with exact arguments, so neither can pass on silence; the drawer lit control under this host sits in the preceding describe of the same file, and the inline-rows no-objectName row keeps its negative under a retitled name. The guard row still names this file. The report's rows (d) and (e), the value provider with and without objectName, are not pinned; they follow from the schema.objectName fall-through and nothing in the text rests on them.

Changeset — RIGHT. 11168-object-tree-inputs keeps its frontmatter ('@object-ui/plugin-tree': minor); only the prose moved, to the measured rule.

② Semver level

Carried from 5928833509: plugin-tree / types ×2 / plugin-map minor, the rest patch, no major, Clause-②: yes (narrowing) the right arm. Round 5 adds no changeset and moves no bump; 8253, 8654 and 8655 stay dated, append-only, frontmatter untouched, as judged in 5929481787. No pending sentence is made false by this round.

③ Boundary flags

  1. The blocking item of 5929481787 — closed (①).
  2. 5929481787 ③ items 2 to 6 and 5928833509 ③ items 1 to 8 carry forward as recorded there; nothing in round 5 reopens any of them.
  3. The PR is behind origin/main by merges that share no file with it; no merge is owed for this record, and the queue rebuilds on current main.

Implemented-by: claude/issue-11168-lazy-blocks-form-layout
Reviewed-by: session_01VhxTqosz7wn54ahqyxgERT

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 1, 2026 10:52
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 1, 2026 10:52
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 1, 2026
Merged via the queue into main with commit 20d23be Oct 1, 2026
47 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-11168-lazy-blocks-form-layout branch October 1, 2026 11:07
huangyiirene pushed a commit that referenced this pull request Oct 1, 2026
Takes 20d23be, which carries PR #11335 (objectui#11168 slice 3), so the
object-master-detail-form.details parity row can be patched on this
branch (seat answer A). The merge touches none of this branch's files.

Claude-Session: https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh
Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Oct 7, 2026
…al) (objectstack-ai#11361)

Fixes objectstack-ai#11356

## Summary

`next` 16.3.3 carries GHSA-vcvr-r3jv-pc5j (critical, CVSS 9.5, "Remote
Code Execution in next/og ImageResponse"). The fixed release is 16.3.6.
On `main` at `2124d04111`, `apps/site/package.json` pins `"next":
"16.3.3"` and `pnpm-lock.yaml` resolves `next@16.3.3`. The surface is
reachable: `apps/site/app/og/docs/[...slug]/route.tsx` imports
`ImageResponse` from `'next/og'`, and the production build prerenders
184 PNGs through it.

This PR moves to the patched release. It supersedes Dependabot's objectstack-ai#11235
(manifest only, so every install step failed with
`ERR_PNPM_OUTDATED_LOCKFILE`) and objectstack-ai#11236 (a full re-resolution that gave
nanoid and vite each an extra physical copy). It adds no OSV or audit
exemption, and the lockfile is not hand-edited.

## Change

- `apps/site/package.json`: the `next` line only, `16.3.3` to `16.3.6`,
still an exact pin. No other objectui manifest declares `next`: `git
grep '"next"'` over every `package.json` at `2124d04111` has exactly one
hit, this line.
- `pnpm-lock.yaml`: regenerated with `pnpm install --lockfile-only`
under pnpm 10.31.0 (the `packageManager` version), starting from
`main`'s lockfile unchanged.

## Lockfile

55 lines changed on each side.

- `packages:` has 1767 entries before and after. 10 are removed and 10
added: `next`, `@next/env` and the eight `@next/swc-*` binaries, each
16.3.3 to 16.3.6.
- `snapshots:` has 1773 entries before and after. 14 are removed and 14
added: the same 10, plus the four snapshot keys that embed the `next`
version as a peer. Those are `fumadocs-core`, `fumadocs-mdx`,
`fumadocs-ui`, and `better-auth`, whose optional `next` peer resolves to
the workspace copy. None of the four changes version.
- Two importers change: `apps/site`, and `packages/auth` through the
`better-auth` snapshot key only (its specifier and version are
unchanged).
- The string `16.3.3` no longer appears anywhere in the lockfile.

## Verification

Every row below was measured on `76e9ee3`, this branch's head. `main`
was still at `2124d04111` when this PR was opened, so there was nothing
to merge.

The runs used Node 22.23.3. This container ships Node 22.22.0, and the
repository's `engine-strict=true` refuses it, because `jsdom@30.0.1`
requires `^22.22.2`. CI is unaffected: its `setup-node` asks for `22.x`,
which takes the newest 22 release. See Acceptance notes.

| Check | Result |
|---|---|
| `node scripts/check-lockfile-integrity.mjs --base-ref origin/main`
(the gate objectstack-ai#11236 failed) | exit 0, `VERDICT clean`: 1773 resolutions on
both sides, no backward move, no new copy. `--self-test` exit 0, 29
cases |
| `pnpm install --frozen-lockfile` | exit 0, and the installed
`apps/site/node_modules/next` reads 16.3.6 |
| Control leg: `main`'s lockfile with this branch's manifest, `pnpm
install --frozen-lockfile` | exit 1, `ERR_PNPM_OUTDATED_LOCKFILE` (the
objectstack-ai#11235 failure). Restored afterwards: `git diff HEAD` empty, blob hash
equal to HEAD's |
| `node scripts/check-lockfile-dedupe.mjs` (bare, the hard verdict) |
exit 0, `VERDICT deduped` |
| `pnpm turbo run build --filter='@object-ui/site'` (the `Build Docs`
command), under the shared verify lock, `--concurrency=2` | exit 0,
30/30 tasks, `Next.js 16.3.6 (Turbopack)`. The site task was a cache
miss, so it really ran: 559 static pages, including 184
`/og/docs/**/image.png` prerendered through `ImageResponse`, each a real
PNG |
| `pnpm --filter @object-ui/site type-check` (`fumadocs-mdx && next
typegen && tsc --noEmit`) | exit 0 |
| `pnpm --filter @object-ui/auth type-check`, then `pnpm exec vitest run
packages/auth/` (the importer whose snapshot key moved) | exit 0. 28
files, 282 tests passed |
| The 10 test files that read `pnpm-lock.yaml` or resolve through
`apps/site`: `ci-cd-pipeline-doc`, `docs-build-trigger`,
`check-installed-spec-pin-claims`, `check-lockfile-integrity`,
`check-lockfile-dedupe`, `check-pre-install-import-graph`,
`markdown-test-inputs`, `check-doc-links`, `check-changeset-presence`,
`MetadataService.specKeyReference` | 10 files, 479 tests passed |
| `node scripts/check-changeset-presence.mjs` | exit 0, "no changeset is
owed" |
| `check:control-bytes`, `check:new-line-citations`,
`check:pre-install-import-graph`, `check:phantom-deps`,
`check:unused-deps`, `type-check:coverage` | all exit 0 |
| `node scripts/check-governed-queue-guard.mjs --test
apps/site/package.json pnpm-lock.yaml` | `NOT GOVERNED` |

Left to CI: the repository-wide `pnpm lint` and `pnpm type-check`, the
eight test shards, coverage, `Build & E2E`, and `Bundle Analysis`. No
package source changes.

## OSV-Scanner, before and after

I ran OSV-Scanner v2.3.8 as `scan --offline-vulnerabilities
--lockfile=pnpm-lock.yaml`, against its offline npm database. The
binary's SHA-256 (`bc98e153…`) matches the release's checksum file.
objectui has no OSV workflow, so this is a reading, not a gate.

| Tree | Exit | Result |
|---|---|---|
| `main` at `2124d04111` | 1 | 62 rows, 1 Critical:
`GHSA-vcvr-r3jv-pc5j`, `next` 16.3.3 |
| this branch at `76e9ee3` | 1 | 61 rows, 0 Critical |

The two row sets differ by exactly one line: the `next` advisory is
gone, and no row is added. The other 61 rows are already on `main` and
are outside this card.

## Changeset

None. `apps/site` is `"private": true`, and no published package's
source or contract changes. `node scripts/check-changeset-presence.mjs`
settles it (exit 0, above).

## Acceptance notes

- **Overlap with draft objectstack-ai#11335** (another seat). It also touches
`pnpm-lock.yaml`, but only in the `importers:` block of a different
package (it adds an `@object-ui/sdui-parser` workspace link). Its hunks
are disjoint from this PR's. If git reports a conflict for whichever
lands second, regenerate with `pnpm install --lockfile-only`; do not
hand-merge.
- **Alert count.** The push banner reported 2 critical Dependabot alerts
on the default branch. OSV over `pnpm-lock.yaml` reads 1 Critical, the
one fixed here. Where the second one comes from was not measured.
- **Node floor.** Root `engines.node` declares `>=22.11`, and
`CONTRIBUTING.md` repeats it. But `jsdom@30.0.1` declares `^22.22.2 ||
^24.15.0 || >=26.0.0`, and `.npmrc` sets `engine-strict=true`. So on
Node 22.22.0, which the declared floor admits, `pnpm install
--frozen-lockfile` stops with `ERR_PNPM_UNSUPPORTED_ENGINE` naming
jsdom. This PR does not change that. It is reported to the dispatching
seat for a separate card. Locally I used a checksum-verified Node
22.23.3 from a scratch directory rather than turning `engine-strict`
off.

Session: `https://claude.ai/code/session_018gA1pE6eJtwHhqx72G8U9X`

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

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Oct 7, 2026
…tail's sort field is derived only (objectui#11070 round 9) (objectstack-ai#11376)

Refs objectstack-ai#11070
Clause-②: yes (narrowing). `MasterDetailDetailConfig.sortField` retires,
and `GridFieldMetadata` gains `sort_field`. It is priced in the
changeset.

Round 9 of objectui#11070: the seat's answer B on `sort_field`
(`5928627070`), plus `grid.mdx`'s Features prose. Claim `5929173058`;
dev session `session_01TdiauJaVCHuj45EzZGUxHh`. DRAFT: no ready, no
enqueue.

## What changes

- **(i) `sort_field` is declared** (`@object-ui/types`).
`GridFieldMetadata.sort_field?: string`. Its JSDoc says what it does
(the CHILD field the grid stamps with each row's index on every change,
so a drag-reorder persists; not one of the columns) and who produces it
(`deriveDetail` in `@object-ui/plugin-form`, through
`MasterDetailForm`). `GridField` drops `UndeclaredGridKeys` and reads
its config as `Partial` of `GridFieldMetadata` alone, so every key it
reads is declared there (`@object-ui/fields`, comments and one type
annotation; no runtime change).
- **(ii) The authored override retires** (`@object-ui/plugin-form`).
`MasterDetailDetailConfig` has no `sortField` member. The derived value
travels on the form's internal per-detail record
(`DetailEntry.sortField`, beside the existing internal
`amountFieldDef`), set only from `derived.sortField`, and the grid
config reads `sort_field: entry.sortField`. The `d.sortField ??
derived.sortField` bridge is gone from both derive branches. The amount
override (`amountField`) is untouched.
- Why `DetailEntry` and not a second config type: the authored object is
spread into the resolved config (`{ ...d, … }`), and a fully configured
detail keeps it as is. A resolved-config type that carried `sortField`
would therefore still carry a stray authored `sortField` at runtime on
that path, which is the dual read this round removes. On `DetailEntry`
the field has one writer, the derivation.
- **(iii) `grid.mdx`.** The typed fence writes `sort_field: 'position'`,
and the field-level key list documents it the way round 8 documented
`total_field` and `add_label`. The Features list was audited bullet by
bullet against `GridField` (below).
- **Pins.** The 11144 row "an authored sort field wins over the derived
one" retires with the member; its three derived-`position` / amount rows
stay green. The 8071 members pin is re-counted and re-pointed: header
prose (eleven members, four renamed on the way, `sort_field` derived),
row 2 with `sortField` moved to the off-list set (not read), a new row
2c (derived pick vs a written `sortField`, with a lit control), and a
compile-time block (an exact `keyof MasterDetailDetailConfig` list, a
`@ts-expect-error` refusal of `sortField`, and a lit control).
`GridField.fieldKeys-11070` gains a `sort_field` drag-reorder row and
its no-key control, and its `declared` fixture writes `sort_field`.
- **Changeset.** `.changeset/11070-grid-sort-field-round9.md`:
`@object-ui/types` minor (one new optional member, a feature),
`@object-ui/plugin-form` minor (a published member retires: breaking,
priced minor under the fixed group's policy, stated),
`@object-ui/fields` patch (no runtime change). Dated, append-only notes
on the two pending changesets this round makes false:
`11070-grid-field-keys-round8` ("`sort_field` … stays read and
undeclared") and `11144-inline-columns-sort-amount` ("or a detail's own
`amountField` / `sortField` still wins").

## The `sortField` census (measured first)

objectui base `1a88ce22` (exact-word `git grep -w`), objectstack
`origin/main` `e952cff5` (same query; control `amountField` hits 12
files there).

| Site | Role | Before | After |
|---|---|---|---|
| `deriveDetail` (`plugin-form/src/deriveMasterDetail.ts`) | writer
(derived) | the child's first `position` / `sort_order` / `sequence` /
`line_no` / `line_number` / `sort` field | unchanged: the one producer |
| Authored documents, example apps, catalog fixtures, docs (objectui) |
writer | 0 | 0 |
| objectstack: spec, examples, docs, `os migrate meta` conversions |
writer | 0 (`sortField` and `sort_field` both) | 0 |
| `attachInlineSubforms` (app-shell) | writer of `subforms[]` | writes
`childObject` / `relationshipField` / `inlineMode` / `title` / `columns`
/ `amountField`; no `sortField` | unchanged |
| Drawer / Modal `subforms` types | declared shape | no `sortField`
member | unchanged |
| `object-master-detail-form` registration `inputs`
(`plugin-form/src/index.tsx`) | declared shape | `details`: array, no
members | unchanged |
| 11144 pin, row "an authored sort field wins" | test writer |
`sortField: 'line_no'` | row retired |
| 8071 pin, row 2 | test writer | `sortField: 'position'` as a member |
an off-list key, asserted not read |
| `MasterDetailDetailConfig.sortField` | declared member | declared |
retired (compile error) |
| `MasterDetailForm` resolve effect, both derive branches | reader |
`d.sortField ?? derived.sortField` | `derived.sortField`, on
`DetailEntry` |
| `MasterDetailForm` grid config | reader | `sort_field: d.sortField` |
`sort_field: entry.sortField` |
| `MasterDetailForm`, fully configured detail (no derive) | reader | the
authored `sortField` only | none (nothing is derived there, before or
after) |
| `GridField` `cfg.sort_field` | reader | through `UndeclaredGridKeys` |
declared `GridFieldMetadata.sort_field` |

objectui#11144's thread names no user of the authored override: the
issue body says `attachInlineSubforms` carries only `inlineColumns` and
`inlineAmountField`, and the claim's "an authored `amountField`
(`inlineAmountField`) or `sortField` still wins" states a symmetry, not
a writer.

## `grid.mdx` Features audit

| Bullet | Against `GridField` | Disposition |
|---|---|---|
| Table Display: clean tabular layout | true | kept, with "every row
shown" |
| Pagination Preview: first 5 rows, "Showing X of Y" | false: every row
renders (`rows.map` in both the read-only and editable bodies), and no
"Showing" text exists in the widget | removed |
| Type-Specific Rendering: each column renders according to its type |
true for editable cells; in read-only mode only `date` / `datetime` /
`time`, `file` and `lookup` cells are formatted (probe below) |
corrected to say exactly that |
| Read-Only Mode: full table view without editing | true | kept |
| Responsive: scrollable for many columns | true (`overflow-x-auto`,
per-type minimum widths) | kept |

## M3 (`scripts/measure-strict-authoring-face.mjs --json`, types dist
built)

| | base `1a88ce22` | `5376e31a` (this diff) |
|---|---|---|
| nodes / strict-refused / strict-only / red today | 2229 / 77 / 46 / 31
| 2229 / 77 / 46 / 31 |
| documents / strict-refused whole tree / red-today documents | 601 / 72
/ 24 | 601 / 72 / 24 |
| per-component rows | identical | identical |

Only `rendererMentionsOfUndeclaredKeys` (a raw word count over renderer
sources: `change` and `key` each one lower, `field` and `schema` each
one higher, from this diff's comments) and `unloadableModules`
(environmental: app-shell unbuilt at base) differ. H3 holds.

## Ablations (predictions written before each run; every mutation
through objectstack `scripts/ablation-replace.mjs`, anchor hit as
declared, blob changed, restored to the HEAD blob with `git diff HEAD`
empty)

- **A1, an undeclared `sort_field` read** (the member deleted from
`GridFieldMetadata`, types rebuilt; dist marker 1 to 0). Predicted and
observed: fields `tsc --noEmit` TS2339 on `cfg.sort_field`; fields `tsc
-p tsconfig.test.json` TS2353 on the `declared` fixture and on `grid({
sort_field })`; `check:doc-snippets` 1 failed block, `grid.mdx` TS2353
`sort_field`. Restore leg: types rebuilt, dist marker back to 1, `git
status --porcelain` empty.
- **A2, an authored `sortField` type-checking again** (`sortField?:
string;` planted back on `MasterDetailDetailConfig`). Predicted and
observed: plugin-form `tsc --noEmit` green; `tsc -p tsconfig.test.json`
exactly 2 errors in the 8071 file, TS2322 on the member list and TS2578
on the now-unused `@ts-expect-error`.
- **A3, the `??` bridge restored** (both `sortField: derived.sortField,`
sites to `(d as any).sortField ?? derived.sortField`). Predicted and
observed over the three touched test files: 1 failed, 24 passed; the
failure is row 2c, `expected 'line_no' to be 'position'`.
- **A4, the false Features bullet restored** in `grid.mdx` (the "Showing
X of Y" pagination claim). Predicted and observed: `check:doc-snippets`,
`check:doc-fences`, `docs:check-links`, `check:doc-types` and
`check:doc-example-ids` all green. No gate can see a prose bullet; this
round's correction is held by review only.

## Local verification (final head `2c59d3b5`, the merge of `origin/main`
`c82ff391` on top of `5376e31a`)

- Builds through the verify lock: the `check-doc-snippet-types
--build-filter` closure, `turbo --concurrency=2`, 35 of 35 tasks.
- `type-check` green for `@object-ui/types`, `@object-ui/fields` and
`@object-ui/plugin-form` (each `tsc --noEmit` plus its test project;
`--listFiles` shows the touched test files are in those programs). The
type-checker set is these three: `MasterDetailDetailConfig` is
referenced only inside plugin-form (a comment in an `apps/console` test
names it), and `GridFieldMetadata` gains one optional member, which no
consumer can break.
- vitest (`--maxWorkers=2`, through the lock): `packages/plugin-form/`
159 files, 1836 passed and 1 skipped; `packages/types/` plus every
`GridField*` / `LineItems*` test, 325 files and 8103 tests passed.
- Green: `check:doc-snippets` (768 of 768 blocks), `check:doc-fences`,
`docs:check-links`, `check:control-bytes`, `check:changeset-claims`,
`check:pending-changeset-literals`, `check-changeset-presence`,
`changeset:check`, `check-changeset-overwrite` (it reports the two dated
notes, its case 2), `check:new-line-citations` (0 new),
`check:test-path-roots`, `check:doc-types`, `check:doc-example-ids`,
`check:vi-mock-specifiers`, `check:readme-exports`,
`check-type-check-coverage`. `check-governed-queue-guard --test`: NOT
GOVERNED.
- The claims gate names two pending changesets on touched files
(`6357-basechema-bind-declaration` on `grid.mdx`,
`6625-retire-fieldmeta-decimals` on `GridField.tsx`). Both paragraphs
were read and still hold.
- Narrowed lint (`eslint --no-inline-config --format json`) over the 6
changed ts/tsx files: 0 errors, 68 warnings, the same per-file warning
counts as the base blobs. Population: the config's `**/*.{ts,tsx}`
blocks (the `.mdx` and `.md` files match none). File count 6, read from
the json. Invariance: no `parserOptions.project` or `projectService`,
and no rule in `eslint-rules/` reads the filesystem, so this diff cannot
move a verdict on an untouched file.

## Hypotheses

- H1 holds: 0 authored writers in either repository; the writers are
`deriveDetail` and two test fixtures.
- H2 holds: a type split was enough, with no renderer change, for every
child that has a sort-named field and is derived. A fully configured
detail derives nothing, before and after; that is objectui#11144's own
carrier note, not a change here.
- H3 holds: M3 does not move.

## Stop condition reached: the `object-master-detail-form.details`
parity row (excluded file, PR objectstack-ai#11335)

`apps/console/src/__tests__/registry-inputs-spec-parity.test.ts` is not
touched. Its `MEMBER_PINS['object-master-detail-form.details']` row
stays mechanically green (the locator needs only the pin file to exist
and to name the block and the key). Its `pins` prose, though, is now
false in two places: "five members reach the grid through ONE
hand-written object and four are RENAMED on the way (`sortField` to
`sort_field`, …)" and "names five of the twelve members the renderer
reads". The brief says to stop and report before that row changes, so
the row is reported, not edited, and the dev report puts it to the seat
with options.

## Acceptance notes (not this round's scope)

- Read-only `GridField` formats only `date` / `datetime` / `time`,
`file` and `lookup` cells. A scratch probe (uncommitted, removed)
rendered one row `{ amount: 1234.5, status: 'paid', qty: 1234.5 }` with
`currency`, `select` (option label `Paid`) and `number` columns:
read-only cells read `1234.5`, `paid`, `1234.5`; the same row in list
mode reads `1,234.5`, `Paid`, `1,234.5`. No reach was measured (the docs
page's read-only example writes values that read the same either way),
so it is noted here and not filed.
- `grid.mdx`'s "Integration with Advanced Grid" list (outside the
Features list) offers add/remove rows, drag-and-drop reordering and
formula columns as plugin features, which the basic grid also has. Left
as is.
- `docs/adr/0001-master-detail-subform.md` (governed) names `sort_field`
and `row[sortField] = index`. An ADR records the decision taken; left as
is.

## Patch (answer A) — added by the seat

- Seat answer A (`5929940616` on objectui#11070) to this PR's open
question. After PR objectstack-ai#11335 landed, `origin/main` `20d23bef` was merged in
as `36313b7f`; the merge touches none of this PR's files.
- `09d3ebf0` edits exactly the two clauses of the
`object-master-detail-form.details` row's `pins` prose in
`apps/console/src/__tests__/registry-inputs-spec-parity.test.ts`: "four
members … each RENAMED … beside `columns` and the DERIVED `sort_field`",
and "five of the eleven members".
- The same commit moves the 8071 pin's fully configured `sortField`
check from row 2 into row 2c, so row 2 keeps exactly the two off-list
members that the parity row describes.
- The follow-up dev report is `5930257276`. The dev's contract forbids a
dev PATCH of this body, so the seat wrote this section.

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

---------

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

Development

Successfully merging this pull request may close these issues.

2 participants