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
TranslationData.flows — the screen-flow copy vocabulary #7646 declared — has no reader anywhere in the shipped platform. An app can author flows.<name>.screens.<node_id>.title today, have it parse, validate and load into the i18n tree, and the wizard still renders the author's source-language strings in every locale.
This is the runner half #7646 explicitly deferred. Filing it because it is not filed anywhere: #7646 is closed, its report named two follow-ups (the objectui runner application, and a FLOW_SCREEN_COPY_KEYS resolver helper Blocked-by: #7634), and neither exists as a card. #7634 closed 2026-08-11, so the resolver half is unblocked.
Measurement (@objectstack/spec / service-automation / console all at 17.1.0)
Real AutomationEngine running HotCRM's real lead_conversion flow, with the real zh-CN bundle carrying the proposed flows section loaded into a real FileI18nAdapter registered as the i18n service on the engine context, and locale: 'zh-CN' on the execution context:
=== 1. carrier: does TranslationDataSchema accept `flows`? ===
safeParse(zh-CN + flows).success = true
=== 2. liveness ledger shipped in @objectstack/spec@17.1.0 ===
flows.status = planned
flows.authorWarn = true
children.label = planned
children.screens = planned
(sibling control) objects.status = live | dashboards.status = live | pages.status = live
=== 3. spec i18n-resolver export surface ===
resolver/translator exports: resolveActionConfirm, resolveActionLabel, resolveActionResultDialog,
resolveActionSuccess, resolveBookClaimedDocs, resolveBookTree, resolveBundleLocale, resolveDocAudiences,
resolveDocLocale, resolveMetadataFormLabels, resolveMetadataTypeDescription, resolveMetadataTypeLabel,
resolveObjectFieldLabels, resolveSettings*, resolveTabLabel, resolveViewDescription, resolveViewLabel,
translateAction, translateApp, translateDashboard, translateMetadataDocument, translateObject,
translatePage, translateView
any naming flow/screen? -> NONE
=== 4. POSITIVE CONTROL — same bundle, a surface that does have a reader ===
translateObject(crm_lead).label = 线索
i18n.t("objects.crm_lead.label","zh-CN") = 线索
i18n.t("flows.lead_conversion.screens.screen_1.title","zh-CN") = 转化详情 <- the string IS in the tree
=== 5. THE MEASUREMENT — the real screen flow, same bundle live ===
run status = paused
screen.nodeId = screen_1
screen.title = "Conversion Details"
screen.fields[].label = ["createOpportunity: Create Opportunity?",
"opportunityName: Opportunity Name",
"opportunityAmount: Opportunity Amount"]
The string is resolvable by key and nothing asks for it.
Where the two halves would land
Server.packages/services/service-automation — the screen executor builds the spec from raw node config with no locale in scope:
Client. The console's screen-flow dialog renders the payload verbatim — no t() on this path:
// @objectstack/console 17.1.0, RecordDetailView chunk(0,J.jsx)(Ge,{children: s.title||`Input`})// dialog title
children:[e.label||e.name,e.required&&…]// field label
and the console's metadata-l10n hook enumerates exactly objects / fields / fieldOptions / apps / dashboards / pages / reports / globalActions — it exposes objectLabel, fieldLabel, dashboardLabel, widgetTitle, pageLabel, viewLabel, sectionLabel, actionLabel, … and no flow/screen accessor. Its namespace-detection predicate does not list flows either.
Neither is a drift: the ledger row says so in its own words —
No shipped screen-flow runner reads this group yet — until the objectui half of #7646 lands, a type: 'screen' flow renders the strings authored on the flow (config.title, fields[].label, fields[].placeholder) in every locale.
Also unreachable from the tooling side
@objectstack/cli's i18n-coverage.jsCOVERAGE_SOURCE taxonomy has no flow/screen bucket, so os lint's i18n/missing-* family can never report a screen-flow gap, and os i18n extract never scaffolds one. An app whose i18n gate is green is green because the surface is invisible to it, not because it is covered. Measured on HotCRM: pnpm lint:i18n-gate prints 0 i18n/missing-* issues on a tree where six screen dialogs are English in all four locales.
packages/lint/src/validate-translation-references.ts skips unrecognised top-level namespaces, so authored flows keys are also unvalidated against the flow they name.
The ledger's own authorWarn: true on this row does not reach the author either — measured, and it falsified my expectation rather than confirming it. Injecting a flows: section into a real app's locale bundle and re-running objectstack lint --json produced zero new findings (91 total issues before and after, 0 liveness findings naming flows). Cause: lintLivenessProperties walks stack.translations items as TranslationItems, but that collection is z.array(TranslationBundleSchema) — locale-keyed — so every warned-property lookup misses. Filed separately as lintLivenessProperties walks stack.translations as if each item were a TranslationItem — it is a locale-keyed bundle, so the whole translation ledger warns nobody #11288. Net effect for this card: authoring the group today is silent, not merely inert.
The runner application: either server-side (localize ScreenSpec before it goes on the wire, which needs a locale on the execution context) or client-side in the console/objectui screen dialog. Picking the side is the design decision this card owes — the client already holds ScreenSpec.nodeId and ScreenFieldSpec.name, which is what the addressing was measured against, so the client side needs no new identifier.
@objectstack/cli i18n coverage/extract: add a flow/screen bucket so the gap becomes visible to os lint once a reader exists.
Flip the flows ledger row (and its label / screens children) to live with the evidence pointer; drop authorWarn.
Runner chrome stays out of scope, per the #7646 maintainer ruling: Cancel / Submit / Submitting… / Save & Continue / the terminal Done toast are the console's own words in every app (currently hardcoded English literals in the console bundle) and belong in the console's message catalog, not in a per-app bundle. Worth noting the toast separately: FlowSchema.successMessage overrides Done, but successMessage is deliberately not on the flows translation surface, so a localized terminal toast has no channel at all today.
Back-link
Found while working objectstack-ai/hotcrm#1210 — the HotCRM card asking for the four bundle sections. That card cannot be implemented as written: authoring the sections today writes translations nothing renders, and (per the measurement above) nothing warns about it. HotCRM is holding until this lands.
No labels applied — this spans spec + console/objectui, so routing is triage's call.
Summary
TranslationData.flows— the screen-flow copy vocabulary #7646 declared — has no reader anywhere in the shipped platform. An app can authorflows.<name>.screens.<node_id>.titletoday, have it parse, validate and load into the i18n tree, and the wizard still renders the author's source-language strings in every locale.This is the runner half #7646 explicitly deferred. Filing it because it is not filed anywhere: #7646 is closed, its report named two follow-ups (the objectui runner application, and a
FLOW_SCREEN_COPY_KEYSresolver helperBlocked-by: #7634), and neither exists as a card. #7634 closed 2026-08-11, so the resolver half is unblocked.Measurement (
@objectstack/spec/service-automation/consoleall at 17.1.0)Real
AutomationEnginerunning HotCRM's reallead_conversionflow, with the real zh-CN bundle carrying the proposedflowssection loaded into a realFileI18nAdapterregistered as thei18nservice on the engine context, andlocale: 'zh-CN'on the execution context:The string is resolvable by key and nothing asks for it.
Where the two halves would land
Server.
packages/services/service-automation— thescreenexecutor builds the spec from raw node config with no locale in scope:Client. The console's screen-flow dialog renders the payload verbatim — no
t()on this path:and the console's metadata-l10n hook enumerates exactly
objects / fields / fieldOptions / apps / dashboards / pages / reports / globalActions— it exposesobjectLabel,fieldLabel,dashboardLabel,widgetTitle,pageLabel,viewLabel,sectionLabel,actionLabel, … and no flow/screen accessor. Its namespace-detection predicate does not listflowseither.Neither is a drift: the ledger row says so in its own words —
Also unreachable from the tooling side
@objectstack/cli'si18n-coverage.jsCOVERAGE_SOURCEtaxonomy has noflow/screenbucket, soos lint'si18n/missing-*family can never report a screen-flow gap, andos i18n extractnever scaffolds one. An app whose i18n gate is green is green because the surface is invisible to it, not because it is covered. Measured on HotCRM:pnpm lint:i18n-gateprints0 i18n/missing-* issueson a tree where six screen dialogs are English in all four locales.packages/lint/src/validate-translation-references.tsskips unrecognised top-level namespaces, so authoredflowskeys are also unvalidated against the flow they name.authorWarn: trueon this row does not reach the author either — measured, and it falsified my expectation rather than confirming it. Injecting aflows:section into a real app's locale bundle and re-runningobjectstack lint --jsonproduced zero new findings (91 total issues before and after, 0 liveness findings naming flows). Cause:lintLivenessPropertieswalksstack.translationsitems asTranslationItems, but that collection isz.array(TranslationBundleSchema)— locale-keyed — so every warned-property lookup misses. Filed separately aslintLivenessPropertieswalksstack.translationsas if each item were aTranslationItem— it is a locale-keyed bundle, so the whole translation ledger warns nobody #11288. Net effect for this card: authoring the group today is silent, not merely inert.Suggested scope
packages/spec/src/system/i18n-resolver.ts— aFLOW_SCREEN_COPY_KEYSsibling ofPAGE_COMPONENT_COPY_KEYS, plus thetranslateFlow/resolveFlowScreenTitleoverlay it drives (i18n: TranslationBundle has no surface for screen-flow UI — wizard screens render source-language only, even in a fully localized app #7646's own recommendation B;Blocked-by: #7634is discharged — i18n:GET /i18n/localesdeclares a localelabelas "Display name" but every producer sets it to the code #7634 closed 2026-08-11).ScreenSpecbefore it goes on the wire, which needs a locale on the execution context) or client-side in the console/objectui screen dialog. Picking the side is the design decision this card owes — the client already holdsScreenSpec.nodeIdandScreenFieldSpec.name, which is what the addressing was measured against, so the client side needs no new identifier.@objectstack/clii18n coverage/extract: add a flow/screen bucket so the gap becomes visible toos lintonce a reader exists.flowsledger row (and itslabel/screenschildren) tolivewith the evidence pointer; dropauthorWarn.Runner chrome stays out of scope, per the #7646 maintainer ruling:
Cancel/Submit/Submitting…/Save & Continue/ the terminalDonetoast are the console's own words in every app (currently hardcoded English literals in the console bundle) and belong in the console's message catalog, not in a per-app bundle. Worth noting the toast separately:FlowSchema.successMessageoverridesDone, butsuccessMessageis deliberately not on the flows translation surface, so a localized terminal toast has no channel at all today.Back-link
Found while working objectstack-ai/hotcrm#1210 — the HotCRM card asking for the four bundle sections. That card cannot be implemented as written: authoring the sections today writes translations nothing renders, and (per the measurement above) nothing warns about it. HotCRM is holding until this lands.
No labels applied — this spans spec + console/objectui, so routing is triage's call.