Repository navigation
i18n: the flow runner reads translation.flows.FLOW.label for its header and completion toast, falling back to the served flowLabel (stage 2 of objectstack-ai/objectstack#20318) #11092
Description
This card carries the objectui half (stage 2) of objectstack-ai/objectstack#20318: the flow runner reads translation.flows.FLOW.label. objectstack-ai/objectstack#20318 keeps the served label (stage 1, objectstack-ai/objectstack#20633) and the ledger flip that follows this card's landing.
Filing gate: ④ a coordination node, the per-layer child of an in-flight card. Filed by the domain:spec execution seat 2 (session_014EJ1ED8X4MMrT18BhVx4tx, seat post objectstack-ai/objectstack#18549) from the stage-1 ACCEPT on objectstack-ai/objectstack#20318. ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim.
Why this is owed
objectstack triage graded objectstack-ai/objectstack#20318 ENFORCE by the maintainer's criterion: translating a flow's label is mainstream (the Salesforce Translation Workbench translates Flow labels). Today nothing in objectui reads translation.flows.FLOW.label, and no surface displays the flow's own label. The domain:spec seat 4 measured that on objectui 797a30f4 (objectstack-ai/objectstack#20318, comment 5880531686):
FlowRunner.tsxreads onlyflows.FLOW.screens(localizeScreen, about :219–:233).- The runner
DialogTitle(about :462) is the screen title orflowRunner.title. - The completion toast (about :385) interpolates
flowName, the API name. - The launcher's notices use the action's label or
Flow "NAME".
The served shape this reader consumes (objectstack-ai/objectstack#20633, at-tier PASS)
data.flowLabel(optional string) is on every 200 fromPOST /api/v1/automation/NAME/triggerandPOST /api/v1/automation/NAME/runs/RUN_ID/resume: paused, finished, refused or skipped.- It is not on a 400
FLOW_FAILED, nor on the 404 / 409 / 422 refusals. - It is the flow definition's authored label, verbatim. For a subflow chain it is the addressed parent's label, never the child's.
AutomationResult.flowLabelin@objectstack/spec(contracts/automation-service.ts) declares it, andTriggerFlowResponseSchema.datamirrors it.
What the reader does (the direction triage set; ⛔ not a ruling on UI design)
FlowRunnerkeepsflowLabelfrom the trigger and resume responses (ScreenFlowStategains it). Two hosts mountFlowRunner(useConsoleActionRuntime.tsxandRecordDetailView.tsx), so the read belongs inFlowRunner.- The displayed label resolves as: the active locale's
translation.flows.FLOW.label, thenflowLabel, thenflowName. - It gets a display slot: the runner header, and the completion toast in place of the API name.
- Pins: a zh bundle and an en fallback, and a flow with no translation showing its authored label.
Sequencing
- The reader reads an optional field, so it can be written against the declared shape now. It takes effect once the console serves a backend that includes feat(spec,service-automation): a flow run's result carries the flow's authored label as flowLabel objectstack#20633.
- 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 (translation.jsonflows.children.label), drops the container'sauthorWarn, and rewrites thecontent/docs/ui/translations.mdxbullet (thedomain:cliseat's pointer5863837992).
Dedupe words: flows label translation FlowRunner · flowLabel runner header · translation.flows label reader
Generated by Claude Code
Activity
objectstack-fleet commentedon Sep 29, 2026
Restart-when:
npm view @objectstack/spec versionanswers a release whoseAutomationResultdeclaresflowLabel(objectstack PR #20633), and objectuimain'spnpm-lock.yamlresolves itTriage: first grade —
enhancement·priority:p3·domain:ui·area:i18n·pm:on-hold. It is held on the install surface: the middle step of the label chain is not in any spec objectui can installTriage: lands in
FlowRunner.tsx(inapp-shell), the one component both hosts mount ⇒domain:ui. It takes the p3 andarea:i18nof its parent, objectstack-ai/objectstack#20318.Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-09-29T12:58Z. ⛔ Not a claim, ⛔ not a dispatch.Why held, not queued.
translation.flows.FLOW.label, thenflowLabel, thenflowName.flowLabelis declared by objectstack PR #20633 (AutomationResult.flowLabel, and mirrored inTriggerFlowResponseSchema.data). It merged at 2026-09-29T12:25Z, but it isn't in any published release: npm's latest is17.5.0, and objectui's lockfile resolves17.4.0.Direction at restart (the card body's "What the reader does" stands):
ScreenFlowStatekeepsflowLabelfrom the trigger and resume responses.translation.flows.<flow>.label(1 key) objectstack#20318 flips its ledger row when the objectstack console pin moves past this card (#20638 is the current pin-bump card).objectstack-fleet commentedon Oct 5, 2026
Hold released:
pm:on-hold→pm:queue. TheRestart-when:condition (this card's 2026-09-29 triage grade) holds on the installed@objectstack/spec17.6.0Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-05T00:08Z. ⛔ Not a claim, ⛔ not a dispatch. Found in a full pass over blocked and held cards.The condition: "
npm view @objectstack/spec versionanswers a release whoseAutomationResultdeclaresflowLabel(objectstack PR #20633), and objectuimain'spnpm-lock.yamlresolves it."Readings at this write:
@objectstack/spec@17.6.0from npm, unpacked:dist/contracts/index.d.tsinterface AutomationResultdeclaresflowLabel?: string.TriggerFlowResponse's data schema indist/automation-api.zod-*.d.tscarriesflowLabel: z.ZodOptional<z.ZodString>.origin/mainpnpm-lock.yamlresolves@objectstack/specat17.6.0only (since objectui#11438 / PR chore(deps): resolve @objectstack/* 17.6.0 in pnpm-lock.yaml and follow its mechanical contract moves; five owner-held reds reported by row (objectui#11438) #11531).So the hold could have released when objectui moved to 17.6.0 (2026-10-03). The grade in this card's triage comment stands:
enhancement· p3 ·domain:ui·area:i18n. objectstack-ai/objectstack#20318 is blocked on this card, and it unlocks when this one closes.Generated by Claude Code
objectstack-fleet commentedon Oct 5, 2026
Claim: PM loop round 2
Session:
session_015W8GBu6sBiqus2L2xjMsALAccount:
os-steveBranch:
claude/issue-11092-flow-label-readerWorktree:
objectui-issue-11092Domain:
domain:uiSeat:
domain:ui#1File surface:
packages/app-shell/src/views/FlowRunner.tsx(ScreenFlowState, the runner header and the completion toast),packages/app-shell/src/utils/flowResponse.ts, the hosts that buildScreenFlowStatefrom a trigger answer (packages/app-shell/src/hooks/useConsoleActionRuntime.tsx,packages/app-shell/src/views/RecordDetailView.tsx,apps/console/src/pages/developer/FlowRunsPage.tsx), theFlowRunnertests beside them, the app-shell locale bundles only if a toast string changes, and.changeset/11092-*.md(stop on breach; explain in the report)Container & model:
M,mode:subagent,model: default tierfor the build (dispatch-gates --tier --repo objectstack-ai/objectui: no path-derived mandate; floor sonnet · default opus · ceiling fable). The delivered PR owes an isolated review atCONTRACT_REVIEW_TIER(reason:Clause-②: yes, below).Clause-②: yes
Thread-read: 5985889871
Serial constraints cleared: none, read 2026-10-05T01:06Z. The open objectui PRs (#11624
plugin-form+types, #11069packages/cli, #11600 the release PR) touch none of these files. The served half, objectstack-ai/objectstack#20633, declaresAutomationResult.flowLabelin@objectstack/spec17.6.0, which objectuimainresolves (triage5985889871).Why
Clause-②: yes:ScreenFlowStateis exported from@object-ui/app-shell(src/index.ts:224), and the card's direction addsflowLabelto it. That widens a published type.Generated by Claude Code
objectstack-fleet commentedon Oct 5, 2026
os-dev-report
{
"issue": 11092,
"status": "done",
"branch": "claude/issue-11092-flow-label-reader",
"pr": "#11636",
"session": "session_015W8GBu6sBiqus2L2xjMsAL (subagent run; the parent's harness-stamped id)",
"premise_still_valid": true,
"summary": "FlowRunner now names the flow by a label resolved in the card's order: the active locale's flows.FLOW.label, then the served AutomationResult.flowLabel, then flowName. The first two steps are the spec's own translateFlow, fed the existing activeFlowsBundle read. The label is drawn in a muted line above the screen heading (the DialogTitle stays the step title and the accessible name) and as the flow parameter of the existing flowRunner.completed toast, with no new i18n key. interpretFlowResponse lifts flowLabel once (paused and done arms; judgeFlowLaunch's screen follow-up), typed via Pick of AutomationResult. ScreenFlowState (published) gains optional flowLabel the same way, and all three hosts thread it: useConsoleActionRuntime, RecordDetailView, and FlowRunsPage, at both of its open sites. Zone 2 #2 was confirmed: client.automation.execute resolves to the unwrapped AutomationResult from the trigger door, so it carries flowLabel. Landing site as expected.",
"tests": "Pins: new packages/app-shell/src/views/tests/FlowRunner.flowLabel-11092.test.tsx (zh-CN bundle → translation; en → served label; untranslated flow with bundle live for the screen → authored label; no served label → API name; paused resume answer carries label; each on header line AND toast, toast text asked of the i18n instance, not spelled). flowResponse.test.ts +4 (lift on paused/done, undefined for absent/non-string, screen follow-up). One paused-launch pin each appended to useConsoleActionRuntime.refusedLaunch-9973 and RecordDetailView.refusedLaunch-9973 harnesses, and one in FlowRunsPage.test.tsx (Run + Continue run). Runs, all through os-verify-lock, from the repo root: AT 5c65546 (pre-merge): touched files 'Test Files 5 passed (5) / Tests 51 passed (51)'; pnpm exec vitest run packages/app-shell/ 'Test Files 1001 passed | 1 skipped (1002) / Tests 9941 passed | 9 skipped (9950)' VERDICT command-exit 0; pnpm --filter @object-ui/app-shell type-check exit 0 (tsconfig.test.json --listFilesOnly lists all 4 touched app-shell test files); pnpm --filter @object-ui/app-shell lint '0 errors' and 0 warnings on the 385 added lines (warning lines intersected with git diff -U0 hunks); console type-check exit 0 (after building app-shell + 6 other dist-less deps), console lint '0 errors' and 0 warnings on the 34 added lines; FlowRunsPage.test.tsx + loadLanguage.test.tsx 'Test Files 2 passed / Tests 8 passed'. AFTER merging origin/main 1c2e2c4 (merge commit 6950ab0, = PR head): rebuilt types/plugin-form/app-shell exit 0; touched files + loadLanguage 'Test Files 6 passed (6) / Tests 56 passed (56)'; app-shell type-check exit 0; console type-check exit 0; console lint '0 errors'; whole app-shell rerun 'Test Files 1 failed | 1000 passed | 1 skipped / Tests 1 failed | 9940 passed' VERDICT command-exit 1, the one failure AppContent.deniedVsUnpublished.test.tsx (untouched by this diff; network-escape guard tripped on a real localhost:3000/api/v1/meta read; the file stubs no fetch), rerun alone at 6950ab0 'Tests 15 passed (15)' exit 0, and green in the pre-merge whole run. App-shell lint not rerun post-merge: eslint.config.js is not type-aware (0 projectService/project keys) and the merge touched no app-shell file. Ablation at 5c65546 via objectstack scripts/ablation-replace.mjs (literal anchor x1, on-disk marker 1 / anchor 0 printed inside the held child, restore from HEAD proven by blob hash == HEAD and empty git diff HEAD, plus a script-level EXIT/INT/TERM trap), expected split written down before each run, tests resolve FlowRunner/flowResponse from src by relative import (no dist in path): leg A (displayFlowLabel returns flowName) 'Tests 7 failed | 44 passed' = zh, en, untranslated, resume + 3 host pins red; no-label and 4 lift pins green, as predicted. Leg B (servedFlowLabel returns undefined) 'Tests 8 failed | 43 passed' = 3 lift pins + 2 app-shell host pins + en, untranslated, resume red; zh (bundle wins), FlowRunsPage (no flowResponse), no-label, absent/non-string green, as predicted. Post-ablation: git diff HEAD empty-exit 0, both blob hashes == HEAD, status porcelain 0.",
"gates": {
"check:new-line-citations": "0 (0 new citations) at 6950ab0",
"check:control-bytes": "0 at 6950ab0",
"check:i18n-keys": "0 at 6950ab0",
"check:changeset-claims": "0 at 6950ab0",
"check:pending-changeset-literals": "0 at 6950ab0",
"check:vi-mock-specifiers": "0 at 6950ab0",
"check:vi-mock-inherit": "0 at 6950ab0",
"check:vi-mock-override-shape": "0 at 6950ab0",
"check:test-path-roots": "0 at 6950ab0",
"check:phantom-deps": "0 at 6950ab0",
"check:installed-pin-claims": "0 at 6950ab0",
"check:spec-symbols": "0 at 6950ab0",
"check:esm-specifiers": "0 at 6950ab0 (specifiers leg only, by the script's own design)",
"scripts/check-changeset-presence.mjs": "0 at 6950ab0",
"scripts/check-changeset-no-major.mjs": "0 at 6950ab0",
"scripts/check-governed-queue-guard.mjs --test (11 paths)": "0, NOT GOVERNED",
"app-shell type-check": "0 at 6950ab0",
"app-shell lint": "0 at 5c65546 (not type-aware; merge touched no app-shell file)",
"console type-check": "0 at 6950ab0",
"console lint": "0 at 6950ab0",
"vitest touched files + loadLanguage": "0 at 6950ab0 (6 files / 56 tests)",
"vitest packages/app-shell/": "0 at 5c65546; 1 at 6950ab0 (1 unrelated flake, solo rerun 0)",
"check:i18n-drift": "not owed (no locale bundle changed)"
},
"files_changed": [
".changeset/11092-flow-label-reader.md",
"apps/console/src/pages/developer/FlowRunsPage.tsx",
"apps/console/src/pages/developer/FlowRunsPage.test.tsx",
"packages/app-shell/src/hooks/useConsoleActionRuntime.tsx",
"packages/app-shell/src/hooks/tests/useConsoleActionRuntime.refusedLaunch-9973.test.tsx",
"packages/app-shell/src/utils/flowResponse.ts",
"packages/app-shell/src/utils/tests/flowResponse.test.ts",
"packages/app-shell/src/views/FlowRunner.tsx",
"packages/app-shell/src/views/RecordDetailView.tsx",
"packages/app-shell/src/views/RecordDetailView.refusedLaunch-9973.test.tsx",
"packages/app-shell/src/views/tests/FlowRunner.flowLabel-11092.test.tsx"
],
"line_budget": "n/a (no skills/** or governed path touched); diff vs merged main 1c2e2c4: 11 files, +438 / -19",
"deviations": [
"Post-merge whole-package app-shell rerun was not fully green: 1 failure in AppContent.deniedVsUnpublished.test.tsx, outside this diff, green alone and green pre-merge; reported, not fixed here.",
"App-shell lint was measured pre-merge only (declared narrowing: eslint is not type-aware and the merge changed no app-shell file); console lint was rerun post-merge.",
"Host pins were appended to the two existing refusedLaunch-9973 harness files (a separate describe block naming objectui#11092) instead of duplicating those harnesses in new files.",
"Commits carry the model-free trailer pair per objectui AGENTS.md, not the harness reminder's model-named Co-Authored-By line.",
"One relay invocation failed before sending anything (relative script path after a cwd reset, MODULE_NOT_FOUND); rerun with the absolute path. No write was made by the failed call."
],
"mcp_calls": "0 — no MCP GitHub tool was called",
"api_writes": "3 — each sent through scripts/pm relay (one repository_dispatch POST /repos/objectstack-ai/objectstack/dispatches per write, executed as objectstack-fleet[bot]): (1) pr_create → POST /repos/objectstack-ai/objectui/pulls (draft, #11636; read-back 9085/9085 bytes identical); (2) label-write --assign → POST /repos//issues/11636/assignees (os-steve; read-back matches; zero label writes); (3) this os-dev-report → POST /repos//issues/11092/comments. Plus git pushes of the branch (empty probe, 3f6183a, 5c65546, merge 6950ab0), which are not REST writes.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: 承接者:无 · noted, not filed — AppContent.deniedVsUnpublished.test.tsx can trip the network-escape guard under whole-package load (its meta probe reads localhost:3000 with no fetch double; the guard's own message names the fix shape). Observed once in two whole-package runs; not reproducible alone, so not a class (a) card. Dedupe words: AppContent deniedVsUnpublished network escape · meta probe fetch double · localhost:3000 flake",
"carrier: the PM seat (dispatch gate list) · noted, not filed — the dispatch-named whole-package app-shell run held the shared verify lock 21m42s and 22m44s on this box; the lock tool flags holds that long as a starvation risk for sibling seats."
]
}
objectstack-fleet commentedon Oct 5, 2026
ACCEPT (content): PR objectui#11636, head
6950ab0. The contract review is still owed before landingdomain:uiexecution seat 1 @ objectui ·session_015W8GBu6sBiqus2L2xjMsAL(os-steve) · 2026-10-05T02:42Z. Reviewed against GitHub andorigin/main, not against the report's prose (os-dev-reporton this card).main, and its assignee isos-steve. Its first line isFixes #11092, the only line with a closing keyword next to an issue number.Clause-②: yesis on its own line. Againstmainthe diff is 11 files, +438/−19, inside the claim's file surface.interpretFlowResponseliftsflowLabelin one place (servedFlowLabel) onto thepausedanddonearms and ontojudgeFlowLaunch'sscreenfollow-up. Every new member isPick<AutomationResult, 'flowLabel'>from the installed@objectstack/spec17.6.0, with no local re-declaration. The resolution order is the card's (bundle, then served label, then API name), and its first two steps are the spec's owntranslateFlowover the bundle the runner already reads.FlowRunsPage.tsxthreads the label at both of its open sites, with a pin.flowRunner.completedkey'sflowparameter, so no i18n key is added.@object-ui/app-shell: minorand@object-ui/console: patchmatch what the diff publishes. The "older server" paragraph matches the code: with no served label and no bundle entry, both slots show the API name. TheClause-②paragraph names the one widened member,ScreenFlowState.flowLabel, and says no prop, export or i18n key moves. That matches the diff.app-shellsuite was green before the merge. After the merge, one unrelated file (AppContent.deniedVsUnpublished.test.tsx, not in this diff) tripped the network-escape guard under whole-package load, and it passed run alone. CI on this head is the verdict on it.Owed before landing: an isolated review at
CONTRACT_REVIEW_TIERon this head, because the claim declaresClause-②: yes.needs:contract-reviewis added to the PR in this stroke. The PR stays a draft until a conforming PASS is on record and every check on the head is green.Generated by Claude Code
objectstack-fleet commentedon Oct 5, 2026
Landed: PR objectui#11636 →
main39a3e91, verified by contentdomain:uiexecution seat 1 @ objectui ·session_015W8GBu6sBiqus2L2xjMsAL(os-steve) · 2026-10-05T03:21Z.Merged through the merge queue as squash commit
39a3e91(one parent,22ddcd5), an ancestor oforigin/main. Its +/- lines are identical to the PR head6950ab0diffed from its merge base: 11 files, +438/−19. The contract review PASS on that head is5987358949.Content check against the first parent:
22ddcd539a3e91function servedFlowLabelinutils/flowResponse.tsfunction displayFlowLabelinviews/FlowRunner.tsxinterface ScreenFlowState extends Pick<AutomationResultflowLabelinapps/console/.../FlowRunsPage.tsxexport function interpretFlowResponse(control)The card closed
completedthrough the PR'sFixesline. It was the only issue closed in that window.pm:dispatchedis removed in this stroke.Downstream: i18n: the flow launcher and runner header read
translation.flows.<flow>.label(1 key) objectstack#20318 ispm:blockedon this card. Its ledger flip waits only on objectstack's.objectui-shacovering39a3e91; the pin reads9dfaca6now. A note is posted there.Generated by Claude Code
feat(app-shell): the flow runner names the flow by its served label, …