Skip to content

fix(app-shell): declare the spec default for escalation.notifySubmitter in the flow-node inspector - #6831

Merged
os-sam merged 2 commits into
mainfrom
claude/issue-6794-notify-submitter-default
Aug 30, 2026
Merged

os-sam merged 2 commits into
mainfrom
claude/issue-6794-notify-submitter-default

Conversation

@claude

@claude claude Bot commented Aug 29, 2026 •

Copy link
Copy Markdown
Contributor

Part of #6794

The metadata-admin flow-node table declared no defaultValue for escalation.notifySubmitter while the installed @objectstack/spec defaults the key to true. One line of table data, plus the reconciliation assertion that keeps the two sides pinned.

Premise, re-derived against the install surface

Triage asked the implementing seat to re-derive rather than trust the pin, because the pin moves. Two independent readings of the artefact a consumer actually installs, plus the runtime's own answer:

reading result
pnpm-lock.yaml resolution @objectstack/spec: 17.2.0
npm pack @objectstack/spec@17.2.0 — package/dist/automation/index.js:3497 notifySubmitter: zod.z.boolean().default(true).describe(...)
this worktree's installed copy — node_modules/.pnpm/@objectstack+spec@17.2.0_ai@7.0.65_zod@4.4.3_/.../dist/automation/index.js:3497 identical line
the runtime's own answer — ApprovalEscalationSchema.safeParse({ timeoutHours: 24 }) { enabled: false, timeoutHours: 24, action: 'notify', notifySubmitter: true }

The premise holds: live today, not latent. The card's collapse-into-a-duplicate branch stays closed, and the split was correct.

The same reading also re-confirms the sibling card's latency in the other direction: enabled is still default(false) in 17.2.0, so installed spec and table still agree on it.

What changed

  • flow-node-config.ts — escalation.notifySubmitter gains defaultValue: 'true', with a comment naming the spec and the online half.
  • flow-node-config.spec-reconciliation.test.ts — a new describe block. The file is already the objectui half of the form-versus-spec ledger; it carried the KEY axis, this adds the DEFAULT axis for this one field.

The expected value is read out of the installed spec, never spelled out as a literal: objectui consumes that contract and must not become a second source of truth for it. A vacuity guard beside it asserts the key still materialises from an omitted input, so the comparison can never quietly become a comparison against undefined.

A corroboration worth recording: the online half of this same form already carried the default. A backend that publishes the approval configSchema sends default: true for the key, and json-schema-to-fields turns a JSON-Schema default into defaultValue: 'true' — the fixture in json-schema-to-fields.test.ts:68 is that exact payload. Offline and online were rendering the same key from two different claims about the spec.

Ablation — red without the fix, green with it

Mutation and restore both proven on disk, both legs under one trap, restore pinned to the HEAD blob rather than to a bare checkout.

leg tree result
green as committed Test Files 1 passed (1) · Tests 12 passed, 1 skipped (13)
red defaultValue: 'true' removed Test Files 1 failed (1) · Tests 1 failed, 11 passed, 1 skipped (13)

The red leg's named failure is the new assertion and nothing else:

FAIL  packages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.spec-reconciliation.test.ts
  approval escalation: declared default vs ApprovalEscalationSchema (#6794)
  the form declares the default the spec applies
AssertionError: expected undefined to be 'true'

The mutation was confirmed on disk before the red leg ran — anchor replaced exactly once (a zero-hit replace exits non-zero), declared-default marker count 1 to 0, bare marker count 0 to 1, blob hash df04b55 to 93555d5. The restore reproduced the HEAD blob df04b55 byte-for-byte with git diff HEAD empty. The first attempt at this ablation failed loudly on a bad grep anchor rather than reporting a no-op as a measurement, which is the check working.

No existing test moved. Not one assertion was edited or deleted; the whole metadata-admin suite is green unchanged (216 files, 2264 tests). Stating it plainly because it is a real answer: this field had no default-side coverage at all, so there was nothing to update.

Read before grading this as the p1 remedy

The card's severity narrative is that the checkbox reads unchecked while the runtime notifies. This fix does not change what the checkbox renders, and no fix confined to the table could:

FlowConfigField.defaultValue has exactly one consumer in the repo — isFieldVisible (flow-node-config.ts), which resolves an unset controller for showWhen. The render path never reads it: FlowNodeConfigField draws a boolean as checked={value === true} off the stored value alone, and no field currently gates on notifySubmitter. So what lands here is the declaration made honest — the table now states what the runtime does, the offline half agrees with the online half, and any future showWhen naming this controller resolves correctly.

The remaining half — ten fields in this table declare a default and the rendered control reads none of them — is filed as #6830 and deliberately not ridden in here. It also is not obviously a bug in the direction one would guess: an adjacent maintainer ruling on #6263 says the console needs no second default contract, so the question there is how an effective default is shown, not whether the designer should start writing defaults into stored metadata. That is a triage call, not this card's.

Not absorbed

#6620 is the same class and its stated shape would have covered this field too, but it is pm:on-hold deliberately, behind a future spec bump that flips its own key's default. Adopting the general shape here would discharge it without its restart condition being met, so this PR does not: the new assertion is scoped to notifySubmitter alone, with the reason written in the test, and the sibling card is untouched and stays open. A note about the general shape belongs on that card, for its own grading.

Verification

Union re-run after the final commit, at 7bf9a6313:

command verdict line
pnpm exec vitest run (3 inspector files, from the repo root) Test Files 3 passed (3) · Tests 84 passed, 1 skipped (85)
pnpm --filter @object-ui/app-shell run type-check tsc --noEmit && tsc -p tsconfig.test.json, exit 0
pnpm --filter @object-ui/app-shell run lint 2789 problems (0 errors, 2789 warnings) — the package baseline; neither changed file emits a diagnostic
node scripts/check-changeset-presence.mjs 2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)
node scripts/check-changeset-no-major.mjs No changeset declares a major bump
pnpm check:control-bytes OK (scanned 5648 tracked text file(s); skipped 85 binary)
pnpm check:designer-field-key-parity · check:spec-symbols · check:i18n-keys · check:i18n-drift · check:vi-mock-specifiers all exit 0

Tests were run from the repo root, the way the repo's own scripts do — pnpm --filter PKG exec vitest run FILE re-roots at the package and is a false green here. The executed file names are in the verbose run: every one of the 13 cases is prefixed packages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.spec-reconciliation.test.ts.

The type-check was proven to actually cover both edited files rather than assumed to: tsc --listFiles names flow-node-config.ts in both projects and the new test file in tsconfig.test.json. The heavier runs above (type-check, lint, the 216-file suite) executed at 980a5f90c; the only delta to 7bf9a6313 is the added changeset file, no source change.


Generated by Claude Code


Generated by Claude Code

claude added 2 commits August 29, 2026 21:19
…w-node table

The metadata-admin flow-node config table declared no `defaultValue` for
`escalation.notifySubmitter` while the installed `@objectstack/spec` (17.2.0)
defaults the key to `true` — so the hand-written table stated the opposite of
what an omitted key does at runtime, and disagreed with the online half of the
same form (a published `configSchema` carries `default: true`, which
`json-schema-to-fields` turns into `defaultValue: 'true'`).

Adds the missing declaration and a reconciliation assertion that reads the
expected value out of the installed spec rather than pinning a literal, so
objectui never becomes a second source of truth for a published contract.

Scoped to `notifySubmitter`: the sibling `escalation.enabled` controller is a
separate, deliberately on-hold card whose default flips on a future spec bump.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 45 chunks) 3174.0 KB 3222.7 KB
Main entry chunk (gzip) 148.1 KB 350 KB
Entry file index-CShJKZtd.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) 11.89KB 4.50KB
app-shell (runtime-config.js) 20.61KB 7.35KB
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) 511.50KB 116.32KB
core (index.js) 5.30KB 2.13KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 173.10KB 47.96KB
fields (index.js) 240.93KB 60.76KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
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.44KB 1.39KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 33.40KB 8.71KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.95KB 10.97KB
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.55KB 0.62KB
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 (useResponsiveConfig.js) 1.37KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 9.53KB 3.38KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 4.64KB 1.50KB
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) 1.93KB 0.88KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 46.92KB 12.93KB
plugin-charts (index.js) 64.68KB 18.35KB
plugin-chatbot (index.js) 190.33KB 45.10KB
plugin-dashboard (index.js) 133.48KB 34.51KB
plugin-designer (index.js) 212.87KB 43.19KB
plugin-detail (index.js) 245.46KB 62.46KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 133.03KB 32.64KB
plugin-gantt (index.js) 165.23KB 40.37KB
plugin-grid (index.js) 201.57KB 54.55KB
plugin-kanban (index.js) 53.14KB 14.64KB
plugin-list (index.js) 113.15KB 27.59KB
plugin-map (index.js) 20.20KB 6.66KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 43.51KB 11.94KB
plugin-timeline (index.js) 28.95KB 8.33KB
plugin-tree (index.js) 9.00KB 3.08KB
plugin-view (index.js) 85.87KB 21.12KB
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) 76.75KB 25.49KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 3.11KB 1.48KB
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 0.99KB
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 (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.72KB 2.24KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
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 (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) 3.40KB 1.71KB
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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant