Repository navigation
Commit aead296
fix(spec): the flows translation group goes live — the flow runner reads flows.FLOW.label (#21859)
Fixes #20318
Clause-②: no
Stage 3 of the card: the objectstack half that follows objectui's new
reader. Three things land together. The `flows` translation group goes
`live` in the liveness ledger, in both halves. The tests that pinned the
old ledger state move with it. And the CLI's coverage walker demands a
flow's own label only where the runner can show it, per `domain:cli`'s
ruling A on #6024. Every check measured here is green,
`check:i18n-coverage` included, and every example sits at its committed
baseline.
## The reader, measured at the pin
`.objectui-sha` on `main` is `0abd4f9f8769`. `git merge-base
--is-ancestor 39a3e91fad 0abd4f9f8769` exits 0, so the pin carries
objectui PR 11636. A positive answer proves itself even on a shallow
clone. Read at `0abd4f9f8769`, read-only:
- `packages/app-shell/src/views/FlowRunner.tsx:268-275`
`displayFlowLabel` returns `translateFlow({ name: flowName, label:
flowLabel }, bundle, { locale: language }).label || flowName`. That is
the active language's `flows.FLOW.label`, then the served `flowLabel`,
then the API name.
- `:377` computes `shownFlowLabel`. `:541` draws it in the dialog
header, above the step's `DialogTitle`.
- `:441` hands `displayFlowLabel(...)` to the `flowRunner.completed`
completion toast. The toast shows only when the flow authored no
`successMessage`.
- `:211-229` `activeFlowsBundle` reads the active language's `flows`
group from the i18next `translation` resource tree. The `screens`
overlay uses the same read.
- `translateFlow` is imported from `@objectstack/spec/system` (`:163`).
- The served label comes from
`packages/app-shell/src/utils/flowResponse.ts:234-235`
(`servedFlowLabel`, on the trigger and resume 200 bodies).
- The three hosts open the runner with that label:
`hooks/useConsoleActionRuntime.tsx:653`,
`views/RecordDetailView.tsx:1118`, and
`apps/console/src/pages/developer/FlowRunsPage.tsx:310`.
- The reader grep from `domain:cli`'s ruling was re-run at the pin. `git
grep -E "translateFlow\b|displayFlowLabel"` over `packages/`, excluding
tests and stories, still returns 8 hits: 7 in `FlowRunner.tsx` and 1
comment in `metadata-admin/inspectors/flow-nested-selection.ts`. No
reader of a non-screen flow's translated label exists, and the pin has
not moved.
## What changed
- **`packages/spec/liveness/translation.json`**
- `flows.children.label`: `planned` to `live`, with `verifiedAt
2026-10-05` and `evidenceScope: cross-repo`. The evidence uses
`file#symbol` anchors, with each objectui pointer prefixed `objectui
@0abd4f9f8:` as the ledger README prescribes. The `producer` names the
engine's `AutomationResult.flowLabel` copy, the objectui lift and hosts,
and the bundle delivery chain the `screens` row already cites.
- The `flows` container: both children are now `live`, so it is `live`
too, with its own evidence. It drops `authorWarn` and `authorHint`,
because a `live` row refuses them.
- The `screens` row's note gains one dated line saying that its "NOT
flipped with it" clause is superseded.
- **`packages/spec/liveness/state-counts/translation.md`**: regenerated
with `gen:liveness-counts`. `translation` is now `26 live · 0 planned`,
up from `25 · 1`.
- **`content/docs/ui/translations.mdx`**: the boundary bullet now says
what is true.
- Both halves are applied: `screens` through the screen copy, and
`label` through the runner header and the toast.
- Three strings stay outside the group: the screen `description`, an
authored `successMessage`, and the runner chrome.
- The tooling asks for the keys, and asks for a flow's own `label` only
when the flow has a screen node.
- The "the day the runner lands" sentence is gone.
- **`packages/spec/src/system/translation.zod.ts`**: the `flows` TSDoc
said "The flow's own `label` is read by nothing yet". It now names the
reader and the pin. This is the JSDoc only. No `.describe()` string
moved, so nothing generated was built from it.
- **`examples/`**: the three screen wizards get their `flows` copy in
every supported locale. That covers `crm_convert_lead_wizard` (zh-CN, 4
strings), `showcase_reassign_wizard` (zh-CN, 3) and `quick_add_task`
(zh-CN and ja-JP, 7 each).
- **Changesets**, measured with `npm pack --dry-run`:
- `.changeset/20318-flows-translation-live.md` is an `@objectstack/spec`
patch. `liveness/translation.json`,
`liveness/state-counts/translation.md` and
`src/system/translation.zod.ts` all ship, and the control
`dist/index.js` ships too.
- `.changeset/20318-cli-flow-label-demand.md` is an `@objectstack/cli`
patch. `dist/utils/i18n-extract.js` ships (597 files packed; the control
`package.json` is present), and the built file carries the predicate.
- `content/docs` and `examples/` publish nothing.
### The flow label is demanded only for a flow the runner can open
(`domain:cli` ruling A)
- **`packages/cli/src/utils/i18n-extract.ts`**: one predicate in
`walkScreenFlows`. The `flows.FLOW.label` entry is emitted only when
`collectFlowNodesDeep`, the node universe the screens walk already reads
at any depth, yields a `screen` node. Nothing else in the walker moves:
the node list is computed one statement earlier, so the label entry
keeps its place ahead of the screen entries.
- The predicate's comment names `FlowRunner` as the reader it mirrors
(ruling condition (b)). It also says the predicate widens in the same
change as any future reader of a non-screen flow's label.
- **Pins** in `packages/cli/test/i18n-flow-screen-coverage.test.ts`
(ruling condition (a)):
- a scheduled flow with a region and no screen node emits no
`flows.FLOW.label` entry;
- a flow whose only screen node sits two regions deep (a loop body, then
a parallel branch) still emits it;
- in one tree holding both flows, the coverage gate asks for the screen
flow's label and not the other's.
- The existing "reads the region TABLE" test now expects `[]` for its
flow, whose only screen-shaped object sits in an `http` payload, so the
flow has no screen node and owes no label. It gains a lit control: the
same flow with one real screen emits the label and that screen, and
still not the payload object.
- **Control legs**, each run through `scripts/ablation-replace.mjs` and
proved by "ok mutation landed" and "ok restored: blob == HEAD
(521163a) and `git diff HEAD` is empty". The test imports `../src`,
so no build is in the path.
- **The predicate removed** (the label pushed unconditionally) reads
`Tests 3 failed | 21 passed (24)`: the no-screen pin, the one-tree pin
and the region-table test.
- **The predicate over the flat top-level `flow.nodes`** instead of the
deep universe reads `Tests 2 failed | 22 passed (24)`: the region-nested
pin and the one-tree pin. The region-nested pin cannot go red by
removing the predicate, since an unconditional label satisfies it, so
this second leg is the one that proves it.
### The tests that pinned the old ledger move with the flip
These are test and comment edits, with cross-lane declarations to
`domain:cli` and `domain:devx`. No pin was deleted.
- **`packages/cli/test/i18n-flow-liveness-gate.test.ts`**: its
real-ledger pins now read the post-flip ledger.
- The census pin: `authorWarnedTranslationGroups()` equals `[]`, still
an equality.
- Its "can say no" twin gets its positive from the same reader,
`authorWarnedProperties('object')` naming `externalSharingModel`.
- The walk with the ledger's own set equals the ungated walk, and
`flows` is among its roots.
- The untranslated wizard draws exactly five `i18n/missing-flow` keys,
and the translated one draws none.
- The collision pin: omitting the keys draws demand and no warning, and
authoring them draws neither. A witness in the same call raises the one
warned object row.
- The skeleton scaffolds `flows`.
- The injected-set pin "gates whatever the ledger names" still holds the
gate for a warned group.
- **`packages/lint/src/lint-liveness-properties.test.ts`**:
- The locale-keyed walk pins and the runtime-`TranslationItem`
anti-fixture now read a planted synthetic `planned` + `authorWarn` group
row. They go through `lintLivenessPropertiesFromLedgerDir`, the seam the
synthetic `field` ledger already uses.
- The real-ledger `translation.flows (planned)` case splits into a
silence pin on the shipped row (beside a witness that still warns) and a
planted twin that holds the planned-with-`authorHint` wording.
- The two silence pins now author `flows` too.
- The `authorWarnedProperties` census reads `[]` for translation, with
the `object` positive.
- The anti-drift pin asks over every top-level row the translation
ledger governs, and over the `object` type's non-empty warned set.
- **Comments only**:
- In `packages/cli/src/utils/i18n-extract.ts`, the extract-surface
header lists `inlineHelpText` and says why `flows` is walked, and the
`authorWarnedTranslationGroups` docblock says the set is empty today.
- The header of `packages/cli/test/i18n-flow-screen-coverage.test.ts`.
- `translateFlow`'s docblock in
`packages/spec/src/system/i18n-resolver.ts`.
No new id: no new or rewritten test title, test string, comment headline
or runtime string carries a tracker id as its citation. The new pins,
the predicate's comment and the CLI changeset sentence cite behaviour
and the reader, not a tracker number.
## What dropping `authorWarn` switched on, and what the predicate takes
back
With the ledger flipped and no predicate, `os lint ... --json` reported
56 new `i18n/missing-flow` findings across the examples:
| example | findings | from screen flows | `label` of a flow with no
screen node |
|:--|--:|--:|--:|
| `app-crm` | 4 | 4 | 0 |
| `app-showcase` | 32 | 3 | 29 |
| `app-todo` (zh-CN + ja-JP) | 20 | 14 | 6 |
The 21 screen-flow strings are translated in this PR. The 35 label
demands for flows that never open the runner are what the predicate
removes. `check:i18n-coverage` now reads "OK (13 config(s), 621
baselined untranslated string(s), none new)". The ratchet is two-sided,
so crm, showcase and todo sit exactly at their committed 101, 414 and
106. `scripts/i18n-coverage-baseline.json` is untouched, and none of the
35 inert strings is authored. `check:i18n` stays green.
## Tests and gates (at `bac6651dd7`, every package built with
declarations)
- The build was `turbo run build --filter=!@objectstack/docs` (72 tasks,
exit 0).
- CLI unit tier: `Test Files 259 passed (259)`, `Tests 3786 passed
(3786)`. That is 3783 plus the three new predicate pins. The
packed-`.d.ts` pins are included.
- Full `@objectstack/lint` suite: `Tests 5629 passed (5629)`.
- `@objectstack/cli` typecheck exit 0, test layer included ("held in
test-typecheck-debt.json", unchanged).
- The re-pinned files: the CLI screen-coverage file reads `Tests 24
passed (24)` at HEAD. lint `lint-liveness-properties` is unchanged since
`dfa18e7699`, where it read `Tests 97 passed (97)`, and it is inside the
5629 above. The flip's control leg on the re-pinned real-ledger pins
(pre-flip ledger restored by a trap, proved by blob hash and an empty
`git diff HEAD`) read CLI `6 failed | 23 passed` and lint `5 failed | 92
passed`, while the planted pins stayed green.
- `check:liveness` exit 0. `check:i18n-coverage` exit 0, as above.
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 115. The `--ran` reconciliation
reads "115 derived famil(ies) accounted for — 115 run, 0 NOT-MEASURED",
and all 115 exited 0.
- `origin/main` (`9f9510f25e`) touches none of these 15 files and does
not move `.objectui-sha` (still `0abd4f9f8769`; its last change is
`8832655af2`). Not merged, not rebased. The one derived-from file main
changed, `scripts/engine-double-contract.pinned.json`, gained an
objectql test pin unrelated to this diff.
## Acceptance notes
- The flip, the re-pins and the predicate are one commit set on purpose.
Each re-pinned file pins the real ledger state, and the predicate is
what lets the flip's demand side match its reader, so any part landed
alone is red.
- Ruling condition (c) is still live until this lands: if
`.objectui-sha` moves first, re-run the reader grep on the new pin, and
stop if a reader of a non-screen flow's translated label has appeared.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 9f9510f commit aead296
15 files changed
Lines changed: 471 additions & 186 deletions
File tree
- .changeset
- content/docs/ui
- examples
- app-crm/src/translations
- app-showcase/src/system/translations
- app-todo/src/translations
- packages
- cli
- src/utils
- test
- lint/src
- spec
- liveness
- state-counts
- src/system
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
382 | 382 | | |
383 | 383 | | |
384 | 384 | | |
385 | | - | |
386 | | - | |
387 | | - | |
388 | | - | |
389 | | - | |
390 | | - | |
391 | | - | |
392 | | - | |
393 | | - | |
394 | | - | |
395 | | - | |
396 | | - | |
397 | | - | |
398 | | - | |
399 | | - | |
400 | | - | |
401 | | - | |
402 | | - | |
403 | | - | |
404 | | - | |
405 | | - | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
406 | 409 | | |
407 | 410 | | |
408 | 411 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
210 | 210 | | |
211 | 211 | | |
212 | 212 | | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
213 | 225 | | |
214 | 226 | | |
215 | 227 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1088 | 1088 | | |
1089 | 1089 | | |
1090 | 1090 | | |
| 1091 | + | |
| 1092 | + | |
| 1093 | + | |
| 1094 | + | |
| 1095 | + | |
| 1096 | + | |
| 1097 | + | |
| 1098 | + | |
| 1099 | + | |
| 1100 | + | |
| 1101 | + | |
| 1102 | + | |
| 1103 | + | |
1091 | 1104 | | |
1092 | 1105 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
91 | 91 | | |
92 | 92 | | |
93 | 93 | | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
94 | 113 | | |
95 | 114 | | |
96 | 115 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
99 | 99 | | |
100 | 100 | | |
101 | 101 | | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
102 | 121 | | |
103 | 122 | | |
104 | 123 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
107 | 107 | | |
108 | 108 | | |
109 | 109 | | |
110 | | - | |
111 | | - | |
112 | | - | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
113 | 114 | | |
114 | 115 | | |
115 | 116 | | |
| |||
1067 | 1068 | | |
1068 | 1069 | | |
1069 | 1070 | | |
1070 | | - | |
1071 | | - | |
| 1071 | + | |
| 1072 | + | |
| 1073 | + | |
1072 | 1074 | | |
1073 | 1075 | | |
1074 | 1076 | | |
| |||
1083 | 1085 | | |
1084 | 1086 | | |
1085 | 1087 | | |
1086 | | - | |
1087 | | - | |
1088 | | - | |
1089 | | - | |
1090 | | - | |
| 1088 | + | |
| 1089 | + | |
| 1090 | + | |
| 1091 | + | |
| 1092 | + | |
| 1093 | + | |
1091 | 1094 | | |
1092 | 1095 | | |
1093 | 1096 | | |
1094 | 1097 | | |
1095 | | - | |
1096 | | - | |
1097 | | - | |
1098 | | - | |
1099 | | - | |
| 1098 | + | |
| 1099 | + | |
| 1100 | + | |
| 1101 | + | |
1100 | 1102 | | |
1101 | 1103 | | |
1102 | 1104 | | |
| |||
1823 | 1825 | | |
1824 | 1826 | | |
1825 | 1827 | | |
| 1828 | + | |
| 1829 | + | |
1826 | 1830 | | |
1827 | 1831 | | |
1828 | 1832 | | |
1829 | | - | |
1830 | | - | |
1831 | | - | |
| 1833 | + | |
| 1834 | + | |
| 1835 | + | |
| 1836 | + | |
| 1837 | + | |
| 1838 | + | |
| 1839 | + | |
| 1840 | + | |
| 1841 | + | |
| 1842 | + | |
| 1843 | + | |
| 1844 | + | |
| 1845 | + | |
| 1846 | + | |
1832 | 1847 | | |
1833 | 1848 | | |
1834 | 1849 | | |
| |||
0 commit comments