Skip to content

[finding] os lint now demands flows.* translation keys and warns the author for writing them — #11615's new bucket collides with the planned liveness row's authorWarn #11624

Description

@os-zhuang

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 doeswhich rule fireswhat it says
omitsflows.<flow>.… from the bundlei18n/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)lintLivenessPropertiesthe 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 sidepackages/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 sidepackages/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?

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions