@tiphys/kernel 0.0.0 → 0.1.0

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 (181) hide show
  1. package/AGENTS.md +611 -0
  2. package/LICENSE +202 -0
  3. package/assurance-modes.yaml +278 -0
  4. package/checklists/clean-room.yaml +325 -0
  5. package/checklists/env-failure-diagnosis.yaml +68 -0
  6. package/checklists/flake-playbook.yaml +68 -0
  7. package/checklists/hazard-review.yaml +144 -0
  8. package/checklists/plan-review.yaml +103 -0
  9. package/dist/bin/tiphys.d.ts +2 -0
  10. package/dist/bin/tiphys.js +14 -0
  11. package/dist/src/brief.d.ts +32 -0
  12. package/dist/src/brief.js +59 -0
  13. package/dist/src/checklists.d.ts +169 -0
  14. package/dist/src/checklists.js +310 -0
  15. package/dist/src/checks.d.ts +828 -0
  16. package/dist/src/checks.js +3314 -0
  17. package/dist/src/cli.d.ts +9 -0
  18. package/dist/src/cli.js +57 -0
  19. package/dist/src/commands/brief.d.ts +92 -0
  20. package/dist/src/commands/brief.js +320 -0
  21. package/dist/src/commands/checklist.d.ts +42 -0
  22. package/dist/src/commands/checklist.js +168 -0
  23. package/dist/src/commands/doctor.d.ts +35 -0
  24. package/dist/src/commands/doctor.js +605 -0
  25. package/dist/src/commands/gates.d.ts +9 -0
  26. package/dist/src/commands/gates.js +360 -0
  27. package/dist/src/commands/init.d.ts +30 -0
  28. package/dist/src/commands/init.js +128 -0
  29. package/dist/src/commands/lock.d.ts +1 -0
  30. package/dist/src/commands/lock.js +229 -0
  31. package/dist/src/commands/mode.d.ts +40 -0
  32. package/dist/src/commands/mode.js +134 -0
  33. package/dist/src/commands/plan.d.ts +20 -0
  34. package/dist/src/commands/plan.js +105 -0
  35. package/dist/src/commands/pool.d.ts +1 -0
  36. package/dist/src/commands/pool.js +128 -0
  37. package/dist/src/commands/spawn.d.ts +1 -0
  38. package/dist/src/commands/spawn.js +146 -0
  39. package/dist/src/commands/status.d.ts +19 -0
  40. package/dist/src/commands/status.js +120 -0
  41. package/dist/src/commands/teardown.d.ts +1 -0
  42. package/dist/src/commands/teardown.js +79 -0
  43. package/dist/src/commands/tuition.d.ts +21 -0
  44. package/dist/src/commands/tuition.js +218 -0
  45. package/dist/src/commands/validate.d.ts +78 -0
  46. package/dist/src/commands/validate.js +360 -0
  47. package/dist/src/commands/watch.d.ts +1 -0
  48. package/dist/src/commands/watch.js +172 -0
  49. package/dist/src/exec/env.d.ts +128 -0
  50. package/dist/src/exec/env.js +190 -0
  51. package/dist/src/fleet.d.ts +51 -0
  52. package/dist/src/fleet.js +80 -0
  53. package/dist/src/gates/adapters/http-json.d.ts +5 -0
  54. package/dist/src/gates/adapters/http-json.js +283 -0
  55. package/dist/src/gates/adapters/migrations-command.d.ts +1 -0
  56. package/dist/src/gates/adapters/migrations-command.js +373 -0
  57. package/dist/src/gates/citations.d.ts +408 -0
  58. package/dist/src/gates/citations.js +1163 -0
  59. package/dist/src/gates/coverage.d.ts +284 -0
  60. package/dist/src/gates/coverage.js +701 -0
  61. package/dist/src/gates/credentials.d.ts +74 -0
  62. package/dist/src/gates/credentials.js +533 -0
  63. package/dist/src/gates/deploy.d.ts +1 -0
  64. package/dist/src/gates/deploy.js +33 -0
  65. package/dist/src/gates/manifest.d.ts +99 -0
  66. package/dist/src/gates/manifest.js +208 -0
  67. package/dist/src/gates/migrations.d.ts +1 -0
  68. package/dist/src/gates/migrations.js +36 -0
  69. package/dist/src/gates/pin.d.ts +114 -0
  70. package/dist/src/gates/pin.js +154 -0
  71. package/dist/src/gates/red-witness.d.ts +22 -0
  72. package/dist/src/gates/red-witness.js +390 -0
  73. package/dist/src/gates/release.d.ts +283 -0
  74. package/dist/src/gates/release.js +820 -0
  75. package/dist/src/gates/result.d.ts +116 -0
  76. package/dist/src/gates/result.js +91 -0
  77. package/dist/src/gates/run.d.ts +566 -0
  78. package/dist/src/gates/run.js +1536 -0
  79. package/dist/src/gates/schemas/citation-config.schema.json +59 -0
  80. package/dist/src/gates/schemas/coverage-config.schema.json +77 -0
  81. package/dist/src/gates/schemas/gate-manifest.schema.json +125 -0
  82. package/dist/src/gates/schemas/gate-result.schema.json +160 -0
  83. package/dist/src/gates/schemas/phase-declaration.schema.json +42 -0
  84. package/dist/src/gates/schemas/release-record.schema.json +119 -0
  85. package/dist/src/gates/schemas/verifier-config.schema.json +101 -0
  86. package/dist/src/gates/schemas/witness-spec.schema.json +110 -0
  87. package/dist/src/gates/scope.d.ts +131 -0
  88. package/dist/src/gates/scope.js +1018 -0
  89. package/dist/src/gates/suite.d.ts +217 -0
  90. package/dist/src/gates/suite.js +927 -0
  91. package/dist/src/gates/validate.d.ts +121 -0
  92. package/dist/src/gates/validate.js +414 -0
  93. package/dist/src/hooks.d.ts +32 -0
  94. package/dist/src/hooks.js +62 -0
  95. package/dist/src/liveness.d.ts +321 -0
  96. package/dist/src/liveness.js +396 -0
  97. package/dist/src/lock.d.ts +178 -0
  98. package/dist/src/lock.js +500 -0
  99. package/dist/src/modes.d.ts +149 -0
  100. package/dist/src/modes.js +258 -0
  101. package/dist/src/path-identity.d.ts +2 -0
  102. package/dist/src/path-identity.js +10 -0
  103. package/dist/src/plan.d.ts +73 -0
  104. package/dist/src/plan.js +153 -0
  105. package/dist/src/pool.d.ts +130 -0
  106. package/dist/src/pool.js +721 -0
  107. package/dist/src/roles.d.ts +430 -0
  108. package/dist/src/roles.js +734 -0
  109. package/dist/src/spawn.d.ts +177 -0
  110. package/dist/src/spawn.js +332 -0
  111. package/dist/src/status.d.ts +91 -0
  112. package/dist/src/status.js +119 -0
  113. package/dist/src/task.d.ts +264 -0
  114. package/dist/src/task.js +305 -0
  115. package/dist/src/teardown.d.ts +32 -0
  116. package/dist/src/teardown.js +314 -0
  117. package/dist/src/tuition.d.ts +159 -0
  118. package/dist/src/tuition.js +311 -0
  119. package/dist/src/validate.d.ts +230 -0
  120. package/dist/src/validate.js +732 -0
  121. package/dist/src/version.d.ts +3 -0
  122. package/dist/src/version.js +38 -0
  123. package/dist/src/watcher.d.ts +275 -0
  124. package/dist/src/watcher.js +859 -0
  125. package/dist/src/witness/run.d.ts +274 -0
  126. package/dist/src/witness/run.js +1327 -0
  127. package/dist/src/witness/spec.d.ts +102 -0
  128. package/dist/src/witness/spec.js +253 -0
  129. package/dist/tsconfig.src.tsbuildinfo +1 -0
  130. package/gate-registry.yaml +390 -0
  131. package/gates.manifest.json +195 -0
  132. package/package.json +57 -3
  133. package/role-model-config.yaml +88 -0
  134. package/roles/README.md +128 -0
  135. package/roles/_shared-dispatch-contract.md +87 -0
  136. package/roles/adversarial-plan-reviewer.md +80 -0
  137. package/roles/clean-room-reviewer.md +140 -0
  138. package/roles/implementer.md +460 -0
  139. package/roles/investigator.md +138 -0
  140. package/roles/plan-writer.md +95 -0
  141. package/schemas/README.md +81 -0
  142. package/schemas/assurance-modes.schema.json +264 -0
  143. package/schemas/charter.schema.json +166 -0
  144. package/schemas/checklist.schema.json +114 -0
  145. package/schemas/decision-record.schema.json +88 -0
  146. package/schemas/final-report.schema.json +90 -0
  147. package/schemas/finding.schema.json +106 -0
  148. package/schemas/gate-registry.schema.json +260 -0
  149. package/schemas/mechanism-index.schema.json +94 -0
  150. package/schemas/plan.schema.json +300 -0
  151. package/schemas/report.schema.json +579 -0
  152. package/schemas/role-brief.schema.json +105 -0
  153. package/schemas/role-model-config.schema.json +90 -0
  154. package/schemas/status-line.schema.json +40 -0
  155. package/schemas/tuition.schema.json +191 -0
  156. package/schemas/verdict.schema.json +289 -0
  157. package/schemas/work-history.schema.json +183 -0
  158. package/templates/charter.example.yaml +54 -0
  159. package/templates/decision-record.example.yaml +27 -0
  160. package/templates/final-report.example.yaml +80 -0
  161. package/templates/plan.example.yaml +87 -0
  162. package/templates/report.example.yaml +236 -0
  163. package/templates/warnings.md +74 -0
  164. package/templates/work-history.example.yaml +185 -0
  165. package/tuition/README.md +76 -0
  166. package/tuition/T-001.yaml +48 -0
  167. package/tuition/T-002.yaml +51 -0
  168. package/tuition/T-003.yaml +100 -0
  169. package/tuition/T-004.yaml +52 -0
  170. package/tuition/T-005.yaml +72 -0
  171. package/tuition/T-006.yaml +81 -0
  172. package/tuition/T-007.yaml +56 -0
  173. package/tuition/T-008.yaml +111 -0
  174. package/tuition/T-009.yaml +50 -0
  175. package/tuition/T-015.yaml +36 -0
  176. package/tuition/T-016.yaml +36 -0
  177. package/tuition/T-017.yaml +46 -0
  178. package/tuition/T-018.yaml +84 -0
  179. package/tuition/T-021.yaml +40 -0
  180. package/tuition/T-022.yaml +36 -0
  181. package/tuition/mechanism-index.yaml +256 -0
@@ -0,0 +1,284 @@
1
+ /**
2
+ * THE COVERAGE CHECKER (kernel plan M2, M2-P6).
3
+ *
4
+ * "No orphans" as a check with an exit code: every id in an INVENTORY
5
+ * document lands in exactly one row of a COVERAGE-TABLE document, a bucket
6
+ * value that does not match a declared kind is named rather than silently
7
+ * uncounted, a bucket kind that requires a note (`parked`) is red when the
8
+ * note is empty, and a coverage-table row whose id is absent from the
9
+ * inventory (a renumbering) is named as phantom coverage.
10
+ *
11
+ * HAZARD CLASS (T-007, M2-D-18): arithmetic over two documents that can
12
+ * drift, where a total can add up while a row is lost. Named defenses,
13
+ * each tied to a criterion:
14
+ *
15
+ * - a renumbering that leaves a bucket row pointing at a dead id: the
16
+ * PHANTOM check (every coverage-table row's id is required to be in
17
+ * the inventory), independent of the orphan check (every inventory id
18
+ * is required to have a coverage-table row).
19
+ * - a duplicated id INSIDE the inventory document itself (fix round 1,
20
+ * CR-985): counted once as a unit is not the same claim as "appears
21
+ * once in the text", so `units` is the count of DISTINCT ids and a
22
+ * repeated id is its own named finding (`duplicate-inventory-id`),
23
+ * symmetric with the coverage side's `double-bucketed` check.
24
+ * **CORRECTED CLAIM (fix round 1): an earlier revision of this comment
25
+ * said the orphan and phantom checks "scan in opposite directions on
26
+ * purpose, so neither can compensate for the other's blind spot." That
27
+ * was false for cardinality: a duplicated inventory id defeated BOTH
28
+ * of them (it is not an orphan, because a coverage row exists; it is
29
+ * not phantom, because the id is genuinely in the inventory), and
30
+ * inflated every count while staying green. The true property is
31
+ * narrower: orphan and phantom together catch every EXISTENCE
32
+ * mismatch (an id present on one side and not the other); cardinality
33
+ * mismatches (an id present more than once on one side) need the
34
+ * separate check named above, and a row lost from BOTH documents
35
+ * needs the `expectedUnits` check below, because the two documents
36
+ * still agree with each other in that case and existence/cardinality
37
+ * checks over agreeing documents find nothing.
38
+ * - a row lost from BOTH documents (fix round 1, CR-986): the previous
39
+ * bullet's residue. An optional `expectedUnits` config field states
40
+ * the anchor no arithmetic over the two documents alone can produce;
41
+ * a computed unit count that does not equal it is the
42
+ * `expected-units-mismatch` finding. This repository's real config
43
+ * sets it to 115 (kernel plan v1 Appendix A's stated total).
44
+ * - a bucket value matching no declared kind, silently uncounted: every
45
+ * bucket-kind pattern is compiled ANCHORED (`^(?:pattern)$`) and tested
46
+ * against the whole cell; a value none of them match is the
47
+ * `unknown-kind` finding, named by id and value, never absorbed into a
48
+ * nearby kind by a partial match.
49
+ * - a bucket value matching MORE than one declared kind (fix round 1,
50
+ * CR-992): resolved by silent first-match-wins until this round; now
51
+ * the `ambiguous-kind` finding, naming every kind that matched, rather
52
+ * than one kind's `requiresNote` silently never being consulted.
53
+ * - an empty inventory producing a green with nothing examined: `units`
54
+ * is always the number of DISTINCT ids the inventory actually
55
+ * produced, and this module never constructs a `GateResult` except
56
+ * through `makeGateResult`, whose M2-C-2 rewrite turns a
57
+ * green-with-zero-units record into `error` with `vacuous: true`.
58
+ * This gate does not duplicate that rule; it relies on the shared
59
+ * constructor the way M2-C-6 requires reuse of `classifyEntry` rather
60
+ * than a second implementation of "may this path be read".
61
+ * - a milestone extraction that cannot produce the totals the plan
62
+ * states: the milestone view and the kind view are two INDEPENDENT
63
+ * tallies computed from the same classification pass (`perKind` keyed
64
+ * by the bucket kind's name, `perMilestone` keyed by the pattern's
65
+ * first capture group when present, else by the kind's own name), so
66
+ * the two views can be compared against each other and against the
67
+ * plan's stated totals rather than one being asserted to imply the
68
+ * other.
69
+ * - a finding whose outcome cell is present but empty:
70
+ * `checkFindingOutcomeParity` treats an empty outcome as a named
71
+ * failure distinct from a missing row, so "the row exists" and "the
72
+ * row says something" are not conflated. Fix round 1, CR-987: "empty"
73
+ * is ONE shared predicate (`isEmptyCell`, below), applied wherever a
74
+ * note or an outcome is tested, so whitespace-only and zero-width-only
75
+ * content (U+200B, which `String.prototype.trim` does not strip) is
76
+ * empty on both sides rather than only where a trim happened to run.
77
+ * - a finding-to-outcome report carrying a PHANTOM outcome (fix round 1,
78
+ * CR-988): `checkFindingOutcomeParity` used to scan inventory ids
79
+ * only, so a report row for an id absent from the inventory (the
80
+ * renumbering shape the coverage side's phantom check exists for) was
81
+ * silently accepted. Now symmetric: a phantom outcome row is named.
82
+ *
83
+ * M2-C-6: the inventory path, the coverage-table path and an optional
84
+ * `--config` document are all supplied by configuration and none of them
85
+ * is a path this module created, so every read goes through
86
+ * `readRegularFileIfPresent` (which itself routes through `classifyEntry`),
87
+ * never a bare `readFileSync`. A named pipe at any of the three paths is
88
+ * `error` naming the path and the observed type, and this module never
89
+ * blocks on one. Fix round 1, CR-989: the same discipline now applies to
90
+ * the WRITE side inside the evidence directory (`counts.json`): a refused
91
+ * or failed write there used to be computed and discarded, leaving a
92
+ * silent green with empty evidence; it is now loud and makes the gate
93
+ * `error`, the same way a refused result-path write already did.
94
+ *
95
+ * Fix round 1 also closes the REGEX MECHANISM (CR-990/991/992): every
96
+ * config-supplied pattern (both `idPattern`s and every `bucketKinds[].pattern`)
97
+ * is VALIDATED (compiles, and is rejected if it matches a known
98
+ * catastrophic-backtracking shape) before it is ever executed, and every
99
+ * EXECUTION of a config-supplied pattern is BOUNDED by a wall-clock timeout
100
+ * (`boundedExec`, below), so neither a malformed pattern (CR-990, used to
101
+ * throw with no result record written) nor a ReDoS pattern (CR-991, used
102
+ * to hang indefinitely) can defeat this gate; CR-992 (overlapping kinds)
103
+ * is folded into the same fix because it is the same "a config string is
104
+ * trusted further than its syntax justifies" mechanism one property over.
105
+ */
106
+ /** One row of the `bucketKinds` config. */
107
+ export interface BucketKindConfig {
108
+ kind: string;
109
+ /** Regex source, compiled anchored `^(?:pattern)$` against a bucket value. */
110
+ pattern: string;
111
+ requiresNote: boolean;
112
+ }
113
+ export interface DocumentConfig {
114
+ path: string;
115
+ /** Regex source, compiled anchored, tested against a row's first cell. */
116
+ idPattern: string;
117
+ }
118
+ export interface CoverageTableConfig extends DocumentConfig {
119
+ /** 0-based cell index (cell 0 is the id) holding the bucket value. */
120
+ bucketColumn: number;
121
+ /** 0-based cell index holding the note. */
122
+ noteColumn: number;
123
+ }
124
+ export interface CoverageConfig {
125
+ inventory: DocumentConfig;
126
+ coverageTable: CoverageTableConfig;
127
+ bucketKinds: BucketKindConfig[];
128
+ /**
129
+ * CR-986: the exact expected count of DISTINCT inventory ids. Optional;
130
+ * when absent, no floor is enforced. A row deleted from BOTH documents
131
+ * leaves the two documents agreeing with each other, which every other
132
+ * check in this module is powerless against, so this is a config-stated
133
+ * anchor rather than a derived one.
134
+ */
135
+ expectedUnits?: number;
136
+ }
137
+ /**
138
+ * THE KERNEL CONFIG (kernel plan M2-P6 step 2): this repository's real
139
+ * pair and the four bucket kinds the plan states verbatim. It is a plain
140
+ * exported constant, not a checked-in data file, because M2-C-1's
141
+ * verification-first reading of the plan's files-to-touch list for this
142
+ * phase names the module, its schema and its test, and no committed
143
+ * configuration document alongside them; a `--config` flag (validated
144
+ * against `coverage-config.schema.json`) exists for a caller that wants a
145
+ * different pair, and every fixture test in `test/coverage-gate.test.ts`
146
+ * uses it rather than editing this constant.
147
+ */
148
+ export declare const KERNEL_COVERAGE_CONFIG: CoverageConfig;
149
+ /**
150
+ * THE SHARED EMPTINESS PREDICATE (fix round 1, CR-987).
151
+ *
152
+ * Before this round, `checkCoverage`'s note check compared a value already
153
+ * passed through `String.prototype.trim` (applied once, at extraction) to
154
+ * `""`, and `checkFindingOutcomeParity`'s outcome check compared a value
155
+ * that was never trimmed at all to `""`. Two definitions, only one of
156
+ * which trimmed, and NEITHER strips a zero-width character: `trim()`
157
+ * removes Unicode whitespace (which includes U+00A0 and U+FEFF) but not
158
+ * U+200B ZERO WIDTH SPACE, U+200C ZERO WIDTH NON-JOINER or U+200D ZERO
159
+ * WIDTH JOINER, so a cell or an outcome containing only one of those reads
160
+ * as non-empty to a bare `=== ""` comparison, trimmed or not. One
161
+ * predicate, called at every point this module asks "is this empty",
162
+ * closes both instances at once.
163
+ */
164
+ export declare function isEmptyCell(value: string): boolean;
165
+ /** Wall-clock bound on one regex execution. Measured (this round): a real
166
+ * catastrophic pattern, `(a+)+b` against 30 a's, which never returns on
167
+ * its own, is interrupted within 251-267ms under this mechanism; a safe
168
+ * pattern executes in under a millisecond, so the bound is not on the
169
+ * critical path for any pattern this repository's own config uses. */
170
+ export declare const REGEX_EXEC_TIMEOUT_MS = 250;
171
+ export declare class RegexBoundExceededError extends Error {
172
+ }
173
+ /**
174
+ * Execute `compiled.exec(value)` inside a v8 context with a wall-clock
175
+ * timeout. `node:vm`'s `timeout` option interrupts synchronous JavaScript
176
+ * execution, INCLUDING regex backtracking (v8 checks for the termination
177
+ * request during a regex match, not only between statements), which is
178
+ * why this bound can stop a hung `.exec()` where a plain try/catch around
179
+ * a synchronous call cannot: a catastrophic match never throws on its
180
+ * own, it simply never returns. Measured directly (this round): the same
181
+ * `(a+)+b` pattern against inputs of length 18 through 40 completes in
182
+ * under 40ms up to length 22, then 63ms, 302ms, and is interrupted at the
183
+ * 250ms bound from length 26 onward, rather than running to the multi-
184
+ * second and then multi-minute times the unbounded engine produces at
185
+ * length 26 and 40.
186
+ */
187
+ export declare function boundedExec(compiled: RegExp, value: string): RegExpExecArray | null;
188
+ /**
189
+ * A STATIC heuristic for the single most common catastrophic-backtracking
190
+ * shape (fix round 1, CR-991): a parenthesised group containing an
191
+ * unbounded quantifier (`+` or `*`), itself immediately followed by
192
+ * another unbounded quantifier, e.g. `(a+)+`, `(a*)+`, `(a+)*`. This is
193
+ * exactly the shape the round's own red witness constructs
194
+ * (`(a+)+b`). It is a heuristic, not a proof: it does not see every
195
+ * catastrophic shape (a documented residue below), which is why every
196
+ * EXECUTION is also bounded by `boundedExec` regardless of whether a
197
+ * pattern passes this check.
198
+ */
199
+ export declare function catastrophicShapeReason(pattern: string): string | undefined;
200
+ /**
201
+ * Validate one config-supplied pattern SOURCE before it is ever compiled
202
+ * into a live regex the gate will execute (fix round 1, CR-990/991):
203
+ * compiles cleanly, and does not match the static catastrophic-shape
204
+ * heuristic. `label` identifies which config field failed, so a config
205
+ * error names the field, not just "a pattern".
206
+ */
207
+ export declare function validatePatternSource(label: string, pattern: string): string | undefined;
208
+ /**
209
+ * Validate every config-supplied pattern this module will ever compile
210
+ * and execute: both `idPattern`s and every `bucketKinds[].pattern`. Run
211
+ * once per config, before the config is used, so a malformed or dangerous
212
+ * pattern is a named CONFIG error (a clean result record) rather than an
213
+ * uncaught exception with no record (CR-990) or an unbounded hang
214
+ * (CR-991).
215
+ */
216
+ export declare function validateConfigPatterns(config: CoverageConfig): string | undefined;
217
+ interface TableRow {
218
+ id: string;
219
+ cells: string[];
220
+ line: number;
221
+ }
222
+ /**
223
+ * Walk a document's lines and return every markdown-table row whose first
224
+ * cell matches `idPattern` (compiled anchored). Separator rows
225
+ * (`|---|:--:|---|`) are skipped by construction: every cell in one matches
226
+ * `/^:?-+:?$/`, which cannot also match a realistic id pattern, but the
227
+ * separator test is applied explicitly rather than relied on implicitly, so
228
+ * a future id pattern that could coincide does not silently absorb one.
229
+ * Every match against a config-supplied pattern is BOUNDED (fix round 1,
230
+ * `boundedExec`), so a hostile `idPattern` cannot hang this walk.
231
+ */
232
+ export declare function extractIdRows(text: string, idPattern: string): TableRow[];
233
+ export type CoverageFindingKind = "orphan" | "double-bucketed" | "phantom" | "unknown-kind" | "ambiguous-kind" | "missing-note" | "duplicate-inventory-id" | "expected-units-mismatch";
234
+ export interface CoverageFinding {
235
+ kind: CoverageFindingKind;
236
+ id: string;
237
+ detail: string;
238
+ }
239
+ export interface CoverageReport {
240
+ totalInventoryIds: number;
241
+ perKind: Record<string, number>;
242
+ perMilestone: Record<string, number>;
243
+ findings: CoverageFinding[];
244
+ }
245
+ /**
246
+ * The core check. Pure: takes the two documents' TEXT (already read through
247
+ * M2-C-6's guarded read at the call site) and the config, and returns a
248
+ * report with nothing hidden inside a status string.
249
+ */
250
+ export declare function checkCoverage(config: CoverageConfig, inventoryText: string, coverageTableText: string): CoverageReport;
251
+ /**
252
+ * R-089b, THE FINDING-TO-OUTCOME PARITY CONTRACT (M2-P6 step 4).
253
+ *
254
+ * Section 2 item 2 of the M2 plan states that the report contract does not
255
+ * exist yet, so this module defines its own input shape and M3's report
256
+ * schema must emit it or supersede it. The shape declared here:
257
+ *
258
+ * inventoryIds: string[] every id that must be resolved
259
+ * findings: { id: string; outcome: string }[] the report's rows
260
+ *
261
+ * A CONFORMING future report is a list of `{id, outcome}` pairs, one row
262
+ * per id, `outcome` a non-empty string. This function does not care what
263
+ * the outcome STRING says (that is a judgement M3 owns); it only checks
264
+ * that every id got exactly one non-empty say. Fix round 1 adds the
265
+ * direction the original version lacked: a report row whose id is not in
266
+ * the inventory at all (CR-988, a PHANTOM outcome, symmetric with
267
+ * `checkCoverage`'s phantom finding) is also named, not silently accepted.
268
+ */
269
+ export interface FindingOutcomeRow {
270
+ id: string;
271
+ outcome: string;
272
+ }
273
+ export interface FindingParityResult {
274
+ ok: boolean;
275
+ checked: number;
276
+ missing: string[];
277
+ duplicated: string[];
278
+ empty: string[];
279
+ /** CR-988: report rows whose id is absent from `inventoryIds`. */
280
+ phantom: string[];
281
+ }
282
+ export declare function checkFindingOutcomeParity(inventoryIds: string[], findings: FindingOutcomeRow[]): FindingParityResult;
283
+ export declare function main(argv: string[]): number;
284
+ export {};