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(pm): derive check:i18n by owning-package walk in dispatch-gates (#8363)
* fix(pm): derive check:i18n by owning-package walk in dispatch-gates
`check:i18n` was invisible to the derivation in BOTH halves of the output.
The path half cannot reach it: check-i18n-bundles.mjs discovers its targets
at runtime by walking packages/ for files named i18n-extract.config.ts, so
its source names no population path to match. Worse than silent — it does
carry eleven path-ish literals (CLI prerequisite, stale-dist checks), so it
also never lands in the "repo-wide / undetermined" bucket. The convention
half did not know the kind. Measured on PR #8348: an object-definition edit
under packages/services/service-messaging/src/objects/ regenerates that
package's four bundles, and the derivation named the gate nowhere at all.
Adds a second CHANGE_KIND_GATES entry that repeats the gate's own walk
(same skip set, same filename-plus-/scripts/ test) and matches any input
path inside an owning package. The whole package counts, config file
included — narrowing to src/objects/** would under-cover, since extraction
reads whatever each package's config enumerates.
Lists nothing: the KIND is written down, never its population, so a tenth
package growing a bundle is matched by the next run. Owner population
verified at parity with the gate's own nine. Self-test 28 -> 47 cases,
pinning both directions and the new entry's STALE branch.
Fixes#8352
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01139NJ9Wg5pFeZi1Zh8WLg6
* test(pm): anchor the i18n gate-name pins on exact rendered delimiters
Found by reverse-verifying the entry above: renaming its gate to
`check:i18n-renamed-probe` made the live run print the STALE line exactly as
designed, and the self-test stayed green at 47/47 — `includes('pnpm
check:i18n')` is satisfied by every name that merely STARTS with it, so a
prefix-preserving rename is invisible to a substring pin. That is precisely
the rot the STALE branch exists to report.
Anchors both pins on the rendered delimiters (`- pnpm x —`, `⚠ x: STALE`)
so the name must match exactly. Re-verified: the same rename now fails 2
cases instead of 0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01139NJ9Wg5pFeZi1Zh8WLg6
---------
Co-authored-by: Claude <noreply@anthropic.com>
why: "it re-extracts every owning package's translation bundles and fails on drift, so any edit that changes what the extractor emits (an object definition, a label, the config itself) moves it — regenerate with `node scripts/check-i18n-bundles.mjs --write`",
// i18n change-kind derivation — the pure judgments first, each mirroring one
525
+
// line of the gate's own `findConfigs`.
526
+
t('an extract config under scripts/ is one',isExtractConfigPath('packages/services/service-messaging/scripts/i18n-extract.config.ts'));
527
+
t('the same filename OUTSIDE scripts/ is not',!isExtractConfigPath('packages/services/service-messaging/src/i18n-extract.config.ts'));
528
+
t('another config under scripts/ is not',!isExtractConfigPath('packages/platform-objects/scripts/build-docs.config.ts'));
529
+
t('owner is the package above scripts/',owningPackageOfExtractConfig('packages/plugins/plugin-audit/scripts/i18n-extract.config.ts')==='packages/plugins/plugin-audit');
530
+
t('an owner collapsing to a bare top-level dir is refused',owningPackageOfExtractConfig('packages/scripts/i18n-extract.config.ts')===null);
t('a deep path inside an owning package qualifies',isInI18nBundlePackage('packages/services/service-messaging/src/objects/http-delivery.object.ts',owners));
534
+
t('the config file itself qualifies (whole package, not just objects)',isInI18nBundlePackage('packages/services/service-messaging/scripts/i18n-extract.config.ts',owners));
t('a path in a package WITHOUT a config does not',!isInI18nBundlePackage('packages/objectql/src/engine.ts',owners));
537
+
t('a sibling sharing a name prefix does not',!isInI18nBundlePackage('packages/services/service-messaging-extra/src/x.ts',owners));
538
+
t('a parent directory does not drag in owners below it',!isInI18nBundlePackage('packages/services',owners));
539
+
540
+
// The walk itself, against the real tree — the half no fixture can prove.
541
+
constliveOwners=i18nBundlePackageDirs();
542
+
t('the live walk discovers owning packages',liveOwners.length>0&&liveOwners.every((d)=>d.startsWith('packages/')));
543
+
t('the live walk finds no duplicate owners',newSet(liveOwners).size===liveOwners.length);
544
+
t('the live walk excludes a package that owns no config',!liveOwners.includes('packages/objectql'));
545
+
// Regression pin for the measured miss (PR #8348): this exact path derived no
546
+
// check:i18n. If service-messaging ever stops owning a bundle, this case fails
547
+
// and the answer is to re-point it at a package that does, not to delete it.
548
+
t('the measured incident path now derives the kind',isInI18nBundlePackage('packages/services/service-messaging/src/objects/http-delivery.object.ts',liveOwners));
549
+
550
+
// The name assertions below anchor on the rendered DELIMITERS (`- pnpm x —`,
551
+
// `⚠ x: STALE`), not on a bare substring. Measured while reverse-verifying this
552
+
// entry: renaming the gate to `check:i18n-renamed-probe` made the live run
553
+
// print STALE exactly as designed, and a `includes('pnpm check:i18n')` pin
554
+
// stayed green through it — every prefix-preserving rename is invisible to a
555
+
// substring, which is the one class of rot the STALE branch exists to catch.
t('a path outside every owning package emits no i18n section',!changeKindLines(['packages/objectql/src/engine.ts'],resolved).some((l)=>l.includes('check:i18n')));
560
+
395
561
// The table's own rot detector: a name no live run discovers must say so,
0 commit comments