You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
i18n: TranslationBundle has no surface for screen-flow UI — wizard screens render source-language only, even in a fully localized app #7646
HotCRM (main 333259a4, @objectstack 17.0.0-rc.5) completed all four locales and retired its i18n exemption ledger (hotcrm#645/#679). Yet its lead-conversion wizard (lead_conversion, type: 'screen') renders entirely in English on a zh-CN console:
screen title: Conversion Details
field labels: Create Opportunity? / Opportunity Name / Opportunity Amount
runner buttons: Cancel / Submit
Root cause
The TranslationBundle schema (@objectstack/spectranslation.zod) exposes surfaces for objects, apps, dashboards, actions — there is no flows / screen surface at all. An app author has no place to put translations for screen titles or screen field labels: this is not a missing key, it is a missing vocabulary.
A flows surface in the bundle (per-flow: label, per-screen: title, per-field: label/placeholder/help), applied by the screen-flow runner the same way object/field labels already are. The runner chrome (Cancel/Submit) presumably belongs to the console's own message catalog instead.
Repro
zh-CN console → HotCRM → any lead → 转化线索 → wizard is English top to bottom.
Filed from HotCRM main; happy to wire the HotCRM-side bundle entries as soon as the surface exists.
Classification: contract-shape proposal, not a defect — the card asks for a NEW top-level surface (flows) in a public spec schema. That is new accepted vocabulary (the acceptance surface widens), which is a maintainer call on shape and appetite: per-flow/per-screen/per-field granularity, whether runner chrome (Cancel/Submit) is console-catalog instead (as the card itself suggests), and whether this rides the v17 window or waits.
Premise verified on origin/main @ 5823d59: packages/spec/src/system/translation.zod.ts declares exactly objects (:391), apps (:394), dashboards (:440) as top-level bundle surfaces (+ nested actions); zero flows:/screens: hits in the schema. The gap is real and is a missing vocabulary, not a missing key.
Routing: acceptance-surface change ⇒ domain:spec (red line: any card changing accept/reject behavior lands there, however it eventually splits). If accepted, expect a rule-2 contract-first split: spec surface first, screen-flow runner application + console catalog second (Blocked-by: the spec half).
Maintainer ruling recorded 2026-08-11 (spec-lane PM session chat, verbatim: 「接受你的建议,开始加速处理」, accepting the lane sweep's recommendation on this card).
Ruling: direction accepted. TranslationBundle gains a flows surface — per-flow label; per-screen title; per-field label/placeholder/help — applied by the screen-flow runner the same way object/field labels already are. Runner chrome (Cancel/Submit) stays in the console's own message catalog, NOT the bundle. Contract-first split: this card is the spec surface; the objectui runner-application card and the HotCRM wiring are downstream, filed by their consuming seats once this lands.
State: needs-user-decision → pm:queue (spec lane; dispatches at the next batch slot).
Claim: PM loop round 2 (spec lane — slot freed by #7657's merge)
Session: session_01JY2Q5Xto1u8YHADgrZDTnk
Branch: claude/issue-7646-translationbundle-flows-surface
Worktree: cloud dev session (own container)
Domain: domain:spec
File surface: packages/spec translation schema family (translation.zod — locate the source; the card quotes the dts) + pins + generated closure. ⛔ The screen-flow RUNNER application (objectui) and HotCRM bundle wiring are downstream cards, NOT this one. Stop on breach; explain in the report.
Container & model: M (new contract vocabulary surface under a direction ruling — shape design within a ruled boundary), mode:cloud, model: opus
Serial constraints cleared: no in-flight writer on the translation schema family (open PRs + queue checked 12:55Z); in-flight lane cards touch secret-fields / i18n-resolver+protocol / component.zod / userActions / filter.zod — disjoint. Note #7634 (in flight) touches packages/spec/src/system/i18n-resolver.ts — i18n-ADJACENT but a different file face; both devs' surfaces declared, no overlap expected.
{
"issue": 7646,
"status": "done",
"branch": "claude/issue-7646-translationbundle-flows-surface",
"pr": "https://github.com/objectstack-ai/objectstack/pull/7763",
"premise_still_valid": true,
"summary": "Premise re-verified on origin/main @2f8328c: translationDataShape() declared objects/apps/messages/globalActions/dashboards/pages/settings/metadataForms/settingsCommon and zero flows/screens keys, so a wizard's copy had no authoring slot and .strict() refused any group a translator invented. Added a `flows` group to the SHARED shape, so it lands on both doors at once (file-authored bundle + `translation` metadata item) — flows.<flow_name>.label, .screens.<node_id>.title, .screens.<node_id>.fields.<field_name>.{label,placeholder} — strictObject at all three levels with the family's surface/history/aliases/guidance conventions and every key optional (partial-locale semantics). Addressing was MEASURED, not assumed: Flow.name (flow.zod.ts:614), FlowNode.id (flow.zod.ts:263, forwarded verbatim as ScreenSpec.nodeId at contracts/automation-service.ts:138 — the same id that correlates a resume back to its pause point), ScreenFieldConfig.name (builtin-node-config.zod.ts:382, forwarded as ScreenFieldSpec.name); a test derives every bundle key from a parsed FlowSchema so the surface cannot be keyed by names nothing produces. Per-field face measured against ScreenFieldConfigSchema and one of the ruling's three keys FALSIFIED: `help` has no counterpart at all (the field is name/label/type/required/options/defaultValue/placeholder/visibleWhen), so declaring it would parse clean and translate nothing — the ADR-0078 shape #6080 kept out of the page-component face — and unlike #6080 there is no honest alias target either, since `placeholder` is the in-input hint rather than help text; it rides `guidance` instead, alongside `options` (ScreenFieldConfig.options[].value is z.unknown(), so a value-keyed map cannot address them). Runner chrome stays out of the bundle, pinned by a test. Liveness row is `planned` + authorWarn/authorHint, drilled over label/screens (app.navigation.runAction precedent), because no shipped runner reads the group yet. Changeset @objectstack/spec MINOR: the accepted authoring surface widens additively — a bundle previously rejected for carrying `flows` now parses; no existing shape changes meaning, so it is not a patch and not a break.",
"tests": "All FOREGROUND, under the shared flock and NODE_OPTIONS=--max-old-space-size=4096. `pnpm --filter @objectstack/spec test -- --maxWorkers=2` → 'Test Files 378 passed (378) / Tests 9947 passed (9947)'. translation.test.ts alone → 110 passed (12 new, in a `screen-flow copy (#7646)` block). `pnpm --filter @objectstack/spec typecheck` → clean; test layer: 'OK — compiles under tsconfig.test.json; 57 file(s) / 265 error(s) held in test-typecheck-debt.json' (pre-existing shrink-only debt, unchanged by this PR). REVERSE VERIFICATION, direction predicted before running as PLAIN RED (the surface is new and its pins address it directly, so no inversion was possible): fix removed via patch file (`git diff > schema.patch; git checkout -- <file>`) — NEVER git stash — gave 'Tests 11 failed | 99 passed (110)', the failures being the fully-populated parse, the partial-locale parse, the addressing pin, both alias/guidance pins, the JSON-Schema describe pin, the item-door pin and the all-groups-together pin; `git apply` restored 110/110. GATES: check:liveness → first run '✗ 1 UNCLASSIFIED — translation/flows', then '✗ 1 UNDECLARED container inheritance', both closed by the ledger row + one-level drill + gen:liveness-counts, final run '✓ every governed-type property … is classified … every container inheritance is declared' with 'translation 21 classified (live 19, planned 2)'. check:generated → first run named 3 stale artifacts (gen:api-surface / gen:docs / gen:strictness-ledger), regenerated after `pnpm --filter @objectstack/spec build`, final '✓ All 13 generated artifacts are up to date'. check:authorable-surface → green, 1590 schemas (the baseRev lag line is informational, per its own text 'Not an error either way'). check:merge-driver / check:adr-anchors / check:spec-parsed-alias / check-nul-bytes → all exit 0 (verified with explicit echo EXIT=$?, not by pipe status). check:i18n → first run 'PREREQUISITE NOT MET — the workspace CLI is not built'; after `turbo run build --filter=@objectstack/cli` → 'OK (9 package(s) — all bundles in sync, no undeclared authoring keys)'. Self-scanned the touched files for control bytes beyond the gate's surface (grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]') → no hits. CI on PR #7763: NOT polled — status in_progress at report time, per the draft-PR-time reporting contract (#6644 L2).",
"open_questions": [
{
"question": "Three further screen-flow strings are authorable and user-visible but are OUTSIDE the recorded ruling's enumeration, so I implemented the ruled face exactly and did not widen. Should the flows surface grow to cover them? (a) `screens.<node_id>.description` — ScreenConfigSchema.description, the body text under the screen heading; (b) `flows.<name>.successMessage` / `.errorMessage` — FlowSchema declares both and AutomationResult carries them to the runner as the terminal toast; (c) `screens.<id>.fields.<name>.options` — select-option labels, which need an addressing decision because ScreenFieldConfig.options[].value is z.unknown().",
"options": [
"A — leave the face as ruled; each of the three is refused by name with a guidance sentence saying it is not on this surface (what this PR does).",
"B — widen now to (a) and (b) in a follow-up commit on this PR, leaving (c) for its own addressing ruling.",
"C — widen to all three, deciding (c)'s addressing (e.g. String(value)-keyed with a documented coercion, or an index-keyed array).",
"D — decide nothing until the objectui runner card measures which of the three the runner actually draws."
],
"recommendation": "A now, B as the follow-up card, and 裁决不可重裁 is why: the ruling enumerated per-flow label / per-screen title / per-field copy, and widening a ruled contract surface unilaterally is exactly what the escalation path exists to prevent — so this PR ships the ruled face and reports the measurement instead of acting on it. On the three axes: real business need is REAL but unproven for (a)/(b) — HotCRM's filed repro lists the screen title and three field labels and no body text or toast, so the pull is inferred rather than measured, and the honest next step is for the HotCRM seat to say whether its wizard authors them; long-term soundness favours widening in ONE further ruled step rather than key-by-key, since each addition re-cuts the same generated closure and ledger; AI-authoring safety already holds either way, because every excluded key is refused loudly with a guidance sentence rather than silently stripped — the failure mode is a visible rejection, not a translation that vanishes. (c) should not ride (a)/(b): its cost is an addressing decision on unconstrained values, not a key."
},
{
"question": "The resolver-side half — a `FLOW_SCREEN_COPY_KEYS` sibling of `PAGE_COMPONENT_COPY_KEYS`, which drives both translatePage's overlay and the CLI's skeleton extraction — was deliberately NOT written here, because it lives in packages/spec/src/system/i18n-resolver.ts and #7634 is in flight on that file (flagged in the dispatch prompt). Who picks it up?",
"options": [
"A — fold it into the downstream objectui runner-application card, which is what will consume it.",
"B — file a standalone spec follow-up with `Blocked-by: #7634` so it lands the moment that file is free."
],
"recommendation": "B, and it is closer to ready than it looks: the pattern is fully worked out by #6080 (a const key list, a translate* overlay reading it, the CLI extractor reading the same list, and a test pinning the list against the schema so neither can drift), so it is a mechanical port rather than a design. Doing it as its own card also keeps the objectui runner card free to be about rendering. I did not file it — a `Blocked-by:` filing against an in-flight sibling is the PM's race to arbitrate, not mine."
}
],
"out_of_scope_findings": []
}
Notes the JSON does not carry:
No issues filed. Nothing off-card broke; both items above are decision-class on the surface this PR just built, so they are open_questions for adjudication rather than findings.
packages/lint/src/validate-translation-references.ts is untouched and correct as-is: its own header states that "an unrecognised top-level namespace is skipped, never reported", so flows is inert there rather than broken. Reference validation for flow/screen/field keys is a natural third follow-up, in the same lane as the resolver half.
Addendum to the report above (not a second report — the JSON in that comment stands, and its <!-- os-dev-report --> marker was read back intact).
One further commit landed on the same branch after the report, c0ebc18 — content/docs/ui/translations.mdx only, so PR #7763 is now 2 commits / 9 files. The docs-drift check fanned out over 106 pages (advisory, and mostly the @objectstack/spec dependency graph rather than this diff), but one of them is this surface's own reference page: it enumerates every translatable surface in a table and keeps a "Current boundaries" honest-limits list. Adding a group without touching it would have left the page describing a bundle that no longer exists — the drift this repo's docs-accuracy discipline is aimed at — so it gains the flows row plus a boundary bullet stating what is true today: declared and addressed the way the runner resolves it, no shipped runner reading it yet (liveness planned, compile lint warns), no help text on a screen field to translate, and runner chrome in the console's message catalog. No release-owned page touched. check:generated re-run green after the commit.
Everything else is unchanged: still a draft PR, CI still not polled, and CI convergence / ready-flip / landing remain the PM's. Worktree torn down (unforced git worktree remove, node_modules deleted first) and no background process or monitor left running.
Activity
os-zhuang commentedon Aug 11, 2026
Triage:
needs-user-decision+domain:spec.flows) in a public spec schema. That is new accepted vocabulary (the acceptance surface widens), which is a maintainer call on shape and appetite: per-flow/per-screen/per-field granularity, whether runner chrome (Cancel/Submit) is console-catalog instead (as the card itself suggests), and whether this rides the v17 window or waits.origin/main@5823d59:packages/spec/src/system/translation.zod.tsdeclares exactlyobjects(:391),apps(:394),dashboards(:440) as top-level bundle surfaces (+ nested actions); zeroflows:/screens:hits in the schema. The gap is real and is a missing vocabulary, not a missing key.domain:spec(red line: any card changing accept/reject behavior lands there, however it eventually splits). If accepted, expect a rule-2 contract-first split: spec surface first, screen-flow runner application + console catalog second (Blocked-by:the spec half).target:v17: not applied while in the decision box (decision-card precedent updateViewConfig stores "per-view personal config" as ONE org-wide metadata item — sort/hiddenFields/columnState/rowHeight written by any user apply to every user of the view #7494); if the maintainer accepts it as a v17 item, the execution split inherits the judgment then.本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
os-zhuang commentedon Aug 11, 2026
Maintainer ruling recorded 2026-08-11 (spec-lane PM session chat, verbatim: 「接受你的建议,开始加速处理」, accepting the lane sweep's recommendation on this card).
Ruling: direction accepted. TranslationBundle gains a
flowssurface — per-flow label; per-screen title; per-field label/placeholder/help — applied by the screen-flow runner the same way object/field labels already are. Runner chrome (Cancel/Submit) stays in the console's own message catalog, NOT the bundle. Contract-first split: this card is the spec surface; the objectui runner-application card and the HotCRM wiring are downstream, filed by their consuming seats once this lands.State:
needs-user-decision→pm:queue(spec lane; dispatches at the next batch slot).Generated by Claude Code
os-zhuang commentedon Aug 11, 2026
Claim: PM loop round 2 (spec lane — slot freed by #7657's merge)
Session:
session_01JY2Q5Xto1u8YHADgrZDTnkBranch:
claude/issue-7646-translationbundle-flows-surfaceWorktree: cloud dev session (own container)
Domain:
domain:specFile surface:
packages/spectranslation schema family (translation.zod— locate the source; the card quotes the dts) + pins + generated closure. ⛔ The screen-flow RUNNER application (objectui) and HotCRM bundle wiring are downstream cards, NOT this one. Stop on breach; explain in the report.Container & model: M (new contract vocabulary surface under a direction ruling — shape design within a ruled boundary),
mode:cloud, model: opusSerial constraints cleared: no in-flight writer on the translation schema family (open PRs + queue checked 12:55Z); in-flight lane cards touch secret-fields / i18n-resolver+protocol / component.zod / userActions / filter.zod — disjoint. Note #7634 (in flight) touches
packages/spec/src/system/i18n-resolver.ts— i18n-ADJACENT but a different file face; both devs' surfaces declared, no overlap expected.Generated by Claude Code
os-zhuang commentedon Aug 11, 2026
OS-DEV-REPORT — #7646
{ "issue": 7646, "status": "done", "branch": "claude/issue-7646-translationbundle-flows-surface", "pr": "https://github.com/objectstack-ai/objectstack/pull/7763", "premise_still_valid": true, "summary": "Premise re-verified on origin/main @2f8328c: translationDataShape() declared objects/apps/messages/globalActions/dashboards/pages/settings/metadataForms/settingsCommon and zero flows/screens keys, so a wizard's copy had no authoring slot and .strict() refused any group a translator invented. Added a `flows` group to the SHARED shape, so it lands on both doors at once (file-authored bundle + `translation` metadata item) — flows.<flow_name>.label, .screens.<node_id>.title, .screens.<node_id>.fields.<field_name>.{label,placeholder} — strictObject at all three levels with the family's surface/history/aliases/guidance conventions and every key optional (partial-locale semantics). Addressing was MEASURED, not assumed: Flow.name (flow.zod.ts:614), FlowNode.id (flow.zod.ts:263, forwarded verbatim as ScreenSpec.nodeId at contracts/automation-service.ts:138 — the same id that correlates a resume back to its pause point), ScreenFieldConfig.name (builtin-node-config.zod.ts:382, forwarded as ScreenFieldSpec.name); a test derives every bundle key from a parsed FlowSchema so the surface cannot be keyed by names nothing produces. Per-field face measured against ScreenFieldConfigSchema and one of the ruling's three keys FALSIFIED: `help` has no counterpart at all (the field is name/label/type/required/options/defaultValue/placeholder/visibleWhen), so declaring it would parse clean and translate nothing — the ADR-0078 shape #6080 kept out of the page-component face — and unlike #6080 there is no honest alias target either, since `placeholder` is the in-input hint rather than help text; it rides `guidance` instead, alongside `options` (ScreenFieldConfig.options[].value is z.unknown(), so a value-keyed map cannot address them). Runner chrome stays out of the bundle, pinned by a test. Liveness row is `planned` + authorWarn/authorHint, drilled over label/screens (app.navigation.runAction precedent), because no shipped runner reads the group yet. Changeset @objectstack/spec MINOR: the accepted authoring surface widens additively — a bundle previously rejected for carrying `flows` now parses; no existing shape changes meaning, so it is not a patch and not a break.", "tests": "All FOREGROUND, under the shared flock and NODE_OPTIONS=--max-old-space-size=4096. `pnpm --filter @objectstack/spec test -- --maxWorkers=2` → 'Test Files 378 passed (378) / Tests 9947 passed (9947)'. translation.test.ts alone → 110 passed (12 new, in a `screen-flow copy (#7646)` block). `pnpm --filter @objectstack/spec typecheck` → clean; test layer: 'OK — compiles under tsconfig.test.json; 57 file(s) / 265 error(s) held in test-typecheck-debt.json' (pre-existing shrink-only debt, unchanged by this PR). REVERSE VERIFICATION, direction predicted before running as PLAIN RED (the surface is new and its pins address it directly, so no inversion was possible): fix removed via patch file (`git diff > schema.patch; git checkout -- <file>`) — NEVER git stash — gave 'Tests 11 failed | 99 passed (110)', the failures being the fully-populated parse, the partial-locale parse, the addressing pin, both alias/guidance pins, the JSON-Schema describe pin, the item-door pin and the all-groups-together pin; `git apply` restored 110/110. GATES: check:liveness → first run '✗ 1 UNCLASSIFIED — translation/flows', then '✗ 1 UNDECLARED container inheritance', both closed by the ledger row + one-level drill + gen:liveness-counts, final run '✓ every governed-type property … is classified … every container inheritance is declared' with 'translation 21 classified (live 19, planned 2)'. check:generated → first run named 3 stale artifacts (gen:api-surface / gen:docs / gen:strictness-ledger), regenerated after `pnpm --filter @objectstack/spec build`, final '✓ All 13 generated artifacts are up to date'. check:authorable-surface → green, 1590 schemas (the baseRev lag line is informational, per its own text 'Not an error either way'). check:merge-driver / check:adr-anchors / check:spec-parsed-alias / check-nul-bytes → all exit 0 (verified with explicit echo EXIT=$?, not by pipe status). check:i18n → first run 'PREREQUISITE NOT MET — the workspace CLI is not built'; after `turbo run build --filter=@objectstack/cli` → 'OK (9 package(s) — all bundles in sync, no undeclared authoring keys)'. Self-scanned the touched files for control bytes beyond the gate's surface (grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]') → no hits. CI on PR #7763: NOT polled — status in_progress at report time, per the draft-PR-time reporting contract (#6644 L2).", "open_questions": [ { "question": "Three further screen-flow strings are authorable and user-visible but are OUTSIDE the recorded ruling's enumeration, so I implemented the ruled face exactly and did not widen. Should the flows surface grow to cover them? (a) `screens.<node_id>.description` — ScreenConfigSchema.description, the body text under the screen heading; (b) `flows.<name>.successMessage` / `.errorMessage` — FlowSchema declares both and AutomationResult carries them to the runner as the terminal toast; (c) `screens.<id>.fields.<name>.options` — select-option labels, which need an addressing decision because ScreenFieldConfig.options[].value is z.unknown().", "options": [ "A — leave the face as ruled; each of the three is refused by name with a guidance sentence saying it is not on this surface (what this PR does).", "B — widen now to (a) and (b) in a follow-up commit on this PR, leaving (c) for its own addressing ruling.", "C — widen to all three, deciding (c)'s addressing (e.g. String(value)-keyed with a documented coercion, or an index-keyed array).", "D — decide nothing until the objectui runner card measures which of the three the runner actually draws." ], "recommendation": "A now, B as the follow-up card, and 裁决不可重裁 is why: the ruling enumerated per-flow label / per-screen title / per-field copy, and widening a ruled contract surface unilaterally is exactly what the escalation path exists to prevent — so this PR ships the ruled face and reports the measurement instead of acting on it. On the three axes: real business need is REAL but unproven for (a)/(b) — HotCRM's filed repro lists the screen title and three field labels and no body text or toast, so the pull is inferred rather than measured, and the honest next step is for the HotCRM seat to say whether its wizard authors them; long-term soundness favours widening in ONE further ruled step rather than key-by-key, since each addition re-cuts the same generated closure and ledger; AI-authoring safety already holds either way, because every excluded key is refused loudly with a guidance sentence rather than silently stripped — the failure mode is a visible rejection, not a translation that vanishes. (c) should not ride (a)/(b): its cost is an addressing decision on unconstrained values, not a key." }, { "question": "The resolver-side half — a `FLOW_SCREEN_COPY_KEYS` sibling of `PAGE_COMPONENT_COPY_KEYS`, which drives both translatePage's overlay and the CLI's skeleton extraction — was deliberately NOT written here, because it lives in packages/spec/src/system/i18n-resolver.ts and #7634 is in flight on that file (flagged in the dispatch prompt). Who picks it up?", "options": [ "A — fold it into the downstream objectui runner-application card, which is what will consume it.", "B — file a standalone spec follow-up with `Blocked-by: #7634` so it lands the moment that file is free." ], "recommendation": "B, and it is closer to ready than it looks: the pattern is fully worked out by #6080 (a const key list, a translate* overlay reading it, the CLI extractor reading the same list, and a test pinning the list against the schema so neither can drift), so it is a mechanical port rather than a design. Doing it as its own card also keeps the objectui runner card free to be about rendering. I did not file it — a `Blocked-by:` filing against an in-flight sibling is the PM's race to arbitrate, not mine." } ], "out_of_scope_findings": [] }Notes the JSON does not carry:
open_questionsfor adjudication rather than findings.packages/lint/src/validate-translation-references.tsis untouched and correct as-is: its own header states that "an unrecognised top-level namespace is skipped, never reported", soflowsis inert there rather than broken. Reference validation for flow/screen/field keys is a natural third follow-up, in the same lane as the resolver half.Generated by Claude Code
docs(ui): document the `flows` translation surface and its planned bo…
os-zhuang commentedon Aug 11, 2026
Addendum to the report above (not a second report — the JSON in that comment stands, and its
<!-- os-dev-report -->marker was read back intact).One further commit landed on the same branch after the report,
c0ebc18—content/docs/ui/translations.mdxonly, so PR #7763 is now 2 commits / 9 files. The docs-drift check fanned out over 106 pages (advisory, and mostly the@objectstack/specdependency graph rather than this diff), but one of them is this surface's own reference page: it enumerates every translatable surface in a table and keeps a "Current boundaries" honest-limits list. Adding a group without touching it would have left the page describing a bundle that no longer exists — the drift this repo's docs-accuracy discipline is aimed at — so it gains theflowsrow plus a boundary bullet stating what is true today: declared and addressed the way the runner resolves it, no shipped runner reading it yet (livenessplanned, compile lint warns), no help text on a screen field to translate, and runner chrome in the console's message catalog. No release-owned page touched.check:generatedre-run green after the commit.Everything else is unchanged: still a draft PR, CI still not polled, and CI convergence / ready-flip / landing remain the PM's. Worktree torn down (unforced
git worktree remove,node_modulesdeleted first) and no background process or monitor left running.Generated by Claude Code
feat(spec): give TranslationBundle a `flows` surface for screen-flow …
feat(spec): FLOW_SCREEN_COPY_KEYS resolver family for screen-flow copy (
tooling(i18n): ledger the three unwalked translation groups by mainta…
tooling(i18n): gate that every declared translation group is walked b…
spec: a flow screen field can express a numeric bound, help text and …
fix(spec): grade action.onSuccess.navigate/openIn and translation.flo…
test(spec): the last system/ file group's test titles state each cite…