@sleepy_panda_srl/portulan 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +6 -5
  2. package/cli/README.md +142 -358
  3. package/cli/advisory.mjs +354 -0
  4. package/cli/comments.mjs +932 -0
  5. package/cli/compile.mjs +1322 -1607
  6. package/cli/context.mjs +883 -0
  7. package/cli/control-chars.mjs +12 -301
  8. package/cli/discover.mjs +17 -574
  9. package/cli/doctor.mjs +224 -1074
  10. package/cli/drills.mjs +68 -502
  11. package/cli/feedback.mjs +16 -237
  12. package/cli/finish.mjs +437 -0
  13. package/cli/form.mjs +644 -0
  14. package/cli/fuzz-shell.mjs +20 -305
  15. package/cli/gate.mjs +7 -199
  16. package/cli/goldens.mjs +26 -184
  17. package/cli/index.mjs +305 -655
  18. package/cli/init.mjs +243 -566
  19. package/cli/inside.mjs +3 -28
  20. package/cli/instructions.mjs +829 -0
  21. package/cli/ledger.mjs +778 -0
  22. package/cli/librarian.mjs +135 -599
  23. package/cli/manifest.mjs +2 -39
  24. package/cli/mutants.mjs +9 -170
  25. package/cli/new.mjs +12 -243
  26. package/cli/pack-identity.mjs +5 -74
  27. package/cli/pack-version.mjs +13 -255
  28. package/cli/plugin-lint.mjs +42 -658
  29. package/cli/portulan.mjs +5 -124
  30. package/cli/recipe-set.mjs +15 -245
  31. package/cli/release-eval.mjs +28 -348
  32. package/cli/review-meter.mjs +17 -258
  33. package/cli/rule-carriers.mjs +12 -175
  34. package/cli/sessions.mjs +82 -0
  35. package/cli/skill-goldens.mjs +10 -176
  36. package/cli/skills-set.mjs +12 -414
  37. package/cli/stop-gate.mjs +83 -514
  38. package/cli/symbols.mjs +1132 -0
  39. package/cli/telemetry.mjs +27 -492
  40. package/cli/upgrade.mjs +171 -314
  41. package/cli/vendor.mjs +146 -476
  42. package/cli/version-carriers.mjs +8 -86
  43. package/core/README.md +1 -1
  44. package/core/engine.md +3 -9
  45. package/core/operating/autonomy.md +1 -1
  46. package/core/operating/context.md +183 -0
  47. package/core/operating/evolution.md +3 -3
  48. package/core/operating/loop.md +13 -20
  49. package/core/operating/memory.md +12 -3
  50. package/core/operating/sessions.md +100 -0
  51. package/core/personas/librarian.md +2 -2
  52. package/core/skills/consolidate/SKILL.md +4 -2
  53. package/core/templates/handoff.md +28 -15
  54. package/core/templates/memory-entry.md +1 -0
  55. package/evals/README.md +255 -845
  56. package/evals/releases/0.2.0.json +146 -0
  57. package/evals/releases/0.2.0.md +67 -0
  58. package/evals/releases/README.md +3 -3
  59. package/package.json +4 -2
  60. package/packs/rituals/checkpoints/README.md +1 -1
  61. package/packs/tools/github/verify/actions-pinned.sh +10 -55
  62. package/spec/README.md +112 -6
  63. package/spec/migrations/0001-repository-declares-its-tree.mjs +1 -26
  64. package/spec/migrations/0002-bundle-fallback-path.mjs +13 -74
  65. package/spec/migrations/0003-handoff-index-not-kept.mjs +66 -0
  66. package/spec/migrations/0004-changelog-fragments.mjs +53 -0
  67. package/spec/migrations/0005-session-log-retired.mjs +69 -0
  68. package/spec/migrations/0006-boot-card.mjs +77 -0
  69. package/spec/migrations/0007-guidance-compiled.mjs +31 -0
  70. package/spec/migrations/0008-card-reading.mjs +55 -0
  71. package/spec/migrations/0009-instruction-sections-on-read.mjs +60 -0
  72. package/spec/migrations/0010-comment-rail.mjs +57 -0
  73. package/spec/migrations/README.md +45 -10
  74. package/spec/pack.schema.json +1 -1
  75. package/spec/slots.md +333 -21
  76. package/spec/workspace.schema.json +145 -4
package/cli/manifest.mjs CHANGED
@@ -1,39 +1,9 @@
1
- // The two facts every tool here needs out of `package.json`, read once and in a module that imports
2
- // nothing of ours.
3
- //
4
- // ## Why this is its own file, which is the part worth reading
5
- //
6
- // `VERSION` lived in `portulan.mjs` because that is where it was first needed, and it was right there:
7
- // one carrier, read from the manifest that publishes it rather than written down twice.
8
- //
9
- // Then `feedback.mjs` needed the same value and imported it from `portulan.mjs` — still one carrier,
10
- // and **it hung the command line**. `portulan.mjs` ends in `process.exitCode = await run(…)`, `run`
11
- // dynamically imports the subcommand, and the subcommand statically imported `portulan.mjs` back. Node
12
- // cannot finish evaluating a module that is waiting on a module waiting on it: `portulan feedback
13
- // --help` printed nothing and exited **13**, `Detected unsettled top-level await`.
14
- //
15
- // Nothing in the suite could see it. Every dispatch case injects the loader — correctly, since
16
- // re-asserting what each tool proves itself would make the entry point's suite a second carrier of it
17
- // — and an injected loader never imports the real module, so the cycle does not exist under test. It
18
- // was found by the one case that spawns the real binary, written in the same change for exactly this
19
- // reason. **A harness you write to check your own change inherits your change's blind spot**, for the
20
- // third recorded time in this repository.
21
- //
22
- // So the shared read moved down here, where it has no dependencies to be circular with. `portulan.mjs`
23
- // re-exports `VERSION` so its published surface is unchanged.
1
+ // The two facts every tool here needs out of `package.json`, read once and in a module that imports nothing of ours.
24
2
 
25
3
  import fs from "node:fs";
26
4
  import path from "node:path";
27
5
  import { fileURLToPath } from "node:url";
28
6
 
29
- /**
30
- * The manifest, in both layouts that matter: from a checkout it is one level above `cli/`, and the
31
- * published package keeps that shape because `files` ships `cli/` under the package root.
32
- *
33
- * Falls back rather than throwing. A version string and a repository name are not worth taking the
34
- * command line down for — and the callers that need the repository to be real say so themselves
35
- * rather than inheriting a crash from here.
36
- */
37
7
  const manifest = (() => {
38
8
  try {
39
9
  const at = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json");
@@ -45,14 +15,7 @@ const manifest = (() => {
45
15
 
46
16
  export const VERSION = manifest.version ?? "unknown";
47
17
 
48
- /**
49
- * The repository issues are filed into, derived from the `bugs.url` the manifest already publishes
50
- * rather than minted as a second constant — `portulan feedback` would otherwise carry a copy that
51
- * nothing holds to this one.
52
- *
53
- * `null` when the manifest carries no usable URL, which the sender turns into a refusal naming
54
- * `--repo`; guessing a repository to file a public issue into is the one wrong answer here.
55
- */
18
+ /** The repository issues are filed into, as `owner/name` from `bugs.url`; null, never a guess, when that names none. */
56
19
  export const REPOSITORY = (() => {
57
20
  const match = /github\.com\/([^/]+\/[^/]+?)(?:\/issues)?\/?$/.exec(manifest.bugs?.url ?? "");
58
21
  return match ? match[1] : null;
package/cli/mutants.mjs CHANGED
@@ -1,66 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  // Mutation-test this repository's two gate matchers against the corpus that claims to cover them.
3
3
  //
4
- // Milestone 8, clause (b), first half: *mutation testing over both matchers.* The second half —
5
- // grammar-aware fuzzing over the shell segmenter — is ./fuzz-shell.mjs, and the two are separate
6
- // because a surviving mutant and a grammar bypass are different verdicts with different repairs.
7
- //
8
- // ## The question this answers, and it is the one `./goldens.mjs` says out loud that it cannot
9
- //
10
- // That runner prints on every green: *"this is a PRESENCE floor — whether a corpus is a real attack
11
- // is a reviewer's judgement, not this rail's."* One trivial fixture per rule satisfies it while
12
- // proving nothing adversarial. This runner stands in exactly that stated gap. It does not ask whether
13
- // a fixture exists; it breaks a matcher on purpose and asks whether the corpus NOTICES.
14
- //
15
- // A corpus that cannot tell a working matcher from a broken one is a corpus whose green means
16
- // nothing, and until this file existed no check in this repository could tell those two apart.
17
- //
18
- // ## What a mutant is here — anchored, not generated
19
- //
20
- // An operator is a **declared textual substitution** into `./compile.mjs`, anchored to a named
21
- // construct, required to match **exactly once**. Not a random AST perturbation: a random mutator over
22
- // a 2681-line module spends most of its budget outside the matchers, and this clause is about the
23
- // matchers. Every operator names the region member it attacks, and the census prints coverage per
24
- // member INCLUDING THE ZEROES — otherwise "mutation testing over both matchers" is satisfiable by
25
- // three operators inside `matchesRule` and no reader would see the narrowing.
26
- //
27
- // ## The record, and why it reads in both directions
28
- //
29
- // Each operator declares the outcome expected of it:
30
- //
31
- // - `killed` — the corpus must catch this. If it stops catching it, the kill-set has weakened.
32
- // - `survives` — the corpus cannot catch it AND THAT IS PROVEN, not observed. Two proofs are
33
- // admissible and each entry states which: the mutant is semantically **equivalent**
34
- // to the original, or it is equivalent **under the yielded policy** — no rule of the
35
- // shape that would distinguish it is declared, so no fixture could exist to kill it.
36
- //
37
- // **A survivor that is neither is not a resting state.** `matchesRule` is a pure function of
38
- // `(rule, tool, input)` and a fixture is exactly that triple, so any non-equivalent mutant is killable
39
- // by adding one case. A standing ledger of named-but-unfilled gaps would rebuild the prose hole list
40
- // that clause (a) exists to have replaced, one altitude up. The repair for such a survivor is a new
41
- // fixture in the corpus, in the corpus's own shape — never a new `survives` entry.
42
- //
43
- // Both directions are red, which is `documented-hole`'s discipline in `./goldens.mjs` applied to a
44
- // second subject: an operator recorded `killed` that now survives, and an operator recorded `survives`
45
- // that is now killed. The second is good news and the message says so — but a record that still
46
- // describes a gap somebody closed is as wrong as one that hides a gap somebody opened.
47
- //
48
- // ## Three things it deliberately does not do
49
- //
50
- // **It does not run the `node --test` suite as a kill-set.** The corpus is the kill-set this
51
- // repository designated — `../evals/README.md` says clause (b) *"needs this corpus as its kill-set
52
- // and this fixture format as its output shape"* — running the suite per mutant would need the
53
- // subprocesses this design refuses, and a `survives` record citing the suite as the killer would put
54
- // the kill-claim in a second carrier.
55
- //
56
- // **It executes nothing.** Fixture command strings are data here exactly as they are in
57
- // `./goldens.mjs`; the corpus holds `git push --force` and constitution-write spellings by design.
58
- // The only thing this module runs is `import()` on a JavaScript file it wrote itself, and
59
- // `./mutants.test.mjs` asserts it imports no process-spawning API.
60
- //
61
- // **It writes nothing inside the tree.** Mutants live under `os.tmpdir()`, one fresh directory each,
62
- // removed in a `finally`.
63
- //
64
4
  // Exit 0 green · 1 red · 2 could not run.
65
5
 
66
6
  import fs from "node:fs";
@@ -71,20 +11,9 @@ import { pathToFileURL } from "node:url";
71
11
  import { CompileError } from "./compile.mjs";
72
12
  import { CouldNotRun, partition, readCorpus, yieldedRules } from "./goldens.mjs";
73
13
 
74
- /** The module under mutation, relative to the repository root. */
75
14
  export const SUBJECT = "cli/compile.mjs";
76
15
 
77
- /**
78
- * The matcher region — every function and table `matchesRule` can reach, named.
79
- *
80
- * This list is the **coverage floor**, and it is written down rather than derived because deriving it
81
- * would mean a call-graph walker, and a walker that is wrong is wrong silently. Each member must carry
82
- * at least one operator or the table below must say why not; `census` prints the per-member count
83
- * including zeroes, the way `./goldens.mjs` prints its per-matcher-path census including zeroes.
84
- *
85
- * The three entry branches of `matchesRule` are the two matchers the clause names plus `read:`, which
86
- * shares `matchesPath` with the write branch and so cannot be mutated separately from it.
87
- */
16
+ /** Every function and table `matchesRule` can reach, listed by hand rather than derived. */
88
17
  export const REGION = [
89
18
  "matchesRule",
90
19
  "matchesPath",
@@ -107,30 +36,13 @@ export const REGION = [
107
36
  "SEGMENT_LEADERS",
108
37
  "OPERATOR",
109
38
  "ASSIGNMENT",
110
- // Added at the pre-commit checkpoint, which derived the reachable set rather than reading this
111
- // list and found both missing while the sentence above said "every function and table
112
- // `matchesRule` can reach". They are the tool tables the write and read branches dispatch on —
113
- // `compile.mjs:92` and `:93`, read at `:1003` and `:1033` — and a list that omits a dispatch
114
- // table is exactly the narrowing this census exists to make visible. Adding them found a real
115
- // gap: the corpus had no `NotebookEdit` case at all.
116
39
  "WRITE_TOOLS",
117
40
  "READ_TOOLS",
118
41
  ];
119
42
 
120
- /** The two outcomes an operator may record. */
121
43
  export const OUTCOMES = ["killed", "survives"];
122
44
 
123
- /**
124
- * The operators, and the outcome recorded for each.
125
- *
126
- * **One table, in this module, deliberately.** An operator is an anchor into `./compile.mjs`'s source
127
- * text plus the outcome expected of it; splitting those across code and a JSON file would make two
128
- * carriers of one operator, which is the defect this repository names more often than any other. The
129
- * corpus is data because it is per-rule and large; an operator is neither.
130
- *
131
- * `find` must occur **exactly once** in the subject. A missing or ambiguous anchor is could-not-run,
132
- * never a skip — see `mutate`.
133
- */
45
+ /** Record `survives` only for a mutant equivalent outright or under the yielded policy: a fixture can kill any other. */
134
46
  export const OPERATORS = [
135
47
  // ---------------------------------------------------------------------------------- matchesRule
136
48
  {
@@ -233,12 +145,7 @@ export const OPERATORS = [
233
145
  {
234
146
  id: "matchesPath-stops-stripping-a-leading-dot-slash",
235
147
  member: "matchesPath",
236
- // **The anchor carries the line BELOW it, and that is not decoration.** `neverMatches` in the
237
- // same file re-spells this strip — deliberately, so that predicate reads without `matchesPath`
238
- // in front of you — and the moment it did, this operator placed TWICE and the whole census
239
- // exited 2: could-not-run wearing a pass's clothes, over a cause no reader would guess from the
240
- // exit code. The recipe's own advice is to lengthen the anchor until it is unique, and the
241
- // `if (clean === ...)` guard is the line only `matchesPath` has. Measured 2026-09-03.
148
+ // The second line keeps the anchor unique: `neverMatches` repeats the first.
242
149
  find: "const clean = String(target ?? \"\").replace(/^\\.\\//, \"\").replace(/^\\/+/, \"\");\n if (clean === \"\" || clean === \"/\") return false;",
243
150
  replace: "const clean = String(target ?? \"\").replace(/^\\/+/, \"\");\n if (clean === \"\" || clean === \"/\") return false;",
244
151
  outcome: "killed",
@@ -578,14 +485,6 @@ export const OPERATORS = [
578
485
  },
579
486
  ];
580
487
 
581
- /**
582
- * Apply one operator to the subject's source.
583
- *
584
- * **A missing or ambiguous anchor is could-not-run, never a skip.** If `compile.mjs` moves and an
585
- * anchor no longer places, a harness that skipped it would report a clean sweep of the operators it
586
- * happened to apply — `../.portulan/memory/a-checker-must-refuse-what-it-cannot-check.md`, in the one
587
- * tool whose entire subject is checks that must not be trusted on their own word.
588
- */
589
488
  export function mutate(source, op) {
590
489
  let count = 0;
591
490
  let at = 0;
@@ -610,33 +509,15 @@ export function mutate(source, op) {
610
509
  return source.replace(op.find, () => op.replace);
611
510
  }
612
511
 
613
- /**
614
- * Rewrite the subject's relative imports to absolute `file://` URLs.
615
- *
616
- * A mutant lives in a temp directory, so `./discover.mjs` and `./inside.mjs` would not resolve from
617
- * there. Both import node builtins only, so pointing at the real modules costs nothing and copies
618
- * nothing. Measured before this was written rather than assumed: `${SUBJECT}` has exactly two relative
619
- * imports, no dynamic `import(`, and its only top-level `import.meta.url` use is the entry guard —
620
- * which compares against `process.argv[1]` and therefore cannot fire for a module imported from a
621
- * temp directory while argv[1] is this runner.
622
- */
512
+ /** Only `from "./….mjs"` imports are rewritten, and a mutant's `import.meta.url` names its temp directory. */
623
513
  export function absolutiseImports(source, cliDir) {
624
514
  return source.replace(/from "\.\/([A-Za-z0-9._-]+\.mjs)"/g, (_, name) => `from "${pathToFileURL(path.join(cliDir, name)).href}"`);
625
515
  }
626
516
 
627
- /** Run the whole corpus through one `matchesRule`, and report the first disagreement. */
628
517
  export function runCorpus(matchesRule, byId, corpus) {
629
518
  for (const { where, doc } of corpus) {
630
519
  const rule = byId.get(doc.rule);
631
- // **Never a skip.** This read `if (!rule) continue;`, which let a fixture file whose rule the
632
- // policy does not declare — renamed, misfiled, outlived its gate — sit ungraded while the
633
- // census reported green. A coverage tool that silently thins its own kill-set and then reports
634
- // on it is the loudest false green available here, and it is the failure
635
- // `../.portulan/memory/a-checker-must-refuse-what-it-cannot-check.md` names, in the module
636
- // whose whole subject is that class. `run` refuses the whole census before the baseline for
637
- // exactly this, so this arm is unreachable from there and is kept because a direct caller of
638
- // an exported function must not get the silent behaviour back. Reported as a suppressed note
639
- // by Copilot, round 5 on #338.
520
+ // `run` refuses such a corpus first; this arm answers a direct caller.
640
521
  if (!rule) {
641
522
  throw new CouldNotRun(
642
523
  `${where} attacks \`${doc.rule}\`, which the yielded policy does not declare — its cases cannot be ` +
@@ -648,8 +529,7 @@ export function runCorpus(matchesRule, byId, corpus) {
648
529
  try {
649
530
  actual = matchesRule(rule, c.tool, c.input);
650
531
  } catch (error) {
651
- // A mutant that THROWS is caught, and loudly: `matchesRule` promises never to throw,
652
- // so a throw is a disagreement with the contract as much as a wrong boolean is.
532
+ // `matchesRule` never throws, so a mutant that throws is killed.
653
533
  return { agreed: false, at: `${where} → ${c.id}`, how: `threw ${error?.name ?? "an error"}` };
654
534
  }
655
535
  if (actual !== c.expect) {
@@ -660,16 +540,7 @@ export function runCorpus(matchesRule, byId, corpus) {
660
540
  return { agreed: true };
661
541
  }
662
542
 
663
- /**
664
- * Import a mutated subject from a fresh temp directory.
665
- *
666
- * **A fresh directory per mutant, and that is load-bearing rather than tidy.** ESM caches by resolved
667
- * URL and offers no invalidation, so writing every mutant to one reused path would import mutant 1
668
- * and then grade it forty more times — silently, and *including while the record is first being
669
- * written*, where the two-directional rail cannot see it because the record would be built from the
670
- * same wrong readings. `./mutants.test.mjs` pins it with two mutants that must answer differently in
671
- * one process.
672
- */
543
+ /** A fresh directory per mutant: ESM caches a module by its URL and never forgets one. */
673
544
  async function importMutant(source, cliDir, op) {
674
545
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), "portulan-mutant-"));
675
546
  const file = path.join(dir, "compile.mjs");
@@ -678,10 +549,6 @@ async function importMutant(source, cliDir, op) {
678
549
  try {
679
550
  return { module: await import(pathToFileURL(file).href), dir };
680
551
  } catch (cause) {
681
- // **An unimportable mutant is could-not-run, never a kill.** A substitution that produces a
682
- // syntax error checks nothing about the corpus, and counting it as killed would let a
683
- // broken operator masquerade as a well-covered one — the loudest possible false green in a
684
- // tool whose whole output is a coverage claim.
685
552
  throw new CouldNotRun(
686
553
  `operator \`${op.id}\` produced a module that will not import — ${cause?.message ?? cause}. ` +
687
554
  `A mutant that cannot load tests nothing; fix the substitution`,
@@ -693,7 +560,6 @@ async function importMutant(source, cliDir, op) {
693
560
  }
694
561
  }
695
562
 
696
- /** Per-region-member operator counts, zeroes included. */
697
563
  export function census(operators = OPERATORS) {
698
564
  const counts = new Map(REGION.map((m) => [m, 0]));
699
565
  const stray = [];
@@ -754,9 +620,6 @@ export async function run(argv = [], { stdout = process.stdout, stderr = process
754
620
  } else throw new CouldNotRun(`unknown argument ${JSON.stringify(argv[i])}`);
755
621
  }
756
622
 
757
- // The coverage floor is checked before a single mutant is written, because it is a property of
758
- // the TABLE and a run that spends thirty seconds before reporting a structural gap is a run
759
- // nobody waits for.
760
623
  const { counts, stray, uncovered } = census();
761
624
  if (stray.length) {
762
625
  throw new CouldNotRun(
@@ -764,12 +627,7 @@ export async function run(argv = [], { stdout = process.stdout, stderr = process
764
627
  `Add the member to REGION or correct the operator — an operator outside the census is coverage nobody counts`,
765
628
  );
766
629
  }
767
- // **The WHOLE table, in both modes.** This read `only === null ? OPERATORS : []`, so `--only`
768
- // — the mode a person reaches for precisely when something is wrong — validated nothing, and a
769
- // malformed operator slipped through exactly where somebody was looking closely. `--only`
770
- // narrows what RUNS; it does not narrow what must be well formed, because the table is the
771
- // artifact under review and a reviewer reading it in `--only` mode is reading the same table.
772
- // Reported by Copilot, round 1 on #338.
630
+ // Every operator, even under `--only`: it narrows what runs, not what must be well formed.
773
631
  for (const op of OPERATORS) {
774
632
  if (!OUTCOMES.includes(op.outcome)) {
775
633
  throw new CouldNotRun(`operator \`${op.id}\` records outcome ${JSON.stringify(op.outcome)} — one of ${OUTCOMES.join(" / ")}`);
@@ -795,13 +653,6 @@ export async function run(argv = [], { stdout = process.stdout, stderr = process
795
653
  const { matchable, exempt } = partition(rules);
796
654
  const byId = new Map(matchable.map((r) => [r.id, r]));
797
655
 
798
- // **The corpus and the policy must correspond before a single mutant is written.** A fixture
799
- // naming a rule this census cannot grade is not a case that happens to pass — it is a piece of
800
- // the kill-set that never runs, and the census's whole output is a claim about how much the
801
- // kill-set catches. Refused here rather than at the first mutant so the message names the
802
- // repair once instead of forty-nine times, and refused at all because the alternative measured
803
- // green. `goldens` reds on the same condition; this does not lean on that recipe having run,
804
- // since a rail that depends on a sibling rail is a rail with a precondition nobody states.
805
656
  const ungradable = corpus
806
657
  .filter(({ doc }) => !byId.has(doc.rule))
807
658
  .map(({ where, doc }) => {
@@ -818,10 +669,6 @@ export async function run(argv = [], { stdout = process.stdout, stderr = process
818
669
  );
819
670
  }
820
671
 
821
- // **The baseline, before any mutant.** A mutation census over a corpus that is already red
822
- // measures nothing — every mutant would "survive" against a kill-set that cannot even agree
823
- // with the unmutated matcher. `goldens` is the recipe that grades this; here it is a
824
- // precondition, and a failed precondition is could-not-run rather than a verdict.
825
672
  const { matchesRule } = await import(pathToFileURL(subject).href);
826
673
  const baseline = runCorpus(matchesRule, byId, corpus);
827
674
  if (!baseline.agreed) {
@@ -870,14 +717,9 @@ export async function run(argv = [], { stdout = process.stdout, stderr = process
870
717
  }
871
718
 
872
719
  say(`mutants: ${selected.length} operator(s) over ${corpus.length} fixture file(s) — ${killed} killed, ${survived} survived`);
873
- // Per region member, zeroes included — see REGION. A total says nothing about where the
874
- // operators landed, and "mutation testing over both matchers" is satisfiable by three
875
- // operators in one function if nobody prints the distribution.
876
720
  say(`mutants: by matcher-region member —`);
877
721
  for (const member of REGION) say(` ${String(counts.get(member)).padStart(2)} ${member}`);
878
722
  if (uncovered.length) {
879
- // A gap in the floor is a RED rather than a could-not-run: the table is well-formed, it
880
- // simply does not reach part of what the clause names.
881
723
  findings.push({
882
724
  where: "the operator table",
883
725
  what:
@@ -907,10 +749,7 @@ export async function run(argv = [], { stdout = process.stdout, stderr = process
907
749
  }
908
750
  }
909
751
 
910
- // The entry guard, in the ONE form `./rule-carriers.mjs` designates. `file://${argv[1]}` is NOT that
911
- // form: `import.meta.url` percent-encodes, this working copy lives under a path with spaces, and the
912
- // comparison fails — so the tool exits 0 having run nothing. Copied rather than re-derived, for the
913
- // reason `./goldens.mjs` states after meeting the false green a third time.
752
+ // URLs on both sides, since `import.meta.url` percent-encodes; the realpath covers a symlinked `bin`.
914
753
  function isMain() {
915
754
  const invoked = process.argv[1];
916
755
  if (!invoked) return false;