docs.yml's build-and-freshness job regenerates the gettext catalogs and then asserts
git diff --exit-code -- docs/po. That check is only meaningful if every machine that regenerates
them runs the same gettext version — and nothing pins one. The result is that the committed
catalogs have drifted from what CI's gettext produces, so the gate fails on content the PR never
touched.
Evidence
On PR #10569 (which adds one new page, docs/src/internals/node-api-host.md), the failing step's
diff is dominated by re-wrapping of obsolete #~ entries — text that page has nothing to do with:
-#~ "server. Tracked at [issue #589](https://github.com/PerryTS/perry/issues/"
-#~ "589)."
+#~ "server. Tracked at [issue #589](https://github.com/PerryTS/perry/"
+#~ "issues/589)."
Same string, same content, different wrap column.
Reproducing locally (./docs/i18n.sh extract && ./docs/i18n.sh sync, GNU gettext-tools as shipped by
Homebrew) produces an even larger disagreement across all 11 catalogs:
11 files changed, 92112 insertions(+), 63180 deletions(-)
of which 111,278 are #: source-reference lines and only 3,487 added reference lines mention
the actually-new page. So <3% of the churn is the new content; the rest is generator-version noise.
Why it has gone unnoticed
docs.yml has not run on main since 2026-06-25 (all runs since are workflow_dispatch or PR
events), so the drift accumulated invisibly. build-and-freshness is also not part of the required
pr-gate context, so a red result blocks nothing — it just trains people to ignore the job.
Suggested fix
- Pin the gettext version the catalogs are generated with (a container image, an apt pin, or a
msgmerge --version assertion in i18n.sh that fails loudly on a mismatch).
- Regenerate all 11 catalogs once with that pinned version and commit the result as its own change.
- Consider
--no-location (or --add-location=file) so that moving a line in a source page does not
rewrite thousands of catalog lines.
- Decide whether this job should be required. As it stands it satisfies ★ "a gate that reports
failure without blocking is documentation, not a gate" — and worse, it is currently unable to pass.
Until then, a docs-touching PR cannot make this job green by any action of its author, which is the
state PR #10569 is in.
docs.yml's build-and-freshness job regenerates the gettext catalogs and then assertsgit diff --exit-code -- docs/po. That check is only meaningful if every machine that regeneratesthem runs the same gettext version — and nothing pins one. The result is that the committed
catalogs have drifted from what CI's gettext produces, so the gate fails on content the PR never
touched.
Evidence
On PR #10569 (which adds one new page,
docs/src/internals/node-api-host.md), the failing step'sdiff is dominated by re-wrapping of obsolete
#~entries — text that page has nothing to do with:Same string, same content, different wrap column.
Reproducing locally (
./docs/i18n.sh extract && ./docs/i18n.sh sync, GNU gettext-tools as shipped byHomebrew) produces an even larger disagreement across all 11 catalogs:
of which 111,278 are
#:source-reference lines and only 3,487 added reference lines mentionthe actually-new page. So <3% of the churn is the new content; the rest is generator-version noise.
Why it has gone unnoticed
docs.ymlhas not run onmainsince 2026-06-25 (all runs since areworkflow_dispatchor PRevents), so the drift accumulated invisibly.
build-and-freshnessis also not part of the requiredpr-gatecontext, so a red result blocks nothing — it just trains people to ignore the job.Suggested fix
msgmerge --versionassertion ini18n.shthat fails loudly on a mismatch).--no-location(or--add-location=file) so that moving a line in a source page does notrewrite thousands of catalog lines.
failure without blocking is documentation, not a gate" — and worse, it is currently unable to pass.
Until then, a docs-touching PR cannot make this job green by any action of its author, which is the
state PR #10569 is in.