@3f-consulting/spec-controller-core 0.1.0-alpha.8 → 0.1.0-alpha.81
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 +3 -3
- package/dist/assemble/evidenceReconciliationFromSources.d.ts +18 -53
- package/dist/assemble/evidenceReconciliationFromSources.d.ts.map +1 -1
- package/dist/assemble/evidenceReconciliationFromSources.js +31 -326
- package/dist/assemble/evidenceReconciliationFromSources.js.map +1 -1
- package/dist/assemble/renderInput.d.ts +15 -81
- package/dist/assemble/renderInput.d.ts.map +1 -1
- package/dist/assemble/renderInput.js +3 -45
- package/dist/assemble/renderInput.js.map +1 -1
- package/dist/census/evidenceObligations.d.ts +18 -52
- package/dist/census/evidenceObligations.d.ts.map +1 -1
- package/dist/census/evidenceObligations.js +22 -25
- package/dist/census/evidenceObligations.js.map +1 -1
- package/dist/domain/evidenceOrder.d.ts +3 -0
- package/dist/domain/evidenceOrder.d.ts.map +1 -0
- package/dist/domain/evidenceOrder.js +15 -0
- package/dist/domain/evidenceOrder.js.map +1 -0
- package/dist/domain/evidenceReconciliation.d.ts +45 -410
- package/dist/domain/evidenceReconciliation.d.ts.map +1 -1
- package/dist/domain/evidenceReconciliation.js +74 -388
- package/dist/domain/evidenceReconciliation.js.map +1 -1
- package/dist/domain/featureCode.d.ts +4 -0
- package/dist/domain/featureCode.d.ts.map +1 -0
- package/dist/domain/featureCode.js +12 -0
- package/dist/domain/featureCode.js.map +1 -0
- package/dist/domain/journal.d.ts +27 -411
- package/dist/domain/journal.d.ts.map +1 -1
- package/dist/domain/journal.js +7 -118
- package/dist/domain/journal.js.map +1 -1
- package/dist/domain/ledger.d.ts +37 -357
- package/dist/domain/ledger.d.ts.map +1 -1
- package/dist/domain/ledger.js +43 -346
- package/dist/domain/ledger.js.map +1 -1
- package/dist/domain/messages.d.ts +224 -0
- package/dist/domain/messages.d.ts.map +1 -0
- package/dist/domain/messages.js +335 -0
- package/dist/domain/messages.js.map +1 -0
- package/dist/domain/outcome.d.ts +3 -0
- package/dist/domain/outcome.d.ts.map +1 -0
- package/dist/domain/outcome.js +2 -0
- package/dist/domain/outcome.js.map +1 -0
- package/dist/domain/posting.d.ts +24 -121
- package/dist/domain/posting.d.ts.map +1 -1
- package/dist/domain/posting.js +4 -56
- package/dist/domain/posting.js.map +1 -1
- package/dist/domain/qualityCheckFault.d.ts +4 -44
- package/dist/domain/qualityCheckFault.d.ts.map +1 -1
- package/dist/domain/qualityCheckFault.js +0 -24
- package/dist/domain/qualityCheckFault.js.map +1 -1
- package/dist/domain/qualityCheckReconciliation.d.ts +16 -288
- package/dist/domain/qualityCheckReconciliation.d.ts.map +1 -1
- package/dist/domain/qualityCheckReconciliation.js +84 -479
- package/dist/domain/qualityCheckReconciliation.js.map +1 -1
- package/dist/domain/qualityCheckReconciliationFault.d.ts +0 -19
- package/dist/domain/qualityCheckReconciliationFault.d.ts.map +1 -1
- package/dist/domain/qualityCheckReconciliationFault.js +1 -24
- package/dist/domain/qualityCheckReconciliationFault.js.map +1 -1
- package/dist/domain/scenarioCodeError.d.ts +16 -0
- package/dist/domain/scenarioCodeError.d.ts.map +1 -0
- package/dist/domain/scenarioCodeError.js +4 -0
- package/dist/domain/scenarioCodeError.js.map +1 -0
- package/dist/domain/scenarioReconciliation.d.ts +19 -216
- package/dist/domain/scenarioReconciliation.d.ts.map +1 -1
- package/dist/domain/scenarioReconciliation.js +29 -101
- package/dist/domain/scenarioReconciliation.js.map +1 -1
- package/dist/domain/scnAllocation.d.ts +27 -0
- package/dist/domain/scnAllocation.d.ts.map +1 -0
- package/dist/domain/scnAllocation.js +33 -0
- package/dist/domain/scnAllocation.js.map +1 -0
- package/dist/domain/verdictCode.d.ts +10 -0
- package/dist/domain/verdictCode.d.ts.map +1 -0
- package/dist/domain/verdictCode.js +16 -0
- package/dist/domain/verdictCode.js.map +1 -0
- package/dist/index.d.ts +14 -40
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -88
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +3 -27
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +3 -94
- package/dist/internal.js.map +1 -1
- package/dist/lib/bareTerm.d.ts +0 -39
- package/dist/lib/bareTerm.d.ts.map +1 -1
- package/dist/lib/bareTerm.js +0 -39
- package/dist/lib/bareTerm.js.map +1 -1
- package/dist/lib/portablePath.d.ts.map +1 -1
- package/dist/lib/portablePath.js +2 -16
- package/dist/lib/portablePath.js.map +1 -1
- package/dist/lib/scnCitation.d.ts +0 -32
- package/dist/lib/scnCitation.d.ts.map +1 -1
- package/dist/lib/scnCitation.js +2 -42
- package/dist/lib/scnCitation.js.map +1 -1
- package/dist/model.d.ts +0 -23
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +0 -23
- package/dist/model.js.map +1 -1
- package/dist/parse/checkScriptsRunInCI.d.ts +6 -169
- package/dist/parse/checkScriptsRunInCI.d.ts.map +1 -1
- package/dist/parse/checkScriptsRunInCI.js +11 -171
- package/dist/parse/checkScriptsRunInCI.js.map +1 -1
- package/dist/parse/ciStepRecord.d.ts +4 -118
- package/dist/parse/ciStepRecord.d.ts.map +1 -1
- package/dist/parse/ciStepRecord.js +20 -227
- package/dist/parse/ciStepRecord.js.map +1 -1
- package/dist/parse/commandRuns.d.ts +0 -39
- package/dist/parse/commandRuns.d.ts.map +1 -1
- package/dist/parse/commandRuns.js +6 -67
- package/dist/parse/commandRuns.js.map +1 -1
- package/dist/parse/cucumber.d.ts +13 -66
- package/dist/parse/cucumber.d.ts.map +1 -1
- package/dist/parse/cucumber.js +42 -146
- package/dist/parse/cucumber.js.map +1 -1
- package/dist/parse/discoveryScope.d.ts +1 -46
- package/dist/parse/discoveryScope.d.ts.map +1 -1
- package/dist/parse/discoveryScope.js +4 -61
- package/dist/parse/discoveryScope.js.map +1 -1
- package/dist/parse/fitnessCitations.d.ts +1 -72
- package/dist/parse/fitnessCitations.d.ts.map +1 -1
- package/dist/parse/fitnessCitations.js +3 -55
- package/dist/parse/fitnessCitations.js.map +1 -1
- package/dist/parse/reconcilerSteps.d.ts +0 -46
- package/dist/parse/reconcilerSteps.d.ts.map +1 -1
- package/dist/parse/reconcilerSteps.js +2 -64
- package/dist/parse/reconcilerSteps.js.map +1 -1
- package/dist/parse/runningFitnessFunctions.d.ts +9 -0
- package/dist/parse/runningFitnessFunctions.d.ts.map +1 -0
- package/dist/parse/runningFitnessFunctions.js +57 -0
- package/dist/parse/runningFitnessFunctions.js.map +1 -0
- package/dist/parse/scenarioCodeErrors.d.ts +11 -0
- package/dist/parse/scenarioCodeErrors.d.ts.map +1 -0
- package/dist/parse/scenarioCodeErrors.js +78 -0
- package/dist/parse/scenarioCodeErrors.js.map +1 -0
- package/dist/parse/scenarios.d.ts +5 -230
- package/dist/parse/scenarios.d.ts.map +1 -1
- package/dist/parse/scenarios.js +25 -467
- package/dist/parse/scenarios.js.map +1 -1
- package/dist/parse/stepAccounting.d.ts +0 -46
- package/dist/parse/stepAccounting.d.ts.map +1 -1
- package/dist/parse/stepAccounting.js +1 -55
- package/dist/parse/stepAccounting.js.map +1 -1
- package/dist/parse/stepExclusions.d.ts +0 -47
- package/dist/parse/stepExclusions.d.ts.map +1 -1
- package/dist/parse/stepExclusions.js +2 -50
- package/dist/parse/stepExclusions.js.map +1 -1
- package/dist/parse/stepNames.d.ts +0 -27
- package/dist/parse/stepNames.d.ts.map +1 -1
- package/dist/parse/stepNames.js +0 -27
- package/dist/parse/stepNames.js.map +1 -1
- package/dist/parse/targetCorpus.d.ts +3 -80
- package/dist/parse/targetCorpus.d.ts.map +1 -1
- package/dist/parse/targetCorpus.js +4 -46
- package/dist/parse/targetCorpus.js.map +1 -1
- package/dist/parse/targetManifest.d.ts +8 -146
- package/dist/parse/targetManifest.d.ts.map +1 -1
- package/dist/parse/targetManifest.js +48 -198
- package/dist/parse/targetManifest.js.map +1 -1
- package/dist/parse/vitest.d.ts +8 -68
- package/dist/parse/vitest.d.ts.map +1 -1
- package/dist/parse/vitest.js +4 -83
- package/dist/parse/vitest.js.map +1 -1
- package/dist/provenance/provenance.d.ts +30 -138
- package/dist/provenance/provenance.d.ts.map +1 -1
- package/dist/provenance/provenance.js +5 -65
- package/dist/provenance/provenance.js.map +1 -1
- package/dist/render/behaviouralRunners.d.ts +0 -29
- package/dist/render/behaviouralRunners.d.ts.map +1 -1
- package/dist/render/behaviouralRunners.js +8 -71
- package/dist/render/behaviouralRunners.js.map +1 -1
- package/dist/render/configurationNotRead.d.ts +3 -0
- package/dist/render/configurationNotRead.d.ts.map +1 -0
- package/dist/render/configurationNotRead.js +17 -0
- package/dist/render/configurationNotRead.js.map +1 -0
- package/dist/render/discoveryScope.d.ts +0 -37
- package/dist/render/discoveryScope.d.ts.map +1 -1
- package/dist/render/discoveryScope.js +3 -39
- package/dist/render/discoveryScope.js.map +1 -1
- package/dist/render/evidenceGrid.d.ts +3 -37
- package/dist/render/evidenceGrid.d.ts.map +1 -1
- package/dist/render/evidenceGrid.js +23 -107
- package/dist/render/evidenceGrid.js.map +1 -1
- package/dist/render/excludedSteps.d.ts +0 -26
- package/dist/render/excludedSteps.d.ts.map +1 -1
- package/dist/render/excludedSteps.js +4 -32
- package/dist/render/excludedSteps.js.map +1 -1
- package/dist/render/fitnessFunctions.d.ts +3 -0
- package/dist/render/fitnessFunctions.d.ts.map +1 -0
- package/dist/render/fitnessFunctions.js +12 -0
- package/dist/render/fitnessFunctions.js.map +1 -0
- package/dist/render/json.d.ts +0 -11
- package/dist/render/json.d.ts.map +1 -1
- package/dist/render/json.js +0 -11
- package/dist/render/json.js.map +1 -1
- package/dist/render/markdown.d.ts +1 -55
- package/dist/render/markdown.d.ts.map +1 -1
- package/dist/render/markdown.js +106 -487
- package/dist/render/markdown.js.map +1 -1
- package/dist/render/provenanceSection.d.ts +0 -22
- package/dist/render/provenanceSection.d.ts.map +1 -1
- package/dist/render/provenanceSection.js +10 -53
- package/dist/render/provenanceSection.js.map +1 -1
- package/dist/render/reconcilerSteps.d.ts +0 -25
- package/dist/render/reconcilerSteps.d.ts.map +1 -1
- package/dist/render/reconcilerSteps.js +5 -30
- package/dist/render/reconcilerSteps.js.map +1 -1
- package/dist/render/runVerdict.d.ts +0 -38
- package/dist/render/runVerdict.d.ts.map +1 -1
- package/dist/render/runVerdict.js +62 -129
- package/dist/render/runVerdict.js.map +1 -1
- package/dist/render/scenarioCodeErrorsSection.d.ts +3 -0
- package/dist/render/scenarioCodeErrorsSection.d.ts.map +1 -0
- package/dist/render/scenarioCodeErrorsSection.js +45 -0
- package/dist/render/scenarioCodeErrorsSection.js.map +1 -0
- package/dist/render/stepAccounting.d.ts +0 -36
- package/dist/render/stepAccounting.d.ts.map +1 -1
- package/dist/render/stepAccounting.js +5 -49
- package/dist/render/stepAccounting.js.map +1 -1
- package/dist/render/testVolume.d.ts +0 -25
- package/dist/render/testVolume.d.ts.map +1 -1
- package/dist/render/testVolume.js +4 -35
- package/dist/render/testVolume.js.map +1 -1
- package/package.json +4 -1
- package/dist/domain/checkCharacteristics.d.ts +0 -88
- package/dist/domain/checkCharacteristics.d.ts.map +0 -1
- package/dist/domain/checkCharacteristics.js +0 -109
- package/dist/domain/checkCharacteristics.js.map +0 -1
- package/dist/domain/fitnessChecks.d.ts +0 -79
- package/dist/domain/fitnessChecks.d.ts.map +0 -1
- package/dist/domain/fitnessChecks.js +0 -81
- package/dist/domain/fitnessChecks.js.map +0 -1
- package/dist/domain/qualityCheckFaultMessages.d.ts +0 -43
- package/dist/domain/qualityCheckFaultMessages.d.ts.map +0 -1
- package/dist/domain/qualityCheckFaultMessages.js +0 -38
- package/dist/domain/qualityCheckFaultMessages.js.map +0 -1
- package/dist/lib/featureCode.d.ts +0 -21
- package/dist/lib/featureCode.d.ts.map +0 -1
- package/dist/lib/featureCode.js +0 -24
- package/dist/lib/featureCode.js.map +0 -1
- package/dist/parse/corpusCodeFaults.d.ts +0 -76
- package/dist/parse/corpusCodeFaults.d.ts.map +0 -1
- package/dist/parse/corpusCodeFaults.js +0 -121
- package/dist/parse/corpusCodeFaults.js.map +0 -1
- package/dist/parse/corpusFaults.d.ts +0 -124
- package/dist/parse/corpusFaults.d.ts.map +0 -1
- package/dist/parse/corpusFaults.js +0 -72
- package/dist/parse/corpusFaults.js.map +0 -1
- package/dist/parse/corpusGrammarFaults.d.ts +0 -71
- package/dist/parse/corpusGrammarFaults.d.ts.map +0 -1
- package/dist/parse/corpusGrammarFaults.js +0 -292
- package/dist/parse/corpusGrammarFaults.js.map +0 -1
- package/dist/parse/runningStaticChecks.d.ts +0 -125
- package/dist/parse/runningStaticChecks.d.ts.map +0 -1
- package/dist/parse/runningStaticChecks.js +0 -316
- package/dist/parse/runningStaticChecks.js.map +0 -1
- package/dist/render/staticChecks.d.ts +0 -23
- package/dist/render/staticChecks.d.ts.map +0 -1
- package/dist/render/staticChecks.js +0 -69
- package/dist/render/staticChecks.js.map +0 -1
|
@@ -1,486 +1,134 @@
|
|
|
1
|
-
|
|
2
|
-
* THE QUALITY CHECK RECONCILIATION — one reconciling item in the accounting frame: the
|
|
3
|
-
* per-evidence-obligation line explaining why obligation and observation disagree. This module
|
|
4
|
-
* co-locates the concept's type with its OWN behaviour — the outcome vocabulary, the canon
|
|
5
|
-
* descriptions, and `reconcileEvidenceObligation`, the pure derivation that computes a single
|
|
6
|
-
* obligation's outcome from the observed evidence (module, not class; the rule lives WITH the
|
|
7
|
-
* concept, not in the reconcile orchestration that folds it).
|
|
8
|
-
*/
|
|
9
|
-
import { isStaticCheckObservation, evidenceObligationPairKey, } from "./journal.js";
|
|
1
|
+
import { isFitnessObservation, evidenceObligationPairKey, } from "./journal.js";
|
|
10
2
|
import { QualityCheckFaultCode } from "./qualityCheckFault.js";
|
|
11
3
|
import { QualityCheckReconciliationFault } from "./qualityCheckReconciliationFault.js";
|
|
12
|
-
|
|
13
|
-
// posts, blank on both sides (label: "Evidence Obligation and Evidence Observation are both
|
|
14
|
-
// missing — the Scenario is untagged and uncited"). Detected by the reconciler emitting NOTHING either way (NOT a
|
|
15
|
-
// hard evidenceObligations.length === 0 map — a zero-evidence obligation scenario cited by a stray
|
|
16
|
-
// credit is `no-evidence-obligation`, @SCN-LDG-010). Retires the zero-evidence-scenario warning (@SCN-WRN-001
|
|
17
|
-
// Update): the zero-evidence scenario is now this out-of-balance row, never a separate axis.
|
|
18
|
-
// The verdict stays two-valued (out-of-balance VIA this reconciling item, never a third verdict).
|
|
19
|
-
/**
|
|
20
|
-
* The canon description for each reconciling-item Outcome — the human-readable gloss a
|
|
21
|
-
* markdown reader needs (the bare handle means nothing in a CI log). SINGLE SOURCE: the
|
|
22
|
-
* markdown renderer renders this description in place of the handle EVERYWHERE an outcome
|
|
23
|
-
* surfaces to a reader — the Evidence-reconciliation summary's `↳` decomposition rows, the
|
|
24
|
-
* per-scenario reconciling-items list, and the lint-obligation table's Outcome column. The
|
|
25
|
-
* JSON keeps the bare handle (the machine axis; a programmatic consumer uses it).
|
|
26
|
-
*
|
|
27
|
-
* Keyed by EVERY `Outcome` (a `Record<Outcome, …>`), so a new outcome cannot be added to the
|
|
28
|
-
* union without a description here — the description can never silently diverge from the
|
|
29
|
-
* outcome it names (the drift class this project has been burned by: the census, the views).
|
|
30
|
-
* These phrasings ARE the original canon — later abbreviated to the handles; 3F-1626 restores
|
|
31
|
-
* them. `satisfied` (the balanced outcome, excluded from the out-of-balance decomposition)
|
|
32
|
-
* carries one for totality's sake.
|
|
33
|
-
*
|
|
34
|
-
* ⚠ KEY ORDER IS LOAD-BEARING (3F-1806). `OUT_OF_BALANCE_OUTCOMES` is DERIVED from these keys,
|
|
35
|
-
* and the renderer walks that list to emit the decomposition rows — so this map's key order IS
|
|
36
|
-
* the report's row order (`missing` first: the headline cause, the one carrying the bare-level
|
|
37
|
-
* split as Detail). Reordering these keys is a REPORT CHANGE, not a cosmetic tidy. The order
|
|
38
|
-
* follows canon, grouped by reconciling-item shape: a DEBIT block (missing, failing, watermelon,
|
|
39
|
-
* no-scn-code), a CREDIT block (cites-unknown-scenario, cites-no-scenario, no-evidence-obligation), then
|
|
40
|
-
* NEITHER (broken, both-missing). @SCN-RPT-019 now pins this SEQUENCE against the rendered `↳`
|
|
41
|
-
* rows (3F-1926), so a reorder reds — the exhaustiveness guard alone proved MEMBERSHIP, not
|
|
42
|
-
* order, and used not to catch one (which is how the order drifted before it was pinned).
|
|
43
|
-
*/
|
|
4
|
+
import { CORE_MESSAGES, CoreMessage } from "./messages.js";
|
|
44
5
|
export const OUTCOME_DESCRIPTION = {
|
|
45
|
-
satisfied:
|
|
46
|
-
missing:
|
|
47
|
-
failing:
|
|
48
|
-
watermelon:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
"cites-
|
|
53
|
-
"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
// credit-side fact rather than echoing the handle's root. 3F-2308 landed the sentence; 3F-2300
|
|
57
|
-
// then renamed the HANDLE to match it — the old one was the last survivor of a word canon has
|
|
58
|
-
// retired — moving the emitted `byOutcome` key and every @SCN-LDG scenario that reads it.
|
|
59
|
-
"no-evidence-obligation": "Evidence Observation cites a Scenario with no matching Evidence Obligation",
|
|
60
|
-
// GENERIC over its producers (@SCN-LDG-017 + @SCN-LDG-019, 3F-1654): `broken` has two instances
|
|
61
|
-
// — a vitest suite that failed to COLLECT, and a cucumber step two or more step definitions
|
|
62
|
-
// match, which therefore never ran. An ambiguous step is neither a "suite" nor a "collection
|
|
63
|
-
// error", so the gloss names what BOTH have in common: the proof exists and did not run.
|
|
64
|
-
// REMEDY-NEUTRAL by design: what broke is equally a missing dependency, a syntax error, a bad
|
|
65
|
-
// import, a top-level throw, or two colliding step definitions — the gloss names the FACT, not a
|
|
66
|
-
// guessed cure. Contrast `missing`, whose remedy is "write the test": conflating the two is
|
|
67
|
-
// exactly the false-`missing` this outcome retires.
|
|
68
|
-
broken: "Evidence Observation ATTEMPT errored before it could cite a Scenario (the proving suite failed to collect; behaviour never exercised)",
|
|
69
|
-
"both-missing": "Evidence Obligation and Evidence Observation are both missing — the Scenario is untagged and uncited",
|
|
6
|
+
satisfied: CORE_MESSAGES[CoreMessage.OutcomeSatisfied],
|
|
7
|
+
missing: CORE_MESSAGES[CoreMessage.OutcomeMissing],
|
|
8
|
+
failing: CORE_MESSAGES[CoreMessage.OutcomeFailing],
|
|
9
|
+
watermelon: CORE_MESSAGES[CoreMessage.OutcomeWatermelon],
|
|
10
|
+
"no-scn-code": CORE_MESSAGES[CoreMessage.OutcomeNoScnCode],
|
|
11
|
+
"duplicate-scn-code": CORE_MESSAGES[CoreMessage.OutcomeDuplicateScnCode],
|
|
12
|
+
"cites-unknown-scenario": CORE_MESSAGES[CoreMessage.OutcomeCitesUnknownScenario],
|
|
13
|
+
"cites-no-scenario": CORE_MESSAGES[CoreMessage.OutcomeCitesNoScenario],
|
|
14
|
+
"no-evidence-obligation": CORE_MESSAGES[CoreMessage.OutcomeNoEvidenceObligation],
|
|
15
|
+
broken: CORE_MESSAGES[CoreMessage.OutcomeBroken],
|
|
16
|
+
"both-missing": CORE_MESSAGES[CoreMessage.OutcomeBothMissing],
|
|
70
17
|
};
|
|
71
|
-
/**
|
|
72
|
-
* The out-of-balance reconciling-item outcomes, in report reading order — every `Outcome`
|
|
73
|
-
* except `satisfied` (the BALANCED outcome, not part of the OUT-of-balance decomposition).
|
|
74
|
-
* The renderer walks this to emit the summary's `↳` leaf rows (@SCN-RPT-019), each reconciling
|
|
75
|
-
* to the out-of-balance total exactly once (a FLAT list: the old debit/credit/neither grouping
|
|
76
|
-
* emitted a `↳ <group>-side` subtotal AND its leaves at the same indent, so a naive column-sum
|
|
77
|
-
* double-counted — the live 170 vs 85).
|
|
78
|
-
*
|
|
79
|
-
* DERIVED from the type-forced `OUTCOME_DESCRIPTION`, never hand-listed (3F-1806). It was a
|
|
80
|
-
* hand-maintained `readonly Outcome[]` in the renderer, which accepted a missing union member
|
|
81
|
-
* SILENTLY: `broken` (@SCN-LDG-017) joined the union, tallied into `byOutcome`, compiled clean,
|
|
82
|
-
* and would have been omitted from the table. Deriving the keys makes that omission IMPOSSIBLE
|
|
83
|
-
* rather than merely CAUGHT — a new `Outcome` cannot compile without a description, and the
|
|
84
|
-
* moment it has one it renders a leaf row with no change to the renderer.
|
|
85
|
-
*
|
|
86
|
-
* Lives in the model, not the renderer: which outcomes are out-of-balance is the model's rule,
|
|
87
|
-
* not a presentation choice (`entity-owns-its-behaviour`). The @SCN-RPT-019 guard remains as the
|
|
88
|
-
* end-to-end backstop — it renders real markdown and reads the rows back, deriving its
|
|
89
|
-
* expectation from its OWN type-forced record (`double-entry`, not a re-read of this source).
|
|
90
|
-
*/
|
|
91
18
|
export const OUT_OF_BALANCE_OUTCOMES = Object.keys(OUTCOME_DESCRIPTION).filter((outcome) => outcome !== "satisfied");
|
|
92
|
-
/**
|
|
93
|
-
* THE CANON OUTCOME-SIDE PROFILE (@SCN-LDG-030, 3F-2016) — the (outcome → side-presence) table
|
|
94
|
-
* transcribed from canon (`double-entry-manifesto.md` lines 35-44), keyed by EVERY `Outcome` (a
|
|
95
|
-
* `Record<Outcome, …>`), so a new outcome cannot join the union without STATING its side
|
|
96
|
-
* profile here — the type-forced totality that makes this map the SINGLE SOURCE the invariant is
|
|
97
|
-
* checked against (mirroring `OUTCOME_DESCRIPTION`).
|
|
98
|
-
*
|
|
99
|
-
* `obligation`/`observation` are `"Exists" | "None"` — whether a canon-correct reconciling item
|
|
100
|
-
* of that outcome CARRIES the debit (evidence obligation) and the credit (evidence observation)
|
|
101
|
-
* side respectively. NOT a naive function of side-presence: `missing` and `no-scn-code` share
|
|
102
|
-
* (Exists, None); the three credit outcomes (`cites-unknown-scenario` / `cites-no-scenario` /
|
|
103
|
-
* `no-evidence-obligation`) share (None, Exists). So the invariant `makeQualityCheckReconciliation` enforces
|
|
104
|
-
* is (outcome, side-presence) CONSISTENCY against this row, never a derivation of outcome FROM the
|
|
105
|
-
* sides.
|
|
106
|
-
*/
|
|
107
19
|
export const OUTCOME_SIDE_PROFILE = {
|
|
108
20
|
satisfied: { obligation: "Exists", observation: "Exists" },
|
|
109
21
|
missing: { obligation: "Exists", observation: "None" },
|
|
110
22
|
failing: { obligation: "Exists", observation: "Exists" },
|
|
111
23
|
watermelon: { obligation: "Exists", observation: "Exists" },
|
|
112
24
|
"no-scn-code": { obligation: "Exists", observation: "None" },
|
|
25
|
+
"duplicate-scn-code": { obligation: "Exists", observation: "None" },
|
|
113
26
|
"cites-unknown-scenario": { obligation: "None", observation: "Exists" },
|
|
114
27
|
"cites-no-scenario": { obligation: "None", observation: "Exists" },
|
|
115
28
|
"no-evidence-obligation": { obligation: "None", observation: "Exists" },
|
|
116
29
|
broken: { obligation: "None", observation: "None" },
|
|
117
30
|
"both-missing": { obligation: "None", observation: "None" },
|
|
118
31
|
};
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
* side-presence alone), looks up the canon row in `OUTCOME_SIDE_PROFILE`, and THROWS if the
|
|
124
|
-
* carried sides disagree with it: a divergent (outcome, side-presence) pair — the watermelon class
|
|
125
|
-
* this tool exists to catch, in its own output — is UNCONSTRUCTIBLE.
|
|
126
|
-
*
|
|
127
|
-
* Returns `fields` UNCHANGED (cast to the branded type): the producer's own key order is preserved
|
|
128
|
-
* verbatim, so every unchanged row stays byte-identical (no object is reconstructed).
|
|
129
|
-
*/
|
|
32
|
+
export function isObservationOnly(outcome) {
|
|
33
|
+
const profile = OUTCOME_SIDE_PROFILE[outcome];
|
|
34
|
+
return profile.obligation === "None" && profile.observation === "Exists";
|
|
35
|
+
}
|
|
130
36
|
export function makeQualityCheckReconciliation(fields) {
|
|
131
37
|
const profile = OUTCOME_SIDE_PROFILE[fields.outcome];
|
|
132
|
-
// The DEBIT-side check: the obligation is carried IFF canon says it Exists for this outcome.
|
|
133
38
|
if ((fields.evidenceObligation !== undefined) !== (profile.obligation === "Exists")) {
|
|
134
39
|
throw new QualityCheckReconciliationFault({
|
|
135
40
|
code: QualityCheckFaultCode.ObligationSideDivergesFromCanon,
|
|
136
41
|
outcome: fields.outcome,
|
|
137
|
-
|
|
138
|
-
// The ternary STAYS in code (@SCN-MSG-001): it is parameter logic, not prose, so mutation
|
|
139
|
-
// still reaches it — and now an assertion does too, which is what kills it.
|
|
42
|
+
canonical: profile.obligation,
|
|
140
43
|
actual: fields.evidenceObligation !== undefined ? "Exists" : "None",
|
|
141
44
|
});
|
|
142
45
|
}
|
|
143
|
-
// The CREDIT-side check: the observation is carried IFF canon says it Exists for this outcome.
|
|
144
46
|
if ((fields.evidenceObservation !== undefined) !== (profile.observation === "Exists")) {
|
|
145
47
|
throw new QualityCheckReconciliationFault({
|
|
146
48
|
code: QualityCheckFaultCode.ObservationSideDivergesFromCanon,
|
|
147
49
|
outcome: fields.outcome,
|
|
148
|
-
|
|
50
|
+
canonical: profile.observation,
|
|
149
51
|
actual: fields.evidenceObservation !== undefined ? "Exists" : "None",
|
|
150
52
|
});
|
|
151
53
|
}
|
|
152
54
|
return fields;
|
|
153
55
|
}
|
|
154
|
-
/**
|
|
155
|
-
* The where-to-fix a credit carries, for the credit-side items that now see BOTH axes.
|
|
156
|
-
*
|
|
157
|
-
* A static-check observation has no `locator` and never will: it is read from a target's CI
|
|
158
|
-
* config and manifest, not from a test file, so there is no file to send the reader to beyond the
|
|
159
|
-
* check's own handle — which the item already carries. Reading the field off the union rather
|
|
160
|
-
* than off `RuntimeEvidenceObservation` is what let those items widen to the static axis at all
|
|
161
|
-
* (@SCN-LNT-001, since a citing static credit can now post to a real or a phantom row).
|
|
162
|
-
*/
|
|
163
56
|
function locatorOf(observation) {
|
|
164
|
-
return observation.qualityCheck === "
|
|
57
|
+
return observation.qualityCheck === "fitness" ? undefined : observation.locator;
|
|
58
|
+
}
|
|
59
|
+
const OBSERVATION_PRECEDENCE = ["failing", "watermelon", "satisfied"];
|
|
60
|
+
function answerOf(matches, outcomeOf) {
|
|
61
|
+
for (const outcome of OBSERVATION_PRECEDENCE) {
|
|
62
|
+
const observation = matches.find((match) => outcomeOf(match) === outcome);
|
|
63
|
+
if (observation)
|
|
64
|
+
return { outcome, observation };
|
|
65
|
+
}
|
|
66
|
+
return { outcome: "missing" };
|
|
165
67
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
* citation, on both of its terms (@SCN-LNT-001).
|
|
169
|
-
*
|
|
170
|
-
* THE CREDIT CITES, and it is the same rule the runtime arm applies: an observation citing this
|
|
171
|
-
* scenario's `@SCN` answers its obligation. That is what lets a FITNESS obligation reconcile at
|
|
172
|
-
* all — its `staticCheck` is the BARE CHECK TAG the scenario carries (`traceability`), while the
|
|
173
|
-
* run knows only the runnable handle (`lint:feature-codes`), so nothing matches by name and
|
|
174
|
-
* nothing ever could. The citation is the seam that already crossed between them.
|
|
175
|
-
*
|
|
176
|
-
* AND THE CITATION IS NOT AN ADDRESS ON ITS OWN (@SCN-LNT-012). The `@SCN` says which ROW the
|
|
177
|
-
* credit belongs to; it does not say which of that row's obligations it ANSWERS. A stepless
|
|
178
|
-
* scenario may carry more than one bare check tag, so a row can raise two fitness obligations and
|
|
179
|
-
* be cited by two runs — and matching on the `@SCN` alone hands the first credit found to both,
|
|
180
|
-
* silently absorbing the second. Measured: a genuine `typecheck` watermelon read `satisfied` off
|
|
181
|
-
* `lint:complexity`'s credit, and the row read `balanced`. So the observation's own BARE CHECK TAG
|
|
182
|
-
* is compared too, and it is the observation's `checkTag` — NOT its `staticCheck`. `staticCheck` is
|
|
183
|
-
* the runnable handle, a different vocabulary from the obligation's bare tag, and comparing those
|
|
184
|
-
* two is name-match in a new spelling: it agrees only where the words coincide, which is
|
|
185
|
-
* `typecheck`, the one unprefixed check. `checkTag` is what the seam resolved, so the comparison is
|
|
186
|
-
* like with like.
|
|
187
|
-
*
|
|
188
|
-
* NAME-MATCH IS GONE FROM HERE TOO (3F-2625). A by-handle fallback stood second, taken only for a
|
|
189
|
-
* run that cited NOTHING. 3F-2590 retired the by-name route and removed `posting.ts`'s arm, but
|
|
190
|
-
* never touched this file, so this half outlived the retirement it was written to announce — and
|
|
191
|
-
* it was LIVE, not dead code. It compared an uncited observation's RUNNABLE handle against the
|
|
192
|
-
* obligation's BARE tag, which differ for every prefixed check (`lint:feature-codes` vs
|
|
193
|
-
* `traceability`) and are the SAME WORD for a prefix-less one (`typecheck`). On that check an
|
|
194
|
-
* uncited credit both posted to suspense AND answered this obligation: the row read `satisfied` on
|
|
195
|
-
* evidence that cited nothing, and one observation became the credit side of two items on two rows
|
|
196
|
-
* — the exactly-one-posting claim broken by the reconciler rather than by the posting rule.
|
|
197
|
-
*
|
|
198
|
-
* SO A RUN THAT CITES NOTHING NOW ANSWERS NOTHING. It posts to SUSPENSE (`postingOf`), where a
|
|
199
|
-
* check nobody named is meant to surface, and the obligation naming that check reads `missing` —
|
|
200
|
-
* an honest pair, both halves loud. Pinned by plant, not by the absence of a symbol:
|
|
201
|
-
* `uncitedFitnessCredit.test.ts`, @SCN-USG-001.
|
|
202
|
-
*
|
|
203
|
-
* ONE WORD IS PASSED, NOT TWO, and it is the BARE CHECK TAG. Every surviving `lint` obligation
|
|
204
|
-
* names one: the fitness rule raises it from the tag its stepless scenario carries, and the retired
|
|
205
|
-
* `@lint=<handle>` form raises nothing at all (@SCN-LNT-007). The obligation's word is now matched
|
|
206
|
-
* ONLY against the observation's own `checkTag` — never against its `staticCheck`, which is the
|
|
207
|
-
* runnable handle and a different vocabulary. That comparison was the deleted arm's, and it is what
|
|
208
|
-
* made the arm name-match.
|
|
209
|
-
*
|
|
210
|
-
* Lives beside `reconcileEvidenceObligation` rather than inside it: which observation answers an
|
|
211
|
-
* obligation is one idea with a name, and the outcome mapping that follows it is another.
|
|
212
|
-
*/
|
|
213
|
-
function staticCheckObservationFor(scnId, checkTag, evidenceObservations) {
|
|
214
|
-
return evidenceObservations.find((o) => isStaticCheckObservation(o) &&
|
|
215
|
-
// THE FAIL-SAFE, THE SAME ONE THE RUNTIME MATCH BELOW KEEPS (@SCN-LNT-026). A proof that
|
|
216
|
-
// never ran can neither discharge an obligation nor fail it, so a broken credit is excluded
|
|
217
|
-
// from the match entirely and its obligation reconciles `missing` by absence — while the
|
|
218
|
-
// credit itself posts to suspense, where the reader is told what broke. The exclusion is
|
|
219
|
-
// STRUCTURAL rather than incidental: this observation carries its `scnId` and its
|
|
220
|
-
// `checkTag`, exactly as cucumber's broken observation carries the `@SCN` its scenario
|
|
221
|
-
// cited, so nothing but this clause keeps it out.
|
|
68
|
+
function fitnessObservationsCiting(scnId, evidenceObservations) {
|
|
69
|
+
return evidenceObservations.filter((o) => isFitnessObservation(o) &&
|
|
222
70
|
o.broken !== true &&
|
|
223
|
-
o.scnId === scnId
|
|
224
|
-
o.checkTag === checkTag);
|
|
71
|
+
o.scnId === scnId);
|
|
225
72
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
*
|
|
236
|
-
* NAMED FOR WHAT IT HOLDS. Every surviving fitness obligation carries the BARE CHECK TAG its
|
|
237
|
-
* stepless scenario spells (`complexity`), never the runnable handle that invokes it
|
|
238
|
-
* (`lint:complexity`) — the retired `@lint=<handle>` form raises nothing (@SCN-LNT-007). The old
|
|
239
|
-
* name `guard` read as the handle and was the tag, which is exactly the confusion that let an
|
|
240
|
-
* observation's handle be compared against it.
|
|
241
|
-
*/
|
|
242
|
-
function reconcileStaticObligation(scnId, req, evidenceObservations) {
|
|
243
|
-
const checkTag = req.staticCheck; // present by construction for a fitness evidence obligation.
|
|
244
|
-
const obs = staticCheckObservationFor(scnId, checkTag, evidenceObservations);
|
|
245
|
-
let outcome;
|
|
246
|
-
// THE CHECK'S OWN VERDICT FIRST, WHERE THE OBSERVATION CARRIED ONE (@SCN-LNT-028), and the order
|
|
247
|
-
// is the rule rather than a convenience. `exists` and `enforcedOverRealSource` say a check is
|
|
248
|
-
// there and is wired, which is exactly what a check that ran and found violations also is — so
|
|
249
|
-
// asked in the other order the wiring facts would answer `satisfied` over a red and the verdict
|
|
250
|
-
// would never be reached. It is the same arrangement `broken` takes one function up, for the
|
|
251
|
-
// same reason: the fact that reds is the one asked about first.
|
|
252
|
-
if (obs?.status === "failed") {
|
|
253
|
-
outcome = "failing";
|
|
254
|
-
}
|
|
255
|
-
else if (obs && obs.enforcedOverRealSource) {
|
|
256
|
-
outcome = "satisfied";
|
|
257
|
-
}
|
|
258
|
-
else if (obs?.exists) {
|
|
259
|
-
outcome = "watermelon";
|
|
260
|
-
}
|
|
261
|
-
else {
|
|
262
|
-
outcome = "missing";
|
|
263
|
-
}
|
|
264
|
-
// The CREDIT side (@SCN-LDG-027/@SCN-LDG-028): the matched static-check observation, carried
|
|
265
|
-
// ONLY when it is a REAL observation — `exists:true` (the two-sided `satisfied` / `watermelon`).
|
|
266
|
-
// A guard absent from both package.json and CI is FOUND as the negative fact
|
|
267
|
-
// `{exists:false}` — an ABSENCE, not an observed piece of evidence — so `missing` carries
|
|
268
|
-
// NOTHING on the credit side (Obligation Exists, Observation None; double-entry-manifesto line
|
|
269
|
-
// 36), uniform with the runtime `missing` path. `obs?.exists` (not `obs`) is the discriminator:
|
|
270
|
-
// `find` locating the absent-guard record must not be mistaken for an observation existing.
|
|
73
|
+
function fitnessOutcomeOf(observation) {
|
|
74
|
+
if (observation.status === "failed")
|
|
75
|
+
return "failing";
|
|
76
|
+
if (observation.enforcedOverRealSource)
|
|
77
|
+
return "satisfied";
|
|
78
|
+
return observation.exists ? "watermelon" : undefined;
|
|
79
|
+
}
|
|
80
|
+
function reconcileFitnessObligation(scnId, obligation, evidenceObservations) {
|
|
81
|
+
const { outcome, observation } = answerOf(fitnessObservationsCiting(scnId, evidenceObservations), fitnessOutcomeOf);
|
|
271
82
|
return makeQualityCheckReconciliation({
|
|
272
|
-
qualityCheck: "
|
|
273
|
-
staticCheck: checkTag,
|
|
83
|
+
qualityCheck: "fitness",
|
|
274
84
|
outcome,
|
|
275
|
-
evidenceObligation:
|
|
276
|
-
...(
|
|
85
|
+
evidenceObligation: obligation,
|
|
86
|
+
...(observation ? { evidenceObservation: observation } : {}),
|
|
277
87
|
});
|
|
278
88
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
export function reconcileEvidenceObligation(scnId, req, evidenceObservations) {
|
|
286
|
-
if (req.qualityCheck === "lint") {
|
|
287
|
-
return reconcileStaticObligation(scnId, req, evidenceObservations);
|
|
288
|
-
}
|
|
289
|
-
// A runtime evidence obligation — matched by (scnId, quality-check) among the runtime
|
|
290
|
-
// evidence observations (a static check evidence observation carries qualityCheck "lint" and no scnId, so it
|
|
291
|
-
// never matches here). present+pass → satisfied; present+fail → failing;
|
|
292
|
-
// absent → missing (a required ledger entry never recorded).
|
|
293
|
-
//
|
|
294
|
-
// THE FAIL-SAFE (@SCN-LDG-017, operator-mandated): `broken` is checked BEFORE `status`. A
|
|
295
|
-
// broken observation — one whose PROOF NEVER RAN — is excluded from the match entirely, so it
|
|
296
|
-
// can NEVER reconcile as `satisfied` or `failing`: a proof that never ran can neither discharge
|
|
297
|
-
// an obligation nor fail it. The obligation then finds no match and reconciles `missing` —
|
|
298
|
-
// honest at its own resolution (no evidence citing it arrived); the broken evidence itself
|
|
299
|
-
// posts to the SUSPENSE row below, where the reader is sent to fix the harness. The exclusion
|
|
300
|
-
// is structural rather than incidental: `broken` rides an OPTIONAL
|
|
301
|
-
// FLAG on RuntimeEvidenceObservation, so a broken credit CAN carry an scnId and a status — the
|
|
302
|
-
// guard is what makes the fail-safe hold whatever it carries.
|
|
303
|
-
const match = evidenceObservations.find((o) =>
|
|
304
|
-
// TWO SURVIVING MUTANTS LIVE ON THIS CLAUSE, KEPT DELIBERATELY (3F-2237). Stryker has no
|
|
305
|
-
// equivalence detection, so a mutant no test can kill is indistinguishable from one nobody
|
|
306
|
-
// has written yet. The argument is recorded here because a reader meeting this line has no
|
|
307
|
-
// other way to recover that the clause is redundant ON PURPOSE.
|
|
308
|
-
//
|
|
309
|
-
// `ConditionalExpression` (this clause → `true`) is redundant FOR ANY INPUT WHATSOEVER.
|
|
310
|
-
// The arm is reached only when `req.qualityCheck !== "lint"` (the fitness fork above
|
|
311
|
-
// returns first), and `o.qualityCheck === req.qualityCheck` two lines down entails
|
|
312
|
-
// `o.qualityCheck !== "lint"` for every `o` that survives the chain. The redundancy is
|
|
313
|
-
// LOGICAL, not type-based — no input, however constructed, separates mutant from original.
|
|
314
|
-
//
|
|
315
|
-
// `StringLiteral` (`"lint"` → `""`) rests on a NARROWER argument, and the difference is the
|
|
316
|
-
// point. Mutated, the clause reads `o.qualityCheck !== ""`. On the `"lint"` side the logical
|
|
317
|
-
// argument above still covers it — a `"lint"` observation can never satisfy the third clause
|
|
318
|
-
// below the fitness fork. The `""` side IS separable: an evidence obligation and a credit
|
|
319
|
-
// both carrying `""` reconcile `satisfied` here and `missing` under the mutant. But that
|
|
320
|
-
// input is out of domain — no member of `QualityCheck` spells `""`, and nothing in this
|
|
321
|
-
// package emits it (every producer is a literal, a closed alternation, or a lookup hit), so
|
|
322
|
-
// it is reachable only by casting PAST the parameter type. 3F-2879 drew exactly that line: a
|
|
323
|
-
// shape admitted by width subtyping WITH NO CAST is a real published-surface input and kills
|
|
324
|
-
// (it killed `locatorOf`, and `fitnessCheckTagOf` with it); an out-of-domain value for a
|
|
325
|
-
// typed field is not one, and pinning behaviour for it would assert a case no reader of this
|
|
326
|
-
// model can reach.
|
|
327
|
-
//
|
|
328
|
-
// Kept because it is load-bearing for the compiler — delete it and `pnpm typecheck` reds
|
|
329
|
-
// with `TS2339: Property 'broken' does not exist on type 'EvidenceObservation'`, the `"lint"`
|
|
330
|
-
// observations never having been narrowed away — and because it states the fail-safe above
|
|
331
|
-
// at the point the reader meets it.
|
|
332
|
-
o.qualityCheck !== "lint" &&
|
|
333
|
-
o.broken !== true &&
|
|
334
|
-
o.qualityCheck === req.qualityCheck &&
|
|
335
|
-
o.scnId === scnId);
|
|
336
|
-
let outcome;
|
|
337
|
-
if (!match) {
|
|
338
|
-
outcome = "missing";
|
|
339
|
-
}
|
|
340
|
-
else if (match.status === "passed") {
|
|
341
|
-
outcome = "satisfied";
|
|
342
|
-
}
|
|
343
|
-
else {
|
|
344
|
-
outcome = "failing";
|
|
89
|
+
function runtimeOutcomeOf(observation) {
|
|
90
|
+
return observation.status === "passed" ? "satisfied" : "failing";
|
|
91
|
+
}
|
|
92
|
+
export function reconcileEvidenceObligation(scnId, obligation, evidenceObservations) {
|
|
93
|
+
if (obligation.qualityCheck === "fitness") {
|
|
94
|
+
return reconcileFitnessObligation(scnId, obligation, evidenceObservations);
|
|
345
95
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
96
|
+
const { outcome, observation } = answerOf(evidenceObservations.filter((o) => o.broken !== true &&
|
|
97
|
+
o.qualityCheck === obligation.qualityCheck &&
|
|
98
|
+
o.scnId === scnId), runtimeOutcomeOf);
|
|
349
99
|
return makeQualityCheckReconciliation({
|
|
350
|
-
qualityCheck:
|
|
100
|
+
qualityCheck: obligation.qualityCheck,
|
|
351
101
|
outcome,
|
|
352
|
-
evidenceObligation:
|
|
353
|
-
...(
|
|
102
|
+
evidenceObligation: obligation,
|
|
103
|
+
...(observation ? { evidenceObservation: observation } : {}),
|
|
354
104
|
});
|
|
355
105
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
// answers it for a DEBIT matched against the observed evidence; these answer it for the entries
|
|
361
|
-
// that match no obligation at all (the credit-side outcomes) and for the two degenerate rows
|
|
362
|
-
// (no-scn-code, both-missing).
|
|
363
|
-
//
|
|
364
|
-
// They were inline `.map()` bodies in the `reconcile` fold. Posting-destination left in 3F-2202;
|
|
365
|
-
// this is the sibling class — WHAT an entry reconciles to, as opposed to WHERE it lands. Both are
|
|
366
|
-
// domain rules, so neither belongs in the orchestration. `reconcile` keeps the walk and builds the
|
|
367
|
-
// rows; the outcomes are asked for.
|
|
368
|
-
// ---------------------------------------------------------------------------
|
|
369
|
-
/**
|
|
370
|
-
* What a FITNESS credit calls the check that ran — its bare tag where it has one, else the runnable
|
|
371
|
-
* handle that invoked it. `undefined` for a runtime credit, which names a test file instead.
|
|
372
|
-
*
|
|
373
|
-
* The bare tag is preferred because a reconciling item's `staticCheck` holds the bare tag on the
|
|
374
|
-
* debit side, so a reader comparing "the row names complexity" against "a credit for typecheck
|
|
375
|
-
* arrived" is comparing like with like. The handle is a fallback rather than a second choice of
|
|
376
|
-
* vocabulary: a credit with no tag cites nothing and posts to suspense, so it should not reach a
|
|
377
|
-
* real row at all — but if one ever does, naming its handle beats naming nothing.
|
|
378
|
-
*/
|
|
379
|
-
function fitnessCheckNameOf(observation) {
|
|
380
|
-
return isStaticCheckObservation(observation)
|
|
381
|
-
? (observation.checkTag ?? observation.staticCheck)
|
|
382
|
-
: fitnessCheckTagOf(observation);
|
|
383
|
-
}
|
|
384
|
-
/**
|
|
385
|
-
* A fitness credit's BARE CHECK TAG, or `undefined` — the term the pair key compares.
|
|
386
|
-
*
|
|
387
|
-
* Deliberately NO handle fallback, unlike `fitnessCheckNameOf` above. The key is matched against an
|
|
388
|
-
* obligation's `staticCheck`, which since the bare-tag unification always holds the BARE tag; a
|
|
389
|
-
* handle substituted here would compare `lint:typecheck` against `typecheck` and miss — name-match
|
|
390
|
-
* in a new spelling. A credit with no tag keys distinctly from every tagged one, which is what
|
|
391
|
-
* stops it matching by having nothing to compare.
|
|
392
|
-
*/
|
|
393
|
-
function fitnessCheckTagOf(observation) {
|
|
394
|
-
return isStaticCheckObservation(observation) ? observation.checkTag : undefined;
|
|
106
|
+
function fitnessFunctionNameOf(observation) {
|
|
107
|
+
return isFitnessObservation(observation)
|
|
108
|
+
? observation.fitnessFunction
|
|
109
|
+
: undefined;
|
|
395
110
|
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
* evidence obligation, so the item is CREDIT-ONLY (no obligation is in scope).
|
|
400
|
-
*
|
|
401
|
-
* The pair-check is what distinguishes no-evidence-obligation from a matched credit: a credit at a NAMED
|
|
402
|
-
* `(scnId, quality-check)` pair reconciles satisfied/failing via `reconcileEvidenceObligation` and
|
|
403
|
-
* is NOT re-listed here. `evidenceObligationPairs` is the journal's rule (`evidenceObligationPairsOf`),
|
|
404
|
-
* built once and shared — which is what makes a credit citing a PENDING pair land here rather than
|
|
405
|
-
* nowhere (3F-1858: a pending obligation names nothing, so such a credit is no-evidence-obligation exactly
|
|
406
|
-
* as when nothing was raised at all, and the run that used to read balanced over it now fails).
|
|
407
|
-
*/
|
|
408
|
-
export function noEvidenceObligationReconciliationsOf(scnId, postedCredits, evidenceObligationPairs) {
|
|
409
|
-
return postedCredits
|
|
410
|
-
.filter((o) =>
|
|
411
|
-
// THE CREDIT'S OWN CHECK TAG, not the axis (@SCN-LNT-013). Keyed on `lint` alone, a row's
|
|
412
|
-
// obligation for one check answered a credit for any other — so a credit the debit match had
|
|
413
|
-
// already, correctly, refused by tag was ALSO excluded here and reconciled to nothing.
|
|
414
|
-
// Composed by the model's own `evidenceObligationPairKey`, the same spelling the set is
|
|
415
|
-
// built with: a key built one way and read another is the same bug in a new spelling.
|
|
416
|
-
!evidenceObligationPairs.has(evidenceObligationPairKey(scnId, o.qualityCheck, fitnessCheckTagOf(o))))
|
|
111
|
+
export function noEvidenceObligationReconciliationsOf(scnId, postedObservations, evidenceObligationPairs) {
|
|
112
|
+
return postedObservations
|
|
113
|
+
.filter((o) => !evidenceObligationPairs.has(evidenceObligationPairKey(scnId, o.qualityCheck)))
|
|
417
114
|
.map((o) => makeQualityCheckReconciliation({
|
|
418
115
|
qualityCheck: o.qualityCheck,
|
|
419
116
|
outcome: "no-evidence-obligation",
|
|
420
|
-
|
|
421
|
-
// test file to be sent to. Without it the item reads "lint: no evidence obligation" and a
|
|
422
|
-
// reader learns only that SOME check arrived uninvited, on a row that may name several.
|
|
423
|
-
// Preferring the BARE TAG over the runnable handle is what lets it be read against the row's
|
|
424
|
-
// own obligations, which are named by the bare tag — like compared with like. The handle is
|
|
425
|
-
// the fallback for a credit that carries no tag, so the item still names something.
|
|
426
|
-
...(fitnessCheckNameOf(o) !== undefined ? { staticCheck: fitnessCheckNameOf(o) } : {}),
|
|
427
|
-
// The CREDIT side (@SCN-LDG-027): the citing observation this item reconciles.
|
|
117
|
+
...(fitnessFunctionNameOf(o) !== undefined ? { fitnessFunction: fitnessFunctionNameOf(o) } : {}),
|
|
428
118
|
evidenceObservation: o,
|
|
429
119
|
...(locatorOf(o) !== undefined ? { locator: locatorOf(o) } : {}),
|
|
430
120
|
}));
|
|
431
121
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
* `@SCN` no scenario carries, in citation order. CREDIT-ONLY: a phantom row is minted BY a credit,
|
|
435
|
-
* so nothing can ever have been named of it, and no obligation is in scope.
|
|
436
|
-
*
|
|
437
|
-
* THE WHERE-TO-FIX IS THE WHOLE OF WHAT THIS ITEM CAN OFFER, which is why both halves ride through
|
|
438
|
-
* (ruling Q5): a phantom item has no obligation to point a reader at instead. A runtime credit
|
|
439
|
-
* carries its locator (the test file that cited); a FITNESS credit carries the CHECK THAT RAN —
|
|
440
|
-
* it is read from a target's config, not from a test file, so it has no locator and the handle is
|
|
441
|
-
* all there is. That half became reachable when the fitness axis started citing (@SCN-LNT-009);
|
|
442
|
-
* the suspense arm below already carried it, and the two unmatched-credit destinations must not
|
|
443
|
-
* tell a reader different amounts about the same class of fault.
|
|
444
|
-
*/
|
|
445
|
-
export function citesUnknownScenarioReconciliationsOf(credits) {
|
|
446
|
-
return credits.map((o) => makeQualityCheckReconciliation({
|
|
122
|
+
export function citesUnknownScenarioReconciliationsOf(observations) {
|
|
123
|
+
return observations.map((o) => makeQualityCheckReconciliation({
|
|
447
124
|
qualityCheck: o.qualityCheck,
|
|
448
|
-
|
|
449
|
-
// sent to. For a static run that is its runnable handle.
|
|
450
|
-
...(isStaticCheckObservation(o) ? { staticCheck: o.staticCheck } : {}),
|
|
125
|
+
...(isFitnessObservation(o) ? { fitnessFunction: o.fitnessFunction } : {}),
|
|
451
126
|
outcome: "cites-unknown-scenario",
|
|
452
127
|
evidenceObservation: o,
|
|
453
128
|
...(locatorOf(o) !== undefined ? { locator: locatorOf(o) } : {}),
|
|
454
129
|
}));
|
|
455
130
|
}
|
|
456
|
-
|
|
457
|
-
* The items on the SUSPENSE row (@SCN-LDG-007) — every entry that names no scenario at all,
|
|
458
|
-
* in the ONE order canon fixes.
|
|
459
|
-
*
|
|
460
|
-
* THE ORDER IS THE RULE, not an accident of assembly. `broken` comes FIRST because a broken run's
|
|
461
|
-
* other suspense items are downstream noise: the reader must be sent to the harness before being
|
|
462
|
-
* asked to read anything the harness failed to produce.
|
|
463
|
-
*
|
|
464
|
-
* THE SPLIT (@SCN-LDG-017) is why `broken` and `cites-no-scenario` are separate arms rather than
|
|
465
|
-
* one map over "everything uncited". Both land here because they name no scenario, but they are
|
|
466
|
-
* distinguished by errored-vs-completed and their remedies differ: a `cites-no-scenario` test RAN
|
|
467
|
-
* and forgot its citation (add the `@SCN`); a `broken` suite never ran at all (fix the harness).
|
|
468
|
-
* Folding the second into the first would hide a collection failure behind the wrong cure.
|
|
469
|
-
*
|
|
470
|
-
* The three arms carry different payloads, each for a stated reason:
|
|
471
|
-
* - `broken` carries its WHERE-to-fix (locator) and its WHAT-broke (the runner's message), and
|
|
472
|
-
* NO `evidenceObservation` — a proof that never ran is not an observed piece of evidence.
|
|
473
|
-
* - an uncited runtime credit carries the observation it reconciles, plus its locator.
|
|
474
|
-
* - a no-evidence-obligation static check carries its handle and its observation; it has no locator (a
|
|
475
|
-
* static check is read from config, not from a test file).
|
|
476
|
-
*
|
|
477
|
-
* THE BROKEN STREAM TAKES EITHER AXIS SINCE @SCN-LNT-026, AND THE ARM NEEDED NO BRANCH FOR IT.
|
|
478
|
-
* A converted static check that did not run carries the same two payload halves under the same
|
|
479
|
-
* two names, so the map reads what the observation holds exactly as it does for a vitest suite
|
|
480
|
-
* that failed to collect. What differs is only the `qualityCheck` the item then carries — `lint`
|
|
481
|
-
* rather than a runtime tier — which is read off the observation and not decided here.
|
|
482
|
-
*/
|
|
483
|
-
export function suspenseReconciliationsOf(brokenEvidence, uncited, noEvidenceObligationGuards) {
|
|
131
|
+
export function suspenseReconciliationsOf(brokenEvidence, uncited, uncitedFitnessFunctions) {
|
|
484
132
|
return [
|
|
485
133
|
...brokenEvidence.map((o) => makeQualityCheckReconciliation({
|
|
486
134
|
qualityCheck: o.qualityCheck,
|
|
@@ -494,73 +142,30 @@ export function suspenseReconciliationsOf(brokenEvidence, uncited, noEvidenceObl
|
|
|
494
142
|
evidenceObservation: o,
|
|
495
143
|
...(o.locator !== undefined ? { locator: o.locator } : {}),
|
|
496
144
|
})),
|
|
497
|
-
...
|
|
498
|
-
qualityCheck: "
|
|
499
|
-
|
|
500
|
-
staticCheck: o.staticCheck,
|
|
145
|
+
...uncitedFitnessFunctions.map((o) => makeQualityCheckReconciliation({
|
|
146
|
+
qualityCheck: "fitness",
|
|
147
|
+
fitnessFunction: o.fitnessFunction,
|
|
501
148
|
outcome: "cites-no-scenario",
|
|
502
149
|
evidenceObservation: o,
|
|
503
150
|
})),
|
|
504
151
|
];
|
|
505
152
|
}
|
|
506
|
-
/**
|
|
507
|
-
* The item on an UNTAGGED scenario's row (@SCN-LDG-021) — the DEBIT twin of `cites-no-scenario`
|
|
508
|
-
* (same missing link, opposite side; suspense stays credit-only).
|
|
509
|
-
*
|
|
510
|
-
* A scenario carrying no `@SCN` is REAL and ORDINARY — cucumber runs it — but its obligation has no
|
|
511
|
-
* CODE for an observation to link to, so it could reconcile NOTHING: not satisfied, not failing,
|
|
512
|
-
* not even `missing`, because an obligation never RAISED cannot be ABSENT. Without this item it
|
|
513
|
-
* contributed nothing to the verdict and the run went green over work nothing proves.
|
|
514
|
-
*
|
|
515
|
-
* It carries NO `qualityCheck`: with no code, no observation can link at ANY tier, so the missing
|
|
516
|
-
* link is SCENARIO-scope, not tier-scope (the shape `both-missing` also uses). The synthesised
|
|
517
|
-
* scenario-scope obligation — an empty `{}` — lives ONLY on this item; the row's own
|
|
518
|
-
* `evidenceObligations` stays empty (canon citation: double-entry-manifesto line 39, Obligation
|
|
519
|
-
* Exists / Observation None).
|
|
520
|
-
*/
|
|
521
153
|
export function noScnCodeReconciliations() {
|
|
522
154
|
return [makeQualityCheckReconciliation({ outcome: "no-scn-code", evidenceObligation: {} })];
|
|
523
155
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
156
|
+
export function duplicateScnCodeReconciliationsOf(obligations) {
|
|
157
|
+
const firstNaming = new Map();
|
|
158
|
+
for (const obligation of obligations) {
|
|
159
|
+
if (obligation.qualityCheck !== undefined && !firstNaming.has(obligation.qualityCheck)) {
|
|
160
|
+
firstNaming.set(obligation.qualityCheck, obligation);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (firstNaming.size === 0) {
|
|
164
|
+
return [makeQualityCheckReconciliation({ outcome: "duplicate-scn-code", evidenceObligation: {} })];
|
|
165
|
+
}
|
|
166
|
+
return [...firstNaming].map(([qualityCheck, evidenceObligation]) => makeQualityCheckReconciliation({ qualityCheck, outcome: "duplicate-scn-code", evidenceObligation }));
|
|
167
|
+
}
|
|
533
168
|
export function bothMissingReconciliation() {
|
|
534
169
|
return makeQualityCheckReconciliation({ outcome: "both-missing" });
|
|
535
170
|
}
|
|
536
|
-
/**
|
|
537
|
-
* The item on the row a CONFIGURED ROW WITH NO SCENARIO earns (@SCN-LNT-027, 3F-3472) — canon's
|
|
538
|
-
* `missing`: Obligation Exists, Observation None.
|
|
539
|
-
*
|
|
540
|
-
* NO NEW OUTCOME, AND THAT IS THE ARGUMENT RATHER THAN THE CONVENIENCE. A row the manifest declares
|
|
541
|
-
* and no scenario specifies is a debit nothing observed, which is what `missing` already means and
|
|
542
|
-
* what `features/fitness-checks.feature` promises in those words. Minting a fourth corpus-shape
|
|
543
|
-
* outcome would put a second sentence beside a row canon already has a sentence for.
|
|
544
|
-
*
|
|
545
|
-
* THE OBLIGATION IS THE ROW'S OWN, and it is REAL — `{ qualityCheck: "lint", staticCheck: <the
|
|
546
|
-
* characteristic> }`, the same shape a converted scenario raises. That is what separates this from
|
|
547
|
-
* the uncoded-work item beside it: THAT one synthesises an empty `{}` because an untagged scenario
|
|
548
|
-
* names no check at all, and this row names exactly one. So the row posts it as a debit too, and
|
|
549
|
-
* the summary counts the obligation the manifest raised.
|
|
550
|
-
*
|
|
551
|
-
* NOTHING HERE CAN DISCHARGE IT, and that is structural rather than asserted: no observation is
|
|
552
|
-
* offered to this item at all, and the minted row's `@SCN` is not a member of the set the citation
|
|
553
|
-
* router posts credits against. A credit citing the row's code still mints a phantom row
|
|
554
|
-
* (@SCN-LNT-009); there is no route by which it reaches this one.
|
|
555
|
-
*/
|
|
556
|
-
export function unspecifiedRowReconciliations(characteristic) {
|
|
557
|
-
return [
|
|
558
|
-
makeQualityCheckReconciliation({
|
|
559
|
-
qualityCheck: "lint",
|
|
560
|
-
staticCheck: characteristic,
|
|
561
|
-
outcome: "missing",
|
|
562
|
-
evidenceObligation: { qualityCheck: "lint", staticCheck: characteristic },
|
|
563
|
-
}),
|
|
564
|
-
];
|
|
565
|
-
}
|
|
566
171
|
//# sourceMappingURL=qualityCheckReconciliation.js.map
|