Skip to content

test(types): pin every AnyComponentSchema arm to a named zod export (objectui#8784) - #9070

Merged
os-warren merged 4 commits into
mainfrom
claude/issue-8784-arm-named-export-pin
Sep 11, 2026
Merged

test(types): pin every AnyComponentSchema arm to a named zod export (objectui#8784)#9070
os-warren merged 4 commits into
mainfrom
claude/issue-8784-arm-named-export-pin

Conversation

@os-warren

@os-warren os-warren commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Fixes #8784

Adds packages/types/src/__tests__/arm-named-export-8784.test.ts: every AnyComponentSchema arm is a NAMED export of @object-ui/types/zod, or carries a ledger row saying why it is not. Test-only; the changeset declares an empty frontmatter.

What the pin does

Derive the arm list on every run — flatten AnyComponentSchema recursively through nested unions and z.lazy — then match each leaf arm to a named export of the barrel by identity. Two properties are load-bearing, and both are controls inside the file rather than claims in a comment:

  • It reads the BARREL. The population is Object.entries of the ../zod/index.zod.js module namespace, i.e. the runtime export surface itself, so a re-export counts and a declaration that never reached the barrel does not.
  • It matches by IDENTITY. An arm's identity chain starts at the union OPTION, so a containing union is never a candidate for the arm's name.

⛔ No count is hard-coded. The only bound asserted against the real union is a floor read off that union's own top-level option count, which is what proves the walk recursed instead of stopping at those 13 top-level members (12 sub-unions plus AppComponentSchema, an object arm at depth 1).

The category modules are imported too, but only to turn a failing arm into a name a reader can act on — namesOn takes ONE namespace and the real reading passes it the barrel.

Round 2 — what the contract review corrected, and what it did not

Round 1 (fc1ab897c) came back FAIL on a factual point, mechanism passing. Round 2 is prose plus two hardening nits; the census, the identity matching, the ledger rows, the derived floor and both firing controls are unchanged, and the firing controls were re-run on the patched head to prove it (same mutation hashes, same output).

What was wrong. Round 1's docblock filed 507b61bf7 (PR #8763, objectui#8499) under the barrel's SILENT failure modes, next to d908a82f4's collateral drop, and argued the four omissions were accidental. They were not. Three records say so, re-derived here rather than taken on trust:

  1. .changeset/8499-node-slot-registered-arms.md, present in this very tree (blob c055695d1 on origin/main), declares the omission and its reason: 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 wants the number back down should treat naming these families as its own decision." It also declares the movement it caused — unexportedNodeSchemas from [breadcrumb, object-tree] to 49 node types, "Measured at this branch's head, not estimated."
  2. objectui#8499's ceiling review raised exactly this as blocking F1 (5597569641): the four "are published by the union but unreachable by name from any published entry", with the eight barrel lines named as the remedy.
  3. That review then PASSed the deferral "on the surface-widening reason alone" (5598263402).

⇒ A deferral with a written reason, reviewed twice. Withdrawn accordingly: round 1's claim that this was the objectui#7917 defect recurring silently, and its reading of the stale census as the card's thesis arriving as evidence. objectui#8784's thesis — that the invariant has no keeper — stands on its own and does not need this commit.

What survives, and is now the docblock's argument. The pin still earns its place against a declared deferral, for a reason about where the declaration lives: a .changeset/*.md is consumed and deleted at release — measured, 59f61cfb8 "chore: release packages (#4655)" removes the batch it versioned — so the absence outlives its own explanation, and once the explanation is gone a deferred non-export and a collateral drop are the same two lines of nothing in index.zod.ts. The ledger moves that reason into the tree, where it is re-read on every run and deleted only when it stops being true. The classification is unchanged and was independently confirmed: the #8499 changeset itself leaves naming to "its own decision", so "a decision is owed" is the right state for those four rows.

ABSENT_PENDING_RULING is renamed ABSENT_PENDING_DECISION and its comment now carries the recorded argument AGAINST naming these families, not only the case for it — so a reader who meets the rows meets the counter-argument too.

Hardening, both nits the review raised. hasRow replaces key in LEDGER, so an arm declaring type: 'constructor' cannot ledger itself off Object.prototype; Object.hasOwn is the modern spelling and does not compile under this package's "lib": ["ES2020", "DOM"] (measured: tsc TS2550, "Try changing the 'lib' compiler option to 'es2022' or later"), so the .call form stands with that measurement recorded beside it — a lib bump is a build-config change this pin does not own. And the walk's if (!def || seen.has(schema)) return is split, so a member carrying no zod definition is pushed to unresolved instead of dropped, which is what the docblock already promised. Neither moved a reading: the types suite is identical at 172 files / 3385 tests, and unresolved is still empty on the real union.

The census on this base

Base 7f27bc54343e6193f11ec3f58927fb9afb1bc635. The card reads 107 arms / 107 literals / 107 named / 0 unnamed. This base reads 111 arms / 154 type literals / 106 named / 5 unnamed, derived as described above. The review reproduced the same four numbers with two instruments of its own, and reproduced 107 / 107 / 107 / 0 at ee4d19f6c, the parent of 507b61bf7 — so the card's census was correct when it was taken.

Sequence, by merged_at rather than committer date: card filed 2026-09-09T07:05:02Z → PR #8777 merged 07:20:02Z (the objectui#7917 repair, census 107/107/0) → PR #8763 merged 2026-09-09T08:11:20Z, arming four arms in layout.zod.ts and form.zod.ts with the barrel deliberately untouched. (07:53:59Z, which round 1 used, is 507b61bf7's committer date, not its merge time.)

The four are NOT exported here: a new exported symbol is the Clause-2 branch and the dispatch said stop and report. objectui#9067 carries the decision and its framing has been corrected there by the seat. The fifth unnamed arm, RetiredKanbanNodeSchema, is a refusal arm the barrel already declares deliberately internal in as many words (objectui#8802) and is ledgered as a decision taken. A stale-row leg deletes any row the moment its arm is exported or stops existing, so the ledger cannot outlive its cause — and Legs A and B below show any arm outside those five rows still reddens.

Cross-check between the two instruments: the five unnamed arms carry 7 + 37 + 2 + 1 + 1 = 48 type literals, which is the #8499 changeset's unexportedNodeSchemas metric after PR #8777 landed (its own conditional, "If PR #8777 lands first, the same movement reads 0 to 47") plus kanban from objectui#8802.

Firing controls — run for real, on the committed tree, both restored, re-run on the patched head

Both legs mutate ONLY packages/types/src/zod/index.zod.ts, which is the shape of both historical breaks. Each leg proves the mutation reached disk by counting the anchor line before and after and by comparing git hash-object against the HEAD blob; each restore is proved by hash equality plus an empty git diff HEAD.

Leg A — the dropped re-export. Delete the barrel's BreadcrumbSchema, line, module untouched. This is d908a82f4 in miniature. It can fire because breadcrumb is a live arm that IS named on the barrel today (PR #8777 put it back) and carries no ledger row, so the mutation moves it from named to unnamed — the exact transition the pin claims to detect, on a symbol this PR did not introduce.

before: exact anchor lines = 1
after : exact anchor lines = 0
after : file hash = 0dd47171df8382efc4df9f29630cf9a1d2b8745d  (HEAD blob 2307622df6e3dce011c9a483cb72ae8a27a85c54)
 packages/types/src/zod/index.zod.ts | 1 -
LEG A : vitest exit 1
  Tests  1 failed | 16 passed (17)

The failure names the arm:

AssertionError: These `AnyComponentSchema` arms have no named export on `./zod` … : expected [ Array(1) ] to deeply equal []
+ [
+   "navigation.zod.ts#BreadcrumbSchema [type: breadcrumb]",
+ ]

Leg B — the parent union under the arm's name. Replace that line with NavigationSchema as BreadcrumbSchema,. The NAME is present and bound to the parent sub-union. It can fire for the same reason as leg A, and it is the sharper control because a name-based census goes green on it:

after : BreadcrumbSchema declaration lines = 0
after : aliased lines = 1
after : file hash = 6def775ec20a5b2b0902358c5e1b06b743d1e386  (HEAD blob 2307622df6e3dce011c9a483cb72ae8a27a85c54)
LEG B : vitest exit 1
  Tests  1 failed | 16 passed (17)
+ [
+   "navigation.zod.ts#BreadcrumbSchema [type: breadcrumb]",
+ ]

On the SAME mutation, breadcrumb-object-tree-nameable-7917.test.ts is fully green — vitest exit 0, Tests 9 passed (9). That reproduces the ceiling reviewer's adversarial finding on this base: the accept leg succeeds, the near-miss refusal is byte-identical, another node type is still refused. Only identity catches it.

Restore. After both legs: RESTORED: hash matches HEAD blob and git diff HEAD is empty, then RESTORED : vitest exit 0, Tests 17 passed (17), git status --porcelain empty. Both legs were re-run after the round-2 hardening and produced byte-identical mutation hashes and identical output, which is how "the walk did not change" is a reading rather than an assurance.

Four further controls live in the file permanently rather than as one-off runs — a dropped re-export, the parent-union alias, a NESTED union whose arms must be counted instead of the union (the "13 arms / 2 literals" failure), and a z.lazy arm named through either side of the wrapper.

Verification

All commands run from the repository root, on the patched head. Exit codes captured to a file before any pipe; heavy runs serialised through the shared verify lock.

  • pnpm exec vitest run packages/types/Test Files 172 passed (172), Tests 3385 passed (3385), exit 0. 172 = 171 tracked test files plus this one, so the new file is in the run rather than assumed to be.
  • pnpm --filter @object-ui/types type-check — exit 0 (tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json). ⚠️ A green type-check does not by itself prove it read the new file: tsc -p tsconfig.test.json --listFiles was grepped and returns the file, 1 hit.
  • pnpm exec eslint --format json on the changed file in packages/types — 0 errors, 0 warnings, counts read from the JSON. The package baseline measured in round 1 was 242 files, 0 errors, 278 warnings; the repo-wide baseline is red on main.
  • node scripts/check-changeset-presence.mjs — exit 0: "Every one of them has an EMPTY frontmatter — declared as releasing nothing".
  • node scripts/check-control-bytes.mjs — exit 0, 7258 tracked text files scanned. The changed file was also scanned directly with grep -naP; grep exited 1 with no output lines, which is a reading on that file and not a general claim about the tree.
  • node scripts/check-new-cross-file-line-citations.mjs — exit 0, 0 new citations.
  • node scripts/check-vi-mock-override-shape.mjs — exit 0.

Not run locally and left to CI: the repo-wide lint and type-check farms, the CLI self-check, and every gate outside the families above.

Changeset level, from this repository's own precedent

Empty frontmatter, unchanged in round 2. scripts/check-changeset-presence.mjs's header is the authority and states it in as many words: "No carve-out for test files under src/. A change confined to src/__tests__/ is answered by the empty-frontmatter exemption, in one line." Read by content, the nearest precedent is .changeset/4971-declared-arm-subset-gate.md — the same shape of change (a test-only gate over a declared surface, carrying reasoned, issue-backed exemptions and a stale-exemption leg), declared as releasing nothing. Nothing in packages/types/package.json's files list moves and no published contract field moves, so no package is released by this change.

⚠️ Round 3, one clause, on the seat's ruling. Round 2 flagged rather than acted on a conflict: .changeset/8784-arm-named-export-pin.md still carried round 1's "48 minutes after objectui#8784 was filed" — the same arithmetic corrected everywhere else, off the committer date — while the patch instruction listed the changeset among the files not to touch. The seat ruled to correct it. ⚠️ The ground the seat and I both gave for that ruling — that the changeset is release-note input consumed into packages/types/CHANGELOG.md, so it outlives the PR body — is FALSE for this changeset, and the contract review on 19f8ab48d measured both sides of it: in release commit 59f61cfb8, 0 of the 62 deleted changesets with EMPTY frontmatter reach any CHANGELOG hunk, against a control of 40/40 for the non-empty ones. This file has empty frontmatter, so it is deleted at release and published nowhere. My own earlier evidence (git log --diff-filter=D showing the release commit deleting the batch) measured the DELETION and never the COPYING, and publication was inferred from deletion — ⛔ "X is deleted at release" does not establish "X is published at release". The edit stands on its own merit instead: a knowingly wrong figure in a file this PR ships is worth one clause whether or not anyone downstream reads it. That clause now reads "and touched no barrel deliberately (PR #8763 merged 2026-09-09T08:11:20Z; the omission and its reason are declared in .changeset/8499-node-slot-registered-arms.md)". ⛔ The interval is not restated as its true value of 66.3 minutes: what made an interval meaningful was the accident premise, and that premise is withdrawn. Nothing else moved in that round.

⚠️ Round 4, one further clause, also on the seat's ruling. Round 2 corrected the top-level-member fact in the pin's docblock and did not carry it into the changeset, so this PR was shipping two faces that disagreed about the same fact. ⚠️ That, and only that, is the reason it earned a clause — ⛔ NOT the "published into packages/types/CHANGELOG.md" rationale given at the time, which the contract review falsified for empty-frontmatter changesets (see the round-3 note above). That clause now reads: a walk that stops at the 13 top-level members — 12 sub-unions plus one object arm (AppComponentSchema, an arm at depth 1), not 13 sub-unions — scores exactly options.length arms. The wording is taken from the docblock rather than paraphrased, and the match is measured rather than eyeballed: with jsdoc leaders stripped and whitespace collapsed, that phrase occurs exactly once in each file. The claim itself is unchanged. ⛔ The paragraph's opening clause, "Red on arrival, reported rather than declared away", stays byte-unchanged — it asserts nothing false and is a borrowed repo idiom (.changeset/4971-declared-arm-subset-gate.md), so rewording it would be editing voice rather than fact.

Scope

⛔ No arm added or renamed. ⛔ No existing parity check weakened — zod-mirror-parity.test.ts is untouched, and this pin asks a question that census is structurally unable to ask rather than taking over any part of it. ⛔ No export added: that is objectui#9067's decision to take, not this PR's.

The diff touches no governed surface. It stays draft regardless: landing is the seat's act.

Carries needs:contract-review to match the card's carrier, per the dual-carrier rule.


Generated by Claude Code


Generated by Claude Code

…objectui#8784)

objectui#7917 found two arms with no named export on the `./zod` barrel, so
`breadcrumb` and `object-tree` could not be validated on their own. PR #8777
repaired both and nothing kept it repaired.

Each blindness was re-measured on this base rather than inherited:
`zod-mirror-parity.test.ts` builds its export population with
`/^export const NAME/m`, which matches a DECLARATION and can never match
`export { NAME } from './x.js'` — so it is structurally blind to a missing
re-export even though `index.zod.ts` is one of the files it reads; no test
pinned a list or a count on the barrel; and `scripts/measure-strict-authoring-face.mjs`,
the only script that computes the census, is a self-described MEASUREMENT
THROWAWAY wired into no `package.json` script and no workflow.

The pin derives the arm list on every run — flatten the union recursively
through nested unions and `z.lazy`, then match each leaf arm to a named export
BY IDENTITY. Both properties are controls in the file rather than claims in a
comment: a walk that stops at the top-level sub-unions scores exactly
`options.length` arms, and a name-based match is satisfied by
`export { NavigationSchema as BreadcrumbSchema }`, the parent sub-union under
the arm's name, which passes every behavioural leg of #8777's pin. No count is
hard-coded; the only bound asserted against the real union is a floor read off
that union's own top-level option count.

Red on arrival, ledgered rather than repaired: `507b61bf7` (PR #8763,
objectui#8499) armed four arms in `layout.zod.ts` and `form.zod.ts` and touched
no barrel, 48 minutes after objectui#8784 was filed on a census reading 107
arms / 107 named / 0 unnamed. This base reads 111 arms / 154 type literals /
106 named / 5 unnamed. Four rows point at objectui#9067, which holds the
ruling, because exporting them mints new names on a published surface; the
fifth, `RetiredKanbanNodeSchema`, is a refusal arm the barrel already declares
deliberately internal. A stale-row leg deletes any row the moment its arm is
exported or stops existing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3490.1 KB 3512.7 KB
Main entry chunk (gzip) 144.2 KB 350 KB
Entry file index-PeWB9QBA.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.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
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) 26.08KB 7.56KB
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) 500.20KB 114.67KB
core (index.js) 8.28KB 3.31KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 207.56KB 57.44KB
fields (index.js) 247.01KB 62.29KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 6.57KB 2.76KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.94KB
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) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
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) 14.81KB 3.63KB
plugin-calendar (index.js) 49.03KB 13.93KB
plugin-charts (index.js) 71.50KB 19.97KB
plugin-chatbot (index.js) 195.32KB 46.51KB
plugin-dashboard (index.js) 131.09KB 34.58KB
plugin-designer (index.js) 215.68KB 44.27KB
plugin-detail (index.js) 251.44KB 65.17KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.65KB 40.91KB
plugin-grid (index.js) 211.56KB 57.50KB
plugin-kanban (index.js) 46.07KB 14.32KB
plugin-list (index.js) 112.52KB 27.64KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.42KB 20.80KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 83.34KB 27.61KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.27KB 5.47KB
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

Copy link
Copy Markdown
Collaborator Author

Contract review

PR #9070 · card objectui#8784 · head fc1ab897c6281a3670da8d0dc6e049fb46ca6f5e · round 1 · seat domain:spec @ objectui

Charter read fresh from objectstack origin/main @ ad715aca57b44bbe745a20cf20f339cd5195df14 (references/contract-review.md, landing-operations.md, true-green.md, platform-readings.md, SKILL.md §复核 / §入队与落地). Head re-read immediately before posting: unchanged.

Tier fuse — MEASURED

  • CONTRACT_REVIEW_TIER read from scripts/pm/dispatch-gates.mjs @ that sha: claude-fable-5-1.
  • Transcript located by a unique marker string written first, not by filename. Harness per-message model stamps: 79/79 × claude-fable-5-1, no other value. Positive control: the same counter over a synthetic file carrying two different stamps reads 2 with both values, so it reads the file rather than returning a constant.

Independence pair (machine-readable)

  • Implemented-by: branch claude/issue-8784-arm-named-export-pin (a mode:subagent dev is recorded by its branch — charter line: 「mode:subagent dev 记其分支 claude/issue-…(子代理无自有 session)」).
  • Reviewed-by: session_01Jmxdo7bmeqCQHLSfmLVX9w (the domain:spec seat), ruling produced by a context-isolated claude-fable-5-1 review subagent fed only the card, the existing rulings and the PR.
  • Same-session test (「两者同 session ⇒ 报 SELF-REVIEW」): applied literally, Implemented-by: carries a branch and no session, so the two lines are not the same session identifier and the SELF-REVIEW trigger does not fire by the letter. Stated plainly rather than overstated: both the dev subagent and this review subagent were dispatched from that same seat session (the Claim: and objectui#9067 both name it), so independence here does NOT rest on distinct sessions — it rests on what the charter prescribes for the spec seat: context isolation plus the ceiling tier, both measured above. The commit trailer Co-Authored-By: Claude Opus 5 is a signature constant and is non-evidential per the charter.

① Derived judgments — each named, measured, judged

Baseline I derived myself (two instruments, my own walk, not the pin's). Instrument 1: plain Node over the BUILT dist/zod/index.zod.js (published surface; zod 4 public .def, iterative stack walk recording depth). Instrument 2: tsx over packages/types/src/zod source. Both on head:

reading value
top-level options of AnyComponentSchema 13 (= 12 sub-unions + AppComponentSchema, an object arm at depth 1)
leaf arms 111 (depth histogram {1: 1, 2: 110}; one arm via z.lazy: crud.zod.ts#ActionSchema)
type literals 154, 154 distinct (zero collisions)
named on the barrel by identity 106
unnamed 5: complex.zod.ts#RetiredKanbanNodeSchema [kanban], form.zod.ts#UiCalendarSchema [ui:calendar], form.zod.ts#InputShorthandSchema [email, password], layout.zod.ts#HtmlElementSchema [h1 … ×37], layout.zod.ts#SemanticElementSchema [aside … ×7]
members neither union / lazy / object 0

Same walk at ee4d19f6c (parent of 507b61bf7): 107 / 107 / 107 / 0 — the card's census, reproduced. At 507b61bf7: 111 / 154 / 107 / 4 — exactly the four. Compare API 507b61bf7...7f27bc543: ahead_by 154, behind_by 0 ⇒ that commit is an ancestor of this PR's base. The fifth unnamed arm arrives later with the objectui#8802 retirement: git diff 507b61bf7 HEAD -- index.zod.ts is exactly the KanbanSchema line replaced by the "deliberately NOT exported" comment.

  1. Accept set of AnyComponentSchema and of every exported schema — UNCHANGED. 2 added files (test + changeset); barrel blob 2307622df6e3… identical at base and head; no src/zod edit. Correct.
  2. Public surface of @object-ui/types/zod — UNCHANGED. No export added; Clause-②: yes was declared conservatively in the Claim: and the delivered diff takes the no-widening branch. Correct, and the declaration stays honest.
  3. Fresh census 111 / 154 / 106 / 5 and premise_still_valid: false — TRUE for the NUMBERS (two independent instruments agree). The card's thesis ("nothing enforces it") also still holds — see item 6 for what does NOT survive.
  4. Attribution to 507b61bf7 (PR fix(types): arm AnyComponentSchema for seven registered node-slot renderers (objectui#8499) #8763) — TRUE for four of five. Its file list touches form.zod.ts and layout.zod.ts, not index.zod.ts; barrel blob d6fe74c4a… identical at that commit and its parent. Precision correction: 07:53:59Z is the squash commit's committer date; PR fix(types): arm AnyComponentSchema for seven registered node-slot renderers (objectui#8499) #8763 merged_at is 2026-09-09T08:11:20Z (66 min after the card, not 48). Sequence: card 07:05:02Z → PR feat(types): re-export BreadcrumbSchema and ObjectTreeSchema from the zod barrel (objectui#7917) #8777 merged 07:20:02Z (107/107/0) → 507b61bf7 committed 07:53:59Z / merged 08:11:20Z. Non-blocking.
  5. "Reads the BARREL, matches by IDENTITY, the containing union is never a candidate" — TRUE. Code read: a union option starts its own chain [option], a z.lazy appends its inner, a union is never pushed onto any chain. Verified two ways: (a) on disk, Leg B below; (b) in memory with my instrument, binding BreadcrumbSchema ↦ NavigationSchema moves the identity census to 105 named / 6 unnamed (breadcrumb joins) while a name-based census stays at 106 / 5 — identity is what separates the two.
  6. "The four are unnamed by ACCIDENT / the objectui#7917 defect recurred SILENTLY / nothing said a word" — FALSE. This is the one derived judgment that does not survive. The non-export of exactly these four was declared, reasoned and reviewed at the time: .changeset/8499-node-slot-registered-arms.md (still in the tree at this head, lines 51–64) states "The four new arms are not in that barrel's explicit export lists, so the metric moves … to 49 node types … 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 metric is left to move and said out loud instead … whoever wants the number back down should treat naming these families as its own decision." The objectui#8499 ceiling review raised it as blocking F1 (5597569641), a delta commit with the eight barrel lines was prepared, and the review then PASSed option (b) "on the surface-widening reason alone" (5598263402). That is a deferral with a written reason — the opposite of d908a82f4's collateral drop or PR feat(plugin-tree): add tree / tree-grid object view type (#1885) #1892's omission. Consequences: (i) the pin's docblock files 507b61bf7 under "BOTH of the ways that has happened were silent … one of them was LIVE" — wrong in kind; (ii) the ABSENT_PENDING_RULING comment says the four show "the same evidence objectui#7917 used to conclude its two omissions were accidental" while omitting the file in the same tree that records the decision; (iii) the PR body (= squash message) says the stale census is "the card's own thesis, arriving as evidence" — the thesis was invisible omission, and this one was visible and declared, merely ungated; (iv) objectui#9067 is framed on the accident premise and presents recommendation A without the recorded counter-argument. One-line finder the dev's dedup would have needed: git grep -n SemanticElementSchema -- .changeset. ⚠️ The classification survives: the finding(types): 9 renderer type spellings reached through declared node slots resolve in NO arm of AnyComponentSchema — live components the validator cannot name #8499 changeset explicitly left naming to "its own decision", so ABSENT_PENDING_RULING ("ruling owed") is the correct state — only its stated basis is wrong.
  7. Floor is derived, no hard-coded count — TRUE. The only numeric literals asserted are on synthetic probe schemas (3, 0, 1) and toBeGreaterThan(0). Ablation with my instrument: a walk that stops at the top-level options scores 13 arms / 1 literal, and the floor 13 > 13 is false ⇒ red. ⚠️ Nit: it is 12 sub-unions + one object arm, not "13 sub-unions"; the floor holds either way.
  8. Firing controls, reproduced on my own clone, on the committed tree. Leg A (delete the BreadcrumbSchema, line): anchor 1→0, git hash-object = 0dd47171df8382efc4df9f29630cf9a1d2b8745d — byte-identical to the dev's mutation; pin exit 1, 1 failed | 16 passed (17), message names navigation.zod.ts#BreadcrumbSchema [type: breadcrumb]. Restore: hash = HEAD blob, git diff HEAD empty. Leg B (NavigationSchema as BreadcrumbSchema,): declaration 0 / aliased 1, hash 6def775ec20a5b2b0902358c5e1b06b743d1e386 — byte-identical; pin exit 1 naming the same arm; breadcrumb-object-tree-nameable-7917.test.ts on the SAME mutation exit 0, 9 passed (9) — the 7917 ceiling finding reproduced. Restore verified; pin at head 17 passed (17) before and after. Both legs can fire for the stated reason (a live, named, unledgered arm moved to unnamed).
  9. Parity-census refinement — TRUE. zod-mirror-parity.test.ts:3191-3194: readdirSync(ZOD_DIR) filtered by endsWith('.zod.ts') (includes index.zod.ts), population regex /^export const ([A-Za-z0-9_]+)/gm — cannot match export { X } from. Blind to the re-export FORM, not the file.
  10. scripts/measure-strict-authoring-face.mjs unwired — TRUE (no package.json script, no workflow reference; header line 10 says MEASUREMENT THROWAWAY). README negative also TRUE (2 BreadcrumbSchema mentions, 0 of the four).
  11. Ledger mechanics. ABSENT_BY_DECISION[kanban] matches the barrel comment (objectui#8802) — correct. Stale-row leg fails in both directions (literal gone / arm exported) — present and correct; it means the ledger cannot outlive its cause, so the invariant "named or declared" is not weakened for NEW omissions (Legs A/B prove any arm outside the five rows still reddens). Hardening nits, non-blocking: literal in LEDGER uses in on a plain object (inherits Object.prototype keys — no current literal collides; Object.hasOwn closes it); if (!def …) return silently drops a non-schema member, contrary to the "never a silent skip" docblock.
  12. Restraint on the four exports — RIGHT. A new exported symbol is the mechanical Clause-②: yes branch and the dispatch said stop and report; the 4971 precedent carries red-on-arrival findings as issue-backed rows with a stale leg. Risk to carry: objectui#9067 has no labels or triage yet — the rows point at an ungraded card; the seat should see it triaged.
  13. Verification re-run on my clone from repo root: pin 17 passed; pnpm exec vitest run packages/types/172 passed (172), 3385 passed (3385) (172 = 171 tracked at base + this file); pnpm --filter @object-ui/types type-check exit 0 with tsc -p tsconfig.test.json --listFiles → 1 hit; eslint on the new file 0 errors / 0 warnings (JSON); check-changeset-presence.mjs exit 0; control bytes on both added files: none (grep -P exit 1).

② Semver grading vs. the changeset

Empty frontmatter — correct. packages/types/package.json files = dist, README.md, CHANGELOG.md, LICENSE; tsconfig.json excludes **/__tests__/**; no barrel or module edit ⇒ nothing published moves. The presence gate's own header (check-changeset-presence.mjs:162) states the rule: "No carve-out for test files under src/. A change confined to src/__tests__/ is answered by the empty-frontmatter exemption"; nearest precedent .changeset/4971-declared-arm-subset-gate.md is the same shape. The changeset TEXT is factual as written (it says "reported rather than declared away", not "accident").

③ Dev flags, each answered or escalated

  • open_questions[0] — export the four or mark internal. ⛔ Not this PR's decision; escalated to objectui#9067. Answer on this PR's handling: "ruling owed" is the right classification. Required before the ruling is taken: finding(types): four objectui#8499 arms of AnyComponentSchema have no named export on ./zod — the objectui#7917 defect recurred one day after its repair #9067 must quote the objectui#8499 changeset's reasoning (lines 51–64) and the F1 / option-(b) trail (5597569641, 5598263402) so the maintainer sees the recorded argument AGAINST naming z.enum families, not only recommendation A. That is the seat's correction to make on the issue.
  • open_questions[1] — in-file ledger vs. reading the seven markers. A is right, and the measurement is confirmed: all seven markers sit on non-arm helpers; the only arm-level marker is on the barrel. Add the fact the dev missed: for THESE four the declared reason lives in a changeset (a release-notes carrier), which a marker reader would also miss — so an in-file ledger whose rows cite that changeset is the right shape.
  • out_of_scope_findings[0] — objectui#9067 filed. Dedup re-done: REST recency window of 400 issues+PRs (back to 2026-09-09T17:47Z) plus the MCP semantic search (55 results, top 10 read): no pre-existing card asks for these exports ⇒ not a duplicate. Its body is wrong on history per ① item 6 — escalated to the seat to correct.
  • out_of_scope_findings[1] — parity wording refinement, not filed. Agreed; measured above.
  • out_of_scope_findings[2] — unexportedNodeSchemas would report the four. Agreed, and it already does on paper: the finding(types): 9 renderer type spellings reached through declared node slots resolve in NO arm of AnyComponentSchema — live components the validator cannot name #8499 changeset declares that metric moving to 49 with its reason — this is the place the deferral was written down.
  • out_of_scope_findings[3] — README negative. Confirmed.

CI on the head

GET /commits/fc1ab897c…/check-runs: total_count 35, returned array length 35; all completed; 32 success, 3 skipped, 0 failures, 0 in progress. The skips are by design on a pull_request event: test-coverage is if: github.event_name == 'push', coverage-report is if: always() && github.event_name == 'push'; dependabot skip condition not read by me. Legacy combined status success. Path face: no governed path.

Platform artifacts, not findings

REST-appended PR-body footer; repo-wide eslint . --no-inline-config red on main; /search/* REST 403 for this session (dedup done over list endpoints + MCP semantic search, both bounded and both said so).

Not measured

check-clause2-carriers.mjs --pair 9070 (landing pre-check, the seat's act); check-half-states.mjs; the repo-wide lint / type-check farms and the CLI self-check beyond CI's own green; the dependabot check's skip condition; whether 507b61bf7's dedup at the time filed any card for the exports (I found none in a 400-item window and the semantic search).

VERDICT

The pin's mechanism passes every measurement I could make — barrel read, identity matching, derived floor, both firing controls byte-identical, restores proven, ledger stale-leg correct, semver correct, no widening. What fails is factual: the diff's own prose (the top docblock's "one of them was LIVE" under the silent-failure modes, and the ABSENT_PENDING_RULING comment) and the PR body that becomes the squash message record the four omissions as an accidental, silent recurrence of objectui#7917, when the tree at this very head carries their declared, reviewed deferral in .changeset/8499-node-slot-registered-arms.md. That prose is the reader's only explanation of the rows and steers the ruling the rows point at. Patch round, prose only — no mechanism change: (1) correct the pin's two docblock passages to say the four were armed by 507b61bf7 with the barrel omission declared and reasoned in the #8499 changeset and passed by its review under option (b), and that the naming decision was left open there — hence "ruling owed"; cite the changeset path; (2) rewrite the PR body's history paragraph the same way; (3) fix the 07:53:59Z merge time; optionally take the two hardening nits. Outside this PR, the seat corrects objectui#9067's framing before any ruling is taken. Re-review on the patched head is a prose re-read plus a pin re-run.

VERDICT: FAIL — patch round (prose and PR body only; the pin itself needs no change)


Generated by Claude Code

…d (objectui#8784)

The contract review on `fc1ab897c` returned FAIL on a factual point, and it is
right. This commit changes prose and two hardening details; the census, the
identity matching, the ledger rows, the firing controls and the derived floor
are untouched.

WHAT WAS WRONG. The docblock filed `507b61bf7` (PR #8763, objectui#8499) under
the barrel's SILENT failure modes, beside `d908a82f4`'s collateral drop and PR
#1892's omission, and `ABSENT_PENDING_RULING`'s comment argued from "the same
evidence objectui#7917 used to conclude its two omissions were accidental".
That is refuted by three records, re-derived here rather than taken on trust:

  1. `.changeset/8499-node-slot-registered-arms.md`, present in this very tree
     (blob c055695 on origin/main), declares the omission and its reason —
     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", so "the metric is left to move and
     said out loud instead", and "whoever wants the number back down should
     treat naming these families as its own decision". It also declares the
     movement it caused: `unexportedNodeSchemas` from `[breadcrumb,
     object-tree]` to 49 node types, "Measured at this branch's head, not
     estimated."
  2. objectui#8499's ceiling review raised exactly this as blocking F1
     (`5597569641`: the four "are published by the union but unreachable by name
     from any published entry"), with the eight barrel lines named as the
     remedy.
  3. That review then PASSed the deferral "on the surface-widening reason
     alone" (`5598263402`).

So the omission was declared, reasoned and reviewed. The state "a decision is
owed" is correct and the rows stay; their stated BASIS was wrong.

WHAT CHANGED. The docblock gains a third, non-silent way an arm loses its barrel
line and says why the pin still earns its place against it: the declaration
lives in a `.changeset/*.md`, which is consumed and deleted at release
(measured: `59f61cfb8` "chore: release packages (#4655)" removes the batch it
versioned), so the absence outlives its own explanation. `ABSENT_PENDING_RULING`
becomes `ABSENT_PENDING_DECISION`, and its comment now carries the recorded
argument AGAINST naming these families rather than only the case for it. The
failure message names all three acceptable answers. Timing corrected: PR #8763's
`merged_at` is 2026-09-09T08:11:20Z; `07:53:59Z` was the squash commit's
committer date, and the "48 minutes" arithmetic built on it is gone. The
"13 sub-unions" aside is corrected to 12 sub-unions plus one object arm.

HARDENING, both nits the review raised. `hasRow` replaces `key in LEDGER`, so an
arm declaring `type: 'constructor'` cannot ledger itself off `Object.prototype`;
`Object.hasOwn` does not compile under this package's `"lib": ["ES2020", "DOM"]`
(tsc TS2550), so the `.call` form stands with that measurement recorded beside
it. The walk's `if (!def || seen.has(schema)) return` is split: a member with no
zod definition is now pushed to `unresolved` instead of being dropped, which is
what the docblock already promised.

Readings unchanged after both: types suite 172 files / 3385 tests green,
`type-check` exit 0, eslint 0/0 on the file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3490.1 KB 3512.7 KB
Main entry chunk (gzip) 144.2 KB 350 KB
Entry file index-CBNW8r8T.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.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
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) 26.08KB 7.56KB
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) 500.20KB 114.67KB
core (index.js) 8.28KB 3.31KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 207.56KB 57.44KB
fields (index.js) 247.01KB 62.29KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 6.57KB 2.76KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.94KB
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) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
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) 14.81KB 3.63KB
plugin-calendar (index.js) 49.03KB 13.93KB
plugin-charts (index.js) 71.50KB 19.97KB
plugin-chatbot (index.js) 195.32KB 46.51KB
plugin-dashboard (index.js) 131.21KB 34.62KB
plugin-designer (index.js) 215.68KB 44.27KB
plugin-detail (index.js) 251.44KB 65.17KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.65KB 40.91KB
plugin-grid (index.js) 211.56KB 57.50KB
plugin-kanban (index.js) 46.07KB 14.32KB
plugin-list (index.js) 112.52KB 27.64KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.42KB 20.80KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 83.34KB 27.61KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.27KB 5.47KB
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

… changeset

One clause, on the seat's ruling. `.changeset/8784-arm-named-export-pin.md` is
release-note input — it is consumed into `packages/types/CHANGELOG.md` at the
next publish — so it outlives the PR body that already carries the correction,
and it was the last place in this branch still reading off the committer date.

Replaced: "and touched no barrel — 48 minutes after objectui#8784 was filed on a
census reading 107 arms / 107 named / 0 unnamed."
With:      "and touched no barrel deliberately (PR #8763 merged
2026-09-09T08:11:20Z; the omission and its reason are declared in
`.changeset/8499-node-slot-registered-arms.md`)."

⛔ The interval is not restated as 66 minutes. Measured here — card created
2026-09-09T07:05:02Z, PR objectui#8763 `merged_at` 2026-09-09T08:11:20Z, so the
true figure is 66.3 rather than the 48 that came off `507b61bf7`'s committer
date 07:53:59Z — but the framing that made an interval meaningful was the
accident premise, and that premise is withdrawn: the barrel omission was
declared in objectui#8499's own changeset, raised as blocking F1 (`5597569641`)
and passed on the surface-widening reason (`5598263402`).

Nothing else in the file moves. Gates on the edited tree: check-changeset-presence
exit 0 ("Every one of them has an EMPTY frontmatter"), check-changeset-no-major
exit 0, check-changeset-fixed exit 0, check-changeset-overwrite exit 0 ("No
pre-existing changeset was modified or deleted"), check-control-bytes exit 0,
plus a direct `grep -naP` control-byte scan of the edited file (exit 1, no lines).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3490.4 KB 3512.7 KB
Main entry chunk (gzip) 144.2 KB 350 KB
Entry file index-Bc0w89ul.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.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
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) 26.08KB 7.56KB
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) 500.20KB 114.67KB
core (index.js) 8.28KB 3.31KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 208.91KB 57.80KB
fields (index.js) 247.14KB 62.34KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 6.57KB 2.76KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.94KB
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) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
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) 14.81KB 3.63KB
plugin-calendar (index.js) 49.03KB 13.93KB
plugin-charts (index.js) 71.50KB 19.97KB
plugin-chatbot (index.js) 195.32KB 46.51KB
plugin-dashboard (index.js) 131.21KB 34.62KB
plugin-designer (index.js) 215.68KB 44.27KB
plugin-detail (index.js) 251.44KB 65.17KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.51KB 40.89KB
plugin-grid (index.js) 211.56KB 57.50KB
plugin-kanban (index.js) 46.07KB 14.32KB
plugin-list (index.js) 112.52KB 27.64KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.54KB 3.31KB
plugin-view (index.js) 84.42KB 20.80KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 83.34KB 27.61KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.27KB 5.47KB
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

…e pin's docblock

One clause, on the seat's ruling. Round 2 corrected this fact in the pin's
docblock and did not carry the correction into the changeset, so the PR shipped
two faces disagreeing about the same thing — and the changeset is the face that
is published into `packages/types/CHANGELOG.md` and outlives the docblock's
audience.

Before: "a walk that stops at the top-level sub-unions scores exactly
`options.length` arms"
After:  "a walk that stops at the 13 top-level members — 12 sub-unions plus one
object arm (`AppComponentSchema`, an arm at depth 1), not 13 sub-unions — scores
exactly `options.length` arms"

The wording is taken from the docblock rather than paraphrased, and the match is
measured rather than eyeballed: with jsdoc leaders stripped and whitespace
collapsed, the phrase "12 sub-unions plus one object arm (`AppComponentSchema`,
an arm at depth 1), not 13 sub-unions" occurs exactly once in each file.

⛔ The claim itself is unchanged — such a walk does still score exactly
`options.length` arms, which is the property the derived floor rests on.
⛔ Nothing else in the file moves: "Red on arrival, reported rather than declared
away" stays byte-unchanged (it asserts nothing false and is a borrowed repo
idiom, `.changeset/4971-declared-arm-subset-gate.md`), and the withdrawn
interval stays dropped rather than restated.

Gates on the edited tree: check-changeset-presence exit 0 ("Every one of them has
an EMPTY frontmatter"), check-changeset-no-major exit 0, check-changeset-fixed
exit 0, check-changeset-overwrite exit 0 ("No pre-existing changeset was modified
or deleted"), check-control-bytes exit 0, plus a direct `grep -naP` control-byte
scan of the edited file (exit 1, no lines).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3490.4 KB 3512.7 KB
Main entry chunk (gzip) 144.2 KB 350 KB
Entry file index-ANxrSf1-.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.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
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) 26.08KB 7.56KB
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) 500.20KB 114.67KB
core (index.js) 8.28KB 3.31KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 208.91KB 57.80KB
fields (index.js) 247.14KB 62.34KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 6.57KB 2.76KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.94KB
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) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
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) 14.81KB 3.63KB
plugin-calendar (index.js) 49.03KB 13.93KB
plugin-charts (index.js) 71.50KB 19.97KB
plugin-chatbot (index.js) 195.32KB 46.51KB
plugin-dashboard (index.js) 131.21KB 34.62KB
plugin-designer (index.js) 215.68KB 44.27KB
plugin-detail (index.js) 251.53KB 65.20KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.51KB 40.89KB
plugin-grid (index.js) 211.56KB 57.50KB
plugin-kanban (index.js) 46.07KB 14.32KB
plugin-list (index.js) 112.52KB 27.64KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.54KB 3.31KB
plugin-view (index.js) 84.42KB 20.80KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 83.34KB 27.61KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.27KB 5.47KB
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

Copy link
Copy Markdown
Collaborator Author

Contract review record — ADOPTED VERBATIM. Ruling produced by a context-isolated ceiling-tier (CONTRACT_REVIEW_TIER) review subagent, fed only card objectui#8784, this PR, and the prior FAIL 5626404152. ⛔ Per the charter the seat has exactly two moves on a subagent verdict — adopt verbatim or void entirely — so nothing below is rewritten, abridged or polished. The only change is decoding HTML entities the transport introduced (&gt;>); no word of the ruling is altered.


Contract review

PR #9070 · card objectui#8784 · head 19f8ab48d3803690935db75f717a832ae0c8029a · base 7f27bc54343e6193f11ec3f58927fb9afb1bc635 · prior ruling FAIL at fc1ab897c6281a3670da8d0dc6e049fb46ca6f5e (comment 5626404152)

Tree: fresh git clone --filter=blob:none into my scratchpad, git fetch origin 19f8ab48… + git checkout (exit 0/0/0), git rev-parse HEAD = the sha above. pnpm install --frozen-lockfile exit 0 (pnpm 10.31.0, node 22.22.2; pnpm ignored 7 postinstall scripts — esbuild, @swc/core, better-sqlite3, msw, keytar, vsce-sign — every run below was green regardless). Shared checkouts /home/user/objectui and /home/user/objectstack untouched except one read-only grep of objectstack.

① What the card asks for, and whether the diff delivers exactly that

The card (#8784) asks for a keeper of "every AnyComponentSchema arm is a named export of @object-ui/types/zod", with two properties it learned the hard way — recurse into nested sub-unions and match by identity, not name — plus an exemption path for deliberately-internal arms, "declared rather than accidental". Triage adds: derive the arm list, never hard-code a count; do not add/rename arms; do not weaken any parity check. The dispatch (Claim 5625742535) adds: read the BARREL not the modules; a firing control that reproduces the historical break with proof on disk; run once for real and stop/report if an arm is missing (Clause-②); changeset level from precedent.

The diff is two added files, +517/-0 (git diff --stat 7f27bc54…..HEAD): packages/types/src/__tests__/arm-named-export-8784.test.ts (480 lines, 17 tests) and .changeset/8784-arm-named-export-pin.md (empty frontmatter). Delivery, item by item, each re-derived in ②:

owed delivered how I know
derive, no hard-coded count yes — only numeric literals asserted are on synthetic probes (3,0,1) and toBeGreaterThan(0); the real-union bound is arms.length > options.length read of the file; ABL-4
recurse below top level yes — my walker: 111 leaf arms, depth {1:1, 2:110}; ABL-4 reddens the floor
identity, not name yes — chain starts at the union option; unions never pushed Leg B on disk + in-memory Leg B in my walker
read the barrel yes — Object.entries of ../zod/index.zod.js namespace Leg A on disk
firing control on disk, proven, restored yes Legs A/B reproduced byte-identically
stop and report on Clause-② yes — no export added, barrel blob identical at base and head (2307622df6e3dce011c9a483cb72ae8a27a85c54 both) git rev-parse
exemption declared, not accidental in-file ledger (5 rows) with a stale-row leg in both directions ABL-1/2/3
no arm added/renamed, no parity check weakened true — no file under src/zod or the parity test in the diff diff file list
changeset by precedent empty frontmatter, check-changeset-presence exit 0 quoting its own "no carve-out for test files … EMPTY frontmatter" rule gate run

One deviation from the card's wording: the card suggested the gate "read those [seven 'Deliberately NOT exported'] markers rather than invent a second ledger". The dev built an in-file ledger instead. Measured: the marker sites I can find (base.zod.ts:131, complex.zod.ts:653, data-display.zod.ts:727, :790, views.zod.ts:34) sit on helpers, none is within 25 lines above any of the four unnamed declarations (0/0/0/0), and the only arm-level marker is on the barrel itself (index.zod.ts:254-258, kanban). A marker reader would today find zero exemptions and one special case, so the deviation is justified and was accepted by the round-1 ruling. Not a finding.

What the FAIL demanded, and whether it arrived. (1) Correct the two docblock passages so the four are described as a declared, reasoned, reviewed deferral citing the #8499 changeset — done: the top docblock (lines 36-58) and the ABSENT_PENDING_DECISION comment (lines 154-172) now quote .changeset/8499-node-slot-registered-arms.md and cite 5597569641 / 5598263402; every quoted passage occurs exactly once in that changeset at head (blob c055695d1…), and both comments say what is attributed to them (F1 blocking, "Option (b) stands on the surface-widening reason alone", VERDICT: PASS). (2) PR body rewritten — done, with the withdrawal stated. (3) 07:53:59Z fixed — done; 48 minutes occurs 0 times across both diff files; PR #8763 merged_at is 2026-09-09T08:11:20Z (API), committer date 07:53:59Z (git). Optional nits taken: hasRow (0 remaining in LEDGER), split !def return. Rounds 3–4 (changeset clauses) were not demanded by the FAIL; both edits are correct by measurement (§②) and the phrase-match the PR body claims is 1 occurrence in each face.

Anything the FAIL did not demand that has since gone wrong. Nothing in the mechanism: the code delta since fc1ab897c is exactly the constant rename, hasRow, the split return, and the failure-message text (git diff fc1ab897c..HEAD code hunks, ②). The prose items in ③ are all non-blocking; two of them (F1, F5) were present at the FAIL head and one (F1) was introduced by the FAIL's own wording.

Protocol. The diff touches no TypeScript face, no zod face, no content/docs/releases/. grep -rIl 'index.zod' /home/user/objectstack → exit 1; AnyComponentSchema appears there only in packages/console/CHANGELOG.md. The protocol does not govern this barrel's export surface, and a test+changeset diff cannot be narrower than the protocol by construction. The only "two faces" here are docblock vs changeset vs PR body, compared in ②/③.

Scope. Nothing outside the card. git status --porcelain empty at start and end.

② What I re-derived from the tree

All commands from the clone root unless noted; exit codes captured to files before any pipe.

Runs on the head

command exit reading
pnpm exec vitest run packages/types/src/__tests__/arm-named-export-8784.test.ts 0 Tests 17 passed (17)
pnpm exec vitest run packages/types/ 0 Test Files 172 passed (172), Tests 3385 passed (3385)
git ls-tree -r --name-only <base|HEAD> -- packages/types | grep -c '\.test\.ts$' 0/0 171 → 172 (so "172 = 171 + this file" holds)
pnpm --filter @object-ui/types type-check 0 tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json
cd packages/types && pnpm exec tsc -p tsconfig.test.json --listFiles | grep -c arm-named-export-8784.test.ts 0 1
cd packages/types && pnpm exec eslint --format json src/__tests__/arm-named-export-8784.test.ts 0 1 file, 0 errors, 0 warnings (read from JSON)
cd packages/types && pnpm exec eslint . --format json 0 242 files, 0 errors, 278 warnings; pin row (0,0) — matches the PR body's baseline
node scripts/check-changeset-presence.mjs 0 "Every one of them has an EMPTY frontmatter"
node scripts/check-changeset-no-major.mjs · -overwrite · -fixed 0 · 0 · 0 overwrite: "1 added, 0 modified, 0 deleted"
node scripts/check-control-bytes.mjs 0 7258 tracked text files
grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' <the two added files> 1 zero output lines — a reading on those two files only
node scripts/check-new-cross-file-line-citations.mjs · check-vi-mock-override-shape.mjs 0 · 0 0 new citations

My own census (independent walker, tsx, not the pin's code; exit 0) over packages/types/src/zod/index.zod.ts: root def.type = union, 13 top-level options = 12 union + 1 object (app.zod.ts#AppComponentSchema); 111 leaf arms, depth histogram {1:1, 2:110}, unresolved = []; 154 type literals, 154 distinct, 0 arms with zero literals; one arm via z.lazy (crud.zod.ts#ActionSchema); 106 named / 5 unnamed by identity — RetiredKanbanNodeSchema [kanban], UiCalendarSchema [ui:calendar], InputShorthandSchema [email, password], HtmlElementSchema [37], SemanticElementSchema [7]; unnamed literal sum 48 (= 7+37+2+1+1). In-memory ablation "stop at top level": 13 arms / 1 literal, floor 13 > 13 false. In-memory Leg B (BreadcrumbSchema ↦ NavigationSchema): identity census 6 unnamed, name-based census 5 — identity is what separates them. Every number in the card-vs-base census, the changeset (line 33) and the PR body reproduces.

Firing legs on disk (HEAD blob 2307622df6e3dce011c9a483cb72ae8a27a85c54; git checkout HEAD -- restore, each proven by hash + empty git diff HEAD):

leg mutation hash pin other
A delete BreadcrumbSchema, (anchor 1→0) 0dd47171df8382efc4df9f29630cf9a1d2b8745d exit 1, 1 failed | 16 passed (17), names navigation.zod.ts#BreadcrumbSchema [type: breadcrumb]
B NavigationSchema as BreadcrumbSchema, (decl 0, alias 1) 6def775ec20a5b2b0902358c5e1b06b743d1e386 exit 1, same failure, same name breadcrumb-object-tree-nameable-7917.test.ts exit 0, 9 passed (9)
restore hash 2307622d…, git diff HEAD --stat empty exit 0, 17 passed (17) git status --porcelain -uno empty

Both hashes are byte-identical to the ones the PR body and the round-1 ruling report. Both legs can fire for the stated reason: breadcrumb is live, barrel-named today, and carries no ledger row, so the mutation moves it named→unnamed.

Does the pin pin — ablations of the test file itself (each restored, git diff HEAD --stat empty after):

ablation result
ABL-1 delete the kanban row exit 1 — no arm is missing from the barrel without a ledger row saying why red
ABL-2 add a stale row breadcrumb exit 1 — ledger row \breadcrumb` still names a live, still-absent arm` red
ABL-3 add a row on a literal no arm declares exit 1 — ledger row \zz-no-such-arm` …` red
ABL-4 walk stops at depth 1 exit 1 — 10 failed | 7 passed: recurses BELOW red, all 5 ledger rows red, lazy/identity/nested controls red. ⚠️ no arm is missing stayed GREEN (the 12 sub-unions are all barrel-named by identity) — which is why the derived floor is load-bearing, exactly as the docblock says
ABL-5 namesOn accepts identity OR the arm's declaring name barrel intact: exit 0, 17/17; with Leg B on the barrel: exit 0, 17/17 — see finding F7
ABL-6 Object.hasOwn tsc -p tsconfig.test.json exit 2, TS2550: Property 'hasOwn' does not exist on type 'ObjectConstructor' — matches the docblock; tsconfig.test.json:22 sets "lib": ["ES2020", "DOM"]

Historical and prose claims, re-derived

claim (where) measurement verdict
./zod is the only zod subpath → dist/zod/index.zod.js (docblock) packages/types/package.json exports true
barrel carries no star re-export grep -n 'export \*' index.zod.ts exit 1 true
d908a82f4 "Fix schema duplication" dropped BreadcrumbSchema as collateral; on barrel at creation; gone 41 commits (docblock) commit d908a82f4… 2026-01-25 "Fix schema duplication and remove deprecated property", diff shows - BreadcrumbSchema,; barrel created 56d5c8f26 5 min earlier with 1 BreadcrumbSchema line; git rev-list --count d908a82f4..0f5cadfa1^ -- packages/types/src/zod/index.zod.ts = 41 true, with "41" meaning barrel-touching commits after the drop and before the restoring commit
ObjectTreeSchema never added; PR #1892's file list omits the barrel API: 22 files, objectql.zod.ts present, index.zod.ts absent true
507b61bf7 = PR #8763, touched form.zod.ts/layout.zod.ts only, merged 08:11:20Z git file list; barrel blob d6fe74c4a… at commit and parent; merge-base --is-ancestor of base exit 0; API merged_at true
59f61cfb8 "chore: release packages (#4655)" deletes the changeset batch git show --diff-filter=D: 291 .changeset/*.md deleted true
an empty-frontmatter changeset is "consumed into packages/types/CHANGELOG.md" (PR body, rounds 3–4 rationale; also this brief's premise) of the 291 deleted, 62 had empty frontmatter; 0 of their first lines appear in any CHANGELOG hunk of that commit; control: 40/40 non-empty ones do false for this PR's changeset — see F6
parity census regex blind to re-export FORM (docblock, changeset) zod-mirror-parity.test.ts:3191-3194: readdirSync(ZOD_DIR), .endsWith('.zod.ts'), /^export const ([A-Za-z0-9_]+)/gm true
measure-strict-authoring-face.mjs is a MEASUREMENT THROWAWAY wired nowhere header line 10; grep -rn over package.json, packages/*/package.json, .github/ exit 1 true
README advertises BreadcrumbSchema (2), none of the four grep -c true
ActionSchema reached through z.lazy crud.zod.ts:72 true
kanban barrel comment text + "(objectui#8802, maintainer ruling 2026-09-09)" index.zod.ts:254-258 verbatim true
#8499 changeset quotes (6 passages) each occurs exactly 1× in blob c055695d1… true
5597569641 = blocking F1 naming "the eight barrel lines"; 5598263402 = PASS "on the surface-widening reason alone" both comments read in full / in section true
"a delta commit carrying the eight barrel lines was prepared" (docblock:45) F1 names the remedy; 5598263402 Item 2: "Phantom SHA confirmed. b7af6b52 → 422 … Option (a) had nothing to lean on" unsupported — see F1
"Zod 4 … nothing public exposes it" (test:209) zod 4.4.3: typeof z.object({}).def === 'object', o.def === o._zod.def → true false — F2
"a bare z.lazy member computes no propValues, which zod 4 refuses as a discriminated option (fanout test measures that refusal)" (test:468-469) z.discriminatedUnion('type', [z.lazy(() => …'lz'), …'other']) constructs; propValues.type = ["lz","other"]; parses lz/other true, bogus false. Fanout test lines 106-111 measure the refusal of a plain z.union member, not a lazy false on mechanism — F3
docblock:93 cites a control named the parent union under the arm's name is still unnamed 0 it() with that title; the control is still reports the arm when the PARENT union carries the arm name (test:441) dangling — F4
phrase "12 sub-unions plus one object arm (AppComponentSchema, an arm at depth 1), not 13 sub-unions" jsdoc leaders stripped, whitespace collapsed: 1× in test, 1× in changeset true
48 minutes · ABSENT_PENDING_RULING · in LEDGER residues 0 · 0 · 0 clean
literal uniqueness pinned elsewhere (so a new arm cannot hide behind a ledgered literal) any-component-union-fanout.test.ts:86-96 pins distinctness with a firing control; zod 4.4.3 throws "Duplicate discriminator value" closed
CI on head total_count 35 = returned 35; all completed; 32 success, 3 skipped (Test (coverage), the unexpanded shard placeholder, dependabot), 0 failures terminal green
#9067 exists, is the ledger's target open, bug pm:queue priority:p2 domain:ui; body still framed on the accident premise; corrections on the thread at 5626421146 and 5626709880 see F8
#8802 / #8606 exist and are what the prose says #8802 kanban retirement (closed by #8865); #8606 "a number stated in a comment that nothing checks" true

Not measured (an unmeasured thing is not a clean thing): check-clause2-carriers.mjs --pair 9070 and check-half-states.mjs (objectstack scripts, the seat's landing acts); repo-wide lint/type-check farms and the CLI self-check locally (CI's own 35/35 is the only reading); the dependabot skip condition; whether any delta commit for #8499 F1 ever existed off-GitHub (the record says the offered SHA was a phantom; nothing beyond that is knowable); the tier fuse / transcript stamps of the dev runs (not available to this subagent).

③ Findings

Blocking: none.

Non-blocking

  • F1 — arm-named-export-8784.test.ts:45 — "a delta commit carrying the eight barrel lines was prepared" is not supported by the record. 5597569641 names the remedy ("one additive commit on 90fcf4f2 with the eight barrel lines"); 5598263402 Item 2 then records that the SHA offered for it (b7af6b52) returned 422 in both repos — "Option (a) had nothing to lean on." The phrase was introduced by the round-1 FAIL's item 6 ("a delta commit with the eight barrel lines was prepared") and inherited verbatim. The classification the sentence supports ("a decision is owed") is unaffected. Fix: "the remedy was named (eight barrel lines), and the review then PASSed the deferral…".
  • F2 — :209 — "nothing public exposes it" is false on zod 4.4.3: schema.def is public and is the same object as _zod.def. defOf works either way; the comment misstates why it reads _zod.
  • F3 — :466-470 — the reason given for using z.union in the lazy control is wrong on mechanism. zod 4.4.3 accepts a bare z.lazy as a discriminated option and computes its propValues through the getter (measured ["lz","other"]); the fanout test measures the refusal of a plain z.union member (its lines 106-111), not of a lazy. The real obstacle is TypeScript's typing of ZodLazy._zod.propValues as PropValues | undefined — which is exactly what crud.zod.ts:211-220 says and why that site carries a cast. The control itself is sound; the comment should say "tsc refuses it without a cast; the runtime does not".
  • F4 — :93 — dangling cross-reference. The docblock cites a control by a name that is no it() title; the control at :441 is still reports the arm when the PARENT union carries the arm name.
  • F5 — :52-56 (and :169-171) — "the absence outlives its own explanation / once the explanation is gone" overstates the mechanism. A non-empty changeset is deleted from .changeset/ and copied into the package CHANGELOG (control: 40/40 in 59f61cfb8), so the finding(types): 9 renderer type spellings reached through declared node slots resolve in NO arm of AnyComponentSchema — live components the validator cannot name #8499 reason will survive in packages/types/CHANGELOG.md. The operative argument — nothing in the tree re-reads that reason and nothing reddens when it stops being true — is correct and is what the ledger provides; the seat's own 5626709880 states it accurately ("disappears into a CHANGELOG entry nobody greps"). Reword to that.
  • F6 — PR body (squash message), rounds 3–4 rationale — "a changeset is release-note input consumed into packages/types/CHANGELOG.md at the next publish" is false for an empty-frontmatter changeset. Measured: 62 empty-frontmatter changesets deleted by 59f61cfb8, 0 reached any CHANGELOG hunk (40/40 control). This PR's changeset will be deleted at release and published nowhere. Both edits it motivated are nonetheless correct (a stale interval removed; two faces made to agree), so nothing in the tree needs to move; the body's stated mechanism is wrong and a body PATCH costs no head move. This also falsifies the premise handed to this review; I held the changeset to the code standard regardless, and it is clean.
  • F7 — :285-290 namesOn — the permanent in-file "parent-union alias" control (:441) does not fire against a name-based regression. ABL-5 (accept identity OR the arm's declaring name) passes 17/17 with the barrel intact and 17/17 under Leg B; only the one-off on-disk Leg B catches it. The in-file control guards the value-bound-to-union shape by identity, which is what it claims, but the PR body's "four further controls live in the file permanently rather than as one-off runs" is narrower than "identity vs name" in general. Hardening (one it): an in-memory Leg B on the real union — namesOn({ ...BARREL, BreadcrumbSchema: BARREL.NavigationSchema }, breadcrumbArm) expected [] — which ABL-5 would redden.
  • F8 — outside the diff, recorded because the rows point at it: objectui#9067's body still carries "unnamed by accident", "merged 2026-09-09T07:53:59Z" and "48 minutes"; the corrections exist only as thread comments 5626421146 / 5626709880. Not this PR's to fix; the seat said it would correct the card.
  • On the earlier FAIL: its blocking finding is right and I re-derived every part of it (the finding(types): 9 renderer type spellings reached through declared node slots resolve in NO arm of AnyComponentSchema — live components the validator cannot name #8499 changeset at head declares the deferral; both cited comments say what was claimed). One sentence of it was wrong (F1 above) and propagated into the patched docblock.

Independence

Implemented-by: branch `claude/issue-8784-arm-named-export-pin` (a mode:subagent dev is recorded by its BRANCH, having no session of its own)
Reviewed-by: session_01Jmxdo7bmeqCQHLSfmLVX9w (the domain:spec@objectui seat), ruling produced by a context-isolated ceiling-tier review subagent

Same-session test, applied literally to what the two lines carry: Implemented-by: resolves to a branch and no session; Reviewed-by: resolves to session_01Jmxdo7bmeqCQHLSfmLVX9w. They are not the same session identifier, so SELF-REVIEW does not fire by the letter and I rule. Stated plainly rather than overstated: the card's Claim: names that same session as the PM seat that dispatched the dev, and all four commits carry a Claude-Session: trailer naming it — those trailers are constants, not readings, and are not treated as evidence here; independence rests on context isolation (this subagent was fed only the card, the PR, the prior ruling and its own clone), not on distinct sessions. The Co-Authored-By trailers were likewise given no weight.

VERDICT: PASS — mechanism, firing legs, census and the FAIL's demands all re-derived; seven non-blocking prose/hardening items in ③


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Seat correction — F6 falsifies the premise I overrode the dev on. Recording it here because I asserted it to the dev as the decisive ground.

In round 3 the dev recommended shipping two residuals untouched. I overrode it and authorised one, and the reason I gave was:

a PR that corrects a fact on one surface and leaves the same fact imprecise on another is shipping two disagreeing faces, and the changeset is the face that gets published into packages/types/CHANGELOG.md and outlives the docblock's audience.

The bolded half is false for this changeset, and the review measured both sides of it rather than one:

population in release commit 59f61cfb8 reached a CHANGELOG hunk
62 changesets with empty frontmatter 0
40 changesets with non-empty frontmatter (the control) 40

.changeset/8784-arm-named-export-pin.md has empty frontmatter. ⇒ it will be deleted at release and published nowhere. My "outlives the docblock's audience" was wrong in exactly the direction that made my argument work.

The error was a one-sided measurement, and it was available to both of us. The dev reported the mechanism as "confirmed by measurement rather than assumption", citing git log --diff-filter=D showing the release commit deleting the batch. That measures the deletion. It does not measure the copying, and publication was inferred from deletion. I repeated the inference instead of asking for the other half. The reviewer measured the other half, with a control that fires — which is the whole difference between the two readings.

What does and does not change

Nothing in the tree moves. The review is explicit and I agree: both edits my override produced are correct on their own merits — a stale interval removed, and two faces of one PR made to agree. A correct edit reached through a false premise is still a correct edit. ⇒ ⛔ this is not a reason to revert round 4, and ⛔ not a reason to move the head and void a PASS.

⚠️ The PR body is a different matter — it becomes the squash message, i.e. permanent history, and it states the false mechanism as its rationale. A body PATCH moves no head and voids no review, so that one is being corrected before landing.

The generalisable form, since this is the second time tonight

I have twice reported a negative from a probe whose other half I never ran — once with a control that could not fire (objectui#8563, objectui#8811), and now once with a measurement that established half a mechanism and let me infer the rest. ⛔ "X is deleted at release" does not establish "X is published at release." Both halves, or no claim.

⭐ And the dev was right that the review round was free; that part of my override stands. It was the reason I gave for spending it that was wrong, not the arithmetic.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Landing provenance — PR #9070 (card objectui#8784)

Reviewed and landed head: 19f8ab48d3803690935db75f717a832ae0c8029a. Gate checkout: fresh objectstack clone os-fresh2, origin/main = ef180302f55955baf9994a91d0793bbd3926b068.

① in-seat clause-② contract review — PASS ON RECORD

Clause-②: yes, so a review at CONTRACT_REVIEW_TIER was owed and was performed. Record: 5627360729, adopted verbatim on head 19f8ab48d3803690935db75f717a832ae0c8029aVERDICT: PASS, no blocking findings, seven non-blocking items in ③.

⭐ The ruling was produced by a context-isolated ceiling-tier subagent fed only the card, this PR, and the prior FAIL 5626404152. ⛔ It was not fed the dispatch order or this seat's conclusions. Independence is recorded as the charter's pair — Implemented-by: the branch (a mode:subagent dev has no session of its own), Reviewed-by: this seat — and the reviewer applied the same-session test explicitly and stated that independence rests on context isolation, ⛔ not on distinct sessions, and that the Co-Authored-By / Claude-Session: trailers were given no evidentiary weight. That is the correct reading and it is why the PASS stands.

⚠️ The head has not moved since the review: git ls-remote and the PR object both read 19f8ab48d…. The only write after the ruling was a PR-body PATCH, which moves no head and voids no review.

⭐ The review overturned this seat, and the correction is part of the record

Finding F6 falsified the premise on which I overrode the dev in round 4. I argued the changeset "is the face that gets published into packages/types/CHANGELOG.md". Measured in release commit 59f61cfb8:

population reached a CHANGELOG hunk
62 changesets with empty frontmatter 0
40 with non-empty frontmatter (control) 40

This PR's changeset has empty frontmatter ⇒ deleted at release, published nowhere. The dev's supporting evidence measured the deletion and never the copying, and I took the inference. ⛔ "X is deleted at release" does not establish "X is published at release."

⇒ ⛔ Nothing in the tree was reverted — a correct edit reached through a false premise is still correct — but the PR body, which becomes the squash message and therefore permanent history, was PATCHed so neither paragraph asserts the false mechanism. Seat correction at 5627367527.

② the enqueue gates

gate exact command exit
carriers PM_SWEEP_REPO=objectstack-ai/objectui node scripts/pm/check-clause2-carriers.mjs --pair 9070 0 — "the clause-② declaration is readable in the fixed spelling and both carriers agree"
governed surface node scripts/pm/check-governed-merges.mjs --pr objectstack-ai/objectui#9070 0 — 0 of 2 paths hit the register

Run before the carriers were stripped, so "both carriers agree" was read at present, which is the state a Clause-②: yes pair is supposed to hold until its review clears.

⚠️ check-widening-tells is not cited: its C5 row applies to pairs declaring no, and this pair declares yes. ⛔ Not run, ⛔ not claimed.

③ every check, not a required subset

35 check-runs on 19f8ab48d…: 32 success + 3 skipped, 0 non-green, all completed. Reported total_count 35 == returned array length 35. Legacy combined status success. A body PATCH does not re-trigger CI, so this remains the reading on the landed head.

charter provenance, with its own controls

The seven governing files are byte-identical between the clone's ad715aca57… and main ef180302f5…. Controls: the same two commits diff to 36 files / +3466 / -147 unfiltered, and each of the seven paths resolves to a real blob (not MISSING) at both revs.

carriers — STRIPPED, both together

needs:contract-review removed from both this PR and card objectui#8784 in one stroke, now that the review has cleared. The six non-blocking findings that would have required a head move are routed to objectui#9087 rather than patched here, because fixing prose in the tree would void the PASS for no gain — the reviewer's own words: "nothing in the tree needs to move."

Landing by squash through the merge queue.


Generated by Claude Code

@os-warren
os-warren marked this pull request as ready for review September 11, 2026 00:37
@os-warren
os-warren added this pull request to the merge queue Sep 11, 2026
Merged via the queue into main with commit ce7c5e9 Sep 11, 2026
37 checks passed
@os-warren
os-warren deleted the claude/issue-8784-arm-named-export-pin branch September 11, 2026 00:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants