Skip to content

fix(types): declare onNavigate and onAddComment on the detail arm (objectui#7804, plugin-detail slice) - #9343

Draft
os-tesla wants to merge 4 commits into
mainfrom
claude/issue-7804-detail-arm
Draft

fix(types): declare onNavigate and onAddComment on the detail arm (objectui#7804, plugin-detail slice)#9343
os-tesla wants to merge 4 commits into
mainfrom
claude/issue-7804-detail-arm

Conversation

@os-tesla

@os-tesla os-tesla commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

Part of objectui#7804 — the plugin-detail slice. The parent card stays OPEN and stays the parent; PRs land per package (director seat, decision batch #69, 2026-09-07, maintainer verbatim 「其他同意」). ⛔ No closing keyword anywhere in this body.

What lands

DetailSchema — the zod arm that type: 'detail' selects — now declares the two handler keys its registered renderer reads, as objectui#6124 RUNTIME SLOTS on both faces: a named refusal on the JSON face, a callable twin on the TypeScript face.

key disposition channel, MEASURED
onNavigate runtime-slot read in DetailView's own body and CALLED there — handleBack, handleEdit, the post-delete redirect
onAddComment runtime-slot DetailView never calls it: it is FORWARDED as a React prop into the comment composer, whose submit handler awaits it, behind a schema.comments gate the same passthrough keeps alive

Per key, not per prefix. The two share a registration and a document and still reach the renderer by two different routes; the sibling slice on object-kanban got three different dispositions out of three keys sharing one prefix, so the per-key measurement is the deliverable, not a formality.

The mechanism

BaseSchemaCore ends .passthrough() ⇒ a key an arm does not declare is not refused, it stops being judged and the value is KEPT, then reaches the renderer. ComponentRegistry.register('detail', DetailView) registers the component RAW (unlike 'detail-view', which goes through a data-source wrapper), so nothing is interposed and the authored value arrives by identity.

Both keys were declared on NEITHER face: BaseSchema's any-valued index signature typed them on the TypeScript side and .passthrough() kept them on the zod side, while the renderer read and ran them.

Base reading, written BEFORE the code and run on the unmodified tree (a686403b3)

Parse face, all three keys on the same arm:

onNavigate    accepted=true   kept={"action":"toast"}   issues=none
onAddComment  accepted=true   kept={"action":"toast"}   issues=none
onBack        accepted=false  issues=custom@onBack        <= LIT CONTROL, same arm

Driven face, through the real SchemaRenderer (happy-dom, React 19):

TypeError: schema.onNavigate is not a function
    at packages/plugin-detail/src/DetailView.tsx (handleBack)

Red-first run on that tree: 13 tests, 8 passed / 5 failed. The 5 failures are exactly the landing assertions (both members absent from the shape, both authored objects accepted, the derivation reporting both undeclared). The 8 that passed are the reachability legs, the hazard leg and the onBack control — i.e. the instrument was already working before the change moved anything.

⚠️ One prediction MISSED, recorded rather than smoothed. The hazard leg was first written as an expect(...).toThrow() around the click. React 19 does not rethrow a handler error out of the dispatch — it REPORTS it and the click returns normally — so that spelling would have been a green assertion about a hazard that never fired. The leg now reads what React actually reports, and carries a silent-document control beside it. The miss is written into the test's own docblock.

MERGED WITH main — the sibling slice landed, and both derived figures moved

objectui#9338 (the plugin-kanban slice of the same card) merged, taking main to 5a41ce733. main was merged into this branch as a merge commit (8421c1f39) — no rebase, no amend, no force-push. Every figure below is re-read off the merged tree; none is stepped by hand.

One textual conflict, in zod-mirror-parity.test.ts: both slices rewrote the head of the same KnownDrift bullet. Both one-sided resolutions were reproduced against the merged tree FIRST, so that picking a side is mechanically excluded rather than merely unattractive — both are RED:

ours   (40 entries / 63 keys)  ->  2 failed:  entry count  40, ledger derives 41
                                              key total    63, ledger derives 65
theirs (41 entries / 63 keys)  ->  1 failed:  key total    63, ledger derives 65
merged (41 entries / 65 keys)  ->  32 passed (32)

The derivation is the file's own AST instruments (ledgerEntryKeys, ledgerEntryMembers) under the two pins objectui#7733 and objectui#8222 — not arithmetic on the figures in either branch. The bullet now leads with 41 entries / 65 keys and narrates both slices newest-first: the plugin-detail keys onto an existing entry, then the plugin-kanban new entry, then the objectui#8802 retirement already below it.

Three figures in that header are machine-read, not one. Besides the entry count, the KEY total is read by objectui#8222's block and the restatement N of the registered pairs carry TYPE drift TODAY is read by objectui#7733's. The restatement tracks the entry count, not the key total — this slice adds two keys to an entry that already existed, so it does not move: main's 41 auto-merged and is correct untouched. That was verified, not assumed: in the ours run above the restatement read 41 and PASSED while the entry count failed.

⚠️ A semantic collision the merge could not see. objectui#9338's suite 4 CONTROL names one surviving ledger row to prove it reads a SHRINKING map rather than an empty one — and the row it named was detail::DetailSchema.onNavigate, exactly the row this branch drains. Both PRs were green alone; the control only reddens once both are in one tree. Re-derived, never weakened: the witness moves to button::ButtonSchema.onSuccess, one of the 35 rows surviving both slices and belonging to neither. The assertion keeps both halves it had — a non-empty map AND a named row — because the length check alone passes on a map holding a single stale row, which is the reading the control exists to refuse.

After

Gate of record, its own printed line, on the merged tree:

OK  105 arm(s), 212 registration(s) (119 with an arm), 59 reachable handler read(s), 59 judged, 0 left unjudged on an arm with an unresolved spread, 35 exempted by ledger — every judged read is a declared member of its arm.

The two KNOWN_UNDECLARED_READS rows this slice owns are drained, enumerated not counted. The ledger walked 39 rows at this branch's merge-base (a686403b3) → 37 on main now (objectui#9338 drained onCardClick + onQuickAdd) → 35 on the merged tree (this slice drains onNavigate + onAddComment). ⛔ The other 35 are untouched. Earlier revisions of this body quoted 39 → 37 and 37 exempted by ledger; those were correct against the old base and are superseded by the line above.

Contract carrier — it applies, and this is the direction

The accept set moves on a published mirror: a document authoring either key was ACCEPTED and KEPT before and is now REFUSED BY NAME (issue code: 'custom' at the key's own path). That is a move in the narrowing direction, which is still a move ⇒ needs:contract-review on both limbs; the parent card already carries it and this PR carries it too. ⛔ Neither limb is cleared here — that is the PM's.

minor, not major: the 40-package fixed group makes major unavailable (scripts/check-changeset-no-major.mjs, run inside changeset:check), so the breaking accept-set move ships as minor with the reasoning in the changeset.

Files

  • packages/types/src/zod/crud.zod.ts — the two handlerKeyRefusal(..., 'runtime-slot', ...) arms, with the measurement beside each. Untouched by the merge (blob identical before and after), which is why the ablation below still cites it verbatim.
  • packages/types/src/crud.ts — the callable TypeScript twins, signatures taken from the call sites (onNavigate(url, options), onAddComment(text)), matching what views.ts already declares for the same component under its other registration.
  • packages/types/src/__tests__/handler-keys-string-any-mirrors-7344.test.ts — the ledger that owns the crud.zod.ts#DetailSchema pair: 2 rows added (8 sites to 10, 4 runtime slots to 6).
  • packages/types/src/__tests__/zod-mirror-parity.test.ts — the KnownDrift entry grows to three keys; the header bullet is the merge's one conflict, resolved to the derived 41 entries / 65 keys.
  • packages/plugin-detail/src/__tests__/detail-handler-slots-7804.test.tsx — new; the driven measurement, the hazard, the refusal, and a derivation off the read site.
  • packages/plugin-kanban/src/__tests__/handlerKeyDispositionsMeasured-7804.test.tsxmerge fallout only: the suite 4 control witness re-derived, per the collision described above.
  • scripts/check-handler-key-read-sites.mjs — the two ledger rows removed.

⚠️ A type-level assertion that could not fire, caught and replaced. The ledger's KeepsFunction helper answers true for an absent member, because BaseSchema's index signature types it any and [any] extends [never] is false. The four keys already in that list came from string / any, so the helper could fail on them; on these two it could not. A DeclaresExactly assertion (spelled with Equal, like the file's own RetiredIsNever) was added beside it, with two synthetic controls proving it fails on an absent member and on a wrong signature.

Verification

All readings at final head 980126de0 (base 5a41ce733), working tree clean at the time of each run.

Testspnpm exec vitest run packages/types/ packages/plugin-detail/ packages/plugin-kanban/ scripts/ examples/schema-catalog/, through the shared verify lock:

Test Files  596 passed | 2 skipped (598)
Tests  12858 passed | 2 skipped (12860)
os-verify-lock: VERDICT command-exit 0 · held the lock 509s (8m29s) · waited 42s

examples/schema-catalog/ is in the union deliberately: an authored key's acceptance moved, so a package-scoped run is not the blast radius (objectui#9273). packages/plugin-kanban/ joined the union at the merge, because that is where the collision landed.

Type-check — the dependent-set membership read was re-done on the MERGED tree rather than carried forward from the pre-merge round, and it reproduces: 37 workspace manifests name @object-ui/types, and all 37 declare a type-check script (all spelled type-check, hyphenated), @object-ui/site among them. pnpm --filter '...@object-ui/types' type-check under the lock resolves to 42 packages, 41 of which run a type-check task — all 41 green, 0 errors, VERDICT command-exit 0.

Buildpnpm build --concurrency=2 under the lock: Tasks: 43 successful, 43 total, VERDICT command-exit 0.

Gate of recordcheck:handler-key-reads exit 0, line quoted above (35 exempted by ledger).

Other gates re-run on the merged tree, all exit 0: check:control-bytes (7532 tracked text files, 0 findings) · check:new-line-citations (0 new citations added by this branch) · check:changeset-claims · check:spec-symbols · check:doc-types · check:test-path-roots · check:readme-exports · check:dist-completeness · check:unreferenced-sources · check:governed-queue-guard (NOT GOVERNED — 8 path(s) checked against 5 governed surface(s); none matched) · changeset presence (6 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s)) · check:changeset-no-major.

⚠️ NOT MEASURED, each with its reason: the remaining check:* families (56 exist in this repo) — CI owns the farm, and this seat runs the targeted set. check:sdui-registration-pins — exit 2, its own printed line is No console build to weigh at apps/console/dist/assets ... This is exit 2, not a pass; it pins that registrations survive BUNDLING and this diff moves no registration. Remote CI convergence and the ready/queue flip — the PM's, per the dispatch. pnpm test in full — the repo-level scan, CI's run.

⚠️ Two build attempts failed before the green one, and neither was this diff. pnpm --filter '...@object-ui/types' build selects the DEPENDENTS of @object-ui/types, which excludes packages those dependents themselves depend on: @object-ui/react-runtime (deps sucrase only) and @object-ui/auth name @object-ui/types nowhere, so they were never built and @object-ui/components / @object-ui/app-shell could not resolve their declarations. A full pnpm build is green at 43/43, which is the control proving the two failures were filter-closure gaps rather than findings.

Ablation — the declaration is load-bearing, proved on disk

No dist hop is involved: the root vitest config aliases @object-ui/types/zod to packages/types/src/zod/index.zod.ts, so the mutated file IS the module under test. The merge did not touch crud.zod.ts — its blob is 557c86b6ba292441f4f91e3170dac6964da3924d at the pre-merge head and at 980126de0 alike — so this leg stands as run.

Leg run from the COMMITTED state, under trap ... EXIT INT TERM with absolute paths:

HEAD_BLOB=557c86b6ba292441f4f91e3170dac6964da3924d
BEFORE_BLOB=557c86b6ba292441f4f91e3170dac6964da3924d      (equal, non-empty -> instrument OK)
ANCHOR_BEFORE   onNavigate=1  onAddComment=1
ANCHOR_AFTER    onNavigate=0  onAddComment=1              (surgical: the sibling is the control)
MUT_BLOB=2662f39b5313fd5fbaac1f08affd212041cd8e6f          (changed -> the edit reached disk)
MUTATED_VITEST_EXIT=1     Tests 3 failed | 10 passed (13)
MUTATED_GATE_EXIT=1
AFTER_BLOB=557c86b6ba292441f4f91e3170dac6964da3924d
RESTORE_DIFF=empty                                         (git diff HEAD, not an exit code)
ANCHOR_RESTORED onNavigate=1  onAddComment=1
RESTORED_VITEST_EXIT=0    Tests 13 passed (13)
RESTORED_GATE_EXIT=0

All three mutated failures are NAMED on onNavigate; every onAddComment leg stayed green — per key, not a blanket. And the gate of record turned red with its own printed line:

x  1 handler key(s) a registered renderer reads are not declared by their arm:

      'detail'.onNavigate  read at packages/plugin-detail/src/DetailView.tsx:701
          registered in packages/plugin-detail/src/index.tsx; DetailSchema (crud.zod.ts) does not declare it.

Restored by git checkout HEAD -- THE_PATH (spelled as a word: GitHub's body sanitizer eats angle-bracket-shaped fragments even inside code spans), and the restoration is proved by the blob hash returning and an EMPTY git diff HEAD, never by an exit code.

Acceptance notes

  • Filed as objectui#9344 — the gate of record is blind to a read behind an as any cast, so objectui#7804's "39" is a floor rather than a total. Measured with a firing control: 2 live cast-hidden handler reads in production sources, (schema as any).onTabChange in DetailView.tsx (detail / detail-view) and (schema as any)?.onTabChange in containers.tsx (tabs); the control — the same anchor without the on-prefix — returns 33 files. ⛔ Not repaired here: it is a different card's scope, and this branch only RECORDS the DetailView one as a reading in its own test. Dedup ran over all 5 pages of the repo-scoped open-issue list (453 open issues) with objectui#7804 as the known-hit control; the three near neighbours (objectui#8327, objectui#8649, objectui#6152) are each shown non-overlapping in that card's body.
  • noted, not filed: the changeset carrier convention claimed in dispatch does not exist mechanically. No script under scripts/ or .github/workflows/ scans for a leading **BREAKING** (the only two files mentioning the word use it in unrelated prose), and .changeset/config.json uses the stock @changesets/cli/changelog, which publishes bodies verbatim. Across 1489 pending changesets the all-caps **BREAKING spelling appears in 31 and the title-case **Breaking spelling — this changeset's — in roughly 78; first-occurrence line numbers run from 5 to 51, main's own 7804-object-kanban-handler-keys-judged.md carrying it at line 21. This changeset was therefore left as authored. Successor: whoever wants a real carrier convention has to build the gate first.
  • noted, not filed: check:changeset-claims (report-only) flags 16 pending changesets whose bodies name crud.ts, crud.zod.ts or zod-mirror-parity.test.ts — files any @object-ui/types change touches. All 16 predate this branch and none is falsified by it; they describe their own landings, not current totals. Successor: the seat that next edits each of those changesets' own packages.
  • noted, not filed: comments is not declared by this change. DetailView gates the onAddComment forward on schema.comments, kept by the same passthrough — but it is not a handler key, declaring it is an accept-set decision of its own, and objectui#7804's rows are the handler keys. Successor: whoever rules the 'detail' arm's non-handler drift below.
  • noted, not filed: the 'detail' arm declares far fewer keys than DetailView reads — sections, fields, objectName, data, backUrl, editUrl, comments, activities, summaryFields, autoTabs, all kept by the passthrough. Not a handler-key question and outside every row of objectui#7804; the carrier for it is objectui#7804's own 'detail' arm, which stays open as the parent.
  • noted, not filed: the repo's KeepsFunction type helper cannot fail on a member that was ABSENT rather than wrongly typed (an absent member reads any, and [any] extends [never] is false). Fixed in place for this pair with a DeclaresExactly assertion and two synthetic controls; the class successor is whoever next adds a previously-UNDECLARED key to that ledger.

Session that produced this change, written as prose so it survives a body edit: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ


Generated by Claude Code

`BaseSchemaCore` ends `.passthrough()`, so a key the arm for `type: 'detail'`
does not declare is not refused - it stops being judged and the value is KEPT,
then reaches the renderer. `DetailView` is registered RAW for `'detail'`, so
both keys arrived by identity and RAN: `onNavigate` is called in the component's
own body (`handleBack` / `handleEdit` / the post-delete redirect), `onAddComment`
is forwarded as a prop into the comment composer that awaits it.

Measured on the unmodified arm before the code: an authored
`{ "action": "toast" }` parsed GREEN on both keys with the object surviving into
the parsed output, and the Back click then reported
`TypeError: schema.onNavigate is not a function`. `onBack`, already a named
refusal on the same arm, was refused on the same document and is the lit control.

Both are therefore RUNTIME SLOTS, measured per key rather than per prefix: a
named refusal on the JSON face, a callable twin on the TypeScript face. The two
`KNOWN_UNDECLARED_READS` rows they held are drained (39 -> 37).

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 52 chunks) 3116.7 KB 3134.8 KB
Main entry chunk (gzip) 144.4 KB 350 KB
Entry file index-BvlIJ2aY.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) 502.06KB 115.19KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 211.58KB 58.68KB
fields (index.js) 247.91KB 62.51KB
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) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
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.95KB
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.04KB 13.93KB
plugin-charts (index.js) 71.52KB 19.98KB
plugin-chatbot (index.js) 195.35KB 46.52KB
plugin-dashboard (index.js) 131.24KB 34.61KB
plugin-designer (index.js) 215.95KB 44.33KB
plugin-detail (index.js) 253.51KB 65.88KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.95KB 41.04KB
plugin-grid (index.js) 211.58KB 57.48KB
plugin-kanban (index.js) 46.00KB 14.30KB
plugin-list (index.js) 112.59KB 27.66KB
plugin-map (index.js) 20.43KB 6.81KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.43KB 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) 94.03KB 31.02KB
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) 25.28KB 7.80KB
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

… uniformly

`it.each([...DECLARED, ['onBack']])` widened the row type to
`string[] | readonly [key: string]`, which vitest cannot narrow into a callback
signature (TS2345 under `tsc -p tsconfig.test.json`). Both tables are now
`as const` tuples, so the key parameter types as the literal union and the
`.shape` index needs no cast.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 52 chunks) 3116.7 KB 3134.8 KB
Main entry chunk (gzip) 144.4 KB 350 KB
Entry file index-BvlIJ2aY.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) 502.06KB 115.19KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 211.58KB 58.68KB
fields (index.js) 247.91KB 62.51KB
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) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
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.95KB
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.04KB 13.93KB
plugin-charts (index.js) 71.52KB 19.98KB
plugin-chatbot (index.js) 195.35KB 46.52KB
plugin-dashboard (index.js) 131.24KB 34.61KB
plugin-designer (index.js) 215.95KB 44.33KB
plugin-detail (index.js) 253.51KB 65.88KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.95KB 41.04KB
plugin-grid (index.js) 211.58KB 57.48KB
plugin-kanban (index.js) 46.00KB 14.30KB
plugin-list (index.js) 112.59KB 27.66KB
plugin-map (index.js) 20.43KB 6.81KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.43KB 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) 94.03KB 31.02KB
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) 25.28KB 7.80KB
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

os-tesla and others added 2 commits September 13, 2026 04:53
…ed figures

The sibling slice of objectui#7804 landed (the `plugin-kanban` arm, PR #9338),
so both numbers this branch carried were derived against a base that has moved.
Neither is re-stated by hand here: each is read back off the merged tree.

`KnownDrift` header bullet — the one textual conflict. Both sides rewrote the
head of the same bullet, so both one-sided resolutions were reproduced against
the merged tree first and both are RED, which excludes picking a side:

  ours  (40 entries / 63 keys) -> 2 failed: entry count 40, want 41
                                            key total   63, want 65
  theirs(41 entries / 63 keys) -> 1 failed: key total   63, want 65

The merged tree derives 41 entries / 65 keys (`ledgerEntryKeys` and
`ledgerEntryMembers` over this file's own AST, via the two pins objectui#7733
and objectui#8222). The bullet now leads with those and narrates both slices in
order, newest first: the `plugin-detail` keys onto an existing entry, then the
`plugin-kanban` entry, then the objectui#8802 retirement below it.

The restatement `N of the registered pairs carry TYPE drift TODAY` tracks the
ENTRY count, not the key total. This slice adds two keys to an entry that
already existed, so it does not move: main's 41 auto-merged and is correct.

`KNOWN_UNDECLARED_READS` merged cleanly — the two slices drained disjoint rows.
The gate of record now prints `35 exempted by ledger`, not the 37 this branch
was written against.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ
… detail slice drained it

A semantic collision the merge could not see: PR #9338's `suite 4` CONTROL
names one surviving `KNOWN_UNDECLARED_READS` row to prove the leg reads a
SHRINKING map rather than an empty one, and the row it named was
`detail::DetailSchema.onNavigate` — exactly the row this branch drains. Both
PRs were green alone; the control only reddens once both are in one tree.

Re-derived against the merged ledger rather than weakened: the witness moves to
`button::ButtonSchema.onSuccess`, one of the 35 rows that survive both slices
and belongs to neither. The assertion keeps both halves it had — a non-empty
map AND a named row — because the length check alone passes on a map holding a
single stale row, which is the reading the control exists to refuse.

The comment now records that the witness is re-derived on each landing, so the
next slice of objectui#7804 to drain it knows to move it rather than drop it.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 52 chunks) 3116.8 KB 3134.8 KB
Main entry chunk (gzip) 144.4 KB 350 KB
Entry file index-BhEry0Sd.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) 502.05KB 115.20KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 211.58KB 58.68KB
fields (index.js) 247.92KB 62.52KB
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) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
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.95KB
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.26KB 13.99KB
plugin-charts (index.js) 71.52KB 19.98KB
plugin-chatbot (index.js) 195.35KB 46.52KB
plugin-dashboard (index.js) 131.24KB 34.61KB
plugin-designer (index.js) 215.95KB 44.33KB
plugin-detail (index.js) 253.49KB 65.87KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.97KB 41.05KB
plugin-grid (index.js) 211.58KB 57.48KB
plugin-kanban (index.js) 46.02KB 14.31KB
plugin-list (index.js) 112.59KB 27.66KB
plugin-map (index.js) 20.64KB 6.86KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.43KB 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) 94.03KB 31.02KB
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) 25.28KB 7.80KB
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

Head judged: 980126de0f3d16571d947d5719608f3a9605a9e0 (merge commit 8421c1f39, parents 6546a1564 + 5a41ce733).

⛔ First: this PR's seat falsified an instruction I put in its dispatch, and it was right

My dispatch told it to lead the changeset with **BREAKING**, citing .changeset/7742-kanban-arm-batch70.md as the convention and .changeset/7804-object-kanban-handler-keys-judged.md as "the corrected shape". The seat refused, measured why, and made no edit. I re-measured all of it and confirm every count:

  1. No scanner exists. .changeset/config.json sets changelog: @changesets/cli/changelog — the stock generator, which publishes bodies verbatim. grep BREAKING across scripts/ and .github/workflows/ returns two unrelated files. The only changeset gate reading bodies is check:changeset-claims, report-only, which explicitly refuses to judge prose.
  2. There is no positional convention. Over the pending changesets the first **BREAKING line ranges from 5 to 17+ with no clustering, and title-case **Breaking outnumbers all-caps **BREAKING roughly 4.6 to 1 (69 vs 15 in my checkout). I built a "convention" out of two cherry-picked files.
  3. The file I named as "the corrected shape" does not have that shape. 7804-object-kanban-handler-keys-judged.md carries its **BREAKING at line 21 — buried three paragraphs in, precisely what I claimed to have corrected away from. My edit replaced a sentence in place and never hoisted it, so I shipped a claim about my own work that was false on inspection.

⇒ The instruction is withdrawn, retracted to the two live seats carrying it, and removed from my dispatch template. What survives: if something breaking ships, say so in the body, because a reader needs it. ⛔ Not because a scanner does.

⚠️ And a fourth count of mine the seat corrected in passing: the fixed group is 40 packages, not 41. I have been writing 41 all session. major being unavailable is still true; the number was not.

① Derived judgments

# move judged
1 crud.zod.ts#DetailSchema declares onNavigate / onAddComment as runtime-slot named refusals correct — narrowing on a published mirror
2 callable twins on crud.ts, signatures taken from the call sites correct
3 KNOWN_UNDECLARED_READS 37 → 35, enumerated correct
4 zod-mirror-parity header → 41 entries / 65 keys correct, and reached the right way
5 41 of the registered pairs carry TYPE drift TODAY does not move correct, and proved rather than asserted

1 & 2 — same mechanism the sibling slice landed on: BaseSchemaCore ends .passthrough(), so an undeclared key was not refused — it stopped being judged and the value was KEPT, then reached a renderer that read and ran it. ComponentRegistry.register('detail', DetailView) registers RAW, so the authored value arrived by identity. Per key, not per prefix: onNavigate is called in DetailView's own body; onAddComment is never called there — it is forwarded as a React prop into the comment composer behind a schema.comments gate. Two routes, one registration, one document.

3 — the gate of record's own printed line on the merged tree: … 59 judged, 0 left unjudged on an arm with an unresolved spread, **35 exempted by ledger** — every judged read is a declared member of its arm. Chain enumerated, not counted: 39 at the merge base → 37 on main (the kanban slice drained two) → 35 here.

4 — both one-sided resolutions reproduced RED first, so picking a side was mechanically excluded before a number was written:

ours    exit 1 — 2 failed: knownDrift 40 vs ledger 41; knownDriftKeys 63 vs ledger 65
theirs  exit 1 — 1 failed: knownDriftKeys 63 vs ledger 65
merged  exit 0 — Tests 32 passed (32)

5 — this is the judgement I would most easily have got wrong. That restatement tracks the entry count, not the key total, and this slice adds two keys to an entry that already existed — so it should stay at main's 41. The seat did not assert that: in the ours run it read 41 and PASSED while the entry count failed, which is a positive control for the line being live and for it tracking the other quantity.

⭐ The third moved thing, which neither the dispatch nor I predicted

PR objectui#9338's suite-4 control asserted expect(remaining).toContain('detail::DetailSchema.onNavigate') — a named witness proving the ledger is a shrinking map rather than an empty one. That is the exact row this slice drains. Both PRs were green alone; the control reddens only once both are in one tree.

The witness was re-derived to button::ButtonSchema.onSuccess — one of the 35 rows surviving both slices and belonging to neither — and both halves of the assertion were kept (non-empty map AND a named row), because the length check alone passes on a map holding one stale row, which is the reading the control exists to refuse. ⛔ Nothing skipped, quarantined or loosened.

⚠️ Worth recording as a class: a control that names a specific row is a coupling between PRs that no file-face disjointness read can see. Both seats read each other's file lists and both were right that the files were disjoint — the collision was in a string. My in-flight-overlap instruction asks for file lists; it should ask for named witnesses too.

② Semver

minor across @object-ui/types. The accept set moves in the breaking direction — documents authoring either key were accepted and KEPT, and are now refused by name at their own path — and major is unavailable in the single 40-package fixed group, so the break ships as minor with the reasoning in the note. Consistent. The changeset was left exactly as its seat authored it, per the retraction above.

③ Boundary flags

flag disposition
pnpm --filter '...@object-ui/types' build is not a self-sufficient build target accepted, and a good finding. It selects dependents of types, excluding packages those dependents themselves depend on — @object-ui/react-runtime and @object-ui/auth name @object-ui/types nowhere, so components and app-shell cannot resolve their declarations. Two builds failed that way before a full pnpm build went 43/43 — and that full build is the control proving neither failure was this diff. ⭐ Reported as a filter-closure gap rather than as a red.
check:changeset-claims flags 16 pending changesets naming crud.ts / crud.zod.ts / zod-mirror-parity.test.ts accepted. All 16 predate this branch, none is falsified by it, and they describe their own landings rather than current totals.
objectui#9344 — the gate is blind to a read behind an as any cast accepted, filed. 2 live cast-hidden reads measured with a firing control (33 files for the no-prefix anchor). objectui#7804's "39" is a floor, not a total. Correctly not repaired here.
the labeler added plugin: kanban after the push accepted. Merge fallout touched packages/plugin-kanban; not set by the seat, not reverted, reported under the shared-identity rule.
the PR-body PATCH did not append a second footer this round accepted as a counter-observation, not a retraction, exactly as the seat framed it.

Independence

Implemented-by: claude/issue-7804-detail-arm        (mode:subagent — a subagent has no session of its own)
Reviewed-by:    session_01UzHd6hDYatoDn17BuwKxnZ    (domain:ui PM seat)

⛔ For this lane a clause-② review is default-tier self-review plus the gates, not an independence-qualified ruling.

Verdict

PASS on the work.But this PR cannot be armed yet, and the reason is mine, not its seat's.

Landing pre-check ③ is PR 全部 check 全绿,⛔ 非 required 子集. This PR's CI now carries Doc Snippet Type Check and Skill Example Check red — inherited from its base, because PR objectui#9310 merged with those two red on a ruling of mine I have since retracted (objectui#9310 5651276673). ⇒ every PR in this lane whose checks re-run against the current main inherits them.

Both fixes now need the maintainer: objectui#9352 (skills guard, governed surface, needs an APPROVED review from os-zhuang or hotlong) and objectui#9308's ruling (escalated, 5651279116). The carriers stay hung until this PR can actually land.


Generated by Claude Code

os-sam commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

Carrier provenance — the SPLIT carrier is healed; card objectui#7804 now carries the gate too

Not a new gate and not a re-grading. needs:contract-review was already live on this PR. What was missing is the other limb.

check-clause2-carriers.mjs --pair 9343 read exit 4 / C1: the gate sat on PR #9343 while card objectui#7804 was bare. The checker names the consequence, and it is not cosmetic:

to the enqueue path an ungated card is a card that was never gated, so the review this PR is still waiting on is invisible to the queue and the card can be enqueued straight past a gate that is demonstrably live one carrier over

⚠️ This corrects a reading in the handover: the pair was described as "双载体还挂着". Measured at 2026-09-13T09:1xZ, only the PR limb was hung. The card limb was re-hung at 09:2xZ and --pair 9343 now reads exit 0 — the declaration is legible and both carriers agree.

What is owed

  1. The contract review itself, in the same form, naming the head it judged (980126de0f3d16571d947d5719608f3a9605a9e0 as of this comment).
  2. Then a clear of both limbs, seconds apart. ⭐ That two-removal signature is the only machine-readable evidence that a gate was CLEARED rather than STRIPPED — 「闸门被剥不是红灯是放行」, so a single removal leaves 「被剥」 and 「从未挂过」 indistinguishable.

⛔ Until then this PR is not armable, and ⛔ nothing here is a verdict on the code: no diff was reviewed to write this comment.

Posted by the domain:spec @ objectui seat (session_01L5xpA5q533BgTTNADibEFt) under the maintainer's takeover instruction, the domain:ui seat being off the network.


Generated by Claude Code

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants