Skip to content

feat(types): TypeScript node types for the authored properties-bag carriers (flex and the eight object-* blocks), by reference, joined into AuthoringNode (objectui#11468) - #11477

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-11468-bag-carrier-node-types
Oct 2, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-11468-bag-carrier-node-types

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #11468

Clause-②: yes

F3 of objectui#8347 (that card stays open and waits on this one and its two siblings). Dispatched by the domain:spec @ objectui seat (objectui#10217) and implemented by an os-dev subagent of session https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC. Generic parameters below are spelled in words (for example "a record of string to never"), because GitHub strips tag-shaped text from a body.

What changes on the published face

@object-ui/types adds ten exported types. Each is derived by reference from its zod arm, and none is restated:

export what it is
ObjectQLPublicBlockNode one member per arm of ObjectQLPublicBlockComponentSchema, each the arm's own input: the derivation PublicBlockNode uses
ObjectMetricBlockNode, ObjectMasterDetailFormBlockNode, ObjectTimelineBlockNode, ObjectFormBlockNode, ObjectMapBlockNode, ObjectChartBlockNode, ObjectGanttBlockNode, ObjectGridBlockNode the member of ObjectQLPublicBlockNode for each type
FlexBlockNode the FlexBlockSchema arm's input

AuthoringNode gains two members: it is now PublicBlockNode | ObjectQLPublicBlockNode | FlexBlockNode | ElementTextInputNode | ElementRecordPickerNode | PageDocumentNode. The type expression of SchemaRenderer's schema prop does not change (BaseSchema | AuthoringNode | string | null | undefined), and the prop accepts the nine carriers through AuthoringNode. No zod schema, renderer or runtime code changes.

Accept set, before and after (TypeScript face only)

  • Before: an authored object-map, object-grid or other carrier node, or a flex node, had no TypeScript type. The docs annotated it BaseSchema, whose index signature accepts any key, so a misspelled bag key compiled.
  • After: the node type is the arm's own input. It refuses a misspelled bag key, a bag member written flat on the node, and a node-level children or body, as the zod arm and the strict authoring face do.

The one derivation change: the bag is read closed, like the arm

ClosedArmInput read an arm's shape with the object config left closed, but it read the properties member as it stood. flex and object-chart have no ComponentPropsMap row. Their bags are the flat mirrors' own members (FlexPropsBag, ObjectChartPropsBag), kept .passthrough() for the tolerant face (objectui#11276). So the shape-only read left each of those bags with an index signature of unknown, and a misspelled bag key compiled. The derivation now reads the bag's shape too (ClosedBag), so the TypeScript face is closed where the strict authoring face closes it.

A spec-row bag is already closed. PublicBlockNode is unchanged, and an exact type equality pins that. Measured: among the nine carrier arms, the shape-only read leaves exactly two bags open (flex and object-chart). Among the 27 public blocks it leaves none (the element:divider bag is a record of string to never, which refuses every key).

Pins

All in packages/types/src/__tests__/authoring-nodes-bag-carriers-11468.test.ts. The type-level assertions are judged by the package's type-check test leg:

  1. No arm is lost: the derived type sets equal the arms' own, for the eight-arm union and for flex.
  2. Each named alias is its arm. The carrier members of AuthoringNode are exactly ObjectQLPublicBlockNode | FlexBlockNode.
  3. No AuthoringNode member has an open bag. The control: the shape-only derivation leaves exactly object-chart and flex open.
  4. PublicBlockNode equals the shape-only derivation, so closing the bag changes nothing for a spec row.

The behaviour checks use the 11364 pin file's method: BaseSchema's index signature is stripped at the type level, so they run now and after the removal.

  • object-map: the spelled bag compiles. A misspelled bag key and a flat prop are refused.
  • flex: the bag is closed. A flat prop, a node-level children and body are refused. An inline literal compiles at the prop.
  • object-chart: the bag is closed.
  • A runtime check that every carrier arm and both mirror bags are zod objects (the derivation's premises).

Ablations (run once and restored, not kept as tests)

Each ran through ablation-replace.mjs in wrap mode against the committed head, with the two authoring-nodes pin files compiled by tsc:

  • Bag closure removed (the properties member read as the arm declares it): 3 errors. The no-open-bag assertion fails with TS2344, and the closed-bag @ts-expect-error directives in the flex and object-chart cases become unused (TS2578). The PublicBlockNode equality stays green, as predicted.
  • The two members removed from AuthoringNode: 3 errors. The join assertion fails with TS2344, and both inline literals (object-map and flex) are refused at the post-removal prop with TS2353 on properties.

The restore was proven each time: the blob equals the HEAD blob, and git diff HEAD is empty. The first attempt of each was refused by the tool before it measured anything, because the replacement text was a substring of the anchor. Each was restored and re-run with a distinct replacement.

An earlier fifth type-level assertion ("every carrier fits the post-removal prop") still passed with the join removed, because a typed constant fits BaseSchema structurally either way. The flex inline literal replaced it, and that literal does discriminate.

The doc corpus, re-judged under the P1 mutation

The P1 mutation deletes BaseSchema's index signature and its docblock (wrap mode, restored afterwards) and rebuilds @object-ui/types. The dist then carries 1 signature, on ComponentRendererProps. The readings are from check:doc-snippets, check:doc-examples, check:doc-types and the types examples' tsc.

reading doc-snippets, mutated F3 diagnostics types examples tsc, mutated
base d93e53f5, all at base 70 of 776 blocks failed, 75 diagnostics 46 3 errors
head, node types plus re-annotations 30 of 776 failed, 33 diagnostics 5 (H3 below) 1 error (the F1 header-bar cast)

The base column matches the parent's P1 census (46 F3 diagnostics in 75).

  • Removed: 41 F3 diagnostics and 1 F1 diagnostic (the types README crumbs, explained under "flex child list"). The 41 are 39 re-annotated blocks plus 2 inline SchemaRenderer object-grid literals in objectos-integration.mdx, which compile through AuthoringNode with no edit.
  • Added: none.

Without the mutation, at head: check:doc-snippets judged 776 of 776 blocks with 0 failed, and check:doc-examples and check:doc-types exit 0.

Every document re-judged:

  • content/docs/plugins/plugin-map.mdx: 7 blocks re-annotated as ObjectMapBlockNode. 5 keep BaseSchema (H3).
  • content/docs/plugins/plugin-gantt.mdx: 10 blocks, ObjectGanttBlockNode.
  • packages/plugin-grid/README.md: 11 blocks, ObjectGridBlockNode. Their satisfies ObjectGridProps bag checks are kept.
  • packages/plugin-form/README.md: 2 blocks, ObjectFormBlockNode.
  • packages/plugin-map/README.md: 2 blocks, ObjectMapBlockNode.
  • packages/plugin-gantt/README.md: 1 block, ObjectGanttBlockNode.
  • content/docs/guide/objectos-integration.mdx: 1 block, ObjectGridBlockNode. Its 2 inline literals needed no edit.
  • content/docs/utilities/index.md and content/docs/utilities/data-objectstack.mdx: 1 block each, ObjectGridBlockNode.
  • Root README.md: 1 block, ObjectFormBlockNode.
  • packages/types/README.md: the nested flex block is FlexBlockNode, and its two satisfies FlexLayoutProps bag checks are replaced ("flex child list" below). "Authoring the spec's blocks in TypeScript" lists the new types.
  • packages/types/examples/dashboard.ts: both flex nodes are FlexBlockNode (the outer one annotated, the inner one with satisfies).

H3: five plugin-map.mdx blocks author a key the arm refuses

The blocks are clickableMap, storeLocator, deliveryMap, propertyMap and serviceMap. Each writes onMarkerClick (a function) on the node, beside the bag. The page teaches "a schema built in code carries it on the node, beside the bag".

  • No contract declares the key: not ObjectMapBlockSchema, not the TS twin ObjectMapSchema. The strict face refuses it as an unrecognized key.
  • Annotated ObjectMapBlockNode, each block failed check:doc-snippets with TS2353 on onMarkerClick (measured). The type was not widened.
  • So these five keep BaseSchema. They compile while the signature exists, and under the mutation each still fails (on properties, the first excess key TypeScript reports).
  • This is the handler-key class that objectui#8347's last PR already carries for plugin-calendar.mdx (onEventClick, onDateClick). The P1 census did not see these five, because TypeScript reported properties first.
  • Each block's bag already type-checks as ObjectMapBlockNode, so the annotation can move once the handler key has a disposition.

flex child list

FlexBlockSchema declares properties.children as a list of unknown, because the page walk judges each entry. The node type carries that by reference, so FlexBlockNode does not check a child written inline in a flex bag. The types README and the dashboard example name each inline child's type with satisfies (FlexBlockNode, HeaderBarSchema). That is also what removes the types README crumbs diagnostic that P1 classed as F1: the header-bar child is now checked against HeaderBarSchema, which declares crumbs. Nothing is widened or narrowed.

Gates, all at head 333f5a89 unless named

  • pnpm --filter @object-ui/types run type-check (src, examples and tests): exit 0.
  • pnpm --filter @object-ui/react run type-check (the reader of AuthoringNode through SchemaRendererProps): exit 0.
  • pnpm exec vitest run --maxWorkers=2 packages/types/, from the worktree root: 331 files and 8766 tests passed. The two authoring-nodes pin files run by name: 2 files and 11 tests passed.
  • check:doc-snippets (776 of 776 judged, 0 failed), check:doc-examples and check:doc-types: exit 0, after a rebuild of @object-ui/types at head.
  • check:control-bytes, check:new-line-citations (0 new), check:test-path-roots, check:pending-changeset-literals, check-changeset-no-major, check-changeset-presence (1 package, 1 changeset), check:readme-exports, check:doc-fences, check:doc-example-ids, check:doc-example-readers, check:spec-symbols, check:handler-key-reads, check:unreferenced-sources: exit 0.
  • check:changeset-claims is report-only. It names 12 pending changesets that cite a file this change touches. I read each paragraph, and none is falsified (the edits change annotations only).
  • Targeted eslint: the package's own eslint on the 4 changed TS files gives 0 errors and 0 warnings. The repository lint run is CI's.
  • Not run: check:component-surface-parity and check:sdui-registration-pins, because no registration input moved. Other packages' vitest was not run, because no runtime code changed.

Overlap with in-flight work

  • objectui#11466 (F1) also edits packages/types/src/index.ts (read uncommitted in its worktree). Whichever lands second merges main. F1's slot union includes AuthoringNode, so the nine carriers flow into it.
  • objectui#11467 (F2) shares no file with this change.

Acceptance notes

  • The flex bag's child list is a list of unknown on the TypeScript face, by reference to the arm. A nested node written inline there is unchecked unless the author names its type. This is recorded for objectui#11466's slot typing and is not changed here.
  • Toolchain: the container's Node v22.22.0 is below jsdom 30's engine floor, so every command ran with a SHA256-verified nodejs.org v22.23.3 tarball on PATH from this issue's scratch directory. Nothing system-wide changed.

Generated by Claude Code

claude added 4 commits October 2, 2026 14:23
…carrier outside the public blocks, by reference, joined into AuthoringNode (objectui#11468)

The eight ObjectQLPublicBlockComponentSchema arms and FlexBlockSchema had a zod
arm and no TypeScript node type. Each is now its arm's own authoring input,
derived as PublicBlockNode is: ObjectQLPublicBlockNode (one member per arm),
the eight ObjectXBlockNode aliases picked from it, and FlexBlockNode. All of
them join AuthoringNode, so SchemaRenderer's schema prop accepts them.

The derivation now reads a bag's shape the way it reads the arm's. flex and
object-chart have no spec row, so their bags are the flat mirrors' own
.passthrough() members; reading the arm's shape alone left each bag open. A
spec-row bag is already closed, and the 27 public blocks are typed exactly as
before (pinned).

Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC
Co-authored-by: Claude <noreply@anthropic.com>
…lexBlockNode; changeset (objectui#11468)

The example annotated its root flex node BaseSchema and checked each bag with
satisfies FlexLayoutProps, which typed the nested flex node's bag against
SchemaNode. Both flex nodes are now FlexBlockNode, the arm's own input.

Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC
Co-authored-by: Claude <noreply@anthropic.com>
…ode types, not BaseSchema (objectui#11468)

Each doc block that authors an object-grid, object-form, object-map,
object-gantt or flex node in its properties bag now annotates the arm's node
type (ObjectGridBlockNode, ObjectFormBlockNode, ObjectMapBlockNode,
ObjectGanttBlockNode, FlexBlockNode). The bag is checked against the arm, and
the block keeps compiling once BaseSchema's index signature goes. The types
README lists the new node types; its nested flex example names each inline
child's type with satisfies, because the flex bag's child list is unknown[] as
the arm declares it.

Five plugin-map.mdx blocks keep BaseSchema: each also writes onMarkerClick on
the node, a key the object-map arm does not declare and its node type refuses.

Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC
Co-authored-by: Claude <noreply@anthropic.com>
… structural fit (objectui#11468)

A typed FlexBlockNode constant fits BaseSchema-without-its-index-signature
structurally whether or not AuthoringNode includes the carriers, so that
assertion could not fail. An inline flex literal where SchemaRenderer takes a
schema compiles only through AuthoringNode.

Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added documentation Improvements or additions to documentation package: types plugin tests labels Oct 2, 2026
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 12 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/10387-header-bar-unread-keys-retired.md

  • names packages/types/examples/dashboard.ts → packages/types/examples/dashboard.ts — edited by this change

    The in-tree examples that used these keys (packages/types/examples/dashboard.ts, the @object-ui/types README and the ObjectOS integration guide) now use crumbs and actions.

.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/11117-datasource-objectname-waiver.md

  • names content/docs/utilities/data-objectstack.mdx → content/docs/utilities/data-objectstack.mdx — edited by this change

    Docs: in content/docs/utilities/data-objectstack.mdx, the "Narrowing a saved view" example writes its filter as the rule array [{ field, operator, value }] the spec takes, and the ElementDataSource excerpt types filter as ViewFilterRule[]. The data-binding guide and the schema reference say that a bound node needs no objectName.

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

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

    GanttConfig itself gains nine members and is a published type, exported by name from packages/types/src/index.ts: lockField, objectField, summaryExtent, defaultCollapsedDepth, borderColorField, dependencyTypes, timeZone, exportFileName, interactions. The entry file's diff is empty only because the export list already named the type — the widening happened at the declaration.

.changeset/6470-gantt-dependency-field-deprecated.md

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

    packages/plugin-gantt/README.md's ObjectGanttSchema example authored the singular; it was the only in-repo site that did, and it now authors the plural with the alias named as legacy. No runtime code, fixture, example app or catalog schema authored it.

.changeset/6475-gantt-block-face-declared.md

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

    Maintainer ruling, objectui#6475 (2026-08-27), Option A — enforce as-is, immediately, no warning window (the startup-stage no-gradualism rule, objectstack#12668: transitions do not get phased windows without named external-user evidence, and none exists here). A census of every gantt block reachable through ObjectGanttSchema in this repository — the examples/schema-catalog fixtures, content/docs/plugins/plugin-gantt.mdx, and the published skills/objectui/guides/page-builder.md guide — found zero blocks missing the trio.

.changeset/6650-listcolumn-wrap-implemented.md

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

    @objectstack/spec declares ListColumn.wrap and describes it to authors as "Allow text wrapping", and packages/plugin-grid/README.md shows it in its authored-column example. No renderer anywhere implemented it. Long cell text stayed clipped to one line, with no error, no warning and no feedback of any kind — a promise made at authoring time and silently broken at render time.

.changeset/7323-adapter-factory-return.md

  • names content/docs/utilities/data-objectstack.mdx → content/docs/utilities/data-objectstack.mdx — edited by this change

    The README's note saying the page could not yet teach the factory's shape is removed, and the four sections built on the adapter-only members (Metadata Caching, Connection State Monitoring, Batch Operation Progress, Troubleshooting → Cache Issues) now continue from Basic Setup's createObjectStackAdapter(…) call instead of declaring the class by hand. The docs-site page content/docs/utilities/data-objectstack.mdx is corrected the same way: its prose, its factory signature fragment and its "hold the class type to reach these" section described the old narrow return, and its Mutations and Troubleshooting examples told the reader to construct the class by hand to reach members the factory now declares. src/adapterFactoryReturn.types.test.ts pins the card's TS2339 reproduction inverted, with two controls: the adapter-only members stay absent from DataSource (fires on option B), and the widened return stays assignable to DataSource (swappability kept).

.changeset/7780-object-kanban-record-source.md

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

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

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

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

  • names content/docs/utilities/data-objectstack.mdx → content/docs/utilities/data-objectstack.mdx — edited by this change

    • packages/plugin-list/src/ListView.tsx generates the node as groupBy: laneField. objectDef loads asynchronously, so laneField is undefined on every load until it lands, and stays undefined whenever the object offers no stageField hint and none of status / stage / state / phase. The renderer serves that node; both published faces refused it. - content/docs/utilities/data-objectstack.mdx documents an object-kanban node that is exactly { type, dataSource }, with no groupBy. ⚠️ This one is weaker and is cited for what it is: that fragment is still refused after this change, at RECORD_SOURCE_REQUIRED, because dataSource is not a rung of the record-source ladder. It shows a lane-less board is a documented authoring; it is not a document this change admits.
  • names data-objectstack.mdx → content/docs/utilities/data-objectstack.mdx — edited by this change

    Correction, 2026-10-01 (objectui#11117). The second bullet above says the data-objectstack.mdx fragment { type, dataSource } is still refused at RECORD_SOURCE_REQUIRED because dataSource is not a rung. That no longer holds: objectui#11117 counts a node's dataSource.object binding as a record source, as the registration's ElementDataSourceGate already did, so the fragment now parses.

.changeset/component-meta-converge-and-deprecate-alias-5893.md

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

    ⚠️ Dated note, 2026-09-29 — the one-occurrence reading above has since moved — objectui#10979. "In-repo, PluginComponentMeta has exactly one occurrence — its own export line" above is the reading of the commit this change landed on (575b71c850), where a whole-tree search for the name returned that one line, in packages/types/src/index.ts. This change then added its own pins and this entry, and later in this same release objectui#5674's retirement of the sibling alias PluginComponentInput (PR objectui#10826) added mentions in its test and its entry. Re-measured on main at 2eaf5be27, the name occurred on 19 lines in 7 files. In non-test source it appeared only on the export line and on one doc-comment line in plugin-scope.ts, and the one file that imported it was this change's own component-meta-single-declaration.test.ts. The text above is kept as the reading of this change.

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

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

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

Compared the checked-out tree with 6007dd4e4 (merge-base with origin/main): 15 file(s) changed outside .changeset/, read against 2023 pending declaration(s) that publish a body (2652 pending in total). · run

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3550.7 KB 3574.6 KB
Main entry chunk (gzip) 150.4 KB 350 KB
Entry file index-Cjyv0H-d.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.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 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) 569.61KB 136.76KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 230.46KB 63.94KB
fields (index.js) 261.25KB 66.11KB
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) 7.14KB 2.92KB
layout (index.js) 39.36KB 11.18KB
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.04KB 3.92KB
plugin-calendar (index.js) 53.17KB 15.46KB
plugin-charts (index.js) 83.53KB 22.84KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 141.23KB 38.00KB
plugin-designer (index.js) 231.28KB 48.76KB
plugin-detail (index.js) 245.27KB 64.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 172.73KB 44.50KB
plugin-gantt (index.js) 179.14KB 45.06KB
plugin-grid (index.js) 232.66KB 63.77KB
plugin-kanban (index.js) 49.51KB 15.54KB
plugin-list (index.js) 116.60KB 28.96KB
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) 38.80KB 11.71KB
plugin-tree (index.js) 14.51KB 5.15KB
plugin-view (index.js) 90.23KB 22.73KB
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) 120.63KB 39.56KB
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 (authoring-nodes.js) 0.20KB 0.19KB
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 (dashboard-widget-layout.js) 2.06KB 0.96KB
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) 5.07KB 2.39KB
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: 333f5a89d20f9a259483053a9153efda38649e6a
Local-runs: none

Inputs: card #11468 (body and both comments: the claim 5954372420 and the os-dev-report 5955478276), PR #11477 (body, the 16-file list, the net diff against main), the head's check-runs, and the head's own sources read through the contents API at 333f5a89 (authoring-nodes.ts, index.ts, zod/objectql.zod.ts, zod/layout.zod.ts, zod/public-blocks.zod.ts, zod/index.zod.ts, base.ts, the 11364 pin file, package.json, tsconfig.test.json, .changeset/config.json, .changeset/11364-public-block-ts-faces.md, packages/react/src/SchemaRenderer.tsx, content/docs/plugins/plugin-map.mdx). Nothing checked out, built or run.

① Derived judgments

  1. Ten exported types, by reference: RIGHT. ObjectQLPublicBlockNode is ClosedArmInput over (typeof ObjectQLPublicBlockComponentSchema)['options'][number]; at head that schema is z.discriminatedUnion('type', [...]) over the eight BaseSchema.extend arms (objectql.zod.ts 4609-4618; the arms at 3606, 3942, 4000, 4087, 4185, 4295, 4459, 4554), so .options exists and every arm is a ZodObject (a .superRefine on an object arm returns the same object type in zod 4; pin 1 proves no arm maps to never). FlexBlockNode is the same derivation over FlexBlockSchema (layout.zod.ts 491, also BaseSchema.extend). All three values are re-exported from zod/index.zod.ts (lines 106, 353, 497), so the pin file's imports resolve. The eight aliases are Extract picks off the union by type. Nothing is restated: the card's "not hand-written twins" holds.
  2. AuthoringNode gains ObjectQLPublicBlockNode | FlexBlockNode: RIGHT, and the accept-set reading is right in both directions. SchemaRendererProps.schema at head is BaseSchema | AuthoringNode | string | null | undefined (SchemaRenderer.tsx 1058), unchanged in text, so the nine carriers now enter the prop through AuthoringNode with no react edit. No member was removed and BaseSchema stays in the prop, so every value accepted before is accepted after; the widening is input-side only. The changeset says the read side (TS2339 on a key one closed member lacks) needs narrowing first; the head's Type Check is the gate for whether any in-repo reader is hit by that.
  3. ClosedBag, the one mechanism change: RIGHT and bounded. It matches a properties member of shape ZodOptional over a ZodObject and re-reads the object's shape with the default, closed, config. At head propsBag returns row.optional().describe(...) (public-blocks.zod.ts 253), so every carrier's bag matches the pattern. The two bags the PR names as open are the only non-row bags: ObjectChartPropsBag = z.looseObject(ObjectChartSchema.shape).omit(...) (objectql.zod.ts 3492) and FlexPropsBag = FlexSchema.omit(...).extend(...) off the .passthrough() mirror (layout.zod.ts 430). The control pin asserts exactly 'object-chart' | 'flex' are open under the shape-only read, and _PublicBlocksUnchanged pins PublicBlockNode equal to the pre-11468 derivation, so the 27 public blocks are untouched. Reach: the bag's top-level keys only; an object nested inside a bag keeps its own posture. That is what the card asked for (a misspelled bag key refused) and no more.
  4. The post-removal pins discriminate: RIGHT. BaseSchema in base.ts declares no properties member, only [key: string]: any (line 598), so WithoutIndexSignature over it refuses properties, and each @ts-expect-error on a PostRemovalSchema literal is real on both the BaseSchema member and the AuthoringNode member. Same method as the 11364 pin file. The pins are judged by tsc -p tsconfig.test.json (include src/**/*.test.ts), the third leg of the package's type-check script, so the head's Type Check run is their gate verdict.
  5. The doc corpus: RIGHT. Per-document counts in the diff match the PR body: plugin-map.mdx 7, plugin-gantt.mdx 10, plugin-grid README 11 (the satisfies ObjectGridProps bag checks kept), plugin-form README 2, plugin-map README 2, plugin-gantt README 1, objectos-integration 1, utilities index 1, data-objectstack 1, root README 1, types README 1 block plus the "Authoring the spec's blocks in TypeScript" bullets; dashboard.ts moves to FlexBlockNode (outer annotated, inner satisfies), and the satisfies FlexLayoutProps bag checks it drops are subsumed, since the bag's members are FlexSchema's own by reference. Doc Snippet Type Check, Doc Component Type Check, README Export Check and Build Docs are green on the head.
  6. Two wording nits, no contract effect. (a) FLEX_BAG_CHILDREN at head is z.union([z.array(z.unknown()), SchemaNodeSchema]).optional() (layout.zod.ts 405), so on FlexBlockNode the member is a list of unknown OR one node; the PR body, the types README comment and the dashboard.ts comment say "a list of unknown". The conclusion drawn (an inline child in the list is unchecked unless its author names a type with satisfies) still holds. (b) ObjectQLPublicBlockNodeOf is module-private where PublicBlockNodeOf is exported; the eight aliases cover every pick, so nothing is missing from the face, only the symmetry.

② Semver level

.changeset/11468-bag-carrier-node-types.md: '@object-ui/types': minor. Matches the diff: ten new exports and an input-side widening of AuthoringNode; nothing removed or renamed; no zod, renderer or runtime change. @object-ui/react carries no entry: its declaration text is unchanged, and it sits in the same fixed group as @object-ui/types in .changeset/config.json, so it versions with types regardless. The precedent .changeset/11364-public-block-ts-faces.md listed react because the prop's own text changed then; it does not here. Changeset Bump Policy, Changeset Declaration, Changeset Fixed Group Check and Changeset Claim Re-read are green on the head. The changeset's read-side paragraph states the one consequence of a wider union honestly.

Clause-②: yes. The card's claim and the PR body both say yes, and the diff publishes ten new exported types on @object-ui/types; this at-tier record is the one owed on the landing head.

③ Boundary flags

The report's deviations and out_of_scope_findings; its open_questions is empty.

  • Toolchain (a Node 22.23.3 tarball on PATH): local only, nothing in the tree; the head's check-runs are the gate verdicts. Answered.
  • Zone 3 taken differently (no nine-member alias; ObjectQLPublicBlockNode mirrors the zod union and FlexBlockNode stands alone, both joined): consistent with the card's A, "a node type for each of these arms, by reference, joined into AuthoringNode". Accepted.
  • ClosedBag growth: judged in ① 3, pinned a no-op for spec rows. Accepted; without it the card's "misspelled bag key refused" would be false for flex and object-chart.
  • File surface beyond the claim: packages/types/src/index.ts is the export carrier the claim's "exported node types" implies, and the types README bullets sit in a listed document. The satisfies HeaderBarSchema in the README nested-flex block removes the crumbs diagnostic P1 classed F1, which objectui#11466 may also hold: a merge-order matter inside the same seat (whichever lands second merges main), not an escalation. Accepted; the seat carries this note to spec(types,react,core): every node slot is SchemaNode (= BaseSchema), so after objectui#8347's removal an inline child carrying its own type's keys stops compiling; type node slots and SchemaRenderer's schema prop as the declared node union #11466.
  • H3, five plugin-map.mdx blocks keep BaseSchema (clickableMap, storeLocator, deliveryMap, propertyMap, serviceMap): verified at head (lines 361-517). Each authors a node-level onMarkerClick, the page itself says "the spec's row declares none", and the key appears in none of objectql.zod.ts, objectql.ts, public-blocks.zod.ts or base.ts. Annotating them ObjectMapBlockNode would mean widening the type by hand, which the card forbids, so leaving them is the right call under A. The residual, 5 of the 46 F3 diagnostics still failing under the P1 mutation, is the handler-key family objectui#8347's last PR already carries (plugin-calendar's onEventClick / onDateClick), and the dev noted it rather than filing. Accepted, with one instruction: because the report says "noted, not filed", the seat records the five blocks on objectui#8347 so the parent's ACCEPT names them before its last PR, rather than leaving them to be rediscovered under the removal. Fixes #11468 is acceptable, since the card's deliverable (node types by reference, joined, the docs annotated wherever the contract permits) is met; the seat may switch to "Part of" if it reads the five as this card's.
  • Measurement hygiene (ablation-replace refusals re-run, dist briefly stale on the base leg, the fifth pin replaced by the discriminating flex literal, the worktree removed): local, restored, no tree effect. Answered.
  • Out-of-scope 2 (the flex child list is unknown on the TS face): a correct observation for objectui#11466, with the ① 6a wording nit.

Check-runs on the head: read 2026-10-02T15:20:46Z, 30 completed (success, or skipped for the coverage and dependabot jobs) and 11 in progress; read 2026-10-02T15:26:23Z, 33 success, 3 skipped, 6 in progress (Type Check, Test shards 2, 3, 4, 5, 8); final read 2026-10-02T15:27:48Z, just before this post: 37 success, 3 skipped, 2 in progress (Test shards 2/8 and 4/8), nothing red. Bearing on the contract: Type Check, the gate for every type-level pin and for the read-side consequence in ① 2, completed success at 15:26:36Z; Doc Snippet Type Check, Doc Component Type Check, README Export Check, Build Docs, Lint, Spec Main Shape Gate and the four changeset checks are success. The two pending shards can only carry the one runtime premise test (every carrier arm and both mirror bags are zod objects), whose premises I read directly off the head's sources above; six of the eight shards and Test (dist pins) are already success. No red or pending run bears on this verdict.

Implemented-by: claude/issue-11468-bag-carrier-node-types
Reviewed-by: session_01XvhGmGAP79ZB8swnkapxPC

VERDICT: PASS

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation package: types plugin tests

Projects

None yet

2 participants