@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.
Files changed (257) hide show
  1. package/README.md +3 -3
  2. package/dist/assemble/evidenceReconciliationFromSources.d.ts +5 -40
  3. package/dist/assemble/evidenceReconciliationFromSources.d.ts.map +1 -1
  4. package/dist/assemble/evidenceReconciliationFromSources.js +30 -325
  5. package/dist/assemble/evidenceReconciliationFromSources.js.map +1 -1
  6. package/dist/assemble/renderInput.d.ts +7 -73
  7. package/dist/assemble/renderInput.d.ts.map +1 -1
  8. package/dist/assemble/renderInput.js +3 -45
  9. package/dist/assemble/renderInput.js.map +1 -1
  10. package/dist/census/evidenceObligations.d.ts +11 -46
  11. package/dist/census/evidenceObligations.d.ts.map +1 -1
  12. package/dist/census/evidenceObligations.js +19 -25
  13. package/dist/census/evidenceObligations.js.map +1 -1
  14. package/dist/domain/evidenceOrder.d.ts +3 -0
  15. package/dist/domain/evidenceOrder.d.ts.map +1 -0
  16. package/dist/domain/evidenceOrder.js +15 -0
  17. package/dist/domain/evidenceOrder.js.map +1 -0
  18. package/dist/domain/evidenceReconciliation.d.ts +21 -391
  19. package/dist/domain/evidenceReconciliation.d.ts.map +1 -1
  20. package/dist/domain/evidenceReconciliation.js +87 -382
  21. package/dist/domain/evidenceReconciliation.js.map +1 -1
  22. package/dist/domain/featureCode.d.ts +4 -0
  23. package/dist/domain/featureCode.d.ts.map +1 -0
  24. package/dist/domain/featureCode.js +12 -0
  25. package/dist/domain/featureCode.js.map +1 -0
  26. package/dist/domain/journal.d.ts +8 -393
  27. package/dist/domain/journal.d.ts.map +1 -1
  28. package/dist/domain/journal.js +6 -118
  29. package/dist/domain/journal.js.map +1 -1
  30. package/dist/domain/ledger.d.ts +8 -328
  31. package/dist/domain/ledger.d.ts.map +1 -1
  32. package/dist/domain/ledger.js +23 -326
  33. package/dist/domain/ledger.js.map +1 -1
  34. package/dist/domain/messages.d.ts +213 -0
  35. package/dist/domain/messages.d.ts.map +1 -0
  36. package/dist/domain/messages.js +313 -0
  37. package/dist/domain/messages.js.map +1 -0
  38. package/dist/domain/outcome.d.ts +3 -0
  39. package/dist/domain/outcome.d.ts.map +1 -0
  40. package/dist/domain/outcome.js +2 -0
  41. package/dist/domain/outcome.js.map +1 -0
  42. package/dist/domain/posting.d.ts +8 -105
  43. package/dist/domain/posting.d.ts.map +1 -1
  44. package/dist/domain/posting.js +4 -56
  45. package/dist/domain/posting.js.map +1 -1
  46. package/dist/domain/qualityCheckFault.d.ts +3 -43
  47. package/dist/domain/qualityCheckFault.d.ts.map +1 -1
  48. package/dist/domain/qualityCheckFault.js +0 -24
  49. package/dist/domain/qualityCheckFault.js.map +1 -1
  50. package/dist/domain/qualityCheckReconciliation.d.ts +7 -280
  51. package/dist/domain/qualityCheckReconciliation.d.ts.map +1 -1
  52. package/dist/domain/qualityCheckReconciliation.js +63 -463
  53. package/dist/domain/qualityCheckReconciliation.js.map +1 -1
  54. package/dist/domain/qualityCheckReconciliationFault.d.ts +0 -19
  55. package/dist/domain/qualityCheckReconciliationFault.d.ts.map +1 -1
  56. package/dist/domain/qualityCheckReconciliationFault.js +1 -24
  57. package/dist/domain/qualityCheckReconciliationFault.js.map +1 -1
  58. package/dist/domain/scenarioCodeError.d.ts +16 -0
  59. package/dist/domain/scenarioCodeError.d.ts.map +1 -0
  60. package/dist/domain/scenarioCodeError.js +4 -0
  61. package/dist/domain/scenarioCodeError.js.map +1 -0
  62. package/dist/domain/scenarioReconciliation.d.ts +2 -207
  63. package/dist/domain/scenarioReconciliation.d.ts.map +1 -1
  64. package/dist/domain/scenarioReconciliation.js +4 -101
  65. package/dist/domain/scenarioReconciliation.js.map +1 -1
  66. package/dist/domain/scnAllocation.d.ts +27 -0
  67. package/dist/domain/scnAllocation.d.ts.map +1 -0
  68. package/dist/domain/scnAllocation.js +33 -0
  69. package/dist/domain/scnAllocation.js.map +1 -0
  70. package/dist/domain/verdictCode.d.ts +10 -0
  71. package/dist/domain/verdictCode.d.ts.map +1 -0
  72. package/dist/domain/verdictCode.js +16 -0
  73. package/dist/domain/verdictCode.js.map +1 -0
  74. package/dist/index.d.ts +14 -40
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +8 -88
  77. package/dist/index.js.map +1 -1
  78. package/dist/internal.d.ts +3 -27
  79. package/dist/internal.d.ts.map +1 -1
  80. package/dist/internal.js +3 -94
  81. package/dist/internal.js.map +1 -1
  82. package/dist/lib/bareTerm.d.ts +0 -39
  83. package/dist/lib/bareTerm.d.ts.map +1 -1
  84. package/dist/lib/bareTerm.js +0 -39
  85. package/dist/lib/bareTerm.js.map +1 -1
  86. package/dist/lib/portablePath.d.ts.map +1 -1
  87. package/dist/lib/portablePath.js +2 -16
  88. package/dist/lib/portablePath.js.map +1 -1
  89. package/dist/lib/scnCitation.d.ts +0 -32
  90. package/dist/lib/scnCitation.d.ts.map +1 -1
  91. package/dist/lib/scnCitation.js +2 -42
  92. package/dist/lib/scnCitation.js.map +1 -1
  93. package/dist/model.d.ts +0 -23
  94. package/dist/model.d.ts.map +1 -1
  95. package/dist/model.js +0 -23
  96. package/dist/model.js.map +1 -1
  97. package/dist/parse/checkScriptsRunInCI.d.ts +5 -168
  98. package/dist/parse/checkScriptsRunInCI.d.ts.map +1 -1
  99. package/dist/parse/checkScriptsRunInCI.js +11 -171
  100. package/dist/parse/checkScriptsRunInCI.js.map +1 -1
  101. package/dist/parse/ciStepRecord.d.ts +4 -118
  102. package/dist/parse/ciStepRecord.d.ts.map +1 -1
  103. package/dist/parse/ciStepRecord.js +18 -225
  104. package/dist/parse/ciStepRecord.js.map +1 -1
  105. package/dist/parse/commandRuns.d.ts +0 -39
  106. package/dist/parse/commandRuns.d.ts.map +1 -1
  107. package/dist/parse/commandRuns.js +6 -67
  108. package/dist/parse/commandRuns.js.map +1 -1
  109. package/dist/parse/cucumber.d.ts +0 -53
  110. package/dist/parse/cucumber.d.ts.map +1 -1
  111. package/dist/parse/cucumber.js +42 -146
  112. package/dist/parse/cucumber.js.map +1 -1
  113. package/dist/parse/discoveryScope.d.ts +1 -46
  114. package/dist/parse/discoveryScope.d.ts.map +1 -1
  115. package/dist/parse/discoveryScope.js +2 -57
  116. package/dist/parse/discoveryScope.js.map +1 -1
  117. package/dist/parse/fitnessCitations.d.ts +1 -72
  118. package/dist/parse/fitnessCitations.d.ts.map +1 -1
  119. package/dist/parse/fitnessCitations.js +3 -55
  120. package/dist/parse/fitnessCitations.js.map +1 -1
  121. package/dist/parse/reconcilerSteps.d.ts +0 -46
  122. package/dist/parse/reconcilerSteps.d.ts.map +1 -1
  123. package/dist/parse/reconcilerSteps.js +2 -64
  124. package/dist/parse/reconcilerSteps.js.map +1 -1
  125. package/dist/parse/runningFitnessFunctions.d.ts +9 -0
  126. package/dist/parse/runningFitnessFunctions.d.ts.map +1 -0
  127. package/dist/parse/runningFitnessFunctions.js +57 -0
  128. package/dist/parse/runningFitnessFunctions.js.map +1 -0
  129. package/dist/parse/scenarioCodeErrors.d.ts +11 -0
  130. package/dist/parse/scenarioCodeErrors.d.ts.map +1 -0
  131. package/dist/parse/scenarioCodeErrors.js +78 -0
  132. package/dist/parse/scenarioCodeErrors.js.map +1 -0
  133. package/dist/parse/scenarios.d.ts +3 -228
  134. package/dist/parse/scenarios.d.ts.map +1 -1
  135. package/dist/parse/scenarios.js +20 -457
  136. package/dist/parse/scenarios.js.map +1 -1
  137. package/dist/parse/stepAccounting.d.ts +0 -46
  138. package/dist/parse/stepAccounting.d.ts.map +1 -1
  139. package/dist/parse/stepAccounting.js +1 -55
  140. package/dist/parse/stepAccounting.js.map +1 -1
  141. package/dist/parse/stepExclusions.d.ts +0 -47
  142. package/dist/parse/stepExclusions.d.ts.map +1 -1
  143. package/dist/parse/stepExclusions.js +2 -50
  144. package/dist/parse/stepExclusions.js.map +1 -1
  145. package/dist/parse/stepNames.d.ts +0 -27
  146. package/dist/parse/stepNames.d.ts.map +1 -1
  147. package/dist/parse/stepNames.js +0 -27
  148. package/dist/parse/stepNames.js.map +1 -1
  149. package/dist/parse/targetCorpus.d.ts +1 -78
  150. package/dist/parse/targetCorpus.d.ts.map +1 -1
  151. package/dist/parse/targetCorpus.js +4 -46
  152. package/dist/parse/targetCorpus.js.map +1 -1
  153. package/dist/parse/targetManifest.d.ts +8 -146
  154. package/dist/parse/targetManifest.d.ts.map +1 -1
  155. package/dist/parse/targetManifest.js +48 -198
  156. package/dist/parse/targetManifest.js.map +1 -1
  157. package/dist/parse/vitest.d.ts +0 -60
  158. package/dist/parse/vitest.d.ts.map +1 -1
  159. package/dist/parse/vitest.js +4 -83
  160. package/dist/parse/vitest.js.map +1 -1
  161. package/dist/provenance/provenance.d.ts +2 -110
  162. package/dist/provenance/provenance.d.ts.map +1 -1
  163. package/dist/provenance/provenance.js +2 -58
  164. package/dist/provenance/provenance.js.map +1 -1
  165. package/dist/render/behaviouralRunners.d.ts +0 -29
  166. package/dist/render/behaviouralRunners.d.ts.map +1 -1
  167. package/dist/render/behaviouralRunners.js +6 -69
  168. package/dist/render/behaviouralRunners.js.map +1 -1
  169. package/dist/render/configurationNotRead.d.ts +3 -0
  170. package/dist/render/configurationNotRead.d.ts.map +1 -0
  171. package/dist/render/configurationNotRead.js +17 -0
  172. package/dist/render/configurationNotRead.js.map +1 -0
  173. package/dist/render/discoveryScope.d.ts +0 -37
  174. package/dist/render/discoveryScope.d.ts.map +1 -1
  175. package/dist/render/discoveryScope.js +3 -39
  176. package/dist/render/discoveryScope.js.map +1 -1
  177. package/dist/render/evidenceGrid.d.ts +1 -35
  178. package/dist/render/evidenceGrid.d.ts.map +1 -1
  179. package/dist/render/evidenceGrid.js +21 -103
  180. package/dist/render/evidenceGrid.js.map +1 -1
  181. package/dist/render/excludedSteps.d.ts +0 -26
  182. package/dist/render/excludedSteps.d.ts.map +1 -1
  183. package/dist/render/excludedSteps.js +4 -32
  184. package/dist/render/excludedSteps.js.map +1 -1
  185. package/dist/render/fitnessFunctions.d.ts +3 -0
  186. package/dist/render/fitnessFunctions.d.ts.map +1 -0
  187. package/dist/render/fitnessFunctions.js +12 -0
  188. package/dist/render/fitnessFunctions.js.map +1 -0
  189. package/dist/render/json.d.ts +0 -11
  190. package/dist/render/json.d.ts.map +1 -1
  191. package/dist/render/json.js +0 -11
  192. package/dist/render/json.js.map +1 -1
  193. package/dist/render/markdown.d.ts +1 -55
  194. package/dist/render/markdown.d.ts.map +1 -1
  195. package/dist/render/markdown.js +128 -467
  196. package/dist/render/markdown.js.map +1 -1
  197. package/dist/render/provenanceSection.d.ts +0 -22
  198. package/dist/render/provenanceSection.d.ts.map +1 -1
  199. package/dist/render/provenanceSection.js +10 -53
  200. package/dist/render/provenanceSection.js.map +1 -1
  201. package/dist/render/reconcilerSteps.d.ts +0 -25
  202. package/dist/render/reconcilerSteps.d.ts.map +1 -1
  203. package/dist/render/reconcilerSteps.js +5 -30
  204. package/dist/render/reconcilerSteps.js.map +1 -1
  205. package/dist/render/runVerdict.d.ts +0 -38
  206. package/dist/render/runVerdict.d.ts.map +1 -1
  207. package/dist/render/runVerdict.js +62 -129
  208. package/dist/render/runVerdict.js.map +1 -1
  209. package/dist/render/scenarioCodeErrorsSection.d.ts +3 -0
  210. package/dist/render/scenarioCodeErrorsSection.d.ts.map +1 -0
  211. package/dist/render/scenarioCodeErrorsSection.js +45 -0
  212. package/dist/render/scenarioCodeErrorsSection.js.map +1 -0
  213. package/dist/render/stepAccounting.d.ts +0 -36
  214. package/dist/render/stepAccounting.d.ts.map +1 -1
  215. package/dist/render/stepAccounting.js +5 -49
  216. package/dist/render/stepAccounting.js.map +1 -1
  217. package/dist/render/testVolume.d.ts +0 -25
  218. package/dist/render/testVolume.d.ts.map +1 -1
  219. package/dist/render/testVolume.js +4 -35
  220. package/dist/render/testVolume.js.map +1 -1
  221. package/package.json +4 -1
  222. package/dist/domain/checkCharacteristics.d.ts +0 -88
  223. package/dist/domain/checkCharacteristics.d.ts.map +0 -1
  224. package/dist/domain/checkCharacteristics.js +0 -109
  225. package/dist/domain/checkCharacteristics.js.map +0 -1
  226. package/dist/domain/fitnessChecks.d.ts +0 -79
  227. package/dist/domain/fitnessChecks.d.ts.map +0 -1
  228. package/dist/domain/fitnessChecks.js +0 -81
  229. package/dist/domain/fitnessChecks.js.map +0 -1
  230. package/dist/domain/qualityCheckFaultMessages.d.ts +0 -43
  231. package/dist/domain/qualityCheckFaultMessages.d.ts.map +0 -1
  232. package/dist/domain/qualityCheckFaultMessages.js +0 -38
  233. package/dist/domain/qualityCheckFaultMessages.js.map +0 -1
  234. package/dist/lib/featureCode.d.ts +0 -21
  235. package/dist/lib/featureCode.d.ts.map +0 -1
  236. package/dist/lib/featureCode.js +0 -24
  237. package/dist/lib/featureCode.js.map +0 -1
  238. package/dist/parse/corpusCodeFaults.d.ts +0 -76
  239. package/dist/parse/corpusCodeFaults.d.ts.map +0 -1
  240. package/dist/parse/corpusCodeFaults.js +0 -121
  241. package/dist/parse/corpusCodeFaults.js.map +0 -1
  242. package/dist/parse/corpusFaults.d.ts +0 -124
  243. package/dist/parse/corpusFaults.d.ts.map +0 -1
  244. package/dist/parse/corpusFaults.js +0 -72
  245. package/dist/parse/corpusFaults.js.map +0 -1
  246. package/dist/parse/corpusGrammarFaults.d.ts +0 -71
  247. package/dist/parse/corpusGrammarFaults.d.ts.map +0 -1
  248. package/dist/parse/corpusGrammarFaults.js +0 -292
  249. package/dist/parse/corpusGrammarFaults.js.map +0 -1
  250. package/dist/parse/runningStaticChecks.d.ts +0 -125
  251. package/dist/parse/runningStaticChecks.d.ts.map +0 -1
  252. package/dist/parse/runningStaticChecks.js +0 -316
  253. package/dist/parse/runningStaticChecks.js.map +0 -1
  254. package/dist/render/staticChecks.d.ts +0 -23
  255. package/dist/render/staticChecks.d.ts.map +0 -1
  256. package/dist/render/staticChecks.js +0 -69
  257. package/dist/render/staticChecks.js.map +0 -1
@@ -1,69 +1,11 @@
1
- /**
2
- * THE EVIDENCE RECONCILIATION — canon's Layer 1, *"the whole reconciliation: all Scenario
3
- * Reconciliations taken together"*, and **the key concept the domain models**
4
- * (`double-entry-manifesto.md`). This module is its home.
5
- *
6
- * IT ARRIVED HERE FROM `reconcile/` (3F-2232). After 3F-2202/2203/2204 that module held no domain
7
- * rule — but it was still a named layer beside the domain doing the concept's own work, which is
8
- * the shape `functional-domain-modelling.md` rejects: *"orchestration is thin composition, not a
9
- * named layer."* Constructing a concept is the concept's business. The module's residual tell was
10
- * already visible — it re-exported `isBalanced` / `hasPendingItems` / `hasBrokenEvidence` FROM
11
- * `domain/ledger` purely to preserve importers' paths, a shell around behaviour that had already
12
- * gone home. Every importer now points at the module that OWNS the symbol.
13
- *
14
- * (3F-2233 completes the move: the concept's TYPE, its `Summary`, and the factory that fuses this
15
- * fold with the summary tally. Today this module holds the fold alone.)
16
- *
17
- * The pure obligation-vs-observation join, keyed by one flat
18
- * `qualityCheck` (the two-axis `(kind, level)` grid collapsed). This module is THIN
19
- * COMPOSITION: the domain RULES it once held moved to the
20
- * concept modules that own them (3F-1994) — `reconcileEvidenceObligation` (the per-obligation
21
- * outcome) to `domain/qualityCheckReconciliation`, `verdictFor` / `reconcilingItemsOf` (the
22
- * account roll-up) to `domain/scenarioReconciliation`, `isBalanced` / `hasPendingItems` /
23
- * `hasBrokenEvidence` (the whole-run predicates) to `domain/ledger`. `reconcile` is the fold that
24
- * WALKS the inputs, CALLS those rules, and BUILDS the rows; it is the orchestration, not their home.
25
- *
26
- * POSTING-DESTINATION left too (3F-2202) — WHERE an entry lands is canon's own glossary group
27
- * ("### Posting") and now lives on `domain/posting`: `postingOf`, the one citation route over both
28
- * axes (plus the no-evidence-obligation-static-check arm). The by-name half is gone (3F-2590).
29
- * The fold no longer DECIDES a destination anywhere — it
30
- * asks the rule once per credit and files the answer. What remains is assembly: which rows exist,
31
- * in what order, holding which items.
32
- *
33
- * Per the slice design: for each evidence obligation a scenario raises (a reconciling
34
- * item), present+pass → satisfied; present+fail → failing; absent →
35
- * missing. `reconcileEvidenceObligation` branches on the `qualityCheck === "lint"`
36
- * watermelon discriminator (replacing the retired `kind === "static"` split): a `lint`
37
- * evidence obligation reconciles against a static-check evidence observation (exists+wired → satisfied;
38
- * exists+unwired → watermelon; absent → missing); a runtime evidence obligation
39
- * reconciles against a test result matched by (scnId, quality-check).
40
- *
41
- * The scenario VERDICT is binary (superseding the earlier prioritising
42
- * aggregate): a scenario is `balanced` IFF every required quality-check balances;
43
- * otherwise `out-of-balance`, carrying the LIST of EVERY non-balanced reconciling
44
- * item — no prioritisation, no picked winner ("missing beats failing" was a false
45
- * order that hid one of two genuinely different problems). The renderers list
46
- * every item.
47
- *
48
- * Parent: @SCN-LDG-001.
49
- */
50
- import { evidenceObligationPairsOf,
51
- // THE PENDING PARTITION (3F-2204) — the two exact complements over a scenario's obligations.
52
- evidenceObligationsOf, pendingItemsOf, } from "../model.js";
53
- import { reconcileEvidenceObligation,
54
- // THE CREDIT-SIDE AND DEGENERATE OUTCOME RULES (3F-2204) — what an entry reconciles TO, as
55
- // opposed to where it lands. `reconcile` asks; it no longer maps outcomes itself.
56
- noEvidenceObligationReconciliationsOf, citesUnknownScenarioReconciliationsOf, suspenseReconciliationsOf, noScnCodeReconciliations, unspecifiedRowReconciliations, } from "./qualityCheckReconciliation.js";
57
- import { ledgerTally, runtimeObservationTally, staticObservationTally, } from "./ledger.js";
58
- import { verdictFor, reconcilingItemsOf,
59
- // THE ROW-SHAPE RULE (3F-2204) — the both-missing degeneration and its pending clause.
60
- qualityCheckReconciliationsOf, } from "./scenarioReconciliation.js";
61
- // THE POSTING RULE (3F-2202) — where an entry lands is canon's own glossary group, so it lives on
62
- // its own concept module. `reconcile` no longer decides a destination; it ASKS.
1
+ import { evidenceObligationPairsOf, evidenceObligationsOf, pendingItemsOf, } from "./journal.js";
2
+ import { reconcileEvidenceObligation, noEvidenceObligationReconciliationsOf, citesUnknownScenarioReconciliationsOf, suspenseReconciliationsOf, noScnCodeReconciliations, duplicateScnCodeReconciliationsOf, } from "./qualityCheckReconciliation.js";
3
+ import { ledgerTally, runtimeObservationTally, fitnessFunctionTally, } from "./ledger.js";
4
+ import { verdictFor, reconcilingItemsOf, qualityCheckReconciliationsOf, } from "./scenarioReconciliation.js";
63
5
  import { postingOf } from "./posting.js";
64
- // The `lint` quality-check's own predicate, asked here to NARROW what the posting rule has already
65
- // routed — see the fitness arm below.
66
- import { isStaticCheckObservation } from "../model.js";
6
+ import { CORE_MESSAGES, CoreMessage } from "./messages.js";
7
+ import { inEvidenceOrder } from "./evidenceOrder.js";
8
+ import { isFitnessObservation } from "./journal.js";
67
9
  function citationIndex() {
68
10
  const byScn = new Map();
69
11
  const phantomIds = [];
@@ -80,164 +22,99 @@ function citationIndex() {
80
22
  phantomIds,
81
23
  };
82
24
  }
83
- /**
84
- * THE ONE ROUTING PASS (3F-2202). Every credit is routed ONCE, by THE posting rule
85
- * (`domain/posting`), into the structures the fold reads. `reconcile` no longer DECIDES a
86
- * destination anywhere — it asks, and files the answer.
87
- *
88
- * This replaces two passes that re-stated the SAME four routing decisions (the citation index and
89
- * the phantom-id scan) plus three stream filters. They agreed by copy-paste, not by construction:
90
- * either could have grown an arm alone and nothing would have redded — the identical anaemia
91
- * `evidenceObligationPairsOf` was extracted to cure.
92
- *
93
- * ITS OWN FUNCTION, extracted at @SCN-LNT-026 when the static axis gained its second suspense
94
- * reason and took `reconcile` past the `complexity` and `max-lines-per-function` ceilings. Routing
95
- * is one idea with a name and the fold below is another; nothing about what it decides moved, and
96
- * the exception list is untouched — an exception buys no headroom.
97
- */
25
+ function routeFitnessCredit(observation, posting, citations, streams) {
26
+ if (posting.kind !== "suspense-scenario") {
27
+ citations.file(posting, observation);
28
+ return;
29
+ }
30
+ if (posting.posted === "broken" && isFitnessObservation(observation)) {
31
+ streams.brokenEvidence.push(observation);
32
+ }
33
+ else
34
+ streams.noEvidenceObligationFitnessFunctions.push(observation);
35
+ }
36
+ function routeRuntimeCredit(observation, posting, citations, streams) {
37
+ switch (posting.kind) {
38
+ case "scenario":
39
+ case "phantom-scenario": {
40
+ citations.file(posting, observation);
41
+ break;
42
+ }
43
+ case "suspense-scenario":
44
+ if (posting.posted === "broken")
45
+ streams.brokenEvidence.push(observation);
46
+ else
47
+ streams.uncited.push(observation);
48
+ break;
49
+ }
50
+ }
98
51
  function routeCredits(evidenceObservations, scenarioIds) {
99
- // THE CITATION INDEX holds BOTH axes now (@SCN-LNT-001): a fitness check's run cites the
100
- // scenario that raises its obligation, so it files here exactly as a test result does.
101
52
  const citations = citationIndex();
102
- // The three SUSPENSE streams, kept separate and each in observation order — the suspense row's
103
- // item order (broken first, then uncited, then the no-evidence-obligation static checks) is row
104
- // assembly, owned by the fold in `reconcile`, not a routing decision.
105
- // BROKEN EVIDENCE FROM EITHER AXIS (@SCN-LNT-026). It was a runtime-only stream while `broken`
106
- // was a runtime-only fact; a converted static check that did not run is the same fact about the
107
- // same kind of absence, so it lands in the same stream and renders through the same section.
108
- const brokenEvidence = [];
109
- const uncited = [];
110
- const noEvidenceObligationGuards = [];
53
+ const streams = {
54
+ brokenEvidence: [],
55
+ uncited: [],
56
+ noEvidenceObligationFitnessFunctions: [],
57
+ };
58
+ const { brokenEvidence, uncited, noEvidenceObligationFitnessFunctions } = streams;
111
59
  for (const o of evidenceObservations) {
112
- // The union NARROW — `qualityCheck === "lint"` is the `EvidenceObservation` union's TYPE TAG,
113
- // not a posting decision. Which axis a credit rides is read here; where it LANDS is the rule's,
114
- // on both branches.
115
- if (o.qualityCheck === "lint") {
116
- const posting = postingOf(o, scenarioIds);
117
- // THE SPLIT ON THIS AXIS TOO (@SCN-LNT-026), the twin of the runtime arm's below. Both
118
- // reasons post to suspense because neither names a row, and they stay in separate streams
119
- // because they are different facts with different remedies: a check nobody named RAN and
120
- // wants an obligation written for it; a broken one never ran and wants its harness fixed.
121
- //
122
- // THE SECOND CLAUSE IS THE NARROW, NOT A SECOND DECISION. `postingOf` reaches `broken` on
123
- // this axis only through `isStaticCheckObservation`, so asking the same predicate here
124
- // cannot disagree with it — it re-states what the rule already settled in the one form the
125
- // compiler accepts. Nothing falls between the two arms: an observation that somehow took
126
- // neither still lands in the second, misfiled rather than vaporised.
127
- if (posting.kind === "suspense-scenario") {
128
- if (posting.posted === "broken" && isStaticCheckObservation(o))
129
- brokenEvidence.push(o);
130
- else
131
- noEvidenceObligationGuards.push(o);
132
- }
133
- // A CITING run files into the citation index like any other credit (@SCN-LNT-001) —
134
- // exactly one row, real or invented. There is no third destination left (3F-2590).
135
- else
136
- citations.file(posting, o);
60
+ if (o.qualityCheck === "fitness") {
61
+ routeFitnessCredit(o, postingOf(o, scenarioIds), citations, streams);
137
62
  continue;
138
63
  }
139
- const posting = postingOf(o, scenarioIds);
140
- switch (posting.kind) {
141
- case "scenario":
142
- case "phantom-scenario": {
143
- // THE CITATION INDEX holds real AND invented ids alike — a phantom row reads its posted
144
- // credits from here too, so both citation-routed destinations file together.
145
- citations.file(posting, o);
146
- break;
147
- }
148
- case "suspense-scenario":
149
- // THE SPLIT (@SCN-LDG-017) — `broken` and `cites-nothing` both land on suspense because
150
- // neither names a scenario, but they are different facts with different remedies (fix the
151
- // harness vs add the @SCN), so their streams stay apart. The rule already told them apart.
152
- if (posting.posted === "broken")
153
- brokenEvidence.push(o);
154
- else
155
- uncited.push(o);
156
- break;
157
- }
64
+ routeRuntimeCredit(o, postingOf(o, scenarioIds), citations, streams);
158
65
  }
159
- return { citations, brokenEvidence, uncited, noEvidenceObligationGuards };
66
+ return { citations, brokenEvidence, uncited, noEvidenceObligationFitnessFunctions };
160
67
  }
161
- /**
162
- * ONE REAL SCENARIO'S ROW — the debit side reconciled against the observed evidence, the credit
163
- * side's no-evidence-obligation items beside it, and the row assembled from both.
164
- *
165
- * ITS OWN FUNCTION, extracted at @SCN-LNT-027 (3F-3472) when the fourth population took `reconcile`
166
- * past the `max-lines-per-function` ceiling — `routeCredits`' own precedent, and the same seam.
167
- * Routing is one idea with a name, what each population RECONCILES to is another, and `reconcile`
168
- * is the ORDER they are filed in. Nothing any of them decides moved.
169
- */
170
68
  function scenarioRow(d, evidenceObservations, citations, evidenceObligationPairs) {
171
- // THE PENDING PARTITION (@SCN-PND-004) — `pendingItemsOf` and `evidenceObligationsOf`
172
- // are exact complements over the scenario's obligations, so nothing is dropped and nothing is
173
- // counted twice. Rule + rationale: `domain/journal`.
174
69
  const pendingItems = pendingItemsOf(d);
175
- // The DEBIT side — each NAMED obligation reconciled against the observed evidence.
176
- const reconciledEvidenceObligations = evidenceObligationsOf(d.evidenceObligations).map((req) => reconcileEvidenceObligation(d.scnId, req, evidenceObservations));
177
- // The CREDIT side — NO-EVIDENCE-OBLIGATION (@SCN-LDG-008): a credit citing THIS real scenario at a
178
- // quality-check it never named. Rule + rationale: `domain/qualityCheckReconciliation`.
70
+ const reconciledEvidenceObligations = evidenceObligationsOf(d.evidenceObligations).map((obligation) => reconcileEvidenceObligation(d.scnId, obligation, evidenceObservations));
179
71
  const noEvidenceObligationItems = noEvidenceObligationReconciliationsOf(d.scnId, citations.creditsFor(d.scnId), evidenceObligationPairs);
180
- // The row's SUB-ROWS, degenerating to one `both-missing` item when the row reconciled to
181
- // nothing either way (@SCN-LDG-009). Rule + rationale: `domain/scenarioReconciliation`.
182
72
  const qualityCheckReconciliations = qualityCheckReconciliationsOf(reconciledEvidenceObligations, noEvidenceObligationItems, pendingItems);
183
73
  return {
184
74
  scnId: d.scnId,
185
75
  kind: "scenario",
186
76
  verdict: verdictFor(qualityCheckReconciliations),
187
- // THE POSTED CREDITS (@SCN-LDG-022) — every credit CITING this scenario, whatever it
188
- // reconciles to above. The citation index holds every citing credit, so this includes one citing a
189
- // PENDING quality-check, which reconciles `no-evidence-obligation` (3F-1858 — it used to reconcile to
190
- // NO ROW AT ALL, the false-green this posting made visible without fixing). It posts and is
191
- // findable WHATEVER it reconciles to; posting is not classifying, and a misclassification
192
- // must misfile an entry, never vaporise it. A fitness check's run is in here too and by the
193
- // SAME route (3F-2590): it cites this scenario, so the citation index holds it like any other.
194
77
  evidenceObservations: citations.creditsFor(d.scnId),
195
- // THE POSTED DEBITS (@SCN-LDG-023) — every obligation THIS scenario RAISES, in document
196
- // order, keyed by quality-check. The debit half of the same posting: where a credit is
197
- // routed by its citation, a debit needs no routing at all — the account IS the scenario.
198
- // Includes a PENDING obligation, which partitioned off above and earns NO reconciled row: it
199
- // posts ANYWAY. Posting is not classifying, on this side too.
200
78
  evidenceObligations: [...d.evidenceObligations],
201
79
  qualityCheckReconciliations,
202
80
  reconcilingItems: reconcilingItemsOf(qualityCheckReconciliations),
203
81
  pendingItems,
204
82
  };
205
83
  }
206
- /**
207
- * THE PHANTOM ROWS, assembled (@SCN-LDG-006). Extracted beside its siblings at 3F-3472; the
208
- * routing that produced `citations.phantomIds` is `routeCredits`', and unchanged.
209
- */
84
+ function accountsByCode(scenarios) {
85
+ const byCode = new Map();
86
+ for (const scenario of scenarios) {
87
+ const sharers = byCode.get(scenario.scnId) ?? [];
88
+ sharers.push(scenario);
89
+ byCode.set(scenario.scnId, sharers);
90
+ }
91
+ return [...byCode.values()];
92
+ }
93
+ function duplicateScnCodeRow(sharers, citations) {
94
+ const scnId = sharers[0].scnId;
95
+ const evidenceObligations = sharers.flatMap((s) => s.evidenceObligations);
96
+ const items = duplicateScnCodeReconciliationsOf(evidenceObligations);
97
+ return {
98
+ scnId,
99
+ kind: "scenario",
100
+ verdict: verdictFor(items),
101
+ evidenceObservations: citations.creditsFor(scnId),
102
+ evidenceObligations,
103
+ qualityCheckReconciliations: items,
104
+ reconcilingItems: reconcilingItemsOf(items),
105
+ pendingItems: [],
106
+ };
107
+ }
210
108
  function phantomScenarioRows(citations) {
211
109
  const rows = [];
212
- // CREDIT-SIDE — PHANTOM scenarios (@SCN-LDG-006). One phantom Scenario Reconciliation per
213
- // UNKNOWN cited id, in first-seen order, its Quality Check Reconciliation(s) carrying the
214
- // outcome `cites-unknown-scenario` (and the evidence observation's locator, ruling Q5), verdict
215
- // ALWAYS out-of-balance. Grouped AFTER the scenario rows.
216
- //
217
- // `phantomIds` was ROUTED ABOVE (3F-2202) — this block only ASSEMBLES the rows. The scan that
218
- // stood here re-stated the citation index's four routing decisions verbatim, including the
219
- // fail-safe arms (a BROKEN credit mints no phantom row; an uncited credit is suspense, never
220
- // phantom); both now come from the one `postingOf` call.
221
110
  for (const scnId of citations.phantomIds) {
222
111
  const credits = citations.creditsFor(scnId);
223
- // ASKED, not decided (3F-2204).
224
112
  const items = citesUnknownScenarioReconciliationsOf(credits);
225
113
  rows.push({
226
114
  scnId,
227
115
  kind: "phantom-scenario",
228
- // ASKED, not restated (3F-2204, the close-gate read). Canon says a phantom row is ALWAYS
229
- // out-of-balance, and it is — BY CONSTRUCTION, because a phantom row exists only where a
230
- // credit minted it, so it always carries ≥1 `cites-unknown-scenario` item. Deriving the
231
- // verdict from the items keeps that a consequence of the rule rather than a constant asserted
232
- // beside it: a hardcoded verdict would go on reading `out-of-balance` even if the row ever
233
- // came to carry none, which is precisely the drift the verdict/items lock-step exists to stop.
234
116
  verdict: verdictFor(items),
235
- // The credits that CITE this invented scenario post to it — canon organises the ledger by
236
- // "real or INVENTED scenario", so a phantom account holds its postings like any other.
237
117
  evidenceObservations: [...credits],
238
- // A phantom account is MINTED BY A CREDIT — no scenario raises it, so nothing can have
239
- // been named of it. Its debit side is empty by construction (@SCN-LDG-023); the asymmetry
240
- // with the credit side is real, not an omission.
241
118
  evidenceObligations: [],
242
119
  qualityCheckReconciliations: items,
243
120
  reconcilingItems: items,
@@ -246,33 +123,15 @@ function phantomScenarioRows(citations) {
246
123
  }
247
124
  return rows;
248
125
  }
249
- /**
250
- * THE SUSPENSE ROW, assembled (@SCN-LDG-007) — nil rows or one. Extracted beside its siblings at
251
- * 3F-3472; what lands on it is `routeCredits`' decision, and unchanged.
252
- */
253
- function suspenseScenarioRows(brokenEvidence, uncited, noEvidenceObligationGuards) {
126
+ function suspenseScenarioRows(brokenEvidence, uncited, noEvidenceObligationFitnessFunctions) {
254
127
  const rows = [];
255
- // THE SUSPENSE ROW (@SCN-LDG-007) — every entry that names no scenario aggregates into ONE row
256
- // (sentinel scnId `(no scenario)`), grouped LAST, verdict always out-of-balance.
257
- //
258
- // Its three streams were ROUTED by the posting rule (3F-2202); their OUTCOMES and their ORDER
259
- // are the item concept's (3F-2204, `suspenseReconciliationsOf` — rule + rationale there). What
260
- // is decided HERE is only whether the row exists at all: suspense is minted by a credit landing
261
- // on it, so no credit means no row.
262
- if (brokenEvidence.length > 0 || uncited.length > 0 || noEvidenceObligationGuards.length > 0) {
263
- const items = suspenseReconciliationsOf(brokenEvidence, uncited, noEvidenceObligationGuards);
128
+ if (brokenEvidence.length > 0 || uncited.length > 0 || noEvidenceObligationFitnessFunctions.length > 0) {
129
+ const items = suspenseReconciliationsOf(brokenEvidence, uncited, noEvidenceObligationFitnessFunctions);
264
130
  rows.push({
265
- scnId: "(no scenario)",
131
+ scnId: CORE_MESSAGES[CoreMessage.SuspenseScenarioId],
266
132
  kind: "suspense-scenario",
267
- // ASKED, not restated — same reason as the phantom row. The `if` above is what makes this
268
- // out-of-balance by construction: the row exists only when a credit landed on it.
269
133
  verdict: verdictFor(items),
270
- // The credits citing NO scenario post here (@SCN-LDG-022) — the uncited, the broken (it
271
- // named no test, so it cites nothing whatever scnId it structurally carries), and the
272
- // no-evidence-obligation static checks. With the citation index this exhausts the credit stream.
273
- evidenceObservations: [...brokenEvidence, ...uncited, ...noEvidenceObligationGuards],
274
- // Suspense is MINTED BY A CREDIT too — it raises nothing, so it names nothing
275
- // (@SCN-LDG-023). It can therefore carry no Pending Item either.
134
+ evidenceObservations: [...brokenEvidence, ...uncited, ...noEvidenceObligationFitnessFunctions],
276
135
  evidenceObligations: [],
277
136
  qualityCheckReconciliations: items,
278
137
  reconcilingItems: items,
@@ -281,32 +140,15 @@ function suspenseScenarioRows(brokenEvidence, uncited, noEvidenceObligationGuard
281
140
  }
282
141
  return rows;
283
142
  }
284
- /**
285
- * THE UNCODED-WORK ROWS, assembled (@SCN-LDG-021). Extracted beside its siblings at 3F-3472.
286
- */
287
143
  function untaggedScenarioRows(untaggedScenarios) {
288
144
  const rows = [];
289
- // UNCODED WORK (@SCN-LDG-021) — a scenario carrying no @SCN. It is REAL and ORDINARY
290
- // (`kind: "scenario"`, never phantom or suspense: it exists, and cucumber runs it); what is
291
- // absent is the CODE its obligation needs in order to link to an observation. It gets its own
292
- // out-of-balance row so the verdict can see it at all.
293
- //
294
- // The item's outcome, its scenario-scope shape and the synthesised `{}` obligation it carries
295
- // are the item concept's (3F-2204, `noScnCodeReconciliations` — rule + rationale there).
296
145
  for (const name of untaggedScenarios) {
297
146
  const items = noScnCodeReconciliations();
298
147
  rows.push({
299
- // The scenario's NAME is its only handle — it has no code. (Precedent: the suspense row's
300
- // "(no scenario)" sentinel; `featureCodeOf` falls back to the raw id when it cannot parse.)
301
148
  scnId: name,
302
149
  kind: "scenario",
303
- // ASKED, not restated — `noScnCodeReconciliations` always returns the one `no-scn-code`
304
- // item, so the verdict follows from it rather than being asserted alongside it.
305
150
  verdict: verdictFor(items),
306
- // An untagged scenario carries no @SCN, so no credit can cite it — nothing can post here.
307
151
  evidenceObservations: [],
308
- // It reaches the ledger by NAME, carrying no parsed obligations — so it posts no debits
309
- // either (@SCN-LDG-023).
310
152
  evidenceObligations: [],
311
153
  qualityCheckReconciliations: items,
312
154
  reconcilingItems: items,
@@ -315,184 +157,47 @@ function untaggedScenarioRows(untaggedScenarios) {
315
157
  }
316
158
  return rows;
317
159
  }
318
- /**
319
- * THE ROWS A CONFIGURED ROW WITH NO SCENARIO EARNS, assembled (@SCN-LNT-027, 3F-3472).
320
- */
321
- function unspecifiedFitnessRowRows(unspecifiedFitnessRows) {
322
- const rows = [];
323
- // A CONFIGURED ROW THE CORPUS CARRIES NO SCENARIO FOR (@SCN-LNT-027, 3F-3472) — a check the
324
- // target's manifest declares and nothing in its corpus specifies. The obligations travel with
325
- // the scenarios, so such a row raised NOTHING: it was not `missing`, not phantom, not suspense,
326
- // and a tree carrying one reconciled balanced at exit 0 over a check declared and never written.
327
- // `features/fitness-checks.feature` promises the opposite in terms — "a row whose scenario the
328
- // corpus does not carry as an Evidence Obligation nothing observed" — and this is that promise.
329
- //
330
- // THE PRECEDENT IS UNCODED WORK, three lines up: a fact the ordinary obligation path cannot raise
331
- // reaches the ledger by a reading of its own and gets its own out-of-balance row so the verdict
332
- // can see it at all. There the CORPUS supplies a row the obligations cannot; here the MANIFEST
333
- // does.
334
- //
335
- // THE ROW IS NOT IN `scenarioIds`, WHICH IS WHAT MAKES IT UNDISCHARGEABLE. That set was built
336
- // from `scenarios` alone and is not widened here, so a credit citing this row's code still mints
337
- // a PHANTOM row (@SCN-LDG-006, @SCN-LNT-009) and a broken credit still lands on SUSPENSE — and
338
- // there is no route left by which any credit settles this debit. Synthesising a `Scenario` for
339
- // the row instead would have put it INTO that set, so a run whose CI step cited a declared-but-
340
- // unwritten check would reconcile BALANCED: a new false green, in the case the phantom row
341
- // already covers.
342
- for (const row of unspecifiedFitnessRows) {
343
- const items = unspecifiedRowReconciliations(row.characteristic);
344
- rows.push({
345
- scnId: row.scnId,
346
- kind: "scenario",
347
- // ASKED, not restated — the phantom and suspense rows' rule. `unspecifiedRowReconciliations`
348
- // always returns the one `missing` item, so the verdict follows from it.
349
- verdict: verdictFor(items),
350
- // No scenario carries this code, so the citation router posts no credit here — see above.
351
- evidenceObservations: [],
352
- // THE DEBIT IS REAL AND IS POSTED (@SCN-LDG-023). Unlike the untagged row above, which
353
- // reaches the ledger by name and names no check, this row names exactly one characteristic:
354
- // that is the obligation, and the manifest is what raised it.
355
- evidenceObligations: [...items.map((item) => item.evidenceObligation)],
356
- qualityCheckReconciliations: items,
357
- reconcilingItems: items,
358
- pendingItems: [],
359
- });
360
- }
361
- return rows;
362
- }
363
- export function reconcile(scenarios, evidenceObservations, untaggedScenarios = [],
364
- /**
365
- * The CONFIGURED ROWS the target's corpus carries no scenario for (@SCN-LNT-027, 3F-3472) — the
366
- * fourth population, minted below.
367
- *
368
- * IT DEFAULTS, WHERE THE TWO GATES BELOW DELIBERATELY DO NOT (3F-2875). A gate that defaults is a
369
- * gate a caller can forget and thereby CHANGE what the summary claims. This is a population, and
370
- * an omitted population is indistinguishable from an empty one in the only way that matters:
371
- * both mint no rows, and neither says anything about a manifest. Every caller that constructs
372
- * `Scenario[]` by hand has no manifest to read rows from, which is `untaggedScenarios`' own
373
- * argument for the default it already carries.
374
- */
375
- unspecifiedFitnessRows = []) {
376
- // THE OBLIGATION SIDE, prepared once — the three sets the posting rule and the outcome rules read.
377
- // Each is already the MODEL's own rule (never re-derived here), and each is built ONCE and
378
- // shared, so no two consumers can drift: `scenarioIds` is which scenarios exist,
379
- // and `evidenceObligationPairs` is what they name at each quality-check (3F-1858).
160
+ export function reconcile(scenarios, evidenceObservations, untaggedScenarios = []) {
380
161
  const scenarioIds = new Set(scenarios.map((d) => d.scnId));
381
- // The NAMED pairs — the model's rule, built ONCE and shared with the census (3F-1858).
382
- // Pending obligations are excluded: a pending obligation names nothing.
383
162
  const evidenceObligationPairs = evidenceObligationPairsOf(scenarios);
384
- // THE ONE ROUTING PASS (3F-2202) — asked of the posting rule, in one place, and read back as
385
- // four streams. What each of them then RECONCILES to is the fold's, below.
386
- const { citations, brokenEvidence, uncited, noEvidenceObligationGuards } = routeCredits(evidenceObservations, scenarioIds);
387
- // THE ORDER THE POPULATIONS ARE FILED IN IS THE LEDGER'S GROUPING, and it is the whole of what
388
- // this function decides now: the real scenario rows, then the phantom rows a credit invented,
389
- // then the one suspense row, then uncoded work, then the configured rows no scenario specifies.
163
+ const observations = inEvidenceOrder(evidenceObservations);
164
+ const { citations, brokenEvidence, uncited, noEvidenceObligationFitnessFunctions } = routeCredits(observations, scenarioIds);
390
165
  return {
391
166
  scenarioReconciliations: [
392
- ...scenarios.map((d) => scenarioRow(d, evidenceObservations, citations, evidenceObligationPairs)),
167
+ ...accountsByCode(scenarios).map((sharers) => sharers.length === 1
168
+ ? scenarioRow(sharers[0], observations, citations, evidenceObligationPairs)
169
+ : duplicateScnCodeRow(sharers, citations)),
393
170
  ...phantomScenarioRows(citations),
394
- ...suspenseScenarioRows(brokenEvidence, uncited, noEvidenceObligationGuards),
171
+ ...suspenseScenarioRows(brokenEvidence, uncited, noEvidenceObligationFitnessFunctions),
395
172
  ...untaggedScenarioRows(untaggedScenarios),
396
- ...unspecifiedFitnessRowRows(unspecifiedFitnessRows),
397
173
  ],
398
174
  };
399
175
  }
400
- /**
401
- * Materialise the summary totals from the balance — the aggregate the markdown's trial-balance
402
- * table derives, carried AS DATA so a later render-from-saved-JSON rebuilds it with zero
403
- * computation (the MATERIALISE ruling). THIN COMPOSITION (3F-2000): the LEDGER-PURE totals
404
- * (`scenarioReconciliations`, `evidenceObligations`, `pending`, `lint`) come from `ledgerTally`
405
- * — the Ledger owns its own totals ("the summary is its totals") — and are MERGED here with the
406
- * census pass-throughs (`testVolume`, `staticChecks`), which are NOT Ledger-pure. The keys are
407
- * assembled in DOCUMENT ORDER (…, testVolume?, staticChecks?, lint) so the JSON stays
408
- * byte-identical.
409
- */
410
- export function summaryOf(ledger, withRuntimeObservations, withStaticChecks) {
176
+ export function summaryOf(ledger, withRuntimeObservations, withFitnessFunctions) {
411
177
  const tally = ledgerTally(ledger);
412
- // The runtime evidence-observation totals are a FOLD OVER THE LEDGER (@SCN-RMD-003, 3F-2102) —
413
- // the Ledger counts its own posted credits; the parallel census is RETIRED. `testVolume` and the
414
- // re-homed `rawByRuntimeQualityCheck` derive from THIS fold. The `withRuntimeObservations` flag is
415
- // now only the GATE (is the runtime-observation axis in play?) — the caller no longer threads a
416
- // census object at all (the assembler folds the Ledger it already has).
417
178
  const observations = runtimeObservationTally(ledger);
418
- // The static-check totals are a FOLD OVER THE LEDGER too (3F-2118) — the Ledger counts its own
419
- // posted static-check credits; the parallel static census is RETIRED. `staticChecks` derives from
420
- // THIS fold. The `withStaticChecks` flag is now only the GATE (is the static-check axis in play?) —
421
- // the caller no longer threads a census object at all (the assembler folds the Ledger it holds).
422
- const staticChecks = staticObservationTally(ledger);
179
+ const fitnessFunctions = fitnessFunctionTally(ledger);
423
180
  return {
424
181
  scenarioReconciliations: tally.scenarioReconciliations,
425
182
  evidenceObligations: tally.evidenceObligations,
426
183
  pending: tally.pending,
427
- // The runtime test-volume kind-counts (RMD-005), materialised only when the evidenceObservations
428
- // read is threaded. The surface reads the fold's per-scenario-kind counts DIRECTLY (an identity
429
- // — the fold already groups by the scenario-kind the credit posted to). `onScenario` is every
430
- // credit posted to a scenario's account, which INCLUDES no-evidence-obligation as a subset
431
- // (matched-pair + no-evidence-obligation) — a DIFFERENT number from the reconciliation-matched-attributed
432
- // (the retired section's `attributed[]` sum, which excluded no-evidence-obligation). Keeping no-evidence-obligation
433
- // inside this count preserves the 3-termed identity `totalEvidenceObservations = onScenario +
434
- // onPhantomScenario + onSuspenseScenario` (no-evidence-obligation is a subset here, NOT a 4th identity term)
435
- // — the same shape the markdown's `### Test volume` line derives. `onPhantomScenario` /
436
- // `onSuspenseScenario` are the credits posted to the phantom / suspense accounts (suspense
437
- // includes `broken`).
438
184
  ...(withRuntimeObservations
439
185
  ? {
440
186
  testVolume: {
441
- // ASKED, not re-derived (3F-2231) — the Ledger's fold computes this total and now
442
- // returns it. The assembler summed the same record again for it, which is an adapter
443
- // owning a model number by copied arithmetic.
444
187
  totalEvidenceObservations: observations.total,
445
188
  onScenario: observations.onScenario,
446
189
  onPhantomScenario: observations.onPhantomScenario,
447
190
  onSuspenseScenario: observations.onSuspenseScenario,
448
191
  },
449
- // The re-homed raw per-quality-check headline (@SCN-RMD-003) — a materialised Ledger
450
- // fold, sibling of `testVolume` and present on the SAME gate.
451
192
  rawByRuntimeQualityCheck: observations.rawByRuntimeQualityCheck,
452
193
  }
453
194
  : {}),
454
- // The static-check kind-counts (RMD-005), materialised only when the static-check axis is in
455
- // play — now a FOLD OVER THE LEDGER (3F-2118, `staticObservationTally`), symmetric with the
456
- // runtime `testVolume` fold. The surface keeps the census shape exactly
457
- // (`{ runningStaticCheckCount, uncitedStaticCheckCount }`; named is implicit). The member detail
458
- // lives in the rows (named → each scenario's `@lint` sub-row; no-evidence-obligation → the suspense
459
- // row) — there is no census section (2.4).
460
- ...(withStaticChecks ? { staticChecks } : {}),
461
- // The static-only lint tally (RMD-005), a LEDGER-PURE derive from `ledgerTally` — LAST key in
462
- // document order (after the census pass-throughs).
463
- lint: tally.lint,
195
+ ...(withFitnessFunctions ? { fitnessFunctions } : {}),
196
+ fitness: tally.fitness,
464
197
  };
465
198
  }
466
- /**
467
- * BUILD the Evidence Reconciliation from the journal — THE FACTORY (3F-2233).
468
- *
469
- * Journal inputs in, whole concept out: the fold posts every entry into the Ledger, and the tally
470
- * totals it. They are ONE act, not two stages a caller sequences — which is the point. While
471
- * `reconcile()` and `buildSummary()` were separate, an adapter held a Ledger and derived its own
472
- * totals, so "the procedure can sum the wrong rows" (`why-ddd-for-spec-controller.md`) was a
473
- * REACHABLE state. Here there is no such seam: you cannot obtain a summary except from the concept
474
- * that owns the rows it counts.
475
- *
476
- * The two flags are GATES, not data — they state whether the runtime-observation and static-check
477
- * axes are in play for this run; the counts themselves are always folded from the Ledger.
478
- *
479
- * WHY THE TRAILING THREE ARE REQUIRED AND CARRY NO DEFAULTS (3F-2875). A gate that defaults to
480
- * `false` is a gate a caller can forget to state, and forgetting it CHANGES THE REPORT — the
481
- * summary then claims neither axis is in play, silently and plausibly. That is not a hypothetical:
482
- * when the defaults were removed, eleven call sites turned out to have been leaning on them. Every
483
- * one wanted exactly what it had been getting, so nothing moved; what changed is that each now says
484
- * so. A default is the wrong shape for an argument whose omission is indistinguishable from a
485
- * deliberate `false`.
486
- */
487
- export function evidenceReconciliationOf(scenarios, evidenceObservations, untaggedScenarios, withRuntimeObservations, withStaticChecks,
488
- /**
489
- * The CONFIGURED ROWS the corpus carries no scenario for (@SCN-LNT-027, 3F-3472) — passed
490
- * through to `reconcile`, which mints one row apiece. It defaults for the reason given there, and
491
- * that reason is NOT the trailing three's: a population omitted mints nothing and claims nothing,
492
- * where a gate omitted silently changes what the summary says is in play.
493
- */
494
- unspecifiedFitnessRows = []) {
495
- const ledger = reconcile(scenarios, evidenceObservations, untaggedScenarios, unspecifiedFitnessRows);
496
- return { summary: summaryOf(ledger, withRuntimeObservations, withStaticChecks), ledger };
199
+ export function evidenceReconciliationOf(scenarios, evidenceObservations, untaggedScenarios, withRuntimeObservations, withFitnessFunctions) {
200
+ const ledger = reconcile(scenarios, evidenceObservations, untaggedScenarios);
201
+ return { summary: summaryOf(ledger, withRuntimeObservations, withFitnessFunctions), ledger };
497
202
  }
498
203
  //# sourceMappingURL=evidenceReconciliation.js.map