|
55 | 55 | * `apps/`, not `examples/`, not package `scripts/` trees. |
56 | 56 | * - The ledger file itself is excluded by name: its waiver tables spell |
57 | 57 | * `code: '…'` about codes, which is mention, not stamping. |
| 58 | + * - A CONSTANT is read at its definition, by its initializer, whatever its |
| 59 | + * name (`constdef`). The rule used to be the NAME — a `*_CODE` binding — |
| 60 | + * and that proxy missed eleven declared constants in five packages, three |
| 61 | + * of them with no provenance record at all (#22664). Two consequences are |
| 62 | + * declared rather than left implicit: the site is attributed to the |
| 63 | + * package that DEFINES the constant, so a second package that imports it |
| 64 | + * and stamps it is not seen as a second emitter (a shared constant one |
| 65 | + * package over is the ledger's waiver class: |
| 66 | + * `UNIQUE_SCOPE_CONFIRMATION_REQUIRED`, `UPDATE_ID_MISMATCH`); and a |
| 67 | + * constant that is only ever COMPARED against is still a site — the |
| 68 | + * over-match bound below, measured at zero such constants when the rule |
| 69 | + * widened. |
58 | 70 | * - BLIND, and inheriting the sibling gate's declared blindness rather than |
59 | | - * re-litigating it: a code arriving through a constant NOT named `*_CODE` |
60 | | - * (`GLOBAL_UNIQUE_CONFIRMATION_REQUIRED` in `@objectstack/types` defines a |
61 | | - * registered code and is invisible here), an object-literal or helper |
62 | | - * indirection (`{ code }` shorthand, `makeError(code, …)` call sites), a |
63 | | - * template literal, and a class field. Those shapes DO have recognizers in |
64 | | - * the sibling gate; a registered code reaching a stamp through one of them |
65 | | - * is simply not this gate's finding yet. Widening is a gate-population |
66 | | - * change with an unmeasured blast radius — its own card, never a rider. |
| 71 | + * re-litigating it: an object-literal or helper indirection (`{ code }` |
| 72 | + * shorthand, `makeError(code, …)` call sites, a `cond ? 'A' : 'B'` or |
| 73 | + * `?? 'X'` operand), a template literal, and a class field. Those shapes |
| 74 | + * DO have recognizers in the sibling gate; a registered code reaching a |
| 75 | + * stamp through one of them is simply not this gate's finding yet. |
| 76 | + * Widening is a gate-population change with an unmeasured blast radius — |
| 77 | + * its own card, never a rider. |
67 | 78 | * - OVER-matching is accepted and absorbed by rows/waivers rather than |
68 | 79 | * heuristics: a TYPE-position literal (`{ code: 'ITEM_LOCKED'; reason: |
69 | 80 | * string }`) matches the object-literal pattern. That is deliberate — the |
@@ -109,10 +120,20 @@ export const STAMP_PATTERNS: ReadonlyArray<{ name: string; re: RegExp }> = [ |
109 | 120 | { name: 'objlit', re: /\bcode:\s*'([A-Z][A-Z0-9_]*)'/g }, |
110 | 121 | // `err.code = 'X'` — stamped onto a value about to be thrown. |
111 | 122 | { name: 'assign', re: /\.code\s*=\s*'([A-Z][A-Z0-9_]*)'/g }, |
112 | | - // `X_CODE = 'X'` — a `*_CODE`-named constant's literal initializer (the |
113 | | - // driver-memory `UNIQUE_VIOLATION_CODE` shape). The optional `[^=\n]*?` |
114 | | - // limb admits a type annotation between name and `=`. |
115 | | - { name: 'constdef', re: /\b[A-Z][A-Z0-9_]*_CODE\s*(?::[^=\n]*?)?=\s*'([A-Z][A-Z0-9_]*)'/g }, |
| 123 | + // A constant whose literal initializer is a registered code: the binding is |
| 124 | + // the site, whatever it is named. Two limbs: |
| 125 | + // - `const|let|var NAME = 'X'` — a DECLARED binding of any name (the |
| 126 | + // `@objectstack/mcp` stdio bridge's `const OBJECT_API_DISABLED = …`, the |
| 127 | + // plugin-auth `SELF_REGISTRATION_CLOSED` shape). The registered set is |
| 128 | + // the filter, so the name no longer has to say "code". |
| 129 | + // - `X_CODE = 'X'` — a `*_CODE`-named binding with no keyword in front (a |
| 130 | + // class `static readonly`, a second declarator), kept so the widening |
| 131 | + // is a superset of the name rule it replaced, never a trade. |
| 132 | + // The optional `[^=\n]*?` limb admits a type annotation between name and `=`. |
| 133 | + { |
| 134 | + name: 'constdef', |
| 135 | + re: /(?:\b(?:const|let|var)\s+[A-Za-z_$][\w$]*|\b[A-Z][A-Z0-9_]*_CODE)\s*(?::[^=\n]*?)?=\s*'([A-Z][A-Z0-9_]*)'/g, |
| 136 | + }, |
116 | 137 | ]; |
117 | 138 |
|
118 | 139 | export interface StampSite { |
@@ -301,8 +322,9 @@ export function deriveFindings( |
301 | 322 |
|
302 | 323 | function printBounds(): void { |
303 | 324 | console.log('bounds: packages/**/src non-test .ts/.tsx; ledger file excluded (mention, not stamping);'); |
304 | | - console.log(`bounds: patterns = ${STAMP_PATTERNS.map((p) => p.name).join(', ')} — blind to non-*_CODE constants,`); |
305 | | - console.log('bounds: helper/shorthand indirections, templates and class fields (see the header; sibling-gate shapes).'); |
| 325 | + console.log(`bounds: patterns = ${STAMP_PATTERNS.map((p) => p.name).join(', ')} — a constant is read at its definition`); |
| 326 | + console.log('bounds: and attributed to the DEFINING package; blind to helper/shorthand/ternary indirections, templates'); |
| 327 | + console.log('bounds: and class fields (see the header; sibling-gate shapes).'); |
306 | 328 | } |
307 | 329 |
|
308 | 330 | function run(report: boolean): number { |
@@ -411,8 +433,8 @@ let selfTestReachedVerdict = false; |
411 | 433 | // The counts are a FLOOR, not an equality: adding cases is ordinary work and |
412 | 434 | // must not red. A battery BELOW its floor means cases stopped running. |
413 | 435 | const SELF_TEST_BATTERIES: Readonly<Record<string, number>> = Object.freeze({ |
414 | | - 'each published STAMP pattern catches its own spelling': 4, |
415 | | - 'the population boundary and comment masking: what is NOT a site': 2, |
| 436 | + 'each published STAMP pattern catches its own spelling': 6, |
| 437 | + 'the population boundary and comment masking: what is NOT a site': 4, |
416 | 438 | 'the reconciliation: an unlisted stamper reddens, a listed one is green': 2, |
417 | 439 | 'a waiver admits EXACTLY its (package, code) pair': 2, |
418 | 440 | 'every stale-waiver direction reddens': 3, |
@@ -492,13 +514,34 @@ function selfTest(): number { |
492 | 514 | 'constdef admits a type annotation', |
493 | 515 | scanSourceText(`const MY_CODE: string = '${CODE_A}';`, registered).some((h) => h.pattern === 'constdef'), |
494 | 516 | ); |
| 517 | + // The binding is the site whatever it is named — the stdio-bridge shape the |
| 518 | + // `*_CODE` name rule could not see. |
| 519 | + check( |
| 520 | + 'constdef catches a declared constant of ANY name', |
| 521 | + scanSourceText(`const REFUSAL = '${CODE_A}';`, registered).some((h) => h.pattern === 'constdef'), |
| 522 | + ); |
| 523 | + // …and the keyword-less `*_CODE` limb keeps the widening a superset. |
| 524 | + check( |
| 525 | + 'constdef keeps a keyword-less *_CODE binding', |
| 526 | + scanSourceText(`static readonly MY_CODE = '${CODE_A}';`, registered).some((h) => h.pattern === 'constdef'), |
| 527 | + ); |
495 | 528 | // Population boundary: a code outside the registered set is the sibling |
496 | 529 | // gate's subject, never a site here. |
497 | 530 | battery('the population boundary and comment masking: what is NOT a site'); |
498 | 531 | check( |
499 | 532 | 'a code outside the registered set is out of population', |
500 | 533 | scanSourceText(`return { code: '${CODE_OUT}' };`, registered).length === 0, |
501 | 534 | ); |
| 535 | + // Precision of the widened `constdef`: it reads a DECLARATION, so neither a |
| 536 | + // re-assignment to an undeclared non-`*_CODE` name nor a comparison is one. |
| 537 | + check( |
| 538 | + 'a re-assignment to a non-*_CODE name is not a site', |
| 539 | + scanSourceText(`status = '${CODE_A}';`, registered).length === 0, |
| 540 | + ); |
| 541 | + check( |
| 542 | + 'a comparison against a registered code is not a site', |
| 543 | + scanSourceText(`if (e.code === '${CODE_A}') return;`, registered).length === 0, |
| 544 | + ); |
502 | 545 | // Comment masking: a code quoted in prose is not a site. |
503 | 546 | check( |
504 | 547 | 'a commented stamp is not a site', |
|
0 commit comments