@3f-consulting/spec-controller-core 0.1.0-alpha.7 → 0.1.0-alpha.70
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 +5 -40
- package/dist/assemble/evidenceReconciliationFromSources.d.ts.map +1 -1
- package/dist/assemble/evidenceReconciliationFromSources.js +30 -325
- package/dist/assemble/evidenceReconciliationFromSources.js.map +1 -1
- package/dist/assemble/renderInput.d.ts +7 -73
- 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 +11 -46
- package/dist/census/evidenceObligations.d.ts.map +1 -1
- package/dist/census/evidenceObligations.js +19 -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 +21 -391
- package/dist/domain/evidenceReconciliation.d.ts.map +1 -1
- package/dist/domain/evidenceReconciliation.js +87 -382
- 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 +8 -393
- package/dist/domain/journal.d.ts.map +1 -1
- package/dist/domain/journal.js +6 -118
- package/dist/domain/journal.js.map +1 -1
- package/dist/domain/ledger.d.ts +8 -328
- package/dist/domain/ledger.d.ts.map +1 -1
- package/dist/domain/ledger.js +23 -326
- package/dist/domain/ledger.js.map +1 -1
- package/dist/domain/messages.d.ts +213 -0
- package/dist/domain/messages.d.ts.map +1 -0
- package/dist/domain/messages.js +313 -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 +8 -105
- 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 +3 -43
- 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 +7 -280
- package/dist/domain/qualityCheckReconciliation.d.ts.map +1 -1
- package/dist/domain/qualityCheckReconciliation.js +63 -463
- 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 +2 -207
- package/dist/domain/scenarioReconciliation.d.ts.map +1 -1
- package/dist/domain/scenarioReconciliation.js +4 -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 +5 -168
- 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 +18 -225
- 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 +0 -53
- 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 +2 -57
- 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 +3 -228
- package/dist/parse/scenarios.d.ts.map +1 -1
- package/dist/parse/scenarios.js +20 -457
- 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 +1 -78
- 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 +0 -60
- 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 +2 -110
- package/dist/provenance/provenance.d.ts.map +1 -1
- package/dist/provenance/provenance.js +2 -58
- 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 +6 -69
- 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 +1 -35
- package/dist/render/evidenceGrid.d.ts.map +1 -1
- package/dist/render/evidenceGrid.js +21 -103
- 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 +128 -467
- 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,146 +1,43 @@
|
|
|
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
|
-
// evidence obligation AND no citing evidence observation) — the ONE degenerate Quality Check Reconciliation it
|
|
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
|
-
*/
|
|
44
4
|
export const OUTCOME_DESCRIPTION = {
|
|
45
5
|
satisfied: "Evidence Observation satisfies its Evidence Obligation",
|
|
46
6
|
missing: "Evidence Observation is missing",
|
|
47
7
|
failing: "Evidence Observation is failing",
|
|
48
8
|
watermelon: "Evidence Observation is passing but fabricated",
|
|
49
|
-
// The DEBIT TWIN of `cites-no-scenario` (@SCN-LDG-021): same missing LINK, opposite side — the
|
|
50
|
-
// scenario is real and ORDINARY, but carries no @SCN for an observation to link to.
|
|
51
9
|
"no-scn-code": "Evidence Obligation has no @SCN code for an Evidence Observation to link to",
|
|
10
|
+
"duplicate-scn-code": "Evidence Obligation's @SCN code is carried by another Scenario",
|
|
52
11
|
"cites-unknown-scenario": "Evidence Observation cites an unknown Scenario",
|
|
53
12
|
"cites-no-scenario": "Evidence Observation cites no Scenario",
|
|
54
|
-
// CANON VERBATIM — double-entry-manifesto.md:42, the Outcome column. The gloss is the outcome
|
|
55
|
-
// SENTENCE, canon's checkably-true half where the handle is only a label, so it states the
|
|
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
13
|
"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
14
|
broken: "Evidence Observation ATTEMPT errored before it could cite a Scenario (the proving suite failed to collect; behaviour never exercised)",
|
|
69
15
|
"both-missing": "Evidence Obligation and Evidence Observation are both missing — the Scenario is untagged and uncited",
|
|
70
16
|
};
|
|
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
17
|
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
18
|
export const OUTCOME_SIDE_PROFILE = {
|
|
108
19
|
satisfied: { obligation: "Exists", observation: "Exists" },
|
|
109
20
|
missing: { obligation: "Exists", observation: "None" },
|
|
110
21
|
failing: { obligation: "Exists", observation: "Exists" },
|
|
111
22
|
watermelon: { obligation: "Exists", observation: "Exists" },
|
|
112
23
|
"no-scn-code": { obligation: "Exists", observation: "None" },
|
|
24
|
+
"duplicate-scn-code": { obligation: "Exists", observation: "None" },
|
|
113
25
|
"cites-unknown-scenario": { obligation: "None", observation: "Exists" },
|
|
114
26
|
"cites-no-scenario": { obligation: "None", observation: "Exists" },
|
|
115
27
|
"no-evidence-obligation": { obligation: "None", observation: "Exists" },
|
|
116
28
|
broken: { obligation: "None", observation: "None" },
|
|
117
29
|
"both-missing": { obligation: "None", observation: "None" },
|
|
118
30
|
};
|
|
119
|
-
/**
|
|
120
|
-
* THE SMART CONSTRUCTOR (@SCN-LDG-030, 3F-2016) — the ONLY way to mint a
|
|
121
|
-
* `QualityCheckReconciliation`. It takes `outcome` as GIVEN (a consistency check, NOT a
|
|
122
|
-
* derivation — `missing`/`no-scn-code` share a profile, so the outcome cannot be recovered from
|
|
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
|
-
*/
|
|
130
31
|
export function makeQualityCheckReconciliation(fields) {
|
|
131
32
|
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
33
|
if ((fields.evidenceObligation !== undefined) !== (profile.obligation === "Exists")) {
|
|
134
34
|
throw new QualityCheckReconciliationFault({
|
|
135
35
|
code: QualityCheckFaultCode.ObligationSideDivergesFromCanon,
|
|
136
36
|
outcome: fields.outcome,
|
|
137
37
|
required: profile.obligation,
|
|
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.
|
|
140
38
|
actual: fields.evidenceObligation !== undefined ? "Exists" : "None",
|
|
141
39
|
});
|
|
142
40
|
}
|
|
143
|
-
// The CREDIT-side check: the observation is carried IFF canon says it Exists for this outcome.
|
|
144
41
|
if ((fields.evidenceObservation !== undefined) !== (profile.observation === "Exists")) {
|
|
145
42
|
throw new QualityCheckReconciliationFault({
|
|
146
43
|
code: QualityCheckFaultCode.ObservationSideDivergesFromCanon,
|
|
@@ -151,336 +48,82 @@ export function makeQualityCheckReconciliation(fields) {
|
|
|
151
48
|
}
|
|
152
49
|
return fields;
|
|
153
50
|
}
|
|
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
51
|
function locatorOf(observation) {
|
|
164
|
-
return observation.qualityCheck === "
|
|
52
|
+
return observation.qualityCheck === "fitness" ? undefined : observation.locator;
|
|
165
53
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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.
|
|
54
|
+
const CREDIT_PRECEDENCE = ["failing", "watermelon", "satisfied"];
|
|
55
|
+
function answerOf(matches, outcomeOf) {
|
|
56
|
+
for (const outcome of CREDIT_PRECEDENCE) {
|
|
57
|
+
const credit = matches.find((match) => outcomeOf(match) === outcome);
|
|
58
|
+
if (credit)
|
|
59
|
+
return { outcome, credit };
|
|
60
|
+
}
|
|
61
|
+
return { outcome: "missing" };
|
|
62
|
+
}
|
|
63
|
+
function fitnessCreditsCiting(scnId, evidenceObservations) {
|
|
64
|
+
return evidenceObservations.filter((o) => isFitnessObservation(o) &&
|
|
222
65
|
o.broken !== true &&
|
|
223
|
-
o.scnId === scnId
|
|
224
|
-
o.checkTag === checkTag);
|
|
66
|
+
o.scnId === scnId);
|
|
225
67
|
}
|
|
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.
|
|
68
|
+
function staticOutcomeOf(credit) {
|
|
69
|
+
if (credit.status === "failed")
|
|
70
|
+
return "failing";
|
|
71
|
+
if (credit.enforcedOverRealSource)
|
|
72
|
+
return "satisfied";
|
|
73
|
+
return credit.exists ? "watermelon" : undefined;
|
|
74
|
+
}
|
|
75
|
+
function reconcileStaticObligation(scnId, obligation, evidenceObservations) {
|
|
76
|
+
const { outcome, credit } = answerOf(fitnessCreditsCiting(scnId, evidenceObservations), staticOutcomeOf);
|
|
271
77
|
return makeQualityCheckReconciliation({
|
|
272
|
-
qualityCheck: "
|
|
273
|
-
staticCheck: checkTag,
|
|
78
|
+
qualityCheck: "fitness",
|
|
274
79
|
outcome,
|
|
275
|
-
evidenceObligation:
|
|
276
|
-
...(
|
|
80
|
+
evidenceObligation: obligation,
|
|
81
|
+
...(credit ? { evidenceObservation: credit } : {}),
|
|
277
82
|
});
|
|
278
83
|
}
|
|
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";
|
|
84
|
+
function runtimeOutcomeOf(credit) {
|
|
85
|
+
return credit.status === "passed" ? "satisfied" : "failing";
|
|
86
|
+
}
|
|
87
|
+
export function reconcileEvidenceObligation(scnId, obligation, evidenceObservations) {
|
|
88
|
+
if (obligation.qualityCheck === "fitness") {
|
|
89
|
+
return reconcileStaticObligation(scnId, obligation, evidenceObservations);
|
|
345
90
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
91
|
+
const { outcome, credit } = answerOf(evidenceObservations.filter((o) => o.broken !== true &&
|
|
92
|
+
o.qualityCheck === obligation.qualityCheck &&
|
|
93
|
+
o.scnId === scnId), runtimeOutcomeOf);
|
|
349
94
|
return makeQualityCheckReconciliation({
|
|
350
|
-
qualityCheck:
|
|
95
|
+
qualityCheck: obligation.qualityCheck,
|
|
351
96
|
outcome,
|
|
352
|
-
evidenceObligation:
|
|
353
|
-
...(
|
|
97
|
+
evidenceObligation: obligation,
|
|
98
|
+
...(credit ? { evidenceObservation: credit } : {}),
|
|
354
99
|
});
|
|
355
100
|
}
|
|
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);
|
|
101
|
+
function fitnessFunctionNameOf(observation) {
|
|
102
|
+
return isFitnessObservation(observation)
|
|
103
|
+
? observation.fitnessFunction
|
|
104
|
+
: undefined;
|
|
383
105
|
}
|
|
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;
|
|
395
|
-
}
|
|
396
|
-
/**
|
|
397
|
-
* The NO-EVIDENCE-OBLIGATION items on a real scenario's row (@SCN-LDG-008) — one per credit citing THIS
|
|
398
|
-
* scenario at a quality-check it never named. The credit posts to a real row against NO
|
|
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
106
|
export function noEvidenceObligationReconciliationsOf(scnId, postedCredits, evidenceObligationPairs) {
|
|
409
107
|
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))))
|
|
108
|
+
.filter((o) => !evidenceObligationPairs.has(evidenceObligationPairKey(scnId, o.qualityCheck)))
|
|
417
109
|
.map((o) => makeQualityCheckReconciliation({
|
|
418
110
|
qualityCheck: o.qualityCheck,
|
|
419
111
|
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.
|
|
112
|
+
...(fitnessFunctionNameOf(o) !== undefined ? { fitnessFunction: fitnessFunctionNameOf(o) } : {}),
|
|
428
113
|
evidenceObservation: o,
|
|
429
114
|
...(locatorOf(o) !== undefined ? { locator: locatorOf(o) } : {}),
|
|
430
115
|
}));
|
|
431
116
|
}
|
|
432
|
-
/**
|
|
433
|
-
* The items on a PHANTOM row (@SCN-LDG-006) — one `cites-unknown-scenario` per credit citing an
|
|
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
117
|
export function citesUnknownScenarioReconciliationsOf(credits) {
|
|
446
118
|
return credits.map((o) => makeQualityCheckReconciliation({
|
|
447
119
|
qualityCheck: o.qualityCheck,
|
|
448
|
-
|
|
449
|
-
// sent to. For a static run that is its runnable handle.
|
|
450
|
-
...(isStaticCheckObservation(o) ? { staticCheck: o.staticCheck } : {}),
|
|
120
|
+
...(isFitnessObservation(o) ? { fitnessFunction: o.fitnessFunction } : {}),
|
|
451
121
|
outcome: "cites-unknown-scenario",
|
|
452
122
|
evidenceObservation: o,
|
|
453
123
|
...(locatorOf(o) !== undefined ? { locator: locatorOf(o) } : {}),
|
|
454
124
|
}));
|
|
455
125
|
}
|
|
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) {
|
|
126
|
+
export function suspenseReconciliationsOf(brokenEvidence, uncited, noEvidenceObligationFitnessFunctions) {
|
|
484
127
|
return [
|
|
485
128
|
...brokenEvidence.map((o) => makeQualityCheckReconciliation({
|
|
486
129
|
qualityCheck: o.qualityCheck,
|
|
@@ -494,73 +137,30 @@ export function suspenseReconciliationsOf(brokenEvidence, uncited, noEvidenceObl
|
|
|
494
137
|
evidenceObservation: o,
|
|
495
138
|
...(o.locator !== undefined ? { locator: o.locator } : {}),
|
|
496
139
|
})),
|
|
497
|
-
...
|
|
498
|
-
qualityCheck: "
|
|
499
|
-
|
|
500
|
-
staticCheck: o.staticCheck,
|
|
140
|
+
...noEvidenceObligationFitnessFunctions.map((o) => makeQualityCheckReconciliation({
|
|
141
|
+
qualityCheck: "fitness",
|
|
142
|
+
fitnessFunction: o.fitnessFunction,
|
|
501
143
|
outcome: "cites-no-scenario",
|
|
502
144
|
evidenceObservation: o,
|
|
503
145
|
})),
|
|
504
146
|
];
|
|
505
147
|
}
|
|
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
148
|
export function noScnCodeReconciliations() {
|
|
522
149
|
return [makeQualityCheckReconciliation({ outcome: "no-scn-code", evidenceObligation: {} })];
|
|
523
150
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
151
|
+
export function duplicateScnCodeReconciliationsOf(obligations) {
|
|
152
|
+
const firstNaming = new Map();
|
|
153
|
+
for (const obligation of obligations) {
|
|
154
|
+
if (obligation.qualityCheck !== undefined && !firstNaming.has(obligation.qualityCheck)) {
|
|
155
|
+
firstNaming.set(obligation.qualityCheck, obligation);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (firstNaming.size === 0) {
|
|
159
|
+
return [makeQualityCheckReconciliation({ outcome: "duplicate-scn-code", evidenceObligation: {} })];
|
|
160
|
+
}
|
|
161
|
+
return [...firstNaming].map(([qualityCheck, evidenceObligation]) => makeQualityCheckReconciliation({ qualityCheck, outcome: "duplicate-scn-code", evidenceObligation }));
|
|
162
|
+
}
|
|
533
163
|
export function bothMissingReconciliation() {
|
|
534
164
|
return makeQualityCheckReconciliation({ outcome: "both-missing" });
|
|
535
165
|
}
|
|
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
166
|
//# sourceMappingURL=qualityCheckReconciliation.js.map
|