|
16 | 16 | // precedents are check-type-source-resolution.mjs, check-test-source-alias.mjs, |
17 | 17 | // check-adr-links.mjs and check-driver-memory-census.mjs. |
18 | 18 | // |
| 19 | +// ── The ruled EXCEPTION: an authorised cross-file move (2026-09-03) ───────── |
| 20 | +// |
| 21 | +// The disjunction above asks WHO may take a registry-growing remedy, and on a |
| 22 | +// shrink-only ratchet the answer was always "a maintainer, per instance". One |
| 23 | +// class of act is now authorised generally instead of one PR at a time. |
| 24 | +// Maintainer ruling, 2026-09-03, adopting option A of item 5 in the skills |
| 25 | +// optimization programme's third decision batch (#14685, comment 5520452691) — |
| 26 | +// verbatim and untranslated: 「同意」; the text it adopts: |
| 27 | +// |
| 28 | +// the per-file line ratchet admits a cross-file move in one PR when the |
| 29 | +// destination's allowance rises by no more than the source's net decrease and |
| 30 | +// total lines do not increase, with the ruling cited in the ratchet comment; |
| 31 | +// `check:ratchet-remedy-authority`'s self-description gains this exception |
| 32 | +// |
| 33 | +// So: a cross-file MOVE in the per-file line ratchet is NOT a maintainer-only |
| 34 | +// remedy while all three conditions hold — the destination's raise is covered by |
| 35 | +// the named sources' net decrease, the map-wide total does not increase, and the |
| 36 | +// raised entry cites that ruling. The scope is exactly the act the ruling names, |
| 37 | +// in the gate the ruling names. A raise failing any condition is an ordinary |
| 38 | +// raise and belongs to a maintainer as before, and no other ratchet in this farm |
| 39 | +// gains anything: the reason a move is not a weakening is that the corpus SHRANK |
| 40 | +// on net, which is a property of the act, not of the author. |
| 41 | +// |
| 42 | +// ⚠️ TODAY THE EXCEPTION IS DOCUMENTARY, and recording that is half the point of |
| 43 | +// writing it here. The corpus walk is a NON-RECURSIVE readdir of `scripts/` (see |
| 44 | +// corpusFiles), and the gate the ruling amends lives one directory down, in |
| 45 | +// scripts/pm/ — it has never been in this sweep, so no verdict of this gate |
| 46 | +// moves. A self-test assertion pins that, so a wider walk reds HERE and makes |
| 47 | +// the class a decision someone takes rather than one that lands in silence. |
| 48 | +// |
| 49 | +// ⛔ NO NEW VERDICT CLASS. `marked` / `refused` / `unmarked` / `excluded` stay as |
| 50 | +// they are. A fifth class for "authorised by standing ruling" would have a |
| 51 | +// population of zero in this corpus, and an unreachable class is a claim of |
| 52 | +// enforcement nobody has — the same defect this file's control corpus exists to |
| 53 | +// prevent, one level up. If the walk ever reaches a gate offering this act, the |
| 54 | +// honest first move is to read that gate's own verdict and record it in CONTROL, |
| 55 | +// the way every instance in this file arrived. |
| 56 | +// |
| 57 | +// ⛔ THIS SECTION IS COMMENT, never a string literal — the header warning above, |
| 58 | +// applied to itself. It describes an act that grows a registry, so in |
| 59 | +// author-facing text it would make this gate an instance of the convention it |
| 60 | +// enforces and flip its own verdict off `excluded`. |
| 61 | +// |
19 | 62 | // WHY A SWEEP AND NOT A SHARED MODULE. #8519 proposed a shared helper module as |
20 | 63 | // the enforcement route. It is not one: a module is reachable only from gates |
21 | 64 | // that choose to import it, so it standardises the gates that already agreed and |
@@ -146,11 +189,12 @@ const SELF_TEST_BATTERIES = Object.freeze({ |
146 | 189 | '(19) …and the mirror: the token in a string literal DOES count. Paired with': 1, |
147 | 190 | '(17) This gate must not be an instance of its own convention.': 1, |
148 | 191 | '(20) The declared population, held to the walk in BOTH directions (#13813)': 7, |
| 192 | + '(21) The ruled cross-file-move exception (2026-09-03)': 2, |
149 | 193 | }); |
150 | 194 |
|
151 | 195 | // DELETING an entry silences that battery's floor exactly as effectively as |
152 | 196 | // zeroing it, so the roster's own size is pinned too. |
153 | | -const SELF_TEST_BATTERY_FLOOR = 20; |
| 197 | +const SELF_TEST_BATTERY_FLOOR = 21; |
154 | 198 |
|
155 | 199 | // The key an assertion is filed under when no battery is open. It is not a |
156 | 200 | // declared battery, so it reds by the same set difference rather than silently |
@@ -1347,6 +1391,29 @@ function selfTest() { |
1347 | 1391 | + 'nothing, and a dead declaration prints as the same silence as declaring nothing)', |
1348 | 1392 | !ROOT_DIR_WATCH_HINTS.some((h) => h.includes('{'))); |
1349 | 1393 |
|
| 1394 | + // ── (21) The ruled cross-file-move exception (2026-09-03) ─────────────── |
| 1395 | + // |
| 1396 | + // Two assertions, and both are about THIS FILE rather than about the ratchet |
| 1397 | + // the ruling amends: where the exception is written down, and whether it is |
| 1398 | + // doing anything yet. Neither can be held by the run — a header section runs |
| 1399 | + // green whatever it says, which is exactly how a documented exception drifts |
| 1400 | + // into a claimed one. |
| 1401 | + battery('(21) The ruled cross-file-move exception (2026-09-03)'); |
| 1402 | + const selfSrc = readFileSync(join(SCRIPTS_DIR, SELF_FILE), 'utf8'); |
| 1403 | + // ⛔ The phrase is ASSEMBLED, never spelled, for the reason the ADD hoist above gives in |
| 1404 | + // its own case: a literal spelling would put the phrase into THIS file's author-facing text |
| 1405 | + // and falsify the assertion below by the act of making it. |
| 1406 | + const EXCEPTION_PHRASE = ['cross-file', 'MOVE'].join(' '); |
| 1407 | + expect('the ruled exception is recorded in COMMENT space only — written into author-facing text ' |
| 1408 | + + 'it would describe a registry-growing act, and this gate would then read as an instance of ' |
| 1409 | + + 'the convention it enforces, which is what assertion (17) catches', |
| 1410 | + selfSrc.includes(EXCEPTION_PHRASE) |
| 1411 | + && !authorFacingMessages(selfSrc).some((m) => m.includes(EXCEPTION_PHRASE))); |
| 1412 | + |
| 1413 | + expect('the ruled exception is documentary today: the amended line ratchet lives one directory ' |
| 1414 | + + 'down and this walk is non-recursive, so a wider walk reds HERE rather than admitting a new ' |
| 1415 | + + 'remedy class in silence', |
| 1416 | + !corpusFiles().includes('check-skill-line-ratchet.mjs')); |
1350 | 1417 | // ── The floor: every declared battery RAN, and ran its cases (#13489) ─── |
1351 | 1418 | // |
1352 | 1419 | // Evaluated after every battery has had its chance and BEFORE the verdict, so |
|
0 commit comments