Skip to content

fix(app-shell): the Studio decides a screen field's visibleWhen live, through the runner's renderer, over the screen's declared fields (objectui#10743) - #10763

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-10743-screen-predicate-run-scope
Sep 27, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-10743-screen-predicate-run-scope

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #10743
Clause-②: no — the Studio's screen preview, Debug run and Problems panel rebind to the runner's own evaluator and its scope; no export on @object-ui/app-shell's index, no spec shape and no accept set moves (screen-spec.ts, flow-expr-problems.ts and ScreenView's scope helper are module-internal)

Executes the seat's ruling on objectui#10743 (comment 5853038135, option C: one contract, one scope) with the amended claim 5852879659, the round-1 contract review (comment 5853440854) and the round-2 contract review (comment 5855514425, PASS) on this PR. The server half is objectstack-ai/objectstack#20178 and does not block this. The flow runner (ScreenView, FlowRunner) has NO behaviour change; ScreenView only exports its existing screenPredicateScope.

What changed

  • screen-spec.ts — buildScreenSpec(node) keeps every authored field and carries its visibleWhen raw, as the runtime screen executor sends it to the client; it no longer takes variables and no longer decides visibility. ScreenView decides it live, over the screen's declared fields and the values the author collects in the preview — the one client evaluator. The Studio's own evaluator for this key is removed: isFieldVisibleWhen, fieldVisibility, and the simulator's evalVisibleWhen (flow-sim-validate.ts; evalCelPredicate keeps serving edge guards and decision branches).
  • screenPredicateRoots(node) is the ONE scope rule: the keys of the renderer's own screenPredicateScope(spec, {}) plus record, the namespace evalFieldPredicate binds the same values under (that binding happens inside evalFieldPredicate, so record is restated by name; the bare names are read off the scope). screenVisibleWhenScopeError(visibleWhen, node) judges one predicate against it — shape refusal, CEL parse (a {var} brace), then the free roots — naming a root that is not a field on this screen with the nearest declared field for a typo. unevaluableVisibleWhen(node) lists the fields it applies to, for the Debug run's screen step (recorded on the step's error, drawn in rose). A sibling-field predicate (discount above zero, record.discount) is not reported.
  • Comprehension variables are bound, not roots (round 2, the review's optional item): ["a","b"].exists(t, t == note) no longer reports t. The free roots are walked over the canonical parseCelToAst, with the binding scoped to the macro's own arguments (all, exists, exists_one, map, filter): ["a","b"].exists(t, t == tags) still reports tags, and an outer bare t in t == 1 && ["a"].exists(t, t == note) is still a root. collectCelRootIdentifiers is the fallback when the parse hands back no AST, so an unexpected shape is never read as "no roots".
  • flow-expr-problems.ts (round 2, the review's blocking item) — the Problems panel judged the screen node's fields[].visibleWhen column over the FLOW scope: it warned "discount is not a reference in scope at this step" on the sibling-field shape and stayed silent on a run variable, the inverse of the Debug-run step. That column now goes through screenVisibleWhenScopeError, the same rule: the sibling shape is clean, and a run variable the flow scope holds (needsApproval == true) is the unknown reference, named by field and root, at warning — this panel's level for a reference nothing binds, since the runtime accepts such a flow today (the registerFlow refusal is objectstack#20178). Brace errors stay error, ahead of the scope. Every other expression slot keeps the flow scope.
  • hiddenFieldCount(spec, values) is the renderer's own verdict (screenFields minus visibleScreenFields), so the preview's "N fields hidden by their visible when condition" hint follows every tick and keystroke; it moved into ScreenFormPreview, where the values live.
  • inspectors/flow-node-config.ts (round 2) — the screen field inspector's visibleWhen placeholder is a sibling-field example, createOpportunity == true (the flows guide's shape); it read stage == "review", a run-variable shape the runner cannot bind. Only that column entry and a comment above it change.
  • ScreenPreview.tsx — variables now feed {var} interpolation only; the docblock says so.
  • Published text rewritten wherever it said the Studio takes "the runtime's resume-door reading": screen-spec.ts, ScreenPreview.tsx, flow-simulator.ts, flow-sim-validate.ts, packages/app-shell/README.md, and the pending .changeset/10692-debug-run-routing-parity.md (a dated note in the objectui#10533 form; its frontmatter is untouched). Each states that the server's resume door still reads the run's variables until objectstack#20178 lands, and screen-spec.ts now says that divergence "is to be closed on the server side". No server behaviour that is not on objectstack main is claimed.
  • One changeset, .changeset/10743-studio-screen-visiblewhen-live.md, '@object-ui/app-shell': patch.

Still on the flow scope — stated, not aligned

The inline inspector's visibleWhen cell (FlowObjectListField, an expression column fed the flow scopeGroups) is outside the claimed surface and is unchanged. Measured at head with the real FlowObjectListField, the screen node's fields columns and resolveFlowScope (flow variable needsApproval; scope tokens needsApproval, record, previous): the cell's inline note reads "discount is not a reference in scope at this step." on the sibling-field shape (note, visibleWhen discount above zero) and shows nothing for reason (needsApproval == true); its picker offers the flow scope; the placeholder now reads createOpportunity == true. So the inline cell still says the inverse of the Problems panel and the Debug run on the sibling shape. Stated in the changeset and the README; aligning it (screen-field groups for that one column, from screenPredicateRoots) is the follow-up beside objectstack#20178.

Pending changesets that name inspectors/flow-node-config.ts (changeset-claim-re-read, comment 5855257425)

  • .changeset/6499-inactive-values-retained.md — still true. Its paragraph names inactiveRetainedKind(field, node, fields) and the showWhen population ("all 33 gated fields"); this diff touches neither: at the base and at head the file exports inactiveRetainedKind once and carries the same number of showWhen occurrences, and the change is one placeholder string plus a comment on the screen fields column, which declares no showWhen.
  • .changeset/9278-end-outcome-select.md — still true. Its paragraph describes the end group's outcome control; this diff does not touch the end group: at head outcome is still the select over completed | refused with defaultValue: 'completed', and the only success · failure left in the file is the comment recording that placeholder's removal. No correction, both frontmatters untouched.

H1 — the paused-run payload carries no run variables (both sides, from source)

  • objectstack service-automation engine.ts, the screen-pause arm of execute and of resumeInternal: return { success: true, status: 'paused', runId, durationMs, screen: err.screen }; — no variables member; the subflow re-pause arm is the same shape with childRes.screen.
  • spec contracts/automation-service.ts AutomationResult members: success, output, error, durationMs, code, status, runId, screen, successMessage, errorMessage, refusalMessage, summary — no variables. ScreenSpec members: nodeId, title, description, fields, kind, objectName, mode, recordId, defaults, idVariable.
  • runtime domains/automation.ts: the trigger door serves the engine result verbatim, if (isPausedRun(result)) { return { handled: true, response: deps.success(result) }; }; the resume door likewise after automationService.resume(parts[2], signal); the screen door answers deps.success({ runId: parts[2], screen }).
  • The one door that discloses variables for a paused run is GET runs/RUN_ID (an ExecutionLogEntry, objectstack#7639), behind isRunStateRead and refuseUngrantedRunRead (the sys_automation_run read grant); its own comment: applying the grant to the screen route "would lock out the end user the flow paused for".
  • objectui: interpretFlowResponse's paused arm carries { kind: 'paused', runId, screen, data }, judgeFlowLaunch forwards { kind: 'screen', runId, screen }, FlowRunner's ScreenFlowState is { flowName, runId, screen }. Nothing is received and dropped.

H2 — producers

  • No shipped flow authors a screen visibleWhen over a run variable, in either repo. objectstack example flows with screen nodes: examples/app-crm convert-lead.flow.ts (a waitForInput message screen and two object-form screens, no visibleWhen), examples/app-showcase showcase_reassign_wizard (one flat field, no visibleWhen), examples/app-todo task.flow.ts (visibleWhen appears only in a comment).
  • Every measured screen visibleWhen predicates over a sibling field: the flows guide (opportunityName, createOpportunity == true), the console preview sample (note, discount above zero), and the objectstack and lint test fixtures.

H3 — the ruling, and what the Studio was doing (M1, M2, measured on the base)

Option C: a screen field's visibleWhen binds the same screen's declared fields plus the values being collected — the spec ScreenFieldSpec.visibleWhen docblock ("Bare CEL over the screen's own field names", "evaluated by the CLIENT … not by the server"). The runner already implements it; the Studio, since PR objectui#10736, read the resume door instead. Measured with the real FlowSimulator and ScreenPreview on the base 704e05b09d (probe file, not committed):

  • M1 console sample: at the pause note is absent (buildScreenSpec dropped it; the step note read "Hidden because its visibleWhen could not be evaluated … Unknown variable: discount"); typing 5 into Discount leaves note absent; the control comment is drawn throughout. Docs shape: opportunityName absent; ticking the checkbox (data-state goes to checked) leaves it absent; control plain drawn. With the flow variable declared false in the run (as the guide recommends): the predicate evaluated false, the field was dropped, the tick changed nothing. ⇒ frozen, exactly the outcome the runtime's screen executor forwards the predicate raw to avoid.
  • M2 unevaluableVisibleWhen over the run's variables named the sibling predicates (note, opportunityName) as unevaluable, and did NOT name a genuine run-variable predicate when the run held the variable (needsApproval: true → nothing reported); with needsApproval: false seeded, reason was hidden silently.
  • Toolchain facts the fix rests on: @objectstack/formula 17.4.0 exports collectCelRootIdentifiers, parseCelToAst and nearestName; a comprehension arrives in the AST as an rcall whose third argument opens with the id it binds; on the runner's seeded scope, createOpportunity == true with false evaluates false, and discount above zero with null faults ("no such overload"), which ScreenView's fallback then decides — its direction (objectui#8069), not this card's.

Pins — red on the base, green on the head, each with a control in the same tree

Files: ScreenPreview.debugRunScope-10743.test.tsx (new; real FlowSimulator → real ScreenPreview, mounted as the panel mounts them), ScreenPreview.test.tsx (re-judged, plus the round-2 comprehension and one-rule pins), flow-expr-problems.test.ts (round 2: the Problems panel), simulator/__tests__/flow-simulator.parity-10692.test.ts (re-judged).

  • the sibling-field predicate reveals its field live: console sample (5 reveals note, 0 hides it; comment drawn throughout), docs shape (tick reveals opportunityName; plain drawn throughout; hint appears then goes), docs shape with the flow variable seeded false;
  • a run-variable predicate is not read from the run's variables: the render is the same whether the run holds needsApproval true or false (plain drawn in both), and the screen step names "reason" with needsApproval as an error whatever the run holds; the sibling pause carries no error and names no field;
  • unevaluableVisibleWhen: sibling, record. sibling, two siblings, no predicate, blank → not reported; run variable, stdlib call over a run variable, vars root, brace, non-string → reported by field name; typo → nearest declared field named; comprehension (exists, all, nested map and exists_one) over siblings → not reported; an undeclared root inside a comprehension body, and the same name used bare outside one → reported;
  • one rule: screenPredicateRoots(node) equals the keys of screenPredicateScope(buildScreenSpec(node), {}) plus record;
  • Problems panel: sibling, record. sibling, comprehension over a sibling, no predicate → no problem on the screen; a run variable the flow scope holds → warning on the screen node, "Note: needsApproval is not a field on this screen"; a {var} brace → error ahead of the scope; control: a decision downstream keeps the FLOW scope — needsApproval and the screen output discount are clean there, and an unknown root warns with the flow wording ("did you mean needsApproval");
  • buildScreenSpec keeps every field with its visibleWhen; hiddenFieldCount(spec, values) reads 1 then 0.

Red legs, each with the head pins over base sources checked out from the base commit, trap-restored with git checkout HEAD --, blob hashes re-checked against HEAD and git diff HEAD empty: round 1 over 704e05b09d (five source files, three pin files): Tests 24 failed | 35 passed (59); round 2 over 39fbfac155 (screen-spec.ts, flow-expr-problems.ts; ScreenPreview.test.tsx, flow-expr-problems.test.ts): Tests 8 failed | 37 passed (45) — the four Problems-panel pins, the three comprehension pins and the one-rule pin. Green leg at cd8e9713e7, eight files (the four above plus flow-simulator.test.ts, FlowSimulatorPanel.test.tsx, spec-symbol-parity.test.ts, ScreenView.requiredMarker-10367.test.tsx): Test Files 8 passed (8), Tests 158 passed (158).

Ablations (each through ablation-replace.mjs in wrap mode: anchor hit exactly once, blob changed, tests run, restored with blob equal to HEAD and git diff HEAD empty):

  • A1 toScreenFields stops carrying visibleWhen → 8 red (the live-reveal pins, keeps every field in both files);
  • A2 hiddenFieldCount returns 0 → 3 red (the two hint pins, the count pin);
  • A3 the undeclared-roots filter yields nothing → 8 red at 39fbfac, 9 at head (the comprehension-does-not-hide pin joins) — run-variable and vars rows in both files, the typo pin, the step-error pin; brace and non-string stay reported by the parse and shape legs;
  • A4 the simulator no longer asks unevaluableVisibleWhen → 6 red (every step-error row, the debug-run step pin);
  • A5 the hint never renders → 2 red (both hint pins);
  • A6 the roots ignore screenPredicateScope and keep only record → 5 red at 39fbfac, 9 at head (the three comprehension rows and the one-rule pin join) — sibling not-reported rows, the sibling pause row, the two live-reveal debug-run pins whose steps now carry an error;
  • A7 (round 2) isScreenVisibleWhenColumn returns false, so the panel judges the column over the flow scope → 4 red (the sibling and comprehension no-problem pins, the run-variable pin, the flow-scope control's clean half);
  • A8 (round 2) the canonical parse hands back no AST, so the roots fall back to collectCelRootIdentifiers → 4 red (the three comprehension pins and the panel's comprehension pin).

Round 3 (head c06451c56b, prose only)

  • The changeset's screenPredicateScope clause now reads the record's text: the Studio reads the bare roots a predicate may name off the renderer's own scope; only the record namespace, which the renderer binds inside evalFieldPredicate, is restated by name. Its "Still on the flow scope" paragraph adds "and says nothing on a run variable the other two report". Frontmatter byte-identical: md5 4e8ce55021fe3b14329c8b4cb3f9f354 before and after.
  • Code byte-identical to cd8e9713e7 (git diff cd8e9713e7 HEAD --stat: the one changeset file), so the green and red legs, type-check and eslint above stand as measured there. A3 and A6 re-measured at head over the three round-1 pin files: 9 failed | 55 passed (64) each, anchor hit once, restored with blob equal to HEAD.
  • Gates at c06451c56b: check-changeset-presence ✅, check-changeset-claims ✅ (report-only), check-pending-changeset-literals ✅, check-changeset-no-major ✅, check-changeset-overwrite ✅, check-control-bytes ✅, check-new-line-citations: VERDICT 0 new citation(s).
  • FlowExprIssue.tsx and the inline cell are untouched; the seat files that follow-up.

Gates (at cd8e9713e7; code unchanged at c06451c56b)

  • Dependency closure turbo run build --filter='@object-ui/app-shell^...' --concurrency=2 under the verify lock: Tasks: 28 successful, 28 total, VERDICT command-exit 0 (round 2: 28 cached, FULL TURBO).
  • pnpm --filter @object-ui/app-shell type-check (script echoed @object-ui/app-shell@17.6.0 type-check, tsc --noEmit && tsc -p tsconfig.test.json): exit 0 in both rounds; tsc -p tsconfig.test.json --listFiles lists the new pin file (1).
  • eslint on the changed source/test files: 0 errors in both rounds (round 1: 12 pre-existing warnings in ScreenView.tsx, react-refresh and no-explicit-any, untouched lines).
  • check-changeset-presence ✅ (round 2: 11 published source files, 1 changeset declared); check-control-bytes ✅; check-vi-mock-specifiers ✅; check-vi-mock-override-shape ✅; check-test-path-roots ✅; check-pending-changeset-literals ✅; check-changeset-claims ✅ (report-only); check-new-line-citations: VERDICT 0 new citation(s) at both commits.
  • NOT MEASURED locally: check-readme-exports — exit 1 on a prerequisite (app-shell dist/index.d.ts absent, "run pnpm build first", 8 packages unbuilt); the README edits are prose and add no fenced import block (the round-1 CI run of README Export Check was green). The repo-level pnpm lint and full pnpm test are CI's runs.

Serial

Claim time 05:28Z: no open PR touched metadata-admin/previews/, ScreenView.tsx or FlowRunner.tsx. Re-checked before each push; round 3: 7 other open PRs (10768, 10760, 10759, 10714, 10278, 8941, 5400 — the release PR across all 1732 files, 18 pages), every page of every file list read by REST: none touches the PR's 14 files, metadata-admin/previews/, inspectors/flow-node-config.ts, ScreenView.tsx or FlowRunner.tsx. origin/main fetched into the private ref refs/issue-10743/main = b5b6f37531 before the round-3 push; git merge-tree --write-tree exit 0 (no commit on main since the base touches a file this PR changes; main moved in 7 commits and 56 files since 704e05b09d, and the only ones in a directory this PR touches are six unrelated .changeset/ entries and six unrelated metadata-admin/ files — PermissionMatrixEditor.tsx with its clone test, i18n.ts, permission-set-clone-dispatch.ts, inspectors/ReportDefaultInspector.tsx with its test). No merge commit was needed.

Acceptance notes

  • Direction, unchanged and out of scope (objectui#8069): under ScreenView's fallback the console sample's note is drawn before any discount is typed — discount is seeded null, the comparison faults in CEL, and evalFieldPredicate falls open — and hides once 0 is typed. The pins read the live decision after typing, not the initial fallback. The inspector's standalone preview has no step, so a scope fault there is named by the Problems panel (now the right shape) and not inline.
  • Two vocabularies for one finding, stated: the Debug run records an undeclared root on the step's error (the renderer cannot evaluate it there), the Problems panel at warning (the runtime accepts the flow today; objectstack#20178 makes it a refusal). Both are English literals like their neighbouring step notes and the panel's other messages are localized through engine.flowRef.*; the new sentence is not in the i18n catalogue.
  • findUnknownRefs, the panel's flow-scope checker, allow-lists env, request, context, user, now, today, self, data; the screen column no longer goes through it, so user == "x" on a screen field is reported — the runner binds none of those.
  • Object-form screens are untouched (screenFields is empty for them); interpolate and the {var} title/description path are untouched.
  • The retired Studio functions were never on @object-ui/app-shell's index; the ScreenView export is additive and module-level only.
  • The runtime's resume door still evaluates over the run's variables until objectstack#20178 lands: until then a predicate over a run variable is an error in the Studio and shown by the runner, while the server enforces its required only when the run's variables make the predicate true, and waives it when they make it false or leave it unevaluable.

Generated by Claude Code

… through the runner's renderer, over the screen's declared fields

The Studio screen preview and the Debug run's screen pause judged each
field's `visibleWhen` once, against the run's variables, dropped the
fields judged hidden and stripped the predicate from the rest, so a
sibling-field predicate (`createOpportunity == true`, `discount > 0`)
stayed hidden however the author ticked or typed. `buildScreenSpec` now
carries the predicate raw and `ScreenView` decides it live; the Studio's
own evaluator for this key (`isFieldVisibleWhen`, `fieldVisibility`,
`evalVisibleWhen`) is removed. The Debug run's screen step names, as an
error, a predicate that references a name that is not a field declared
on this screen, or a shape `registerFlow` refuses; a sibling predicate
is no longer reported. `ScreenView` exports `screenPredicateScope`,
unchanged. The runner's fallback direction is untouched.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
@github-actions github-actions Bot added documentation Improvements or additions to documentation tests package: app-shell labels Sep 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3066.2 KB 3104.5 KB
Main entry chunk (gzip) 148.3 KB 350 KB
Entry file index-D22T5J3b.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.57KB 6.15KB
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) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.17KB 10.58KB
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.39KB 1.21KB
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.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
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) 554.92KB 132.66KB
core (index.js) 9.52KB 3.79KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.93KB 62.31KB
fields (index.js) 259.29KB 65.64KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 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.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.65KB 14.69KB
plugin-charts (index.js) 82.37KB 22.57KB
plugin-chatbot (index.js) 198.40KB 47.22KB
plugin-dashboard (index.js) 134.11KB 35.62KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 233.23KB 61.74KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 157.08KB 40.13KB
plugin-gantt (index.js) 169.75KB 41.96KB
plugin-grid (index.js) 218.12KB 59.72KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 114.70KB 28.42KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.91KB 9.05KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 87.83KB 22.01KB
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) 116.21KB 38.14KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
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) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 39fbfac15527cf018db22acd9888752532ff2057

① Derived judgments

  • M1, M2 on base — hold. Own probe (base worktree at 704e05b09d, real FlowSimulator → real ScreenPreview, under the verify lock). Console sample: note absent at the pause, still absent after typing 5, comment drawn throughout; buildScreenSpec(node, variables).fields was [discount, comment] with no visibleWhen carried. Docs shape: opportunityName absent; after the tick (data-state checked) still absent; plain drawn; identical with createOpportunity: false seeded. The base step note read "Hidden because its visibleWhen could not be evaluated … note (Unknown variable: discount)". M2: unevaluableVisibleWhen(node, variables) on base named note and opportunityName; with needsApproval: true seeded it reported nothing and Reason was drawn; with false it reported nothing and Reason was hidden silently. Exactly the dev's reading.
  • One evaluator — partly. Preview and Debug run both render through ScreenPreview → ScreenView (visibleScreenFields/screenPredicateScope); screen-spec.ts evaluates no value. Repo-wide grep at head for fieldVisibility|isFieldVisibleWhen|evalVisibleWhen: only the 10743 changeset text (the FieldVisibilityDeclaration hit in ExpressionProvider.tsx is form field rules, unrelated). evalVisibleWhen's removal is safe: on main its only caller was screen-spec.ts; evalGuard and evalBranchPredicate still route through evalCelPredicate over flowCelScope, untouched. Parity-pin re-judging: every removed row pinned the Studio's own value evaluation over run variables, which the ruling abolishes; the replacement rows pin scope only. No drift; one CEL-semantics family (size(), n == "2", vars control) retires with the evaluator whose subject is now ScreenView's.
    ⛔ A second scope judgement of the same key survives in metadata-admin/previews/flow-expr-problems.ts (checkCel → findUnknownRefs over resolveFlowScope, the FLOW scope: upstream outputs, not the screen's own fields). Measured at head with an assignment upstream of the console-sample screen: Problems panel emits warning "Note: \discount` is not a reference in scope at this step."while the Debug-run step is silent; forneedsApproval == truethe Problems panel is silent while the step errors. Same code on base (measured: same warning), but on base both surfaces read the flow scope and agreed; this PR makes the Studio contradict itself on the only shipped producer shape. The inline inspector'sFlowObjectListFieldreceives the same flowscopeGroups for expression columns (:282`), not rendered here.
  • Direction fence. A faulting predicate is drawn at head (measured: needsApproval == true with the run holding false → Reason drawn, no hint; console sample with discount seeded null → note drawn, hiddenFieldCount 0, because dyn null greater-than int is a CEL fault and evalFieldPredicate falls open). Stated as a rebind to ScreenView's fallback with objectui#8069 named, in the changeset, the dated note, screen-spec.ts, flow-simulator.ts. The Debug run names the fault on the step (error, drawn rose at FlowSimulatorPanel:415): "A screen field's visibleWhen may reference only the fields declared on this screen; the screen cannot evaluate "note" (needsApproval is not a field on this screen)." The inspector's standalone preview has no step, so a scope fault there is named nowhere (the Problems panel names the wrong shape, above). The 10692 dated note is true about what changed.
  • Scope rule. record IS bound by the runner: evalFieldPredicate(pred, scope, true, undefined, scope) → engine buildScope binds record plus extra keys only; measured record.discount == null ok, and vars, previous, input, os, user, current_user all "Unknown variable" — so declared fields plus record is exactly the runner's binding. record is restated by hand (RECORD_ROOT), not read off screenPredicateScope, contrary to the docblock's "so the two cannot disagree". collectCelRootIdentifiers (formula 17.4.0): size(tags), has(record.x), string literals, ternary, note.contains("x") all fine; comprehension variables are false-reported — ["a","b"].exists(t, t == note) → "t is not a field on this screen" while the runner evaluates it. No ScreenView field type yields a list, so only list literals reach it; unpinned, non-blocking. Typo row holds: nearestName cap is max(2, len/3) → dicount → discount, needsApproval → none; names of one or two characters always get a suggestion.
  • hiddenFieldCount(spec, values). Follows the renderer exactly (measured along null, 0, 5: count 0, 1, 0; hint absent, present, absent). required plays no part and the preview has no Submit.
  • Runner. ScreenView.tsx diff: export on screenPredicateScope plus five docblock lines; zero behaviour change. Not on src/index.ts (no export *; the views block names FlowRunner and the types ScreenFlowState, ScreenSpec, ScreenFieldSpec only); README Export Check green. Clause-② no holds.
  • Pins. Head, 7 files: Test Files 7 passed, Tests 138 passed. Base leg (head pins over 704e05b09d): 24 failed | 35 passed (59), all three files red. Ablations over the 3 pin files, anchor hit once, blob restored equal to HEAD each time: A1 8 failed | 51 passed; A3 8 failed | 51 passed; A6 5 failed | 54 passed. No tautology; the run-variable render pin (reasonWithFalse equals reasonWithTrue) is weak alone but red on base and paired with the step-error pin.
  • Type-check app-shell at head after the closure build (Tasks: 28 successful, 28 total): tsc --noEmit && tsc -p tsconfig.test.json exit 0.

② Semver level

  • patch, Clause-②: no: confirmed (export question above; retired functions were never on the index).
  • 10692 changeset: frontmatter md5 4e8ce55021fe3b14329c8b4cb3f9f354 on both sides; dated note in the objectui#10533 form. Pending changesets naming touched files still hold: 10367 (ScreenView aria-required, behaviour untouched), 8526 (ScreenPreview renders EmptyDescription, line 83 at head), 10692 (note appended). No changeset-claim-re-read bot comment exists on the PR; the check run is green.
  • Server claims against objectstack main 455dcc060d: refuseInvalidScreenInput still builds new Map(Object.entries(run.variables)) with the bag layered; registerFlow's ledger arm still calls check(slotWhere, found.value, false) with no identifier set — the undeclared-identifier refusal does not exist, and no sentence in changeset, README, docblocks or PR body claims it does. Brace, non-string and blank refusals (predicateSlotRefusal, validateExpression) exist as stated.
  • ⛔ Overstatement, changeset last sentence and PR body Acceptance notes last bullet: "while the server waives its required" / "its required waived by the server". validateScreenInputs enforces required whenever visibility(field) === true, so a run whose variable makes the predicate true has the server ENFORCE it. Replacement: "while the server enforces its required only when the run's variables make the predicate true, and waives it when they make it false or leave it unevaluable."
  • Minor: screen-spec.ts docblock "it is closed on the server side" reads as done; write "it is to be closed on the server side".

③ Boundary flags

  • CI on head: 43 runs — 39 success; 3 skipped by design (Test coverage, coverage shard, dependabot); Changeset Bump Policy reports status in_progress with conclusion success. All 8 test shards, dist pins, Type Check, Lint, README Export Check, Governed Surface Queue Guard, Changeset Declaration and Claim Re-read: success.
  • git merge-tree --write-tree against current main f308a655b8 (two commits past the PR base: feat(core,sdui-parser,components): the published manifest declares the html tier's intrinsic elements, marked tier html — div stays out (objectui#10735) #10753, docs(skills): the grid 2xl paragraph names objectui's own BreakpointColumnMap and reads all six keys #10708, neither touching the 11 files): clean, exit 0; PR mergeable_state clean.
  • Open PRs: 12 others, file lists read (release PR 5400 over 5 pages): none touches the 11 files, previews/, ScreenView.tsx or FlowRunner.tsx.
  • Governed predicate: 0 of 11 path(s) governed, 846 lines under 5000, exit 0 — ordinary queue landing.
  • Draft: true. Labels documentation, tests, package: app-shell; assignee os-elon-musk; card pm:dispatched.
  • Dev's out-of-scope finding is real: inspectors/flow-node-config.ts:743 placeholder stage == "review" sits on the visibleWhen column of the SCREEN node's fields objectList (screen: block from :31, help "Input fields collected on this screen") — the screen-field slot, a run-variable-shaped example the runner cannot bind. Not filed; it belongs on the same follow-up card as the Problems-panel scope.

Implemented-by: claude/issue-10743-screen-predicate-run-scope
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: FAIL

Two items stop the landing: (1) previews/flow-expr-problems.ts still judges a screen field's visibleWhen over the flow scope, so at head the Studio's Problems panel warns on the sibling-field shape and stays silent on the run-variable shape — the inverse of the Debug-run step — which contradicts the ruling's "sibling-field predicate is not reported" and the PR body's unqualified "one client evaluator / diagnostics evaluate over the declared screen scope"; either amend the claim to include that file and derive the fields[].visibleWhen column's roots from screenPredicateScope(buildScreenSpec(node), {}) plus record, or state the remaining divergence in the changeset, README and PR body and file the follow-up card. (2) The "server waives its required" sentence in the changeset and PR body is false whenever the run's variable makes the predicate true; use the replacement text above.

…n over the screen's declared fields, by the Debug run's rule

Round-2 review items on the same branch. `flow-expr-problems.ts` judged
the `screen` node's `fields[].visibleWhen` column over the FLOW scope,
so it warned on a sibling-field predicate and stayed silent on a run
variable, the inverse of the Debug run's screen step. It now applies
`screenVisibleWhenScopeError` from `screen-spec.ts`, the one rule both
surfaces share: the roots are the keys of the renderer's own
`screenPredicateScope` plus `record`. A comprehension's iteration
variable is bound, not a root (walked over `parseCelToAst`, scoped to
the macro's arguments). The screen field inspector's `visibleWhen`
placeholder is a sibling-field example. The changeset's server
sentence states when the resume door enforces `required`; the
`screen-spec.ts` docblock says the server side is to be closed and
that `record` is restated by name because the renderer binds it inside
`evalFieldPredicate`.

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

github-actions Bot commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 2 pending changeset(s) describe a file this change touches

Their bodies publish verbatim into the CHANGELOG at the next release, so this is a request to re-read them against your diff — addressed here because you are the one seat that can answer it without re-deriving anything.

⛔ Nothing here blocks, and nothing here is a verdict on your change. This gate exits 0, is not a required context, and judges name resolution, never meaning: it asked whether a pending body names a file you touched. "Is this sentence still true?" is the one question it will not answer, and the one you are being asked to answer.

.changeset/6499-inactive-values-retained.md

  • names flow-node-config.ts → packages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.ts — edited by this change

    • New inactiveRetainedKind(field, node, fields) in flow-node-config.ts — a pure read that reports a field rendered ONLY because the stored-value re-show rule fired. It distinguishes 'controller-off' (a real toggle the author can switch back on) from 'no-controller' (the __legacy__ render-only keys, where no such toggle exists and saying otherwise would be a fresh lie). - FlowNodeConfigField renders the notice beside the affected control, with a "Clear value" action so the author can discard the residue deliberately. Read-only inspectors show the notice without the action. - Coverage is every showWhen group in the inspectors, pinned mechanically: all 33 gated fields across the descriptor tables, plus the two runtime producers (an engine-published configSchema and a connector input schema) that mint groups no source file contains.

.changeset/9278-end-outcome-select.md

  • names packages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.ts → packages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.ts — edited by this change

    packages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.ts declared the end group's outcome key as a free-text box with placeholder: 'success · failure'. EndConfigSchema.outcome is a closed enum of completed | refused defaulting to completed, and FlowNodeSchema discriminates an end node's config through it — so both printed words are refused at parse, not ignored at run time. Measured against the installed @objectstack/spec (17.4.0), with the accepted row in the same output so the refusals are a reading rather than a dead probe:

Read the paragraph, not the line: both false halves of the objectui#8617 claim sat in one paragraph, and correcting either alone would have left it asserting the same wrong thing.

If a claim did go false, correct the body. That is precedented and prose-only, frontmatter untouched; check-changeset-overwrite.mjs will report the correction as its own case 2 ("correcting a declaration on purpose … legitimate"), which is the intended shape — one gate asks for the read, the other records the write.

Not covered, stated so nobody reads this as more: a born-false claim that spells no line address at all (objectui#9495 coordinated one by ORDINAL — "a grep finds that member first" — and deciding that means reading what the sentence means), a claim spelled as a symbol or a package rather than a backticked file name, and a file named ambiguously.

Compared the checked-out tree with b5b6f3753 (merge-base with origin/main): 12 file(s) changed outside .changeset/, read against 1578 pending declaration(s) that publish a body (2166 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3066.8 KB 3104.5 KB
Main entry chunk (gzip) 148.3 KB 350 KB
Entry file index-C-hQw-Wm.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.57KB 6.15KB
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) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.17KB 10.58KB
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.39KB 1.21KB
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.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
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) 554.94KB 132.67KB
core (index.js) 9.57KB 3.81KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.93KB 62.31KB
fields (index.js) 259.29KB 65.64KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 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.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.65KB 14.69KB
plugin-charts (index.js) 82.37KB 22.57KB
plugin-chatbot (index.js) 198.40KB 47.22KB
plugin-dashboard (index.js) 134.11KB 35.62KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 233.23KB 61.74KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 157.44KB 40.24KB
plugin-gantt (index.js) 169.75KB 41.96KB
plugin-grid (index.js) 218.12KB 59.72KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 114.70KB 28.42KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.91KB 9.05KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 87.83KB 22.01KB
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) 116.21KB 38.14KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
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) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: cd8e9713e7d567f6f976f2230b4288f900bf6e80

① Derived judgments

  • Delta 39fbfac..head: one commit, 7 files, +269/−49. M .changeset/10743-studio-screen-visiblewhen-live.md 17/2 · M packages/app-shell/README.md 4/2 · M inspectors/flow-node-config.ts 3/1 · M previews/ScreenPreview.test.tsx 25/1 · M previews/flow-expr-problems.test.ts 60/0 · M previews/flow-expr-problems.ts 36/1 · M previews/screen-spec.ts 124/42. Whole PR: 14 files, +831/−235, 2 commits (REST agrees).
  • One rule — holds. screenPredicateRoots(node) is Object.keys(screenPredicateScope(buildScreenSpec(node), {})) plus the record literal (screen-spec.ts:99-101); measured equal to the renderer's keys plus record on a four-field screen (discount, flag, note, record, tagsField). screenVisibleWhenScopeError is the only judge: unevaluableVisibleWhen (:231-243) and the panel's checkScreenVisibleWhen (flow-expr-problems.ts:76-81) both call it. Grep at head for RECORD_ROOT|screenPredicateScope|screenPredicateRoots|screenVisibleWhenScopeError|collectCelRootIdentifiers|parseCelToAst outside tests: ScreenView.tsx, screen-spec.ts, flow-expr-problems.ts, flow-simulator.ts:326, and requiredInGatedSection.ts (form field rules, a different key). No third copy. The inline cell's FlowExprIssue → findUnknownRefs is the flow-scope checker, not a copy of this rule.
  • Problems panel at head (own probe through flowExpressionProblems): sibling discount above zero → []; record.discount → []; with an assignment upstream writing needsApproval → still []; needsApproval == true → one warning on node review, "Note: needsApproval is not a field on this screen"; {discount} brace → one error, the brace text; user == "x" → warning (the flow checker's allow-list no longer applies); non-string 5 → warning carrying the spec's predicateSlotRefusal text; unlabelled row → prefix "Visible when:". A non-screen slot keeps the flow scope through the same objectList branch: a decision conditions[].expression upstream of the screen warns "Big: discount is not a reference in scope at this step." and is clean on needsApproval; downstream both are clean and nope warns "did you mean note?". Locale zh-CN: the flow wording localizes (未知引用), the screen wording stays English, as the body states.
  • Comprehensions (own battery, 41 predicates): exists, all, filter, map (two- and three-argument), exists_one, nested and chained, all clean over siblings; a shadowed outer t before or after the macro is reported; nested all(u, t == v) reports v only; undeclared receiver tags.exists(...) reports tags; a macro on a declared text field tagsField.exists(t, t == note) is clean at scope (the runner then faults on the string — value, not scope). No false negative found: has(tags.x), size(tags), tags[0], note in tags, map literal {"a": tags}, list literal, ternary, and an rcall argument note.startsWith(prefix) each name the undeclared root; every root collectCelRootIdentifiers reports is still reported except the bound iteration variables. AST as the docblock says: rcall is [name, receiver, [args]] with args[0] the bound id; . is [object, "name"]; call is [name, [args]]; map-literal entries are node pairs, so the generic walk reaches them. parseCelToAst cannot throw (parseCelToAstWithReason wraps the parse) and returned null only for the brace, the truncated discount greater-than and a depth-32 overrun, each already refused by validateExpression one line earlier — so the collectCelRootIdentifiers fallback is live only under A8. Gap outside the rule's claim, pre-existing from round 1: record.zzz above zero is clean on both surfaces (root record admitted) while the runner faults on it (measured).
  • Runner binding re-measured through evalFieldPredicate over screenPredicateScope: discount and record.discount evaluate; user, now, today, env, request, context, self, data, vars, previous, needsApproval all fault. The body's "the runner binds none of those" holds.
  • Placeholder: flow-node-config.ts +3/−1 — the one column entry and a two-line comment above it; stage == "review" 1 → 0. Changesets 6499 and 9278 hold: inactiveRetainedKind exported once at base and head, showWhen 37 at both, outcome still cfg('outcome', 'Outcome', 'select', …) at :502, success · failure only in the :494 comment; both frontmatter md5 unchanged base→head.
  • Pins, own detached worktree at head (offline install; closure dist from a 28/28 turbo cache hit), every run under the verify lock: green leg, 8 files: Test Files 8 passed, Tests 158 passed. Red leg, screen-spec.ts and flow-expr-problems.ts from 39fbfac (blobs d9632ed8ee, 48ee96df28 confirmed) under the head pins: 8 failed | 37 passed (45) — the four panel pins, three comprehension rows, the one-rule pin. A7 isScreenVisibleWhenColumn → false: 4 failed | 41 passed (45). A8 ast = null: 4 failed | 46 passed (50) over three files. Each mutation hit its anchor once and was restored with blob equal to HEAD and git diff HEAD empty. Type-check pnpm --filter @object-ui/app-shell type-check: exit 0, 0 errors.
    ⚠ Re-measured A3 and A6 AT HEAD over the same three round-1 pin files: A3 9 failed (64), not 8 (the comprehension-does-not-hide pin joins); A6 9 failed (64), not 5 (the three comprehension rows and the one-rule pin join). The dev report's "A1–A6 … unchanged" is false for those two; the body's A3/A6 lines hold only at 39fbfac. Understated — the pins are stronger than stated — but the numbers do not hold at head.
  • Inline cell, measured at head with the real FlowObjectListField, the screen fields columns and resolveFlowScope groups (tokens needsApproval, record, previous): one role="note", "discount is not a reference in scope at this step." on note; nothing on reason (needsApproval == true); four placeholders createOpportunity == true; the picker ("Insert a reference") lists needsApproval, record, previous. The "Still on the flow scope" statement is true at head.

② Semver level

  • patch, Clause-②: no — holds. src/index.ts unchanged base→head; no export *; the two new module-level exports (screenPredicateRoots, screenVisibleWhenScopeError) and ScreenView.screenPredicateScope are not on the index. 10743 frontmatter '@object-ui/app-shell': patch; 10692 frontmatter md5 4e8ce55021… unchanged.
  • Server sentence — true against objectstack origin/main 4df101c383: validateScreenInputs (screen-input-contract.ts:137-149) skips required unless visibility(field) === true, so false and undefined both waive it; refuseInvalidScreenInput (engine.ts:7234) still runs at the resume door (:6613). Present verbatim in the changeset's last paragraph and the body's last bullet.
  • "is to be closed on the server side": screen-spec.ts:45. The record restatement: the RECORD_ROOT docblock (:80-88) says the binding happens inside evalFieldPredicate and the name is restated while the bare names are read off the scope — accurate (ScreenView.tsx:96-103 passes scope as both record and the extra roots).
  • Minor, published text, one clause: changeset lines 47-48 still read "so the Studio reads the roots a predicate may name off the renderer's own scope instead of restating them" — record IS restated, and this PR's own docblock says so. Replacement: "so the Studio reads the bare roots a predicate may name off the renderer's own scope; only the record namespace, which the renderer binds inside evalFieldPredicate, is restated by name."
  • The changeset's "Still on the flow scope" paragraph is true but states only the sibling half; the run-variable half (the cell silent where the panel warns and the step errors) is in the body and README only. Optional: append "and says nothing on a run variable the other two report".
  • The divergence is not stated at the divergent site: FlowExprIssue.tsx's docblock still lists "screen visibleWhen" among the cells it serves. Outside the claim; belongs on the follow-up card.
  • "one client evaluator" (changeset :21, screen-spec.ts heading, README :608) stands: the inline cell is a scope hint and a picker, not an evaluator.
  • Replacement body against head: shas, red and green legs, A7, A8, the findUnknownRefs allow-list, the i18n statement, 1732 files on chore: release packages #5400, the AST claim — all hold. Two lines do not, and the seat should correct them when pasting:
    1. Ablations "A3 … → 8 red" and "A6 … → 5 red" hold at 39fbfac, not at head. Replacement: "A3 … → 8 red at 39fbfac, 9 at head (the comprehension-does-not-hide pin joins)"; "A6 … → 5 red at 39fbfac, 9 at head (the three comprehension rows and the one-rule pin join)".
    2. Serial: "(main moved only in four unrelated .changeset/ entries)" is false — by 25c7d584e4 main had moved in four commits and 45 files (packages/core, packages/types, packages/runner, packages/sdui-parser, docs, skills). Replacement: "(no commit on main since the base touches a file this PR changes; the only files in a directory this PR touches are four unrelated .changeset/ entries)".
      Informational: "9 other open PRs" was true at the push; 7 others now (fix(app-shell): switching a report to joined clears the container binding it hides (objectui#10746) #10762 and docs: cite the landing commits where ten objectui issue links answer 404 (objectui#10755) #10766 have merged).

③ Boundary flags

  • CI on head: 43 check runs — 40 success, 3 skipped by design (Test coverage, coverage shard, dependabot). Test shards 1–8, dist pins, Type Check, Lint, README Export Check, Governed Surface Queue Guard, Changeset Declaration, Changeset Claim Re-read, Changeset Bump Policy, Line Citation Gate, Build & E2E: success.
  • git merge-tree --write-tree against current main b5b6f37531 (seven commits past the base, three past the body's 25c7d584e4): clean, exit 0. None of main's commits since the base touches a PR file, metadata-admin/previews/, ScreenView.tsx, FlowRunner.tsx or inspectors/flow-node-config.ts. GitHub: mergeable true, mergeable_state behind.
  • Open PRs: 7 others (10768, 10760, 10759, 10714, 10278, 8941, 5400 — 1732 files over 18 pages), every file list read: no overlap with the 14 files and nothing on the watched paths.
  • Governed predicate: 0 of 14 path(s) governed, 1066 lines under 5000, exit 0 — ordinary queue landing.
  • Draft: true. Labels documentation, tests, package: app-shell; assignee os-elon-musk; card bug, pm:dispatched, priority:p3, domain:ui, area:workflow. Changeset-claim-re-read comment 5855257425 exists and the body answers it.
  • Follow-up card (the dev's out-of-scope finding, real and measured above): screen-field scope groups for the inline fields[].visibleWhen cell, beside objectstack#20178.

Implemented-by: claude/issue-10743-screen-predicate-run-scope
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: PASS

Nothing in the branch stops the landing: all five round-2 items are delivered and measured true at head, the pins are red on 39fbfac and green on head, and the published text is accurate except the one minor changeset clause above. The two body lines under ② are the seat's to correct when pasting the replacement body.

…ble half of the inline-cell statement (objectui#10743)

Prose only, from the round-2 contract review: the Studio reads the bare
roots off the renderer's own scope and restates only `record`, which the
renderer binds inside `evalFieldPredicate`; the inline inspector's
visibleWhen cell also says nothing on a run variable the Problems panel
and the Debug run report. Frontmatter untouched.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3068.1 KB 3104.5 KB
Main entry chunk (gzip) 148.3 KB 350 KB
Entry file index-B1YReD6r.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.57KB 6.15KB
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) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.17KB 10.58KB
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.39KB 1.21KB
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.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
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) 554.94KB 132.67KB
core (index.js) 9.57KB 3.81KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.93KB 62.31KB
fields (index.js) 259.29KB 65.64KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 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.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.65KB 14.69KB
plugin-charts (index.js) 82.37KB 22.57KB
plugin-chatbot (index.js) 198.40KB 47.22KB
plugin-dashboard (index.js) 134.11KB 35.62KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 233.23KB 61.74KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 157.44KB 40.24KB
plugin-gantt (index.js) 169.75KB 41.96KB
plugin-grid (index.js) 218.12KB 59.72KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 114.70KB 28.42KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.91KB 9.05KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 87.83KB 22.01KB
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) 116.21KB 38.14KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
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) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: c06451c56b24204dd3a08155021f0fbdc2f56853

① Derived judgments

  • Delta cd8e971..head: one commit, one file, .changeset/10743-studio-screen-visiblewhen-live.md +5/−4, two hunks (:47-49, :65-69), prose only. Frontmatter (lines 1–3) md5 re-measured 4e8ce55021fe3b14329c8b4cb3f9f354 at both commits. Whole PR: 14 files, +832/−235, 3 commits; REST agrees. No source, test or README byte moved (git diff --stat: the one changeset).
  • Clause :47-49 is round 2's replacement text verbatim and true at head: screenPredicateRoots (screen-spec.ts:97-99) is the keys of screenPredicateScope(buildScreenSpec(node), {}) plus RECORD_ROOT; ScreenView.tsx:96-99 calls evalFieldPredicate(f.visibleWhen, scope, true, undefined, scope, …), and evalFieldPredicate (packages/core/src/evaluator/fieldRules.ts:231) hands its second argument to the engine as record and the fifth as extra — so record is bound by the renderer's call, inside evalFieldPredicate, and restated by name; the bare roots are read off the scope.
  • Append :68 is round 2's optional text verbatim and true at head: the inline cell shows nothing on needsApproval == true while the panel warns and the Debug step errors (round 2's measurement; code byte-identical). The sentence's subject stays the inline note; it reads cleanly.
  • Body: pr-body-10763-r3.md is byte-identical to the pr_body_replacement in card comment 5855550444 (md5 c5b6a8fd8a…, 21221 bytes). Diff r2→r3 is exactly five hunks: the intro (adds "the round-2 contract review (comment 5855514425, PASS)" — that comment exists, carries VERDICT: PASS at cd8e971, and is byte-equal to the saved record), the A3 line, the A6 line, the new Round 3 section with the Gates header, the Serial paragraph. Nothing else moved.
  • A3/A6 lines: round 2's replacement text verbatim; "9 at head", 9 failed | 55 passed (64) each, equals round 2's own measurement at cd8e971 over the same three pin files, and the code is byte-identical, so it holds without re-running.
  • Round 3 section: every statement checked — md5, byte-identical code, the gate list (CI equivalents all green: Changeset Declaration, Claim Re-read, Bump Policy, Overwrite Report, Control Byte Scan, Line Citation Gate), FlowExprIssue.tsx untouched (not in the 14; its docblock :11 still lists "screen visibleWhen", the follow-up's site). Naming shorthand only: check-new-line-citations is the npm script check:new-line-citations, which runs scripts/check-new-cross-file-line-citations.mjs; the dev report uses the script name. Same gate, not false. Gates header "code unchanged at c06451c56b": true.
  • Serial paragraph, re-measured: main is b5b6f37531, unmoved since round 2 (nothing is stale). git log 704e05b09d..main: 7 commits; git diff --name-only: 56 files; .changeset/: six (10735, 10740, 10746, 5987, 6740, 7469); under packages/app-shell/: exactly six, all metadata-admin/, the six the body names (PermissionMatrixEditor.tsx + cloneToCustomize.test.tsx, i18n.ts, permission-set-clone-dispatch.ts, inspectors/ReportDefaultInspector.tsx + joinedClearsBinding-10746.test.tsx); intersection with the 14 PR files: empty. Reading note, informational: four of the six sit in metadata-admin/ itself, which the PR reaches only through inspectors/ and previews/; the sentence is exact under the packages/app-shell/ tree reading and over-inclusive, never false, under the strict same-directory one. No replacement needed. 5400: changed_files 1732, 18 pages at 100 per page — as stated.

② Semver level

  • patch, Clause-②: no — holds unchanged from the round-2 PASS: code byte-identical, src/index.ts not among the 14 files, 10743 frontmatter '@object-ui/app-shell': patch byte-identical, 10692 frontmatter untouched. No published-text inaccuracy remains: the one minor clause from round 2 is corrected verbatim, and the optional append is in.

③ Boundary flags

  • CI on head, waited to completion (11:56Z): 43 check runs — 40 success, 3 skipped by design (Test coverage, coverage shard, dependabot). Test shards 1–8, dist pins, Type Check, Lint, README Export Check, Governed Surface Queue Guard, Changeset Declaration, Changeset Claim Re-read, Changeset Bump Policy, Changeset Overwrite Report, Control Byte Scan, Line Citation Gate, Spec Main Shape Gate, Build & E2E: success.
  • git merge-tree --write-tree refs/review/main refs/review/pr-10763 against b5b6f37531: clean, exit 0, tree 0596ba3c34; merge-base 704e05b09d. GitHub: mergeable true, mergeable_state clean (read behind at 11:45Z, recomputed since).
  • Open PRs: 8 total, 7 others (10768, 10760, 10759, 10714, 10278, 8941, 5400), every page of every /files list read (5400: 1732 files over 18 pages): no overlap with the 14 files; nothing on metadata-admin/previews/, inspectors/flow-node-config.ts, ScreenView.tsx, FlowRunner.tsx, FlowExprIssue.tsx or FlowObjectListField.
  • Draft: true. Assignee os-elon-musk. Labels documentation, tests, package: app-shell; card open, bug, pm:dispatched, priority:p3, domain:ui, area:workflow, same assignee. No new changeset-claim-re-read comment at head (the check ran green); the body's reference to 5855257425 still resolves.
  • Still the seat's: the follow-up card for the inline fields[].visibleWhen cell (and FlowExprIssue.tsx's docblock) beside objectstack#20178, and pasting pr-body-10763-r3.md as the body (no PATCH was made by the dev).

Implemented-by: claude/issue-10743-screen-predicate-run-scope
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: PASS

The delta is the one changeset file, prose only, with both round-2 sentences landed verbatim and true at head; every changed body line re-measures true against head and current main; CI is fully green and the merge is clean. Nothing found that should stop the landing.

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

Labels

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

Projects

None yet

2 participants