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.
- package/CHANGELOG.md +187 -0
- package/README.md +104 -43
- package/docs/design-v2.md +275 -0
- package/docs/getting-started.md +295 -0
- package/docs/guards.md +226 -0
- package/docs/how-it-stays-stable.md +315 -0
- package/docs/how-v2-works.md +403 -0
- package/docs/mcp.md +286 -0
- package/docs/running-it-in-ci.md +306 -0
- package/docs/watching.md +190 -0
- package/package.json +3 -3
- package/src/cli/index.js +34 -7
- package/src/core/config.js +12 -2
- package/src/v2/adapters/isolate.js +89 -0
- package/src/v2/cause.js +151 -12
- package/src/v2/check.js +345 -3
- package/src/v2/cli.js +71 -12
- package/src/v2/cluster.js +20 -3
- package/src/v2/coverage.js +40 -5
- package/src/v2/detect.js +1413 -20
- package/src/v2/doctor.js +191 -35
- package/src/v2/init.js +470 -57
- package/src/v2/mcp/tools.js +124 -11
- package/src/v2/normalise.js +54 -7
- package/src/v2/observation.js +56 -10
- package/src/v2/rank.js +212 -43
- package/src/v2/run.js +216 -31
- package/src/v2/selfcheck.js +312 -7
- package/src/v2/store.js +269 -45
- package/src/v2/watch/index.js +96 -17
- package/src/v2/watch/window.js +138 -20
package/src/v2/coverage.js
CHANGED
|
@@ -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, {
|
|
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
|
-
|
|
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
|
-
: `
|
|
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
|
|
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,
|