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): let a followed module declare what a gate inherits by importing it (#12346)
* fix(pm): let a followed module declare what a gate inherits by importing it
`scripts/pm/dispatch-gates.mjs` is importable and is not a discovered gate
file — `check:pm-dispatch-gates` resolves to `check-dispatch-gates.mjs`, which
reaches the tool by `spawnSync` — so `discoverFamilies`' "never open a module
that is itself a gate file" rule does not cover it. A gate that imports it
inherits its module-body literals as watch hints: measured on c48d46d over
6840 tracked files, nine literals covering 2660 of them, of which exactly one
(the workflow directory the tool readdirs, 28 files) is a population the module
opens. The other 2632 pairs are package-manifest join bases and tier globs.
The only thing standing between that and a dispatch prompt was prose in one
caller's header (#8162) — a convention held by the caller that remembered, not
a property of the module. This adds a marker the MODULE carries:
// dispatch-gates: inherited-population <path> [<path> ...] -- <reason>
read fresh on every run by the follow, and held to a SUBSET of what the module
really spells: a declared path the module does not spell is refused, so the
marker can only ever narrow what a caller inherits, never invent it.
Measured, on this tree:
a gate importing the tool inherits 9 literals / 2660 files -> 1 / 28
this file's own watch-hint set 9 before, 9 after (added [], removed [])
derivation output over 10 paths byte-identical, plain/--residue/--tier
families / (gate, file) pairs 173 / 70007 before and after
modules declaring the marker 1 (this one), pinned live
Nothing inherits from the module today, so no existing card loses a lead; the
gate now prints the counterfactual price rather than leaving it in prose.
* test(pm): read the live declaration through ?. so deleting the marker names a case instead of aborting the run
---------
Co-authored-by: Claude <noreply@anthropic.com>
// ── What a gate that IMPORTS this module inherits (#11556) ─────────────────
186
+
//
187
+
// This module is importable and is NOT a discovered gate file — `check:pm-dispatch-gates`
188
+
// resolves to `check-dispatch-gates.mjs`, which reaches the tool by `spawnSync`, so the
189
+
// follow's "never open a module that is itself a gate file" rule does not cover it. A gate
190
+
// that imports it therefore inherits its module-body literals as watch hints. Measured on
191
+
// c48d46d70a, over 6840 tracked files: nine literals, covering 2660 of them. Exactly ONE is
192
+
// a population this file opens (the workflow directory `discoverFamilies` readdirs, 28
193
+
// files); the other eight are the package-manifest join bases `discoverFamilies` builds
194
+
// paths FROM and the tier globs `MANDATORY_TIER_GLOBS`/`SUSPECT_TIER_GLOBS` declare — 2632
195
+
// pairs of population no caller reads.
196
+
//
197
+
// Until now the only thing standing between that and a dispatch prompt was prose in ONE
198
+
// caller's header (`check-dispatch-gates.mjs`, #8162): a convention held by the caller that
199
+
// remembered, not a property of this module. The line below makes it the module's own
200
+
// declaration, read fresh on every run and held to a subset of what this file really spells
201
+
// — see `declaredInheritedPopulation`.
202
+
// dispatch-gates: inherited-population .github/workflows -- the workflow directory this tool readdirs; every other module-body literal here is a package-manifest join base or a tier glob, not a path this file opens (#11556)
t('no marker present reads as no declaration — the module contributes everything it spells',declaredInheritedPopulation("const A = '.github/workflows';\n")===null);
6356
+
t(
6357
+
'a marker carrying only a reason does not parse as a declaration (it reads as no marker, so the module keeps contributing — never a silent blanket opt-out)',
6358
+
declaredInheritedPopulation("const A = '.github/workflows';\n// dispatch-gates: inherited-population -- everything here is a join base\n")===null,
6359
+
);
6360
+
t(
6361
+
'the marker must be its OWN line here too — a mention inside prose is a discussion of the convention, not a declaration under it',
6362
+
declaredInheritedPopulation("const A = '.github/workflows';\n// see dispatch-gates: inherited-population .github/workflows -- for how a module opts out\n")===null,
6363
+
);
6364
+
// NARROWING ONLY. This is the load-bearing invariant: an opt-out that could
6365
+
// also opt IN would be the hand-written path map this file's contract exists
6366
+
// to refuse, and it would be invisible — a declared path nothing spells reads
6367
+
// exactly like a real one in the MATCHED column.
6368
+
t(
6369
+
'a declared path the module does not spell is REFUSED, not silently inherited',
6370
+
(()=>{
6371
+
try{
6372
+
declaredInheritedPopulation("const A = '.github/workflows';\n// dispatch-gates: inherited-population packages/spec/src/** -- invented\n");
6373
+
returnfalse;
6374
+
}catch(e){
6375
+
return/mayonlyNARROW/.test(String(e.message));
6376
+
}
6377
+
})(),
6378
+
);
6379
+
t(
6380
+
'and the refusal names every invented path, not just the first',
6381
+
(()=>{
6382
+
try{
6383
+
declaredInheritedPopulation("const A = '.github/workflows';\n// dispatch-gates: inherited-population packages/a packages/b -- invented\n");
0 commit comments