Skip to content

feat(types,plugin-kanban): execute the batch #70 ruling on the kanban arm - #8799

Merged
os-warren merged 5 commits into
mainfrom
claude/issue-7742-kanban-arm-ruling-batch70
Sep 9, 2026
Merged

feat(types,plugin-kanban): execute the batch #70 ruling on the kanban arm#8799
os-warren merged 5 commits into
mainfrom
claude/issue-7742-kanban-arm-ruling-batch70

Conversation

@os-warren

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

Copy link
Copy Markdown
Collaborator

Fixes #7742

Executes maintainer decision batch #70 (2026-09-07, verbatim 「同意」) row by row on the 'kanban' arm.

⭐ Wording patch round — after the contract review PASSED

The contract review adopted the implementation whole (PASS, tier-verified, every leg re-derived). ⛔ Nothing about the retirement, the declaration, the refusals, the pins or the ablation is reopened, and no behaviour changed in this round — it is prose, one file rename, and this body. Seat session session_01Jmxdo7bmeqCQHLSfmLVX9w.

What the review found, and what this round did about it:

finding fixed by
MEDIUM F-A — three texts read as if objectFields had become author-unreachable in general; the changeset is the one that matters, because a changeset is this PR's input to the release notes the changeset section, the KanbanRendererProps docblock and the pin file's docblock now state the scope at the arm level; the pin file is renamed (see row 4 below)
LOW F-D — the base-to-main window paragraph was stale; origin/main had moved origin/main merged in (⛔ no rebase, no force-push) and the paragraph re-measured — see Base to main window check

⚠️ Clause ② — the accept set moves in BOTH directions

Clause-②: yes

That line is in the fixed spelling the seat protocol reads, so the carrier requirement is machine-legible and not merely described: this PR moves a declared accept set in both directions, therefore needs:contract-review is hung on both the card and this PR and ⛔ stays hung — the PR remains draft and is ⛔ not enqueued.

Most cards move one way. This one narrows and widens, and a reviewer must check each half separately.

direction keys face
NARROWS — four spellings the arm accepted are now refused BY NAME allowCollapse, cardTemplates, columnWidths, titleField ?: never on complex.ts + named refusal on complex.zod.ts
WIDENS — one key the board reads and no face named is now declared navigation complex.ts + complex.zod.ts, spec type by reference

Neither half is a drop. BaseSchema is .passthrough(), so a dropped key is kept, not refused — the failure objectui#7664's own first cut shipped at onCardClick.

Both directions are pinned in one file, packages/types/src/__tests__/kanban-arm-batch70-7742.test.ts, because a file that pinned only one half would pass on a change that forgot the other.

The four rows

1. allowCollapse / cardTemplates / columnWidths — RETIRE

Tombstone + named refusal on both faces. Each refusal message names the channel the capability actually lives on: columns[].collapsed for collapsing, the templates component prop for card templates, the useColumnWidths hook option for widths. CardTemplate / ColumnWidthConfig stay exported — the prop and the hook still consume them.

⛔ A new refusal factory (retiredZeroReadKanbanKey), not the existing retiredDeclarativeKanbanKey. That helper's message says the key "belonged to the retired DeclarativeKanbanSchema dialect", which is true of draggable and false of these three: they were members of the plugin dialect objectui#7664 ruled authoritative. Sharing the message would hand the author a false history of their own document.

2. titleField on the kanban arm — RETIRE THE FALLBACK

⚠️ Not an inertness retirement. The read stays — ObjectKanban.tsx reads schema.cardTitle || schema.titleField and it is load-bearing for the sibling arm. What retires is this arm's acceptance of the legacy spelling. Pinned as a PAIR: two documents identical but for type, opposite verdicts.

3. navigation — DECLARE

ObjectKanban.tsx reads it to choose the record-detail overlay and defaults it to a drawer; the (schema as any).navigation cast is gone. Member list is @objectstack/spec's NavigationConfig by reference, not restated. The pin asserts the value is judged, not merely admitted — a bad mode and an undeclared member both draw refusals, which a test asserting only the good config would not have caught (it would have passed on main too).

4. objectFields — off the schema bag, onto a React prop

Moved off the schema bag onto KanbanRendererProps as a real React prop, a sibling of schema, injected by ObjectKanban — the one caller that fetches the object definition. Inside schema it was reachable by an author: on the schema-only kanban-ui entry, which has no object schema of its own to substitute, an authored objectFields reached resolveConditionalFormatting verbatim while no schema face declared or judged it.

⚠️ Scope — the ruled kanban arm is closed, the KEY is not. Measured through the real SchemaRenderer, and stated because the first cut of this PR read as if it were:

node authored objectFields outcome
kanban-ui absent unpainted
kanban-ui present painted rgb(255, 0, 0)
kanban present unpainted

SchemaRenderer strips a fixed metadata list that does not include objectFields, then spreads the rest as React props — and KanbanRenderer, registered for kanban-ui, now reads that prop. So on kanban-ui an authored value still arrives, on the very prop this change adds. The ruled kanban arm is genuinely closed: ObjectKanbanRenderer serves that type key and discards its rest-spread (void _props;), so an authored objectFields there reaches nothing. ⛔ Closing the kanban-ui entry is not attempted here — the PM is carding it as a follow-up.

⚠️ Nothing went red when this moved — no test pinned the channel. packages/plugin-kanban/src/__tests__/objectFieldsIsAPropNotASchemaKey-7742.test.tsx now does, behaviourally: an expanded relation collapses to its stored id only when the field catalogue arrives, so the paint is a positive reading of the channel. The prop leg is asserted first as the firing control for the schema leg's negative, plus a third leg proving the paint is not unconditional. It renders KanbanRenderer directly, so it never exercises SchemaRenderer: the kanban-ui rows in the table above are measured, not pinned by this file, and its docblock says so.

The file was renamed this round, from objectFieldsIsNotAuthorable-7742.test.tsx. Its describes were always accurate ("reaches the predicate layer as a PROP", "is no longer read off the schema bag"); only the name claimed the general unreachability the measurement refutes, and a filename is the widest-reach text this PR ships — it prints in every vitest run, in git log --stat, and in this PR's own file list. Cost of the rename: git mv alone — nothing in the repo referenced the old path (git grep → 0 hits outside the file), the two imports are same-directory relative and still resolve, and check-type-check-coverage enumerates test files from the package root rather than a list, so it re-derives coverage from disk (re-run: exit 0, 42/42 packages compile their tests).

⭐ The re-derived objectui#8410 screen — and a correction to it

The screen recorded on the card (2026-09-07, fff250f) reached the right answer through a wrong discrimination, and the dispatch repeats the error, so it is worth stating.

The screen says KanbanRenderer — which destructures only schema — is "this card's KanbanSchema (type: 'kanban')", and that ObjectKanbanRenderer (which DOES rest-spread) is "the object-kanban registration, not this card's".

Measured on this branch, that is inverted. KanbanRenderer is registered for kanban-ui. ObjectKanbanRenderer is registered for BOTH object-kanban AND kanban — so this card's type key IS served by the rest-spreading renderer.

The zeros survive anyway, for a different reason: the spread terminates. Full channel, traced end to end —

  1. SchemaRenderer.tsx spreads every non-metadata node key as a React prop;
  2. ObjectKanbanRenderer takes ({ schema, ...props }) and forwards props to ObjectKanban;
  3. ObjectKanban takes ({ schema, dataSource, className, data, loading, onRowClick, onCardClick, ..._props }) and the very next statement is void _props; — the rest is discarded, never spread onward;
  4. the schema channel continues (effectiveSchema into KanbanRenderer), but KanbanRenderer destructures only schema and names each key it forwards to LazyKanban.

⇒ no forwarded rest-spread reaches a sink for any of the three keys. This is a retirement, not a regression.

⭐ The screen's own lesson holds and is strengthened: grep -l for a spread is not the instrument, and neither is the render function's signature alone — you must also read which type key that function is REGISTERED for, and follow the spread to where it terminates.

Read census — every zero paired with a firing control on the same instrument

git grep -n -w KEY -- packages/plugin-kanban/src, every file including tests:

key hits / files verdict
allowCollapse 0 / 0 zero
cardTemplates 0 / 0 zero
columnWidths 0 / 0 zero
control groupBy 85 / 27 FIRES
control cardTitle 18 / 9 FIRES
control coverImageField 17 / 3 FIRES

⚠️ Repo-wide, columnWidths is not zero (22 hits / 11 files) — every one is a different key of the same spelling on the grid surface (data-table.tsx, ObjectGrid.tsx, RecordPickerDialog.tsx), untouched here.

The two faces the screen did NOT cover, measured here

  • titleField, re-measured from 25907cd70: both reads are now cast-free (schema.cardTitle || schema.titleField, schema.cardTitle ?? schema.titleField) because PR fix(plugin-kanban): ObjectKanbanComponentProps.schema names both registered node types (objectui#7322 item ②) #8153 widened the prop to KanbanSchema | ObjectKanbanSchema. ObjectKanbanSchema (objectql.ts) declares titleField?: string and this PR does not touch it — the object-kanban arm still declares it, asserted at runtime by the sibling-arm pin.
  • Registry inputs face — ⛔ a divergence this PR creates and does NOT close. OBJECT_KANBAN_INPUTS is ONE shared array spread into both registrations and it declares titleField; objectui#8201 pins that sharing structurally, and ComponentPropsMap has an object-kanban entry but no kanban entry to compare a split list against. So a type: 'kanban' node naming titleField is now accepted by the html tier and refused by the zod tier, and every pin stays green. Un-sharing the array would redden a landed pin and re-open a landed ruling, so it is reported, not done: filed as objectui#8802 with four options and a recommendation. This is the one thing on this PR a reviewer should rule on.

Ablation — every named refusal, mutation proven on disk

Each leg: replace the arm with a permissive declaration, prove the mutation landed (anchor gone, injected text present, byte delta non-zero), run the pin, restore with git checkout HEAD -- ABSOLUTE_PATH, prove restoration by blob-hash equality AND an empty git diff HEAD.

leg anchor injected byte delta mutated exit which row reddened restored
allowCollapse 1 to 0 present −159 1 (1 failed / 10 passed) its own named row blob==HEAD ✅, diff empty ✅
cardTemplates 1 to 0 present −171 1 (1 failed / 10 passed) its own named row blob==HEAD ✅, diff empty ✅
columnWidths 1 to 0 present −270 1 (1 failed / 10 passed) its own named row blob==HEAD ✅, diff empty ✅
titleField 1 to 0 present −499 1 (2 failed / 9 passed) its own row and the arm-pair row blob==HEAD ✅, diff empty ✅
navigation (the WIDENING) 1 to 0 line removed −151 1 (2 failed / 9 passed) accept + value-judged rows blob==HEAD ✅, diff empty ✅

⚠️ The first cut of this ablation is recorded as VOID, not as a pass. It replaced only the opening line of each multi-line refusal call, leaving the rest dangling, so the test file failed to load — vitest printed Tests no tests with a non-zero exit. A collection error is NOT a red; it is NOT MEASURED, and reading it as a red would have credited the pins with a failure they never demonstrated. The table above is the second cut, which replaces each arm as a whole expression so the file still parses and real assertions fail.

Base to main window check — ⚠️ RE-MEASURED; the window is NO LONGER empty

The earlier reading is superseded, not repaired. It recorded origin/main at the same sha as BASE and the window at 0 files, and noted that an empty window means its own control cannot fire. That was true when written. It is now stale: origin/main has moved to 0e3bca45d over three commits (#8795, #8794, #8798), and git diff --name-only b6d07df4 origin/main returns 9 files.

⛔ Do not carry over its conclusion that "no file any pin reads off disk can have moved" — two of the nine are exactly such files:

moved file why it matters to this PR
packages/plugin-kanban/src/KanbanImpl.tsx the objectFields pin imports it at module scope (the lazy-chunk prepay)
apps/console/src/__tests__/registry-inputs-spec-parity.test.ts the console registry-parity pin

So origin/main is merged into this branch (ordinary merge commits; ⛔ no rebase, no force-push, the branch is only ever appended to), and every gate in the table below was re-run on the merged tree. The window control now fires: non-empty, and the pins were re-run against the moved files instead of being reasoned about.

⚠️ main moved a second time while this round was verifying, so there are TWO merges — and a stopping rule.

merge origin/main at window conflicts intersection with this PR's 9 diff paths
first 0e3bca45d (3 commits: #8795, #8794, #8798) 9 files / 1052 insertions 0 KanbanImpl.tsx + the console registry-parity pin — both read by pins here
second 5dee0146d (3 commits: #8804, #8796, #8744) 16 files 0 nonegit diff --name-only of that window, intersected with this PR's paths, is empty

Head is 1ecfafae5 and git merge-base --is-ancestor origin/main HEAD exits 0: the branch is level with main as of 5dee0146d. ⛔ There is no third merge, and that is deliberate rather than an omission — a branch cannot be held permanently level with a moving main from inside one round, and each merge costs a full re-verification. The stopping rule taken here: merge, re-verify, record the sha, and leave any later movement to the queue at land time. The second window is recorded above precisely so the next reader can see it moved and that it could not have touched this PR.

Verification

Re-run on the MERGED tree — the wording round

Exit codes captured before any pipe (cmd > file 2>&1; EXIT=$?), heavy runs serialised through the container's shared verify lock, and each verdict read off the gate's own printed line rather than a bare $?. ⭐ Every row below was re-run on the final head 1ecfafae5 — i.e. after both merges, not just the first — so no number here predates a merge.

gate exit verdict
pnpm --filter @object-ui/types build 0 PASS
pnpm --filter '@object-ui/plugin-kanban^...' build (dep closure) 0 PASS
turbo run type-check --filter types --filter plugin-kanban 0 PASS — 15 tasks successful
vitest — the three pin files by path 0 PASS — 3 files / 25 tests
vitest packages/types/ + packages/plugin-kanban/ 0 PASS — 199 files / 3392 tests (198 / 3384 before the second merge; the +1 file / +8 tests are ai-zero-read-members-retired-8178.test.ts, arriving from main)
vitest apps/console/ 0 PASS — 95 files / 1116 tests
pnpm --filter '@object-ui/console^...' build + --filter @object-ui/console build 0 PASS
check:sdui-registration-pins 0 PASS — 16 registrations, 518 chunks weighed. Its first attempt was exit 2, PRECONDITION NOT MET (its own words: "No console build to weigh … This is exit 2, not a pass"), recorded as NOT MEASURED rather than as a red; the console was built and the gate re-run, and then built and re-run again on the final head
check-changeset-presence.mjs 0 PASS — merge-base 5dee0146d, 9 files changed, 7 published source, 1 changeset
check-changeset-no-major.mjs 0 PASS — no changeset declares major
check-type-check-coverage.mjs 0 PASS — 45/46 via type-check; 43/43 packages compile their tests (this is the gate the rename had to keep resolving; it was 42/42 before main brought plugin-ai a test project)
check-unreferenced-sources.mjs 0 PASS — 208 shipped sources, 208 reached
check-control-bytes.mjs 0 PASS — 7011 tracked text files scanned
check-governed-queue-guard.mjs --test over the 9 diff paths 0 NOT GOVERNED — 9 paths against 5 governed surfaces, none matched
pnpm --filter types --filter plugin-kanban run lint 0 PASS — 0 errors; 271 + 179 warnings, all pre-existing no-explicit-any / react-refresh, none on a line this round touched
control-byte self-scan (grep -naP) over the 3 edited files 1 (no match) PASS — no control bytes

⚠️ The per-package eslint . warning counts above (271 + 179) are not comparable with the 63 quoted in the previous round's narrowed run: that run linted only the 7 diff files, these lint the whole of both packages. Both readings are exit 0 with 0 errors; only the population differs.

First round — the implementation, on the pre-merge tree

gate exit verdict
pnpm --filter @object-ui/types build 0 PASS
pnpm --filter '@object-ui/plugin-kanban^...' build (dep closure) 0 PASS
pnpm --filter '@object-ui/console^...' build 0 PASS
pnpm --filter @object-ui/console build 0 PASS
scoped doc build (35 tasks, the gate's own --build-filter) 0 PASS
pnpm --filter @object-ui/cli build 0 PASS
type-check — types + plugin-kanban 0 PASS
vitest packages/types/ + packages/plugin-kanban/ 0 PASS — 197 files / 3381 tests
vitest apps/console/ 0 PASS — 95 files / 1116 tests
vitest packages/cli/ scripts/ examples/schema-catalog/ 0 PASS — 178 files / 6174 tests
pnpm check (the per-PR gate) 0 PASS — 628 files analysed
pnpm --filter @object-ui/types --filter @object-ui/plugin-kanban run lint 0 PASS — 0 errors, 177 pre-existing warnings
check:control-bytes 0 PASS
check:spec-symbols 0 PASS
check:doc-types 0 PASS
check:doc-snippets 0 PASS — 636 of 636 blocks judged
check:doc-examples 0 PASS — 125 blocks, every failure ledger-declared
check:doc-fences 0 PASS
check:doc-example-readers 0 PASS
check:handler-key-reads 0 PASS
check:sdui-registration-pins 0 PASS — 16 registrations, 518 chunks weighed
check:element-data-source-declaration 0 PASS
check:readme-exports 0 PASS — 3321 symbols from 36 of 40 packages
check:unreferenced-sources 0 PASS
check:changeset-no-major 0 PASS
check-changeset-presence.mjs 0 PASS — 4 published source files, 1 changeset
check:lint-coverage 0 PASS
check:governed-queue-guard --test over the 9 diff paths 0 NOT GOVERNED — ordinary PR
control-byte self-scan over the diff 1 (no match) PASS — no control bytes

⚠️ check:doc-snippets, check:doc-examples, check:sdui-registration-pins and check:readme-exports each returned PRECONDITION NOT MET on the first attempt, naming unbuilt packages in their own output. Those are not reds and are not recorded as passes either — the required builds were run and the gates re-run; the table shows the real verdicts, each with a population that fires (636 blocks, 518 chunks, 3321 symbols).

The eslint narrowing, with its three readings

turbo run lint over the whole farm belongs to CI. The narrowed run is a measurement, not a skip:

  1. Population, read from eslint itself (not guessed): a full eslint . --no-inline-config --format json reports 4634 files linted.
  2. Files in the narrowed run, from --format json: 7 — every .ts / .tsx in the diff, and all 7 are present in the 4634. Exit 0, 0 errors, 63 warnings, all pre-existing no-explicit-any / react-refresh on untouched lines.
  3. Invariance: eslint.config.js declares no parserOptions.project and no projectService anywhere, so type-aware linting is not enabled — this diff cannot move the verdict on any file it does not itself contain.

For completeness the full run was executed: exit 1, 94 errors across 78 files, none of them mine (verified by set intersection against the 7). Those are pre-existing react-hooks/static-components / no-console findings under bare eslint ., which is not this repo's gate — pnpm lint is turbo run lint, i.e. the per-package scripts, and both of mine exit 0.

Changeset

.changeset/7742-kanban-arm-batch70.mdminor for @object-ui/types and @object-ui/plugin-kanban, spelling out the breaking semantics in the body. ⛔ Never major: this repo's fixed group tracks @objectstack's major, and check-changeset-no-major.mjs enforces it.

Found and deliberately NOT done

  • objectui#8801 — FILED. ObjectKanbanSchema.allowCollapse is the same zero-read key one arm over, declared on both objectql faces, read by nothing, and outside batch Add cloud storage functionality with Vercel KV integration in studio #70's reach. After this PR the two arms of one renderer disagree about the key.
  • objectui#8802 — FILED. The registry inputs divergence described above.
  • countsAreWindowed — noted, not filed. Same shape and same argument as objectFields: internal, injected by ObjectKanban, absent from the registry inputs, and still riding the schema bag where an author can reach it on the kanban-ui entry. The ruling did not name it, so it stays. Its successor is named: the KanbanRendererProps docblock in index.tsx records it in as many words, and objectui#8307 owns the key.
  • ⚠️ objectui#8652 is OPEN and overlaps row 3. "navigation is read off the kanban node and declared on neither arm" — the same subject batch Add cloud storage functionality with Vercel KV integration in studio #70 already ruled. This PR executes the ruling and ⛔ does not close that card; a triage seat should reconcile the two, since a later card appears to re-ask a settled question.
  • onColumnToggle / enableVirtualScrolling / virtualScrollThreshold are out of scope here — they belong to 'kanban-enhanced', which has no arm, and objectui#8257 carries them. The root README example is out of scope here — objectui#8256 carries it. The filter key is out of scope here — objectui#7712 carries it. None of those three is addressed by this change.

维护者速读(草稿)

改了什么 — 按 batch #70 裁决逐行执行 kanban 臂:退役 allowCollapse / cardTemplates / columnWidths(两面声明、无渲染器读取)与 titleFieldcardTitle 的旧拼法;兄弟臂 object-kanban 保留自己的),四者都是具名拒绝而非删除;声明 navigation(板子一直在读、两面都没声明);把 objectFields 从 schema 袋子挪到真正的 React prop。

为什么改 — ADR-0049 enforce-or-remove。作者写 allowCollapse: true 今天校验通过、板子却永不折叠;写 navigation 则相反,板子会兑现而校验器看不见。两种都是「声明与实现脱节」,方向相反。

风险与代价(含回滚) — accept set 双向移动:四个拼法从「通过」变成「按名拒绝」,一个键从「不可见」变成「被校验」。今天写了这四个键的板子会开始报错——这正是裁决要的响亮失败,但它是破坏性的。回滚成本低:整个改动是声明面加一个 prop 位置,git revert 一笔即可,无数据迁移、无存储格式变化。⚠️ 一处残留分歧未关:titleField 在注册表 inputs 面仍对 kanban 声明着(共享数组,objectui#8201 结构性钉住),于是 html 层接受、zod 层拒绝,且所有钉子照常绿。拆开那个数组要动一条已落地的裁决,故已立卡 objectui#8802 报告、未擅动。

⚠️ 另一处已实测、本轮明确不关的洞 —— objectFields 只在被裁的 kanban 臂上关死了;在 kanban-ui 入口上,作者写的 objectFields 仍会经 SchemaRenderer 的通用 prop 展开抵达渲染器(实测:卡片会被上色)。合同评审 PASS 之后的这一轮只改文案口径,把三处「读起来像是彻底不可达」的措辞改成按臂陈述,不动任何行为、拒绝、声明、钉子或消融;关掉那个入口是 PM 另行立卡的后续。

席位意见 — (待定稿)

你要做的 — 只需裁一件事:objectui#8802 里那处 titleField 的注册表分歧,选 A(接受、另卡跟进)、B(拆共享数组)、C(不退役)还是 D(上游给 spec 加 kanban 条目)。本席建议 A 现在、D 是真答案。其余四行按裁决原样执行,无需选择。


Generated by Claude Code

…n` arm

Retire `allowCollapse` / `cardTemplates` / `columnWidths` (declared on both
faces, read by no registered board) and `titleField` (the legacy spelling of
`cardTitle`; the `object-kanban` arm keeps its own) as named refusals on both
faces. Declare `navigation`, which `ObjectKanban` reads and no face named, on
the gantt precedent objectui#5903 — the `(schema as any)` cast goes.

Move `objectFields` off the schema bag onto `KanbanRendererProps` as a real
React prop: an internal channel from the one caller that fetched the object
definition, not an authoring surface.

Fixes #7742

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

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3484.2 KB 3512.7 KB
Main entry chunk (gzip) 144.0 KB 350 KB
Entry file index-CuA4D7ir.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) 15.67KB 5.75KB
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) 499.42KB 114.32KB
core (index.js) 7.48KB 2.96KB
create-plugin (index.js) 26.68KB 8.94KB
data-objectstack (index.js) 198.39KB 55.29KB
fields (index.js) 245.88KB 62.05KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 6.57KB 2.76KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.94KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 15.16KB 3.68KB
plugin-calendar (index.js) 49.03KB 13.93KB
plugin-charts (index.js) 71.39KB 19.92KB
plugin-chatbot (index.js) 194.53KB 46.34KB
plugin-dashboard (index.js) 131.43KB 34.44KB
plugin-designer (index.js) 215.51KB 44.29KB
plugin-detail (index.js) 252.39KB 65.32KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 134.25KB 33.52KB
plugin-gantt (index.js) 167.16KB 40.99KB
plugin-grid (index.js) 210.67KB 57.19KB
plugin-kanban (index.js) 56.42KB 16.03KB
plugin-list (index.js) 112.73KB 27.69KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.54KB 20.84KB
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) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 2.32KB 1.24KB
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.55KB 2.45KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 13.64KB 4.59KB
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

…ectFields channel

The batch #70 ruling moves the `kanban` arm's accept set in both directions in
one change, so a file that pinned only one half would pass on a change that
forgot the other. Every refusal is asserted at its own path with an undeclared
key on the same parse as the control (a `.passthrough()` object accepts unknown
keys, so "the document failed" proves nothing alone), `navigation` is asserted
to JUDGE its value rather than merely admit it, and the sibling `object-kanban`
arm is asserted to still accept `titleField`.

The `objectFields` pin discriminates the two channels behaviourally through an
outcome only the field catalogue can produce, with the prop leg asserted first
as the firing control for the schema leg's negative.

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

Copy link
Copy Markdown
Collaborator

Contract review at CONTRACT_REVIEW_TIERVerdict: CHANGES REQUIRED (audit reading; director seat, summon #18 segment 3, session_017Js5kTpTtxieBjPyScgxJ3, 2026-09-09T09:4xZ)

PR objectui#8799 · head e050689af7100c96fb135bd3f4b9d696f555b139 (re-read at posting 09:43:58Z; unchanged since 09:15Z) · reviewed 09:35Z–09:43Z.

  • Reviewed-by: isolated claude-fable-5-1 subagent, transcript-verified (78 harness model stamps, all claude-fable-5-1, zero residue; positive control 68 assistant / 49 user role tokens), adopted verbatim below. Fed only the card objectui#7742, the batch Add cloud storage functionality with Vercel KV integration in studio #70 ruling, this PR and its comments, the red job log, and the checked-out trees.
  • Implemented-by: the spec@objectui seat's dev (mode:subagent, os-warren), branch claude/issue-7742-kanban-arm-ruling-batch70. Distinct sessions ⇒ not a self-review.
  • Reading for the seat: the ruling's kanban arm is executed exactly; the blocker (F1) is an existing census pin the dev did not run (the PR body itself says @object-ui/types tests: NOT MEASURED). F2 goes into the same patch round; F3/F5 are wording; F4 is not folded into this PR — the seat opens the follow-up card re-ruling objectui#8201 for titleField (option (a) below), and the maintainer rules there; until then the html/zod divergence is recorded, not fixed. Re-review owed on the patched head. ⛔ This seat cleared no carrier and touched no PR state.
  • Tooling note for the skills lane, not this PR's: check-widening-tells.mjs is blind to objectui's packages/types/src/zod (T1 globs are spec-only), so the mechanical floor cannot catch a wrong Clause-②: no on an objectui widening.

Verdict: CHANGES REQUIRED

Head reviewed: e050689af7100c96fb135bd3f4b9d696f555b139 (matches the requested prefix; 1 commit; base b6d07df4b7)

Clause-② reading: yes. navigation is a new key on a published payload (packages/types/src/complex.ts:347, packages/types/src/zod/complex.zod.ts:237) — a new key on a shipped payload is always yes per contract-review.md — and four spellings are retired by name on the same arm, so the accept set moves in both directions; the batch #70 ruling itself says the PR carries needs:contract-review. The PR body has no Clause-②: line in the fixed spelling (only a prose "Clause ②" section); the fixed-spelling declaration is on the card's claim comment (5599229588, Clause-②: yes), the label carrier is on both PR and card, and check-clause2-carriers.mjs --pair 8799 exits 0. Note: check-widening-tells.mjs reports no tell on this diff because its T1 globs are objectstack's spec surface, not packages/types/src/zod — the mechanical floor is blind here; the human reading governs.

Governed surface: none. Six files; none under AGENTS.md / CLAUDE.md / .claude/** / skills/** / docs/adr/** (GOVERNED_SURFACES, scripts/check-governed-queue-guard.mjs:227-233); Governed Surface Queue Guard green. content/docs/api/schema-reference.md is hand-written (front-matter prose; every script naming it is a checker — check-doc-component-types.mjs, check-doc-snippet-types.mjs — not a generator), so a hand edit is the correct procedure.

CI on head: Test (shard 1/4) red — 1 failed / 9414 passed. packages/types/src/__tests__/kanban-plugin-dialect-authoritative-7664.test.ts:301 (expect(live).toEqual([...])): the census pin expects 20 live KanbanSchema members including allowCollapse/cardTemplates/columnWidths and exactly 3 tombstones (:307); the head has 18 live (+navigation) and 7 tombstones. Caused by this diff: yes. Every other check on the head is green (Type Check, Lint, shards 2–4, Build & E2E, Changeset Bump Policy, doc checks).

Findings

F1 — blocking. Existing pin not updated → red required check. kanban-plugin-dialect-authoritative-7664.test.ts:297-307. Fix: expected live list becomes 18 (…'grouping', 'navigation'), tombstoned becomes ['allowCollapse','cardTemplates','columnWidths','titleField','draggable','onColumnAdd','onCardAdd'] (declaration order), retitle the it; the "ruled member" list at :215-224 still names the three retired keys as ruled members — move them to the tombstone assertion and add navigation. The PR body's verification table still says @object-ui/types tests: NOT MEASURED — the dev pushed without reading that verdict; CI has now read it.

F2 — non-blocking (checklist: refusal cases must assert). Nothing pins the new shape: no assertion that the four refusals fire by name with their remedy text (objectui#7742, RETIRED, cardTitle, columns[].collapsed); no two-arm split pin (type:'kanban' + titleField refused at path titleField, type:'object-kanban' + titleField still green); no navigation accept-plus-control (spec member accepted, { mode: 'bogus' } refused); no @ts-expect-error compile-time pins for the four ?: never (precedent component-input-retired-keys-7493.test.ts:109-118); no render pin that KanbanRenderer reads objectFields from the prop and not schema.objectFields. The house pattern (refusals() with path+message assertions and a removal control) is in the same 7664 file. Fix: extend it or add kanban-arm-batch70-7742.test.ts plus a small plugin-kanban pin — same patch round as F1.

F3 — non-blocking. Changeset and docblock overclaim the objectFields move. SchemaRenderer.tsx:1451-1480 strips a fixed metadata list and spreads every other node key as a React prop (:1576); objectFields is not in that list; KanbanRenderer is registered directly for 'kanban-ui' (packages/plugin-kanban/src/index.tsx:235-237) and now reads the objectFields prop (:200, :226) → KanbanImpl.tsx:144 resolveConditionalFormatting. So an authored objectFields on a kanban-ui node still reaches the sink, via the prop spread instead of the schema bag. The ruling's letter is executed (on KanbanRendererProps, off the schema read path, documented internal), but .changeset/7742-kanban-arm-batch70.md:55-63 and index.tsx:174-186 ("As a prop the only writer is the one caller that can actually know the answer") are false on kanban-ui. Fix: correct the wording (the move changes the declared .d.ts face and the ObjectKanban channel; author reachability on kanban-ui is unchanged) and file a follow-up if closing it is wanted — wrapping the kanban-ui registration is outside this ruling.

F4 — non-blocking; needs a ruling (dev's open flag). OBJECT_KANBAN_INPUTS (index.tsx:553) declares titleField and is spread into the 'kanban' registration (:581), so the html tier accepts titleField on a kanban node while the zod tier refuses it (complex.zod.ts:238); scalarKeysAreDeclaredAndHonoured-8201.test.ts:86 and its "ONE shared list" pin forbid a per-tag split. My reading: real divergence, lenient-on-html / strict-on-zod, so the author is refused loudly on at least one face — not blocking. Batch #70 named only the two schema faces, and its "one arm, one spelling" makes objectui#8201's premise ("same renderer ⇒ same surface") false for exactly this key. Recommend option (a): accept here, open a follow-up card re-ruling 8201 for titleField (shared base minus titleField on 'kanban', pin rewritten as "same object except the ruled difference"); do not fold into this PR.

F5 — non-blocking. PR body lacks the fixed-spelling Clause-②: yes line (card side has it; carriers script passes). Fix: add the line under the existing "Clause ②" heading so the PR reader does not need the card.

Acceptance notes


Generated by Claude Code

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3484.2 KB 3512.7 KB
Main entry chunk (gzip) 144.0 KB 350 KB
Entry file index-CvFPprbT.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) 15.67KB 5.75KB
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) 499.42KB 114.32KB
core (index.js) 7.48KB 2.96KB
create-plugin (index.js) 26.68KB 8.94KB
data-objectstack (index.js) 198.39KB 55.29KB
fields (index.js) 245.88KB 62.05KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 6.57KB 2.76KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.94KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 15.16KB 3.68KB
plugin-calendar (index.js) 49.03KB 13.93KB
plugin-charts (index.js) 71.39KB 19.92KB
plugin-chatbot (index.js) 194.53KB 46.34KB
plugin-dashboard (index.js) 131.43KB 34.44KB
plugin-designer (index.js) 215.51KB 44.29KB
plugin-detail (index.js) 252.39KB 65.32KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 134.25KB 33.52KB
plugin-gantt (index.js) 167.16KB 40.99KB
plugin-grid (index.js) 210.67KB 57.19KB
plugin-kanban (index.js) 56.48KB 16.06KB
plugin-list (index.js) 112.73KB 27.69KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.54KB 20.84KB
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) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 2.32KB 1.24KB
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.55KB 2.45KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 13.64KB 4.59KB
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

…`kanban` arm

The move takes `objectFields` off the schema READ PATH, and on the ruled
`kanban` arm that closes the key outright — `ObjectKanbanRenderer` serves that
type key and discards its rest-spread (`void _props;`). It does NOT make the key
author-unreachable in general: `objectFields` is absent from `SchemaRenderer`'s
stripped-metadata list, so on the schema-only `kanban-ui` entry an authored
value survives the generic prop spread and lands on the very prop this change
introduced. Measured through the real `SchemaRenderer` — a `kanban-ui` node with
an authored `objectFields` paints the card; the same node without it, and the
same document as `type: "kanban"`, do not.

Three texts read as if the general claim held, and the changeset is the one that
ships: a changeset is this PR's input to the release notes, so a measured-false
claim there becomes a release note. All three now state the scope at the arm
level rather than dropping the claim:

  - .changeset/7742-kanban-arm-batch70.md
  - the KanbanRendererProps docblock in packages/plugin-kanban/src/index.tsx
  - the pin file's docblock, and the pin file's NAME

Renamed objectFieldsIsNotAuthorable-7742.test.tsx to
objectFieldsIsAPropNotASchemaKey-7742.test.tsx. Its describes were always
accurate; only the filename claimed the generality the measurement refutes, and
a filename is the widest-reach text here — it prints in every vitest run and in
every file listing. Nothing in the repo referenced the old path, the two imports
are same-directory relative, and check-type-check-coverage enumerates test files
from the package root, so coverage is re-derived from disk (42/42 packages).

Also corrects the countsAreWindowed docblock, whose "same shape as objectFields
above" pointer this PR's own move had inverted, and the stale "Absent on the
schema-only kanban-ui entry" line that the new scope paragraph contradicted.

Wording only: no behaviour, no refusal, no declaration, no pin assertion and no
ablation leg is touched.

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

Copy link
Copy Markdown
Collaborator

Contract review at CONTRACT_REVIEW_TIERVerdict: PASS WITH FINDINGS (patch-round re-review; audit reading; director seat, summon #18 segment 4, session_017Js5kTpTtxieBjPyScgxJ3, 2026-09-09T10:4xZ)

PR objectui#8799 · head a218a93772f1499dcc34f37fc0d2581b199b2e34 (re-read at posting 10:46:49Z; unchanged since 09:53Z) · reviewed 10:36Z–10:44Z · verdict of record before this one: 5599822824 (CHANGES REQUIRED on e050689af7).


Verdict: PASS WITH FINDINGS

Head reviewed: a218a93772f1499dcc34f37fc0d2581b199b2e34 (matches the requested prefix; 2 commits; base b6d07df4b7). Delta since the verdict-of-record head e050689af7: 3 files, all under __tests__/, +303/−5; zero non-test hunkscomplex.ts, complex.zod.ts, index.tsx, ObjectKanban.tsx, the changeset and schema-reference.md are byte-identical to the reviewed head.

Timeline fact the seat needs: commit a218a937 is authored and committed at 09:18:38Z — 26 minutes before the verdict (09:44:57Z) — and was pushed ~09:50Z (first check-run 09:50:42Z). The dev report (5599907932, 09:50:07Z) and the #8410-correction comment (5599940080, 09:52:48Z) never mention the verdict or F1–F5. This is the dev's own planned test commit that happens to clear F1 and most of F2, not a patch-round response; nobody has acted on F3, F5 or F2's type-level leg.

Clause-② reading: yes — unchanged. The delta is test-only and moves no accept set. PR body still lacks the fixed-spelling line (see F5); the card's claim 5599229588 carries it; PM_SWEEP_REPO=objectstack-ai/objectui node scripts/pm/check-clause2-carriers.mjs --pair 8799 → exit 0 ("readable in the fixed spelling and both carriers agree"; without PM_SWEEP_REPO it reads the objectstack repo and exits 2 — not a reading).

Governed surface: none. Nine files total; the three new ones are __tests__/ paths. Governed Surface Queue Guard green on head.

CI on head: 33 check-runs, 30 success / 3 skipped (Test (coverage), coverage shard, dependabot — push-lane/by design), every head_sha = a218a93772; last to complete Test (shard 1/4) 10:06:47Z; Type Check success 09:59:32Z, Lint 09:55:53Z. Confirms the 10:33Z reading. The previously red shard 1 is green. Job logs are not readable from this seat (the /logs redirect to blob.core.windows.net returns http 000 through the proxy), so "the new files were collected" rests on the root globs — vitest.config.mts:391-395 (packages/**/*.test.ts, unit project) and :406-409 (packages/**/*.test.tsx, dom project) — plus four green shards; consistent with the dev's 197 files / 3381 tests (that local reading is not sha-pinned in the report, checklist :47-48; CI is the reviewer-side reading anyway). No PR reviews (0), no review comments (0), no PR comment after the verdict except the bundle bot at 09:53:58Z.

Findings

F1 — ADDRESSED (was blocking). kanban-plugin-dialect-authoritative-7664.test.ts at head: it retitled :314 ("18 live members and exactly 7 tombstones"); live list :318-322 ends 'conditionalFormatting', 'grouping', 'navigation' with toHaveLength(18); tombstoned :324-327 = ['allowCollapse','cardTemplates','columnWidths','titleField','draggable','onColumnAdd','onCardAdd'], matching declaration order in complex.ts:380/398/422/451/477/486/495. The .shape key list (:219-228) keeps the three retired keys and adds navigation, titleField with a comment — different from the verdict's "move them" suggestion but correct on the mechanism: retirementTombstone() is z.never(...).optional() (tombstone.zod.ts:64-65), so a refused key is a declared .shape key. Shard 1 green.

F2 — PARTLY (4 of 5 legs delivered; non-blocking as before). packages/types/src/__tests__/kanban-arm-batch70-7742.test.ts (11 tests) and packages/plugin-kanban/src/__tests__/objectFieldsIsNotAuthorable-7742.test.tsx (3 tests):

  • Four refusals by name with remedy text — ✓ :77-110: per key, issue at path === key, a same-parse thisKeyIsDeclaredByNoArm control drawing nothing (:97-100), message toContain the remedy ('collapsed'/'COMPONENT PROP'/'HOOK OPTION'/'cardTitle'), which the zod messages carry (complex.zod.ts:223/229/234/240). Not asserted: the literal objectui#7742 / RETIRED tokens (present at complex.zod.ts:117,239) — minor, remedy is the load-bearing part.
  • Two-arm split — ✓ :123-140: type:'object-kanban' + titleField green (:129); type:'kanban' + titleField refused (:138, non-empty only — the path-titleField assertion is on the loop leg :86-92).
  • navigation accept + control — ✓ :142-167: {mode:'drawer'} / {mode:'page'} accepted; {mode:'not-a-mode'} refused with an issue under the navigation path (:153-158); extra control: undeclared member basePath refused (:165-166).
  • @ts-expect-error compile-time pins for the four ?: never — ✗ none (grep ts-expect-error over all three files: 0 hits). The house pattern sits in the same 7664 file, RetiredIsNever<KanbanSchema['draggable']> (:137-139), and was not extended. Test files are compiled (packages/types/tsconfig.test.json include src/**/*.test.ts, chained in type-check, package.json:69), so such pins would be live; today nothing type-level checks the four new tombstones on the .d.ts face. Four one-line type _XRetired = Assert<RetiredIsNever<KanbanSchema['x']>> close it.
  • Render pin — ✓ objectFieldsIsNotAuthorable-7742.test.tsx:89-115: prop leg painted (firing control), schema.objectFields leg unpainted, no-channel leg unpainted. It discriminates against main: origin/main:packages/plugin-kanban/src/index.tsx:210 still reads objectFields={schema.objectFields}, so the schema leg would paint there and fail; at head KanbanRenderer reads the prop (index.tsx:200, :226).

F3 — NOT ADDRESSED (non-blocking). No non-test hunk in the delta. index.tsx:183-184 still says "As a prop the only writer is the one caller that can actually know the answer"; the changeset's @object-ui/plugin-kanban section still presents the kanban-ui author path as closed. SchemaRenderer.tsx still strips a fixed metadata list and spreads the rest (...componentProps, :1576), so an authored objectFields on a kanban-ui node still arrives as the prop. See F6 — the delta adds a third carrier of the same overclaim.

F4 — RECORDED, NOT FOLDED IN (as the verdict asked). html-tier acceptance is unchanged: OBJECT_KANBAN_INPUTS (index.tsx:547) still declares titleField (:553) and is spread into 'kanban' (:581); zod still refuses (complex.zod.ts:238). Follow-up card exists: objectui#8802 (filed by the dev, 09:47:34Z — ~3 min after the verdict, before the pointer comment; open; options A–D; recommendation "A now, D as the real answer" = the verdict's option (a)). It carries no labels — not yet triaged / no needs-user-decision; the seat's pointer said the seat would open it, the dev did instead; either way the card is there. Sibling finding objectui#8801 (ObjectKanbanSchema.allowCollapse) also filed 09:47:07Z, open, unlabeled — out of this PR's scope, correctly not folded.

F5 — NOT ADDRESSED (non-blocking). The PR body fetched at review time has no line matching Clause-②:; only the prose heading ## ⚠️ Clause ② — … (body line 4). The body was evidently rewritten after the second commit (it names both new test files, the 11-test ablation table, and 197 files / 3381 tests where the verdict quoted NOT MEASURED), yet the fixed-spelling line was not added. Card side carries it; carriers script exit 0.

F6 — new, non-blocking (extends F3 into the delta). The new test's filename objectFieldsIsNotAuthorable-7742 and docblock (:21-28, "That made it reachable by an AUTHOR … it is now a React PROP") claim non-authorability, but every leg renders <KanbanRenderer …> directly and never goes through SchemaRenderer. What it proves is exactly what F2 asked — the renderer reads the prop and not schema.objectFields — not that an author cannot reach the prop on kanban-ui. Fix with F3's wording pass: rename/retitle to "reads objectFields from the prop, not the schema bag" and drop the authorability sentence, or add a SchemaRenderer-driven leg that would show the prop spread reaching it.

Acceptance notes

  • Scope: nothing beyond F1–F5 was changed; the delta is the F1 fix plus F2's pins. The full diff is still the ruling's kanban arm exactly (9 files); the object-kanban arm untouched (objectql.ts not in the diff).
  • Base drift (not a finding): the PR body's "origin/main has not moved" is now stale — origin/main is 0e3bca45d1 (3 commits: test(components): pin the bare text key to ui:text (objectui#7450) #8795, test(plugin-detail): pin the member shapes record:related_list reads (objectui#8071 slice 2) #8794, fix(plugin-kanban): the swimlane region owns the vertical scroll, and the column header sticks (objectui#8449) #8798), 9 files, zero overlap with the PR's 9; mergeable: true, mergeable_state: clean. fix(plugin-kanban): the swimlane region owns the vertical scroll, and the column header sticks (objectui#8449) #8798 changed packages/plugin-kanban/src/KanbanImpl.tsx (swimlane scroll/sticky header); the new render pin imports ../KanbanImpl and finds the card's paint by walking parents from the title text, so a DOM restructuring there is the one place the merged tree could read differently — the merge_group lane runs tests (ci.yml:687-696), which is where that gets read.
  • Landing choice for the seat, not mine: the only blocker is cleared and every check on the head is green, so this head is landable. The outstanding items (F2's type-level pins, F3/F6 wording, F5's one line) are small; but the changeset sentence is release-bound changelog text, and fixing it later means a docs-only PR after release. One wording-plus-four-type-lines commit would move the head and re-hang the label (contract-review.md: "head 后移 … 才重挂") — a delta-scoped re-review. Since the dev never saw the verdict, either a patch-round dispatch naming F2(d)/F3/F5/F6 explicitly, or landing as-is with those four carried to a follow-up card, is coherent; landing as-is leaves the changeset overclaim in the release notes.
  • Published-face delta unchanged from the verdict of record (types .d.ts: navigation new, four ?: never; plugin-kanban .d.ts: KanbanRendererProps.objectFields top-level). Changeset present for both published packages, minor; Bump Policy green.
  • PR form: still draft, base main, Fixes #7742 first line, labels unchanged (needs:contract-review still on PR and card — correctly, nothing cleared).
  • check-widening-tells.mjs remains blind to packages/types/src/zod; not re-run, human reading governs (unchanged).

Generated by Claude Code

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3484.3 KB 3512.7 KB
Main entry chunk (gzip) 144.0 KB 350 KB
Entry file index-CAKml1Ka.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) 15.67KB 5.75KB
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) 499.58KB 114.38KB
core (index.js) 7.48KB 2.96KB
create-plugin (index.js) 26.68KB 8.94KB
data-objectstack (index.js) 198.39KB 55.29KB
fields (index.js) 245.88KB 62.05KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 6.57KB 2.76KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.94KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 49.03KB 13.93KB
plugin-charts (index.js) 71.39KB 19.92KB
plugin-chatbot (index.js) 194.53KB 46.34KB
plugin-dashboard (index.js) 131.43KB 34.44KB
plugin-designer (index.js) 215.51KB 44.29KB
plugin-detail (index.js) 252.39KB 65.32KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 134.25KB 33.52KB
plugin-gantt (index.js) 167.16KB 40.99KB
plugin-grid (index.js) 210.67KB 57.19KB
plugin-kanban (index.js) 56.48KB 16.06KB
plugin-list (index.js) 112.73KB 27.69KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.54KB 20.84KB
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) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 2.32KB 1.24KB
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.55KB 2.45KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 13.64KB 4.59KB
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

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3484.5 KB 3512.7 KB
Main entry chunk (gzip) 144.0 KB 350 KB
Entry file index-MQwRiVXU.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) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 500.00KB 114.54KB
core (index.js) 7.48KB 2.96KB
create-plugin (index.js) 26.68KB 8.94KB
data-objectstack (index.js) 198.39KB 55.29KB
fields (index.js) 245.88KB 62.05KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 6.57KB 2.76KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.94KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 49.03KB 13.93KB
plugin-charts (index.js) 71.39KB 19.92KB
plugin-chatbot (index.js) 194.53KB 46.34KB
plugin-dashboard (index.js) 131.43KB 34.44KB
plugin-designer (index.js) 215.51KB 44.29KB
plugin-detail (index.js) 252.45KB 65.33KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 134.25KB 33.52KB
plugin-gantt (index.js) 167.16KB 40.99KB
plugin-grid (index.js) 210.67KB 57.19KB
plugin-kanban (index.js) 56.49KB 16.07KB
plugin-list (index.js) 112.73KB 27.69KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.54KB 20.84KB
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) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 2.32KB 1.24KB
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.55KB 2.45KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 13.64KB 4.59KB
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

Post-merge contract audit at CONTRACT_REVIEW_TIERVerdict: PASS WITH FINDINGS (audit reading; director seat, summon #18 segment 5, session_017Js5kTpTtxieBjPyScgxJ3, 2026-09-09T12:5xZ)

PR objectui#8799 · merged head 1ecfafae5fbac85c83624559b47cb526ef094a79 (re-read at posting 12:51:29Z: merged 11:40:18Z by os-warren, squash dcbf0b2bcb) · reviewed 12:38Z–12:48Z · verdicts of record before this one: 5599822824 (CHANGES REQUIRED on e050689af7) and 5600605700 (PASS WITH FINDINGS on a218a93772); no verdict existed on the merged head when the carrier came off at 11:23Z — incident row 8 on #17040 (5601992331).


Verdict: PASS WITH FINDINGS (post-merge delta audit; nothing on the merged head requires a revert; one small test-only debt is owed on main and is already carded)

Head reviewed: 1ecfafae5fbac85c83624559b47cb526ef094a79 (matches the requested prefix; merge commit, parents 547dade7 + 5dee0146; base at merge 5dee0146). Merged 2026-09-09T11:40:18Z by os-warren via merge queue; merge commit on main dcbf0b2bcb6c2f237092c71f5d1c11dec0960308 (squash, parent b89583bamain had moved 4 commits past the PR base: #8808, #8806, #8805, #8751). Squash stat = PR stat (9 files, +651/−58) and git diff dcbf0b2b 1ecfafae -- <9 PR files> is empty: what landed is the PR head's tree.

Delta summary (a218a9371ecfafae)

Three PR-side commits: 7273bba5 (merge origin/main@0e3bca45), 547dade7 (wording), 1ecfafae (merge origin/main@5dee0146). Both merges recompute exactly (git merge-tree tree == commit tree) — no conflict-resolution edits. On the PR's 9 files the delta is 547dade7 alone: 3 files, +64/−22.

hunk what judged
.changeset/7742-kanban-arm-batch70.md:55-75 heading renamed; "never an authoring surface" sentence replaced; new arm-level scope paragraph. Frontmatter (minor × 2) untouched correct — closes F3's release-bound carrier
packages/plugin-kanban/src/index.tsx:156-166 (countsAreWindowed TSDoc) fixes the "objectFields above" pointer the move had inverted; "Not an authorable input" → "Not MEANT to be"; says an author can write it on kanban-ui beyond F3's named lines but same class, accurate (KanbanRenderer reads schema.countsAreWindowed at :245), no behaviour
index.tsx:176-207 (objectFields TSDoc) "AN INTERNAL CHANNEL" → "INTENDED AS…"; the false "only writer" sentence deleted; new "closes the kanban ARM, not the key" paragraph correct vs SchemaRenderer.tsx:1480/1576 (no objectFields in the strip list) and the registrations :252/:579/:589, ObjectKanban.tsx:219 void _props — one precision gap, F7
__tests__/objectFieldsIsNotAuthorable-7742.test.tsxobjectFieldsIsAPropNotASchemaKey-7742.test.tsx (+17/−1) rename + docblock :10, new Scope section :30-45 docblock-only (mechanically: zero non-comment changed lines); old path referenced nowhere

Mechanically confirmed: every changed line in index.tsx and the test is inside a JSDoc block. complex.ts, complex.zod.ts, ObjectKanban.tsx, schema-reference.md, kanban-arm-batch70-7742.test.ts, the 7664 census pin: byte-identical to a218a937. The 25 other files that moved are main's own (#8798/#8794/#8795/#8804/#8796/#8744); none touches a PR file.

Published-face reading: unchanged. KanbanSchema.navigation?: ViewNavigationConfig (complex.ts:347, new); allowCollapse/cardTemplates/columnWidths/titleField ?: never (:380/:398/:422/:451); zod complex.zod.ts:220/226/231/237/238; KanbanRendererProps.objectFields?: unknown top-level (index.tsx:208, shifted from :193 by docblock growth). The only shipped text that moved is TSDoc on two .d.ts members; no shape moved, so the accept set did not move.

Open-items status

  • F2 (type-level leg) — NOT ADDRESSED. kanban-arm-batch70-7742.test.ts has no type-level construct; the 7664 file (:138-141) still pins only draggable/color/onColumnAdd/onCardAdd. Carded as objectui#8811 (11:14:11Z, before the clear; tests+finding, unassigned) — the "land as-is with a follow-up card" option the verdict of record allowed. Four Assert<RetiredIsNever<KanbanSchema['x']>> lines are owed on main.
  • F3 — ADDRESSED. index.tsx:176-207 and changeset :55-75 (above).
  • F5 — ADDRESSED. PR body line 18 is `Clause-②: yes`; run through the seat's own parser (readClause2Line in scripts/pm/check-clause2-carriers.mjs) → {kind:'declared', value:'yes'}. Body-only: the squash message carries the two commit messages + Fixes #7742, not the body.
  • F6 — ADDRESSED. Rename + docblock state the tests render KanbanRenderer directly and never exercise SchemaRenderer; Type Check green on the head covers compilation of the renamed file.
  • F4 (html-tier titleField) — UNCHANGED, as ruled. OBJECT_KANBAN_INPUTS (index.tsx:564) still declares titleField (:570) and is spread into 'object-kanban' (:586) and 'kanban' (:598) — identical to a218a937 modulo a +17 line shift; zod refusal complex.zod.ts:238 unchanged. objectui#8802 is open but still unlabeled (no needs-user-decision) ~3h after filing — the ruling it needs has no carrier yet.
  • The kanban-ui follow-up the PR body committed the PM to: objectui#8818 filed 11:45:02Z (post-merge), open, plugin: kanban+finding.

Clause-② reading: yes — unchanged. Delta is wording-only; no key, member, export or registration moved in either direction, so no re-derivation. Post-merge --pair 8799 exits 2 (PR not open → pair not formable; not a reading).

Governed surface: none. 9 files; none under AGENTS.md/CLAUDE.md/.claude/**/skills/**/docs/adr/**. Governed Surface Queue Guard green on the head (re-ran 11:23:24Z at ready-for-review) and on the queue run.

CI

  • Head 1ecfafae: 35 check-runs — 32 success / 3 skipped (Test (coverage), coverage shard, dependabot: by design, same skips as the a218a937 reading). Type Check 11:14:36Z, Lint 11:11:10Z, last Test (shard 1/4) 11:22:01Z. Pushed ~11:05:41Z; 547dade7 pushed ~10:48:53Z (one minute after the 10:47:55Z verdict, which was correctly on a218a937).
  • Merge-queue run: merge_group on gh-readonly-queue/main/pr-8799-b89583ba…, head_sha = dcbf0b2b — 18 workflows / 27 check-runs from 11:23:44Z, all success (2 by-design coverage skips), last Test (shard 2/4) 11:39:45Z → merged 11:40:18Z. So the "third window" the dev recorded as NOT MEASURED (b89583ba) was measured by the queue.
  • Post-merge push on main: all success, including the four coverage shards (12:00:22Z); merge-queue head patrol ×2 success.

New findings

F7 — LOW, wording precision (non-blocking; pre-existing surface, not moved by this PR). The delta's "on the 'kanban' arm that closes the key outright — ObjectKanbanRenderer serves that type key" (changeset :64-67, index.tsx:191-193, test docblock :33-36) is true of the plugin's own ComponentRegistry.register('kanban', ObjectKanbanRenderer) (:589). But the shipped "Standard Export Protocol" map kanbanComponents (index.tsx:343-347, in the published .d.ts) maps 'kanban'KanbanRenderer, and packages/plugin-kanban/README.md:63-71 ("Manual Integration") teaches registering every entry of that map by its key; packages/runner/src/plugin-integration.test.ts:11-50 pins the map. An app on that path registers 'kanban'KanbanRenderer, so an authored objectFields on a type:'kanban' node arrives by the same spread as on kanban-ui. Present on main before the PR (5dee0146:311-312), untouched here, named by neither #8818 nor #8811. Disposition: a one-paragraph rider on #8818 ("closed via the plugin's own registration; the kanbanComponents manual-integration route sends 'kanban' to KanbanRenderer"), plus a note that kanbanComponents.kanban and the registry's 'kanban' are two different renderers — a pre-existing inconsistency outside batch #70's ruling.

F8 — process (no fix on main follows). The carrier was cleared on a head with no verdict: needs:contract-review removed card 11:23:12Z / PR 11:23:13Z by os-warren, ready-for-review 11:23:18Z, queued 11:23:24Z — 36 min after the PASS on a218a937, with two later heads (547dade7, 1ecfafae) and no delta verdict. contract-review.md:22 ("head 后移 … 才重挂") and the 10:47Z verdict's own note (a wording commit "would move the head and re-hang the label — a delta-scoped re-review") both called for this audit before the clear. Also contract-review.md:38: no provenance comment on either carrier at clearing time; the card's Release: comment (5601282563, 11:43:31Z, post-merge) cites the PASS retroactively. Outcome-wise benign — this audit finds the delta correct — but it is the discipline gap for the round report.

F9 — cosmetic, carried over. Changeset :10-11 still says "which is why the PR carries needs:contract-review" — process noise in release-bound text (flagged in verdict 1), and now also false of the merged PR. Fold into the next touch of that changeset or the release-notes pass; not a PR on its own.

Acceptance notes


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

3 participants