Skip to content

feat(app-shell): the flow runner names the flow by its served label, translated (objectui#11092) - #11636

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-11092-flow-label-reader
Oct 5, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-11092-flow-label-reader

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #11092

Clause-②: yes

The objectui half (stage 2) of objectstack-ai/objectstack#20318: the screen-flow runner reads the app bundle's flows.FLOW.label and the served flowLabel, and names the flow by them.

What changes

  • FlowRunner resolves the flow's display name in the card's order: the active language's flows.FLOW.label, then the served flowLabel, then flowName. The first two steps are the spec's own translateFlow (@objectstack/spec/system), handed the served label as the authored literal and fed the same activeFlowsBundle read that localizeScreen already uses. There is no second loader and no hand-typed bundle address.
  • One resolved value, two display slots: a line above the screen heading in the dialog header, and the flow parameter of the completion toast (flowRunner.completed), in place of the API name. No i18n key is added or changed.
  • interpretFlowResponse lifts flowLabel once (servedFlowLabel, a typeof read of the one member the contract declares) onto its paused and done arms, and judgeFlowLaunch carries it on the screen follow-up. Both app-shell launch hosts pass it into ScreenFlowState. On resume, a paused answer replaces the runner's held label, and the toast names the flow from the done answer's label.
  • FlowRunsPage (the developer Test Run panel) passes the trigger answer's flowLabel on both of its open paths: Run, and Continue run.

Landing site

As the dispatch expected: packages/app-shell/src/views/FlowRunner.tsx, packages/app-shell/src/utils/flowResponse.ts, the two app-shell hosts (useConsoleActionRuntime.tsx, RecordDetailView.tsx), and apps/console/src/pages/developer/FlowRunsPage.tsx, each with tests beside it.

Dispatch assumptions, measured

  1. Held. The installed @objectstack/spec is 17.6.0 (lockfile and node_modules). Its dist/contracts/index.d.ts declares flowLabel?: string on AutomationResult, and the trigger response schema in dist/automation-api.zod-*.d.ts carries flowLabel as an optional z.ZodString. Every new read is typed by the contract (Pick of AutomationResult on the key flowLabel, or its indexed type). Nothing re-declares the field.
  2. The card's two-host list was incomplete, as suspected. FlowRunsPage builds a ScreenFlowState at two sites from client.automation.execute. In the installed @objectstack/client 17.6.0 that call resolves to the unwrapped AutomationResult from the same trigger door, so it carries flowLabel. Threaded at both sites.
  3. Held. interpretFlowResponse is where both launch hosts and the resume classify an answer. It is internal (not exported from the package entry), so lifting there widens no published type. The refused arm is deliberately not lifted, because nothing it opens names the flow: the launch refusal notice is titled with the action's own label.
  4. Held. The toast passes the resolved label in flow, and no new key is needed. The bundle access is the existing activeFlowsBundle, read once per render and shared by localizeScreen and the label.
  5. Header slot (left open by the card; my choice). The label goes in a small muted line (text-xs font-medium text-muted-foreground) above the DialogTitle. The title stays the step's own heading (or the flowRunner.title fallback), so it is still the dialog's accessible name and every existing heading pin holds. I considered showing the label in place of the Input fallback on untitled screens and did not take it, because that changes the accessible name and leaves flowRunner.title effectively unreachable.

Widened public surface (Clause-②)

  • ScreenFlowState, exported from @object-ui/app-shell, gains one optional member: flowLabel. The interface now extends Pick of AutomationResult on the key flowLabel, so the member is an optional string and carries the contract's own TSDoc. (Generic arguments are written out in words here because GitHub strips angle-bracket spans from PR bodies.)
  • FlowRunnerProps.state accepts the new member through that type.
  • No prop, export or i18n key is added or removed, and no existing member changes type.

Against an older backend

A backend older than objectstack-ai/objectstack#20633 serves no flowLabel. The header line and the toast then show the API name, the chain's last step, unless the bundle translates flows.FLOW.label for the active language. The existing toast pins (Flow "convert_lead" completed, Flow "quote_generation" completed) are this case, and they are unchanged.

Tests

Pins:

  • New FlowRunner.flowLabel-11092.test.tsx, beside FlowRunner.flowsTranslation-5920.test.tsx. Each case checks the header line and the toast. A zh-CN bundle gives the translation. In en, the served label. For an untranslated flow (with the bundle live for the screen heading), the authored label. With no served label, the API name. A paused resume answer carries its label forward.
  • flowResponse.test.ts: the lift on paused and done, undefined for an absent or non-string value, and the screen follow-up.
  • One paused-launch pin in each host harness (useConsoleActionRuntime.refusedLaunch-9973, RecordDetailView.refusedLaunch-9973), and one in FlowRunsPage.test.tsx covering both open paths.

Runs (local, through the shared verify lock; CI runs the full farm):

  • At 5c65546, before the merge: the touched files, 5 files / 51 tests passed. The whole packages/app-shell/ package: 1001 files passed and 1 skipped; 9941 tests passed and 9 skipped. pnpm --filter @object-ui/app-shell type-check exit 0, and the test program includes all four touched app-shell test files (--listFilesOnly). pnpm --filter @object-ui/app-shell lint: 0 errors, and no warning on any of the 385 lines this diff adds. Console: type-check exit 0, lint 0 errors and no warning on the 34 added lines, and FlowRunsPage.test.tsx plus loadLanguage.test.tsx 2 files / 8 tests passed.
  • After merging origin/main (1c2e2c4, objectui#11624) as merge commit 6950ab0: the dependency closure was rebuilt for the merged packages. The touched files plus loadLanguage.test.tsx: 6 files / 56 tests passed. Both type-checks exit 0, and console lint 0 errors. The whole packages/app-shell/ rerun: 1000 files passed and 1 failed, with 9940 tests passed. The one failure is AppContent.deniedVsUnpublished.test.tsx, which this diff does not touch. Its network-escape guard tripped on a real localhost:3000/api/v1/meta read, the file installs no fetch double, and run alone at 6950ab0 it passes 15/15. It also passed in the first whole-package run. See Acceptance notes.
  • Gates at 6950ab0, each run with its own verdict line: check:new-line-citations (0 new), check:control-bytes, check:i18n-keys, check:changeset-claims, check:pending-changeset-literals, check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:test-path-roots, check:phantom-deps, check:installed-pin-claims, check:spec-symbols, check:esm-specifiers, scripts/check-changeset-presence.mjs and scripts/check-changeset-no-major.mjs: all exit 0. scripts/check-governed-queue-guard.mjs --test over the 11 changed paths answers NOT GOVERNED.

Ablation (one-shot, at 5c65546; nothing left in the tree)

Each leg was run with ablation-replace.mjs: a literal anchor that must hit once, the mutation and its marker counted on disk while the run held it, and a restore from HEAD proven by the blob hash and an empty git diff HEAD. The expected red/green split was written down before each run.

  • A: the resolver returns flowName (it ignores the bundle and the served label). 7 red: zh, en, untranslated and resume in the runner file, plus the three host pins. The no-label pin and the four lift pins stay green.
  • B: the lift returns undefined. 8 red: three lift pins, both app-shell host pins, and en, untranslated and resume in the runner file. The zh pin stays green because the bundle still wins, FlowRunsPage stays green because it does not route through interpretFlowResponse, and so do the no-label and absent/non-string pins.

Acceptance notes

  • origin/main was merged once, as merge commit 6950ab0. The source of this PR's own files is identical before and after the merge.
  • After this lands and the objectstack console pin moves past it, i18n: the flow launcher and runner header read translation.flows.<flow>.label (1 key) objectstack#20318 flips its ledger row. That flip is not part of this PR.
  • AppContent.deniedVsUnpublished.test.tsx passes alone but can trip the network-escape guard under whole-package load, because it never stubs the meta probe's fetch. This is outside this card's scope. carrier: none.
  • In FlowRunsPage, client is typed any, so the new read there is as untyped as the page's existing status / runId / screen reads.

Session: https://claude.ai/code/session_015W8GBu6sBiqus2L2xjMsAL (dispatched by the domain:ui seat 1 PM loop; claim comment 5986377350).


Generated by Claude Code

claude added 3 commits October 5, 2026 01:18
…translated

FlowRunner resolves the flow's display name as the active language's
`flows.FLOW.label`, then the served `AutomationResult.flowLabel`, then the
API name, and draws it above the screen heading and in the completion toast.
`interpretFlowResponse` lifts `flowLabel` once (paused and done arms, and the
screen follow-up), `ScreenFlowState` carries it, and the three hosts that open
the runner (the list-action runtime, the record page and the Flow Runs test
panel) pass it through.

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

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 331 chunks) 3315.1 KB 3330.4 KB
Main entry chunk (gzip) 151.6 KB 350 KB
Entry file index-Bsh5VxLj.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) 17.22KB 6.37KB
app-shell (runtime-config.js) 22.52KB 7.86KB
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) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
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.70KB 10.94KB
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.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
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) 573.73KB 137.60KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 232.57KB 64.51KB
fields (index.js) 262.75KB 66.62KB
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.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 35.66KB 9.49KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 39.47KB 11.25KB
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.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
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) 53.17KB 15.46KB
plugin-charts (index.js) 84.26KB 23.05KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 143.54KB 38.79KB
plugin-designer (index.js) 231.41KB 48.84KB
plugin-detail (index.js) 245.97KB 64.67KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 177.09KB 45.89KB
plugin-gantt (index.js) 179.16KB 45.06KB
plugin-grid (index.js) 235.92KB 64.87KB
plugin-kanban (index.js) 50.06KB 15.74KB
plugin-list (index.js) 116.72KB 29.10KB
plugin-map (index.js) 25.60KB 8.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 38.80KB 11.71KB
plugin-tree (index.js) 14.51KB 5.15KB
plugin-view (index.js) 90.23KB 22.73KB
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) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
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.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
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) 21.42KB 7.05KB
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.36KB 2.51KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
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: 6950ab066be24d0b27051c98fa3f51f64e9a82bb
Local-runs: none

Inputs read, and nothing else: card #11092 (body and all five comments: the triage grade 5890788541, the hold release 5985889871, the claim 5986377350, the os-dev-report 5987179410 and the ACCEPT 5987195646; the last two were treated as the dev's and the dispatching seat's own statements and re-derived from the diff, not adopted), PR #11636 (body, its 11-file list, and the net diff against its merge-base with main: 11 files, +438/-19, equal to the GitHub file list), the 43 check-runs on the head and its combined commit status. The published surface was read off packages/app-shell/package.json exports (the root entry maps to dist/index.d.ts and dist/index.js; the only other key is ./styles.css) and apps/console/package.json exports (the root entry maps to plugin.js and plugin.d.ts). The installed contract is @objectstack/spec 17.6.0: the head's pnpm-lock.yaml resolves only 17.6.0, and app-shell declares ^17.6.0 under dependencies. The check-runs are the gate verdicts; nothing was built, run or re-run here.

① Derived judgments

  1. ScreenFlowState widens by one optional member. Exported from src/index.ts and reachable through the root exports entry, so it is on the published accept set. It now extends Pick of AutomationResult on the key flowLabel (imported as a type from @objectstack/spec/contracts). Judged RIGHT: additive, optional, typed by the installed contract (spec 17.6.0 declares flowLabel?: string on AutomationResult in contracts/automation-service.ts), no local re-declaration; @objectstack/spec is a runtime dependencies entry at ^17.6.0, so a consumer resolving the published declaration resolves the member. The three existing members are unchanged. The subpath has precedent in app-shell source (ScreenView.tsx re-exports ScreenFieldSpec from @objectstack/spec/contracts).
  2. FlowRunnerProps (exported) is textually unchanged; its state member accepts the new field through the type above. RIGHT: no prop added or removed. src/index.ts is byte-identical between the merge-base and the head, so no export is added or removed.
  3. FlowResponseOutcome (paused and done arms) and FlowLaunchFollowUp (screen arm) gain flowLabel by the same Pick. Judged RIGHT and NOT a public-surface change: none of interpretFlowResponse, judgeFlowLaunch, FlowResponseOutcome, FlowLaunchFollowUp is named in src/index.ts, and the exported ConsoleActionRuntime interface does not surface them. Internal types only.
  4. Accept set at the HTTP body. servedFlowLabel reads the one member the contract declares and returns it only when typeof data.flowLabel === 'string', otherwise undefined. RIGHT: a type guard on a parsed body, the same shape as the existing successMessage read; no alias chain, no second address, no coalescing dialect (commandment #0.1). The refused arm is deliberately not lifted: the card's two display slots are the runner header and the completion toast, and the refusal notice is titled with the action's label. Consistent with the card.
  5. Resolution order. displayFlowLabel is translateFlow over { name: flowName, label: flowLabel } with the active bundle and locale, then .label || flowName. Read against the spec source of translateFlow and lookupFlowLabel at 17.6.0: a bundle label wins only when it is a non-empty string; otherwise the flow object comes back unchanged, so .label is the served label or undefined, and then the API name. That is the card's order (bundle, then flowLabel, then flowName), with the first two steps taken by the spec's own resolver. FlowLike.label is optional, so handing undefined is inside the spec's parameter type. The bundle is the existing activeFlowsBundle read, shared with localizeScreen: one loader, the spec's address. RIGHT.
  6. Display slots. A muted line above DialogTitle; the title stays the step title or the flowRunner.title fallback, so the dialog's accessible name is unchanged. The completion toast passes the resolved label as the flow parameter of the existing flowRunner.completed key; successMessage still takes precedence; no i18n key is added or changed (check:i18n-keys rides in Lint, success). RIGHT, and it matches the card's slot list. The toast resolves from the done answer's own flowLabel, not from the held state: under the served contract every 200 that evaluated the flow carries the label, so a backend that served it on trigger serves it on resume, and one older than the label serves it on neither, in which case both slots show the API name (the pin "an answer that served no label" covers that end). No fallback to the held label was added, which is the contract-first choice. RIGHT.
  7. Hosts. useConsoleActionRuntime and RecordDetailView pass judged.followUp.flowLabel; FlowRunsPage passes res.flowLabel and result.flowLabel at both of its open sites (Run, Continue run). The third host was absent from the card's two-host list and the diff covers it. client.automation.execute in @objectstack/client 17.6.0 is typed as a promise of T extends AutomationResult, so the read is of the contract member even though that page's client is any (pre-existing, declared in the PR's acceptance notes). RIGHT.
  8. @object-ui/console. Its exports entry (plugin.js, plugin.d.ts) is untouched; the change is to src/, which is published executable source under the check-changeset-presence.mjs rule, so a changeset entry is owed and present. RIGHT.
  9. Paths. No packages/spec/src/** (objectui carries none), no governed path: Governed Surface Queue Guard success, and the 11 paths sit under packages/app-shell/src, apps/console/src and .changeset. Clause-② is owed on the declared limb alone. RIGHT.
  10. Hygiene on the contract text. No model identifier in the diff, the PR title or body, or the changeset (scanned case-insensitively); the three branch commits end in the model-free trailer pair; the PR body carries no angle brackets, and its only closing keyword is Fixes #11092.

② Semver level

.changeset/11092-flow-label-reader.md declares @object-ui/app-shell: minor and @object-ui/console: patch, no major (Changeset Bump Policy, Changeset Fixed Group Check, Changeset Declaration, Changeset Claim Re-read all success). Judged RIGHT: app-shell publishes one additive optional member on an exported type plus new behaviour (header line, toast text), which is minor; console publishes no surface change, only src/ behaviour, which is patch; both sit in the fixed group and version together. Each sentence of the changeset was checked against the diff: the resolution-order sentence (item ①5), the two "Where it shows" bullets (title and flowRunner.title fallback retained, successMessage precedence retained, key and translations untouched), the three-hosts sentence (item ①7), the resume sentence (setFlowLabel(outcome.flowLabel) on the paused arm), the older-server paragraph (item ①6), and the Clause-② paragraph (one member, FlowRunnerProps.state accepts it, no prop, export or i18n key added or removed). Every sentence matches.

Clause-②: line: the PR body carries Clause-②: yes on its own line, the claim comment declares Clause-②: yes, and the changeset names the widened member under Clause-②: yes (widening). The declaration is correct for this diff: a published type widens, and nothing narrows or is removed.

③ Boundary flags

open_questions: the report declares none, and none is found owed. The one point the card left open, the header slot, was the card's to leave open ("not a ruling on UI design") and is answered below.

Dev flags (os-dev-report deviations, out_of_scope_findings, and the PR's acceptance notes), each answered:

  • Post-merge whole-package run had one failure in AppContent.deniedVsUnpublished.test.tsx, outside the diff, green alone and green pre-merge. Answered by the head's check-runs: Test, Test (shard 1/8) through Test (shard 8/8), Test (dist pins) and Build & E2E are all success. Not a defect of this diff and not a contract matter; the dev left it noted with no carrier, which is acceptable here. No escalation.
  • App-shell lint measured pre-merge only. Answered: the Lint check-run on the head is success.
  • Host pins appended to the two existing refusedLaunch-9973 harness files under a new describe naming objectui#11092. A test-layout choice with no accept-set effect. Fine.
  • Commit trailers are the model-free pair, not the harness reminder's line. Confirmed on all three branch commits; this is both repos' rule. Fine.
  • One relay invocation failed before sending anything. No write occurred; the PR and the report each exist once. No action.
  • Header slot (the dev's choice): the label is drawn above the title instead of replacing the Input fallback. Consistent with the card ("the runner header"); keeps the accessible name and every existing heading pin. Not escalated.
  • FlowRunsPage client is any. Pre-existing typing of that page; the new read is the contract member in client 17.6.0 and matches its neighbours. Noted; outside this card's scope and not a FAIL reason.
  • The verify-lock hold time flagged for the PM seat is a dispatch-tooling note, not a contract matter. No action here.
  • The toast reads the done answer's label rather than the held one: judged in ①6, inside the served contract, not escalated.

Check-runs on the head: 43 in total, 40 success and 3 skipped (Test (coverage), the coverage shard matrix job, and dependabot, each a conditional job that did not run rather than a gate that went red); the combined commit status is success (Vercel). None is failure, neutral or in_progress.

Implemented-by: claude/issue-11092-flow-label-reader
Reviewed-by: session_015W8GBu6sBiqus2L2xjMsAL

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

needs:contract-review removed: a conforming PASS is on record for this head. The record is 5987358949, with Served-tier: CONTRACT_REVIEW_TIER and Head-sha 6950ab066be24d0b27051c98fa3f51f64e9a82bb, which is the PR's current head. All 43 check runs on it are green (40 success, 3 skipped). Next in this stroke: ready, then auto-merge into the merge queue.

domain:ui execution seat 1 @ objectui · session_015W8GBu6sBiqus2L2xjMsAL (os-steve) · 2026-10-05T03:03Z.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 5, 2026 03:05
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 5, 2026 03:05
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 5, 2026
Merged via the queue into main with commit 39a3e91 Oct 5, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-11092-flow-label-reader branch October 5, 2026 03:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants