@tiphys/kernel 0.0.0 → 0.2.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 (207) hide show
  1. package/AGENTS.md +663 -0
  2. package/LICENSE +202 -0
  3. package/assurance-modes.yaml +299 -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 +92 -0
  11. package/dist/src/adapters/load.d.ts +202 -0
  12. package/dist/src/adapters/load.js +440 -0
  13. package/dist/src/brief.d.ts +32 -0
  14. package/dist/src/brief.js +66 -0
  15. package/dist/src/checklists.d.ts +169 -0
  16. package/dist/src/checklists.js +310 -0
  17. package/dist/src/checks.d.ts +1539 -0
  18. package/dist/src/checks.js +5025 -0
  19. package/dist/src/cli.d.ts +9 -0
  20. package/dist/src/cli.js +68 -0
  21. package/dist/src/commands/brief.d.ts +92 -0
  22. package/dist/src/commands/brief.js +343 -0
  23. package/dist/src/commands/checklist.d.ts +42 -0
  24. package/dist/src/commands/checklist.js +168 -0
  25. package/dist/src/commands/cutover.d.ts +35 -0
  26. package/dist/src/commands/cutover.js +448 -0
  27. package/dist/src/commands/doctor.d.ts +264 -0
  28. package/dist/src/commands/doctor.js +1546 -0
  29. package/dist/src/commands/gates.d.ts +9 -0
  30. package/dist/src/commands/gates.js +360 -0
  31. package/dist/src/commands/init.d.ts +30 -0
  32. package/dist/src/commands/init.js +177 -0
  33. package/dist/src/commands/lock.d.ts +34 -0
  34. package/dist/src/commands/lock.js +340 -0
  35. package/dist/src/commands/mode.d.ts +40 -0
  36. package/dist/src/commands/mode.js +134 -0
  37. package/dist/src/commands/next.d.ts +130 -0
  38. package/dist/src/commands/next.js +597 -0
  39. package/dist/src/commands/plan.d.ts +20 -0
  40. package/dist/src/commands/plan.js +105 -0
  41. package/dist/src/commands/pool.d.ts +1 -0
  42. package/dist/src/commands/pool.js +139 -0
  43. package/dist/src/commands/resume.d.ts +1 -0
  44. package/dist/src/commands/resume.js +88 -0
  45. package/dist/src/commands/spawn.d.ts +1 -0
  46. package/dist/src/commands/spawn.js +195 -0
  47. package/dist/src/commands/status.d.ts +21 -0
  48. package/dist/src/commands/status.js +122 -0
  49. package/dist/src/commands/sync.d.ts +47 -0
  50. package/dist/src/commands/sync.js +341 -0
  51. package/dist/src/commands/teardown.d.ts +1 -0
  52. package/dist/src/commands/teardown.js +87 -0
  53. package/dist/src/commands/tuition.d.ts +21 -0
  54. package/dist/src/commands/tuition.js +218 -0
  55. package/dist/src/commands/validate.d.ts +78 -0
  56. package/dist/src/commands/validate.js +430 -0
  57. package/dist/src/commands/watch.d.ts +1 -0
  58. package/dist/src/commands/watch.js +172 -0
  59. package/dist/src/cutover.d.ts +584 -0
  60. package/dist/src/cutover.js +1444 -0
  61. package/dist/src/exclusion.d.ts +389 -0
  62. package/dist/src/exclusion.js +843 -0
  63. package/dist/src/exec/env.d.ts +278 -0
  64. package/dist/src/exec/env.js +334 -0
  65. package/dist/src/fleet.d.ts +223 -0
  66. package/dist/src/fleet.js +298 -0
  67. package/dist/src/gates/adapters/http-json.d.ts +5 -0
  68. package/dist/src/gates/adapters/http-json.js +283 -0
  69. package/dist/src/gates/adapters/migrations-command.d.ts +1 -0
  70. package/dist/src/gates/adapters/migrations-command.js +373 -0
  71. package/dist/src/gates/citations.d.ts +408 -0
  72. package/dist/src/gates/citations.js +1169 -0
  73. package/dist/src/gates/coverage.d.ts +375 -0
  74. package/dist/src/gates/coverage.js +836 -0
  75. package/dist/src/gates/credentials.d.ts +233 -0
  76. package/dist/src/gates/credentials.js +752 -0
  77. package/dist/src/gates/deploy.d.ts +1 -0
  78. package/dist/src/gates/deploy.js +33 -0
  79. package/dist/src/gates/gate-classes.d.ts +56 -0
  80. package/dist/src/gates/gate-classes.js +633 -0
  81. package/dist/src/gates/manifest.d.ts +99 -0
  82. package/dist/src/gates/manifest.js +208 -0
  83. package/dist/src/gates/merge-preconditions.d.ts +319 -0
  84. package/dist/src/gates/merge-preconditions.js +932 -0
  85. package/dist/src/gates/migrations.d.ts +1 -0
  86. package/dist/src/gates/migrations.js +36 -0
  87. package/dist/src/gates/pin.d.ts +114 -0
  88. package/dist/src/gates/pin.js +154 -0
  89. package/dist/src/gates/red-witness.d.ts +22 -0
  90. package/dist/src/gates/red-witness.js +482 -0
  91. package/dist/src/gates/release.d.ts +283 -0
  92. package/dist/src/gates/release.js +820 -0
  93. package/dist/src/gates/result.d.ts +116 -0
  94. package/dist/src/gates/result.js +91 -0
  95. package/dist/src/gates/run.d.ts +614 -0
  96. package/dist/src/gates/run.js +1614 -0
  97. package/dist/src/gates/schemas/citation-config.schema.json +59 -0
  98. package/dist/src/gates/schemas/coverage-config.schema.json +77 -0
  99. package/dist/src/gates/schemas/gate-manifest.schema.json +125 -0
  100. package/dist/src/gates/schemas/gate-result.schema.json +160 -0
  101. package/dist/src/gates/schemas/phase-declaration.schema.json +87 -0
  102. package/dist/src/gates/schemas/release-record.schema.json +119 -0
  103. package/dist/src/gates/schemas/verifier-config.schema.json +101 -0
  104. package/dist/src/gates/schemas/witness-spec.schema.json +110 -0
  105. package/dist/src/gates/scope.d.ts +131 -0
  106. package/dist/src/gates/scope.js +1018 -0
  107. package/dist/src/gates/suite.d.ts +217 -0
  108. package/dist/src/gates/suite.js +968 -0
  109. package/dist/src/gates/validate.d.ts +121 -0
  110. package/dist/src/gates/validate.js +414 -0
  111. package/dist/src/hooks.d.ts +84 -0
  112. package/dist/src/hooks.js +125 -0
  113. package/dist/src/index.d.ts +31 -0
  114. package/dist/src/index.js +30 -0
  115. package/dist/src/liveness.d.ts +321 -0
  116. package/dist/src/liveness.js +396 -0
  117. package/dist/src/lock.d.ts +256 -0
  118. package/dist/src/lock.js +792 -0
  119. package/dist/src/model-resolution.d.ts +159 -0
  120. package/dist/src/model-resolution.js +307 -0
  121. package/dist/src/modes.d.ts +149 -0
  122. package/dist/src/modes.js +258 -0
  123. package/dist/src/path-identity.d.ts +34 -0
  124. package/dist/src/path-identity.js +48 -0
  125. package/dist/src/plan.d.ts +73 -0
  126. package/dist/src/plan.js +153 -0
  127. package/dist/src/pool.d.ts +326 -0
  128. package/dist/src/pool.js +988 -0
  129. package/dist/src/roles.d.ts +461 -0
  130. package/dist/src/roles.js +776 -0
  131. package/dist/src/spawn.d.ts +482 -0
  132. package/dist/src/spawn.js +1003 -0
  133. package/dist/src/status.d.ts +116 -0
  134. package/dist/src/status.js +148 -0
  135. package/dist/src/task.d.ts +504 -0
  136. package/dist/src/task.js +307 -0
  137. package/dist/src/teardown.d.ts +39 -0
  138. package/dist/src/teardown.js +422 -0
  139. package/dist/src/tuition.d.ts +159 -0
  140. package/dist/src/tuition.js +311 -0
  141. package/dist/src/validate.d.ts +263 -0
  142. package/dist/src/validate.js +742 -0
  143. package/dist/src/version.d.ts +3 -0
  144. package/dist/src/version.js +38 -0
  145. package/dist/src/watcher.d.ts +275 -0
  146. package/dist/src/watcher.js +849 -0
  147. package/dist/src/witness/run.d.ts +299 -0
  148. package/dist/src/witness/run.js +1373 -0
  149. package/dist/src/witness/spec.d.ts +270 -0
  150. package/dist/src/witness/spec.js +475 -0
  151. package/dist/tsconfig.src.tsbuildinfo +1 -0
  152. package/gate-registry.yaml +526 -0
  153. package/gates.manifest.json +257 -0
  154. package/package.json +72 -3
  155. package/role-model-config.yaml +88 -0
  156. package/roles/README.md +128 -0
  157. package/roles/_shared-dispatch-contract.md +87 -0
  158. package/roles/adversarial-plan-reviewer.md +80 -0
  159. package/roles/clean-room-reviewer.md +140 -0
  160. package/roles/implementer.md +463 -0
  161. package/roles/investigator.md +138 -0
  162. package/roles/plan-writer.md +95 -0
  163. package/schemas/README.md +82 -0
  164. package/schemas/assurance-modes.schema.json +264 -0
  165. package/schemas/charter.schema.json +185 -0
  166. package/schemas/checklist.schema.json +114 -0
  167. package/schemas/cutover-state.schema.json +64 -0
  168. package/schemas/decision-record.schema.json +88 -0
  169. package/schemas/executor-record.schema.json +36 -0
  170. package/schemas/final-report.schema.json +90 -0
  171. package/schemas/finding.schema.json +106 -0
  172. package/schemas/gate-registry.schema.json +260 -0
  173. package/schemas/mechanism-index.schema.json +94 -0
  174. package/schemas/model-resolution.schema.json +362 -0
  175. package/schemas/plan.schema.json +300 -0
  176. package/schemas/report.schema.json +579 -0
  177. package/schemas/role-brief.schema.json +105 -0
  178. package/schemas/role-model-config.schema.json +90 -0
  179. package/schemas/status-line.schema.json +40 -0
  180. package/schemas/tuition.schema.json +191 -0
  181. package/schemas/verdict.schema.json +295 -0
  182. package/schemas/work-history.schema.json +183 -0
  183. package/schemas/write-bypass.schema.json +69 -0
  184. package/templates/charter.example.yaml +54 -0
  185. package/templates/decision-record.example.yaml +27 -0
  186. package/templates/final-report.example.yaml +80 -0
  187. package/templates/plan.example.yaml +87 -0
  188. package/templates/report.example.yaml +236 -0
  189. package/templates/warnings.md +74 -0
  190. package/templates/work-history.example.yaml +185 -0
  191. package/tuition/README.md +76 -0
  192. package/tuition/T-001.yaml +48 -0
  193. package/tuition/T-002.yaml +51 -0
  194. package/tuition/T-003.yaml +100 -0
  195. package/tuition/T-004.yaml +52 -0
  196. package/tuition/T-005.yaml +72 -0
  197. package/tuition/T-006.yaml +81 -0
  198. package/tuition/T-007.yaml +56 -0
  199. package/tuition/T-008.yaml +111 -0
  200. package/tuition/T-009.yaml +50 -0
  201. package/tuition/T-015.yaml +36 -0
  202. package/tuition/T-016.yaml +36 -0
  203. package/tuition/T-017.yaml +46 -0
  204. package/tuition/T-018.yaml +84 -0
  205. package/tuition/T-021.yaml +40 -0
  206. package/tuition/T-022.yaml +36 -0
  207. package/tuition/mechanism-index.yaml +256 -0
@@ -0,0 +1,375 @@
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 (`boundedExec`, below),
100
+ * 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. M4-P28 changed WHAT that
103
+ * bound measures, from elapsed wall clock to CPU work, because the first
104
+ * one reddened for well-behaved patterns whenever the machine was busy.
105
+ * CR-992 (overlapping kinds)
106
+ * is folded into the same fix because it is the same "a config string is
107
+ * trusted further than its syntax justifies" mechanism one property over.
108
+ */
109
+ /** One row of the `bucketKinds` config. */
110
+ export interface BucketKindConfig {
111
+ kind: string;
112
+ /** Regex source, compiled anchored `^(?:pattern)$` against a bucket value. */
113
+ pattern: string;
114
+ requiresNote: boolean;
115
+ }
116
+ export interface DocumentConfig {
117
+ path: string;
118
+ /** Regex source, compiled anchored, tested against a row's first cell. */
119
+ idPattern: string;
120
+ }
121
+ export interface CoverageTableConfig extends DocumentConfig {
122
+ /** 0-based cell index (cell 0 is the id) holding the bucket value. */
123
+ bucketColumn: number;
124
+ /** 0-based cell index holding the note. */
125
+ noteColumn: number;
126
+ }
127
+ export interface CoverageConfig {
128
+ inventory: DocumentConfig;
129
+ coverageTable: CoverageTableConfig;
130
+ bucketKinds: BucketKindConfig[];
131
+ /**
132
+ * CR-986: the exact expected count of DISTINCT inventory ids. Optional;
133
+ * when absent, no floor is enforced. A row deleted from BOTH documents
134
+ * leaves the two documents agreeing with each other, which every other
135
+ * check in this module is powerless against, so this is a config-stated
136
+ * anchor rather than a derived one.
137
+ */
138
+ expectedUnits?: number;
139
+ }
140
+ /**
141
+ * THE KERNEL CONFIG (kernel plan M2-P6 step 2): this repository's real
142
+ * pair and the four bucket kinds the plan states verbatim. It is a plain
143
+ * exported constant, not a checked-in data file, because M2-C-1's
144
+ * verification-first reading of the plan's files-to-touch list for this
145
+ * phase names the module, its schema and its test, and no committed
146
+ * configuration document alongside them; a `--config` flag (validated
147
+ * against `coverage-config.schema.json`) exists for a caller that wants a
148
+ * different pair, and every fixture test in `test/coverage-gate.test.ts`
149
+ * uses it rather than editing this constant.
150
+ */
151
+ export declare const KERNEL_COVERAGE_CONFIG: CoverageConfig;
152
+ /**
153
+ * THE SHARED EMPTINESS PREDICATE (fix round 1, CR-987).
154
+ *
155
+ * Before this round, `checkCoverage`'s note check compared a value already
156
+ * passed through `String.prototype.trim` (applied once, at extraction) to
157
+ * `""`, and `checkFindingOutcomeParity`'s outcome check compared a value
158
+ * that was never trimmed at all to `""`. Two definitions, only one of
159
+ * which trimmed, and NEITHER strips a zero-width character: `trim()`
160
+ * removes Unicode whitespace (which includes U+00A0 and U+FEFF) but not
161
+ * U+200B ZERO WIDTH SPACE, U+200C ZERO WIDTH NON-JOINER or U+200D ZERO
162
+ * WIDTH JOINER, so a cell or an outcome containing only one of those reads
163
+ * as non-empty to a bare `=== ""` comparison, trimmed or not. One
164
+ * predicate, called at every point this module asks "is this empty",
165
+ * closes both instances at once.
166
+ */
167
+ export declare function isEmptyCell(value: string): boolean;
168
+ /**
169
+ * THE VERDICT-PRODUCING BOUND IS CPU WORK, NOT ELAPSED TIME (M4-P28).
170
+ *
171
+ * What this constant replaces, and why the replacement is not a bigger
172
+ * number. Until this phase the bound was `REGEX_EXEC_TIMEOUT_MS = 250`,
173
+ * handed to `vm` as a WALL-CLOCK timeout, and exceeding it was reported as
174
+ * "did not complete within 250ms ... (possible catastrophic
175
+ * backtracking)". Elapsed wall time is complexity DIVIDED BY available
176
+ * CPU, so that condition did not test the property it named: it reddened
177
+ * whenever the machine was busy. Measured and recorded in
178
+ * delivery/verification/wall-clock-budgets-are-load-dependent.md:1, five
179
+ * independent witnesses hit it on six structurally different patterns,
180
+ * including `^(?:parked)$`, a doubly anchored literal with no quantifier,
181
+ * no character class and no alternation, which has nothing to backtrack
182
+ * over at any input length. One million executions of
183
+ * `^(?:R-[0-9]+[a-z]?)$` against `R-094a` take 154.2ms in total, so ONE is
184
+ * roughly 1.6 million times under the old budget; for one of them to
185
+ * exceed it the thread must be descheduled for a quarter of a second,
186
+ * which is a fact about the machine.
187
+ *
188
+ * Raising 250 to a larger number was considered and is REJECTED by the
189
+ * plan (delivery/plan/kernel-plan-m4.md:3784): it keeps the same
190
+ * instrument and only moves the load at which it lies.
191
+ *
192
+ * WHAT IS MEASURED NOW. `process.threadCpuUsage()` reports the CPU time
193
+ * this thread has consumed, in microseconds. Another process being busy
194
+ * does not add to it, because a descheduled thread consumes no CPU while
195
+ * it is not running. The regex runs on THIS thread inside
196
+ * `runInContext`, so the delta across that call is the work the regex
197
+ * did, and nothing else. On an interpreter without
198
+ * `process.threadCpuUsage` (added in Node 22.15; the package floor is 26)
199
+ * the fallback is `process.cpuUsage()`, which is process-wide and
200
+ * therefore an over-estimate, never an under-estimate, so the fallback
201
+ * cannot let a catastrophic pattern through.
202
+ */
203
+ export declare const REGEX_EXEC_CPU_BUDGET_MS = 250;
204
+ /**
205
+ * HOW LONG ONE ATTEMPT IS WILLING TO WAIT. This is PATIENCE, not a
206
+ * verdict: nothing is ever reported about a pattern because this elapsed.
207
+ * A wall-clock interrupt is still needed, because a catastrophic match
208
+ * never returns on its own and `vm`'s `timeout` is the only mechanism
209
+ * here that can stop one (v8 checks for the termination request during a
210
+ * regex match, not only between statements).
211
+ *
212
+ * It is set to TWICE the CPU budget so that a thread receiving at least
213
+ * half of one CPU reaches the budget inside a single attempt; measured on
214
+ * an unloaded box, `(a+)+b` against 30 a's consumes 230.6ms of CPU in a
215
+ * 252ms wall window, so a backstop equal to the budget would have needed
216
+ * a second attempt to reach a verdict it had nearly earned.
217
+ */
218
+ export declare const REGEX_EXEC_WALL_BACKSTOP_MS = 500;
219
+ /**
220
+ * How many interrupted attempts before this module admits it cannot
221
+ * reach a verdict. Each attempt doubles its own patience, and the CPU
222
+ * consumed ACCUMULATES across attempts, so a genuinely spinning pattern
223
+ * is still caught on a loaded machine (it burns CPU whenever it runs)
224
+ * while a benign one accumulates microseconds however often it is
225
+ * interrupted.
226
+ */
227
+ export declare const REGEX_EXEC_MAX_ATTEMPTS = 4;
228
+ /** The pattern did too much WORK. A verdict about the pattern. */
229
+ export declare class RegexBoundExceededError extends Error {
230
+ }
231
+ /**
232
+ * No verdict was reached: every attempt was interrupted before the
233
+ * pattern finished, and the CPU it consumed never came near the budget,
234
+ * which is the signature of a thread that is not being scheduled rather
235
+ * than one that is spinning. Under M2-C-3 a check that cannot reach a
236
+ * verdict reports ERROR, never a verdict it did not earn, so this is
237
+ * deliberately NOT a subclass of `RegexBoundExceededError`: the two must
238
+ * not be confusable by a `catch` or an `instanceof`.
239
+ */
240
+ export declare class RegexBudgetUndeterminedError extends Error {
241
+ }
242
+ /** Overrides for one call, used by the witness tests to stand in for a
243
+ * machine slower or busier than this one. Only the PATIENCE is varied
244
+ * there; the verdict instrument stays the CPU budget. */
245
+ export interface RegexExecBounds {
246
+ cpuBudgetMs?: number;
247
+ wallBackstopMs?: number;
248
+ maxAttempts?: number;
249
+ }
250
+ /**
251
+ * Execute `compiled.exec(value)` inside a v8 context, bounded by the CPU
252
+ * WORK it does rather than by the time it takes.
253
+ *
254
+ * One attempt runs the match with a wall-clock interrupt. Whatever
255
+ * happens, the CPU consumed by this thread during that call is added to a
256
+ * running total:
257
+ *
258
+ * - total CPU at or above the budget: the thread really did burn that
259
+ * much CPU on this one match, which no anchored non-backtracking
260
+ * pattern can do, so `RegexBoundExceededError` is thrown. A busy
261
+ * machine cannot cause this, because a descheduled thread accumulates
262
+ * no CPU.
263
+ * - the match finished: return its result, which is the only path that
264
+ * produces a match.
265
+ * - interrupted with the total still far below the budget: the machine
266
+ * was busy, not the pattern. Double the patience and try again.
267
+ *
268
+ * After `maxAttempts` interruptions with the CPU total still under
269
+ * budget, `RegexBudgetUndeterminedError` is thrown, naming the CPU
270
+ * consumed, the wall clock spent and the load average, so the record says
271
+ * what it observed instead of asserting something about the pattern.
272
+ *
273
+ * An error from `runInContext` that is NOT the timeout is also
274
+ * undetermined rather than a finding: the old code funnelled every throw
275
+ * into the catastrophic-backtracking message, which is the same
276
+ * substitution one cause over.
277
+ */
278
+ export declare function boundedExec(compiled: RegExp, value: string, bounds?: RegexExecBounds): RegExpExecArray | null;
279
+ /**
280
+ * A STATIC heuristic for the single most common catastrophic-backtracking
281
+ * shape (fix round 1, CR-991): a parenthesised group containing an
282
+ * unbounded quantifier (`+` or `*`), itself immediately followed by
283
+ * another unbounded quantifier, e.g. `(a+)+`, `(a*)+`, `(a+)*`. This is
284
+ * exactly the shape the round's own red witness constructs
285
+ * (`(a+)+b`). It is a heuristic, not a proof: it does not see every
286
+ * catastrophic shape (a documented residue below), which is why every
287
+ * EXECUTION is also bounded by `boundedExec` regardless of whether a
288
+ * pattern passes this check.
289
+ */
290
+ export declare function catastrophicShapeReason(pattern: string): string | undefined;
291
+ /**
292
+ * Validate one config-supplied pattern SOURCE before it is ever compiled
293
+ * into a live regex the gate will execute (fix round 1, CR-990/991):
294
+ * compiles cleanly, and does not match the static catastrophic-shape
295
+ * heuristic. `label` identifies which config field failed, so a config
296
+ * error names the field, not just "a pattern".
297
+ */
298
+ export declare function validatePatternSource(label: string, pattern: string): string | undefined;
299
+ /**
300
+ * Validate every config-supplied pattern this module will ever compile
301
+ * and execute: both `idPattern`s and every `bucketKinds[].pattern`. Run
302
+ * once per config, before the config is used, so a malformed or dangerous
303
+ * pattern is a named CONFIG error (a clean result record) rather than an
304
+ * uncaught exception with no record (CR-990) or an unbounded hang
305
+ * (CR-991).
306
+ */
307
+ export declare function validateConfigPatterns(config: CoverageConfig): string | undefined;
308
+ interface TableRow {
309
+ id: string;
310
+ cells: string[];
311
+ line: number;
312
+ }
313
+ /**
314
+ * Walk a document's lines and return every markdown-table row whose first
315
+ * cell matches `idPattern` (compiled anchored). Separator rows
316
+ * (`|---|:--:|---|`) are skipped by construction: every cell in one matches
317
+ * `/^:?-+:?$/`, which cannot also match a realistic id pattern, but the
318
+ * separator test is applied explicitly rather than relied on implicitly, so
319
+ * a future id pattern that could coincide does not silently absorb one.
320
+ * Every match against a config-supplied pattern is BOUNDED (fix round 1,
321
+ * `boundedExec`), so a hostile `idPattern` cannot hang this walk.
322
+ */
323
+ export declare function extractIdRows(text: string, idPattern: string): TableRow[];
324
+ export type CoverageFindingKind = "orphan" | "double-bucketed" | "phantom" | "unknown-kind" | "ambiguous-kind" | "missing-note" | "duplicate-inventory-id" | "expected-units-mismatch";
325
+ export interface CoverageFinding {
326
+ kind: CoverageFindingKind;
327
+ id: string;
328
+ detail: string;
329
+ }
330
+ export interface CoverageReport {
331
+ totalInventoryIds: number;
332
+ perKind: Record<string, number>;
333
+ perMilestone: Record<string, number>;
334
+ findings: CoverageFinding[];
335
+ }
336
+ /**
337
+ * The core check. Pure: takes the two documents' TEXT (already read through
338
+ * M2-C-6's guarded read at the call site) and the config, and returns a
339
+ * report with nothing hidden inside a status string.
340
+ */
341
+ export declare function checkCoverage(config: CoverageConfig, inventoryText: string, coverageTableText: string): CoverageReport;
342
+ /**
343
+ * R-089b, THE FINDING-TO-OUTCOME PARITY CONTRACT (M2-P6 step 4).
344
+ *
345
+ * Section 2 item 2 of the M2 plan states that the report contract does not
346
+ * exist yet, so this module defines its own input shape and M3's report
347
+ * schema must emit it or supersede it. The shape declared here:
348
+ *
349
+ * inventoryIds: string[] every id that must be resolved
350
+ * findings: { id: string; outcome: string }[] the report's rows
351
+ *
352
+ * A CONFORMING future report is a list of `{id, outcome}` pairs, one row
353
+ * per id, `outcome` a non-empty string. This function does not care what
354
+ * the outcome STRING says (that is a judgement M3 owns); it only checks
355
+ * that every id got exactly one non-empty say. Fix round 1 adds the
356
+ * direction the original version lacked: a report row whose id is not in
357
+ * the inventory at all (CR-988, a PHANTOM outcome, symmetric with
358
+ * `checkCoverage`'s phantom finding) is also named, not silently accepted.
359
+ */
360
+ export interface FindingOutcomeRow {
361
+ id: string;
362
+ outcome: string;
363
+ }
364
+ export interface FindingParityResult {
365
+ ok: boolean;
366
+ checked: number;
367
+ missing: string[];
368
+ duplicated: string[];
369
+ empty: string[];
370
+ /** CR-988: report rows whose id is absent from `inventoryIds`. */
371
+ phantom: string[];
372
+ }
373
+ export declare function checkFindingOutcomeParity(inventoryIds: string[], findings: FindingOutcomeRow[]): FindingParityResult;
374
+ export declare function main(argv: string[]): number;
375
+ export {};