Skip to content

fix(app-shell): flow expression notes, the Problems panel and the add-widget picker read the designer locale (objectui#10804) - #10832

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-10804-designer-notes-locale
Sep 27, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-10804-designer-notes-locale

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #10804
Clause-②: no — the locale is passed and rows are added; each en row equals today's literal, and no declared surface or stored value moves.

objectui#10805 is folded into this card and rides here. Dispatched by the domain:ui seat 2 PM (claim 5858412835, session session_014mXUNuFomfj24w7s1pZzhN).

What changed

All under packages/app-shell/src/views/metadata-admin/.

  • Site 1 — FlowExprIssue. flowScopeNote passes the designer locale it already holds to describeUnknownRefs, as the siblings FlowEdgeInspector and FlowNodeConfigField do.
  • Site 2 — previews/flow-expr-problems.ts. checkCel passes its locale to validateExpressionClient('predicate', value, locale). checkScreenVisibleWhen takes the locale from flowExpressionProblems(draft, locale) and passes it to validateExpressionClient and to screenVisibleWhenScopeError.
    • Bounded in-place fix in the same file, same class and same gates: a row with no label of its own was prefixed by the English column label (Visible when: …). The fields are now read through localizeFlowFields, the localizer the inspector already uses, so the prefix is the column label the author sees. Evidence is in H1 below.
  • Site 3 — new engine.* rows.
    • FlowObjectListField's screenScopeGroups heading reads engine.flowScope.group.screenFields (en Screen fields).
    • previews/screen-spec.ts's screenVisibleWhenScopeError gains an optional locale parameter, as describeUnknownRefs has one. The undeclared-root sentence reads engine.flowRef.notAScreenField and engine.flowRef.notAScreenFieldWithSuggestion; each en row is the literal it replaces.
  • Site 4 — the add-widget picker (objectui#10805).
    • AddWidgetPicker reads the designer locale (useMetadataLocale, as FlowExprIssue does) for its search box, empty text, category headings and type names: engine.widgetPicker.*, with en equal to today's literals.
    • The reader is split in previews/widget-types.ts. WIDGET_TYPE_META[].labelKey is what the picker shows. WIDGET_TYPE_META[].label stays the English the stored default New TYPE title is written from. WIDGET_CATEGORY_LABEL became WIDGET_CATEGORY_LABEL_KEY, and its only reader was the picker. The stored type does not move.
    • The zh chart names reuse the packages/i18n widgetType terms (柱状图, 条形图, 折线图, 面积图, 饼图, 环形图, 散点图, 漏斗图, 透视表).
  • One changeset, .changeset/10804-designer-notes-locale.md ('@object-ui/app-shell': patch).

H1 — reproduced on the base, under zh-CN, through the real components

This is a probe run with the base's sources (fb91ac9b0c). It mounted FlowPreview (Problems panel open) and FlowNodeInspector on a screen node, both with locale zh-CN.

  • Problems panel, every expression message English:
    • checkCel, an edge guard: "Unbalanced parentheses in (amount > 1."
    • checkScreenVisibleWhen, a brace: "Reason: It looks like a {discount} template brace was used inside a condition — …"
    • screen visibleWhen scope: "Approval: needsApproval is not a field on this screen"
    • an unlabelled row: "Visible when: needsApproval is not a field on this screen"
    • the parse refusal: "Code: invalid CEL predicate: Unexpected token: EOF … — predicates are bare CEL (e.g. record.rating >= 4)."
  • The inline cell on the same draft showed its brace alert in zh (条件中似乎用了模板花括号 …), while the panel showed the same message in English.
  • Screen visibleWhen cell notes: "needsApproval is not a field on this screen", and the same parse refusal in English.
  • The pins' red run on the base (quoted from the failure diffs):
    • FlowExprIssue: "Unknown reference amout — did you mean amount?"
    • screen picker heading: "Screen fields"
    • dicount note: "dicount is not a field on this screen (did you mean discount?)"
    • picker placeholder: "Search widgets…"
    • picker headings: "Single value" / "Charts" / "Tabular"
    • picker entries: "Metric (KPI)" …
    • picker empty text: "No matches."
  • Control: the sibling FlowEdgeInspector named the same amout reference in zh on the base (未知引用 amout —— 是否想用 amount?). The pin keeps it as a lit control, and it was green on the base.
  • On the head, the same probe reads "(amount > 1 中的圆括号不配对。", "Reason: 条件中似乎用了模板花括号 …", "Approval: needsApproval 不是此页面上的字段" and "显示条件: needsApproval 不是此页面上的字段".

H2 — the screen-spec.ts seam

screenVisibleWhenScopeError(visibleWhen, node, locale?): an optional trailing locale, the describeUnknownRefs shape. An absent locale reads the en rows, so any caller that passes none renders exactly what it did. The call sites:

  • the inline cell, FlowExprIssue: screenVisibleWhenScopeError(value, screenNode, locale). The component already held locale from useMetadataLocale() for validateExpressionClient.
  • the Problems panel, checkScreenVisibleWhen(value, node, locale): screenVisibleWhenScopeError(value, node, locale). flowExpressionProblems prefixes the message with the row's own label, or the column label when the row has none. The card's Reason: is such a row label: author data, which passes through.
  • the Debug run's screen step, unevaluableVisibleWhen(node) in flow-simulator.ts: it passes no locale and still renders the en rows, inside that step's English sentence frame. See the Acceptance notes.

H3 — every reader of WIDGET_TYPE_META[].label

Reader Reads Class
AddWidgetPicker: the entry's name and the search filter label before, now labelKey display
DashboardDefaultInspector.addWidget label, into New TYPE (and the selection label echoing that stored title) stored default
DashboardPreview.handleAddWidget label, into New TYPE (same) stored default
DashboardDefaultInspector's widget rows, DashboardPreview's selected-widget header .icon only not a label reader

No display-only reader exists outside the picker, so nothing else was localized. Both stored-default readers keep writing English.

The search matches the name the entry shows, and the stored type id printed beside it. It does not match the English name the entry hides.

  • Why: the box filters what is on screen. A zh author types the word they see (柱状图), and the id keeps an ASCII handle (bar, pivot).
  • Matching the hidden English would keep entries for a reason nothing on screen shows: under zh, chart would keep five entries that show no such word.
  • Under en the displayed name is the English one, so en search behaves exactly as before.

Pins and ablation

The new file is inspectors/inspectorStrings.i18n-10804.test.tsx, on the objectui#10696 / objectui#10748 harness: the rendered text of the real component, zh read back from the catalogue and guarded by zhRow.

  • One zh pin per site, each with an en control:
    • FlowExprIssue (decision branch);
    • the screen visibleWhen picker heading;
    • the screen visibleWhen note (both keys);
    • the Problems panel: checkCel on an edge, the screen brace, the screen scope, and the unlabelled-row prefix;
    • the add-widget picker: search box, headings, every type, empty text, and search by the displayed name.
  • Lit controls: the FlowEdgeInspector sibling, engine.inspector.flowNode.kind, the Problems panel title, and the add-widget trigger.
  • en controls for new rows assert the literal English, so they hold on the base too.
  • Stored-title control, zh and en: picking the bar entry writes type: 'bar' and title: 'New bar chart'.

Readings (the pin file held at HEAD):

  • Base sources (fb91ac9b0c, restored by trap; git diff HEAD 0 bytes after): Tests 12 failed | 9 passed (21). The 12 are exactly the zh site cases. The en controls, the lit controls and the stored-title control passed.
  • Head 4024c9b826: Tests 21 passed (21).
  • Ablation, via ablation-replace.mjs. It dropped the locale at site 1: describeUnknownRefs(unknown, locale) became describeUnknownRefs(unknown), anchor x1 → x0, blob 772b9c8255 → e8abd2db37.
    • Result: Tests 1 failed | 20 passed (21). The one failure is "FlowExprIssue — … zh: the unknown reference reads engine.flowRef.unknownWithSuggestion", received "Unknown reference amout — did you mean amount?".
    • The FlowEdgeInspector lit control stayed green.
    • Restored: blob == HEAD (772b9c8255), and git diff HEAD is empty.

Gates (HEAD 4024c9b826)

  • Related suites: the 42 packages/app-shell test files that import a touched module or its mounting components, in two batches. Test Files 21 passed (21) · Tests 275 passed (275) and Test Files 21 passed (21) · Tests 295 passed (295).
    • Declared narrowing: the whole metadata-admin + studio-design directory run did not finish inside the 570-second foreground cap (killed, no verdict), so the full package suite is left to CI.
  • pnpm --filter @object-ui/app-shell type-check (tsc --noEmit && tsc -p tsconfig.test.json): exit 0, after building the dependency closure turbo run build --filter='@object-ui/app-shell^...' (28/28 tasks). --listFiles counts the new pin once in tsconfig.test.json.
  • eslint on the 8 changed files:
    • Every file is inside eslint's config (--print-config exit 0 for each).
    • --format json: 8 files, 0 errors, 1 warning. The warning is react-hooks/refs in FlowObjectListField, on a line this diff does not touch.
    • No type-aware linting is configured, and no eslint-rules/ rule reads the disk, so this diff cannot move any verdict on an untouched file. The repo-wide lint is CI's.
  • check:i18n-designer-parity 0 ("Every en row has a zh row, and every shared row carries the same placeholders.").
  • check:i18n-drift 0 ("No designer-table en value changed in this range.").
  • check:i18n-keys 0.
  • check:designer-field-key-parity 0.
  • check:new-line-citations 0 (0 new).
  • check:control-bytes 0.
  • check-changeset-presence 0.
  • check-changeset-no-major 0.
  • check-changeset-fixed 0.
  • check:vi-mock-specifiers 0, check:vi-mock-inherit 0, check-vi-mock-override-shape 0.
  • check-test-path-roots 0.
  • check:unreferenced-sources 0.

Serial

  • At dispatch (18:05Z, ff94a12d24), no open PR touched these files.
  • This PR does not touch ViewPreview.tsx (PR objectui#10821) or inspectors/datasetFilterCondition.ts (seat 4, objectui#10813).
  • Before opening, the branch was dry-merged onto a fresh origin/main fda49e557b: git merge-tree --write-tree exit 0, no conflicts. metadata-admin/ is unchanged between the base and that main.

Acceptance notes

  • The stored default title is left literal on purpose (the objectui#10678 / objectui#10651 ruling). It is not re-ruled here.
    • DashboardDefaultInspector.addWidget and DashboardPreview.handleAddWidget keep writing New ${label.toLowerCase()} in English under every locale.
    • They are the two other WIDGET_TYPE_META[].label readers, and both are stored-default. Neither is a display-only reader, so neither was changed.
  • The card's "plus its parse-refusal text" names no literal in this repo. screenVisibleWhenScopeError returns producer text there:
    • @objectstack/spec's predicateSlotRefusal for the shape;
    • @objectstack/formula's validateExpression and collectCelRootIdentifiers for the parse, which embed the CEL engine's own diagnostic ("invalid CEL predicate: Unexpected token: EOF …").
    • Keying it here would mean a consumer-side match over producer prose, or replacing the producer's English. So it passes through as it is, in every locale, and the new rows' comment says so.
  • The Debug run's screen step (flow-simulator.ts) composes unevaluableVisibleWhen's reasons into an English sentence ("A screen field's visibleWhen may reference only the fields declared on this screen; …"). It is outside this file surface. Its reasons stay on the en rows.
  • AddWidgetPicker's label = 'Add widget' prop default is unreached: both callers pass the translated engine.inspector.add.widget. It is left as is.
  • zh wording. Screen fields became 页面字段: the screen node is 交互页面 and its fields help says 此页面. Data table became 数据表格, and Metric (KPI) became 指标 (KPI).

Generated by Claude Code

…e designer locale

The flow-scope note under a repeater cell, the Problems panel's expression
messages and the screen visibleWhen note now read the designer locale; the
Screen fields heading and the add-widget picker's words read new engine.*
rows whose en text is the literal they replace. A new widget's default
title keeps writing the English label (stored author data).

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 4 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/10743-studio-screen-visiblewhen-live.md

  • names flow-expr-problems.ts → packages/app-shell/src/views/metadata-admin/previews/flow-expr-problems.ts — edited by this change

    • buildScreenSpec keeps every authored field and carries its visibleWhen raw, as the runtime screen executor sends it to the client. ScreenView — the renderer the flow runner uses — decides it live over the screen's declared fields and the values being collected, so ticking the sibling checkbox or typing a discount above zero reveals the field in the preview exactly as it does for the end user, and the "N fields hidden by their visible when condition" hint follows the values. - The Studio no longer has an evaluator of its own for this key: isFieldVisibleWhen, fieldVisibility and the simulator's evalVisibleWhen are removed. buildScreenSpec takes no variables; hiddenFieldCount takes the built spec and the collected values. - The Debug run's screen step judges SCOPE, never value: unevaluableVisibleWhen names, as an error on the paused step, a field whose predicate references an identifier that is not a field declared on this screen (a run variable such as needsApproval == true, the runtime's vars.* root), does not parse as CEL (a {var} brace), or is a shape registerFlow refuses — with the nearest declared field where one is close. A predicate over a sibling field (discount > 0, record.discount > 0) is not reported; it used to be named as "hidden because it could not be evaluated". A comprehension's iteration variable (["a","b"].exists(t, t == note)) is bound, not a root, and is not reported. - The Problems panel (flow-expr-problems.ts) judges the screen node's fields[].visibleWhen column by that same rule instead of the flow scope: it used to warn that discount "is not a reference in scope at this step" on the sibling-field shape and stay silent on a run variable; it now warns on the run variable (needsApproval is not a field on this screen) and is silent on the sibling. Every other expression slot keeps the flow scope. - The screen field inspector's visibleWhen placeholder is a sibling-field example (createOpportunity == true); it used to read stage == "review", a run-variable shape. - ScreenView exports screenPredicateScope, unchanged, 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.

.changeset/6310-designer-formula-key-retired.md

  • names packages/app-shell/src/views/metadata-admin/i18n.ts → packages/app-shell/src/views/metadata-admin/i18n.ts — edited by this change

    Not touched: designer.field.formula ('Formula (CEL)') in packages/app-shell/src/views/metadata-admin/i18n.ts, a different and live key belonging to metadata-admin's ObjectFieldInspector — the surface that still authors formula expressions.

.changeset/7125-dashboard-empty-state-keys-retired.md

  • names packages/app-shell/src/views/metadata-admin/i18n.ts → packages/app-shell/src/views/metadata-admin/i18n.ts — edited by this change

    Not touched: table.noRows ('No rows to display') and engine.form.noRows (packages/app-shell/src/views/metadata-admin/i18n.ts, read at widgets.tsx) — two different, same-named keys in different namespaces. Nor the comments in WidgetEmptyState.tsx, DatasetWidget.tsx, ObjectDataTable.tsx and PivotTable.tsx that record WHY three widgets with three strings became one shared empty state; the packs' own comment keeps that rationale and now names the retirement instead of a row that is gone.

.changeset/8632-malformed-picklist-option-loud.md

  • names views/metadata-admin/i18n.ts → packages/app-shell/src/views/metadata-admin/i18n.ts — edited by this change

    Two new strings land in the designer's own en / zh tables — the metadata-admin console owns its strings in views/metadata-admin/i18n.ts and is deliberately outside the ten locale packs (packages/i18n/README.md, "Scope — the engine.* carve-out").

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 fda49e557 (merge-base with origin/main): 8 file(s) changed outside .changeset/, read against 1621 pending declaration(s) that publish a body (2210 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3079.1 KB 3104.5 KB
Main entry chunk (gzip) 148.3 KB 350 KB
Entry file index-DwQ2T_IY.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) 556.80KB 133.32KB
core (index.js) 9.92KB 3.93KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 224.95KB 62.49KB
fields (index.js) 260.77KB 66.22KB
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.67KB 14.70KB
plugin-charts (index.js) 83.58KB 22.75KB
plugin-chatbot (index.js) 197.67KB 46.90KB
plugin-dashboard (index.js) 134.64KB 35.75KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 233.12KB 61.68KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 158.35KB 40.47KB
plugin-gantt (index.js) 169.83KB 41.99KB
plugin-grid (index.js) 218.66KB 59.90KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 114.78KB 28.44KB
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.64KB 3.75KB
plugin-view (index.js) 87.89KB 22.03KB
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.81KB 3.58KB
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) 119.16KB 39.05KB
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.84KB 1.90KB
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: 4024c9b82619794df7fa8c7ea1a5cac047ec52c2

① Derived judgments

Sites 1 and 2: correct and complete.

  • FlowExprIssue.flowScopeNote passes locale to describeUnknownRefs and to screenVisibleWhenScopeError.
  • checkCel calls validateExpressionClient('predicate', value, locale).
  • checkScreenVisibleWhen(value, node, locale) threads the locale to both callees.
  • The Problems panel adds no frame of its own: flow-problems.ts forwards ep.message unchanged.

Site 3: correct.

  • screenScopeGroups(node, locale) reads engine.flowScope.group.screenFields.
  • screenVisibleWhenScopeError gains an optional trailing locale and formats with tFormat, which uses a replacer function, so $ patterns in the arguments are inert.
  • The en rows are byte-equal to the literals they replace.
  • The Debug run's unevaluableVisibleWhen passes no locale, so it stays on the en rows. That is unchanged English.

zh rows.

  • The Chinese is correct and the placeholders match. 页面字段 / 此页面 match the screen node's name, 交互页面.
  • Nine type names reuse packages/i18n widgetType exactly.
  • metric becomes 指标 (KPI) and table becomes 数据表格. Each follows its own en row; neither is a chart name, and both are disclosed in the PR body. So a zh author who picks 数据表格 later sees 表格 in the widget inspector. Not blocking.
  • All 16 engine.widgetPicker.* en rows are byte-equal to the literals they replace.

Reader split: sound.

  • The AddWidgetPicker entry name and search read tr(t.labelKey, locale), which is the display.
  • DashboardDefaultInspector.addWidget and DashboardPreview.handleAddWidget write New ${meta.label.toLowerCase()}, which is stored.
  • The other readers read .icon only.
  • No stored write reads a localized string, no display reads .label, and the .label values are byte-unchanged.
  • Search matches the stored id and the displayed name, never hidden English. It is pinned under zh.

Parse-refusal ruling: true on the card's surfaces.

  • screenVisibleWhenScopeError returns only producer text: predicateSlotRefusal(...).message (@objectstack/spec), and validateExpression / collectCelRootIdentifiers errors (@objectstack/formula).
  • No copy of that text exists in objectui's metadata-admin.
  • A separate objectui-owned frame wraps it in the Debug run's screen step (flow-simulator.ts). Every step note in that file is English, so it is its own surface and needs its own card. This is not a defect of this PR.

Bounded in-place fix: inside the surface, same class, correct, pinned.

  • localizeFlowFields overlays label / help / options only, so detection and getFieldValue do not move. It is a no-op under en.
  • It is pinned by the zh column-label case (显示条件).

Pins (read, not run).

  • 21 cases in total: 12 zh site cases (exactly base's 12 red), 2 lit controls, 5 en controls and 2 stored-title controls.
  • They mount real components, and the zh values are read from the catalogue. The ablation covers site 1, as ordered.
  • Recommended, non-blocking: the en controls for the engine.flowRef.notAScreenField* rows pin literal hint wording. Role-file rule 231 bars that, and the objectui#10748 precedent reads t(key,'en-US') instead. Follow-up: switch to tFormat(key,'en-US',…).

Residual English on sibling surfaces (outside the named sites, and without a carrier once Fixes #10804 closes):

  • DashboardPreview: the empty-canvas message, "Selected", three aria-labels, "Click to rename" and the fallbackHint.
  • flow-simulator.ts: the Debug run step notes.

Changeset 10804-designer-notes-locale.md: every sentence is true against the diff. Bullet 2 is narrower than the diff. Optional wording: "— a brace, bracket or dialect error on a CEL slot and on a screen field's visibleWhen, and that field's note naming a root that is not a field on the screen —".

Pending family changesets: none becomes false, and no dated note is owed.

② Semver level

'@object-ui/app-shell': patch, Clause-② no. WIDGET_CATEGORY_LABEL_KEY and labelKey are module-internal, since widget-types.ts is not re-exported and exports has only . and ./styles.css. The new trailing locale is internal and additive, and en renders unchanged. Not major.

③ Boundary flags

  • CI: at review time, 32 success, 3 skipped, and 7 in progress (test shards 2, 3, 4, 6, 7 and 8, and Spec Main Shape Gate), with 0 failures. The seat lands only on all-final green.
  • Merge: git merge-tree --write-tree against main fda49e557b is clean (tree 2c792a62fb).
  • Open PRs: only objectui#10821 touches metadata-admin/, and none of these seven files.
  • Governed: check-governed-merges.mjs reports NOT governed (0 of 9 paths). 726 lines.
  • State: draft. Assignee os-elon-musk. The commit lacks a Claude-Session: trailer; objectui squash-merges from the PR title and body, so this is not blocking.

Implemented-by: claude/issue-10804-designer-notes-locale
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 27, 2026 19:04
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 27, 2026
Merged via the queue into main with commit 8eaa1a0 Sep 27, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-10804-designer-notes-locale branch September 27, 2026 19:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants