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
fix(scripts): anchor check-i18n-coverage to a module-derived root and refuse an empty population (#11397)
* fix(scripts): anchor check-i18n-coverage to a module-derived root and refuse an empty population
`scripts/check-i18n-coverage.mjs` resolved every path CWD-relatively — `examples`,
`packages`, the baseline, and the CLI stub it spawns. Run from anywhere but the repo
root it discovered no configs, compared nothing, and printed
`OK (0 config(s), 0 baselined untranslated string(s), none new)` with exit 0 — the
same sentence and the same exit code a real pass uses.
What made that silent rather than merely wrong is an interlock: this is a two-sided
ratchet, so a config that vanishes is normally caught by the DOWN direction. But the
population and the baseline were resolved the same way, so a wrong root emptied both
together and left the comparison with nothing to disagree about.
Two halves:
1. Every read is anchored to a root derived from `import.meta.url`, as
`check-skills-token-ratchet.mjs` and `check-ratchet-remedy-authority.mjs` do. The
repo-relative spellings stay — they are the committed baseline's KEYS and the text
a reader acts on — and `at()` is the one seam between the two. The `os lint` spawn
gets `cwd: REPO_ROOT`, which is what resolves the repo-relative `CLI` and config
paths it is handed.
2. An empty population is refused rather than returned: zero is a broken scan, not a
repo with nothing to translate, the rule `trackedFiles` states in
`scripts/pm/dispatch-gates.mjs`. Judged on the union, not per half, so it cannot
preempt the legitimate ratchet-DOWN path when a single config is retired. It is
placed before any CLI is spawned and before `--update` can write, which over an
empty population would have discarded all twelve baselined entries.
The self-test gains the non-vacuity proof this gate could not previously give: it
chdirs out of the repo and asserts discovery still resolves the same 12 configs,
spelled repo-relative, and pins the empty-population classifier red on nothing and
silent on real work.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx
* fix(pm): record the bare-root verdict PACKAGES_DIR owes the invisible-species worklist
`node scripts/pm/bare-root-worklist.mjs --self-test` went red on f5fe23b:
FRESH: check:i18n-coverage PACKAGES_DIR packages.
Naming the previously anonymous `'packages'` default parameter `PACKAGES_DIR`
made it match `POPULATION_CONSTANT`, so the sweep saw it for the first time and
demanded a verdict. The invisibility is not new — the literal was always a bare
single-segment word the dispatch derivation cannot build a hint from — but it
was previously unnameable by the sweep too, so nothing recorded it. The row now
records a population that was already there.
Verdict: REFUSE-UNSPELLABLE, measured. `discoverPackages` admits files named
`i18n-extract.config.ts` beneath a `scripts` segment — 9 of 5035 tracked files
under the root (0.18%), the narrowest row on the list. That is a FILENAME
filter, and `collapseHint` can only ever name a whole subtree, so the sole
spellable declaration would name this gate for 5035 files to reach 9 — the
costlier error `hintCovers` prices, and the same shape its `EXAMPLES_DIR examples`
sibling was already refused for at 1.3%.
Also records the refusal beside the constants themselves, as
`check-driver-conformance.mjs` and `check-examples-live-imports.mjs` do, so the
next reader meets the reasoning at the population rather than only in the triage.
The gate is not weakened, narrowed or skipped, and the scope expansion that
surfaced the row stands. Executable content of `check-i18n-coverage.mjs` is
byte-identical to f5fe23b with comments masked and blank lines removed
(23418 == 23418); the only non-comment change is one added blank line.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx
---------
Co-authored-by: Claude <noreply@anthropic.com>
console.log('✓ check:i18n-coverage --self-test — the missing-CLI-build, i18n-rule and per-config-failure classifiers all go red, stay distinct, and a failing config does not end the round.');
728
+
console.log(
729
+
`✓ check:i18n-coverage --self-test — the missing-CLI-build, i18n-rule and per-config-failure classifiers all go red, `+
730
+
`stay distinct, and a failing config does not end the round; the population resolves to ${onRoot.length} config(s) `+
731
+
`from outside the repo root as well as inside it, and an empty one is refused rather than reported OK.`,
732
+
);
562
733
}
563
734
564
735
if(process.argv.includes('--self-test')){
@@ -654,6 +825,35 @@ function reportUnmeasuredConfigs(failures, measuredCount) {
654
825
process.exit(1);
655
826
}
656
827
828
+
/**
829
+
* The refusal for an empty population (#10907) — reached before a single CLI is
830
+
* spawned and before `--update` can write, which is the point on both counts.
831
+
*
832
+
* `--update` is the sharper of the two: it runs BEFORE any comparison, so over an
833
+
* empty population it would write `{}` and ratchet all twelve baselined configs
834
+
* out of existence — real, frozen debt discarded by a command whose whole purpose
835
+
* is to record it. Same invariant the two reports above state, and for the same
836
+
* reason: nothing measured, nothing written.
837
+
*
838
+
* Exits 1, the code every other verdict here uses.
0 commit comments