spec-controller 0.1.0-alpha.1 → 0.1.0-alpha.3
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 +94 -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 +2 -19
- package/dist/cli-balance/cli.d.ts.map +1 -1
- package/dist/cli-balance/cli.js +76 -78
- 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 +93 -33
- 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 +128 -0
- package/dist/corpus/cli.js.map +1 -0
- package/dist/deferralTags.d.ts +1 -1
- package/dist/deferralTags.js +1 -1
- package/dist/ingest/gherkinValidation.d.ts +23 -0
- package/dist/ingest/gherkinValidation.d.ts.map +1 -1
- package/dist/ingest/gherkinValidation.js +27 -0
- package/dist/ingest/gherkinValidation.js.map +1 -1
- package/dist/ingest/ingestQualityChecks.d.ts +17 -2
- package/dist/ingest/ingestQualityChecks.d.ts.map +1 -1
- package/dist/ingest/ingestQualityChecks.js +94 -39
- 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/mutation-ratchet/index.d.ts +36 -38
- package/dist/mutation-ratchet/index.d.ts.map +1 -1
- package/dist/mutation-ratchet/index.js +35 -37
- package/dist/mutation-ratchet/index.js.map +1 -1
- package/dist/mutation-ratchet/record.d.ts +21 -53
- package/dist/mutation-ratchet/record.d.ts.map +1 -1
- package/dist/mutation-ratchet/record.js +28 -44
- package/dist/mutation-ratchet/record.js.map +1 -1
- package/dist/mutation-ratchet/report.d.ts +31 -5
- package/dist/mutation-ratchet/report.d.ts.map +1 -1
- package/dist/mutation-ratchet/report.js +11 -3
- package/dist/mutation-ratchet/report.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 +7 -2
- 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/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,387 +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 { counts, floorOf, pct, recordedScoreOf, scoreOf, scoreRoseOver, } from "./record.js";
|
|
46
|
-
/**
|
|
47
|
-
* Fewer kills than the record less the allowance it states.
|
|
48
|
-
*
|
|
49
|
-
* WHAT THE MESSAGE OWES ITS READER. The kills that went missing and the allowance they were measured
|
|
50
|
-
* against, so the size of the gap is legible without arithmetic; both count pairs, so which of the
|
|
51
|
-
* two figures moved is visible; the floor as a percentage, so a reader arriving from the sweep has
|
|
52
|
-
* the bar in the units they are already holding; and then what to do, because a red with no starting
|
|
53
|
-
* point is a red somebody widens the bar to clear.
|
|
54
|
-
*
|
|
55
|
-
* THE ASSERTION IT MAKES IS SAFE BECAUSE IT IS MADE ON KILLS. "A mutant this suite used to kill now
|
|
56
|
-
* survives" is a claim about a mutant that still exists, and the kill count is what licenses it: the
|
|
57
|
-
* population's movement does not enter the comparison, so a run that lost kills is a run that lost
|
|
58
|
-
* kills whatever else it did at the same time.
|
|
59
|
-
*/
|
|
60
|
-
function regression(record, measurement) {
|
|
61
|
-
const lost = record.detected - measurement.detected;
|
|
62
|
-
return {
|
|
63
|
-
kind: "regression",
|
|
64
|
-
ok: false,
|
|
65
|
-
message: `${record.gate}: REGRESSION — ${lost} kill(s) lost against an allowance of ` +
|
|
66
|
-
`${record.allowance.mutants}. Recorded ${counts(record.detected, record.total)}, measured ` +
|
|
67
|
-
`${counts(measurement.detected, measurement.total)}; floor ${pct(floorOf(record))}%. ` +
|
|
68
|
-
"A mutant this suite used to kill now survives — find it in the run's report and restore " +
|
|
69
|
-
"the assertion.",
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* The kills fell past the allowance, and the population fell with them.
|
|
74
|
-
*
|
|
75
|
-
* A REMOVAL AND A REGRESSION ARE COUNT-IDENTICAL HERE, WHICH IS THE WHOLE REASON THIS IS A READING
|
|
76
|
-
* OF ITS OWN. Remove one mutant this suite killed and the record sees one kill fewer over one
|
|
77
|
-
* mutant fewer. Remove one SURVIVING mutant while a weakened assertion loses a kill, and the record
|
|
78
|
-
* sees exactly the same pair. No arithmetic over two whole numbers separates them, so the
|
|
79
|
-
* reconciler must not pretend it can: asserting the lost kill would be crying wolf at an honest
|
|
80
|
-
* refactor, in a gate whose entire thesis is not doing that.
|
|
81
|
-
*
|
|
82
|
-
* SO IT REDS ANYWAY, AND THAT IS NOT A HEDGE. Passing the shrink would let the weakened assertion
|
|
83
|
-
* hide inside the honest refactor, which is precisely the fault this gate exists for. And removed
|
|
84
|
-
* code genuinely does lower the kills the gate can ever reach, so the record has to come down —
|
|
85
|
-
* deliberately, by hand, with the reason in the commit. That is the same conscious lowering the
|
|
86
|
-
* recorder's refusal already points at (@SCN-RAT-012), and it stays the only way down: no flag
|
|
87
|
-
* lowers a record, so this message must not read as though one did. It names no flag and asks for
|
|
88
|
-
* no banking, because banking is up-only and would be refused.
|
|
89
|
-
*
|
|
90
|
-
* THE HONESTY LIVES IN THE MESSAGE, WHICH IS THE HALF A BARE RED ALWAYS DROPS. Both falls, said out
|
|
91
|
-
* loud; both count pairs; that two unrelated events look identical from here; and then BOTH
|
|
92
|
-
* branches, because picking one would be picking on evidence this module does not have.
|
|
93
|
-
*
|
|
94
|
-
* WHAT WOULD ACTUALLY DECIDE IT, CONSIDERED AND NOT BUILT (3F-2786). A per-mutant identity record —
|
|
95
|
-
* WHICH mutants were killed, compared as sets — separates them safely: a lost kill is a mutant that
|
|
96
|
-
* persisted and flipped, a removal is a mutant that no longer exists. It is a materially heavier
|
|
97
|
-
* record than two whole numbers, and it waits until the friction of hand-lowering proves frequent
|
|
98
|
-
* enough to earn it.
|
|
99
|
-
*/
|
|
100
|
-
function populationShrank(record, measurement) {
|
|
101
|
-
const kills = record.detected - measurement.detected;
|
|
102
|
-
const mutants = record.total - measurement.total;
|
|
103
|
-
return {
|
|
104
|
-
kind: "population-shrank",
|
|
105
|
-
ok: false,
|
|
106
|
-
message: `${record.gate}: POPULATION SHRANK — ${kills} kill(s) fewer, and ${mutants} mutant(s) ` +
|
|
107
|
-
`fewer with them. Recorded ${counts(record.detected, record.total)}, measured ` +
|
|
108
|
-
`${counts(measurement.detected, measurement.total)}. Two unrelated things look identical ` +
|
|
109
|
-
"from here. If you removed tested code, the record describes a tree that no longer exists: " +
|
|
110
|
-
"lower it by hand, deliberately, with the reason in the commit — nothing lowers a record " +
|
|
111
|
-
"for you. If you removed none, a test weakened and this is a regression: find the survivor " +
|
|
112
|
-
"in the run's own report and restore the assertion. The counts cannot tell those two apart, " +
|
|
113
|
-
"so this reds either way.",
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* A kill lost, but not past the record less the allowance it states.
|
|
118
|
-
*
|
|
119
|
-
* THE ALLOWANCE IS SPENT IN PUBLIC OR IT IS NOT SPENT AT ALL. This arm is a PASS, and a pass is
|
|
120
|
-
* where a message stops being written carefully — the run stands, so nobody is being sent anywhere.
|
|
121
|
-
* That is exactly what makes the quiet version dangerous: a gate that absorbs a lost kill because a
|
|
122
|
-
* figure in the record said it could has moved, and the move stays invisible for as long as nobody
|
|
123
|
-
* goes and re-derives it. So this one reports as fully as the red does.
|
|
124
|
-
*
|
|
125
|
-
* AND IT CARRIES THE RECORD'S OWN REASON, WHICH IS THE HALF A BARE PASS ALWAYS DROPS. The reason is
|
|
126
|
-
* the standard the allowance answers to — the gate's own measured non-determinism — and a standard
|
|
127
|
-
* nobody is shown is one nobody can notice has stopped being true. It is printed as the record
|
|
128
|
-
* states it rather than paraphrased here, because a reason this module worded would be this
|
|
129
|
-
* module's opinion about a project's gate, and it has none.
|
|
130
|
-
*
|
|
131
|
-
* IT IS REACHED ON KILLS, LIKE THE ARM ABOVE IT. The question this arm answers is "did a kill go
|
|
132
|
-
* missing, and does the allowance cover it" — the population's movement is not consulted, in either
|
|
133
|
-
* direction, for the same reason it is not consulted in `regression`.
|
|
134
|
-
*/
|
|
135
|
-
function withinSlack(record, measurement) {
|
|
136
|
-
const lost = record.detected - measurement.detected;
|
|
137
|
-
return {
|
|
138
|
-
kind: "within-slack",
|
|
139
|
-
ok: true,
|
|
140
|
-
message: `${record.gate}: PASSED INSIDE THE ALLOWANCE — ${lost} kill(s) lost against an allowance ` +
|
|
141
|
-
`of ${record.allowance.mutants}, which the run did not exceed. Recorded ` +
|
|
142
|
-
`${counts(record.detected, record.total)}, measured ` +
|
|
143
|
-
`${counts(measurement.detected, measurement.total)}. The record states that allowance ` +
|
|
144
|
-
`because: ${record.allowance.reason}`,
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Whether the run scored below what the record banked — in whole mutants, never in percentages.
|
|
149
|
-
*
|
|
150
|
-
* CROSS-MULTIPLIED RATHER THAN DIVIDED, so the comparison is exact — `record.ts`'s own comparison,
|
|
151
|
-
* read with the arguments the way round that asks whether the RECORD scores above the run. Two
|
|
152
|
-
* scores that are equal as ratios can differ in their last bit once each has been through a
|
|
153
|
-
* division, and a run whose score did not move would then read as one that fell, which is the
|
|
154
|
-
* difference between this verdict and the two below it.
|
|
155
|
-
*
|
|
156
|
-
* AND ITS ANSWER IS WHAT ESTABLISHES THE GROWTH, RATHER THAN THE OTHER WAY ROUND. It is only ever
|
|
157
|
-
* asked of a run whose kills held or rose, and a run that lost no kills cannot score lower unless it
|
|
158
|
-
* measured more mutants — so a `true` here means the population grew, and by strictly more than
|
|
159
|
-
* nothing, which is what lets the message state the difference as a count of new mutants.
|
|
160
|
-
*/
|
|
161
|
-
function scoreFellFrom(record, measurement) {
|
|
162
|
-
return scoreRoseOver(record.detected, record.total, measurement.detected, measurement.total);
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* No kill lost, and a score that fell only because the run measured more mutants.
|
|
166
|
-
*
|
|
167
|
-
* THIS IS THE DROP A BAR SHAPED LIKE A SCORE CANNOT EXPRESS, and the reason the record keeps two
|
|
168
|
-
* counts. New mutable code arrived carrying survivors with it, so the score fell while every mutant
|
|
169
|
-
* this suite used to kill still dies. A score bar reds on that, and the cheapest way back to green
|
|
170
|
-
* is then to widen the bar — which spends the gate's teeth on a run that lost nothing, and spends
|
|
171
|
-
* them for every real regression that follows. So it passes.
|
|
172
|
-
*
|
|
173
|
-
* AND IT PASSES LOUDLY, BECAUSE THE SILENT VERSION IS THE OPPOSITE ERROR. Mutable code arriving with
|
|
174
|
-
* survivors on it is a real event, and the record has stopped describing the tree it was banked
|
|
175
|
-
* from. So the message states how many mutants the run measured that the record never held, that no
|
|
176
|
-
* kill went missing, both count pairs with the score each implies, and the ask.
|
|
177
|
-
*
|
|
178
|
-
* THE ASK IS IN THE CALLER'S OWN WORDS. What to type to bank a measurement is a project's own fact —
|
|
179
|
-
* its script, its runner, its file — so a command worded here would be this module naming one. It
|
|
180
|
-
* arrives as an input and is carried through unchanged.
|
|
181
|
-
*/
|
|
182
|
-
function populationGrew(record, measurement, bankCommand) {
|
|
183
|
-
const grown = measurement.total - record.total;
|
|
184
|
-
return {
|
|
185
|
-
kind: "population-grew",
|
|
186
|
-
ok: true,
|
|
187
|
-
message: `${record.gate}: PASSED ON A GROWN POPULATION — ${grown} mutant(s) measured that the ` +
|
|
188
|
-
`record never held, and no kill went missing. Recorded ` +
|
|
189
|
-
`${counts(record.detected, record.total)}, measured ` +
|
|
190
|
-
`${counts(measurement.detected, measurement.total)}: the score fell because the population ` +
|
|
191
|
-
"grew, not because a mutant this suite used to kill now survives. The record no longer " +
|
|
192
|
-
`describes the tree it was banked from — bank it: ${bankCommand}`,
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* Whether the run scored ABOVE what the record banked — the mirror of `scoreFellFrom`, and asked
|
|
197
|
-
* the same way for the same reason.
|
|
198
|
-
*
|
|
199
|
-
* THE SAME COMPARISON, THE ARGUMENTS THE OTHER WAY ROUND, and that is now literally true rather
|
|
200
|
-
* than true by diligence: both call `scoreRoseOver`, so above and below are one function's two
|
|
201
|
-
* readings and cannot drift into disagreeing. What that leaves is EQUAL, the one case neither
|
|
202
|
-
* claims, and equal is a verdict of its own (@SCN-RAT-008). Two comparisons reached by different
|
|
203
|
-
* arithmetic could each answer `false` for a run that is genuinely one or the other, and the run
|
|
204
|
-
* would fall out of the reconciler entirely.
|
|
205
|
-
*/
|
|
206
|
-
function scoreRoseFrom(record, measurement) {
|
|
207
|
-
return scoreRoseOver(measurement.detected, measurement.total, record.detected, record.total);
|
|
208
|
-
}
|
|
209
|
-
/**
|
|
210
|
-
* A tree that measured better than the record it is being judged against.
|
|
211
|
-
*
|
|
212
|
-
* THIS IS THE CLAUSE THAT TURNS A CEILING INTO A RATCHET, AND IT IS THE ONE A READER'S INSTINCT
|
|
213
|
-
* FIGHTS. The suite got better and the verdict reds, which reads as a gate punishing the very thing
|
|
214
|
-
* it was built to want. It is not that. The gap between a stale record and a better tree is
|
|
215
|
-
* HEAD-ROOM, and head-room is the one thing a bar must never be allowed to accumulate: a survivor
|
|
216
|
-
* arriving inside it costs nothing and reds nothing, so the gate goes quiet exactly where it was
|
|
217
|
-
* meant to be watching, and stays quiet until the whole gain has been given back.
|
|
218
|
-
*
|
|
219
|
-
* THE SAME RULING IS ALREADY MADE ONE LEVEL UP. `complexity-exceptions` (@SCN-CPX-003) reds an
|
|
220
|
-
* exception list that disagrees with the tree in EITHER direction — over the recorded figure is a
|
|
221
|
-
* regression, under it is an improvement nobody recorded — because a recorded figure that has
|
|
222
|
-
* stopped describing the tree has stopped being a bar, whichever side of it the tree moved to.
|
|
223
|
-
*
|
|
224
|
-
* SO THE MESSAGE IS AN ERRAND RATHER THAN A REPRIMAND. It says how far above the record the tree
|
|
225
|
-
* measured, both count pairs so which figure moved is visible, the head-room argument itself — a
|
|
226
|
-
* red nobody understands is a red somebody deletes — and then the two figures banking would write,
|
|
227
|
-
* beside what to type to write them. Clearing it is one command.
|
|
228
|
-
*
|
|
229
|
-
* THE POINTS ARE A REPORTED FIGURE, NOT THE DECISION VARIABLE, exactly as the floor is above. The
|
|
230
|
-
* comparison was made on whole counts; this division happens after it, so no verdict here was ever
|
|
231
|
-
* reached by dividing. It is stated because a reader arriving from the sweep has percentages in
|
|
232
|
-
* front of them and wants to know how much of a gap they are being asked to bank.
|
|
233
|
-
*
|
|
234
|
-
* AND THE FIGURES IN THE ASK ARE THE MEASURED ONES. "Bank the record" is the sentence, so writing
|
|
235
|
-
* the RECORD's own counts into it is the natural slip — and a message that already prints both
|
|
236
|
-
* pairs elsewhere hides that slip completely, because every figure a reader is hunting for is
|
|
237
|
-
* somewhere on the line.
|
|
238
|
-
*/
|
|
239
|
-
function unrecordedImprovement(record, measurement, bankCommand) {
|
|
240
|
-
const better = scoreOf(measurement.detected, measurement.total) - recordedScoreOf(record);
|
|
241
|
-
return {
|
|
242
|
-
kind: "unrecorded-improvement",
|
|
243
|
-
ok: false,
|
|
244
|
-
message: `${record.gate}: RATCHET DEBT — the tree measured ${pct(better)} points better than the ` +
|
|
245
|
-
`record holds. Recorded ${counts(record.detected, record.total)}, measured ` +
|
|
246
|
-
`${counts(measurement.detected, measurement.total)}, and the gap between the two is ` +
|
|
247
|
-
"head-room: a survivor arriving inside it would cost nothing and red nothing, so this gate " +
|
|
248
|
-
"is quiet exactly where it was meant to be watching until the whole gain is given back. " +
|
|
249
|
-
`Bank ${measurement.detected}/${measurement.total}: ${bankCommand}`,
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
/**
|
|
253
|
-
* A run that scored exactly what the record banked.
|
|
254
|
-
*
|
|
255
|
-
* THE FALL-THROUGH, AND IT TAKES NO CONDITION OF ITS OWN. Every arm above claims its runs on a
|
|
256
|
-
* question it asks out loud; this one claims what is left over, and what is left over is exactly
|
|
257
|
-
* the case the two score comparisons cannot express: they are `<` and `>` of the same two products,
|
|
258
|
-
* so EQUAL is the one answer neither can give. Writing a condition here — "the same two counts" is
|
|
259
|
-
* the one that suggests itself — would narrow the arm to a subset of the runs that reach it and
|
|
260
|
-
* leave the rest with no verdict at all, which is the failure a fall-through exists to make
|
|
261
|
-
* impossible. It is equality of the RATIO, not of the pair: a run measuring 4350 of 4486 against a
|
|
262
|
-
* record of 2175 of 2243 has moved both of its counts and moved its score not at all.
|
|
263
|
-
*
|
|
264
|
-
* AND IT SAYS ALMOST NOTHING, WHICH IS THE OTHER HALF OF WHAT IT IS FOR. Both count pairs and the
|
|
265
|
-
* score they share, because a reader who wants to know a run held its ground wants the two figures
|
|
266
|
-
* it held against. Nothing else — no kills lost, because none were; no head-room, because there is
|
|
267
|
-
* none; no floor, because nothing came near one; and above all no ASK. The two loud greens either
|
|
268
|
-
* side of this one each end in one, and a reader who sees an ask on all three has lost the
|
|
269
|
-
* distinction between a run that needs something doing and a run that does not.
|
|
270
|
-
*/
|
|
271
|
-
function atBaseline(record, measurement) {
|
|
272
|
-
return {
|
|
273
|
-
kind: "at-baseline",
|
|
274
|
-
ok: true,
|
|
275
|
-
message: `${record.gate}: ON THE RECORD — recorded ${counts(record.detected, record.total)}, ` +
|
|
276
|
-
`measured ${counts(measurement.detected, measurement.total)}: the run scored exactly what ` +
|
|
277
|
-
"the record banked, and no kill went missing.",
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* The refusal, as a verdict (@SCN-RAT-009, 3F-2800).
|
|
282
|
-
*
|
|
283
|
-
* IT SITS OUTSIDE `verdictFor` BECAUSE IT IS REACHED BEFORE THERE ARE COUNTS TO RECONCILE. Every
|
|
284
|
-
* arm of that function compares two pairs of whole numbers; this one is what a caller has instead
|
|
285
|
-
* of a pair, so handing it a fabricated one — a zero, most temptingly — would put a real verdict's
|
|
286
|
-
* name on a run nobody measured.
|
|
287
|
-
*
|
|
288
|
-
* AND IT IS RED. A sweep that did not run produced no evidence about the tree, and a reconciler
|
|
289
|
-
* that shrugs at no evidence reports success for a run that measured nothing: the build green, the
|
|
290
|
-
* report absent, and the two facts never meeting.
|
|
291
|
-
*
|
|
292
|
-
* @param gate the gate that was to have been measured, as the caller names it
|
|
293
|
-
* @param reason why there is no measurement, naming the path it was sought at
|
|
294
|
-
*/
|
|
295
|
-
export function noMeasurement(gate, reason) {
|
|
296
|
-
return {
|
|
297
|
-
kind: "no-measurement",
|
|
298
|
-
ok: false,
|
|
299
|
-
message: `${gate}: NO MEASUREMENT — ${reason} A run that produced no score is not a run that ` +
|
|
300
|
-
"passed: a green build over an absent report is how a gate stays broken for nights on end " +
|
|
301
|
-
"with nobody the wiser. It is not a score of zero either — zero is a measurement, and " +
|
|
302
|
-
"reconciling one reaches a regression whose reader goes looking for kills that, here, " +
|
|
303
|
-
"were never made in the first place.",
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
/**
|
|
307
|
-
* Reconcile one gate's run against the bar its record states.
|
|
308
|
-
*
|
|
309
|
-
* INSIDE THAT FIRST COMPARISON, `total` DECIDES WHICH RED IS PRINTED, AND IT IS ASKED THERE RATHER
|
|
310
|
-
* THAN BESIDE IT. The kills must already have fallen past the allowance before the population's
|
|
311
|
-
* direction is consulted at all, so a run that lost no kills is not either red however far its
|
|
312
|
-
* population fell, and a run that lost a kill the allowance covers is the pass below whichever way
|
|
313
|
-
* its population moved. What the direction settles is only which sentence a run that has already
|
|
314
|
-
* failed on kills is told: held still or grew licenses `regression`'s claim that a mutant this
|
|
315
|
-
* suite used to kill now survives, because nothing left the scope; SHRANK does not, and
|
|
316
|
-
* `population-shrank` says the two look identical from here instead (@SCN-RAT-016).
|
|
317
|
-
*
|
|
318
|
-
* THE COUNT COMPARISON IS FIRST, AND IT IS ON `detected` ALONE. Everything below it in this
|
|
319
|
-
* reconciler will be a question about the score, and the score cannot separate a lost kill from a
|
|
320
|
-
* grown population — so the one verdict that must not be reachable through the score is settled
|
|
321
|
-
* before the score is consulted at all. A run that loses kills and grows its population at the same
|
|
322
|
-
* time is named as the regression it is, rather than as the growth that would explain it away.
|
|
323
|
-
*
|
|
324
|
-
* `floorOf` IS DELIBERATELY ABSENT FROM THIS CONDITION. It is the same bar as a percentage, carried
|
|
325
|
-
* in the message for its reader; the enforcement is the comparison above. The two agree exactly
|
|
326
|
-
* while the population holds still, so a reconciler judging on the floor greens every example of
|
|
327
|
-
* this Rule and reds a grown population that lost nothing — which is the confusion the counts model
|
|
328
|
-
* exists to prevent.
|
|
329
|
-
*
|
|
330
|
-
* THE SECOND COMPARISON IS THE SAME BOUNDARY FROM THE OTHER SIDE. A run that lost kills the
|
|
331
|
-
* allowance covers is a pass, and `D == D0 - slack` is the last measurement it covers. The two arms
|
|
332
|
-
* meet at that one mutant and neither pins it alone: every run the arm above judges is a regression
|
|
333
|
-
* under `<` and `<=` alike, and under an allowance consulted or forgotten alike. It is the pass at
|
|
334
|
-
* the boundary that reds a comparison widened by one, and the pass one kill inside it that reds a
|
|
335
|
-
* comparison that dropped the allowance — which is why both examples are asserted and not one.
|
|
336
|
-
*
|
|
337
|
-
* THE THIRD COMPARISON IS THE FIRST ONE THE SCORE IS ALLOWED INTO, AND ONLY BECAUSE THE KILLS ARE
|
|
338
|
-
* ALREADY KNOWN TO HAVE HELD. Below this line nothing that used to die is surviving, so a fallen
|
|
339
|
-
* score has exactly one remaining cause — the run measured more mutants — and naming it is safe in
|
|
340
|
-
* a way it was never safe above. The order is the guarantee: the same growth that explains a drop
|
|
341
|
-
* here would have explained away a regression two comparisons ago, which is why it is not consulted
|
|
342
|
-
* until there is no regression left for it to explain away.
|
|
343
|
-
*
|
|
344
|
-
* A RUN THAT LOST A KILL THE ALLOWANCE COVERS **WHILE** ITS POPULATION GREW IS THE ARM ABOVE, NOT
|
|
345
|
-
* THIS ONE, and that is a ruling rather than an accident of ordering (@SCN-RAT-005 surfaced the
|
|
346
|
-
* shape and asserted neither way; ruled on 2026-08-26 under @SCN-RAT-006). Its score can be far
|
|
347
|
-
* below the floor, so the growth is there to be reached for. But this verdict's load-bearing
|
|
348
|
-
* sentence is that no mutant which used to die now survives, and of that run the sentence is false;
|
|
349
|
-
* answering with the growth would print it anyway. The allowance was also SPENT, and an allowance
|
|
350
|
-
* spends in public or it does not spend at all — so the pass that must fire is the one naming the
|
|
351
|
-
* kills lost and the record's own reason for tolerating them. Nothing is lost by the ruling: the
|
|
352
|
-
* growth stays legible in the count pairs that pass already carries.
|
|
353
|
-
*
|
|
354
|
-
* AND THE LAST ARM TAKES NO CONDITION, WHICH IS WHAT MAKES THIS FUNCTION TOTAL (@SCN-RAT-008). The
|
|
355
|
-
* three comparisons above leave exactly one case: kills that held, and a score that neither fell
|
|
356
|
-
* nor rose. `scoreFellFrom` and `scoreRoseFrom` are the same two cross-multiplied products read the
|
|
357
|
-
* two ways round, so between them they claim every inequality and EQUAL is what is left — and equal
|
|
358
|
-
* is a fact about the ratio rather than about the pair, which is why the fall-through is written
|
|
359
|
-
* with no condition at all. Narrowing it to "the same two counts" would leave a run of 4350/4486
|
|
360
|
-
* against a record of 2175/2243 with no verdict, and a reconciler that can be handed a measurement
|
|
361
|
-
* it does not answer is one whose callers must handle a case nobody named. There is no default
|
|
362
|
-
* green here either: `at-baseline` is a verdict somebody wrote, with its own scenario and its own
|
|
363
|
-
* message, not a shrug at the end of a chain of `if`s.
|
|
364
|
-
*
|
|
365
|
-
* @param record the gate's last-banked counts and the allowance stated against them
|
|
366
|
-
* @param measurement the run being judged, in the same two counts
|
|
367
|
-
* @param bankCommand the caller's own words for banking a measurement, carried into the verdicts
|
|
368
|
-
* that ask for it — never a script name this module invents (@SCN-RAT-006, 3F-2796)
|
|
369
|
-
*/
|
|
370
|
-
export function verdictFor(record, measurement, bankCommand) {
|
|
371
|
-
if (measurement.detected < record.detected - record.allowance.mutants) {
|
|
372
|
-
return measurement.total < record.total
|
|
373
|
-
? populationShrank(record, measurement)
|
|
374
|
-
: regression(record, measurement);
|
|
375
|
-
}
|
|
376
|
-
if (measurement.detected < record.detected) {
|
|
377
|
-
return withinSlack(record, measurement);
|
|
378
|
-
}
|
|
379
|
-
if (scoreFellFrom(record, measurement)) {
|
|
380
|
-
return populationGrew(record, measurement, bankCommand);
|
|
381
|
-
}
|
|
382
|
-
if (scoreRoseFrom(record, measurement)) {
|
|
383
|
-
return unrecordedImprovement(record, measurement, bankCommand);
|
|
384
|
-
}
|
|
385
|
-
return atBaseline(record, measurement);
|
|
386
|
-
}
|
|
387
|
-
//# sourceMappingURL=verdict.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"verdict.js","sourceRoot":"","sources":["../../src/mutation-ratchet/verdict.ts"],"names":[],"mappings":"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"}
|