Skip to content

fix(app-shell,i18n): word the stored_expression strip reason ahead of the spec pin (objectui#13178) - #13233

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-13178-stripped-stored-expression
Oct 20, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-13178-stripped-stored-expression

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #13178
Clause-②: no

What

objectstack-ai/objectstack#24778 (draft) adds a fifth arm, stored_expression, to DroppedFieldsEvent['reason']: a stored field whose declared expression the engine computes on every write keeps the computed value, not the one the caller sent (the field IS written, unlike computed). STRIPPED_LINE in packages/app-shell/src/providers/writeWarningToast.ts is keyed by that union, so the objectstack Console Pin Gate, which builds the pinned objectui against objectstack's own spec, would fail on a missing key once #24778 lands. This lands the row first, in the objectui#11206 shape.

  • STRIPPED_LINE's key type is a Record keyed by DroppedFieldsEvent['reason'] | 'stored_expression'. Against the published pin (which does not declare the arm) the required key set is the pin's four plus the literal; against #24778's spec it is the five it declares. No excess-key error on either side, and the next arm upstream adds is still a missing key here. The docblock records why, and the tripwire: delete the widening at the pin bump that declares the value.
  • One row, stored_expression, through a new key detail.writeStrippedStoredExpression with {{fields}}. English: "Computed from the record, so the computed value was saved instead of the one sent: FIELDS". A real translation in all ten packs under packages/i18n/src/locales/, never "read-only".
  • Changeset .changeset/13178-stripped-stored-expression.md: @object-ui/app-shell and @object-ui/i18n patch.

Pins (writeWarningToast.test.ts)

  • (a) New case words reason stored_expression as a computed value saved in place of the one sent, in en and zh (objectui#13178). It drives emitWriteWarning through a real createI18n instance in en and in zh (zh catalogue loaded with loadBuiltInLocale), asserts the key detail.writeStrippedStoredExpression was asked for, and asserts both full lines with the field labels.
  • (b) The writeWarningToast branches on DroppedFieldsEvent.reason with a binary ternary, so every future reason is labelled "Read-only" #3935 test gives every reason the pinned spec declares its own wording stays green against today's pin. It now drives the installed spec's reasons plus stored_expression, because today's pin does not declare it.
  • (c) CONTROL: names the fields and claims no cause for a reason ahead of the spec pin (#3935) is unchanged and green. An undeclared value still takes the skew arm.
  • Tripwire, the objectui#11206 shape: reminds the pin bump that carries stored_expression to retire the widening (objectui#13178) asserts the installed spec does not declare stored_expression. When it does, the message names both edits: delete | 'stored_expression', and make _ReasonIsTheEnum in spec-symbol-batch6.test.ts the five-member union.

Ablation (one-shot, nothing kept)

The fix was committed first (fbb8c133e4). node scripts/ablation-replace.mjs (objectstack checkout) renamed the row's key stored_expression to stored_expression_ablated, keeping the widened type, so the table's lookup misses. Anchor 1 to 0, blob 5e4d1f9dd772 to c1abca187de5. The run was writeWarningToast.test.ts under the lock: Tests 2 failed | 19 passed (21). The two that failed were (a) and (b); (c) stayed green. The restore was git checkout HEAD -- PATH under an EXIT/INT/TERM trap. Proof: git hash-object after restore = 5e4d1f9dd772f1ccdc6c21c37fcb3f847530a0e3 = the HEAD blob, and git diff HEAD is empty.

Tests and gates (head fbb8c133e4, worktree root, exit captured before any pipe)

  • pnpm exec vitest run --maxWorkers=1 over writeWarningToast.test.ts and saveAdvisoryToast.test.ts (the files naming the module): 2 files, 37 tests passed.
  • The same over the i18n locale families that read every pack: all-locales-key-parity, en-zh-key-parity, de-quote-pairing-3876, ar-participle-orthography-6610, ellipsis-glyph-3878, fallback-placeholder-spelling-3512 and machineLocaleCensus-9909. Result: 7 files, 93 tests passed.
  • pnpm --filter '@object-ui/app-shell^...' build exit 0. Then pnpm --filter @object-ui/app-shell --filter @object-ui/i18n run type-check exit 0. Both scripts run tsc --noEmit && tsc -p tsconfig.test.json. tsc -p tsconfig.test.json --listFilesOnly lists writeWarningToast.test.ts.
  • ESLint, --no-inline-config --format json, over the 12 changed .ts files: exit 0, 0 errors, 0 warnings, 12 files in the JSON. eslint.config.js enables no type-aware linting, so this diff cannot change the verdict on any file it does not touch. The repo-wide pnpm lint runs in CI.
  • These exited 0: check:phantom-deps, check:self-import, check:unreferenced-sources, check:doc-example-readers, check:handler-key-reads, check:side-effects-array, check:element-data-source-declaration, check:spec-symbols, check:action-forward-parity, check:designer-field-key-parity, check:icon-record-names, check:i18n-keys, check:i18n-drift, check:i18n-designer-parity, check:control-bytes, check:new-line-citations, scripts/check-hand-rolled-comment-mask.mjs, scripts/check-changeset-fixed.mjs and scripts/check-changeset-presence.mjs.
  • check:spec-floors: NOT MEASURED. It exited 1 with no-artifact, because it needs a whole-workspace build, which was not run here.
  • Full package directories packages/app-shell/ and packages/i18n/ are left to CI. Only the files above ran here.

Acceptance notes

  • Where the card's two failures surface. The objectstack Console Pin Gate (scripts/build-console.sh) only runs builds: the console dependency closure, then the console. So the missing key is a type error in the app-shell tsc build, and that is what this unblocks. The writeWarningToast branches on DroppedFieldsEvent.reason with a binary ternary, so every future reason is labelled "Read-only" #3935 test and _ReasonIsTheEnum (packages/data-objectstack/src/spec-symbol-batch6.test.ts, a type-level pin of the exact union) are not reached by that gate. Both go red only on objectui's own spec bump to a release that declares the arm. At that bump, the tripwire above names both edits. spec-symbol-batch6.test.ts is outside this claim's file surface and is not touched.
  • When a user first sees the sentence. The adapter judges a reported reason against the installed spec's enum. Until the pin declares stored_expression, such a report from a newer server is still shown on the skew arm: "Not applied by the server: FIELDS". The changeset says so.

Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB


Generated by Claude Code

… the spec pin

STRIPPED_LINE's key type is widened by the one literal, as was done for
computed, so the table compiles against the published spec pin and against
objectstack's spec carrying the fifth arm. A new
detail.writeStrippedStoredExpression row in all ten locale packs; the
exhaustiveness test also drives stored_expression, a new case pins the en and
zh lines, and a tripwire names the widening to delete at the pin bump.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 78 chunks) 3216.1 KB 3744.1 KB
Main entry chunk (gzip) 82.0 KB 350 KB
Entry file index-ovV_00PP.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 (buildIdentity.js) 0.80KB 0.49KB
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 21.17KB 7.81KB
app-shell (runtime-config.js) 24.52KB 8.48KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 45.59KB 12.03KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 20.67KB 6.12KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.74KB 3.94KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.62KB 0.83KB
auth (authStyles.js) 5.27KB 1.86KB
auth (createAuthClient.js) 42.84KB 11.66KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
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.26KB 8.03KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (commentAuthorship.js) 1.39KB 0.78KB
collaboration (index.js) 1.93KB 0.85KB
collaboration (useCollaborationTranslation.js) 6.50KB 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) 615.09KB 149.86KB
core (index.js) 10.18KB 4.04KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 252.96KB 70.67KB
fields (index.js) 294.44KB 75.25KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
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.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.40KB 12.01KB
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) 15.35KB 5.51KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.82KB 2.38KB
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.04KB 3.92KB
plugin-calendar (index.js) 55.56KB 16.21KB
plugin-charts (index.js) 85.41KB 23.46KB
plugin-chatbot (index.js) 238.47KB 56.30KB
plugin-dashboard (index.js) 147.53KB 39.95KB
plugin-designer (index.js) 233.95KB 50.08KB
plugin-detail (index.js) 266.70KB 70.83KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 186.04KB 48.22KB
plugin-gantt (index.js) 178.46KB 45.13KB
plugin-grid (index.js) 274.11KB 76.08KB
plugin-kanban (index.js) 64.49KB 19.83KB
plugin-list (index.js) 149.54KB 39.21KB
plugin-map (index.js) 27.17KB 9.00KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 46.66KB 13.13KB
plugin-timeline (index.js) 39.31KB 11.88KB
plugin-tree (index.js) 15.27KB 5.40KB
plugin-view (index.js) 94.54KB 24.33KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 12.07KB 3.68KB
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) 126.45KB 41.40KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.71KB 2.11KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.81KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
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) 1.13KB 0.65KB
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) 5.78KB 2.70KB
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 (node-slots.js) 7.18KB 2.34KB
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 (product-blocks.js) 0.20KB 0.19KB
types (record-approval-decision.js) 0.20KB 0.20KB
types (record-components.js) 0.20KB 0.19KB
types (record-panels.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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.26KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.48KB 3.50KB
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

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.

app-shell: writeWarningToast needs a stored_expression row before objectstack#24778 widens DroppedFieldsEvent.reason, or the pin bump turns red

2 participants