Introduced by #11615 (card #11485), merged 2026-08-24T09:41:46Z. Filed unassigned, for grading.
Two rules in the same os lint run point opposite ways
| the author does | which rule fires | what it says |
|---|
omitsflows.<flow>.… from the bundle | i18n/missing-flow (new in #11615) | the key is missing a translation for locale X |
adds it (e.g. from os i18n extract, also new in #11615) | lintLivenessProperties | the flows group is planned — nothing reads it |
There is no third option. Both are warning, so nothing is broken today, but under --i18n-strict
the missing side becomes an error — so a project can be forced to author keys that then draw the
planned-group warning.
Measured at origin/main, not inferred
The demand side — packages/cli/src/utils/i18n-extract.ts's walkScreenFlows (added by #11615)
harvests flows.<flow>.label, flows.<flow>.screens.<node>.title and
.fields.<field>.{label,placeholder}; COVERAGE_SOURCE maps them to the flow bucket, reported as
i18n/missing-flow per locale. os i18n extract scaffolds the same skeleton.
The warn side — packages/spec/liveness/translation.json:63:
"flows": {
"status": "planned",
"authorWarn": true,
"authorHint": "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."
}And the warn side genuinely reaches authored bundles — this is the part that makes the collision
live rather than theoretical. packages/lint/src/lint-liveness-properties.ts:387 reads
const bundles = asArray(stack.translations);, walking the locale-keyed bundle shape apps actually
author. That is #11288's fix (closed): before it, the whole translation ledger warned nobody, so
authorWarn: true on this row was inert. It is not inert now.
packages/lint/src/authoring-rules.ts:1081 registers lintLivenessProperties with commands: ALL
and surfaces: CLI_ONLY — the same command the new bucket reports from.
Why this is #11615's to answer rather than a pre-existing tension
Before #11615, os lint could not report a screen-flow copy gap at all — that absence was the
whole defect #11485 fixed. So the demand side did not exist and there was nothing to collide with.
The fix is right; the interaction was not considered.
Suggested shape — an input, ⛔ not a ruling
The mechanism to resolve it is already present on both sides: the flows row's own status.
Gate the flow coverage bucket on that row being live. It then turns itself on the day the
objectui runner half of #7646 lands and the row flips — which is the same event the ledger's note
already names as the flip condition ("Flip to live with an objectui screen-flow-runner evidence
pointer when the downstream consumer card lands").
⛔ Dropping authorWarn from the row is not the fix: the warning is true. No shipped runner reads
the group, so a translated wizard string is still stored and never shown — exactly the failure mode
validationMessages was removed in 17.0.0 for.
⚠️ Whoever takes this should check whether the same collision exists for any other planned +
authorWarn group the coverage walker harvests. This finding measured flows only.
Doc half
content/docs/ui/translations.mdx:255 ("Current boundaries") is now incomplete rather than
false. It still correctly says the group is declared, not applied, and that the compile lint warns
when you author it — but an author reading it has no way to know that os lint will now also report
the keys as missing. Whatever this card resolves to, that paragraph needs the other half.
Review note
This was missed at review. The accepting seat checked #11615's Clause-② determination, the ratchet
bump against the script's own precedent, the ablation, the public surface and the cross-repo
consequence, and did not check the liveness status of the group the new bucket now demands keys
for — despite the PR body naming flows as spec-settled by #11287. Recording it so the next review
of a "new coverage bucket" card has the question in hand: what does the ledger say about the group
this bucket now asks authors to fill in?
Introduced by #11615 (card #11485), merged 2026-08-24T09:41:46Z. Filed unassigned, for grading.
Two rules in the same
os lintrun point opposite waysflows.<flow>.…from the bundlei18n/missing-flow(new in #11615)os i18n extract, also new in #11615)lintLivenessPropertiesflowsgroup isplanned— nothing reads itThere is no third option. Both are
warning, so nothing is broken today, but under--i18n-strictthe missing side becomes an error — so a project can be forced to author keys that then draw the
planned-group warning.
Measured at
origin/main, not inferredThe demand side —
packages/cli/src/utils/i18n-extract.ts'swalkScreenFlows(added by #11615)harvests
flows.<flow>.label,flows.<flow>.screens.<node>.titleand.fields.<field>.{label,placeholder};COVERAGE_SOURCEmaps them to theflowbucket, reported asi18n/missing-flowper locale.os i18n extractscaffolds the same skeleton.The warn side —
packages/spec/liveness/translation.json:63:And the warn side genuinely reaches authored bundles — this is the part that makes the collision
live rather than theoretical.
packages/lint/src/lint-liveness-properties.ts:387readsconst bundles = asArray(stack.translations);, walking the locale-keyed bundle shape apps actuallyauthor. That is #11288's fix (closed): before it, the whole translation ledger warned nobody, so
authorWarn: trueon this row was inert. It is not inert now.packages/lint/src/authoring-rules.ts:1081registerslintLivenessPropertieswithcommands: ALLand
surfaces: CLI_ONLY— the same command the new bucket reports from.Why this is #11615's to answer rather than a pre-existing tension
Before #11615,
os lintcould not report a screen-flow copy gap at all — that absence was thewhole defect #11485 fixed. So the demand side did not exist and there was nothing to collide with.
The fix is right; the interaction was not considered.
Suggested shape — an input, ⛔ not a ruling
The mechanism to resolve it is already present on both sides: the
flowsrow's ownstatus.Gate the
flowcoverage bucket on that row beinglive. It then turns itself on the day theobjectui runner half of #7646 lands and the row flips — which is the same event the ledger's note
already names as the flip condition ("Flip to
livewith an objectui screen-flow-runner evidencepointer when the downstream consumer card lands").
⛔ Dropping
authorWarnfrom the row is not the fix: the warning is true. No shipped runner readsthe group, so a translated wizard string is still stored and never shown — exactly the failure mode
validationMessageswas removed in 17.0.0 for.planned+authorWarngroup the coverage walker harvests. This finding measuredflowsonly.Doc half
content/docs/ui/translations.mdx:255("Current boundaries") is now incomplete rather thanfalse. It still correctly says the group is declared, not applied, and that the compile lint warns
when you author it — but an author reading it has no way to know that
os lintwill now also reportthe keys as missing. Whatever this card resolves to, that paragraph needs the other half.
Review note
This was missed at review. The accepting seat checked #11615's Clause-② determination, the ratchet
bump against the script's own precedent, the ablation, the public surface and the cross-repo
consequence, and did not check the liveness status of the group the new bucket now demands keys
for — despite the PR body naming
flowsas spec-settled by #11287. Recording it so the next reviewof a "new coverage bucket" card has the question in hand: what does the ledger say about the group
this bucket now asks authors to fill in?