Skip to content

docs(governance): objectui is a product-neutral renderer — AGENTS.md rule, ADR-0060, and a ratchet over registry keys, files and locale keys - #12253

Merged
os-zhuang merged 4 commits into
mainfrom
claude/issue-12224-product-neutral-renderer
Oct 13, 2026
Merged

os-zhuang merged 4 commits into
mainfrom
claude/issue-12224-product-neutral-renderer

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #12224
Clause-②: no

objectui is now a product-neutral renderer, as the maintainer ruled on 2026-10-12. This pull request writes the rule into AGENTS.md, records it as ADR-0060, and holds it with a ratchet test. AGENTS.md and docs/adr/** are governed (Tier H), so the whole pull request stays a draft until an authorized approval. Drafted in session session_012dGTuJen61ECNHXYXKChoC, branch claude/issue-12224-product-neutral-renderer.

The ruling, verbatim and untranslated:

现在转向

规则应该写入 objectui 仓库,要不然每个项目都会有页面定制的需求

维护者速读(草稿)

改了什么

  • objectui 从此是「产品中立的渲染器」。任何产品(Cloud、HotCRM、其他应用)都用 @objectstack/spec 的通用块拼自己的页面,文案留在产品自己的包里。页面拼不出来时,有两条路:在 objectui 加一个按「画什么」命名的通用块,或者在 @objectstack/spec 开一个契约门。不再为某个产品加专用的组件、对话框、hook 或文案命名空间。
  • 规则写进 AGENTS.md(§1 新增一节,共 +10 行)和新的 ADR-0060。
  • 新增一个「只减不增」的棘轮测试。今天已有的产品专用面都列在一张清单里:8 个注册名、28 个文件,以及 9 个冻结命名空间下的 90 个文案键(读于 27c30c10)。这张清单只能删,不能加。出现以下任何一种,测试都会变红:新的产品命名注册、冻结目录里的新文件、冻结命名空间下的新文案键。
  • 新增 PR 模板,只有一个勾选项:Adds no product-specific widget, dialog, hook or string。

为什么改

  • Cloud 控制台的页面一点点长进了 objectui。每改一句 Cloud 文案,就要一个 objectui PR 再加一次版本钉更新;有条车道曾为一个字符串(「团队版起」)等 Cloud。下一个接入的产品会重复这条路。

风险与代价(含回滚)

  • 这是一道新门禁。它只读名字,也就是注册名、文件名和文案键名;起了中性名字的产品代码它看不见,只能靠评审和 PR 模板的勾选项。
  • 为了让「只删不增」由机器守住,清单的上限写在 ADR-0060 末尾的 JSON 块里。往清单里加一项,就必须同时改 ADR;ADR 是受管文件,所以加项必须经过您批准。只往清单里追加,测试仍然是红的(见下方 M5)。
  • 卡上说「locale 新增顶层命名空间即失败」。按字面读,连通用块需要的文案也加不进去,与规则 2 矛盾。本 PR 的读法是:名字以产品词(cloud、hotcrm)打头的命名空间或键,拒收;冻结命名空间下,不许加键;起中性名字的新命名空间,放行。详见下文「H3」。
  • 冻结范围比卡上的清单多了几项,都是实测出来的:
    • cloud-connection:panel 这个注册;
    • console/cloud-connection/ 和 console/diagnostics/CloudAiModelStatus.tsx;
    • @object-ui/types 的 cloud.ts 与 zod/cloud.zod.ts;
    • planTier 与 aiModelStatus 两个命名空间;
    • 整个 environment 命名空间(卡上只写了 environment.entitlement)。
  • 回滚:revert 本 PR 即可。它不碰任何运行时代码,也不发版(空 frontmatter changeset)。

席位意见

(留空,由席位定稿)

你要做的

  • 这是一道新门禁,批准本 PR 即授权它:每次 pnpm test 都会在 unit 项目里跑它。
  • 请确认以下几点:H3 的读法;冻结范围的增项;「清单上限写在 ADR 里,加项必须改 ADR」的做法。
  • objectstack 侧那一行 rider 因行数棘轮零余量,本 PR 没有带上,待 PM 决定(见 Acceptance notes)。

What changes

Path What
AGENTS.md A "Product-neutral renderer" section in §1 (608 → 618 lines): the three rules, the escalation path, and where the ratchet and its two lists live. It cites no issue number.
docs/adr/0060-product-neutral-renderer.md The ruling, the readings taken, the bounds, and the Frozen baseline JSON block that the test reads as its ceiling. 0060 was re-read on origin/main before opening and is still free; 0059 is the last ADR there.
packages/app-shell/src/__tests__/product-neutral-renderer.ratchet.test.ts The ratchet, 32 tests, in the root unit project.
packages/app-shell/src/__tests__/product-specific.allowlist.json The live list, edited only by deleting entries.
scripts/markdown-test-inputs.mjs Two ledger rows: the ratchet reads ADR-0060, and check-installed-spec-pin-claims reads the new template. That gate scans every tracked file, and the ledger is pinned total over tracked markdown.
.github/pull_request_template.md One checkbox, nothing else. It carries no closing keyword and no line address, so neither PR-body reader in this repository (cross-repo-issue-closer.yml, check-changeset-claims.mjs) finds anything in it.
.changeset/12224-product-neutral-renderer.md Empty frontmatter: nothing is released, because __tests__ is excluded from app-shell's build.

How the ratchet measures (ADR-0060 has the full text)

  • Registrations. The ratchet takes every key from deriveRegistryKeys (scripts/check-doc-component-types.mjs), the one registration reader that objectui check's known-type list already comes from. A key counts when any colon segment leads with a product word. It does not scan for register('cloud: itself.
  • Files. A file counts when it is a non-test file under packages/, apps/ or examples/ and either sits in a frozen directory (app-shell/src/environment/ or app-shell/src/console/organizations/, recursively) or has a path segment led by a product word.
  • Locale keys. In every built-in locale, two kinds count: every leaf key under a frozen namespace, and every key at any depth whose own name leads with a product word.
  • Product words are cloud and hotcrm, matched only when they lead the name at a word boundary, after skipping a hook's leading use. So useCloudThing counts, while word-cloud and cloudy do not.
  • Two lists. The live list must equal the measurement exactly. An unlisted hit is red. A stale entry is red too, which makes the list shrink as pages migrate. Every live entry must also sit inside ADR-0060's ceiling, so appending to the live list alone stays red.

H1: both registration spellings

At 923504f4a, a literal register('cloud: grep finds two cloud widgets: cloud:ai-model-status and cloud:onboarding-next. cloud:plan-status and cloud:workspace-timezone-notice register as register('plan-status', …, { namespace: 'cloud', skipFallback: true, … }) and similar, so a literal scan misses them. The derivation reads both spellings, and it found a fifth product registration that the card's inventory did not list: cloud-connection:panel, with app-shell: twins. The live list holds 8 keys. A fixture control in the test checks both spellings against a synthetic tree, so the namespace half is still tested after the last product widget has migrated. M1 and M2 below show the real tree going red on each spelling.

H3: the locale rule's reading

Read literally, the card says two things: a new top-level namespace fails, and the list may only lose entries. Together they forbid every new namespace forever, including the generic-block copy that rule 2 asks for. The reading taken here follows rule 2's own words, "never an i18n namespace named for the product":

  • a namespace, or a key at any depth, whose name leads with a product word fails, unless a frozen namespace holds it;
  • under a frozen namespace, no key may be added in any locale;
  • a new namespace with a neutral name passes.

ADR-0060 records this, and the 速读 above asks the maintainer to confirm it.

The frozen namespaces fall into three groups:

  • Led by a product word: cloudConnection, cloudOnboarding, cloudPlanStatus, cloudWorkspaceTimezoneNotice.
  • From the card's inventory: organizations, workspace, and the whole of environment. Every source consumer of environment sits in the frozen environment/ directory.
  • Measured: planTier and aiModelStatus. Each has one consumer, and that consumer is a frozen or product-named file.

Some namespaces were considered and not frozen; the ADR gives the reasons:

  • organization (singular), because @object-ui/auth and the layout also read it;
  • chatbotQuota, a generic quota state;
  • marketplace and connectAgent, features of the open stack.

The locale bundles are the ten in packages/i18n/src/locales/*.ts: en, zh, ja, ko, de, fr, es, pt, ru, ar. The test reads them through @object-ui/i18n/locales.

Mutation proofs (acceptance 2)

All five proofs follow the same method:

  • The implementation was committed first (5298f952).
  • Each mutation went through ablation-replace.mjs (objectstack's scripts/). It needs its anchor to hit exactly once and checks the write on disk, and its WRAP mode runs the test and then restores the file. The new-file leg (M3) was instead a bash script with a trap that removes the file.
  • Every leg ends with git diff HEAD empty and git status --porcelain empty.
  • Nothing was committed or pushed for any leg.
  • Line addresses in the excerpts are elided, as AGENTS.md [WIP] Update documentation for project #11 asks.

Two first attempts did not count:

  • M1 and M4 first used a replacement that still contained the anchor, so the anchor count did not drop.
  • The tool refused both (the anchor count moved 1 -> 1, a drop of 0), and its restore proved the files unchanged.
  • Both were re-run with a non-overlapping anchor. The runs below are those re-runs.

M1: a literal register('cloud:mutation-probe', …) added to console/marketplace/InstalledListWidget.tsx.

ablation-replace: ok mutation landed: anchor 1 -> 0, blob 0e1e48ae5e82 -> 89ebf5d82eb0
 FAIL  |unit| …product-neutral-renderer.ratchet.test.ts > … > registrations > no registry key leads with a product word unless the live list carries it
AssertionError: new product-named registry key(s). objectui is a product-neutral renderer (AGENTS.md › Product-neutral renderer, ADR-0060): …
+   "cloud:mutation-probe (registered at packages/app-shell/src/console/marketplace/InstalledListWidget.tsx, line address elided)",
      Tests  1 failed | 31 passed (32)
ablation-replace: ok restored: blob == HEAD (0e1e48ae5e82) and `git diff HEAD` is empty

M2: the namespace spelling, register('mutation-probe', …, { namespace: 'cloud', skipFallback: true }), in the same file.

ablation-replace: ok mutation landed: anchor 1 -> 0, blob 0e1e48ae5e82 -> fd0368a3b2e7
       × no registry key leads with a product word unless the live list carries it
+   "cloud:mutation-probe (registered at packages/app-shell/src/console/marketplace/InstalledListWidget.tsx, line address elided)",
      Tests  1 failed | 31 passed (32)
ablation-replace: ok restored: blob == HEAD (0e1e48ae5e82) and `git diff HEAD` is empty

M3: a new packages/app-shell/src/environment/Foo.tsx.

LANDED: exists=yes marker_count=1
       × no file is added under a frozen directory, or named for a product, unless the live list carries it
+   "packages/app-shell/src/environment/Foo.tsx",
      Tests  1 failed | 31 passed (32)
RESTORED: exists=no DIFF_HEAD_LINES=0 PORCELAIN=[]

M4: a new key under cloudOnboarding, in zh.ts only.

ablation-replace: ok mutation landed: anchor 1 -> 0, blob 3a8fca3aec69 -> bc0743e069cd
       × zh: no key is added under a frozen namespace unless the live list carries it
+   "cloudOnboarding.mutationProbe",
      Tests  1 failed | 31 passed (32)
ablation-replace: ok restored: blob == HEAD (3a8fca3aec69) and `git diff HEAD` is empty

M5: M1 plus the move an agent reaches for, appending cloud:mutation-probe to the live list.

The registration assertion now passes, because the key is listed. The ceiling assertion turns red instead.

       × every live entry is inside ADR-0060’s ceiling — the live list takes deletions only
AssertionError: packages/app-shell/src/__tests__/product-specific.allowlist.json carries entries ADR-0060's frozen baseline does not. The live list is edited only to DELETE; a new product-specific entry is an amendment to ADR-0060 that the maintainer approves. …
+   "registration cloud:mutation-probe",
      Tests  1 failed | 31 passed (32)
ablation-replace: ok restored: blob == HEAD (0e1e48ae5e82) and `git diff HEAD` is empty
ablation-replace: ok restored: blob == HEAD (8c10754e4d85) and `git diff HEAD` is empty

Gates (objectui, at 27c30c10, local)

objectstack's dispatch-gates.mjs does not live in objectui. These gates were therefore derived by hand from objectui's package.json, .github/workflows/lint.yml, repo-gates.yml, ci.yml, changeset-presence.yml and line-citation-gate.yml. Each exit code was captured before any pipe.

Command Exit Verdict line
pnpm exec vitest run --maxWorkers=2 scripts/__tests__/ packages/app-shell/src/__tests__/product-neutral-renderer.ratchet.test.ts (repository-root form) 0 Test Files 182 passed | 2 skipped (184) · Tests 5573 passed | 2 skipped (5575)
pnpm exec vitest run --project unit --maxWorkers=2 packages/app-shell/ 0 Test Files 277 passed | 1 skipped (278) · Tests 3839 passed | 9 skipped (3848)
pnpm --filter @object-ui/app-shell type-check (echoed > @object-ui/app-shell@17.7.0 type-check, after turbo run build --filter='@object-ui/app-shell^...', 28/28 tasks) 0 tsc --noEmit && tsc -p tsconfig.test.json clean; --listFilesOnly shows the new test among the project's 5366 files
eslint on the two lintable changed files (--format json: 1 + 1 files) 0 0 errors, 0 warnings. Type-aware linting is off (no parserOptions.project / projectService in eslint.config.js), so this diff cannot change a verdict on an untouched file.
node scripts/check-governed-queue-guard.mjs --test (the 7 paths) 3 ⛔ GOVERNED — 2 of 7 path(s) are on a governed surface (docs/adr/**, AGENTS.md), which is the expected verdict
node scripts/check-changeset-presence.mjs 0 ✅ 2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s) (empty frontmatter)
node scripts/check-changeset-no-major.mjs 0 ✅ No changeset declares a major bump.
node scripts/check-changeset-claims.mjs 0 ✅ No pending changeset names a file this change touches.
node scripts/check-new-cross-file-line-citations.mjs 0 VERDICT new-cross-file-line-citations: 0 new citation(s)
node scripts/check-control-bytes.mjs 0 ✅ check-control-bytes: OK (scanned 8511 tracked text file(s); skipped 85 binary).
node scripts/check-test-path-roots.mjs 0 ✅ check-test-path-roots: OK
node scripts/check-doc-links.mjs 0 Links are valid across 17 scan roots.
node scripts/check-lint-coverage.mjs 0 ✅ lint coverage: 46/46 packages linted
node scripts/markdown-test-inputs.mjs --audit 0 123 candidate test files, all adjudicated; 87 declared entries, all present.
node scripts/check-unreferenced-sources.mjs 0 OK Every shipped source file in every covered package is reachable.
node scripts/check-published-tsconfig-tooling-exclude.mjs 0 OK -- all 34 enforced package(s) carry the directory form.
node scripts/check-doc-component-types.mjs 0 ✅ Every documented component type is registered.

Declared to CI, not run locally:

  • the dom and dom-heavy projects of app-shell. This diff adds one unit-project test and changes no source those projects import.
  • pnpm lint (repository-wide).
  • pnpm check (the CLI self-check).
  • the Test (shard N/12) matrix.

Selector check: select-affected-tests.mjs keeps the ratchet in every pull request's fast set. A change confined to packages/plugin-map or packages/vscode-extension still selects it, with the reason "reads the disk or spawns a process". A change to ADR-0060 alone selects it through the markdown ledger.

Acceptance notes

  • What the ratchet cannot see. ADR-0060 states each bound:
    • product code under a neutral name outside the frozen directories;
    • growth inside an existing file;
    • the derivation's own KNOWN GAP readings;
    • re-adding an entry that the live list already deleted, because the ceiling keeps it until an ADR amendment drops it.
  • Test files are outside the file half. A test ships in no dist, and a regression test on frozen code has to stay writable until that code migrates. This is the H2 decision, and it is recorded in ADR-0060. organizations/manage/ is inside the frozen directory, since the freeze is recursive.
  • H4 ledgers. scripts/__tests__/turbo-test-inputs.test.ts guards the Vitest configuration program, not the files a test reads, so the ratchet needs no row there. It ran green above. scripts/markdown-test-inputs.mjs did need rows, both listed under "What changes".
  • H7. objectui#7611 had not landed at 923504f4a, the base, which still matched origin/main when this pull request was opened. The baseline is read at this pull request's head. If feat: Setup catalog pages read the registry (editable under single, read-only under a wall); assignment pages stay data pages; pickers list the registry (objectstack ADR-0131 D3/D7) #7611 lands first with keys under a frozen namespace, the live list and ceiling are re-read at the new head before landing; no exemption is reserved for it.
  • The objectstack rider is not in a pull request yet. The card asks for one line in .claude/skills/pm-dispatch/references/filing-gate.md. Measured on objectstack@9f4b652e3, that file is 58 lines against a check-skill-line-ratchet ceiling of 58, so its headroom is 0. The drafted line (- UI 卡写明所需通用块或 spec 契约门;要产品命名 widget/dialog/hook/命名空间的卡退回重立。, 116 bytes, green on check:pm-skill-id-lint) takes the file to 59. The ratchet then answers ✗ … filing-gate.md is 59 lines; the ratchet ceiling is 58. The dispatch allowed one line and said to stop on a breach, so the rider waits for the PM to choose how the line is paid for. Its branch, claude/issue-12224-objectui-filing-gate-rider, is pushed with no commits.

Generated by Claude Code

…rule, ADR-0060 and its ratchet

A product composes its pages from generic blocks; a page that cannot be
composed is fixed by a generic block here or a contract door in
@objectstack/spec, never by a widget, dialog, hook or i18n namespace
named for the product. What exists stays until its page migrates and
may only shrink.

- AGENTS.md: a "Product-neutral renderer" section in §1.
- docs/adr/0060-product-neutral-renderer.md: the ruling, the readings
  taken, and the frozen-baseline block the ratchet reads as its ceiling.
- app-shell: product-neutral-renderer.ratchet.test.ts and its live list
  product-specific.allowlist.json (delete-only; must stay inside the
  ADR's ceiling).
- scripts/markdown-test-inputs.mjs: the test reads the ADR.
- .github/pull_request_template.md: one checkbox.
- An empty-frontmatter changeset: nothing publishes.

Claude-Session: https://claude.ai/code/session_012dGTuJen61ECNHXYXKChoC
Co-authored-by: Claude <noreply@anthropic.com>
…ew pull-request template

check-installed-spec-pin-claims scans every tracked file, so the new
.github/pull_request_template.md is its input; the ledger is declared
total over tracked markdown and goes red until that read is recorded.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

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

Size Limits

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 27c30c10ae8992bc43855d2cfdf4475495c335f7
Local-runs: none

Inputs: card #12224 (body and all five comments), PR #12253 (body, its seven-file list, the net diff against the merge-base 923504f4a), and the check-runs on the head. Every factual claim below was checked by reading objectui source at the PR base 923504f4a and at current origin/main 1fb1ced36 (sixteen commits past the base at read time). The locale catalogues were compared by parsing their saved source text in scratch space; the ratchet itself was not run, nothing was built, and no gate was re-run.

Check-runs on the head, re-read at 2026-10-12T23:12Z before posting: 33 total, all completed; 30 success, 3 skipped (dependabot, Test (coverage), Test (coverage shard …) — path-switched lanes, not failures); 0 in_progress, 0 failure. Green: Lint, Type Check, Test and all twelve Test (shard N/12), Build & E2E, Repo Gates, Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Line Citation Gate, Governed Surface Queue Guard (its pull_request leg exits 0 by design; the PR is governed and stays a draft).

① Derived judgments

Every accept-set and public-surface change the diff implies, each judged:

  1. A new CI gate runs on every pnpm test. The ratchet is a *.test.ts under packages/, so the root unit project (packages/**/*.test.ts, node environment) runs it; it imports node:fs, so select-affected-tests.mjs keeps it alwaysOn with the reason "reads the disk or spawns a process". The PR body's claim that it sits in every pull request's fast set is right. The triage comment on the card waived the new-gate default because the ratchet is the maintainer's own direction. Right.
  2. Registrations: 8 keys, both spellings. Read at the five sites: cloud-connection:panel, cloud:ai-model-status and cloud:onboarding-next register with namespace: 'app-shell' and no skipFallback, so the derivation stores the app-shell:-prefixed key and the bare key (six); plan-status and workspace-timezone-notice register with namespace: 'cloud', skipFallback: true, so only cloud:plan-status and cloud:workspace-timezone-notice exist (two). The ADR's sentences "Two of today's four cloud widgets use the second spelling" and "a fifth product registration the card's inventory did not list, cloud-connection:panel" are right. Reading keys through deriveRegistryKeys (exported from scripts/check-doc-component-types.mjs, signature (root, options = {}), returning keys and findings) instead of a literal grep is right; the test's pin that findings is empty turns an unrecognised spelling red instead of silently shrinking the population. The five sites are unchanged on current main; the derivation's DOC_TYPE_EXEMPTIONS table moved on main (objectui#12242) but not the export the test calls.
  3. Files: 28; frozen directories recursive; test paths excluded; product-led names repo-wide. git ls-tree of app-shell/src/environment/ and app-shell/src/console/organizations/ at base and at current main holds exactly the 28 listed non-test files plus 23 files under __tests__/. Product-led path segments under packages/, apps/ and examples/ are exactly the five listed non-test files plus test files (cloud-surface-retired-4152.pin.test.ts, cloud-plan-status-arm-10919.test.ts, cloud-workspace-timezone-notice-arm-11930.test.ts, three CloudConnectionPanel.*.test.tsx), every one excluded by isTestPath. The packages/types pair cloud.ts / zod/cloud.zod.ts carries the cloud widgets' node-type arms (the registration comments name CloudPlanStatusSchema and CloudWorkspaceTimezoneNoticeSchema). Right. The repo-wide product-led predicate is a widening of the card's literal console/home/Cloud*.tsx item; it follows rule 2's "named for the product", was flagged as H2, and is escalated in ③.
  4. Locale keys: 90 under 9 namespaces, in all ten catalogues. Parsed en zh ja ko de fr es pt ru ar at base and at current main: each catalogue has 73 top-level namespaces and exactly 90 leaf keys under the nine frozen namespaces, every one listed; no unlisted key under a frozen namespace, no product-led key outside them, no stale entry. Right. The sixteen commits main took since the base touch all ten catalogues (63 insertions) only under detail and console, neither frozen nor product-led, so the ratchet stays green when this PR is rebuilt on current main; the H7 concern (objectui#7611) has not landed.
  5. Consumer readings behind the freeze choices, read at base. environment.* is consumed only by EnvironmentListToolbar.tsx and entitlements.ts (all 14 keys used), both in the frozen directory: right. planTier.teamAndUp has one consumer, that toolbar: right. aiModelStatus.* has one source consumer, CloudAiModelStatus.tsx: right. organization (singular) is read by layout/CurrentOrganizationIndicator.tsx, layout/WorkspaceSwitcher.tsx and packages/auth/src/org-roles.ts: right. chatbotQuota is read by plugin-chatbot/src/ChatbotEnhanced.tsx: right. marketplace and connectAgent are read by console/marketplace/* and console/connect/ConnectAgentWidget.tsx: right as a reading; "features of the open stack" is a judgment the maintainer owns. One reading the ADR does not state, found here: the card-named organizations and workspace namespaces are also consumed outside the frozen directories — organizations.title, organizations.mine, organizations.create in layout/AppHeader.tsx and layout/WorkspaceSwitcher.tsx, and workspace.default in console/home/HomeLayout.tsx. Freezing them is what the card asked, so it is faithful, but those neutral layout files can no longer gain copy under those namespaces, and by the ADR's own reasoning for leaving organization (singular) unfrozen the split deserves the maintainer's eye (③).
  6. Changeset claims. packages/app-shell/tsconfig.json excludes **/__tests__/** and **/*.test.ts(x); package.json files is dist, src/styles.css, README.md, CHANGELOG.md, LICENSE; the build is a bare tsc. Nothing under src/__tests__/ reaches dist. The @ts-expect-error on the .mjs import is not a phantom check: tsconfig.test.json includes src/**/*.test.ts and the Type Check run is green. Right.
  7. Ledger edit. scripts/markdown-test-inputs.mjs documents reads, notRead and walker as the entry keys; the ratchet row declares the ADR read and a not-markdown walker; the check-installed-spec-pin-claims row gains .github/pull_request_template.md, which the dev measured red before the row and green after. Right.
  8. Template and ADR number. .github/pull_request_template.md exists neither at base nor at current main; docs/adr/0059-action-params-shared-field-widgets.md is still the last ADR on main. AGENTS.md goes 608 to 618 lines and is unchanged on main. Right.
  9. Governed surfaces. AGENTS.md and docs/adr/** are Tier H in objectui's own register; the PR is a draft with no auto-merge and no approval from any seat. The other five paths are not governed. This record does not lift Tier H; the maintainer's approval does.
  10. The two-list design. The live list must equal the measurement (an unlisted hit is red, a stale entry is red) and must be a subset of the ADR's fenced json ceiling; the M5 proof in the PR body shows the append-to-allowlist move going red. That mechanises the card's "edited only to delete" and puts the inventory in the ADR as the card's item 2 asked. Faithful, with one cost the maintainer accepts or not: every future addition — a product word, a frozen directory, a frozen namespace, or a surface that must be tolerated — is an ADR amendment carrying the maintainer's own approval.
  11. Declared but not enforced (objectui AGENTS.md 完善设计器的每一个细节 #9). The ADR's consequence "Every pull request's description carries the template's checkbox" overclaims: GitHub inserts a template only for pull requests opened through the web form, a relay-created PR (this one included; its body carries no checkbox) does not get it, and nothing checks for it. AGENTS.md's "left to review and the pull-request template's checkbox" therefore names a weaker residual than the ADR states. Not a FAIL — the card's deliverable is the template line, and the test's four bounds are stated honestly — but the sentence should be narrowed (③).

② Semver level

.changeset/12224-product-neutral-renderer.md carries empty frontmatter: no released package publishes anything. The diff touches two files under packages/app-shell/src/__tests__/ (build-excluded), one root script, governed prose, a template and the changeset itself. Changeset Declaration, Changeset Bump Policy and Changeset Fixed Group Check are green. The level matches what the diff publishes: nothing.

Clause-②: no — no accept-set and no public API of any released package widens or narrows.

③ Boundary flags

The dev's flags (os-dev report 6125187793, deviations and open_questions), each answered or escalated:

  • Ceiling read out of ADR-0060 ("route bettered") — answered: right (① 10); its cost is named for the maintainer below.
  • H1, static derivation instead of a runtime enumeration — answered: right (① 2); a runtime enumeration would see only app-shell's import closure.
  • H2, recursive freeze, test paths outside the file half, product-led names repo-wide — answered: right as read (① 3); the repo-wide widening is escalated below.
  • H3, the locale reading — escalated. The card's literal clause (a locale file that "gains a top-level namespace … that the list does not carry" fails) is replaced by: a product-led namespace or key fails, a frozen namespace takes no new key, a neutral new namespace passes. The ADR's argument — the literal clause would also refuse the copy that rule 2's generic blocks need — is sound, and the maintainer's quoted ruling says nothing about locale namespaces either way, so this is a reading the maintainer confirms, not one this record settles.
  • Frozen i18n set beyond the card (environment whole, planTier, aiModelStatus) — answered: the consumer readings hold (① 5); escalated as a widening of the freeze the maintainer confirms.
  • Second ledger row — answered: right (① 7).
  • Rider not delivered, and how its line is paid for — outside this review: the PM answered A (6125224340) and the rider landed as draft docs(pm-dispatch): a UI card names the generic block or spec door it needs (rider for objectstack-ai/objectui#12224) objectstack#23051 (6125436892), reviewed on its own.
  • Commit trailers, the model-free pair — answered: right under both repositories' AGENTS.md.
  • Full root pnpm test not run locally — answered by the head's check-runs: all twelve shards, Test, Type Check, Lint and Repo Gates are green.
  • open_questions — the one entry (the rider line) was answered by the PM and closed by the rider PR; none is open on this PR.
  • out_of_scope_findings (organization singular and chatbotQuota left unfrozen) — carried to the maintainer below.

What the maintainer must look at (Tier H: the approval is the landing act, and these ride with it):

  1. H3. Confirm the locale reading — neutral new namespaces pass, product-led ones fail, frozen ones take no key — against the card's literal "gains a top-level namespace" clause.
  2. Freeze scope beyond the card: cloud-connection:panel with its app-shell: twin, console/cloud-connection/, console/diagnostics/CloudAiModelStatus.tsx, types/cloud.ts, types/zod/cloud.zod.ts, the whole environment namespace (the card said environment.entitlement), planTier, aiModelStatus.
  3. The ceiling lives in the ADR. Every addition to the list, and every new product word, frozen directory or frozen namespace, is an ADR amendment that needs your approval. Accept that cost, or ask for the rules to move out of the governed block.
  4. organizations and workspace are frozen but also read by neutral layout files (AppHeader.tsx, WorkspaceSwitcher.tsx, HomeLayout.tsx), while organization (singular) and chatbotQuota are left unfrozen on that same consumer logic. Decide whether that split is the one you want.
  5. ADR wording. "Every pull request's description carries the template's checkbox" is not true for pull requests opened through the API; narrow it, or accept that the residual control is review alone.
  6. Product words are cloud and hotcrm only; any other product name passes the name gate until the block is amended.

Implemented-by: claude/issue-12224-product-neutral-renderer
Reviewed-by: session_012dGTuJen61ECNHXYXKChoC

VERDICT: PASS


Generated by Claude Code

…is not a gate

GitHub prefills .github/pull_request_template.md only for pull requests
opened through the web form, so a PR opened through the API carries no
checkbox and nothing checks for one. The Consequences bullet claimed every
pull request carries it; it now says which ones do and that it is a prompt.
The bounds list's "the checkbox and review catch the rest" is narrowed the
same way: the rest is left to review.

The frozen-baseline block is unchanged.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 72 chunks) 3131.2 KB 3204.6 KB
Main entry chunk (gzip) 73.5 KB 350 KB
Entry file index-BJ2UQCtr.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) 20.69KB 7.62KB
app-shell (runtime-config.js) 22.59KB 7.89KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 41.19KB 11.12KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.26KB 8.03KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (commentAuthorship.js) 1.39KB 0.78KB
collaboration (index.js) 1.93KB 0.85KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 588.51KB 141.71KB
core (index.js) 10.18KB 4.04KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 244.49KB 68.27KB
fields (index.js) 280.00KB 71.39KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 40.26KB 11.71KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 15.35KB 5.51KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.82KB 2.38KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 54.31KB 15.86KB
plugin-charts (index.js) 84.72KB 23.27KB
plugin-chatbot (index.js) 201.72KB 48.07KB
plugin-dashboard (index.js) 143.78KB 38.84KB
plugin-designer (index.js) 233.77KB 50.02KB
plugin-detail (index.js) 254.49KB 67.01KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 178.36KB 46.26KB
plugin-gantt (index.js) 178.63KB 45.19KB
plugin-grid (index.js) 255.09KB 70.91KB
plugin-kanban (index.js) 53.23KB 16.71KB
plugin-list (index.js) 123.35KB 31.54KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 45.65KB 12.81KB
plugin-timeline (index.js) 39.21KB 11.85KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 92.11KB 23.27KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 12.07KB 3.68KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.81KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.26KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.48KB 3.50KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 5fed8ccc464cb4f3f8b408eb7efa2dd8efe3e1be
Local-runs: none

Inputs: card #12224 (body and all eight comments, the rework scope 6134419284 and the dev's rework report 6134447475 among them), PR #12253 (body, its seven-file list, its three comments — the record on the previous head, 6134399082, is one — the net diff against main at this head, and the compare 27c30c10ae...5fed8ccc46), and the check-runs on this head. Factual claims were checked by reading objectui at the PR base 923504f4a and at current main 48fb558d36 through the API; the ratchet was not run, nothing was built, and no gate was re-run.

This head is the previously reviewed 27c30c10ae plus one commit. The compare between the two heads touches one file, docs/adr/0060-product-neutral-renderer.md, in two hunks: the first bullet under "What the ratchet cannot see" and the third bullet under "Consequences" — the two sentences the rework asked for and nothing else. The other six files of the net diff are byte-for-byte what record 6134399082 read, so that record's judgments on them are adopted below as readings of unchanged files and are marked as such; they were re-derived here only where this round's own reads touch them.

Check-runs on the head, re-read at 2026-10-12T23:27Z before posting: 33 total (a Test aggregator joined once the shards finished); 0 failure, 0 cancelled, 0 timed_out. 32 completed: 29 success, 3 skipped (dependabot, Test (coverage), Test (coverage shard …/8) — path-switched lanes, not failures). One row, Changeset Fixed Group Check, is recorded as the API gives it: status in_progress with conclusion success and a completed_at ten seconds after its start; it is not counted as a pass here, and the changeset verdicts this record relies on are Changeset Declaration, Changeset Bump Policy, Changeset Claim Re-read and Changeset Overwrite Report, all success. Green: Lint, Type Check, Test and all twelve Test (shard N/12), Test (dist pins), Build & E2E, Build Docs, Bundle Analysis, Repo Gates, Line Citation Gate, Governed Surface Queue Guard (its pull_request leg exits 0 by design; the PR is governed and stays a draft), Spec Main Shape Gate (advisory on pull requests since main's 5039e8117b), Live E2E (informational), label. At this read main is still 48fb558d36, the PR head is still 5fed8ccc46, and mergeable_state reads clean.

① Derived judgments

Every accept-set and public-surface change the net diff implies, each judged:

  1. The rework delta is exactly the two narrowed sentences. 27c30c10ae...5fed8ccc46 is one file, +3/−3 lines, and the ADR stays 234 lines. "are all it reads; the pull-request template's checkbox and review catch the rest;" became "are all it reads; the rest is left to review, which the template's checkbox prompts only on web-form PRs;"; "Every pull request's description carries the template's checkbox: …" became "A pull request opened through the web form starts with the template's checkbox; one opened through the API, as the fleet's are, does not. The checkbox prompts human authors; it is not a gate." Right — this is the scope 6134419284 set.
  2. The frozen-baseline block is byte-identical to the previous head. The section from the ## Frozen baseline heading to end of file is 64 lines with sha256 a7fb7087… on both heads, and the fenced json block inside it is 60 lines with sha256 f7b4d8bb… on both; the dev's report quoted a7fb7087… and the figure holds. readCeiling() reads exactly that block, so the ratchet's input on this head is the previous head's input: 8 registrations, 28 files, 90 keys under 9 namespaces, two product words, two frozen directories. Right.
  3. The narrowed text is now true. (a) "one opened through the API, as the fleet's are, does not" — this PR was opened by objectstack-fleet[bot] through POST /repos/objectstack-ai/objectui/pulls (the os-dev report's api_writes), and its body carries no task-list line; GitHub inserts .github/pull_request_template.md only into a description composed in the web form. (b) "it is not a gate" — no workflow under objectui's .github/ and no script under scripts/ reads a pull-request body for the template or for a task-list box (the only checkbox hits in the tree are the field-kind prose in .github/prompts/component.prompt.md and the retired designer checkboxes named in check-designer-field-key-parity); the two PR-body readers the PR body names look for closing keywords and file paths. (c) "the rest is left to review, which the template's checkbox prompts only on web-form PRs" follows from (a) and (b). Right, and the bullet now does what objectui's Commandment 完善设计器的每一个细节 #9 asks: where nothing re-derives the claim, the text says so.
  4. Two other carriers still say the pre-rework thing, and the dev said so. AGENTS.md's new section ends "is left to review and the pull-request template's checkbox", and the ratchet test's header comment reads "the pull-request template's checkbox and review are what catch the rest" — the wording the ADR bullet had before this commit. The seat scoped the patch to the ADR ("Nothing else in the PR changes"), the dev's report names the AGENTS.md sentence as the maintainer's call, and the test header was named by nobody until here. Neither sentence claims every pull request carries the box, and both point at the ADR, which now states the limit. Not a FAIL: the governed record of record is now true, and the two sentences are destinations for a residual rather than a promise of a mechanism. Escalated as an alignment item in ③.
  5. main moved under the measured populations, and nothing landed in them. main is at 48fb558d36 (committed 2026-10-12T22:58Z), 17 commits past the merge-base 923504f4a, one past the 1fb1ced36 the previous record read. In the ten locale catalogues every line main added sits under detail (en +9, the other nine +6 each) and the one line it removed is console.pages — neither frozen nor product-led. scripts/check-doc-component-types.mjs moved +25/−21 inside DOC_TYPE_EXEMPTIONS prose only; deriveRegistryKeys, which the ratchet imports, is untouched. No file landed under app-shell/src/environment/ or app-shell/src/console/organizations/, none with a product-led path segment, and AGENTS.md, docs/adr/, the template path, the allowlist path and scripts/markdown-test-inputs.mjs are untouched on main. The live list therefore still equals the measurement on a merge with today's main. The pull_request runs on this head started at 2026-10-12T23:14Z, after main's last commit, on the merge ref, so the shard verdicts cover that merge. Right; the H7 concern (objectui#7611) has still not landed.
  6. The ratchet runs in the re-sharded matrix. main's 1fb1ced36 replaced vitest's hash split with scripts/test-shard-partition.mjs, whose header states that a file the timings dataset does not name "is weighed at the dataset's MEDIAN. That keeps the plan whole". The previous head's CI predates that commit; this head's run is the ratchet's first under it, and the twelve shards are its verdict. Right.
  7. Unchanged files, adopted from 6134399082 (its items 1–4 and 6–10) and not re-derived: the ratchet's membership in the root unit project and the always-on fast set; the 8 registration keys read in both spellings through deriveRegistryKeys, with the findings pin; the 28 files under a recursive freeze with test paths outside the file half; the 90 keys under 9 namespaces in all ten catalogues with no stale entry; __tests__ excluded from the app-shell build so nothing reaches dist; the two ledger rows in scripts/markdown-test-inputs.mjs; the two-list design with the ceiling in the governed ADR (the M5 proof). Re-read here: the template's one line equals the card's item 4 verbatim; 0059 is still the last ADR on main and docs/adr/ has no index file to update; the AGENTS.md section is +10 lines inside §1 and cites no issue number.
  8. Governed surfaces. AGENTS.md and docs/adr/** are Tier H in objectui's own register; the PR is a draft, with no approval from any seat; Governed Surface Queue Guard is the pull_request leg, which exits 0 by design. This record does not lift Tier H; the maintainer's approval is the landing act.

② Semver level

.changeset/12224-product-neutral-renderer.md carries empty frontmatter: no released package publishes anything. The rework touches only docs/adr/, which no package ships; the rest of the diff is the previous head's — two build-excluded files under packages/app-shell/src/__tests__/, one root script, governed prose, a template and the changeset. Changeset Declaration, Changeset Bump Policy, Changeset Claim Re-read and Changeset Overwrite Report are success on this head (Changeset Fixed Group Check reads success by conclusion; its status field is noted in the tally above). The level matches what the diff publishes: nothing.

Clause-②: no — no accept-set and no public API of any released package widens or narrows; the PR body's Clause-②: no line is right.

③ Boundary flags

The dev's rework report (6134447475): open_questions: [], out_of_scope_findings: [], and one flag in its prose — the AGENTS.md sentence left alone because the patch scope said nothing else changes. Its three checkable claims hold: the frozen-section sha256 (① 2), "no other file changed" (① 1), and "no merge of origin/main" (the compare between the heads has one parent chain). The flag is escalated as item 5 below.

The earlier dev report's flags (6125187793) were each answered or escalated in 6134399082; the rework re-opens none of them. "Full root pnpm test not run locally" is answered by this head's check-runs (above). The rider half landed as objectstack-ai/objectstack#23051 (6134379319) and is outside this PR.

What the maintainer must look at (Tier H: the approval is the landing act, and these ride with it). Items 1–4 and 6 are carried forward from 6134399082 unchanged and undecided here; item 5 replaces that record's item 5, which the rework answered:

  1. H3. Confirm the locale reading — a neutral new namespace passes, a product-led namespace or key fails, a frozen namespace takes no key — against the card's literal "gains a top-level namespace" clause.
  2. Freeze scope beyond the card: cloud-connection:panel with its app-shell: twin, console/cloud-connection/, console/diagnostics/CloudAiModelStatus.tsx, types/cloud.ts, types/zod/cloud.zod.ts, the whole environment namespace (the card said environment.entitlement), planTier, aiModelStatus.
  3. The ceiling lives in the ADR. Every addition to the list, and every new product word, frozen directory or frozen namespace, is an ADR amendment that needs your approval. Accept that cost, or ask for the rules to move out of the governed block.
  4. organizations and workspace are frozen but also read by neutral layout files (AppHeader.tsx, WorkspaceSwitcher.tsx, HomeLayout.tsx), while organization (singular) and chatbotQuota are left unfrozen on that same consumer logic. Decide whether that split is the one you want.
  5. Alignment after the rework. ADR-0060 now says the checkbox prompts web-form authors only and is not a gate. AGENTS.md's "left to review and the pull-request template's checkbox", the ratchet test's header ("the pull-request template's checkbox and review are what catch the rest") and the PR quick-read's 「只能靠评审和 PR 模板的勾选项」 still name the checkbox without that qualifier. Either ask for the one-line alignment in the same approval, or accept the ADR as the governing text and the other three as pointers to it.
  6. Product words are cloud and hotcrm only; any other product name passes the name gate until the block is amended.

Implemented-by: claude/issue-12224-product-neutral-renderer
Reviewed-by: session_012dGTuJen61ECNHXYXKChoC

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读(终稿)

PR #12253 · card #12224 · head 5fed8ccc46 · seat domain:skills#2 (session_012dGTuJen61ECNHXYXKChoC), written 2026-10-12T23:29Z. The seat checked the dev's draft against the diff it read; this comment is the version to read.

改了什么

  • objectui AGENTS.md 新增「Product-neutral renderer」一节(+10 行),共三条规则:
    • 产品用通用块拼页面;
    • 拼不出来时,在本仓补通用块,或在 @objectstack/spec 开契约门;
    • 现有的产品专属面只许减少。
  • 新增 ADR-0060,记录你的裁决原话、实测的冻结基线和 ratchet 的判定规则。
  • 新增一个 ratchet 测试,随 CI 跑。以下任一情况它会转红:
    • 新增产品命名的注册键(register('cloud:x') 和 namespace 写法都算);
    • 冻结目录里新增文件;
    • 冻结命名空间里新增 locale 键。
  • 新增一个只带一个勾选项的 PR 模板。
  • 另有 objectstack PR #23051,给分诊的立卡门加了一行:要产品专属 widget 的卡退回重立。它已由席位落地入队。

为什么改

  • 你的裁决「现在转向」「规则应该写入 objectui 仓库,要不然每个项目都会有页面定制的需求」。
  • 不写进仓库,每个产品都会继续往 objectui 塞专属 widget 和文案,每改一个字就要一个 PR 加一次 pin。

风险与代价(含回滚)

  • 不改运行时行为,不发布任何包(空 changeset)。新增的是一个只在 CI 里跑的测试。
  • 代价:今后新增产品专属面会被测试拦下。要加,只能修订 ADR-0060,而修订要你批准。
  • 回滚:revert 这一个 PR。
  • 契约复核两轮都是 PASS。第二轮之前做过一次返工:ADR 里「每个 PR 都带勾选项」这句不对(经 API 开的 PR 不带),已改成如实的说法。

席位意见

  • 建议批准。冻结清单是实测的,两种注册写法都能拦住,「清单只许删」靠受管 ADR 里的上限块机械强制。
  • 请你在批准时顺带确认以下 6 点(复核记录 6134585175 的第③节;不回复即按 PR 现状落地):
    1. locale 规则的读法:中性名字的新命名空间放行;产品词打头的命名空间或键拒;冻结命名空间不许加键。这个读法替代了卡上字面那句「新增顶层命名空间即失败」——按字面读,连通用块的文案也加不进来。
    2. 冻结范围比卡上多:cloud-connection:panel、两个 cloud 类型文件、整个 environment 命名空间,以及 planTier、aiModelStatus。
    3. 上限块放在 ADR 里,所以以后每加一项(包括新的产品词),都要你批一次 ADR 修订。接受这个成本,还是把规则移出受管块?
    4. organizations、workspace 冻结了,但中性的布局文件也在读它们;而 organization(单数)和 chatbotQuota 没冻结。这个划分是否就是你要的?
    5. 只有 ADR 写明了勾选项「仅网页开的 PR 才有、不是门禁」;AGENTS.md、测试头注释和 PR 正文提到勾选项时都没带这个限定。要不要一并对齐?
    6. 产品词目前只有 cloud 和 hotcrm,其他产品名要等修订 ADR 才会被拦。

你要做的


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review October 13, 2026 00:57
@os-zhuang
os-zhuang enabled auto-merge October 13, 2026 00:57
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 72 chunks) 3131.3 KB 3204.6 KB
Main entry chunk (gzip) 73.5 KB 350 KB
Entry file index-CJvpW5n9.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) 20.69KB 7.62KB
app-shell (runtime-config.js) 22.59KB 7.89KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 41.19KB 11.12KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.26KB 8.03KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (commentAuthorship.js) 1.39KB 0.78KB
collaboration (index.js) 1.93KB 0.85KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 588.51KB 141.71KB
core (index.js) 10.18KB 4.04KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 244.49KB 68.27KB
fields (index.js) 280.08KB 71.40KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 40.26KB 11.71KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 15.35KB 5.51KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.82KB 2.38KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 54.31KB 15.86KB
plugin-charts (index.js) 84.72KB 23.27KB
plugin-chatbot (index.js) 201.72KB 48.07KB
plugin-dashboard (index.js) 143.63KB 38.79KB
plugin-designer (index.js) 233.77KB 50.02KB
plugin-detail (index.js) 254.49KB 67.01KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 178.28KB 46.22KB
plugin-gantt (index.js) 178.63KB 45.19KB
plugin-grid (index.js) 255.09KB 70.91KB
plugin-kanban (index.js) 53.23KB 16.71KB
plugin-list (index.js) 123.35KB 31.54KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 45.65KB 12.81KB
plugin-timeline (index.js) 39.21KB 11.85KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 92.11KB 23.27KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 12.07KB 3.68KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.81KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.26KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.48KB 3.50KB
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-zhuang
os-zhuang added this pull request to the merge queue Oct 13, 2026
Merged via the queue into main with commit f228d71 Oct 13, 2026
33 of 34 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-12224-product-neutral-renderer branch October 13, 2026 01:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

configuration documentation Improvements or additions to documentation package: app-shell tests

Projects

None yet

3 participants