staysfixed 0.6.2 → 0.7.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.
@@ -660,7 +660,18 @@ export async function ledger(store, product, opts = {}) {
660
660
  /** @type {CoverageGap[]} */
661
661
  const holes = [];
662
662
 
663
- const all = await listBuilds(store, { product });
663
+ const all = await listBuilds(store, {
664
+ product,
665
+ // A build folder that could not be read is a build that is simply not in this ledger,
666
+ // and "not in the ledger" is indistinguishable from "never happened". It is a hole, and
667
+ // holes are this file's entire subject.
668
+ onProblem: (message) =>
669
+ holes.push({
670
+ what: message,
671
+ why: 'A door this tool opened during that build therefore reads here as never opened.',
672
+ unlockedBy: 'Run a check against that build again; a good record replaces the unreadable one.',
673
+ }),
674
+ });
664
675
  const wanted = opts.builds
665
676
  ? all.filter((b) => opts.builds?.includes(b.fingerprint.id))
666
677
  : all.slice(0, opts.maxBuilds ?? 20);
@@ -892,10 +903,12 @@ export function describeCoverage(led) {
892
903
  lines.push(`All ${led.work} of the unopened ones could be covered.`);
893
904
  }
894
905
 
906
+ // Every kind, not the worst four. There are five kinds of door in the whole tool, so
907
+ // cutting the list saved one line and dropped a whole category of the product out of the
908
+ // only sentence that says how much of it is covered.
895
909
  const kinds = Object.entries(led.byKind)
896
910
  .filter(([, k]) => k.doors > 0)
897
911
  .sort((a, b) => b[1].never - a[1].never)
898
- .slice(0, 4)
899
912
  .map(([kind, k]) => `${k.opened} of ${k.doors} ${k.doors === 1 ? KIND_ONE[kind] ?? kind : KIND_MANY[kind] ?? kind}`);
900
913
  if (kinds.length > 0) lines.push(`By kind: ${kinds.join(', ')}.`);
901
914
 
@@ -1024,7 +1037,29 @@ export function gaps(led, opts = {}) {
1024
1037
  });
1025
1038
  }
1026
1039
 
1027
- return jobs.sort((a, b) => (b.rank === a.rank ? b.doors - a.doors : b.rank - a.rank)).slice(0, worst);
1040
+ const ranked = jobs.sort((a, b) => (b.rank === a.rank ? b.doors - a.doors : b.rank - a.rank));
1041
+ if (ranked.length <= worst) return ranked;
1042
+
1043
+ // The cut is real and it used to be invisible. `toCoverage` asks for eight jobs; a product
1044
+ // with forty families of unopened doors handed back eight and said nothing about the other
1045
+ // thirty-two, so the coverage list — the one place in this tool whose entire job is naming
1046
+ // what was NOT looked at — was itself quietly truncated. The last slot says what is missing
1047
+ // rather than being one more job.
1048
+ const shown = ranked.slice(0, Math.max(0, worst - 1));
1049
+ const rest = ranked.slice(Math.max(0, worst - 1));
1050
+ const doors = rest.reduce((n, job) => n + job.doors, 0);
1051
+ shown.push({
1052
+ group: 'everything else that is unopened',
1053
+ what: `${rest.length} more ${rest.length === 1 ? 'family' : 'families'} of doors are never opened and are not listed above.`,
1054
+ why: `Between them they hold ${doors} ${doors === 1 ? 'door' : 'doors'} nothing has ever walked through, so a clean run says nothing about any of them either. Only the ${shown.length} worth doing first are named here.`,
1055
+ howTo: `Work through the ones above, or ask for the whole list at once. The families left out start with ${rest.slice(0, 3).map((job) => job.group).join(', ')}.`,
1056
+ doors,
1057
+ openedHere: 0,
1058
+ examples: rest.slice(0, 5).map((job) => job.group),
1059
+ files: [],
1060
+ rank: 0,
1061
+ });
1062
+ return shown;
1028
1063
  }
1029
1064
 
1030
1065
  /**
@@ -1050,7 +1085,7 @@ function howToCover(kind, never, harvested = false) {
1050
1085
  case 'export':
1051
1086
  return harvested
1052
1087
  ? `The project's own tests have already been harvested and they do not reach these, so nothing existing covers them. Either they are dead code worth deleting, or they need a test that calls them — starting with "${first}".`
1053
- : `Harvest the project's own test suite. Its tests already call most of these, and running them under coverage tells the ledger exactly which — including "${first}".`;
1088
+ : `Write a journeys file that calls them and pass it with --journeys — starting with "${first}". (Harvesting the project's own tests would answer this for free, and that is written and not yet wired into a run.)`;
1054
1089
  default:
1055
1090
  return `Add a journey that reaches "${first}" and the ones beside it.`;
1056
1091
  }
@@ -1076,7 +1111,7 @@ export function toCoverage(led, opts = {}) {
1076
1111
  what: `${led.never} of this product's ${led.doors} doors have never been opened by this tool.`,
1077
1112
  why: 'No journey reaches them, so a break behind one of them would not show up in any run — clean or otherwise.',
1078
1113
  unlockedBy: led.work > 0
1079
- ? `${led.work} of them could be covered. Harvest the project's own test suite, or switch on the journeys read out of the code.`
1114
+ ? `${led.work} of them could be covered by journeys that reach them — read out of your source, or named by hand in a journeys file.`
1080
1115
  : 'Nothing. Every one of them is a door this tool cannot open from here, and each says why.',
1081
1116
  channel: 'contract',
1082
1117
  doors: led.never,