spec-controller 0.1.0-alpha.1 → 0.1.0-alpha.10
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/README.md +112 -0
- package/dist/cli-args.d.ts +73 -0
- package/dist/cli-args.d.ts.map +1 -0
- package/dist/cli-args.js +114 -0
- package/dist/cli-args.js.map +1 -0
- package/dist/cli-balance/cli.d.ts +20 -20
- package/dist/cli-balance/cli.d.ts.map +1 -1
- package/dist/cli-balance/cli.js +221 -202
- package/dist/cli-balance/cli.js.map +1 -1
- package/dist/cli-balance/emit/writer.d.ts +1 -1
- package/dist/cli-registry.d.ts +35 -12
- package/dist/cli-registry.d.ts.map +1 -1
- package/dist/cli-registry.js +95 -36
- package/dist/cli-registry.js.map +1 -1
- package/dist/cli.d.ts +5 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +9 -3
- package/dist/cli.js.map +1 -1
- package/dist/corpus/cli.d.ts +34 -0
- package/dist/corpus/cli.d.ts.map +1 -0
- package/dist/corpus/cli.js +125 -0
- package/dist/corpus/cli.js.map +1 -0
- package/dist/deferralTags.d.ts +3 -3
- package/dist/deferralTags.js +3 -3
- package/dist/ingest/gherkinValidation.d.ts +29 -5
- package/dist/ingest/gherkinValidation.d.ts.map +1 -1
- package/dist/ingest/gherkinValidation.js +33 -5
- package/dist/ingest/gherkinValidation.js.map +1 -1
- package/dist/ingest/ingestQualityChecks.d.ts +15 -25
- package/dist/ingest/ingestQualityChecks.d.ts.map +1 -1
- package/dist/ingest/ingestQualityChecks.js +87 -104
- package/dist/ingest/ingestQualityChecks.js.map +1 -1
- package/dist/ingest/ingestScenarios.d.ts +2 -1
- package/dist/ingest/ingestScenarios.d.ts.map +1 -1
- package/dist/ingest/ingestScenarios.js +32 -3
- package/dist/ingest/ingestScenarios.js.map +1 -1
- package/dist/run-management/resolveRunInputs.d.ts +24 -11
- package/dist/run-management/resolveRunInputs.d.ts.map +1 -1
- package/dist/run-management/resolveRunInputs.js +49 -13
- package/dist/run-management/resolveRunInputs.js.map +1 -1
- package/package.json +2 -2
- package/dist/mutation-ratchet/index.d.ts +0 -48
- package/dist/mutation-ratchet/index.d.ts.map +0 -1
- package/dist/mutation-ratchet/index.js +0 -48
- package/dist/mutation-ratchet/index.js.map +0 -1
- package/dist/mutation-ratchet/ratchet.d.ts +0 -129
- package/dist/mutation-ratchet/ratchet.d.ts.map +0 -1
- package/dist/mutation-ratchet/ratchet.js +0 -222
- package/dist/mutation-ratchet/ratchet.js.map +0 -1
- package/dist/mutation-ratchet/ratchetCli.d.ts +0 -57
- package/dist/mutation-ratchet/ratchetCli.d.ts.map +0 -1
- package/dist/mutation-ratchet/ratchetCli.js +0 -139
- package/dist/mutation-ratchet/ratchetCli.js.map +0 -1
- package/dist/mutation-ratchet/reconcile.d.ts +0 -82
- package/dist/mutation-ratchet/reconcile.d.ts.map +0 -1
- package/dist/mutation-ratchet/reconcile.js +0 -67
- package/dist/mutation-ratchet/reconcile.js.map +0 -1
- package/dist/mutation-ratchet/record.d.ts +0 -210
- package/dist/mutation-ratchet/record.d.ts.map +0 -1
- package/dist/mutation-ratchet/record.js +0 -330
- package/dist/mutation-ratchet/record.js.map +0 -1
- package/dist/mutation-ratchet/report.d.ts +0 -83
- package/dist/mutation-ratchet/report.d.ts.map +0 -1
- package/dist/mutation-ratchet/report.js +0 -148
- package/dist/mutation-ratchet/report.js.map +0 -1
- package/dist/mutation-ratchet/verdict.d.ts +0 -177
- package/dist/mutation-ratchet/verdict.d.ts.map +0 -1
- package/dist/mutation-ratchet/verdict.js +0 -387
- package/dist/mutation-ratchet/verdict.js.map +0 -1
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* THE MEASUREMENT — a gate's run, counted out of its mutation report's own mutants
|
|
3
|
-
* (@SCN-RAT-010, 3F-2799).
|
|
4
|
-
*
|
|
5
|
-
* STATED, NEVER INFERRED, AND THAT IS THE WHOLE MODULE. A mutation report groups its mutants by
|
|
6
|
-
* the file they were made in and gives each one a status. The metric scores two of those statuses
|
|
7
|
-
* over four: the kills are the killed and the timed-out, and the population is those plus the
|
|
8
|
-
* survived and the never-covered. Both sets are written down below, member by member, and the four
|
|
9
|
-
* statuses left over — a mutant that would not compile, one that faulted at run time, one the run
|
|
10
|
-
* was told to ignore, and one that never got as far as being tried — are in neither.
|
|
11
|
-
*
|
|
12
|
-
* THE LENIENT READING IS ONE SENTENCE LONG, AND IT IS WRONG. "Everything not detected is
|
|
13
|
-
* undetected" reaches a population without anybody having to write a second set down, and it folds
|
|
14
|
-
* all four of those statuses into the denominator. A mutant that would not compile is not a
|
|
15
|
-
* survivor: nobody's assertion failed to catch it, because there was never anything there to catch.
|
|
16
|
-
* So that reading answers with a score which disagrees with the one the run itself printed —
|
|
17
|
-
* quietly, and in the direction of looking worse — and a reconciler holding a second opinion about
|
|
18
|
-
* the very number it is reconciling has stopped being one. Naming the two sets is what makes that
|
|
19
|
-
* disagreement impossible rather than merely unlikely.
|
|
20
|
-
*
|
|
21
|
-
* AND THE REPORT'S OWN THRESHOLDS ARE NOT READ, WHICH IS THE SAME RULING ONE ARTEFACT FURTHER OUT.
|
|
22
|
-
* A report carries the thresholds its own run was configured against, and reading them would be
|
|
23
|
-
* taking the runner's verdict on its own homework as the measurement. The bar lives in the record
|
|
24
|
-
* (`record.ts`), and the report is only ever asked what happened. Every figure produced here is
|
|
25
|
-
* counted from the mutants; every other number in the document is left where it lies, however
|
|
26
|
-
* conveniently it is shaped.
|
|
27
|
-
*
|
|
28
|
-
* THE COUNTS ARE TAKEN ACROSS THE WHOLE REPORT RATHER THAN A FILE OF IT. The grouping is
|
|
29
|
-
* presentation and nothing more — a gate's population is every mutant under it, wherever the report
|
|
30
|
-
* chose to file it. A counter that stopped at the first file would be green against every report
|
|
31
|
-
* holding exactly one, which is the shape a small fixture takes by default.
|
|
32
|
-
*
|
|
33
|
-
* AND A REPORT THAT CANNOT BE READ, OR THAT HOLDS NO SCORED MUTANT AT ALL, IS REFUSED RATHER THAN
|
|
34
|
-
* SCORED ZERO (@SCN-RAT-009). Zero is a measurement — a run that mutated the scope and killed none
|
|
35
|
-
* of it — and the two must not be spelled the same, because reconciling a zero reaches a regression
|
|
36
|
-
* whose message sends its reader hunting kills that in this case never happened. The refusal comes
|
|
37
|
-
* back as a value rather than a throw, so a caller cannot drop it by forgetting to catch.
|
|
38
|
-
*
|
|
39
|
-
* NOTHING HERE NAMES A PROJECT, A CHECK, A RUNNER, A WORKFLOW OR A REPOSITORY. The shape read below
|
|
40
|
-
* is the report FORMAT's, which is nobody's installation, and the gate's name arrives from the
|
|
41
|
-
* caller rather than out of the document.
|
|
42
|
-
*/
|
|
43
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
44
|
-
/**
|
|
45
|
-
* The statuses that are a kill.
|
|
46
|
-
*
|
|
47
|
-
* A TIMEOUT IS A KILL AND NOT A NEAR-MISS. The mutant changed the code enough that the suite never
|
|
48
|
-
* finished, which is the suite noticing — the same event as an assertion firing, reached by a
|
|
49
|
-
* slower road.
|
|
50
|
-
*/
|
|
51
|
-
const DETECTED = ["Killed", "Timeout"];
|
|
52
|
-
/**
|
|
53
|
-
* The statuses that are in the population and are not a kill.
|
|
54
|
-
*
|
|
55
|
-
* NEVER-COVERED IS COUNTED, AND THAT IS THE HALF A LENIENT READER LOSES BY LEAVING IT OUT. A mutant
|
|
56
|
-
* no test reached is one this suite would not have noticed, which is precisely what the metric
|
|
57
|
-
* measures; dropping it from the denominator would score untested code as though it were not there.
|
|
58
|
-
*/
|
|
59
|
-
const UNDETECTED = ["Survived", "NoCoverage"];
|
|
60
|
-
/** A refusal, as the value it is. */
|
|
61
|
-
function refused(reason) {
|
|
62
|
-
return { ok: false, reason };
|
|
63
|
-
}
|
|
64
|
-
/** Whatever an error carries by way of a sentence, whatever kind of thing was thrown. */
|
|
65
|
-
function raisedBy(cause) {
|
|
66
|
-
return cause instanceof Error ? cause.message : String(cause);
|
|
67
|
-
}
|
|
68
|
-
/** A JSON object — not a list, and not the null a bare `typeof` reads as one. */
|
|
69
|
-
function isMapping(value) {
|
|
70
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Every mutant in the report, flattened out of the per-file grouping it arrived in — or `null`
|
|
74
|
-
* where the document holds no grouping to flatten.
|
|
75
|
-
*
|
|
76
|
-
* THE TWO EMPTINESSES ARE NOT THE SAME AND ARE NOT ANSWERED THE SAME. `null` is a document that
|
|
77
|
-
* never had a files section; an empty list is a section that holds no mutant. Told apart, one
|
|
78
|
-
* reader is sent to the shape of the artefact and the other to the sweep that filled it; folded
|
|
79
|
-
* together, whichever sentence was chosen sends half of them somewhere useless.
|
|
80
|
-
*/
|
|
81
|
-
function mutantsIn(document) {
|
|
82
|
-
const files = document["files"];
|
|
83
|
-
if (!isMapping(files))
|
|
84
|
-
return null;
|
|
85
|
-
return Object.values(files).flatMap((file) => {
|
|
86
|
-
const mutants = isMapping(file) ? file["mutants"] : undefined;
|
|
87
|
-
return Array.isArray(mutants) ? mutants : [];
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
/** How many of these mutants carry a status the given set names. */
|
|
91
|
-
function countOf(mutants, statuses) {
|
|
92
|
-
return mutants.filter((mutant) => statuses.includes(mutant.status)).length;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Count a gate's measurement out of its report's text, or say why there is not one.
|
|
96
|
-
*
|
|
97
|
-
* THE GATE IS THE CALLER'S WORD FOR IT. A report says which files were mutated and nothing about
|
|
98
|
-
* which gate was being run, so a name taken from the document would be a name this module invented.
|
|
99
|
-
*
|
|
100
|
-
* AND SO IS THE PATH, WHICH IS CARRIED PURELY SO A REFUSAL CAN NAME IT — the same reason
|
|
101
|
-
* `parseGateRecords` carries the record file's name. Text has no path of its own, and "no
|
|
102
|
-
* measurement" over a path nothing writes to and "no measurement" over a sweep that never ran are
|
|
103
|
-
* the same sentence with entirely different fixes.
|
|
104
|
-
*/
|
|
105
|
-
export function measurementFromReport(gate, text, reportPath) {
|
|
106
|
-
let document;
|
|
107
|
-
try {
|
|
108
|
-
document = JSON.parse(text);
|
|
109
|
-
}
|
|
110
|
-
catch (cause) {
|
|
111
|
-
return refused(`the report at ${reportPath} is not readable JSON: ${raisedBy(cause)}`);
|
|
112
|
-
}
|
|
113
|
-
if (!isMapping(document)) {
|
|
114
|
-
return refused(`the report at ${reportPath} is not a mutation-report document.`);
|
|
115
|
-
}
|
|
116
|
-
const mutants = mutantsIn(document);
|
|
117
|
-
if (mutants === null) {
|
|
118
|
-
return refused(`the report at ${reportPath} holds no files section.`);
|
|
119
|
-
}
|
|
120
|
-
const detected = countOf(mutants, DETECTED);
|
|
121
|
-
const undetected = countOf(mutants, UNDETECTED);
|
|
122
|
-
if (detected + undetected === 0) {
|
|
123
|
-
return refused(`the report at ${reportPath} holds no scored mutant — the gate measured nothing.`);
|
|
124
|
-
}
|
|
125
|
-
return { ok: true, measurement: { gate, detected, total: detected + undetected } };
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Read a gate's measurement off the tree, refusing an absent report by the path it was sought at.
|
|
129
|
-
*
|
|
130
|
-
* THE PATH ARRIVES RESOLVED, AND THAT IS THE WHOLE OF THIS MODULE'S OPINION ABOUT WHERE A TREE IS.
|
|
131
|
-
* A reader that joined a root of its own — its own installed location, or wherever the process
|
|
132
|
-
* happened to start — is correct only while those coincide with the caller's, which they stop
|
|
133
|
-
* doing the moment this ships as something another repository installs.
|
|
134
|
-
*/
|
|
135
|
-
export function readMutationReport(gate, reportPath) {
|
|
136
|
-
if (!existsSync(reportPath)) {
|
|
137
|
-
return refused(`no report at ${reportPath} — the gate produced no score, or never ran.`);
|
|
138
|
-
}
|
|
139
|
-
let text;
|
|
140
|
-
try {
|
|
141
|
-
text = readFileSync(reportPath, "utf8");
|
|
142
|
-
}
|
|
143
|
-
catch (cause) {
|
|
144
|
-
return refused(`the report at ${reportPath} could not be read: ${raisedBy(cause)}`);
|
|
145
|
-
}
|
|
146
|
-
return measurementFromReport(gate, text, reportPath);
|
|
147
|
-
}
|
|
148
|
-
//# sourceMappingURL=report.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/mutation-ratchet/report.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* THE VERDICT — what a gate's run turned out to be, reconciled against the record it last banked
|
|
3
|
-
* (@SCN-RAT-004, 3F-2794; @SCN-RAT-005, 3F-2795; @SCN-RAT-006, 3F-2796; @SCN-RAT-007, 3F-2797;
|
|
4
|
-
* @SCN-RAT-008, 3F-2798; @SCN-RAT-016, 3F-2809).
|
|
5
|
-
*
|
|
6
|
-
* A BAR CAN ONLY ASK WHETHER ONE NUMBER FELL, AND THE INTERESTING QUESTION IS WHY. A mutation score
|
|
7
|
-
* is detected over total, so it drops for two entirely unrelated reasons: a mutant this suite used
|
|
8
|
-
* to kill now survives, which is the fault the gate exists for; or new mutable code arrived carrying
|
|
9
|
-
* survivors with it, which is no fault at all. `record.ts` keeps the two counts apart so that this
|
|
10
|
-
* module can tell those apart — and telling them apart is the whole product.
|
|
11
|
-
*
|
|
12
|
-
* THE KILLS ARE ASKED ABOUT FIRST, AND ASKED ABOUT ALONE. A run that loses kills WHILE its mutant
|
|
13
|
-
* population grows is the shape every looser reading gets wrong. The growth is a true fact about
|
|
14
|
-
* that run and innocent on its own, so a reconciler that notices it before it counts the kills
|
|
15
|
-
* answers with the innocent half — the score fell, the population explains why, nobody looks any
|
|
16
|
-
* further and a real regression ships. So the count comparison is the first thing this module does,
|
|
17
|
-
* and the population's movement is never allowed to be the answer while a kill is missing.
|
|
18
|
-
*
|
|
19
|
-
* AND INSIDE THAT FIRST COMPARISON, THE POPULATION'S DIRECTION DECIDES WHICH RED IS PRINTED. A
|
|
20
|
-
* population that held still or grew is what licenses the regression's sentence: nothing left the
|
|
21
|
-
* scope, so the kills that vanished were lost. A population that SHRANK removes that licence, and
|
|
22
|
-
* `population-shrank` says so rather than asserting it anyway — removing one killed mutant, and
|
|
23
|
-
* removing one survivor beside a weakened assertion, move the two counts identically, and no
|
|
24
|
-
* arithmetic over two whole numbers tells them apart (@SCN-RAT-016). Both readings are red and
|
|
25
|
-
* both stop the gate; only the sentence differs, and the sentence is the whole point.
|
|
26
|
-
*
|
|
27
|
-
* AND THE SCORE HAS EXACTLY ONE JOB, WHICH IS THE SECOND COMPARISON AND NOT THE FIRST. Once the
|
|
28
|
-
* kills are known to have held, the run either scored below what the record banked or it did not,
|
|
29
|
-
* and only the first of those is a population that grew: a run whose kills ROSE far enough carries
|
|
30
|
-
* its growth without any drop at all, and is an improvement rather than this. So the growth is not
|
|
31
|
-
* what is asked about — the fallen score is, and the growth follows from it, because a run that
|
|
32
|
-
* lost no kills can only score lower by measuring more mutants. That is the whole of what the score
|
|
33
|
-
* is allowed to decide here, and it is why it is asked second (@SCN-RAT-006).
|
|
34
|
-
*
|
|
35
|
-
* THE FLOOR IS STATED, NEVER CONSULTED. `floorOf` is deliberately absent from every condition here.
|
|
36
|
-
* It is the same bar expressed as a percentage, carried in the message because a reader arriving
|
|
37
|
-
* from the sweep has a percentage in front of them and wants the bar in the units they are already
|
|
38
|
-
* holding. The enforcement is the count comparison above it. The two agree exactly while the
|
|
39
|
-
* population holds still and part company the moment it moves, so reading the floor AS the gate
|
|
40
|
-
* would put the judgement back on the score — the confusion the counts model exists to prevent.
|
|
41
|
-
*
|
|
42
|
-
* NOTHING HERE NAMES A PROJECT, A CHECK, A RUNNER, A WORKFLOW OR A REPOSITORY. The gate's name
|
|
43
|
-
* arrives on the record and on the measurement, both of them the caller's own words.
|
|
44
|
-
*/
|
|
45
|
-
import { type GateRecord } from "./record.js";
|
|
46
|
-
/**
|
|
47
|
-
* One gate's measured run, in the same two counts the record is kept in.
|
|
48
|
-
*
|
|
49
|
-
* THE SAME PAIR AS THE RECORD, AND THAT IS WHAT MAKES THE COMPARISON POSSIBLE. A measurement that
|
|
50
|
-
* arrived as a score would already have folded the two events this module exists to separate into
|
|
51
|
-
* one number, before anything here could look at it.
|
|
52
|
-
*/
|
|
53
|
-
export interface MutationMeasurement {
|
|
54
|
-
/** The gate this run measured, as the caller names it. */
|
|
55
|
-
readonly gate: string;
|
|
56
|
-
/** Mutants the suite detected. */
|
|
57
|
-
readonly detected: number;
|
|
58
|
-
/** Every mutant the gate measured, detected or not. */
|
|
59
|
-
readonly total: number;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* What a run turned out to be.
|
|
63
|
-
*
|
|
64
|
-
* `regression` IS THE ONE DECIDED ON KILLS, which is what makes it immune to the population moving,
|
|
65
|
-
* and `within-slack` (@SCN-RAT-005) is its neighbour on the other side of the same comparison — the
|
|
66
|
-
* two share one boundary and neither can pin it alone. `population-grew` (@SCN-RAT-006) is the third
|
|
67
|
-
* built, and the first decided on anything but kills — it is the drop a bar shaped like a score
|
|
68
|
-
* cannot express, and the reason the record keeps two counts rather than one. `unrecorded-improvement`
|
|
69
|
-
* (@SCN-RAT-007) is the fourth, and the only RED one not decided on kills — it is the clause that
|
|
70
|
-
* turns a ceiling into a ratchet, and without it every other arm here is a bar that quietly drifts
|
|
71
|
-
* away from the tree it is meant to be holding. `at-baseline` (@SCN-RAT-008) is the fifth and the
|
|
72
|
-
* one that CLOSES the set: it is what is left once those four have each declined a run, so from it
|
|
73
|
-
* on there is no measurement this module can be handed that it cannot answer. `population-shrank`
|
|
74
|
-
* (@SCN-RAT-016) is the sixth built and `population-grew`'s twin on the other side of the same
|
|
75
|
-
* movement: it shares `regression`'s condition exactly and parts from it on the population's
|
|
76
|
-
* direction alone, because the sentence `regression` is allowed to say is licensed by nothing
|
|
77
|
-
* having left the scope. The last member sits
|
|
78
|
-
* outside the comparison entirely — `no-measurement` (@SCN-RAT-009) is reached from a report that
|
|
79
|
-
* holds nothing, before there are two counts to reconcile at all.
|
|
80
|
-
*/
|
|
81
|
-
export type MutationVerdictKind = "no-measurement" | "regression" | "population-shrank" | "within-slack" | "population-grew" | "unrecorded-improvement" | "at-baseline";
|
|
82
|
-
/**
|
|
83
|
-
* A verdict: whether the run stands, and the line its reader gets.
|
|
84
|
-
*
|
|
85
|
-
* THE MESSAGE IS NOT DECORATION. A verdict that reds without saying which kills went missing, what
|
|
86
|
-
* allowance they were measured against, and what to do next is a red somebody widens the bar to
|
|
87
|
-
* clear, because widening is cheaper than a hunt with no starting point.
|
|
88
|
-
*/
|
|
89
|
-
export interface MutationVerdict {
|
|
90
|
-
readonly kind: MutationVerdictKind;
|
|
91
|
-
/** Whether the run stands. */
|
|
92
|
-
readonly ok: boolean;
|
|
93
|
-
/** The line its reader gets, carrying every figure the verdict was reached from. */
|
|
94
|
-
readonly message: string;
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* The refusal, as a verdict (@SCN-RAT-009, 3F-2800).
|
|
98
|
-
*
|
|
99
|
-
* IT SITS OUTSIDE `verdictFor` BECAUSE IT IS REACHED BEFORE THERE ARE COUNTS TO RECONCILE. Every
|
|
100
|
-
* arm of that function compares two pairs of whole numbers; this one is what a caller has instead
|
|
101
|
-
* of a pair, so handing it a fabricated one — a zero, most temptingly — would put a real verdict's
|
|
102
|
-
* name on a run nobody measured.
|
|
103
|
-
*
|
|
104
|
-
* AND IT IS RED. A sweep that did not run produced no evidence about the tree, and a reconciler
|
|
105
|
-
* that shrugs at no evidence reports success for a run that measured nothing: the build green, the
|
|
106
|
-
* report absent, and the two facts never meeting.
|
|
107
|
-
*
|
|
108
|
-
* @param gate the gate that was to have been measured, as the caller names it
|
|
109
|
-
* @param reason why there is no measurement, naming the path it was sought at
|
|
110
|
-
*/
|
|
111
|
-
export declare function noMeasurement(gate: string, reason: string): MutationVerdict;
|
|
112
|
-
/**
|
|
113
|
-
* Reconcile one gate's run against the bar its record states.
|
|
114
|
-
*
|
|
115
|
-
* INSIDE THAT FIRST COMPARISON, `total` DECIDES WHICH RED IS PRINTED, AND IT IS ASKED THERE RATHER
|
|
116
|
-
* THAN BESIDE IT. The kills must already have fallen past the allowance before the population's
|
|
117
|
-
* direction is consulted at all, so a run that lost no kills is not either red however far its
|
|
118
|
-
* population fell, and a run that lost a kill the allowance covers is the pass below whichever way
|
|
119
|
-
* its population moved. What the direction settles is only which sentence a run that has already
|
|
120
|
-
* failed on kills is told: held still or grew licenses `regression`'s claim that a mutant this
|
|
121
|
-
* suite used to kill now survives, because nothing left the scope; SHRANK does not, and
|
|
122
|
-
* `population-shrank` says the two look identical from here instead (@SCN-RAT-016).
|
|
123
|
-
*
|
|
124
|
-
* THE COUNT COMPARISON IS FIRST, AND IT IS ON `detected` ALONE. Everything below it in this
|
|
125
|
-
* reconciler will be a question about the score, and the score cannot separate a lost kill from a
|
|
126
|
-
* grown population — so the one verdict that must not be reachable through the score is settled
|
|
127
|
-
* before the score is consulted at all. A run that loses kills and grows its population at the same
|
|
128
|
-
* time is named as the regression it is, rather than as the growth that would explain it away.
|
|
129
|
-
*
|
|
130
|
-
* `floorOf` IS DELIBERATELY ABSENT FROM THIS CONDITION. It is the same bar as a percentage, carried
|
|
131
|
-
* in the message for its reader; the enforcement is the comparison above. The two agree exactly
|
|
132
|
-
* while the population holds still, so a reconciler judging on the floor greens every example of
|
|
133
|
-
* this Rule and reds a grown population that lost nothing — which is the confusion the counts model
|
|
134
|
-
* exists to prevent.
|
|
135
|
-
*
|
|
136
|
-
* THE SECOND COMPARISON IS THE SAME BOUNDARY FROM THE OTHER SIDE. A run that lost kills the
|
|
137
|
-
* allowance covers is a pass, and `D == D0 - slack` is the last measurement it covers. The two arms
|
|
138
|
-
* meet at that one mutant and neither pins it alone: every run the arm above judges is a regression
|
|
139
|
-
* under `<` and `<=` alike, and under an allowance consulted or forgotten alike. It is the pass at
|
|
140
|
-
* the boundary that reds a comparison widened by one, and the pass one kill inside it that reds a
|
|
141
|
-
* comparison that dropped the allowance — which is why both examples are asserted and not one.
|
|
142
|
-
*
|
|
143
|
-
* THE THIRD COMPARISON IS THE FIRST ONE THE SCORE IS ALLOWED INTO, AND ONLY BECAUSE THE KILLS ARE
|
|
144
|
-
* ALREADY KNOWN TO HAVE HELD. Below this line nothing that used to die is surviving, so a fallen
|
|
145
|
-
* score has exactly one remaining cause — the run measured more mutants — and naming it is safe in
|
|
146
|
-
* a way it was never safe above. The order is the guarantee: the same growth that explains a drop
|
|
147
|
-
* here would have explained away a regression two comparisons ago, which is why it is not consulted
|
|
148
|
-
* until there is no regression left for it to explain away.
|
|
149
|
-
*
|
|
150
|
-
* A RUN THAT LOST A KILL THE ALLOWANCE COVERS **WHILE** ITS POPULATION GREW IS THE ARM ABOVE, NOT
|
|
151
|
-
* THIS ONE, and that is a ruling rather than an accident of ordering (@SCN-RAT-005 surfaced the
|
|
152
|
-
* shape and asserted neither way; ruled on 2026-08-26 under @SCN-RAT-006). Its score can be far
|
|
153
|
-
* below the floor, so the growth is there to be reached for. But this verdict's load-bearing
|
|
154
|
-
* sentence is that no mutant which used to die now survives, and of that run the sentence is false;
|
|
155
|
-
* answering with the growth would print it anyway. The allowance was also SPENT, and an allowance
|
|
156
|
-
* spends in public or it does not spend at all — so the pass that must fire is the one naming the
|
|
157
|
-
* kills lost and the record's own reason for tolerating them. Nothing is lost by the ruling: the
|
|
158
|
-
* growth stays legible in the count pairs that pass already carries.
|
|
159
|
-
*
|
|
160
|
-
* AND THE LAST ARM TAKES NO CONDITION, WHICH IS WHAT MAKES THIS FUNCTION TOTAL (@SCN-RAT-008). The
|
|
161
|
-
* three comparisons above leave exactly one case: kills that held, and a score that neither fell
|
|
162
|
-
* nor rose. `scoreFellFrom` and `scoreRoseFrom` are the same two cross-multiplied products read the
|
|
163
|
-
* two ways round, so between them they claim every inequality and EQUAL is what is left — and equal
|
|
164
|
-
* is a fact about the ratio rather than about the pair, which is why the fall-through is written
|
|
165
|
-
* with no condition at all. Narrowing it to "the same two counts" would leave a run of 4350/4486
|
|
166
|
-
* against a record of 2175/2243 with no verdict, and a reconciler that can be handed a measurement
|
|
167
|
-
* it does not answer is one whose callers must handle a case nobody named. There is no default
|
|
168
|
-
* green here either: `at-baseline` is a verdict somebody wrote, with its own scenario and its own
|
|
169
|
-
* message, not a shrug at the end of a chain of `if`s.
|
|
170
|
-
*
|
|
171
|
-
* @param record the gate's last-banked counts and the allowance stated against them
|
|
172
|
-
* @param measurement the run being judged, in the same two counts
|
|
173
|
-
* @param bankCommand the caller's own words for banking a measurement, carried into the verdicts
|
|
174
|
-
* that ask for it — never a script name this module invents (@SCN-RAT-006, 3F-2796)
|
|
175
|
-
*/
|
|
176
|
-
export declare function verdictFor(record: GateRecord, measurement: MutationMeasurement, bankCommand: string): MutationVerdict;
|
|
177
|
-
//# sourceMappingURL=verdict.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"verdict.d.ts","sourceRoot":"","sources":["../../src/mutation-ratchet/verdict.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,EAOL,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;AAErB;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,0DAA0D;IAC1D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kCAAkC;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,uDAAuD;IACvD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,mBAAmB,GAC3B,gBAAgB,GAChB,YAAY,GACZ,mBAAmB,GACnB,cAAc,GACd,iBAAiB,GACjB,wBAAwB,GACxB,aAAa,CAAC;AAElB;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,oFAAoF;IACpF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAkQD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,eAAe,CAW3E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,mBAAmB,EAChC,WAAW,EAAE,MAAM,GAClB,eAAe,CAgBjB"}
|