Skip to content

Commit 9b6df03

Browse files
chore(scripts): keep the spec-major fingerprint in its gate, not the v17 sweep ledger (#26246)
<!-- claude-projects-attribution: {"github_login":"os-steve"} --> _Requested by **Steve** · [project thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi9HiJAYedonvaHNCDwdoW14J)_ Part of #26075 Clause-②: no Metadata-protocol: none PR 1 of 2 under ruling A (6417413905). It moves the detection fingerprint that `scripts/check-future-spec-major.mjs` kept in `docs/v17-docs-sweep.md` into the script, so PR 2 can delete the ledger without touching the gate. #26075 stays open for PR 2, which deletes the eight obsolete planning docs and makes the other root-doc changes. ## What changed (one file: `scripts/check-future-spec-major.mjs`) - **The fingerprint row now lives in the script.** The ledger's `@objectstack/spec 18` fingerprint row is copied verbatim into a new constant, `SWEEP_FINGERPRINT`. Its provenance is `docs/v17-docs-sweep.md` as of `df8aac41`. The two header passages that pointed readers at the ledger now point at that constant. - **The self-test reads the fingerprint from its new home.** The R1 "double-backtick pattern span → GREEN" row now uses `SWEEP_FINGERPRINT` itself. One new battery is its negative control: `R1 — the relocated fingerprint with its double backticks reduced to single → RED`. It runs the same row with only the delimiters changed. If that replacement ever stops matching, the row stays a pattern span and reads GREEN, so the control also proves the fixture still carries the citation. `SELF_TEST_BATTERY_FLOOR` goes from 33 to 34. - **The ledger's `QUOTATION_EXEMPTIONS` entry is removed.** It excused the sweep log line ``**Fingerprint added:** the `@objectstack/spec` 18 row above``. That entry pinned major 18, and `main` already publishes `packages/spec` 18.1.0, so it was DORMANT and excused nothing. The gate's pass line already asked for it to be deleted ("delete once `main` itself carries that major"). - **Self-test rows look up ledger entries by file, not by index**, through a new `ledgerEntry(file)` helper. Removing that entry therefore cannot silently point a row at the entry next to it. Fixture paths that named the ledger file now use `docs/fingerprints.md`. The DORMANT row keeps its ADR-0087 sentence and drops the ledger's. The gate's behaviour is unchanged. It gives the same verdict on the real tree (green at 18, and green at `19.0.0-next.0` in the roll control). Only the pass line changes: it now lists 1 dormant entry instead of 2. ## Evidence (head `afc4d58e`) - `node scripts/check-future-spec-major.mjs --self-test` → `✓ check-future-spec-major self-test: 34 cases pass.` (base: 33). - `node scripts/check-future-spec-major.mjs` → exit 0, `3 live witnessed ledger entr(y|ies)`, `1 DORMANT ledger entr(y|ies) … docs/adr/0087-metadata-protocol-upgrade-contract.md (major 18)`. - **Deletion rehearsal.** With `docs/v17-docs-sweep.md` moved out of the tree, the gate exits 0 (21148 scanned files instead of 21149). The file was then restored and checked byte-identical with `git hash-object`. PR 2's deletion therefore needs no further edit to this script. - Derived gate families: `node scripts/pm/dispatch-gates.mjs --commands` gives 33. All 33 ran locally at exit 0, including `pnpm check:pm-dispatch-gates` (fast tier, 1917 cases). `--ran` reconciliation: `33 run, 0 NOT-MEASURED`. - No `*.test.*` file names this script. Its `--self-test` is its suite. ## Acceptance notes - `scripts/check-future-spec-major.mjs` is the only file changed, so this PR is in-repo tooling with no published surface and carries `skip-changeset`. - The remaining dormant entry (ADR-0087, major 18) is also due for deletion by the gate's own rule. It is not touched here because it has nothing to do with the sweep ledger. Successor: none. --- _Generated by [Claude Code](https://claude.ai/code/session_01SQ8bET7S4BDrvJdXyuLdYi)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 8b7253c commit 9b6df03

1 file changed

Lines changed: 60 additions & 41 deletions

File tree

‎scripts/check-future-spec-major.mjs‎

Lines changed: 60 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@
2626
// an event that will never come.
2727
//
2828
// WHY A GATE AND NOT ANOTHER SWEEP. The prose fix is one commit; the class
29-
// returns because nothing reads the number. `docs/v17-docs-sweep.md` already
30-
// carries a DETECTION FINGERPRINT row for it, and a fingerprint is a thing a
31-
// human remembers to grep. This is the machine that greps.
29+
// returns because nothing reads the number. The v17 docs-sweep ledger carried
30+
// a DETECTION FINGERPRINT row for it, and a fingerprint is a thing a human
31+
// remembers to grep. This is the machine that greps — so the row itself now
32+
// lives here, verbatim, as `SWEEP_FINGERPRINT` below.
3233
//
3334
// ── THE TWO WAYS THIS GATE COULD READ A FALSE ZERO, both measured ──────────
3435
//
@@ -123,17 +124,17 @@
123124
// quotation of a bad tombstone REDS ON THE RULING THAT FORBIDS THE BUG:
124125
//
125126
// • `docs/adr/0087-…md` — the ruling itself, quoting the wrong form.
126-
// • `docs/v17-docs-sweep.md` — the sweep ledger's detection fingerprint for
127-
// this very class, plus the log line recording that it was added.
127+
// • the detection fingerprint for this very class — once a row of the v17
128+
// docs-sweep ledger, now `SWEEP_FINGERPRINT` in this file.
128129
// • `packages/spec/src/shared/retired-key.test.ts` — a synthetic "next
129130
// major" fixture that MUST name a version that does not exist.
130131
//
131132
// Two mechanisms, in this order, and NEITHER is a path allowlist:
132133
//
133134
// R1 — STRUCTURAL. A citation wholly inside a Markdown DOUBLE-backtick code
134135
// span (`` … ``) is the phrase quoted AS A PATTERN, not a sentence
135-
// dating a removal. That is what a fingerprint row is, so the sweep
136-
// ledger's table needs no ledger entry and the next fingerprint row
136+
// dating a removal. That is what a fingerprint row is, so a
137+
// fingerprint table needs no ledger entry and the next fingerprint row
137138
// will not either. A SINGLE-backtick span is NOT exempt — that is the
138139
// live tombstone form axis (2) above.
139140
//
@@ -299,6 +300,22 @@ function findCitations(text) {
299300
return found;
300301
}
301302

303+
// ── The detection fingerprint, relocated ───────────────────────────────────
304+
//
305+
// The v17 docs-sweep ledger's fingerprint row for this class, verbatim — its
306+
// "Fingerprints" table in `docs/v17-docs-sweep.md` as of commit `df8aac41`.
307+
// It lives here because this gate is the machine form of that row: the
308+
// pattern a human greps for, what a hit means and which record arbitrates the
309+
// number stay beside the matcher that enforces them, and outlive the sweep
310+
// that wrote them down. It is R1's fixture too: the self-test reads it back as
311+
// a double-backtick pattern span (GREEN) and, with the double backticks
312+
// reduced to single, as the live tombstone form (RED).
313+
const SWEEP_FINGERPRINT =
314+
'| `` `@objectstack/spec` 18 `` (any v17 removal dated to **18**) | dates a removal that '
315+
+ 'ships in **17** to the next major — the reader plans for it a release late and their '
316+
+ 'upgrade breaks. Check the *number*, not just the surface name: `spec-changes.json` '
317+
+ '`toMajor` is the arbiter | #4286, #3963 |\n';
318+
302319
// ── R2: the witnessed, self-invalidating ledger ────────────────────────────
303320
//
304321
// ⛔ Adding an entry here is not a way to silence a finding — it is a claim
@@ -340,17 +357,6 @@ const QUOTATION_EXEMPTIONS = Object.freeze([
340357
+ 'forbid it. This gate exists because of this sentence; reddening on it '
341358
+ 'would make the gate refuse its own authority.',
342359
},
343-
{
344-
file: 'docs/v17-docs-sweep.md',
345-
major: 18,
346-
kind: 'quotation',
347-
covers: 1,
348-
witness: /\*\*Fingerprint added:\*\* the `@objectstack\/spec` 18 row above/,
349-
why:
350-
'The sweep ledger\'s append-only run log, recording the run that added '
351-
+ 'the detection fingerprint for this class. The log is history: '
352-
+ 'rewriting the number would falsify what that run did.',
353-
},
354360
{
355361
file: 'packages/spec/src/shared/retired-key.test.ts',
356362
major: 99,
@@ -574,6 +580,7 @@ const SELF_TEST_BATTERIES = Object.freeze({
574580
'the protocol major is never attached to the package name → GREEN': 1,
575581
'R1 — a double-backtick pattern span → GREEN': 1,
576582
'R1 — a SINGLE-backtick span is the live tombstone form → RED': 1,
583+
'R1 — the relocated fingerprint with its double backticks reduced to single → RED': 1,
577584
'R1 — a ```-fenced code block is NOT a pattern span → RED': 1,
578585
'R2 — a witnessed quotation entry → GREEN': 1,
579586
'R2 — a witnessed measurement count → GREEN': 1,
@@ -597,7 +604,7 @@ const SELF_TEST_BATTERIES = Object.freeze({
597604
// duplicate-label refusal: two rows sharing a label collapse to ONE key above,
598605
// so the roster falls below this number; the table cross-check names WHICH
599606
// label collided.
600-
const SELF_TEST_BATTERY_FLOOR = 33;
607+
const SELF_TEST_BATTERY_FLOOR = 34;
601608

602609
// The fixtures' reference major, and the tree one major up from it.
603610
const MANIFEST_17 = JSON.stringify({ name: PKG, version: `${SELF_FIXTURE_MAJOR}.4.0` });
@@ -611,6 +618,13 @@ const PARTITION_SHARD_TABLE =
611618

612619
function selfTest() {
613620
const F = (text, file = 'packages/core/src/demo.ts') => [{ file, text }];
621+
// A real ledger entry, looked up by the file it excuses — never by position,
622+
// so retiring one entry cannot silently re-point a row at its neighbour.
623+
const ledgerEntry = (file) => {
624+
const entry = QUOTATION_EXEMPTIONS.find((e) => e.file === file);
625+
if (!entry) throw new Error(`no QUOTATION_EXEMPTIONS entry names ${file}`);
626+
return entry;
627+
};
614628

615629
const cases = [
616630
{
@@ -731,18 +745,27 @@ function selfTest() {
731745
},
732746
{
733747
label: 'R1 — a double-backtick pattern span → GREEN',
734-
files: F(
735-
'| `` `@objectstack/spec` 18 `` (any v17 removal dated to **18**) | dates a removal '
736-
+ 'that ships in **17** to the next major | #4286 |\n',
737-
'docs/v17-docs-sweep.md',
738-
),
748+
files: F(SWEEP_FINGERPRINT, 'docs/fingerprints.md'),
739749
expect: 'green',
740750
},
741751
{
742752
label: 'R1 — a SINGLE-backtick span is the live tombstone form → RED',
743-
files: F('Removed in `@objectstack/spec` 18 under ADR-0049.\n', 'docs/v17-docs-sweep.md'),
753+
files: F('Removed in `@objectstack/spec` 18 under ADR-0049.\n', 'docs/fingerprints.md'),
744754
expect: 'red',
745-
wants: [/v17-docs-sweep\.md:1/],
755+
wants: [/fingerprints\.md:1/],
756+
},
757+
{
758+
// The negative control for the row above: the SAME relocated row with
759+
// only its delimiters changed. Were the replacement to miss, the text
760+
// would stay a pattern span and this row would read GREEN — a red here
761+
// is therefore also proof the fixture still carries the citation.
762+
label: 'R1 — the relocated fingerprint with its double backticks reduced to single → RED',
763+
files: F(
764+
SWEEP_FINGERPRINT.replace('`` `@objectstack/spec` 18 ``', '`@objectstack/spec` 18'),
765+
'docs/fingerprints.md',
766+
),
767+
expect: 'red',
768+
wants: [/fingerprints\.md:1 .*18, above the published major 17/],
746769
},
747770
{
748771
label: 'R1 — a ```-fenced code block is NOT a pattern span → RED',
@@ -767,7 +790,7 @@ function selfTest() {
767790
+ " + 'controls objectstack 12966 and @objectstack/spec 4997 on the same corpus.',\n",
768791
'packages/spec/src/migrations/entries/semantic/18.kernel-health-check-and-hot-reload-durations-unit-in-key.ts',
769792
),
770-
ledger: [QUOTATION_EXEMPTIONS[3]],
793+
ledger: [ledgerEntry('packages/spec/src/migrations/entries/semantic/18.kernel-health-check-and-hot-reload-durations-unit-in-key.ts')],
771794
expect: 'green',
772795
},
773796
{
@@ -777,7 +800,7 @@ function selfTest() {
777800
+ '"`@objectstack/spec` 18" is wrong on the day it is written (#18021).\n',
778801
'docs/adr/0087-metadata-protocol-upgrade-contract.md',
779802
),
780-
ledger: [QUOTATION_EXEMPTIONS[0]],
803+
ledger: [ledgerEntry('docs/adr/0087-metadata-protocol-upgrade-contract.md')],
781804
expect: 'green',
782805
},
783806
{
@@ -788,15 +811,15 @@ function selfTest() {
788811
+ '\nAnd separately: `startTime` was removed in @objectstack/spec 18 (ADR-0049).\n',
789812
'docs/adr/0087-metadata-protocol-upgrade-contract.md',
790813
),
791-
ledger: [QUOTATION_EXEMPTIONS[0]],
814+
ledger: [ledgerEntry('docs/adr/0087-metadata-protocol-upgrade-contract.md')],
792815
expect: 'red',
793816
wants: [/0087-metadata-protocol-upgrade-contract\.md:4/],
794817
},
795818
{
796819
label: 'R2 — a witness that matches nothing → RED (self-invalidating)',
797820
files: F('Nothing here quotes the bad form, though the package is named: @objectstack/spec 17.\n',
798821
'docs/adr/0087-metadata-protocol-upgrade-contract.md'),
799-
ledger: [QUOTATION_EXEMPTIONS[0]],
822+
ledger: [ledgerEntry('docs/adr/0087-metadata-protocol-upgrade-contract.md')],
800823
expect: 'red',
801824
wants: [/matched NOTHING/, /no longer finds the sentence it excuses/],
802825
},
@@ -810,7 +833,7 @@ function selfTest() {
810833
+ "const E = '`z` was removed in @objectstack/spec 99 — a FIFTH, unpinned.';\n",
811834
'packages/spec/src/shared/retired-key.test.ts',
812835
),
813-
ledger: [QUOTATION_EXEMPTIONS[2]],
836+
ledger: [ledgerEntry('packages/spec/src/shared/retired-key.test.ts')],
814837
expect: 'red',
815838
wants: [/covers 5 citation\(s\), pinned at 4/],
816839
},
@@ -821,24 +844,20 @@ function selfTest() {
821844
expect: 'green',
822845
},
823846
{
824-
// The two quotation sentences the real ledger excuses, verbatim in
825-
// shape, on a tree that reached their major: legitimate citations now,
826-
// so neither entry has anything to excuse. Before dormancy both read
827-
// "matched NOTHING" here — the version branch's red, with the quotations
828-
// exactly where they always were.
847+
// The quotation sentence the real ledger excuses at 18, verbatim in
848+
// shape, on a tree that reached its major: a legitimate citation now,
849+
// so the entry has nothing to excuse. Before dormancy it read "matched
850+
// NOTHING" here — the version branch's red, with the quotation exactly
851+
// where it always was.
829852
label: 'R2 — an entry at or below the published major is DORMANT, never a matched-NOTHING red → GREEN',
830853
files: [
831854
{
832855
file: 'docs/adr/0087-metadata-protocol-upgrade-contract.md',
833856
text: 'The ruling: prose dating it to\n'
834857
+ '"`@objectstack/spec` 18" is wrong on the day it is written (#18021).\n',
835858
},
836-
{
837-
file: 'docs/v17-docs-sweep.md',
838-
text: '- **Fingerprint added:** the `@objectstack/spec` 18 row above. Runs 1-2 matched on\n',
839-
},
840859
],
841-
ledger: [QUOTATION_EXEMPTIONS[0], QUOTATION_EXEMPTIONS[1]],
860+
ledger: [ledgerEntry('docs/adr/0087-metadata-protocol-upgrade-contract.md')],
842861
manifest: MANIFEST_18,
843862
expect: 'green',
844863
},

0 commit comments

Comments
 (0)