Skip to content

feat(types): the six held public blocks (action:* and element:definition-list / repeater) validate by their spec ComponentPropsMap rows (objectui#10872, batch 4) - #11180

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-10872-held-blocks-by-reference
Sep 30, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-10872-held-blocks-by-reference

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #10872
Clause-②: yes (widening: six namespaced component types move from refused-at-type to accepted by their spec rows)

Batch 4 of objectui#10872. The six ADR-0080 public blocks held back until @objectstack/spec carried a ComponentPropsMap row for each (action:button, action:icon, action:group, action:menu, element:definition-list, element:repeater) are armed by reference to their 17.5.0 rows, the batch-1 method. The card stays open for record:line_items (no spec row yet) and the flat-props / envelope-key batch.

⚠️ Draft with one known red, deliberately: packages/types/src/__tests__/zod-mirror-parity.test.ts census (every exported const in ../zod/ is either a registered pair or an excluded one) names exactly the six new exports. The claim puts that file off limits (draft PRs objectui#11115 and objectui#11125 hold it) and says to stop and report the shape, so the six rows are not in this diff. They are in the os-dev report on the card. Measured: with those six rows added, the census passes (37/37, in a scratch copy of the test), and git merge-file of the rows against the heads of objectui#11115 (8628df2fd9) and objectui#11125 (a5884b4030) reports 0 conflicts.

What changed

  • packages/types/src/zod/public-blocks.zod.ts: six arms, BaseSchema + the type literal + properties, which is the block's ComponentPropsMap row passed straight through stripImportedDefaults (no member restated). All six join PublicBlockComponentSchema; the barrel re-exports them.
  • action:button / action:icon also declare the two on* keys their renderers read off the node, because check:handler-key-reads requires every such read to be an arm member:
    • onClick: handlerKeyRefusal(..., 'runtime-slot', ...). The renderer calls it only when it is a function (a code-composed schema), and the row does not declare it. This is the ButtonSchema.onClick precedent.
    • flat onSuccess: aliasKeyRefusal naming properties.onSuccess, where the row declares the post-success { navigate, openIn } block. This is the record:alert flat-body precedent (batch 3). The spec's own PageComponentSchema refuses the flat key too, as mis-layered.
  • Ratchet: NAMESPACED_REFUSED_AT_TYPE falls 397 to 391, and ARMED_PUBLIC_BLOCKS_10872_BATCH_4 names the six. The ratchet's validator import and refusedAtType (objectui#11069's hunks) are untouched.
  • Page pin: objectui validate passes a page whose action:button is the quick-start's "Add Actions" node, read from content/docs/guide/quick-start.md (the node AGENTS.md Add default props to all components to prevent collapse in designer #4 teaches). It also passes a page of all six in the spec's properties spelling. The control is an undeclared bag prop, which is refused and named.
  • Arm-list consumer sweep, the edits it required:
    • imported-defaults-8317.test.ts measures the six rows at the import boundary. They are placed away from objectui#11115's insertion point, and none carries a default or a z.lazy, so the lazy count stays 4.
    • public-block-arms-10872.test.ts gets VALID_BAG rows for the six. Its identity leg now compares by definition, because 17.5.0 publishes these rows as lazy proxies whose methods run on the real schema, so .optional() wraps the object behind the proxy.
    • The handler-key census: action:button and action:icon leave the objectui#9573 alias population, which is now view:form, view:grid, view:list. Their four reads are judged on their own arms. Both the gate docblock and the gate test are amended.
  • Changeset 10872-held-public-block-arms.md ('@object-ui/types': minor). A dated note on the pending 10872-public-block-zod-arms.md, whose "Not armed in this release" bullet this falsifies. The zod README lists the six.

The spec round's registration notes, re-measured at main 0ffc423b1

The arm follows the row, and the row follows the read points. Items 1–3 and 5 are pinned at the validator in held-public-block-arms-10872.test.ts; item 4 is a renderer reading.

  1. action:group publishes name, which is read nowhere. Its size enum carries md, which inline mode hands raw to the primitive. Still true. The arm refuses both, with the row's prescription.
  2. element:definition-list publishes columns as the strings '1' / '2', and the renderer compares the number 2. Still true. The arm refuses '2' and accepts 2.
  3. element:repeater advertises fields[].label (its TS interface and its registration description), which is never rendered. Still true. The arm refuses it.
  4. action:menu spreads ...rest after disabled={loading}. Reproduced with a probe through the real SchemaRenderer and registry: an in-flight action (an autoTrigger member whose handler never settles) shows the spinner on both mount channels, but the trigger is disabled: true only on a direct registry mount and disabled: false through SchemaRenderer, whose forwarded disabled: undefined wins. It is reported on the card as a class-(a) finding; it is not fixed here.
  5. objectName: the 17.5.0 rows declare it on action:button and action:icon. On action:group / action:menu it rides each member (actions[] members are open records) and is refused at container level. Pinned.

The registrations did not move in this PR. Items 1–3 sit in apps/console/src/__tests__/registry-inputs-spec-parity.test.ts's objectui#11111 ledger, which the maintainer's decision 3 = B routes to objectui#11168 ("nothing else may own an entry"). objectui#11168's slice 1 (the action:* family, including renderers/action/) was claimed at 2026-09-30T05:39Z, before this card's claim. So the registration moves, and the item-4 renderer fix, would duplicate in-flight work. They are reported on the card instead. This PR does not touch registry-inputs-spec-parity.test.ts or any renderer.

Tests and gates (all after the final commit, c1ea153ad)

  • pnpm exec vitest run packages/types/ packages/cli/src/__tests__/ packages/components/src/renderers/action/ packages/components/src/renderers/basic/ apps/console/src/__tests__/registry-inputs-spec-parity.test.ts: 351 files, 7678 passed, 1 failed. The failure is the zod-mirror-parity census above, and nothing else.
  • Arm-list consumer sweep: 54 files, 2878 passed, 0 failed. It covered:
    • app-shell block-config-schema-parity-8216, block-config, definition-list-item-keys-8279 and PageBlockInspector.retiredBlockProps;
    • examples/schema-catalog/test/;
    • console public-contract, html-tier-manifest, component-input-union-specimens and unfulfilled-chart-stubs-8760;
    • plugin-dashboard metricCardRegisteredInputsStrictFace-11022 and plugin-map bareMapKeyRetired-10393;
    • the scripts tests that read the zod directory.
  • Every test that names check-handler-key-read-sites: 5 files, 132 passed.
  • type-check for @object-ui/types (three programs; tsconfig.test.json lists the three changed test files) and @object-ui/cli, after pnpm --filter '@object-ui/cli^...' build: exit 0. @object-ui/components is not in the diff.
  • eslint on the 8 changed code files: 0 errors, 0 warnings, 0 ignored (--format json). eslint.config.js configures no type-aware parsing, so this diff cannot move a verdict on an untouched file.
  • These gates exit 0: check-changeset-presence, check:control-bytes, check:new-line-citations (0 new), check:spec-symbols, check:handler-key-reads, check:changeset-claims (report-only), check:pending-changeset-literals, check-changeset-no-major.
  • NOT MEASURED: check:readme-exports. Reason: it needs every package's dist and reported "the population COLLAPSED" on this unbuilt tree. CI runs it.
  • Ablation, both legs through ablation-replace.mjs: the anchor hit once, the write was verified on disk, and the restore was proven (blob equals HEAD, git diff HEAD empty).
    • Leg A drops the six from the union: 60 of 179 tests go red across the three pin files.
    • Leg B deletes action:button's onClick / onSuccess members: check:handler-key-reads exits 1 naming both reads, and 2 held-arm tests go red.

Overlap

git merge-tree of this head with objectui#11069, #11115 and #11125: my files merge cleanly with all three. objectui#11069 conflicts in packages/cli/src/commands/check.ts, a file this PR does not touch; the same conflict appears against the base alone.

Acceptance notes

  • The taught action:button node is flat, and the strict face refuses it. AGENTS.md Add default props to all components to prevent collapse in designer #4, the quick-start, the layout guide and the other guides write label / actionType / target on the node. The tolerant face objectui validate runs today passes that node (which is what the page pin reads). StrictAnyComponentSchema refuses exactly actionType and target, as PageComponentSchema does, and this is pinned as a reading. objectui#11069 moves objectui validate onto the strict face. When it next merges main, this PR's taught-node page pin turns red there. That is the flat-props question for action:*, which this card holds, and it is raised on the card.
  • Content channels are not narrowed. The six arms carry no objectui#9256 children / body refusal: that measurement never covered them. They keep BaseSchema's channels, and the module docblock and README say so.
  • Inference, not reproduced, not filed: inline action:group also spreads ...rest onto its wrapping div. This is the item-4 shape on a div.

Generated by Claude Code

…entPropsMap rows (objectui#10872, batch 4)

action:button, action:icon, action:group, action:menu, element:definition-list
and element:repeater each get an arm whose `properties` bag is the block's
@objectstack/spec 17.5.0 ComponentPropsMap row, by reference. action:button
and action:icon also declare the two on* keys their renderers read off the
node: `onClick` as a runtime slot, and a flat `onSuccess` refused with a
pointer to `properties.onSuccess`.

The namespaced ratchet falls 397 -> 391; the handler-key census judges the
action reads on their own arms; the import-boundary differential measures
the six rows.

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

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 21 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/5903-objectgantt-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 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/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/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/7344-handler-string-any-mirrors.md

.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/7694-chart-series-chart-type-alias-refusal.md

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

    • Before: series: [{ name: 'revenue', chartType: 'line' }] validated green through @object-ui/types/zod (safeValidateSchema, objectui check / objectui validate, any pipeline that keeps parse()'s output) — and the key was gone from the output, so a consumer of the parse result drew that series in the chart's own family, precisely what the author was overriding. On the TypeScript face the key was merely an excess property on a fresh literal; a widened object carrying it assigned structurally. - After: the same document REFUSES at series[i].chartType (issue code invalid_type) with one message on both channels — the parse-time issue and the .describe() metadata: Unrecognized key(s) on this chart series: \chartType`. Did you mean `chartType` → `type`? …followed by the reason and the remedy. Writetype: 'bar' | 'line' | 'area'. On the TypeScript face ChartDataSeries.chartTypeis a?: nevertombstone, so both the fresh literal and the widened assignment aretsc errors. - **Both written** ({ type: 'bar', chartType: 'line' }) is refused at chartTypealone — the key is not folded ontotypeand no precedence is minted between the two spellings. - **Which documents to scan.** The narrowing does not stop atChartDataSeriesSchema; it reaches every document through the parents that embed it — ChartSchema.series (zod/data-display.zod.ts, z.array(ChartDataSeriesSchema)) and, one level further out, ReportSectionSchema.chart (zod/reports.zod.ts, ChartSchema.optional()). Authors meet it through safeValidateSchema() (zod/index.zod.ts, which parses AnyComponentSchema) and through the CLI's objectui validate command (packages/cli/src/cli.ts). In practice: every chartnode'sseries[], and every report section whose chart` carries one.

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

  • 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 index.zod.ts → packages/types/src/zod/index.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/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/8344-node-recursion-point-redirect.md

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

    Two mechanical notes for anyone editing the wiring. AnyComponentSchema is built in zod/index.zod.ts from all 13 category modules and 14 modules import zod/base.zod.ts, so the arm cannot be an import — z.lazy defers evaluation, not the module graph. It is a written option slot that index.zod.ts fills inside AnyComponentSchema's own initializer, and it is a z.union option rather than a z.lazy holder because z.lazy memoises its getter: a holder would let whichever module graph parsed first decide the accept set for the whole process. Both constraints are measured, and the reasoning lives on defineNodeComponentUnion in zod/base.zod.ts.

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

    Two mechanical notes for anyone editing the wiring. AnyComponentSchema is built in zod/index.zod.ts from all 13 category modules and 14 modules import zod/base.zod.ts, so the arm cannot be an import — z.lazy defers evaluation, not the module graph. It is a written option slot that index.zod.ts fills inside AnyComponentSchema's own initializer, and it is a z.union option rather than a z.lazy holder because z.lazy memoises its getter: a holder would let whichever module graph parsed first decide the accept set for the whole process. Both constraints are measured, and the reasoning lives on defineNodeComponentUnion in zod/base.zod.ts.

  • names zod/public-blocks.zod.ts → packages/types/src/zod/public-blocks.zod.ts — edited by this change

    ⚠️ Dated note, 2026-09-28 — a second category module — objectui#10872. Later still in this same release zod/public-blocks.zod.ts joins them as well and imports zod/base.zod.ts too, so both counts in the mechanical note above are two higher, not the one the objectui#10859 note says; the reason the arm cannot be an import is unchanged. The rest of this entry, and that note, are kept as their readings.

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

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

    Why 49 is acceptable here where 2 was a defect: the 2 were data blocks a consumer had a standing reason to validate on their own, and objectui#7917 (PR feat(types): re-export BreadcrumbSchema and ObjectTreeSchema from the zod barrel (objectui#7917) #8777, open at the time of writing, and the holder of zod/index.zod.ts) exists to export exactly those. The 47 added here are HTML primitives and two input aliases — they have no per-tag consumer to serve, and exporting a SemanticElementSchema / HtmlElementSchema pair would publish a NAMED authoring surface (z.enum families, not per-tag schemas) that this card's ruling does not cover: the ruling is "arm the registered renderers", not "add public exports to @object-ui/types". So the metric is left to move and said out loud instead. ⇒ Whoever next runs that measurement should expect 49, and whoever wants the number back down should treat naming these families as its own decision. If PR feat(types): re-export BreadcrumbSchema and ObjectTreeSchema from the zod barrel (objectui#7917) #8777 lands first, the same movement reads 0 to 47.

.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/8598-zod-subpath-single-bundled-module.md

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

    The defect. This package declares "sideEffects": false, and src/zod/index.zod.ts fills the node recursion point as the initializer of its AnyComponentSchema const. tsc emitted that barrel as a module whose only other content is re-exports — so a bundler resolving import { CardSchema } from '@object-ui/types/zod' followed the re-export to dist/zod/layout.zod.js, needed nothing from the barrel's own body, and the flag let it drop that body whole. The fill went with it, and every child slot then validated against the pre-spec(types): redirect the node recursion point from BaseSchemaCore to AnyComponentSchema — measured at 9 newly-refused documents, and it drops 118 phantom strict refusals #8344 BaseSchemaCore arm — the ~21 base keys and nothing type-specific — with no error, no warning and no way for the guard inside the dropped code to notice. objectui#8344 shipped that window DECLARED and pointed here to close it.

.changeset/9067-zod-barrel-named-arms.md

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

    Two new exported symbols, both on packages/types/src/zod/index.zod.ts:

.changeset/9447-detail-view-handler-key-refusal.md

  • names scripts/check-handler-key-read-sites.mjs → scripts/check-handler-key-read-sites.mjs — edited by this change

    The read path was measured, not assumed. Unlike 'detail', which registers DetailView raw, 'detail-view' registers DetailViewRenderer — a data-source gate. The gate does not strip handler keys: it hands DetailView either the node unchanged or a shallow spread whose only overwrites are the binding keys, so an authored value arrives at schema.onNavigate / schema.onAddComment by identity and is called. Driven end to end through the real SchemaRenderer in detail-view-handler-slots-9447.test.tsx, which also records why scripts/check-handler-key-read-sites.mjs is green here and always was: its transitive hop stops at the wrapper.

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

  • 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/9659-node-recursion-point-inert-clause.md

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

    Prose amended where this change falsified it, ⛔ not rewritten. zod/index.zod.ts said defineNodeComponentUnion "wraps it rather than replacing it" — true only while a wrapper existed. zod/base.zod.ts's note on the loose parameter bound still names ChatbotSchema's record body in the present tense; the paragraph is kept for the reason it records and carries an AMENDED note saying the exclusion set that pin reads is now empty.

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

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

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3569.8 KB 3607.4 KB
Main entry chunk (gzip) 149.5 KB 350 KB
Entry file index-Seo8DB_G.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) 559.46KB 134.18KB
core (index.js) 9.94KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 227.08KB 62.96KB
fields (index.js) 261.19KB 66.27KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.35KB 9.18KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.58KB 4.90KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.25KB 2.17KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 52.17KB 15.06KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 138.73KB 37.05KB
plugin-designer (index.js) 215.78KB 44.42KB
plugin-detail (index.js) 234.78KB 62.36KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 172.30KB 44.19KB
plugin-gantt (index.js) 172.43KB 42.85KB
plugin-grid (index.js) 229.83KB 63.15KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.82KB 28.67KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 32.26KB 9.42KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 90.43KB 22.76KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.55KB 39.23KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.17KB 2.73KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 22.61KB 7.40KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.82KB 7.15KB
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

…blic-block arms (objectui#10872, batch 4)

Six EXCLUSIONS rows, inserted immediately before the
PublicBlockComponentSchema entry: each arm is BaseSchema + its type literal
+ `properties`, which is the block's ComponentPropsMap row by reference, so
no TS declaration in this package restates the node. action:button and
action:icon also name their onClick runtime slot and flat-onSuccess alias
refusal. Nothing else in the file moves.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3569.8 KB 3607.4 KB
Main entry chunk (gzip) 149.5 KB 350 KB
Entry file index-Seo8DB_G.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) 559.46KB 134.18KB
core (index.js) 9.94KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 227.08KB 62.96KB
fields (index.js) 261.19KB 66.27KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.35KB 9.18KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.58KB 4.90KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.25KB 2.17KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 52.17KB 15.06KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 138.73KB 37.05KB
plugin-designer (index.js) 215.78KB 44.42KB
plugin-detail (index.js) 234.78KB 62.36KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 172.30KB 44.19KB
plugin-gantt (index.js) 172.43KB 42.85KB
plugin-grid (index.js) 229.83KB 63.15KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.82KB 28.67KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 32.26KB 9.42KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 90.43KB 22.76KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.55KB 39.23KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.17KB 2.73KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 22.61KB 7.40KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.82KB 7.15KB
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: 53a1db444075cdbc19a212695914cde2accf5abb
Local-runs: none

PR objectui#11180, card objectui#10872, batch 4 (Part of #10872, Clause-②: yes, widening). Read: the card body and all 30 comments (rulings 5862923926 / 5868698636 / 5882953316, unlock 5903895615, claim 5904933022, amendment 5905916922, triage 5905925465, dev reports 5905878792 and 5906006602), the PR body, the 12-file list, the net diff against main at 0ffc423b1, and the check-runs on the head. Files at the head read for context only: public-blocks.zod.ts, tombstone.zod.ts, imported-defaults.ts, check-handler-key-read-sites.mjs, action-button.tsx, action-icon.tsx, quick-start.md, the batch-1 changeset. Nothing built, run or re-run.

Check-runs on the head (filter=latest, every run completed by 07:17Z): 42 runs, 37 success, 3 skipped (dependabot and the two coverage matrix rows, the expected skips), 2 failure: Test (shard 1/8) and the Test aggregate, which reads each shard and fails because shard 1 did (objectui#9499). Shard 1's annotation names the one red test: scripts/__tests__/markdown-test-inputs.test.ts, "every candidate the scanner finds has been adjudicated, and every entry exists", with two rows: packages/cli/src/__tests__/registered-types-validate-ratchet-10859.test.ts (unadjudicated-document: it resolves content/docs/guide/quick-start.md, which is in neither its reads nor its notRead) and packages/types/src/__tests__/held-public-block-arms-10872.test.ts (unadjudicated-test: it reads markdown the ledger does not describe). Type Check, Lint, the other seven shards, dist pins, Build and E2E, Build Docs, Spec Main Shape Gate, every changeset gate and every doc gate are green.

① Derived judgments

Findings (the FAIL):

  1. A gate on the head is red, and the diff caused it. The two taught-node pins read content/docs/guide/quick-start.md at test time (the ratchet file's new QUICK_START path and held-public-block-arms-10872.test.ts's taughtActionButton()), which makes each file a markdown reader the markdown-test-inputs ledger must adjudicate; neither is. scripts/__tests__/markdown-test-inputs.test.ts fails on 53a1db444 with exactly those two rows, so Test (shard 1/8) and the Test aggregate are red. Batch 1 and batch 3 both ran markdown-test-inputs.mjs --audit as a gate ("all adjudicated"); the batch-4 gate list omits it, which is how the reader shipped unadjudicated. Remedy for the fix round, on this branch: adjudicate both readers in the ledger (the ratchet file's existing entry gains content/docs/guide/quick-start.md in its reads, with what it reads: the json fence under "Add Actions"; a new entry for held-public-block-arms-10872.test.ts with the same read), then re-run markdown-test-inputs.mjs --audit and name its reading in the report. If the ledger file is on a governed surface, the queue guard says so at the push. A new head, a new record; nothing else in this diff needs to move.

Judged right, each named:

  1. The six arms are by reference, and join the union and the barrel — right. Each is BaseSchema.extend({ type: z.literal(...), properties: propsBag(type, stripImportedDefaults(SpecXPropsSchema)) }); propsBag is row.optional().describe(...), so no member is restated. The six spec bindings are new named imports from @objectstack/spec/ui (17.5.0). All six are added to PublicBlockComponentSchema and re-exported from index.zod.ts. Accept-set change implied: six type literals move from invalid_union at type to accepted on both faces, each bag judged by its spec row (strict), with the read-point refusals the rows carry (group-level name, size: md on group and menu, string columns on the definition list, fields[].label on the repeater, objectName at container level). Nothing else in the union, in BaseSchema or in any other arm moves, so this is a pure widening. The identity claim (stripImportedDefaults(row) is row for all six) is the walker's documented identity property (a subtree with no ZodDefault and no z.lazy comes back reference-equal) and is pinned per row in the new test; CI answers it.
  2. The action:button / action:icon handler-key members are required, and correct. The renderers read schema.onClick (called only when it is a function; action-button.tsx 159, action-icon.tsx 122) and schema.onSuccess (forwarded to the runner; 298 / 195). check-handler-key-read-sites.mjs judges every on* read against the arm registered under that type: declared = arm.members.has(key), undeclared is a finding unless ledgered. Before this diff the two types had no arm, so the four reads sat in the objectui#9573 UNMIRRORED-ALIAS population (reported, not judged); with an arm, each read must be a declared member, so the members are required, not optional. Dispositions: onClick is honoured only as a function from a code-composed schema, never from JSON, so handlerKeyRefusal('onClick', 'runtime-slot', ...) is the ButtonSchema.onClick / page:tabs.onTabChange precedent, right. onSuccess is data the row declares in the bag ({ navigate, openIn }), so the flat spelling is refused with aliasKeyRefusal('onSuccess', 'properties.onSuccess', ...), the batch-3 record:alert flat-body precedent, and the spec's own PageComponentSchema refuses the flat key (pinned). Right. Reading: a flat onSuccess is refused on both faces while flat actionType / target pass the tolerant face unjudged; that asymmetry is by construction (an on* read must be a declared member) and matches batch 3. Neither key was waived into KNOWN_UNDECLARED_READS (pinned).
  3. The ratchet, 397 to 391 — right. Six armed namespaced keys, each pinned as a NAMESPACED_KEYS member now accepted at type, in ARMED_PUBLIC_BLOCKS_10872_BATCH_4; the bare pin (73) is untouched. The file's five hunks touch the docblock and constant, the batch-4 list, one new it, the QUICK_START path and three new page tests; the validator import and the body of refusedAtType (objectui#11069's hunks) appear in no hunk. The docblock's "LOWER it ... never raise it" rule is kept.
  4. The action:button page pin through objectui validate — right. The pin reads the first json fence under ### Add Actions in content/docs/guide/quick-start.md (present at the head: { type: action:button, label, actionType: url, target }), asserts non-vacuity on type, wraps it in a page beside a page:header, and expects exit 0 and Schema is valid. It passes the tolerant face because label is a BaseSchema key and actionType / target are undeclared on the node, which the tolerant face leaves unjudged. The second pin passes a page of all six in the bag spelling, and the control refuses an undeclared bag prop by name (inventedProp10872b4), so the two accepting rows cannot pass for the wrong reason. Right in what it judges, with the reading in ③ 4; wrong in the obligation it derives, finding 1: a test that reads a markdown document is a reader the ledger adjudicates, and this one is not.
  5. The six EXCLUSIONS rows — exactly the census's need, correctly worded. The census refuses an exported const in ../zod/ that is neither a registered pair nor excluded; the six new exports are the six keys (public-blocks.zod.ts#X). They sit immediately before the PublicBlockComponentSchema entry, whose reason says its members are accounted for individually above, so the placement is load-bearing and right. Wording: the four plain rows say BY REFERENCE, BaseSchema + literal + properties IS the row, no TS declaration in this package restates the node (true: the batch-1 census found no TS declaration of any of the 28 in @object-ui/types); the button and icon rows add the two handler members; none carries the objectui#9256 clause, which is right because these arms carry no channel refusal. The rows in the diff are byte-identical to the rows the report quoted and the amendment granted. One hunk, +18, nothing else in the file.
  6. Consumer-file edits — right. imported-defaults-8317.test.ts: six IMPORTED rows with aliased spec imports, each expected to be an identity crossing (no default, no z.lazy; the lazy count stays 4), placed away from objectui#11115's insertion point. public-block-arms-10872.test.ts: six VALID_BAG rows, and the identity leg now compares _zod.def rather than the instance. That is sound: .optional() builds a ZodOptional whose innerType is the schema it was called on, and a 17.5.0 row published as a proxy answers .optional() on the object behind it, so unwrap() returns that object, not the proxy; a definition object is per instance (clones get a fresh def), so def identity is instance identity modulo the proxy, which is exactly the property wanted, and a non-proxy row answers the same way, so the leg is not weakened for the earlier rows; the new pin also asserts the key sets are equal. The check-handler-key-read-sites docblock (two prose sites) and test (alias population 5 to 3; a new leg pinning the four reads as declared members on ActionButtonBlockSchema / ActionIconBlockSchema, onClick disposition runtime-slot, not re-keyed onto the bare button / icon arms) follow by construction: an armed type is not an alias, as page:tabs was in batch 1. index.zod.ts: six exports and the header. The zod README: 21 to 27 (7 page + 10 record + 4 element + 6), the six listed, the objectui#9256 sentence scoped to the arms it measured, the two handler keys named. The dated note on 10872-public-block-zod-arms.md is append-only (frontmatter untouched) and its claim is accurate: the "Not armed in this release" bullet's last two items are the two element: blocks and the four action: controls, and record:line_items alone stays refused.
  7. The new pin file and the two ablation legs — right. held-public-block-arms-10872.test.ts pins: union membership under the literal; the row exists and PageComponentSchema accepts { type } (the two element: types are in the vocabulary); bag IS the row (strip identity, def identity, key set); the minimal node on both faces, with an unarmed-literal control; the read-point refusals with accepting controls; objectName on the two singles and per member on the containers; onClick refused as custom with RUNTIME SLOT on both faces and refused in the bag; flat onSuccess refused as invalid_type with the remedy naming properties.onSuccess, the bag accepted, the spec refusing the flat key; the taught node read from the quick-start (non-vacuity), passing the tolerant face in a page, passing both faces in the bag spelling, and the strict face refusing exactly [actionType, target], as the spec does. Ablation, read from the reports: leg A (the six dropped from the union) turned 60 of 179 tests red across the three pin files; leg B (action:button's two members deleted) made check:handler-key-reads exit 1 naming both reads and 2 held-arm tests red; both restores proven by blob and an empty git diff HEAD. Both legs turn the pins in the predicted direction. CI answers the green side: the types shards are green, so every pin in this file passes on the head; the file's one defect is finding 1 (its quick-start read is unadjudicated).
  8. Public surface: @object-ui/types/zod gains six named exports and PublicBlockComponentSchema gains six members; @object-ui/cli changes only a test. No registration, renderer, registry-inputs-spec-parity.test.ts or governed path is in the diff (verified on the file list).

② Semver level

.changeset/10872-held-public-block-arms.md: '@object-ui/types': minor. Right: the diff widens AnyComponentSchema's accept set by six type literals and adds six exports, nothing that parsed is refused, and the batch-1 precedent for the same act (10872-public-block-zod-arms.md, twenty literals, twenty-one exports) is minor. The other released package touched, @object-ui/cli, changes only a test file, so one changeset is the right count (Changeset Declaration, Bump Policy and Fixed Group Check green on the head). The prose, read as a review face: every observable claim is pinned in the diff. The six exports and union membership (pin 1); the strict face accepting the same documents (pin 4); each read-point refusal (group name, size: md on group and menu, string columns, fields[].label); element:repeater requiring properties.object with no waiver (pinned; it declares no dataSource); the two handler keys and their remedies; the flat-prop paragraph (the taught node passes objectui validate today, the strict face and the spec refuse its flat actionType / target, the bag spelling passes both faces); record:line_items still refused at type (no arm). "Nothing that parsed before is refused now" is true: the six literals were refused whole before this diff, and no other arm or BaseSchema moved. The dated note on the batch-1 changeset is accurate (① 6). Clause-②: yes is stated in the changeset, in the PR body and in the claim, all three as a widening.

③ Boundary flags

Batch-4 report 5905878792, deviations (4) and open questions (3); patch-round report 5906006602, none.

  1. Registrations and renderers not moved (deviation 1, open question 1): answered by amendment 5905916922 item 2 under maintainer ruling 5902351047 on objectui#11111 (decision 3 = B): the ledger entries action:group.name, action:group.size md, element:definition-list.columns and the repeater member pin have one owner, objectui#11168, whose slice 1 claimed the action:* registrations and renderers before this card's claim. Zone 1's "or reported" branch is taken; verified that the diff touches no registration, renderer or registry-inputs-spec-parity.test.ts. Right. The validator already refuses the four unread keys, so no author is misled meanwhile.
  2. Continued past "stop and report" on the mirror-parity file (deviation 2, open question 0): the claim's stop was honoured in substance (the rows were supplied as text, not edited in), the amendment granted option A by region, and the patch round inserted the rows byte-identically in one +18 hunk. Serial: git merge-file and merge-tree against objectui#11115 (bdd950ec6d) and feat(types): the object-form zod mirror declares the members its TypeScript twin declared (objectui#6152, round 1) #11125 (a5884b4030) report 0 conflicts, with a non-vacuity check that both PRs' added lines survive the merge; whichever lands second merges main. Answered; nothing further.
  3. The consumer files beyond the order's surface (deviation 3): ratified by the amendment; each edit is additive and judged right in ① 6. The one held file, imported-defaults-8317.test.ts (objectui#11115), merges cleanly per the dev's measurement.
  4. The taught-node pin kept deliberately (deviation 4, open question 2, and the objectui#11069 out-of-scope note): the fork (flat vs properties for action:*) is with triage (5905925465, pm:retriage), and the amendment says it does not block this PR; the pin passes on today's tolerant face, which is what objectui validate runs. Answered: acceptable, because the three pins read the taught node from the quick-start rather than hard-coding a flat node. Under triage option A (the docs migrate to the bag) the ratchet's page pin keeps passing and the two flat readings (node.actionType non-vacuity; the strict face refusing [actionType, target]) move with the docs; under option C the page pin goes red on objectui#11069's next merge of main, which is the intended visible form of the fork. Escalated to the seat, not blocking: record on objectui#11069 (or on the triage request) that the taught-node pins in registered-types-validate-ratchet-10859.test.ts and held-public-block-arms-10872.test.ts are the readings that move with the fork, so a red there is not read as a regression of this PR.
  5. The action:menu in-flight disabled override (out-of-scope, class a, public door, reproduced through the real SchemaRenderer and registry): the amendment routes it to objectui#11168 with the registrations. Answer: the routing is not sufficient on its own; it needs its own card. objectui#11168's mandate under ruling 5902351047 is the registry-inputs parity ledger, and a measured runtime defect ({...rest} carrying disabled: undefined after disabled={loading}, the objectui#9131 shape) is not a ledger entry; "noted, not filed" leaves a class-(a) public-door finding living only in a report comment, where triage cannot grade it. This card's own precedent is objectui#10909 (batch 2's finding, filed by the seat as its own card) and objectui#9131 (the same shape on button and icon, its own card). Escalated to the seat: file it as a finding card with the report's evidence and dedupe words, serial behind objectui#11168 slice 1 on action-menu.tsx (the unreproduced inline action:group ...rest-onto-div inference rides it as a note). Not a defect of this PR; does not block.
  6. The six arms carry no children content-channel refusal (out-of-scope): children passes both faces on them though no renderer reads it. This is the state every batch-1 arm was in before objectui#9256's slice narrowed them, and that card's method is measure-then-narrow per arm; a later narrowing is a Clause-② narrowing with its own record. The module docblock and README scope the objectui#9256 claim to the arms it measured, so nothing false is published. Answered: right for this batch; carrier is this card, as the dev says. Escalated to the seat: name it as a later batch of this card (beside the flat-props / envelope-key batch) so it is not lost.
  7. Housekeeping for the seat, not a finding: the PR body still carries the "Draft with one known red, deliberately" paragraph and dates its gates to c1ea153ad; the patch round closed that red on 53a1db444. The body is not the contract, but it is stale, and the seat's ACCEPT should PATCH that paragraph before ready.

Implemented-by: claude/issue-10872-held-blocks-by-reference
Reviewed-by: session_011p7ikEivgXefNDaE5S5Uec

VERDICT: FAIL


Generated by Claude Code

…ick-start readers (objectui#10872, batch 4)

The batch-4 taught-node pins read the quick-start's "Add Actions" json fence:
the ratchet test's QUICK_START path and held-public-block-arms-10872's
taughtActionButton(). The ratchet file's existing entry gains
content/docs/guide/quick-start.md in `reads`; the held-arms test gets its
own entry with the same read. Answers contract review 5906210193, finding 1.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3570.1 KB 3607.4 KB
Main entry chunk (gzip) 149.6 KB 350 KB
Entry file index-BUz0ZdO0.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) 559.46KB 134.18KB
core (index.js) 9.94KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 227.28KB 63.04KB
fields (index.js) 261.19KB 66.27KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.35KB 9.18KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.58KB 4.90KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.25KB 2.17KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 52.17KB 15.06KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 138.73KB 37.05KB
plugin-designer (index.js) 215.78KB 44.42KB
plugin-detail (index.js) 234.78KB 62.36KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 172.30KB 44.19KB
plugin-gantt (index.js) 172.43KB 42.85KB
plugin-grid (index.js) 229.83KB 63.15KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.82KB 28.67KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 32.26KB 9.42KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 90.43KB 22.76KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.55KB 39.23KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.17KB 2.73KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 22.61KB 7.40KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.82KB 7.15KB
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: f0ad0a0b0befd1ed1a2e005a85a71f2a40f919f0
Local-runs: none

Round 2 on PR objectui#11180, card objectui#10872, batch 4 (Part of #10872, Clause-②: yes, widening). Read: the card body and all 31 comments (rulings 5862923926 / 5868698636 / 5882953316, unlock 5903895615, claim 5904933022, amendment 5905916922, triage 5905925465, dev reports 5905878792 and 5906006602, the patch-round report 5906402838), the round-1 record 5906210193 on this PR at 53a1db444, the PR body, the 13-file list, the net diff against main (merge base 0ffc423b1), compare/53a1db444...f0ad0a0b0, compare/f0ad0a0b0...main, and the check-runs on the head. Files at the head read for context only: scripts/markdown-test-inputs.mjs, content/docs/guide/quick-start.md, action-button.tsx, action-icon.tsx, .github/workflows/ci.yml. Nothing built, run or re-run. This round judges the whole PR at the new head.

The delta since round 1. compare/53a1db444...f0ad0a0b0: one commit, one file, scripts/markdown-test-inputs.mjs (+13, −2). That compare is the whole difference between the two heads, so the net diff outside that file is byte-identical to the round-1 head's: the 12 files 5906210193 judged are unchanged, and its ① 1–8 and ② are re-cited below where they apply.

Check-runs on the head (filter=latest; they were still in progress when this round began, so they were awaited in the foreground, and every run had completed by 07:44Z): 43 runs, 40 success, 3 skipped, 0 failure. The three skips are the expected ones (dependabot and the two coverage matrix rows). Finding 1 of 5906210193 is closed by the gates: Test (shard 1/8) is success, and the Test aggregate (test-aggregate, which reads every shard by name and registers only after the shards finish, which is why the count is 43 and not 42) is success. Type Check, Lint, the other seven shards, Test (dist pins), Build and E2E, Build Docs, Spec Main Shape Gate, README Export Check, Governed Surface Queue Guard, every changeset gate and every doc gate are green. Serial reading on these verdicts: ③ 5.

① Derived judgments

  1. Finding 1 of 5906210193 is closed by this head — right. The two readers of content/docs/guide/quick-start.md are now adjudicated in the markdown-test-inputs ledger (ADJUDICATED), and each entry describes what its test reads:
    • The ratchet file's existing entry: reads gains content/docs/guide/quick-start.md beside packages/plugin-ai/README.md; notRead (README.md, packages/cli/README.md) is unchanged. Its comment names the read: the QUICK_START path, the first json fence under the quick-start's "### Add Actions" heading, the taught action:button node, which the page pin runs through objectui validate beside a page:header. That is what the test does (the QUICK_START constant rooted four segments above packages/cli/src/__tests__, the regex on the slice after ### Add Actions, the page beside page:header, validate(file)). The older sentence about the fs trace opening the plugin-ai README "and nothing else" is now dated to objectui#10859, which keeps it true after the second read was added.
    • The pin file's new entry, packages/types/src/__tests__/held-public-block-arms-10872.test.ts, reads: ['content/docs/guide/quick-start.md'], placed in path order (between filter-builder-mirror-6939 and object-calendar-record-source-7313). Its comment: taughtActionButton() extracts the first json fence under "### Add Actions", the taught action:button node, and judges it on both zod faces, in a page and alone. That is what the file does: the tolerant face on the node in a page, both faces on the bag spelling in a page, the strict face and the spec's PageComponentSchema on the node alone. The file's one readFileSync is QUICK_START, so no notRead is owed.
    • Both ledger paths are the repo-relative spellings the round-1 failure named for each file, and the quick-start at the head carries the heading and the fence (type: action:button, label, actionType: url, target). The ledger file is on no governed surface (Governed Surface Queue Guard success) and in no released package (Changeset Declaration success with the one changeset). The closure itself is CI's answer, in the check-runs paragraph above.
  2. Re-cited from 5906210193, the diff being unchanged: ① 1 (the six arms by reference, in the union and the barrel), ① 2 (the action:button / action:icon handler-key members are required by check:handler-key-reads and correct: onClick a runtime slot, flat onSuccess an alias refusal naming properties.onSuccess), ① 3 (the ratchet 397 to 391 with ARMED_PUBLIC_BLOCKS_10872_BATCH_4; the validator import and refusedAtType are context, not hunks, so objectui#11069's hunks are untouched), ① 4 (the action:button page pin through objectui validate; the obligation it derived, finding 1, is now met by item 1 above), ① 5 (the six EXCLUSIONS rows, exactly the census's need, byte-identical to the rows the amendment granted, one hunk before the PublicBlockComponentSchema entry), ① 6 (the consumer-file edits; the identity-by-definition leg is sound: the definition object is per instance, so def identity is instance identity modulo the 17.5.0 proxy, and the key-set and strip-identity legs stand beside it), ① 7 (the new pin file and the two ablation legs), ① 8 (public surface). Spot-checked on this head against the diff rather than taken on trust: each arm is BaseSchema.extend({ type: z.literal(...), properties: propsBag(type, stripImportedDefaults(SpecXPropsSchema)) }) with no member restated; the union and the barrel gain exactly the six; the renderers at the head read schema.onClick only when it is a function (action-button.tsx 159, action-icon.tsx 122) and forward schema.onSuccess to the runner (298, 195), so the two members are required and their dispositions are the precedents named. Nothing to add.
  3. Accept-set and public-surface change, the whole PR: six type literals move from invalid_union at type to accepted on both faces, each bag judged by its 17.5.0 row with the read-point refusals the rows carry; @object-ui/types/zod gains six named exports and PublicBlockComponentSchema six members; a flat onClick or onSuccess on action:button / action:icon is refused by name where the whole node was refused at type before. @object-ui/cli changes only a test; scripts/markdown-test-inputs.mjs is a ledger, not a published surface. No registration, renderer, registry-inputs-spec-parity.test.ts or governed path is in the diff (verified on the 13-file list). A pure widening.

② Semver level

Re-cited from 5906210193 ②, unchanged: .changeset/10872-held-public-block-arms.md declares '@object-ui/types': minor, right for a widening of AnyComponentSchema's accept set by six literals plus six exports, the batch-1 precedent (10872-public-block-zod-arms.md, twenty literals, minor). The round-2 commit touches scripts/ only, no released package, so the changeset count stays one (Changeset Declaration, Bump Policy, Fixed Group Check, Claim Re-read and Overwrite Report all success on this head). The prose as a review face: every observable claim is pinned in the unchanged diff, the six exports and union membership, the strict face accepting the same documents, each read-point refusal, element:repeater requiring properties.object with no waiver, the two handler keys and their remedies, the flat-prop paragraph (the taught node passes objectui validate today; the strict face and the spec refuse its flat actionType / target; the bag spelling passes both faces), record:line_items still refused at type. "Nothing that parsed before is refused now" holds: the six literals were refused whole, and no other arm or BaseSchema moved. The dated note on the batch-1 changeset is append-only, frontmatter untouched, and accurate. Clause-②: yes (widening) is stated in the changeset, the PR body and the claim.

③ Boundary flags

Patch-round report 5906402838: deviations none, open questions none, two carrier notes addressed to the seat. The round-1 escalations are re-stated as carried; the new head changes none of them.

  1. The action:menu in-flight disabled override (class a, public door, reproduced through the real SchemaRenderer and registry; the amendment routes it to objectui#11168): carried to the seat as 5906210193 ③ 5 escalated it. Routing alone is not sufficient: objectui#11168's mandate is the registry-inputs parity ledger, and a measured runtime defect ({...rest} carrying disabled: undefined after disabled={loading}, the objectui#9131 shape) is not a ledger entry, so it needs its own finding card, serial behind objectui#11168 slice 1 on action-menu.tsx, with the inline action:group ...rest-onto-div inference riding it as a note. No renderer is in this diff; not a defect of this PR.
  2. The taught-node pins that move with triage 5905925465 (the flat-vs-properties fork for action:*): carried as ③ 4 escalated it. Record on objectui#11069 or on the triage request that the pins in registered-types-validate-ratchet-10859.test.ts and held-public-block-arms-10872.test.ts are the readings that move with the fork, so a red there is not read as a regression of this PR. One thing the new head adds in the right direction: both files are now ledgered readers of the quick-start, so under option A (the docs migrate to the bag) a change to that document runs both tests by construction, which is the ledger's purpose.
  3. The six arms' content-channel narrowing (children passes both faces on them; no renderer reads it): carried as ③ 6 escalated it, a later Clause-② narrowing batch of this card beside the flat-props / envelope-key batch. Unchanged.
  4. The PR body's "one known red" paragraph is FALSE at this head, not merely stale. The false sentence is: "The claim puts that file off limits (draft PRs objectui#11115 and objectui#11125 hold it) and says to stop and report the shape, so the six rows are not in this diff." The diff at f0ad0a0b0 carries the six rows (zod-mirror-parity.test.ts, +18, one hunk, since 53a1db444). The paragraph's opener, "⚠️ Draft with one known red, deliberately:", is false as well once shard 1/8 is green: this head has no known red. The "Measured: with those six rows added, the census passes (37/37, in a scratch copy of the test)" clause is a dated reading and stays true. Stale but true, so not findings: the gates heading "all after the final commit, c1ea153ad" (dated readings; the later readings are in 5906006602 and 5906402838), and "draft PRs objectui#11115 and objectui#11125 hold it" (objectui#11115 has since merged). Per the brief, the seat's ACCEPT carries the correction as a PR comment, not a body PATCH.
  5. Serial reading for the seat, not a finding. main has moved four commits past the merge base 0ffc423b1 (mergeable: true, mergeable_state: behind). One of them, b0a05dda1f (PR objectui#11115, objectui#11070), edits two of this PR's files, imported-defaults-8317.test.ts and zod-mirror-parity.test.ts, the two the dev measured merge-clean against objectui#11115's head bdd950ec6d with a non-vacuity check (5906006602). All four landed (07:04Z to 07:16Z) before this head's check-runs started (07:30Z), and the workflow's checkout names no ref, so on pull_request the shards ran on the merge of f0ad0a0b0 with main at 3ab51503b3: the shard verdicts above already answer the combined tree, and the queue build answers it again. Whichever lands second merges main, as the amendment says.
  6. The gap the patch round names, for the seat's next order: the batch-4 gate list omitted markdown-test-inputs.mjs --audit (batches 1 and 3 ran it), which is how two readers shipped unadjudicated. The dev ran it this round ("107 candidate test files, all adjudicated; 78 declared entries, all present") and the whole root scripts/__tests__ suite. An order that adds a markdown reader names the audit as a gate, the way ruling 5862923926 names the arm-list sweep after its own seat error. Not a finding of this PR.

Implemented-by: claude/issue-10872-held-blocks-by-reference
Reviewed-by: session_011p7ikEivgXefNDaE5S5Uec

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 30, 2026 07:55
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 30, 2026
Merged via the queue into main with commit e978ed5 Sep 30, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-10872-held-blocks-by-reference branch September 30, 2026 08:10
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: cli package: types tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants