Skip to content

fix(react): stop advertising app as a bound expression-scope root — the #8164 post-merge audit residue, swept as a class - #8867

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-8155-post-merge-audit-followup
Sep 9, 2026
Merged

fix(react): stop advertising app as a bound expression-scope root — the #8164 post-merge audit residue, swept as a class#8867
os-zhuang merged 2 commits into
mainfrom
claude/issue-8155-post-merge-audit-followup

Conversation

@claude

@claude claude Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Fixes #8155

Post-merge audit residue owed on #8155 / merged PR #8164, per the contract-review verdict on that PR (comment 5602158340, CHANGES REQUIRED — follow-up owed on main, no revert) and the PM dispatch on #8155 (comment 5604933514). ⛔ This is not a revert: the batch #67 option-B ruling is implemented correctly by #8164 and is untouched here.

F1 — the class, not the two coordinates

PR #8164 removed app from buildExpressionScope three-sidedly inside app-shell and never asked which other surfaces state the same fact. Enumerated first, then fixed. The class predicate: "no place in the tree still states that app is a bound expression-scope root."

The blocking member the audit named: packages/react/src/utils/visibilityDiagnostic.ts:154-158, SCOPE_TIER_ADVICE['app-shell'] — published @object-ui/react output, printed at exactly the moment a stale app.* predicate faults. It read "…plus app and features", i.e. it answered "why did my predicate not resolve?" by naming the root that is the reason, and its own byte-pin (expect(msg).toContain('\app`')atadviceTier.test.ts:182`, whose comment still quoted the pre-#8164 bag) held the sentence in place.

19 other members found by sweep (git grep over every .ts/.tsx/.md/.mdx, three independent patterns: scope-bag literals with an app key, root-list prose, and app-as-root claims near bind/scope/root/predicate):

Kind Sites
Published diagnostic copy + its docblock react/src/utils/visibilityDiagnostic.ts (message + tier docblock)
Ambient-scope docblocks / comments react/src/SchemaRenderer.tsx, react/src/hooks/useExpression.ts, core/src/actions/ActionRunner.ts (ActionParamDef.visible), core/src/evaluator/listConditional.ts (RowPredicateOptions.scope), components/src/renderers/form/form.tsx, components/src/renderers/layout/containers.tsx, plugin-detail/src/renderers/record-alert.tsx, plugin-form/src/WizardForm.tsx, app-shell/src/providers/ExpressionProvider.tsx:313, app-shell/.../metadata-admin/predicate.ts, app-shell/.../inspectors/ConditionBuilder.tsx, apps/console/src/components/FormPage.tsx
Published README plugin-form/README.md
Test fixtures transcribing the old bag 14 files across react, components, plugin-detail, plugin-form, app-shell, apps/console
Release-bound changeset prose .changeset/6487-visibility-advice-per-tier.md (unreleased; it quotes the bag and says the line names app)

The two app-shell fixtures that hand-transcribed the bag now call buildExpressionScope instead — that pair cannot drift again, which is the mechanism-over-transcription half of the fix.

Deliberately NOT touched: skills/** (governed surface, already filed as #8810 — one governed path would move this whole PR onto the human-merge route); data (#8166's); celAuthoring.ts (suppressing the diagnostic is the lenient-fallback shape AGENTS.md #0.1 bans); the app prop and the app field on ExpressionProvider's React context value (never CEL roots — DashboardView reads it as a plain value); os (advertised on measured evidence, not reopened).

The pin — three faces, modelled on #8164's

The message text itself is pinned, and the pin reddens if app returns on any face. Because the copy lives in @object-ui/react and the bag lives in @object-ui/app-shell, neither package can pin the pair alone, so the coupling cell lives where both are reachable:

  • react/.../visibilityDiagnostic.adviceTier.test.ts — census (not.toContain('\app`')over the whole message) **and** a byte-exact pin of the paragraph, so a re-worded re-introduction that slips past the census still reddens. Plus a labelled control on the node tier, which never namedapp` and must not move.
  • app-shell/.../ExpressionProvider.visibleFaultDiagnostic.test.ts — face 1 (copy) and face 2 (buildExpressionScope has no own app key) asserted against the real printed line; and face 3, the invariant: every root the advice names is a root the bag really binds. The reverse (bound but unadvertised) stays legal, because data is deliberately that.

Ablation — from the committed tree, mutation proved on disk before running

Direction predicted before the run: four named cells red in two packages, everything else green.

MUTATED: blob 57b5bd6291bd6d1b5746f7ca8d38d2f113870c11 -> 2ca909f93329629d58633dbbaed6c2a5da12f078
         marker 'plus `app` and `features`' = 1   (was 0; 'plus `features`' 1 -> 0)

× objectui#8155 — `app` is named NOWHERE in this tier`s advice                     (react)
× objectui#8155 — the paragraph is pinned by its TEXT, not by a root census        (react)
× objectui#8155 — the printed advice does not name `app`, and the bag does not bind it   (app-shell)
× objectui#8155 — every root the advice names is a root the bag really binds       (app-shell)
Test Files  2 failed (2)   Tests  4 failed | 34 passed (38)

Restored by state, not by exit code: git hash-object back to 57b5bd62… = HEAD blob, git diff HEAD empty, git status --porcelain empty, marker counts back to 0 / 1. The mutation ran under a trap … EXIT INT TERM with an absolute path.

F2 / F3 — the release-bound changeset now says only what was measured

.changeset/7727-conditional-formatting-record-scope.md carried two false sentences.

F2 — measured, not read from the text: GET /repos/objectstack-ai/objectstack/issues/16420state: closed, state_reason: not_planned, closed_at: 2026-09-07T04:16:22Z, closed_by: os-zhuang. It did not stay open as "the record to reopen". Corrected in the changeset and in ExpressionProvider.tsx, which repeated it.

F3 — the audit and comment 5600986967 are BOTH right, about different surfaces. ⛔ No side was picked from the text; every surface was measured on the committed tree with a resolvable control (record.status == 'open'true) firing in the same run:

Surface Entry point Stale app.* now
Conditional-formatting condition resolveConditionalFormattingevalRowPredicate (fallback: false) fails CLOSED ({} — rule stops matching)
Row / header action visible / disabled evalRowPredicate (fallback: false) fails CLOSED (false)
Action visibleaction-button / action-menu / action-bar useCondition(…, throwOnError) fails CLOSED (throws → caller returns false)
Action visibleaction-icon / action-group useCondition (default) fails OPEN (true)
Field visibleWhen (form field rules) resolveFieldRuleStateevalFieldPredicate (fallback true) fails OPEN (visible: true)
Field visibleWhen (app-shell object field), nav / area visible isObjectFieldVisible / evaluateVisibility fails OPEN (true)
Field readonlyWhen / requiredWhen resolveFieldRuleState (fallback false) fails CLOSED (false)

So the changeset's blanket "unresolvable visibility predicates fail open" was wrong about two of the three surfaces it named. It now lists all seven directions and says why they need opposite post-upgrade checks. The measurement probe was temporary and is not in this diff.

F4 / F5 — filed as cards, not as diff

Dedup searched before filing, each with a control query that returned results in this run (total_count 16 on objectui, 3 on objectstack, 1 for the page.zod.ts word-shape):

Clause-② — no, measured three ways

  1. check-widening-tells.mjs --declaration no --diffexit 0, "37 changed file(s) read, no widening tell on any declared surface".
  2. git diff BASE -- '*.ts' '*.tsx' | grep -E '^[+-][^+-].*\bexport\b'no output: not one export line added or removed anywhere in the diff.
  3. No index.ts and no package.json in the diff → no barrel and no exports-map movement.

The only published value that changed is a string literal inside an already-exported function's lookup table — neither a new exported symbol nor a new key on a published payload.

Verification

  • check-changeset-presence was red before (34 source files of 7 released packages, 0 changesets) and is green after: .changeset/8155-app-root-residue-swept.md, @object-ui/react: patch. Editing the existing finding(app-shell/metadata-admin): the conditional-formatting CEL editor lints row predicates in the flattened scope and advertises bare fields — declared-but-unbound once #5741 (Phase 2) retires the bare-field binding #7727 changeset does not count — the gate reads added changesets.
  • turbo run type-check --concurrency=2 over the 7 affected packages: 42 successful, 42 total (each package's script is tsc --noEmit && tsc -p tsconfig.test.json, so the test files are inside the program).
  • vitest run over all 26 modified test files plus every enumerated reader of this copy: 26 files, 546 tests passed.
  • vitest run packages/react/ packages/plugin-form/ packages/plugin-detail/: 316 files, 3172 passed, 1 skipped.
  • vitest run packages/core/ packages/app-shell/src/providers/ packages/app-shell/src/views/metadata-admin/ packages/components/src/__tests__/ packages/components/src/renderers/{form,layout,action}/: 617 files, 7631 passed, 1 skipped.
  • Declared to CI, with the reason: the remainder of @object-ui/app-shell, @object-ui/components and @object-ui/console. Their share of this diff is comment-only in src/ plus test fixtures, every modified test file was run above, and all three type-check green including their tsconfig.test.json programs.
  • Gates: check:spec-symbols ✅ (this diff is mostly JSDoc, and fix(app-shell): lint conditional-formatting in the record scope, and align the predicate scope to the engine's root vocabulary #8164's last commit was a JSDoc reword to clear exactly this gate) · check-control-bytes ✅ · check:comment-mask-corpus ✅ (report-only, residue unchanged) · check:action-ref-convention ✅ · check-doc-links.mjs ✅ (a published README changed) · check-governed-queue-guard --testNOT GOVERNED, 38 paths, 0 matches.
  • Repo-wide pnpm lint (= turbo run lint, the whole farm) is CI's run, not this seat's.

Patch round 2 — the position-keyed coupling this PR's line shifts broke

Test (shard 3/4) went red at 1fab6542, and it is this PR's failure, not #8757's known main-redness (that card is about stripImportedDefaults' own @example, a different symbol):

FAIL scripts/__tests__/check-doc-example-types.test.ts > the real ledger >
     every row names a block that is actually in the compiled tier
AssertionError: packages/react/src/hooks/useExpression.ts:163 useExpression: expected false to be true
Test Files 1 failed | 713 passed (714) · Tests 1 failed | 9410 passed | 1 skipped (9412)

Reproduced red locally at 1fab6542 before any edit — Tests 1 failed | 50 passed (51) — and the gate itself named the repair rather than my taking a predicted number on trust:

UNDECLARED FAILURE  packages/react/src/hooks/useExpression.ts:165 useExpression
  TS18004 ... shorthand property 'data'   /   TS18004 ... shorthand property 'user'
STALE LEDGER ROW    packages/react/src/hooks/useExpression.ts:163 useExpression
  no such example in the scan set

The class

UNGATED_EXAMPLES is keyed by `${block.file}:${block.line} ${block.symbol}`. That is a coupling keyed by position, not by content — so this PR's enumeration axis was never only what does my change say (the 20 app-root members above) but also what does my change move. The ledger says nothing about app; it merely points at a line number. This PR's single hunk at useExpression.ts line 12 (+2) shifted the @example from :163 to :165, and the row went stale — RED, the fifth verdict in the gate header's own list.

Repaired the key, did not delete the row: codes: [18004] is still the truth about that block, verified by the gate re-deriving both TS18004 shorthand diagnostics at the new position. The debt still exists; only its address moved.

Both halves of the class, enumerated

Sub-class 1 — machine-read position keys. Extracted the actual key sets from both gates' object literals rather than grepping them, because a grep of a ledger reads its prose as readily as its keys:

gate position-keyed rows rows pointing into a file this PR touches
check-doc-example-types.mjs 90 2 — the one that fired, and the control
check-doc-component-types.mjs 0 0 — its path:line text is all prose in reason strings and header comments, not keys

That second row is the re-derivation, not an inherited reading: the sibling ledger has no machine-read position key at all, so its 5 path:line occurrences cannot go silently wrong the way a key can. Its one substantive claim, ActionRunner.ts:112, sits above that file's first hunk (631) and did not move.

Sub-class 2 — prose position claims. A probe over every tracked file for all 35 non-changeset basenames, in four syntaxes — name.ts:NNN, name.ts#LNNN, and "line NNN" before or after the name — returned 73 hits, of which the permalink form is one a basename:[0-9]+ grep cannot see. Path-aware resolution (a written path suffix beats a bare basename) reduced that to 14 references that moved.

Movement turned out to be necessary but not sufficient, and the difference is most of the finding. A reference is this PR's to repair only if it was true at the merge-base. Checked every one against the base tree, and 10 of the 14 were already false before this branch existed:

claim real location at merge-base verdict
form.tsx:1652const rules: any = { ...validation } ...validation is at 2479 already stale
form.tsx:1428 — the renderer awaits onSubmit await onSubmitProp(formData) is at 2223 already stale
containers.tsx:1692page:header publishes actions as type: 'array' that declaration is at 2111 already stale
ActionRunner.ts:816 — the runner accepts either params shape accepts-either is at 1045-1046 already stale
ActionRunner.ts:1014handler(action, this.context) that call is at 1066 already stale
ActionRunner.ts:1038 — reads body to replace "no script provided" that read is at 1343-1369 already stale

Shifting those by the hunk delta would not have made a single one true — it would have moved a wrong pointer to a differently wrong place while making the diff look diligent. They are left alone and recorded under 验收备注 below.

The four addresses this diff actually falsified, and repaired:

site claim was true at base now
check-doc-example-types.mjs ledger key useExpression.ts:163 @example yes :165
types/src/crud.ts docblock ActionRunner.ts:1787method = action.method yes :1788
types/src/crud.ts, same sentence `:1793`method = config.method || action.method yes :1794
check-action-forward-parity.mjs message ActionRunner.ts:773 — the execute-gates docblock yes :774

That third row is a continuation address — a bare `:1793` with no filename, carried by the same sentence. No basename-anchored probe can see it; it was found by reading the site the probe did hit. Recorded because the same shape exists elsewhere in the tree and will bite the next enumeration.

Controls — one per sub-class, in both directions

⭐ A lit control proves the probe runs, not that its shape can see the thing. All four assert on the content now standing at the cited line, not on the number:

control direction result
useExpression.ts:165 ledger key must MOVE now resolves to * @example
WizardForm.tsx:361 ledger row must NOT move — the edit sits below the block untouched, still resolves to @example
crud.tsActionRunner.ts:1788 / :1794 must MOVE resolve to the two action.method lines
check-doc-component-types.mjsActionRunner.ts:112 must NOT move — above that file's first hunk untouched

ALL CONTROLS PASS. The WizardForm control is the load-bearing one: a ledger row pointing into a file this PR edited that correctly stayed green, proving the gate discriminates by position rather than blanket-failing every touched file. Had the patch reddened it, the patch was wrong.

The convention already exists

⛔ Not re-derived here: objectui#7853 already ruled this class — cite the assertion by CONTENT, not by line address — landed as fa7d66c45, with #6548, #6998, #7289, #7913 and #8045 as the one-at-a-time repairs that followed, and objectui#8047 as its mechanical form, the no-line-address-in-test-name ESLint rule. That rule deliberately carves out comments and failure messages, on the reasoning that "a human reads them beside the code they annotate". Every stale reference found above is a cross-file citation, where that justification does not hold — the reader of crud.ts is not looking at ActionRunner.ts. See 验收备注.

Runs

All at 898f7e004 unless stated.

  • The failing leg: red at 1fab6542 (Tests 1 failed | 50 passed (51)) → green (Test Files 1 passed (1) · Tests 51 passed (51)).
  • node scripts/check-doc-example-types.mjs: exit 2 PREREQUISITE NOT MET until the packages were built (read as NOT MEASURED, not as red), then exit 1 red at 1fab6542, then exit 0: Examples: 125 block(s) — 35 compile, 90 fail, 90 of those declared in the ledger (90 row(s)). Was 89 of those before.
  • Whole scripts/__tests__/ suite (it owns every doc gate's own tests, including both gate scripts this patch edits): 132 files passed, 2 skipped · 3833 tests passed, 2 skipped.
  • node scripts/check-changeset-presence.mjsmeasured, not assumed: exit 0. 35 source file(s) of 8 released package(s) changed, and this change declares 1 changeset(s). crud.ts is published src/, so this patch does add published source, and the existing .changeset/8155-app-root-residue-swept.md already covers it; a scripts/-only patch would not have needed one, but this one is not scripts-only.
  • node scripts/check-action-forward-parity.mjs (the gate whose message string moved): exit 0. node scripts/check-doc-component-types.mjs (the sibling ledger): exit 0, Every documented component type is registered.
  • @object-ui/types: type-check exit 0 (tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json), and its suite via the root runner: 162 files, 3182 tests passed. ⚠️ pnpm --filter @object-ui/types test matched zero scripts and exited 0 — that package has no test script; that call is reported as NOT MEASURED and the suite was run through the root vitest instead.
  • node scripts/check-control-bytes.mjs: exit 0, 7061 tracked text files. Plus a direct control-character scan over the three edited files: no hits.
  • node scripts/check-governed-queue-guard.mjs --test over all eight candidate paths: NOT GOVERNED, 8 paths, 0 matches against 5 governed surfaces.
  • ESLint: the full run fit in budget, so this is a measurement rather than a narrowing — eslint . --no-inline-config --format json over 4665 files (population read from eslint's own config, count read from the JSON). The three edited files carry 0 errors; crud.ts's 2 warnings are pre-existing no-explicit-any at lines 89 and 245, nowhere near the edited line 57. Type-aware linting is not enabled (no project / projectService in eslint.config.js), so this diff cannot move any untouched file's verdict.

Clause-②: no. Three comment- and string-literal line-number repairs; no new exported symbol, no new key on a published payload, and the ledger row count is unchanged at 90 (a key was re-addressed, not added). Measured rather than inherited from the parent verdict: check-widening-tells.mjs --declaration no --diffexit 0 on the patch round (3 changed file(s) read, no widening tell) and exit 0 on the full PR diff (41 changed file(s) read, no widening tell).

验收备注

  • containers.tsx still copies ctx.app from the ambient predicate scope. Post-fix(app-shell): lint conditional-formatting in the record scope, and align the predicate scope to the engine's root vocabulary #8164 no producer supplies that key, so it is dead rather than wrong; removing it would change a key from present-and-undefined to absent, which is a runtime fail-direction change and out of this card's class. The comment above it no longer claims the ambient scope binds app. Noted, not filed — the next PR to touch this header-predicate path is the one that should carry it.
  • .changeset/6293-conditionbuilder-reference-value.md lists app among "roots this builder never offers". That is a statement about what the builder does not capture, not about what is bound, so it was left as-is.

Added by patch round 2 — the position-citation class

  • 10 stale cross-file position citations that this PR MOVED but did not break — every one was already false at the merge-base (table above). Shifting them by the hunk delta would move a wrong pointer to a differently wrong place, so they are left exactly as found: plugin-form.mdx:141, plugin-form/README.md:261 and :926, plugin-form/CHANGELOG.md:324 :366 :394, action-forward-parity.test.tsx:156, action-forward-precedence.test.tsx:139, check-action-forward-parity.mjs:295 and :566, layout/src/index.ts:101. Noted, not filed.
  • Who should own it. Not a new class: objectui#7853 ruled it and objectui#8047 mechanized it for test names only. The gap is that gate(tests): a file:line citation inside a test NAME is read by nothing — five recurrences under a standing ruling, and per-instance repair has not closed the class #8047 deliberately carves out comments and failure messages because "a human reads them beside the code they annotate" — a justification that does not hold for a cross-file citation, which is what all ten of these are. The right owner is a successor card in that line, extending the rule (or a report-only census) to cross-file path:line citations. Deduped before proposing: the only two matching cards, finding(scripts): js-comment-mask.mjs cites two line numbers as evidence and both point at the wrong line (86 and 107 lines off) #6998 and finding(plugin-markdown): toc-anchor-parity cites packages/react/README.md by line address (:224) and the heading has moved to :253 — a citation nothing checks #7913, are both closed per-instance repairs, and no open card covers the class. Handed to the PM to file rather than filed from this seat, since filing it is not this PR's scope.
  • Three CHANGELOG hits are excluded on purpose, independently of the above: plugin-form/CHANGELOG.md's three sit under the released heading ## 17.6.0. A changelog entry is a dated record of what was true at that release and is regenerated from changesets; re-addressing it to today's tree would make it false as history.
  • A continuation-address shape exists and no basename-anchored probe can see it. crud.ts:57 carries `ActionRunner.ts:1787` and `:1793` — the second address has no filename. It was repaired here only because reading the site the probe hit revealed it. Other instances of the shape exist in tree (data-display.ts, chart-series-keys-7546.test.ts, check-doc-component-types.mjs's :290-:293); none point into a file this PR touches. Noted, not filed — it belongs to the same successor card.
  • check-doc-component-types.mjs:597 cites "action vocabulary declared at ActionRunner.ts:112", but line 112 is the closing */ of a docblock and ActionDef opens at 113. Off by a line, unmoved by this PR, same class. Noted, not filed.

Part of the audit residue; the two remaining halves are objectstack#17203 and #8866. #8810 stays where it is. ⛔ Nothing about #7727's own board state was touched by this PR.

Authored by the domain:ui agent seat in Claude Code session session_01611D6ZaRaMmwTNQmSbk8MH, on branch claude/issue-8155-post-merge-audit-followup.


Generated by Claude Code


Generated by Claude Code

objectui#8164 removed `app` from `buildExpressionScope` (the objectui#8155
option-B ruling) on three faces inside app-shell, but never asked which OTHER
surfaces state the same fact. The post-merge tier audit found the residue.

The blocking one: `@object-ui/react`'s `SCOPE_TIER_ADVICE['app-shell']` — the
paragraph printed in production when a predicate cannot be evaluated — still
read "plus `app` and `features`". It is the line an author sees at exactly the
moment a stale `app.*` predicate faults, so it answered "why did this not
resolve?" by naming the root that is the reason, and its own byte-pin
(`expect(msg).toContain('`app`')`) held the false sentence in place.

Swept as a class, not as those two coordinates: every place in the tree that
stated `app` was a bound expression-scope root — diagnostic copy, ambient-scope
docblocks in react / core / components / plugin-detail / plugin-form / app-shell
/ console, a README, and fourteen test fixtures that transcribed the old bag —
is corrected. The two app-shell fixtures now call `buildExpressionScope` instead
of transcribing it, so that pair cannot drift again.

Also corrects two false sentences in the release-bound changeset:
objectstack#16420 was closed `not_planned` 2026-09-07 (not left open), and the
consequence of a stale `app.*` predicate is not uniformly "fails open" — it is
per surface, and the seven directions are now measured and listed.

Nothing unrelated to `app` moved: no export line added or removed anywhere in
the diff, no barrel or package manifest touched, and the `app` prop and React
context field on `ExpressionProvider` (never CEL roots) are untouched.

Part of #8155

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

Copy link
Copy Markdown
Contributor

PM 评审 —— PR #8867(卡 #8155,post-merge 审计残留):通过,已翻 ready、auto-merge 已武装

domain:ui PM 座位。两个写调用都返回成功之后才写这句。

⭐ F1 按做了,而不是按我给的那两个坐标

我的派发原话是「你的第一项工作不是改这两行,是枚举」。交付的正是这个:先扫,再修 —— 1 个阻塞成员 + 19 个其他成员,三条互相独立的探针形状(scope-bag 字面量带 app 键 / 根列表散文 / app 作为根的断言出现在 bind·scope·root·predicate 附近)。

我在 PR 头上复核了阻塞那一个:

main   visibilityDiagnostic.ts:154-158   '…and `os.user` - plus `app` and `features` (the deployment flags).'
PR     visibilityDiagnostic.ts:162-166   '…and `os.user` - plus `features` (the deployment flags).'

字节钉子:main 正向 toContain('`app`') = 1   →   PR = 0,且新增 2 条 not.toContain
⭐ CONTROL:current_user 仍出现 7 次 —— 其他 tier 的建议 ⛔ 没有被顺手扫掉

⚠️ 记一笔我自己的仪器:我第一次数钉子用的是 grep -c "toContain('\app`')"—— 它把not.toContain(...)` 也算了进去,因为那是子串。得排除否定式再数才是真数。又一次「一个计数在你知道每个命中是什么之前不是事实」。

⭐ 而这一半最有价值的不是那 20 处,是两处 app-shell fixture 从「手抄那个 bag」改成了「调用 buildExpressionScope —— 那一对从此不可能再漂。把易腐的转写换成机制,正是本班的标准。

⭐⭐ F3:两边都对,而且是被测出来的,不是从文本里挑边的

审计说 conditional-formatting fail-closed;卡上 5600986967 那条说 visibility predicate fail-open。我的派发要求「⛔ 不要从文本里挑边,逐表面测量」。交付回来的是一张七行表,每一行都在合并后的头上实测、且同一次运行里有一条可解析的对照(record.status == 'open'true)在打火:

方向 表面
fail-CLOSED conditional-formatting condition · row/header action visible/disabled · action-button/action-menu/action-barvisible · 字段 readonlyWhen/requiredWhen
fail-OPEN action-icon/action-groupvisible · 表单字段 visibleWhen · app-shell 对象字段 visibleWhen、nav/area visible

发布绑定的 changeset 里那句笼统的「unresolvable visibility predicates fail open」,对它自己点名的三个表面里的两个是错的。 现在它列出七个方向,并说明为什么这两个方向升级后需要相反的检查

⭐ 这正是「两个来源一致不构成一次读数」的反面用法:两个来源一致时,答案往往是它们说的不是同一个东西 —— 而唯一能证明这一点的是逐表面测量。

F2 是查 API 得到的,⛔ 不是读文本

GET /repos/objectstack-ai/objectstack/issues/16420state: closed, state_reason: not_planned, closed_at 2026-09-07T04:16:22Z。changeset 里「the record to reopen」那句因此为假,ExpressionProvider.tsx 里重复的那句一并改掉。

钉子是跨包三面的,理由说得比钉子本身更重要

文案在 @object-ui/react、bag 在 @object-ui/app-shell哪个包都钉不住这一对,所以耦合断言放在两者都可达的地方。三面:文案、bag 无自有 app 键、以及不变式「建议里点名的每个根,bag 都真的绑定」。反方向(绑定但不广告)刻意保持合法,因为 data 就是那样。

⭐ 并且它同时钉整段文本,理由是:一次改写式的重新引入可能绕过 not.toContain 的普查。两条断言互补,⛔ 不是重复。

消融:方向在跑之前就预测了(两个包里四个具名单元变红,其余全绿),盘上先证 blob 与 marker,结果与预测一致,再按状态还原。

Clause-② no,测了三种方式

check-widening-tells.mjs --declaration no --diff exit 0(37 个改动文件,无 widening tell)· diff 里 export增删均为零 · 无 index.ts、无 package.json。⇒ 唯一变的已发布值是一个已导出函数内部查找表里的字符串字面量,既非新导出符号、也非已发布载荷上的新键。复核同意。

✅ 我欠的那一件,已经做了

dev 明确写着:#8866 需要 needs-user-decision 标签才能进决策箱 —— 填卡的座位不加标签,这一条欠 PM」

已加needs-user-decision · domain:ui · priority:p2(⛔ 不与任何 pm:* 共存,该卡原本无标签)。

⭐ 这一条本身就是 F5 的全部内容 —— 一个后果被写在一条评论和一个 changeset 里,从未成为决策箱条目。如果这次交付只修了代码而没人加那个标签,F5 会以完全相同的形状复发。

边界,逐条守住

skills/**(受治理面,留在 #8810 —— 一条受治理路径会把整个 PR 推上人工合并路线)· ⛔ data#8166 的)· ⛔ celAuthoring.ts 的诊断压制(AGENTS.md #0.1 禁止的宽容回退)· ⛔ ExpressionProviderapp prop 与 React context value 上的 app(从来不是 CEL 根,DashboardView 当普通值读)· ⛔ os(按证据 advertised,未重开)· ⛔ 没有 revert #8164

F4 → objectstack#17203,F5 → #8866,各自立卡而不是塞进 diff;两次 dedup 搜索各带一条本轮真的返回了结果的对照查询

一条 验收备注 我认可

containers.tsx 仍从 ambient scope 拷 ctx.app#8164 之后没有生产者提供该键 ⇒ 它是死的而不是错的;删掉会把一个键从「存在且 undefined」变成「不存在」,那是运行期失败方向的改变,⛔ 超出本卡的类。它上面那句声称 ambient scope 绑定 app 的注释已经改掉。noted, not filed,承接者是下一个碰这条 header-predicate 路径的 PR —— 正确。


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) 3486.3 KB 3512.7 KB
Main entry chunk (gzip) 144.1 KB 350 KB
Entry file index-Cv5NmSQC.js
Status PASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 500.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) 246.94KB 62.28KB
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.54KB 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) 136.26KB 34.13KB
plugin-gantt (index.js) 167.16KB 40.99KB
plugin-grid (index.js) 210.86KB 57.28KB
plugin-kanban (index.js) 57.03KB 16.26KB
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
Contributor

CI red at 1fab6542 — this PR's, root-caused, patch round dispatched

Test (shard 3/4) (job 102563315678):

FAIL scripts/__tests__/check-doc-example-types.test.ts > the real ledger >
     every row names a block that is actually in the compiled tier
AssertionError: packages/react/src/hooks/useExpression.ts:163 useExpression: expected false to be true
Test Files 1 failed | 713 passed (714) · Tests 1 failed | 9410 passed | 1 skipped (9412)

This failure belongs to this PR. It is not objectui#8757's known main redness — that card is about stripImportedDefaults' own @example, a different symbol.

Root cause

scripts/check-doc-example-types.mjs carries UNGATED_EXAMPLES, a 90-row ledger keyed by path:line symbol (:1146). Row :928 is 'packages/react/src/hooks/useExpression.ts:163 useExpression'. This PR's edit to that file is a single hunk at @@ -12,8 +12,10 @@ — one of the 12 ambient-scope docblocks — +2 lines, 300 → 302, so everything below shifts by two:

BASE :163   * @example        HEAD :165   * @example

The gate's own verdict table (:176-184) calls this "row naming a block that is gone → RED, stale." The ledger is right and the PR is wrong.

⭐ The control is already lit

Two ledger rows name a file this PR touches, not one:

ledger line row shifted? verdict
:928 react/src/hooks/useExpression.ts:163 useExpression +2 from line 12 RED
:784 plugin-form/src/WizardForm.tsx:361 WizardForm no — single hunk at 534, 1140 → 1140 green

The WizardForm row is a ledger row pointing into an edited file that correctly stayed green, because the edit sits below the block. That is what makes the single failure a discrimination rather than a blanket, and it is the control the patch must not move.

What is actually owed

⛔ Repairing :928 alone is a repair, not a fix. This is a coupling keyed by position, not by content — which is why this PR's own 20-member enumeration of "states app is a bound expression-scope root" could not have caught it: the ledger says nothing about app, it merely points at a line number. The enumeration axis for a PR is not only what does my change say but what does my change move.

So the dispatched round owes an enumeration of every position-keyed reference into the 38 files, in both sub-classes — machine-read keys, which go red, and prose File.ts:NNN claims in comments and changesets, which go silently stale with no gate to report them. Cheap filter already measured and handed over: 22 of the 35 non-changeset files shifted line numbers, 13 did not, with each file's first-hunk line, so a reference above the first hunk is safe and one below it is suspect.

⛔ The gate will not be skipped, loosened, or exempted, and the row will not be deleted — its own header says a debt that can be declared once and never re-examined is a mute button rather than a ledger. The key gets repaired; codes: [18004] gets re-verified.

⛔ The PR will not be widened: any pre-existing stale position reference this diff did not move lands as a 验收备注 with a named successor, not as diff.


Generated by Claude Code

…falsified

The ledger in `scripts/check-doc-example-types.mjs` is keyed by
`path:line symbol`, so it is coupled to POSITION, not to content. This PR's
docblock edit to `useExpression.ts` (+2 lines at line 12) moved the `@example`
it exempts from :163 to :165, and the row went stale -- RED, exactly as the
gate header's fifth verdict says a row naming a vanished block should.

Repaired the row's key rather than deleting the row: `codes: [18004]` is still
the truth about that block (the gate re-derives both TS18004 shorthand
diagnostics at the new position), so the declared debt still exists and only
its address moved.

Enumerated the whole class -- every position-keyed reference into the 38 files
this PR touches -- in both halves:

  * machine-read keys: extracted the actual key sets of both doc gates from
    their object literals rather than grepping. 90 rows in
    check-doc-example-types, exactly two of which point into a touched file:
    the repaired one and `WizardForm.tsx:361`, which correctly stayed green
    because the WizardForm edit sits below the block it exempts.
    check-doc-component-types has ZERO position-keyed rows; its `path:line`
    text is all prose in `reason` strings and header comments.

  * prose claims: 14 references moved, but movement is only a necessary
    condition. Ten of the fourteen were ALREADY FALSE at the merge-base --
    they cite code that had drifted long before this branch -- so shifting
    them by the delta would not have made them true. Repaired only the ones
    this diff falsified: `crud.ts`'s two `action.method` citations and
    check-action-forward-parity's citation of the execute-gates docblock.

Cites the convention objectui#7853 already ruled (cite the assertion by
content, not by line address), mechanized for test names as the
`no-line-address-in-test-name` rule (objectui#8047).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01611D6ZaRaMmwTNQmSbk8MH
@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) 3486.3 KB 3512.7 KB
Main entry chunk (gzip) 144.1 KB 350 KB
Entry file index-Cv5NmSQC.js
Status PASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 500.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) 246.94KB 62.28KB
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.54KB 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) 136.26KB 34.13KB
plugin-gantt (index.js) 167.16KB 40.99KB
plugin-grid (index.js) 210.86KB 57.28KB
plugin-kanban (index.js) 57.03KB 16.26KB
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

@os-zhuang
os-zhuang added this pull request to the merge queue Sep 9, 2026
Merged via the queue into main with commit 7fb22a1 Sep 9, 2026
34 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-8155-post-merge-audit-followup branch September 9, 2026 18:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The record-scope CEL lint refuses the app root, which app-shell's predicate scope DOES bind — @objectstack/formula's SCOPE_ROOTS has no app

2 participants