Skip to content

fix(app-shell): lint conditional-formatting conditions in the record scope - #8164

Draft
os-justin wants to merge 3 commits into
mainfrom
claude/issue-7727-conditional-formatting-cel-scope
Draft

fix(app-shell): lint conditional-formatting conditions in the record scope#8164
os-justin wants to merge 3 commits into
mainfrom
claude/issue-7727-conditional-formatting-cel-scope

Conversation

@os-justin

@os-justin os-justin commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator

Part of #7727

⛔ Deliberately not a closing keyword. All six of triage's deliverables for #7727 are done here, but the PM review asked that the card not be shut as fully done, and the ship decision is parked on a maintainer ruling in #8155 — a keyword close would take the card out of the open-card filters while that is still being decided. What is left of #7727's family after this PR: the data.* half of the retirement (#8166) and the ConditionBuilder mounts (#8167). Leave #7727 open; a human can shut it once those are triaged.

⛔ DRAFT, and it carries needs:contract-review. Clause-2 applies on its accept/reject leg: the editor's lint verdict flips for a whole class of input — a bare field ref that lints clean today becomes a blocking ERROR. It does not apply on a published-surface leg, and an earlier revision of this description said it did. Measured: packages/app-shell/src/index.ts has 0 export * lines and names neither ROW_PREDICATE_ROOTS nor ConditionalFormattingEditor; the package exports map is "." plus ./styles.css with no deep subpath. ROW_PREDICATE_ROOTS is an export const that nothing outside the package can import, so @object-ui/app-shell's published face is unchanged. The changeset now says so too.

Contract review returned PASS-WITH-FINDINGS; the commits after the first answer it. No implementation logic changed — ConditionalFormattingEditor.tsx is byte-identical across all three commits (blob 5caa9119). What changed is coverage and text, and the two corrections are worth reading before the diff.

Correction 1 — this editor was NOT "the last place still teaching the retired spelling"

The changeset said so and the claim was false. #7727, its triage and my own sweep all searched the explicit spelling scope="flattened". CelPredicateField also reaches the flattened lint by callers passing no scope prop at all (celAuthoring.ts reads hint.scope ?? 'flattened'). Every mount in the repo, measured:

mount scope= in that file scope in effect
ConditionalFormattingEditor.tsx:341 1 record — this PR
inspectors/ObjectFieldInspector.tsx:760, :874, :888, :902 4 record
PermissionAdvancedFacets.tsx:356, :372 0 flattened — CORRECT, RLS is not a row surface (#5738 stand-down 3)
inspectors/ConditionBuilder.tsx:368 0 flattened — and one of its callers is a row surface

ConditionBuilder has no scope on its own props either, so no caller can override it. ActionDefaultInspector.tsx:671-672 mounts it for an action's visible / disabled, which rowPredicateCanon.ts:16-18 names as a row predicate in as many words, and useExpression.ts:153-158 binds as { record } only. So a bare status == 'done' in an action guard lints clean and never matches: this card's exact defect, untouched here.

Filed as #8167 with all six ConditionBuilder callers classified — two defects, one likely (ObjectValidationsPanel, whose sibling validator already runs scope: 'record' at clientValidation.ts:792), one correct as-is (the flow tier), three needing a tier verdict. ⛔ Not folded in: the fix is not "flip ConditionBuilder", it is "make the scope a prop and rule on three surfaces".

Correction 2 — the fix leaves half the retirement open, and now says so

At scope:'record' the engine still accepts data.status == 'x': @objectstack/formula's SCOPE_ROOTS lists data, so the bare-reference check waves it through. rowPredicateCanon.ts:36-38 already records exactly that for the server oracle — data.status, "⚠️ silently accepted". Dropping data from ROW_PREDICATE_ROOTS stops recommending it; it does not stop accepting it.

So after this PR the editor lints data.status == 'overdue' green while the runtime pin one suite lower asserts the same predicate is false. That pair is now pinned on both sides as a characterization test, labelled not-desired-behaviour, reddening when the acceptance is fixed. Tracked as #8166 (not a duplicate of #8155 — opposite failure from the same root list: #8155 is a root bound-but-refused, #8166 is a root accepted-but-dead).

The changeset now states that this shuts the bare-field half of the retirement only.

The rest of the review

  • The suite modelled the host instead of reading it. hostScope and HOST_BOUND_ROOTS were hand-written literals — the same artefact as the data probe, one level up: a literal cannot disagree with its producer, so it silently absorbs drift. It already had drifted (the literal omitted os, which buildExpressionScope really binds, so the old assertion "proved" os unbound). The suite now imports buildExpressionScope from ../../providers/ExpressionProvider.js, derives the bag and Object.keys(...) from it, and subtracts an explicit CURATED_EXCLUSIONS = ['os','data']. The os pin previously handed os in by hand, which showed only that evalRowPredicate forwards scope; it now reads the producer and carries an unbound-root control (size(zzz) >= 0 against the same bag is false).
  • The app.* pin only tripwires one of 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 #8155's two fixes. Its comment now says so: it reads on option A (add app to SCOPE_ROOTS); option B (stop binding app) is caught by the closure assertion, which reads the advertised list against buildExpressionScope.
  • Autocomplete radius, now in the changeset. CelPredicateField.tsx:216-225 builds bareCandidates with fields: [] under scope === 'record', so typing sta in bare position no longer offers status; fields come as member completion after record..
  • Migration impact, now stated outright. Errors bubble through onBlockingIssuesChange (CEL blocking errors are dropped on the floor in ConditionBuilder and ConditionalFormattingEditor too — same ungated-Save family as #4306 #4527), so a saved view carrying a legacy bare condition becomes unsavable in the designer until rewritten — including when opened for an unrelated edit.
  • Self-contradicting test comment fixed. The host-roots test no longer says "advertised host roots must survive the narrowing" while sitting above a test proving app does not. It now covers current_user, user, ctx and features and is titled by what they have in common: the engine knows them.
  • Open question answered. introspectScope returns byte-identical fields and roots for record and flattened against @objectstack/formula@17.2.0 — it echoes the caller's fields hint rather than deriving one per scope (["status","amount"] both ways; [] both ways with no hint). So member completion after record. does not move; only the bare-position catalog does, and that is CelPredicateField's own fields: [] arm, not the engine's.

What changed in the first commit (unchanged by the review)

  1. The conditional-formatting CelPredicateField authors in scope="record", the scope visibleWhen / readonlyWhen / requiredWhen already use.
  2. ROW_PREDICATE_ROOTS drops 'data'.
  3. The :34-44 docblock (which claimed the row is bound bare, under record.* and under data.*) and the :329-332 inline comment are rewritten to the one binding that survives.
  4. The pin at test.tsx:135-143 is turned — its own comment predicted this edit verbatim: flipping this editor to scope="record" would break this test.
  5. The roots-to-runtime pin at test.tsx:178-200 is repaired: it looped every advertised root asserting size(ROOT) >= 0 against a hostScope that itself carried data: {}, so for data the probe hit the host's own empty object and could not fail.
  6. CelTestRunDialog.tsx:18's bind recipe was re-read and is still true — it describes testRunCelPredicate's own bag (celAuthoring.ts:461-464), not evalRowPredicate's, and that dialog is RLS-only.

⚠️ The measured regression this flip introduces — #8155

app is bound at runtime (ExpressionProvider.tsx:88, handed to resolveConditionalFormatting by ObjectGrid.tsx:2086 / ListView.tsx:639) and advertised by this editor, but SCOPE_ROOTS has no app, so under scope="record" the lint refuses it with the nonsense fix record.app. Measured accept set at scope:'record': accepted record previous parent input os current_user user vars features ctx data env settings; refused app tenant org. Under flattened everything bare is accepted — even zzz.

⚠️ And there is no author workaround, which an earlier revision of this description understated by calling the failure "recoverable". buildExpressionScope binds ctx: { user } and os: { user } — each carries user and nothing else — so ctx.app / os.app are not reachable spellings today; they are candidate rebindings under #8155 option B, not escape hatches. An author who needs app.name has no spelling that both lints clean and resolves. Save stays shut.

⛔ I did not suppress the diagnostic in celAuthoring.ts: that is the lenient-fallback shape AGENTS.md #0.1 bans, and the accept set belongs to the engine. This is the thing to decide in contract review.

Proof the pins are lit

Six ablations across the commits. Each mutated only after the implementation was committed; each proved to have reached disk by an anchor count before/after (a no-op edit exits 0 and would otherwise read as a run); each restored with git checkout HEAD -- PATH inside a trap ... EXIT INT TERM on absolute paths; each restore proved by stategit hash-object equal to git rev-parse HEAD:PATH for every touched file, plus an empty git diff HEAD — never by an exit code.

leg mutation result
A on the pre-change tree, delete data: {} from hostScope old pin RED — root "data" should be bound at runtime: expected false to be true. Its green depended entirely on the host's object.
B put 'data' back into ROW_PREDICATE_ROOTS 2 failed — advertised root "data" is unaccounted for, expected [...] to not include 'data'
C revert scope="record" to scope="flattened" 2 failed — the turned pin and the app known-gap pin
D make the record-scope lint refuse data.* in celAuthoring.ts 1 failed — the new data characterization pin. It is not vacuous: it reddens the day #8166 is fixed.
E drop os from buildExpressionScope 1 failed — expected [ 'current_user', 'user', 'ctx', …(3) ] to include 'os'. The os pin reads the producer now; the old hand-injected version would have stayed green.
F add a root to buildExpressionScope 1 failed — the closure assertion: expected [ 'app', 'ctx', 'current_user', …(3) ] to deeply equal [ 'ablation_new_root', 'app', …(5) ]. That is what catches #8155 option B.

Legs A and E are the point of the exercise: in both, the pin claimed a fact that was already false, and was green.

Verification

Repo root, on the head commit; every heavy run through the shared verify lock, verdicts read from its VERDICT line and never from a bare $?.

  • pnpm exec vitest run --reporter=verbose .../ConditionalFormattingEditor.test.tsx25 passed, each named individually.
  • pnpm exec vitest run packages/app-shell/635 files, 6116 passed, 1 skipped, 0 failed.
  • turbo run type-check --filter=@object-ui/app-shell30 tasks successful, including dependency builds and tsc -p tsconfig.test.json.
  • eslint . in packages/app-shell (the package's own lint script) — 1090 files, 0 errors, exit 0 captured before any pipe.
  • pnpm check:control-bytes — OK, 6512 files; check:shell-escape-residue — OK; check-changeset-presence.mjs — OK; check-governed-queue-guard.mjs --test — NOT GOVERNED.
  • Declared narrowing: apps/console + the two examples (90 files, 1061 passed) were verified on the first commit and not re-run after it. git diff 35e49ace1 HEAD --name-only is one .test.tsx and one changeset .md; the implementation blob is identical; nothing imports that test file. Their inputs are byte-identical. CI runs them anyway.

Findings filed rather than folded in: #8155, #8166, #8167. ⛔ #7728 and #7835 remain separate cards.


Generated by Claude Code

…scope

The conditional-formatting editor authored its CEL in the `flattened` scope,
where any bare identifier is legal, and advertised `data` in its autocomplete
roots. Phase 2 of the row-predicate canon retired both spellings on runtime
record surfaces: `evalRowPredicate` binds the row as `record.*` and nothing
else, so `status == 'overdue'` and `data.status == 'overdue'` fault at runtime
while the editor linted them green.

- `CelPredicateField` authors in `scope="record"`, the scope the field
  conditional rules already use, so a bare field ref is an ERROR carrying the
  `record.<field>` fix.
- `ROW_PREDICATE_ROOTS` drops `'data'`.
- The docblock and the inline comment describing the old three-way binding are
  rewritten to the one binding that survives.

The shared `hint.scope ?? 'flattened'` default is untouched: RLS predicates and
flow conditions are not row surfaces.

Tests: the pin that asserted "a bare field lints clean" is turned to assert the
`record.<field>` diagnostic — its own comment predicted this edit. The
roots-to-runtime pin is repaired: it looped every advertised root asserting
`size(<root>) >= 0` against a host scope that itself carried `data: {}`, so for
`data` the probe hit the host's own empty object and could not fail. Each root
is now checked against the binder that is supposed to supply it, in both
directions, and `data` and `os` get their own pins against a scope that does
carry them.

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

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3187.7 KB 3191.4 KB
Main entry chunk (gzip) 143.5 KB 350 KB
Entry file index-DSzUYrZU.js
Status PASS

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


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 5.13KB 2.35KB
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) 498.09KB 113.98KB
core (index.js) 6.96KB 2.79KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 182.08KB 50.62KB
fields (index.js) 243.04KB 61.36KB
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) 11.71KB 4.29KB
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) 5.12KB 1.74KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 15.16KB 3.68KB
plugin-calendar (index.js) 47.29KB 13.18KB
plugin-charts (index.js) 70.35KB 19.68KB
plugin-chatbot (index.js) 193.54KB 46.04KB
plugin-dashboard (index.js) 131.41KB 34.43KB
plugin-designer (index.js) 211.51KB 43.01KB
plugin-detail (index.js) 247.75KB 63.50KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 131.01KB 32.32KB
plugin-gantt (index.js) 167.16KB 40.99KB
plugin-grid (index.js) 208.58KB 56.63KB
plugin-kanban (index.js) 52.46KB 14.57KB
plugin-list (index.js) 113.34KB 27.72KB
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.33KB 3.25KB
plugin-view (index.js) 84.48KB 20.81KB
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.74KB 1.41KB
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 (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

Copy link
Copy Markdown
Collaborator Author

PM review — verdict on the diff, and why the PR is held rather than landed

Reviewed by the PM seat (session session_01YBWFb5YgMU5dw8p2VKj16S). A second review at the designated contract-review tier is running in parallel; this comment records what the PM seat verified independently. The diff is sound. The ship decision is not mine to make — see the bottom.

Two framings in the report I have to correct

① "the exported ROW_PREDICATE_ROOTS" overstates the public face — it is not on one.

I checked this the way I should have checked TranslateFn on #8152 and did not:

declared in:  ConditionalFormattingEditor.tsx, ConditionalFormattingEditor.test.tsx
importers outside those two:  none
packages/app-shell/src/index.ts:  0 `export *` lines, 83 explicit exports,
                                  ConditionalFormattingEditor not among them
package exports map:  "." -> ./dist/index.js only

A star re-export would have put the name on the published face without the literal ever appearing in index.ts — that is the exact shape that got past me on #8152, so I checked for it explicitly. There are none. @object-ui/app-shell's published surface is unchanged by this PR.

This does not un-bind Clause-②: the clause has two legs, and the accept/reject leg fires hard here — the editor's lint verdict flips for a whole class of input. needs:contract-review is correctly attached. But the reason is the behaviour change alone, not a surface change, and the changeset should not be read as touching a published export.

② The app.* regression is not "loud, recoverable" — it is a hard block with no author workaround.

The report and #8155 both frame ctx.app / os.app as reachable spellings. They are not. Per buildExpressionScope (quoted in #8155):

return { current_user: user, user, ctx: { user }, os: { user }, app, data, features };

ctx and os each carry user and nothing else. So an author who needs app.name has no spelling that both lints clean and resolves at runtime. Save is held shut with no escape hatch. That is a functional regression, not a cosmetic one, and it is why I am not landing this on my own authority.

Mitigating, and measured: zero in-tree uses of app.* in a conditionalFormatting condition. The zero is real — the same grep shape run for record. returns 5 hits across types, plugin-kanban, plugin-list, so the instrument fires. Out-of-tree user metadata I cannot measure.

One gap neither the dispatch nor the report names

The PR closes the bare-field half of the silent-death hole and leaves the data.* half open.

From #8155's own measurement, the engine's accept set at scope:'record' contains data. So after this PR:

authored lints matches the row
status == 'overdue' error (fixed by this PR) no
data.status == 'overdue' clean no
record.status == 'overdue' clean yes

data.* is one of the two spellings #5741 Phase 2 retired, and it is the one the PR's own test proves dead at runtime (expect(evalRowPredicate("data.status == 'overdue'", …)).toBe(false)). Dropping data from ROW_PREDICATE_ROOTS stops recommending it; it does not stop accepting it. An author who already wrote data.status gets the identical silent dead rule this card was filed to abolish.

That is not a defect this PR introduces — it is pre-existing and unchanged — and it is not a reason to hold. But the changeset currently reads as though the editor now catches the retired spellings, and it catches one of two. The changeset's "Known gap" section should name this alongside the app gap, and #7727 must not be closed as fully done. Same root cause as app: the engine's SCOPE_ROOTS is the accept set, and it is out of step with what this surface actually binds — in one direction for app, the other for data.

What was verified and stands

Held, and on what

Staying draft pending a maintainer ruling on #8155, where I have put the ship/hold question with a fourth option the report did not enumerate. My reasoning: this PR trades a silent wrong answer for a hard block with no workaround. The trade is probably right — a silent dead rule is the worse failure — but knowingly shipping a no-escape-hatch block to an unmeasurable population is a call for the maintainer, not for me. The producer-side fix is one string in one array; if it is fast-tracked, the question dissolves.

⛔ Not folded in, correctly: #7728, #7835.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Contract review returned PASS-WITH-FINDINGS — two blockers. Patch round in progress.

Tier note: the designated contract-review tier was measured unavailable this session (rate_limit, HTTP 429), so this ran at the default tier under the quota-exhaustion exemption, with needs:contract-review carried as the compensating control.

Correcting myself: in my review above I wrote "The diff is sound" and separately vouched for the changeset. The implementation logic is indeed sound and is not changing. The changeset is not — it carries a false claim, and I did not catch it.

Blocker 1 — "the last place still teaching a spelling the runtime had already retired" is false

CelPredicateField also reaches the flattened default when a caller omits the scope prop (celAuthoring.ts:214/:315, hint.scope ?? 'flattened'). An omitted prop matches no grep for scope="flattened", which is why neither the dev's census nor mine caught it. Measured by enumerating mounts and then asking which pass the prop:

mount scope= in file verdict
PermissionAdvancedFacets.tsx:356, :372 0 legitimate — RLS is not a row surface (#5738 stand-down 3)
inspectors/ConditionBuilder.tsx:368 0 a genuine untouched instance

ConditionBuilder is reached from ActionDefaultInspector.tsx:671-672 for an action's visible/disabled — a row predicate by the canon's own words (rowPredicateCanon.ts:16-18), with usePredicateRecordContext returning { record } only (useExpression.ts:153-158). Bare status == 'x' in an action guard lints clean today and never matches. Sentence comes out; sibling card filed; ⛔ the fix is not folded in here.

Blocker 2 — the PR leaves its own hole open while implying it is closed

At scope:'record' the engine accepts data (#8155's measured set), and rowPredicateCanon.ts:36-38 already records data.status as "⚠️ silently accepted". So the editor still lints data.status == 'x' green while this PR's own new runtime pin asserts it is false. De-advertising stops autocomplete recommending it; it does not stop the lint accepting it. Wants an authoring characterization pin for data mirroring the app.* one, and changeset wording that stops claiming the retirement is complete.

That hole turns out to be live on main today, independent of this PR, on visibleWhen/readonlyWhen/requiredWhen and formula expression — filed with its full evidence chain as #8166.

Also going into the patch round

  1. The repaired suite still models the host rather than reading it — hostScope and HOST_BOUND_ROOTS are hand-written literals and the file never imports buildExpressionScope. The new os pin passes os in by hand, so it proves only that evalRowPredicate forwards scope; size(zzz) >= 0 with zzz injected passes identically. That is the same artefact the dev correctly killed for data, one test lower. Deriving hostScope from buildExpressionScope would have surfaced blocker 2 by itself.
  2. The app.* pin reddens only under 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 #8155 option A, not option B — the comment overclaims.
  3. Undocumented user-visible radius: under scope="record", CelPredicateField.tsx:217-225 builds bareCandidates with fields: [], so bare-position autocomplete no longer offers field names.
  4. Migration impact undersold: errors bubble via onBlockingIssuesChange (CEL blocking errors are dropped on the floor in ConditionBuilder and ConditionalFormattingEditor too — same ungated-Save family as #4306 #4527), so an already-saved view carrying a legacy bare or data.* condition becomes unsavable in the designer until rewritten — including for unrelated edits.

Confirmed clean, so nobody re-checks it

The visibleWhen/readonlyWhen/requiredWhen parity claim (verified at ObjectFieldInspector.tsx:874/888/902, clientValidation.ts:792); the hint.scope ?? 'flattened' default genuinely untouched; #8155's measurement matching buildExpressionScope's real return; errors genuinely blocking; the turned bare-field pin and the record-wins-over-host-record pin both discriminating properly; and the no-published-face finding independently re-verified — exports is "." plus ./styles.css with no deep subpath, so views/metadata-admin/* is unreachable from outside the package.

Still held in draft, now on two counts: this patch round, and the unresolved ruling on #8155.


Generated by Claude Code

… pin the data half

Contract-review follow-up on the conditional-formatting scope flip. No
implementation logic changes; this is coverage and text.

- The roots-to-runtime suite no longer writes the app-shell predicate bag out
  by hand. It calls `buildExpressionScope`, derives the advertised-root
  expectation from `Object.keys(...)` minus an explicit curated-exclusion list,
  and runs the `os` and `data` pins against that same bag. A literal cannot
  disagree with its producer, so it silently absorbs drift -- and it already
  had: the literal omitted `os`, which the producer really does bind, so the
  old `os` assertion "proved" it unbound. The previous `os` pin also handed
  `os` in by hand, which showed only that `evalRowPredicate` forwards `scope`;
  it now reads the producer and carries an unbound-root control.
- New characterization pin: a `data.*` condition still lints CLEAN. Dropping
  `data` from the advertised roots stops recommending it, not accepting it --
  the engine's `SCOPE_ROOTS` lists `data`, so the record-scope lint waves it
  through while the runtime pin one suite lower asserts it is false. Green
  here plus false there is the defect, and the pair is the referent.
- The `app` pin's comment now says which of its card's two candidate fixes it
  is a tripwire for; the closure assertion covers the other.
- The host-roots test no longer says "advertised host roots must survive"
  while sitting above a test proving one of them does not.

The changeset drops the false "last place" claim, states that this closes the
bare-field half of the retirement and not the `data.*` half, spells out that a
saved view with a legacy condition becomes unsavable in the designer until it
is rewritten, and records the bare-position autocomplete change.

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

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3189.7 KB 3191.4 KB
Main entry chunk (gzip) 143.9 KB 350 KB
Entry file index-DMtn6Nk0.js
Status PASS

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


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 5.13KB 2.35KB
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) 498.05KB 113.95KB
core (index.js) 6.96KB 2.79KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 187.85KB 52.13KB
fields (index.js) 243.04KB 61.36KB
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) 11.71KB 4.29KB
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) 5.12KB 1.74KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 15.16KB 3.68KB
plugin-calendar (index.js) 47.29KB 13.18KB
plugin-charts (index.js) 70.62KB 19.71KB
plugin-chatbot (index.js) 193.54KB 46.04KB
plugin-dashboard (index.js) 131.41KB 34.43KB
plugin-designer (index.js) 211.51KB 43.01KB
plugin-detail (index.js) 247.68KB 63.49KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 131.01KB 32.32KB
plugin-gantt (index.js) 167.16KB 40.99KB
plugin-grid (index.js) 208.58KB 56.63KB
plugin-kanban (index.js) 52.46KB 14.57KB
plugin-list (index.js) 113.34KB 27.72KB
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.33KB 3.25KB
plugin-view (index.js) 84.48KB 20.81KB
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.74KB 1.41KB
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 (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

`ROW_PREDICATE_ROOTS` is an `export const`, which the release note read as an
API change. Measured: `packages/app-shell/src/index.ts` has 0 `export *` lines
and names neither the const nor `ConditionalFormattingEditor`, and the package
`exports` map is `"."` plus `./styles.css` with no deep subpath -- so nothing
outside the package can import it. The behavioural narrowing is real and is
what the note is about; the published surface is unchanged.

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

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3189.7 KB 3191.4 KB
Main entry chunk (gzip) 143.9 KB 350 KB
Entry file index-DMtn6Nk0.js
Status PASS

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


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 5.13KB 2.35KB
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) 498.05KB 113.95KB
core (index.js) 6.96KB 2.79KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 187.85KB 52.13KB
fields (index.js) 243.04KB 61.36KB
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) 11.71KB 4.29KB
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) 5.12KB 1.74KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 15.16KB 3.68KB
plugin-calendar (index.js) 47.29KB 13.18KB
plugin-charts (index.js) 70.62KB 19.71KB
plugin-chatbot (index.js) 193.54KB 46.04KB
plugin-dashboard (index.js) 131.41KB 34.43KB
plugin-designer (index.js) 211.51KB 43.01KB
plugin-detail (index.js) 247.68KB 63.49KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 131.01KB 32.32KB
plugin-gantt (index.js) 167.16KB 40.99KB
plugin-grid (index.js) 208.58KB 56.63KB
plugin-kanban (index.js) 52.46KB 14.57KB
plugin-list (index.js) 113.34KB 27.72KB
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.33KB 3.25KB
plugin-view (index.js) 84.48KB 20.81KB
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.74KB 1.41KB
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 (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

Unblocked — #8155 ruled option B (director seat, decision batch #67, 2026-09-07)

objectui aligns to the engine's SCOPE_ROOTS: drop the app binding from buildExpressionScope, drop app from ROW_PREDICATE_ROOTS, flip the characterization test to the aligned state, and land this PR whole (the scope="record" flip included). No interval regression: in-tree app.* usage is zero. Full ruling on #8155.


Generated by Claude Code

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants