Skip to content

fix(plugin-chatbot): the build panel reads the build-verify part beside the tree, and an unknown phase is not "Building" (objectui#11988) - #11997

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-11988-build-verify-receiver
Oct 9, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-11988-build-verify-receiver

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

Fixes #11988

Clause-②: yes

The build-progress receiver in @object-ui/plugin-chatbot now reads cloud's post-apply verification loop. This is the receiver that objectstack-ai/cloud#2172's ruling A orders re-filed. Implemented in session https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 (seat domain:ui#3, claim 6064346951).

What changed

  • mapMessages.ts
    • extractBuildProgress reads the tree from the data-build-progress parts whose id is not build-verify.
    • A new extractBuildVerify reads the build-verify part: its phase, hop and tool.
    • The caller attaches the verify state to the tree it verifies, as buildProgress.verify. A build-verify part never displaces the tree, in either part order.
    • Phases are read against the spec vocabulary. A value outside it, or a frame without one, is unknown and is no longer coerced to structure.
  • ChatbotEnhanced.tsx
    • ChatBuildProgress.phase widens to the spec's four phases plus unknown.
    • ChatBuildProgress gains an optional verify: { phase, hop?, tool? }.
    • In BuildProgressPanel, an unknown tree phase shows a warning header ("Unknown build phase") instead of "Building X…".
    • Under the tree, a verify line reads "Checking the change… step N" for each hop, with the tool name as its tooltip, and "Checked the change" on done. An unknown verify phase shows the warning.
  • All ten language packs: four new keys, chatbot.build.verifying, chatbot.build.verifyStep, chatbot.build.verified and chatbot.build.unknownPhase.
  • .changeset/11988-build-verify-receiver.md: minor on both packages.

The wire shape these pins read

This session cannot read objectstack-ai/cloud#2721: the GitHub API answers 403, and attaching objectstack-ai/cloud is refused for lack of access. The fixtures therefore follow the card's restatement of the wire shape:

  • the part type is data-build-progress;
  • the part id is build-verify, beside build-progress;
  • a hop is { phase: 'verify', hop, tool };
  • the exit is { phase: 'done' }.

Two measurements changed the route the claim names

The claim says the phase values come from the spec's runtime BUILD_PROGRESS_PHASES / BuildProgressFrameSchema. Two measurements ruled that out.

  1. First-load bytes. A value import of BUILD_PROGRESS_PHASES and BUILD_PROGRESS_FRAME_TYPE from @objectstack/spec/ai puts the spec's AI module on the first load, because that module is not tree-shaken. Measured with a local console vite build and pnpm check:eager-closure on commit ee4fbd1:
    • the aggregate went from 3,390,981 to 3,418,790 B gzip (+27,809), of which vendor-objectstack was +27,540;
    • aggregate verdict: "❌ Console eager closure is 3338.7 KB gzipped across 337 chunks — 26.7 KB over the 3312.0 KB budget."
    • per-chunk verdict: "❌ vendor-objectstack 1595.1 KB / 1585.0 KB ceiling (OVER by 10.1 KB)".
  2. The spec floor. Naming the spec's BuildProgressPhase in the exported ChatBuildProgress puts an @objectstack/spec/ai symbol into the published .d.ts. pnpm check:spec-floors, which runs on pushes to main and before publish, then reports "@object-ui/plugin-chatbot [floor-too-low] packages/plugin-chatbot/dist/ChatbotEnhanced.d.ts references BuildProgressPhase from @objectstack/spec/ai, which @objectstack/spec@17.0.0 does not export". Fixing that needs package.json and the lockfile, which are outside the claim.

What landed instead. The vocabulary is read through the spec's TYPES. That costs nothing at runtime and puts no spec symbol in the typings.

  • mapMessages.ts imports the spec's types with import type, which is erased from the typings. Its phase table is a Record over the spec's BuildProgressPhase, so a missing or extra phase is a compile error. Its frame type is typed typeof BUILD_PROGRESS_FRAME_TYPE.
  • The exported phase union is spelled out. buildVerify-11988.test.tsx asserts at compile time, through the package's tsconfig.test.json, that it equals the spec's union in both directions.
  • The same test iterates the spec's runtime BUILD_PROGRESS_PHASES and pins every member.
  • The four literals therefore appear once in the source, and the compiler holds them to the spec both ways. Whether that meets the card's "not restated" is raised for the PM in the report.

First-load bytes

This change's own cost, from a local console vite build and pnpm check:eager-closure, base fff07fb against 22a59ea: +377 B gzip. Of that, plugin-chatbot is +345, i18n-locale-en +41 and index +9.

At head 4549bcf, which merges main 5d77c09, after the eager ceiling was re-pinned to 3,281,467 B by objectui#11982 under ruling 6070754914:

  • main's own push run at 5d77c09 reads "✅ Console eager closure is 3160.0 KB gzipped across 289 of 2474 chunks (budget: 3204.6 KB, headroom: 44.5 KB)."
  • This PR reads 3160.3 KB against 3204.6 KB: Status PASS (budget bot comment 6071849392). That is about +0.3 KB over main, within the margin.
  • The local reading at 4549bcf is 3,236,156 B against the 3,281,467 B ceiling: "✅ Console eager closure is 3160.3 KB gzipped across 289 of 2474 chunks (budget: 3204.6 KB, headroom: 44.2 KB)."
  • Every per-chunk row is ✅: vendor-objectstack 1561.8 / 1585.0 KB, ui-components 278.8 / 282.2 KB, framework 73.5 / 97.7 KB and i18n-locale-en 44.2 / 48.8 KB.

This PR does not edit any ceiling and moves nothing lazily.

Published typings (Clause-②)

Read on the built dist/index.d.ts:

  • the entry re-exports ChatMessage and ChatbotEnhancedProps from ./ChatbotEnhanced.js;
  • ChatMessage.buildProgress? is a ChatBuildProgress;
  • its phase is ChatBuildPhase = 'structure' | 'data' | 'verify' | 'done' | 'unknown', where it was 'structure' | 'data' | 'done', and it carries the new optional verify.

The entry's type graph widens, so @object-ui/plugin-chatbot takes a minor, as the claim expected. @object-ui/i18n gains four keys, also minor. After this change, check:spec-floors has no finding for @object-ui/plugin-chatbot.

Verification (at 22a59ea; re-run at the merge head 4549bcf as noted)

  • pnpm --filter @object-ui/plugin-chatbot type-check and pnpm --filter @object-ui/i18n type-check (each is tsc --noEmit && tsc -p tsconfig.test.json): exit 0. Re-run at 4549bcf: exit 0.
  • pnpm exec vitest run packages/plugin-chatbot/ packages/i18n/ --maxWorkers=2: "Test Files 140 passed (140)", "Tests 1920 passed | 13 skipped (1933)". Re-run at 4549bcf: the same counts.
  • Tests outside these packages that read the touched files: column-identity.ratchet, check-i18n-call-site-keys, check-i18n-en-drift, check-i18n-dead-keys, one-authority-per-exported-name-6273, check-eager-closure-budget and ObjectView.viewConfigGate. Result: "Test Files 7 passed (7)", "Tests 524 passed (524)".
  • These gates exited 0: check:i18n-keys, check:i18n-drift ("No en value changed in this range."), check:i18n-dead-keys (report only; none of the new keys is listed), check:i18n-designer-parity, check:control-bytes, check:test-path-roots, check:changeset-claims, check:pending-changeset-literals and scripts/check-changeset-presence.mjs.
  • pnpm exec eslint on the 13 touched source files returned 0 errors. All 10 warnings are in ChatbotEnhanced.tsx, on lines this diff does not touch, and the base version has the same 10.
    • The 13 files were read from --format json, and none is ignored by eslint.config.js.
    • Narrowing to these files excludes nothing: type-aware linting is off (parserOptions.project and projectService are unset for these files), and no custom rule reads another file. So this diff cannot change the lint result of a file it does not touch.
  • scripts/check-governed-queue-guard.mjs --test on the diff answers NOT GOVERNED.
  • Not measured locally.
    • The type-check of importers (app-shell, console) is left to CI. The only importer, app-shell's AiChatPage, builds buildProgress from buildProgressFromDraftReview and spreads it, so a wider union cannot break that site.
    • check:spec-floors cannot judge the 15 packages that are not built in this worktree (no-artifact findings).

Reverse verification (at 22a59ea, through ablation-replace)

Every leg restored with the blob equal to HEAD and git diff HEAD empty.

  1. "Last part wins" (the tree reader takes every part): 4 failed, 10 passed. Red: "a verify hop never displaces the finished tree", "a verify part with no tree beside it is not read as a build tree", "shows the finished tree AND a verify line that advances per hop" and "closes the verify line on done".
  2. An unknown phase coerced to structure: 2 failed, 12 passed. Red: "a phase outside the vocabulary is unknown on either part…" and "an unknown tree phase is surfaced as a warning, not rendered as Building".
  3. The verify row deleted from the phase table: 5 failed, including "reads every phase of the spec vocabulary as itself…". tsc --noEmit also fails: "TS2741: Property 'verify' is missing … required in type Record over structure, data, verify, done". (An earlier try at this leg passed an empty --replacement, which the tool refused before running anything. It was redone with --delete.)
  4. The published union gains a member the spec lacks: the source tsc stays green and the test project fails twice with "TS2344: Type 'false' does not satisfy the constraint 'true'". This is the compile-time pin working in the direction the source cannot see.

Acceptance notes

  • A build-verify part on a message with no build tree draws no verify line. This covers an apply_edit turn, if cloud verifies those. The old reader drew "Building your app…" there, and that is gone. Carrying the line without a tree needs a sibling message key, which means widening chatMessageAdapter.ts's runtime-only key list, outside this claim. Carrier: none.
  • A tree frame with no phase now reads unknown (a warning), where it used to read structure. The spec declares phase required on every frame.
  • hop is rendered as sent ("step N"). The spec leaves the base, 0 or 1, to the emitter, so a 0-based loop would read "step 0" first. That is for the cloud seat to know.
  • The verify line keeps its spinner if the stream ends without a done frame. The tree behaves the same way today.
  • The spec's own JSDoc on BUILD_PROGRESS_PHASES goes stale once this lands. It still describes this reader as coercing to structure, and cites it by line address. Carrier: none.
  • This change adds about 0.37 KB to the eager closure. It lands within the margin objectui#11982 re-pinned (see "First-load bytes").

Generated by Claude Code

claude added 3 commits October 8, 2026 17:02
…de the tree, and an unknown phase is not "Building" (objectui#11988)

The receiver took the last data-build-progress part whatever its id and
coerced every phase but data/done to structure, so cloud's post-apply
verification hop (part id build-verify) replaced the finished tree with
"Building your app...". The tree and the verify part are now read apart by
part id; phases are read against @objectstack/spec/ai's
BUILD_PROGRESS_PHASES, and a value outside it is surfaced as unknown. The
panel draws a verification line that advances per hop and closes on done.

Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8
Co-authored-by: Claude <noreply@anthropic.com>
…pec's types, not its runtime module (objectui#11988)

A value import from @objectstack/spec/ai does not tree-shake: it moved the
console's eager closure about 27 KB gzipped over budget. The frame type and
the phase table are now typed by the spec's own declarations (a Record over
BuildProgressPhase, so a missing or extra phase is a compile error), and the
test reads the spec's runtime BUILD_PROGRESS_PHASES to pin every member.

Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8
Co-authored-by: Claude <noreply@anthropic.com>
…hed build-progress typings (objectui#11988)

Naming BuildProgressPhase in ChatbotEnhanced's exported interface put an
@objectstack/spec/ai reference into the published .d.ts that the package's
^17.0.0 floor cannot carry (check:spec-floors: floor-too-low, needs
^17.5.0). The exported phase union is spelled out instead, and a
compile-time test holds it equal to the spec's BuildProgressPhase in both
directions; the type-only spec import stays internal to mapMessages and is
erased from the typings.

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

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 337 chunks) 3311.6 KB 3312.0 KB
Main entry chunk (gzip) 71.4 KB 350 KB
Entry file index-BVyVyEqG.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) 18.33KB 6.73KB
app-shell (runtime-config.js) 22.59KB 7.89KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 41.19KB 11.12KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 587.07KB 141.28KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 240.39KB 67.09KB
fields (index.js) 268.74KB 68.04KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.50KB 11.82KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 53.43KB 15.54KB
plugin-charts (index.js) 84.71KB 23.25KB
plugin-chatbot (index.js) 201.12KB 47.86KB
plugin-dashboard (index.js) 144.20KB 38.95KB
plugin-designer (index.js) 231.46KB 48.87KB
plugin-detail (index.js) 248.57KB 65.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 177.11KB 45.88KB
plugin-gantt (index.js) 179.17KB 45.07KB
plugin-grid (index.js) 248.22KB 68.84KB
plugin-kanban (index.js) 52.22KB 16.38KB
plugin-list (index.js) 117.72KB 29.41KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 39.10KB 11.81KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 91.09KB 22.94KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 22a59eadb1f823b2cbd6a3588cd38bbbd533f24b
Local-runs: none

Inputs: card objectui#11988 (body and comments 6064346951, 6064353482, 6064386281, 6065869924, 6065909581), PR objectui#11997 (body, 14-file list, net diff against main at the head), the published entry packages/plugin-chatbot/src/index.tsx and the two package manifests at the head, the spec at the @objectstack/spec@17.7.0 tag (the version the head's lockfile resolves), and the 43 check-runs on the head: 40 success, 3 skipped (the coverage matrix placeholder, Test (coverage), dependabot), 0 failures. Type Check, Test (8 shards + dist pins), Lint, Bundle Analysis, Changeset Bump Policy, Changeset Declaration, README Export Check, Spec Main Shape Gate and Governed Surface Queue Guard are all success.

① Derived judgments

Accept-set changes the diff implies, each judged:

  1. Tree reader keyed by part id — right. extractBuildProgress now reads the LAST data-build-progress part whose id is not build-verify; it used to take the last such part whatever its id. AnyPart.id already existed. The existing id: 'bp-1' fixture (useObjectChat.setMessagesHonest-8342) still reads as the tree; the new tests pin both part orders. Matches scope item 1 and the card's wire shape.
  2. Verify reader — right. extractBuildVerify reads the last build-verify part: phase through the vocabulary, hop only when a number, tool only when a string. That is the spec frame (hop optional non-negative integer, tool optional string). hop is rendered as sent, which the spec leaves to the emitter's base (escalated under ③).
  3. Phase accept-set is the spec's closed vocabulary — right. BUILD_PHASES is a Record typed over the spec's BuildProgressPhase, so a missing row or an extra row is a compile error; readBuildPhase does an own-property check, so constructor and friends do not leak in. BUILD_FRAME_TYPE is typed typeof BUILD_PROGRESS_FRAME_TYPE, which at the 17.7.0 tag is a const string literal, so the pin holds. Anything else, including a frame with no phase, is unknown. Before: data and done read as themselves and everything else, structure and verify included, read as structure.
  4. Behaviour change for a tree frame whose phase is missing or outside the vocabulary — right, and a real change. Such a frame now maps to unknown and the panel header shows the amber warning icon with "Unknown build phase"; it used to map to structure and show the spinner with "Building X…". Grounds: BuildProgressFrameSchema declares phase required (17.7.0 tag); the ruling asks for an undeclared phase to be warned about, not coerced; every reachable fixture in objectui carries a phase; objectstack's packages emit no data-build-progress frame at all outside the spec and its tests; the reload path (buildProgressFromDraftReview) always emits done and is untouched. The isDone affordances (open, design, preview) and the data counter are unchanged.
  5. Verify state attaches only to a tree — right, ruled. buildProgress is the tree (stream part or draft-review synthesis) spread with verify when both exist. A build-verify part on a message with no tree yields no buildProgress, where it used to yield a "Building your app…" panel. Ruled A in 6065909581 (question 2).
  6. Panel header — right, one note. isUnknown is "not done, not structure, not data", so a tree part carrying the in-vocabulary verify on the build-progress id would also show the "Unknown build phase" warning. That is outside the declared wire shape (verify rides its own part), it is never "Building", and the type allows it only because the two phase slots share one union. Noted, not a defect against the card.
  7. Verify line — right. verify renders spinner + "Checking the change…" and, when hop is present, " step N"; done renders check + "Checked the change"; any other value renders the warning; tool goes to title. Advances per hop, closes on done, as the acceptance asks.
  8. Copy through packages/i18n — right. Four keys, chatbot.build.verifying, verifyStep, verified, unknownPhase, in exactly the ten packs under src/locales/ (ar, de, en, es, fr, ja, ko, pt, ru, zh). verifyStep carries the n hole in all ten and the call site passes n; the other three have no holes. check:i18n-keys, check:i18n-drift ("4 key(s) added", no en value changed) and check:i18n-designer-parity exited 0 per the report, and Lint/Test are green on the head.
  9. Public surface — right, and read off the entry. index.tsx re-exports ChatMessage and ChatbotEnhancedProps from ./ChatbotEnhanced, and ChatMessage.buildProgress? is ChatBuildProgress (:114), so the widened phase ('structure' | 'data' | 'verify' | 'done' | 'unknown', was three members) and the new optional verify reach the published typings through ChatbotEnhancedProps.messages (input) and through uiMessageToChatMessage / buildProgressFromDraftReview (output), and likewise through ObjectChatMessage and SeamChatMessage. ChatBuildPhase is a non-exported alias, so the entry's export list is unchanged (README Export Check success). The only spec import is import type in mapMessages.ts, erased from the typings, and ChatbotEnhanced.tsx imports nothing from the spec, so the published .d.ts names no spec symbol and the ^17.0.0 floor stands. The spec's verify member and the BuildProgressPhase / BUILD_PROGRESS_FRAME_TYPE exports exist at the resolved 17.7.0 (tag read), and the compile-time pin in buildVerify-11988.test.tsx is compiled by CI, because plugin-chatbot's type-check script is tsc --noEmit && tsc -p tsconfig.test.json and Type Check is success.
  10. File surface — right. All 14 files are inside the claim's list: mapMessages.ts, ChatbotEnhanced.tsx, the ten packs, the test beside them, .changeset/11988-build-verify-receiver.md. extractBlueprintProgress, the spec, packages/components/src/ui/**, check-eager-closure-budget.mjs and any ceiling or sideEffects setting are untouched.
  11. First-load — right. Bundle Analysis is success on the head and the budget comment 6065811529 reads 3311.6 KB / 3312.0 KB, so the claim's stop condition was not reached and no ceiling edit was needed. The residual headroom is escalated under ③.

② Semver level

.changeset/11988-build-verify-receiver.md declares minor on @object-ui/plugin-chatbot and minor on @object-ui/i18n; both are in the fixed group of .changeset/config.json, and Changeset Bump Policy, Changeset Declaration, Changeset Claim Re-read and Changeset Fixed Group Check are success.

  • @object-ui/plugin-chatbot minor — right. The entry's type graph widens (judgment 9): additive on the input side; on the output side a host that narrowed buildProgress.phase to the old three-member union would stop compiling. objectui's AGENTS.md rules that changesets never declare major and that even a breaking change takes minor with the breaking semantics written in the body, and the body carries a "Type change" bullet naming both the widened union and the new member.
  • @object-ui/i18n minor — right. ./locales and ./locales/* are published export paths, so four new keys in every pack widen a published surface additively.
  • Clause-②: yes — right. Declared on the claim (6064346951) and on the PR body, and the diff publishes exactly the two widenings the claim pre-named. Seat 2's retriage question (6064353482) was withdrawn in 6064386281; the claim's reading stands.

③ Boundary flags

  • Open question 1 (type-level read of the vocabulary instead of a value import) — answered A in 6065909581; this review concurs. The four literals appear in the source, but the Record over BuildProgressPhase and the two-way compile-time pin make any drift a type error in CI, which is what the card's "not restated" rule exists to prevent; the value import measured 26.7 KB over the eager ceiling and option C needs a floor bump outside the claim.
  • Open question 2 (tree-less build-verify part draws no line) — answered A in 6065909581; this review concurs. No measured producer; carrying it would widen ChatMessage and the adapter's runtime-only key list on a guess. New card if cloud confirms edit turns emit it.
  • Deviation: cloud PR fix(plugin-view): coerce i18n tab-label helpers to string (TS2322) #2721 unreadable, fixtures follow the card's restatement — answered by the card itself, whose wire-shape section is taken from that PR's section the cloud seat keeps authoritative. The acceptance line about "measured frames" is therefore met against the declared shape, not a replay of cloud's bytes.
  • Deviation: exported union spelled out rather than the spec type — answered with question 1; the floor stands (judgment 9).
  • Deviation: check:readme-exports not run locally — answered by README Export Check success on the head.
  • Deviation: importer type-check left to CI — answered by Type Check success; the only importer (AiChatPage) spreads the synthesized value, and app-shell and console hold no exhaustive switch on buildProgress.phase.
  • Deviation: some runs outside the lock — process, not contract; every final suite ran under it and the head's check-runs are the gate verdicts.
  • Deviation: commit trailer form — not a contract matter; noted.
  • Out-of-scope 1: the spec's JSDoc on BUILD_PROGRESS_PHASES still describes this reader as coercing to structure and cites it by line — escalated to the seat: an objectstack follow-up to refresh the Consumer section once this lands; carrier none today.
  • Out-of-scope 2: hop base 0 or 1 — escalated to the cloud seat (objectstack-ai/cloud#2172), as the dev named it.
  • Out-of-scope 3: about 126 B of eager headroom left, with objectui#11973 and objectui#11989 adding to the same en pack — escalated to the objectui#11939 decision box, as the dev named it.

Implemented-by: claude/issue-11988-build-verify-receiver
Reviewed-by: session_01CGZy1BGCjdN5cXqL9cnvB8

VERDICT: PASS

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

❌ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 337 chunks) 3312.0 KB 3312.0 KB
Main entry chunk (gzip) 71.4 KB 350 KB
Entry file index-KUarf0So.js —
Status FAIL —

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.

Which half objected:

Eager-closure half Verdict
Aggregate closure ceiling ❌ over its ceiling
Per-chunk ceilings ✅ pass
Per-chunk membership (declared packages) ✅ pass
Ceiling sensitivity (headroom) ✅ pass
Ceiling freshness (checkout vs. base branch) ✅ pass

📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 18.33KB 6.73KB
app-shell (runtime-config.js) 22.59KB 7.89KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 41.19KB 11.12KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 587.07KB 141.28KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 240.39KB 67.09KB
fields (index.js) 268.74KB 68.04KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.50KB 11.82KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 53.43KB 15.54KB
plugin-charts (index.js) 84.71KB 23.25KB
plugin-chatbot (index.js) 201.12KB 47.86KB
plugin-dashboard (index.js) 144.20KB 38.95KB
plugin-designer (index.js) 231.46KB 48.87KB
plugin-detail (index.js) 248.57KB 65.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 177.11KB 45.88KB
plugin-gantt (index.js) 179.17KB 45.07KB
plugin-grid (index.js) 249.56KB 69.16KB
plugin-kanban (index.js) 52.22KB 16.38KB
plugin-list (index.js) 117.80KB 29.42KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 39.10KB 11.81KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 91.09KB 22.94KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
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

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 289 chunks) 3160.3 KB 3204.6 KB
Main entry chunk (gzip) 71.6 KB 350 KB
Entry file index-BINCfTaX.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) 19.52KB 7.19KB
app-shell (runtime-config.js) 22.59KB 7.89KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 41.19KB 11.12KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 587.83KB 141.44KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 240.39KB 67.09KB
fields (index.js) 268.74KB 68.04KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.50KB 11.82KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 53.43KB 15.54KB
plugin-charts (index.js) 84.71KB 23.25KB
plugin-chatbot (index.js) 201.12KB 47.86KB
plugin-dashboard (index.js) 144.20KB 38.95KB
plugin-designer (index.js) 233.53KB 49.80KB
plugin-detail (index.js) 248.57KB 65.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 177.11KB 45.88KB
plugin-gantt (index.js) 179.17KB 45.07KB
plugin-grid (index.js) 249.56KB 69.16KB
plugin-kanban (index.js) 52.22KB 16.38KB
plugin-list (index.js) 117.83KB 29.43KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 39.10KB 11.81KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 91.16KB 22.97KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 4549bcf5360e658d7f0d2edb3f07593fe8780851
Local-runs: none

Re-record of 6066202758 (PASS on 22a59ead), on the landing head the seat's hold in 6066239380 asked for. Inputs: card objectui#11988 (body and every comment, 6064346951 through 6071719817), PR objectui#11997 (body, 14-file list, net diff against main 5d77c099 at the head), the head's tree where the diff reaches it (packages/plugin-chatbot/src/index.tsx, the two package manifests, .changeset/config.json, scripts/check-eager-closure-budget.mjs, the lockfile's @objectstack/spec resolution), the spec at the @objectstack/spec@17.7.0 tag (the version the head's lockfile resolves), and the 43 check-runs on the head: 40 success, 3 skipped (the coverage matrix placeholder, Test (coverage), dependabot), 0 failures. Type Check, Test (8 shards and dist pins), Lint, Bundle Analysis, Changeset Bump Policy, Changeset Declaration, Changeset Claim Re-read, Changeset Fixed Group Check, README Export Check, Spec Main Shape Gate, Docs Route Eager Closure Check and Governed Surface Queue Guard are all success.

What changed since 6066202758: nothing in the diff. The branch took two merges of main, bffb4c01 (second parent fbad0788) and 4549bcf5 (second parent 5d77c099), each with the branch as first parent. The net diff against main at this head is the diff reviewed at 22a59ead (fff07fbb..22a59ead) once index hashes and hunk offsets are set aside: the same 14 files, +340/−11, every added and removed line the same. The only movement is the ten pack hunks sliding two or three lines down for keys main added in other namespaces (objectui#11989, objectui#11973, objectui#11863 twice). main did not touch packages/plugin-chatbot between fff07fbb and 5d77c099. Judgments 1 to 10 of 6066202758 are therefore re-read against this head and stand; what is new is the first-load reading and the check-runs.

① Derived judgments

  1. Tree reader keyed by part id — right. buildFrame takes the last data-build-progress part the predicate keeps; the tree reads every id but build-verify, an absent id included, so the existing bp-1 and id-less fixtures still read as the tree. AnyPart.id?: string already exists at the head.
  2. Verify reader — right. extractBuildVerify reads the last build-verify part: phase through the vocabulary, hop only when a number, tool only when a string. The spec frame at the 17.7.0 tag is phase required, hop an optional non-negative integer, tool an optional non-empty string. The reader is no stricter than the frame; it does not re-check min(1) or integrality, which would only blank a tooltip or render the hop as sent (③).
  3. Phase accept-set is the spec's closed vocabulary — right. BUILD_PHASES is a Record over the spec's BuildProgressPhase, so a missing row or an extra row is a compile error; readBuildPhase is an own-property check, so prototype names do not leak in; BUILD_FRAME_TYPE is typeof BUILD_PROGRESS_FRAME_TYPE, a const string literal at the tag, so the pin holds. Anything else, a frame with no phase included, is unknown. Before: data and done read as themselves and everything else, structure and verify included, read as structure.
  4. A tree frame whose phase is missing or outside the vocabulary now reads unknown — right, and a real behaviour change. The panel header shows the amber warning "Unknown build phase" where it showed the spinner and "Building X…". BuildProgressFrameSchema declares phase required, the ruling asks for an undeclared phase to be warned about, every reachable fixture in objectui carries a phase, and the reload path (buildProgressFromDraftReview) emits done and is untouched. The isDone affordances and the data counter are unchanged.
  5. Verify state attaches only to a tree — right, ruled A (6065909581, question 2). A build-verify part on a message with no tree yields no buildProgress, where it used to yield a "Building your app…" panel.
  6. Panel header — right, one note. isUnknown is "not done, not structure, not data", so a tree part carrying the in-vocabulary verify on the build-progress id would show the warning. That is outside the declared wire shape (verify rides its own part), it is never "Building", and the type allows it only because the two phase slots share one union. Noted, not a defect against the card.
  7. Verify line — right. verify renders the spinner and "Checking the change…" plus " step N" when hop is present; done renders the check and "Checked the change"; any other value renders the warning; tool goes to title. It advances per hop and closes on done, as the acceptance asks.
  8. Copy through packages/i18n — right. Four keys, chatbot.build.verifying, verifyStep, verified and unknownPhase, in exactly the ten packs under src/locales/ (ar, de, en, es, fr, ja, ko, pt, ru, zh; index.ts, registry.ts and rtl.ts are not packs). verifyStep carries the n hole in all ten and the call site passes n; the other three have no holes. Lint and Test are green on this head, after main's own additions to the same packs merged under them.
  9. Public surface — right, read off the entry at the head. index.tsx re-exports ChatMessage (and its deprecated ChatbotEnhancedMessage alias), ChatbotEnhancedProps, ObjectChatMessage, SeamChatMessage and the two mappers, and ChatMessage.buildProgress? is ChatBuildProgress, so the widened phase ('structure' | 'data' | 'verify' | 'done' | 'unknown', was three members) and the new optional verify reach the published typings on the input side (ChatbotEnhancedProps.messages) and the output side (uiMessageToChatMessage, buildProgressFromDraftReview). ChatBuildPhase is a non-exported alias, so the entry's export list is unchanged (README Export Check success). The only spec import is import type in mapMessages.ts, erased from the typings; ChatbotEnhanced.tsx imports nothing from the spec; the published .d.ts names no spec symbol, the ^17.0.0 floor stands, and the lockfile resolves 17.7.0, where verify, BuildProgressPhase and BUILD_PROGRESS_FRAME_TYPE all exist. The compile-time pin in buildVerify-11988.test.tsx is inside tsconfig.test.json's include, the package's type-check is tsc --noEmit && tsc -p tsconfig.test.json, and CI's Type Check runs it: success.
  10. Importers at the head — right. main moved 25 commits under this branch. The only importer of buildProgress outside the package is still app-shell's AiChatPage, which spreads the value buildProgressFromDraftReview synthesizes, and no file outside the package narrows or switches on buildProgress.phase.
  11. File surface — right. All 14 files are inside the claim's list (6064346951): mapMessages.ts, ChatbotEnhanced.tsx, the ten packs, the test beside them, .changeset/11988-build-verify-receiver.md. extractBlueprintProgress, the spec, packages/components/src/ui/**, scripts/check-eager-closure-budget.mjs, sideEffects and every ceiling are untouched.
  12. First-load — right, and it now passes on the ceiling's own rule. At this head MAX_EAGER_CLOSURE_GZIP_BYTES is 3,281,467 over BASELINE 3,235,899 (objectui#11982, ruling 6070754914 letter D), a 45,568 B margin. Bundle Analysis is success on 4549bcf5 itself (its check-run names this head sha), and the budget comment 6071849392, posted while that run was in progress, reads 3160.3 KB against 3204.6 KB, PASS, with every per-chunk row green. The PR body cites main's own push run at 5d77c099 at 3160.0 KB, so this change costs about 0.3 KB on the first load, the +377 B the dev measured. The ruling's standing rule, quoted in the script's docblock at the head: the ceiling "is not re-pinned per pull request, and a fix that adds first-load bytes within the margin lands without a ruling." This is a fix on a bug card, within the margin, with no ceiling edit and nothing moved lazily, so the hold in 6066239380 and 6066742894 is discharged the way the seat wrote it: merge main, re-measure green, re-record.

② Semver level

.changeset/11988-build-verify-receiver.md declares minor on @object-ui/plugin-chatbot and minor on @object-ui/i18n; both are in the fixed group of .changeset/config.json at the head, and Changeset Bump Policy, Changeset Declaration, Changeset Claim Re-read and Changeset Fixed Group Check are success on 4549bcf5.

  • @object-ui/plugin-chatbot minor — right. The entry's type graph widens (judgment 9): additive on the input side; on the output side a host that narrowed buildProgress.phase to the old three-member union would stop compiling. objectui's AGENTS.md rules that a changeset never declares major and that a breaking change takes minor with the breaking semantics written in the body; the body's "Type change" bullet names both the widened union and the new member.
  • @object-ui/i18n minor — right. ./locales and ./locales/* are published export paths, so four new keys in every pack widen a published surface additively.
  • Clause-②: yes — right. On the claim (6064346951) and at line start of the PR body, with no arm (at most one is allowed, and yes takes at least minor), and the diff publishes exactly the two widenings the claim pre-named. Seat 2's retriage question (6064353482) was withdrawn in 6064386281.

③ Boundary flags

  • Open question 1 (type-level read of the vocabulary instead of a value import) — answered A in 6065909581; this review concurs, unchanged by the merges. The Record over BuildProgressPhase and the two-way compile-time pin make any drift a type error in CI, which is what the card's "not restated" rule exists to prevent. The value import measured +27.8 KB on the first load; the re-pinned margin is, in the ruling's words, "not an allowance to be spent on a feature", and option C needs a floor bump outside the claim.
  • Open question 2 (tree-less build-verify part draws no line) — answered A in 6065909581; this review concurs. No measured producer; carrying it would widen ChatMessage and the adapter's runtime-only key list on a guess. New card if cloud confirms edit turns emit it.
  • Deviation: cloud PR fix(plugin-view): coerce i18n tab-label helpers to string (TS2322) #2721 unreadable, fixtures follow the card's restatement — answered by the card itself, whose wire-shape section is taken from that PR's section the cloud seat keeps authoritative. The acceptance line about "measured frames" is met against the declared shape, not a replay of cloud's bytes.
  • Deviation: exported union spelled out rather than the spec type — answered with question 1; the floor stands (judgment 9).
  • Deviation: check:readme-exports not run locally — answered by README Export Check success on this head.
  • Deviation: importer type-check left to CI — answered by Type Check success on this head (judgment 10).
  • Deviation: some runs outside the lock — process, not contract; every final suite ran under it and the head's check-runs are the gate verdicts.
  • Deviation: commit trailer form — not a contract matter; it follows objectui's AGENTS.md rule on the model-free trailer pair.
  • Acceptance note: a tree frame with no phase reads unknown — answered, judgment 4.
  • Acceptance note: the verify line keeps its spinner if the stream ends without done — answered; the tree behaves the same way today and the declared wire shape carries a done exit.
  • Out-of-scope 1: the spec's JSDoc on BUILD_PROGRESS_PHASES still describes this reader as coercing to structure and cites it by line — escalated to the seat: an objectstack follow-up to refresh the Consumer section once this lands; carrier none today.
  • Out-of-scope 2: hop base 0 or 1 — escalated to the cloud seat (objectstack-ai/cloud#2172), as the dev named it; the receiver renders the integer as sent.
  • Out-of-scope 3: eager headroom — closed. The 126 B reading and the 4 B breach at bffb4c01 were against the 3,391,484 ceiling; ruling 6070754914 re-pinned it, and the card moved from pm:blocked back to pm:dispatched in 6071719817. Nothing is left to escalate (judgment 12).

Implemented-by: claude/issue-11988-build-verify-receiver
Reviewed-by: session_01CGZy1BGCjdN5cXqL9cnvB8

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 9, 2026 01:02
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 9, 2026
Merged via the queue into main with commit 6d5eb34 Oct 9, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-11988-build-verify-receiver branch October 9, 2026 01:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants