Skip to content

feat(core,react): row predicates on runtime record surfaces bind record.* only — the bare-field and data.* spellings retire (objectui#5741) - #7846

Merged
os-justin merged 2 commits into
mainfrom
claude/issue-5741-row-predicate-phase2-retire
Sep 6, 2026
Merged

feat(core,react): row predicates on runtime record surfaces bind record.* only — the bare-field and data.* spellings retire (objectui#5741)#7846
os-justin merged 2 commits into
mainfrom
claude/issue-5741-row-predicate-phase2-retire

Conversation

@os-justin

Copy link
Copy Markdown
Collaborator

Fixes #5741

What this is

Phase 2 of the row-predicate canon ruled on #5330 (2026-08-20, option B), executed exactly as ruled on this card: ruling B of 2026-09-02 (comment 5511170986) as amended on 2026-09-05 (comment 5555359327) — Q1 = A, Q2 = A, Q3 = (i). Runtime record surfaces now bind record.* only; the bare-field shorthand and the data.* root stop being bound there, for both dialects; the Phase-1 warning (PR #5737) goes with the bindings. No survey, no export, no migration rewrite, no sizing (「不考虑存量」). Contract-review tier, Clause-② yes: published runtime behaviour removed. The PR stays draft; needs:contract-review is attached.

Placeholders in this body are spelled as words because GitHub's sanitizer eats angle-bracket shapes (so "a Record of string to any", never the literal generic).

Premise, re-verified on 900f8d99 before editing

  • packages/core/src/evaluator/listConditional.ts:273 built { ...(opts.scope ?? {}), ...rowObj, data: rowObj, record: rowObj }; :317 called warnNonCanonicalRowSpelling(predicateText, rowObj, !opts.rowless, opts.label) inside the CEL guard; the legacy ${…} branch at :298 and the CEL calls at :327 / :329 consumed the same scope.
  • packages/react/src/hooks/useExpression.ts:170 returned { ...row, record: row, data: row }; :251 was the second warning call; :255 served both dialects from one evaluator.
  • rowPredicateCanon.ts exported ROW_PREDICATE_CANONICAL_ROOT (:89), NonCanonicalRowSpelling (:98), detectNonCanonicalRowSpelling (:145), resetRowPredicateCanonWarnings (:208), warnNonCanonicalRowSpelling (:221). declaredPredicate.ts holds no binding and is untouched.
  • The metadata-admin layer is decoupled: views/metadata-admin/predicate.ts buildPredicateCtx binds { data: draft, … } through its own evaluatePredicate, never evalRowPredicate / usePredicateRecordContext. Its data.* pins (predicate.test.ts, SchemaForm.visibleWhen.test.tsx) are the ruled control: byte-for-byte untouched (git diff on that directory is empty) and green in every union below.
  • Today, on the BASE dist: evalRowPredicate on { status: 'active' }record.status true, bare status true, data.status true; on a record-only engine scope the bare and data.* spellings fault [type] Unknown variable: status / Unknown variable: data. Recorded in the measurement script the report names.
  • git log origin/main -- packages/core/src/evaluator packages/react/src/hooks showed nothing after 900f8d99 at dispatch. Five commits landed during the run (origin/main 52cac388, merged here as 14384aa1): they touch packages/core/src/utils/*, packages/react/src/hooks/useNavigationOverlay.ts and changesets — disjoint from this PR. The When bumping @objectstack/spec past objectstack#14075: list-view spec-parity pins need the CalendarConfig titleField-optional update #7122 chain (PR chore(deps): resolve @objectstack/spec at 17.3.0 in the lockfile #7685) and @object-ui/types' Phase-2 ActionCallback surface is a third onSuccess meaning — declared, zod-mirrored, doc-advertised, zero producers #7068 hold none of these paths.

What moved — 17 files, nothing else

Source (3):

  • packages/core/src/evaluator/listConditional.ts — the non-rowless scope is { ...(opts.scope ?? {}), record: rowObj } for BOTH dialect paths (record still pinned after the host spread, objectui#3796, its comment kept); the warnNonCanonicalRowSpelling call and import removed; the evalRowPredicate docblock, the scope / rowless option docs and the two comment blocks now describe the post-Phase-2 truth (record-only, faults, no warning, layer rule).
  • packages/core/src/evaluator/rowPredicateCanon.ts — the runtime warning half deleted (warnNonCanonicalRowSpelling, resetRowPredicateCanonWarnings, the dev gate, the warn-once memo); detectNonCanonicalRowSpelling, ROW_PREDICATE_CANONICAL_ROOT and NonCanonicalRowSpelling stay exported; the canon docblock rewritten. The core barrel export *s this module, so the two deletions are public-export removals — named in the changeset, as the brief asks; git grep across packages/ apps/ examples/ scripts/ content/ skills/ found no importer outside the evaluator's own tests except useExpression.ts and the react useCondition.canonSpelling pin, both on this PR.
  • packages/react/src/hooks/useExpression.tsusePredicateRecordContext returns { record: row }; the {} for the no-row / non-object case stays, and its ledger spelling typeof record !== 'object' || Array.isArray(record) is unchanged, so configBag.pin.test.ts needed no edit (green); the Phase-1 block in useCondition and the two now-unused imports removed; the docblock that declares the two sites one rule keeps the declaration with new text; the useRowPredicate doc updated.

Pins (13) — every pin that held the three-way binding was rewritten to the ruled verdict, never deleted silently; each rewrite cites objectui#5741 in its describe / it text. The brief's list, each line re-verified on this tree, plus five more three-way pins the repo-wide grep found:

  • packages/core/src/evaluator/__tests__/rowPredicateCanon.test.ts — detector pins unchanged; the binding block now pins record.* discriminating, four retired spellings (bare, data.*, ${data.x}, ${x}) at the caller fallback on BOTH rows with both fallbacks driven, the fault warning on both routes, partitionRowsByPredicate, the host's own data left standing, the Phase-1 warning gone, and the two removed exports absent from the barrel.
  • packages/core/src/evaluator/__tests__/rowPredicateCanon.schemaCatalog.test.ts — NEW: the mechanical slice of the corpus sweep (below).
  • packages/core/src/evaluator/__tests__/listConditional.test.ts:41, :97-98, :104, :256-258, :282-286, :436-440 flipped; :480 (rowless, legacy data.tag === 'HOST') needed no flip — a rowless caller keeps the host scope's own data, so its verdict did not move (green, unchanged).
  • packages/react/src/hooks/__tests__/useCondition.canonSpelling.test.tsx — rewritten: the bag shape, record.* on both legs, four retired spellings on the throwing leg (hidden on both rows, reported once naming the variable) and the non-throwing leg (fail-soft true on both rows), the ambient-data consequence, no Phase-1 warning.
  • packages/react/src/hooks/__tests__/useExpression.test.ts — the useRowPredicate bare-field case (:154, whose fallback direction flipped) and a row-present shape pin for the helper.
  • packages/components/src/__tests__/page-header-predicate-dialect.test.tsx :207-216 — bare / data.* hidden on both rows (page:header is fail-closed).
  • packages/components/src/renderers/action/__tests__/action-record-predicate-root.test.tsx — the four generic renderers plus the action:bar overflow: record.* rows kept, bare / data.* re-pinned as "same verdict on both rows" with each site's policy (hidden on the throwing visible legs, shown / greyed / enabled on the fail-soft legs).
  • packages/components/src/renderers/complex/__tests__/data-table-row-action-visible.test.tsx :76-81 — bare role != 'owner' hidden on both rows.
  • packages/app-shell/src/views/__tests__/DeclaredActionsBar.test.tsx:513-521 and :626 re-spelled record.* with new bare-field pins beside them (fail-closed visible: hidden on both rows, reported once; fail-soft disabled: greyed on both rows); the legacy rows :536 / :646 re-spelled ${record.status …}; :703-721 split into the record.* case and the retired pair (app-shell SOURCE untouched).
  • packages/plugin-detail/src/renderers/__tests__/record-alert.rowBinding.test.tsx — group B: shorthand SHOWN on both rows (fail-soft), legacy data.* OFF on both rows (the ambient data: {} answers), the row still wins over an ambient record while a host data is left standing; group C: shorthand no longer gates; the header docblock rewritten. The two sibling record-alert pins import no canon symbol and use only record.* — unchanged.
  • packages/plugin-grid/src/__tests__/predicate-surface-parity.test.tsx :192-193 — bare / data.* expected false on all three surfaces (the parity claim now holds for the retirement).
  • packages/plugin-kanban/src/cardPredicateScope.test.tsx :79-85 — a bare-field formatting condition binds nothing (no style); record.id on the same card is the control.
  • packages/plugin-list/src/__tests__/ListView.test.tsx :2277-2283 — the spec-format formatting case re-spelled ${record.status …}, with a retired ${data.status …} case beside it (no style on either row).

Changeset (1): .changeset/row-predicate-phase2-record-only-5741.md@object-ui/core and @object-ui/react minor (same fixed group); first line is the ruled wording verbatim; then the per-surface fault shapes (Q1 A), the warning removal with the two export names, the detector staying exported, the layer rule, and the release fact: Phase 1 (PR #5737) shipped in @object-ui/core@17.6.0 (npm 2026-08-24, per report 5551793236) while .changeset/row-predicate-record-canon-5330.md is still pending on main, so the next CHANGELOG section reads both phases together.

Before / after, measured on the built dist (BASE 900f8d99 versus this branch)

surface predicate before: matching row / other row after: matching row / other row fault report after
evalRowPredicate (row kebab, selection bar, page:header, conditional formatting) record.status == 'active' true / false true / false none
same bare status == 'active' true / false false / false (the caller's fallback; true / true with fallback: true) once: Unknown variable: status, with the record. hint on the fast route
same data.status == 'active' true / false false / false once: Unknown variable: data
same ${data.status === "active"}, ${status === "active"}, status === 'active' (legacy path) true / false false / false [legacy] … is not defined when warnOnError
partitionRowsByPredicate over [active, closed] bare or data.* 1 eligible / 1 skipped 0 eligible / 2 skipped as above
useCondition, throwing legs (action:button and action:menu visible, the action:bar overflow, DeclaredActionsBar visible) bare, data.*, ${data.x}, ${x} shown / hidden hidden / hidden once: was hidden/disabled: its predicate threw — … status is not defined
useCondition, non-throwing legs (action:icon and action:group visible, every disabled / enabled, record:alert) same four discriminates fail-soft true on both rows (shown / greyed / enabled) the evaluator's own Failed to evaluate expression line
any useCondition leg under a host scope that carries data (app-shell's ambient data: {}) data.status == 'x' discriminates (the row won) constant false on both rows, silent none — not a fault: the host's own data answers
metadata-admin SchemaForm / predicate.ts (the control) data.type == 'list' gates gates (untouched) n/a

The fault warning after the removal — verified, reported, not rewritten

  • Fast route (evalFieldPredicate's one-time warning in fieldRules.ts): names the variable and carries the hint — A conditional predicate failed to evaluate (row action "approve") and was treated as its safe default (false): "amount > 5". Reason: [type] Unknown variable: amount … Values are bound under 'record.' (e.g. record.status). Pinned.
  • Fail-closed route (warnEvalError, warnOnError: true): names the variable, no record. hint — … Reason: [type] Unknown variable: data … Check the field names and CEL syntax. Pinned; report line, not a rewrite.
  • useCondition throwing legs: … was hidden/disabled: its predicate threw — Failed to evaluate expression "status == 'in_review'": status is not defined. Predicate: …. Pinned.
  • Report line for the seat: under a host scope that carries its OWN data — app-shell's ExpressionProvider mounts data: {} on every record page — a data.* row predicate does not fault at all: it reads the host's object and is a constant false with no console line (the pre-finding: record:alert 是唯一没接 #4075/#4077 行绑定的谓词面 —— 裸 { record } 作用域下 row-action 简写与 data.* 拼法 unbound,fail-soft 使作者门控的横幅永远显示 #4807 record-alert polarity, now by ruling). A detector there would be the B / C behaviour the ruling rejected, so it is pinned as measured (useCondition.canonSpelling, record-alert.rowBinding) and stated in the changeset and the canon docblock.

Docs — zero diff, as expected

git grep over content/docs for the Phase-1 prose (deprecated spelling, stored-metadata survey, deprecation window, non-canonical) hits only guide/console-architecture.md:183, which is about something else; the one data.* CEL teaching hit, core/enhanced-actions.mdx:245 (condition: 'data.amount > 1000'), is an ActionRunner condition evaluated on the runner's own context, not a row surface. skills/** is governed and carried by #7728; the metadata-admin editor by #7727; content/docs/releases/ untouched.

Corpus sweep — PR #5758's recipe, re-run on this tree

Key-agnostic string-literal scan (every literal carrying a comparison / boolean operator), classified by CEL root with @objectstack/formula's collectCelRootIdentifiers / firstUndeclaredReference and by detectNonCanonicalRowSpelling. 1,016 files, 82,981 literals, 296 ms; identical counts on 14384aa1.

root files string literals
content/docs 203 19,715
examples (incl. schema-catalog) 489 25,414
apps 237 24,052
packages/*/README.md 43 7,556
skills 27 3,487
docs 17 2,757

1,315 predicate-shaped strings; roots: ${…} legacy 141, record 77, data 4, bare undeclared 79. Positive controls: record. 77 and data 4 both reached; the classifier reports bare-shorthand / metadata-layer-root on the control spellings and nothing on the canon / host-scope ones.

Every non-canonical hit read in source:

  • CEL data.* (4): apps/console/src/components/FormPage.fieldSpec.test.ts:127 is a visibleOn type-face fixture (schema / widget tier, where data is the form scope); content/docs/components/data-display/tree-view.mdx:59 is prose (bind || nodes || data); skills/objectui/guides/schema-expressions.md:220 and :232 are the visibleOn / hiddenOn tier on finding(skills): skills/objectui/guides/schema-expressions.md teaches the bare-field and data.* row spellings as "deprecated and warned once in dev" — stale once #5741 (Phase 2) retires them #7728's governed surface.
  • Legacy ${data.x} with an operator (28): content/docs teaching pages, apps/console fixtures and the ActionRunner condition examples — the ${…} dialect on schema / widget nodes and runner contexts, the class PR docs(plugin-form): Phase 0 of the row-predicate deprecation — stop teaching the bare shorthand (#5738) #5758 stood down on; none is a row surface.
  • Bare undeclared (79): flow-tier trigger / decision / validation conditions in apps/console/src/preview-samples.ts (condition: 'status == "active"' on a flow start trigger, criteria: "status == 'Closed'" on a workflow rule, amount, discount, daysToExpiry), formula-field expressions (content/docs/fields/formula.mdx:68-69), a view filter described in prose (guide/building-crud-app.md:342), diagnostics examples with a deliberate unknown root (guide/metadata-diagnostics.md:181-182), JS expressions in test files (children || body, length > 0), UI strings ("Open in GitHub"), CHANGELOG entries and ADR prose (docs/adr/0036). None is a runtime record surface.
  • One pre-existing doc defect of a DIFFERENT tier surfaced: fieldTabs[].visibleWhen: 'status == "won"' in content/docs/plugins/plugin-form.mdx:173 and packages/plugin-form/README.md:446 — the fieldRules tier binds record only and always did, and a broken tab predicate fails open. Filed as objectui#7834; not edited here.
  • schema-catalog (examples/schema-catalog/src/schemas, 432 JSON documents, 7,302 strings): 20 predicate-shaped — 4 record.*, 1 current_user.*, 14 display text that does not parse as CEL — 0 data.*, 0 legacy, 0 bare. Clean.

Pin decision: the full recipe is deterministic and fast but not MECHANICAL — 79 of its hits need tier judgement — so it stays this reading. Its mechanical slice IS landed: rowPredicateCanon.schemaCatalog.test.ts sweeps the catalog JSON from disk (rooted at the test file per objectui#7799, enumerated rather than listed, no new dependency edge — @objectstack/formula is already core's), with a positive control (documents present, parsed CEL predicates present) and a classifier control.

Gates — exit captured before any pipe, verdict lines quoted, head 14384aa1 unless noted

gate exit its own verdict line
pnpm --filter @object-ui/core build 0 dist completeness: 1 package(s) complete (192 emitted files verified)
pnpm --filter @object-ui/react build 0 dist completeness: 1 package(s) complete (130 emitted files verified)
pnpm exec vitest run --maxWorkers=2 over packages/core/src/evaluator/, packages/react/src/hooks/, configBag.pin.test.ts, the six consumer pin files, packages/plugin-detail/src/renderers/__tests__/, and the two metadata-admin control pins 0 Test Files 79 passed (79) · Tests 1357 passed (1357)
pnpm exec vitest run --maxWorkers=2 packages/react/ packages/app-shell/src/views/metadata-admin/ packages/components/src/renderers/action/ 0 Test Files 313 passed (313) · Tests 3466 passed, 1 skipped (3467) (lock held 7m01s)
pnpm --filter @object-ui/core type-check · react · components · app-shell · plugin-detail (each tsc --noEmit && tsc -p tsconfig.test.json, against the rebuilt dists) 0 × 5 0 error TS in each log; script names echoed
pnpm --filter @object-ui/core lint · pnpm --filter @object-ui/react lint 0 · 0 0 errors, 516 warnings · 0 errors, 347 warnings (on 14384aa1; warnings are not CI-blocking and are the files' own — see the narrowing note)
node scripts/check-changeset-presence.mjs 0 16 source file(s) of 8 released package(s) changed, and this change declares 1 changeset(s)
check-changeset-fixed · check-changeset-no-major · check-changeset-overwrite 0 · 0 · 0 privatePackages declared · No changeset declares a major bump · No pre-existing changeset was modified or deleted
node scripts/check-control-bytes.mjs 0 OK (scanned 6367 tracked text file(s); skipped 85 binary)
check-unreferenced-sources · check-vi-mock-specifiers · check-vi-mock-inherit · check-shell-escape-residue 0 × 4 each prints its own OK line
node scripts/check-readme-exports.mjs NOT MEASURED prerequisite not met locally: 75 self-import(s) could not be judged … type entry ./dist/index.d.ts is not on disk for app-shell / cli / plugin-ai / plugin-gantt (unbuilt here); it names none of this PR's files or symbols, and no README imports the two removed exports (git grep). CI builds the tree.
governed-surface predicate (node scripts/pm/check-governed-merges.mjs --test on the 17-file list, from objectstack) 0 0 of 17 path(s) hit the register … NOT governed

Lint, declared narrowing: repo-wide pnpm lint was not run. Population: eslint.config.js enables no type-aware linting (no parserOptions.project / projectService), so this diff cannot move any untouched file's verdict; the PR's own 16 code files were linted with --format json — 16 files, 0 errors, 78 warnings, run the way CI's turbo run lint runs it (package-level eslint ., no --no-inline-config). On useExpression.ts the 9 no-explicit-any warnings are the BASE file's own: eslint on the BASE blob via stdin at the same path reports 9, the current file reports 9.

Ablation — Clause-② yes

Run on 6e9c7a6e, the implementation commit; the merge left both mutated files byte-identical (git diff 6e9c7a6e 14384aa1 on the two paths is empty). vitest resolves @object-ui/core and @object-ui/react to src through the vitest.config.mts aliases (:408, :413), so no dist is on the resolution path and no rebuild leg applies. Each mutation was proven on disk before the run (grep -c injected = 1, removed = 0, and the blob hash) and restored with git checkout HEAD -- path under a trap on EXIT / INT / TERM; restoration proven by git diff HEAD empty and git hash-object equal to the HEAD blobs (62f0181b… for listConditional.ts, 320a8da7… for useExpression.ts).

  • Leg A — the core scope bag alone, back to { ...scope, ...rowObj, data: rowObj, record: rowObj }: Test Files 4 failed | 1 passed (5) · Tests 18 failed | 110 passed (128). Every red is a retired-spelling pin (rowPredicateCanon ×9, listConditional ×5, predicate-surface-parity ×2, page-header ×2); every record.* pin green; the react tier's file untouched and green, because the hook is the second, independent site.
  • Leg B — the react hook alone, back to { ...row, record: row, data: row }: Test Files 5 failed | 1 passed (6) · Tests 38 failed | 122 passed (160). Every red is a retired-spelling pin (useCondition.canonSpelling, the useExpression shape pin, action-record-predicate-root ×20, DeclaredActionsBar ×3, record-alert.rowBinding ×4); every record.* pin green; core's rowPredicateCanon file green.

Out of scope, filed


Generated by Claude Code

…rd.* only — the bare-field and data.* spellings retire (objectui#5741)

Phase 2 of the row-predicate canon ruled on objectui#5330 (2026-08-20, B) and
executed as ruled on objectui#5741 (2026-09-02, amended 2026-09-05: Q1 = A,
Q2 = A, Q3 = (i)).

- `evalRowPredicate`'s non-rowless scope bag is `{ ...hostScope, record: rowObj }`
  for both dialect paths — no bare-field spread, no `data`; `record` stays pinned
  after the host spread (objectui#3796).
- `usePredicateRecordContext` returns `{ record: row }`; the empty bag for the
  no-row case stays.
- The Phase-1 warning goes with the bindings: both `warnNonCanonicalRowSpelling`
  calls, their imports, and the runtime half of `rowPredicateCanon.ts`
  (`warnNonCanonicalRowSpelling`, `resetRowPredicateCanonWarnings`) are removed.
  `detectNonCanonicalRowSpelling`, `ROW_PREDICATE_CANONICAL_ROOT` and
  `NonCanonicalRowSpelling` stay exported as the offline instrument.
- A retired spelling faults exactly as it already did on the server; each surface
  applies its existing fault policy. No runtime detector, no "as absent" case.
- Pins flipped to the ruled truth at every surface that held the three-way
  binding; a mechanical pin over the schema-catalog corpus lands beside the
  detector pins; docblocks describe the post-Phase-2 truth.
- Changeset: `@object-ui/core` + `@object-ui/react` minor, ruled wording.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BAZFhALsQsGqxui8sNqM8s
@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) 3186.8 KB 3191.4 KB
Main entry chunk (gzip) 143.2 KB 350 KB
Entry file index-BZwfQVWO.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) 510.60KB 116.20KB
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) 242.44KB 61.25KB
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) 4.28KB 1.75KB
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.98KB 10.98KB
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.75KB 3.80KB
plugin-calendar (index.js) 47.87KB 13.31KB
plugin-charts (index.js) 70.92KB 19.75KB
plugin-chatbot (index.js) 196.19KB 46.37KB
plugin-dashboard (index.js) 132.88KB 34.69KB
plugin-designer (index.js) 212.86KB 43.19KB
plugin-detail (index.js) 250.55KB 64.06KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 132.87KB 32.66KB
plugin-gantt (index.js) 167.26KB 41.00KB
plugin-grid (index.js) 209.29KB 56.78KB
plugin-kanban (index.js) 52.71KB 14.55KB
plugin-list (index.js) 113.28KB 27.59KB
plugin-map (index.js) 20.44KB 6.78KB
plugin-markdown (index.js) 13.93KB 4.81KB
plugin-report (index.js) 43.59KB 11.97KB
plugin-timeline (index.js) 30.84KB 8.85KB
plugin-tree (index.js) 9.20KB 3.19KB
plugin-view (index.js) 85.24KB 20.94KB
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) 5.41KB 2.34KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 4.93KB 2.24KB
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) 10.35KB 3.60KB
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

needs:contract-review cleared (2026-09-06T00:14Z) — in-seat contract review PASS at CONTRACT_REVIEW_TIER on head 14384aa1, recorded on the card as objectui#5741's review comment of 00:14Z (Implemented-by: the dev of session_01BAZFhALsQsGqxui8sNqM8s's dispatch on this branch; Reviewed-by: session_01BAZFhALsQsGqxui8sNqM8s, served tier claude-fable-5-1). Label write: read-current [package: core, package: react, package: components, plugin, plugin: kanban, tests, package: app-shell, needs:contract-review] → write the same set without the gate → read-back follows. Ready flip and auto-merge (SQUASH) follow once every check run on 14384aa1 is completed: success; a head move re-hangs the label and re-reviews the delta.


Generated by Claude Code

@os-justin
os-justin marked this pull request as ready for review September 6, 2026 00:24
@os-justin
os-justin added this pull request to the merge queue Sep 6, 2026
Merged via the queue into main with commit 83fe6e7 Sep 6, 2026
34 checks passed
@os-justin
os-justin deleted the claude/issue-5741-row-predicate-phase2-retire branch September 6, 2026 00:59
os-sam pushed a commit that referenced this pull request Sep 6, 2026
…29 mock factories (objectui#7337)

A `vi.mock('@object-ui/i18n', () => ({ … }))` factory that hand-lists its
exports freezes the mock to the names that existed the day it was written.
The next module-scope read of a new export kills the importing file during
COLLECTION, so the suite reports zero failed assertions and the red lands on
an innocent test file.

- convert 29 frozen factories to the obtain-and-spread form;
- delete `apps/console/dev/__tests__/setup/common-mocks.ts`, a mock helper
  with zero importers repo-wide carrying the same shape;
- fix the recogniser's nested-generic blind spot: `<[^>]*>` stopped at the
  first `>`, so `vi.importActual<Record<string, unknown>>(…)` went unmatched
  and four factories that obtain AND spread the real module were reported as
  frozen. Repo-wide across all 21 workspace specifiers: 349 frozen -> 344,
  with no site moving the other way;
- pin the failure mode itself: three nested vitest runs over a throwaway
  package show the frozen factory dying at collection, the converted one
  passing, and a lazy read failing as an ordinary assertion.

`COVERED_SPECIFIERS` is deliberately NOT widened: one frozen factory remains
in `DeclaredActionsBar.test.tsx`, held by open PR #7846, and flipping the
ratchet while it exists turns `main` red on merge.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDq78vMMSzCGWGmhUYBabh
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.

Carrier for Phase 2 of the row-predicate deprecation: the stored-metadata survey that must size the removal window before any spelling retires

2 participants