@descryy/ir 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 (94) hide show
  1. package/LICENSE +21 -0
  2. package/dist/capabilities.d.ts +83 -0
  3. package/dist/capabilities.d.ts.map +1 -0
  4. package/dist/capabilities.js +91 -0
  5. package/dist/capabilities.js.map +1 -0
  6. package/dist/conformance/bind.d.ts +82 -0
  7. package/dist/conformance/bind.d.ts.map +1 -0
  8. package/dist/conformance/bind.js +157 -0
  9. package/dist/conformance/bind.js.map +1 -0
  10. package/dist/conformance/cli.d.ts +25 -0
  11. package/dist/conformance/cli.d.ts.map +1 -0
  12. package/dist/conformance/cli.js +137 -0
  13. package/dist/conformance/cli.js.map +1 -0
  14. package/dist/conformance/compare.d.ts +74 -0
  15. package/dist/conformance/compare.d.ts.map +1 -0
  16. package/dist/conformance/compare.js +140 -0
  17. package/dist/conformance/compare.js.map +1 -0
  18. package/dist/conformance/compose.d.ts +79 -0
  19. package/dist/conformance/compose.d.ts.map +1 -0
  20. package/dist/conformance/compose.js +167 -0
  21. package/dist/conformance/compose.js.map +1 -0
  22. package/dist/conformance/golden.d.ts +110 -0
  23. package/dist/conformance/golden.d.ts.map +1 -0
  24. package/dist/conformance/golden.js +252 -0
  25. package/dist/conformance/golden.js.map +1 -0
  26. package/dist/conformance/index.d.ts +26 -0
  27. package/dist/conformance/index.d.ts.map +1 -0
  28. package/dist/conformance/index.js +18 -0
  29. package/dist/conformance/index.js.map +1 -0
  30. package/dist/conformance/manifest-check.d.ts +78 -0
  31. package/dist/conformance/manifest-check.d.ts.map +1 -0
  32. package/dist/conformance/manifest-check.js +149 -0
  33. package/dist/conformance/manifest-check.js.map +1 -0
  34. package/dist/conformance/manifest.d.ts +83 -0
  35. package/dist/conformance/manifest.d.ts.map +1 -0
  36. package/dist/conformance/manifest.js +158 -0
  37. package/dist/conformance/manifest.js.map +1 -0
  38. package/dist/conformance/report.d.ts +14 -0
  39. package/dist/conformance/report.d.ts.map +1 -0
  40. package/dist/conformance/report.js +87 -0
  41. package/dist/conformance/report.js.map +1 -0
  42. package/dist/conformance/run.d.ts +172 -0
  43. package/dist/conformance/run.d.ts.map +1 -0
  44. package/dist/conformance/run.js +626 -0
  45. package/dist/conformance/run.js.map +1 -0
  46. package/dist/contracts.d.ts +198 -0
  47. package/dist/contracts.d.ts.map +1 -0
  48. package/dist/contracts.js +10 -0
  49. package/dist/contracts.js.map +1 -0
  50. package/dist/contradiction.d.ts +112 -0
  51. package/dist/contradiction.d.ts.map +1 -0
  52. package/dist/contradiction.js +97 -0
  53. package/dist/contradiction.js.map +1 -0
  54. package/dist/finding.d.ts +567 -0
  55. package/dist/finding.d.ts.map +1 -0
  56. package/dist/finding.js +491 -0
  57. package/dist/finding.js.map +1 -0
  58. package/dist/hypothesis.d.ts +130 -0
  59. package/dist/hypothesis.d.ts.map +1 -0
  60. package/dist/hypothesis.js +116 -0
  61. package/dist/hypothesis.js.map +1 -0
  62. package/dist/identity.d.ts +295 -0
  63. package/dist/identity.d.ts.map +1 -0
  64. package/dist/identity.js +404 -0
  65. package/dist/identity.js.map +1 -0
  66. package/dist/index.d.ts +34 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +21 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/ir.d.ts +360 -0
  71. package/dist/ir.d.ts.map +1 -0
  72. package/dist/ir.js +76 -0
  73. package/dist/ir.js.map +1 -0
  74. package/dist/model-tables.d.ts +111 -0
  75. package/dist/model-tables.d.ts.map +1 -0
  76. package/dist/model-tables.js +103 -0
  77. package/dist/model-tables.js.map +1 -0
  78. package/dist/normalise.d.ts +114 -0
  79. package/dist/normalise.d.ts.map +1 -0
  80. package/dist/normalise.js +603 -0
  81. package/dist/normalise.js.map +1 -0
  82. package/dist/reliability.d.ts +89 -0
  83. package/dist/reliability.d.ts.map +1 -0
  84. package/dist/reliability.js +181 -0
  85. package/dist/reliability.js.map +1 -0
  86. package/dist/tool-surface.d.ts +131 -0
  87. package/dist/tool-surface.d.ts.map +1 -0
  88. package/dist/tool-surface.js +133 -0
  89. package/dist/tool-surface.js.map +1 -0
  90. package/dist/vocabulary.d.ts +73 -0
  91. package/dist/vocabulary.d.ts.map +1 -0
  92. package/dist/vocabulary.js +150 -0
  93. package/dist/vocabulary.js.map +1 -0
  94. package/package.json +27 -0
@@ -0,0 +1,491 @@
1
+ /**
2
+ * The shared finding record, and G3/G4 — the gates that decide what may be
3
+ * called what.
4
+ *
5
+ * `documents/architecture/descry-ai-layer.md` §20 is canonical for the type:
6
+ * **fourteen fields, no more and no fewer.** §42 carries a fifteenth,
7
+ * `Hypotheses considered`, which belongs to the output a developer reads and
8
+ * not to the stored record; it is deliberately absent here. DEC-276's own prose
9
+ * says *"eleven fields are extended to thirteen"* — eleven plus three is
10
+ * fourteen, and §20 corrects the arithmetic while keeping the field list the
11
+ * decision actually produced.
12
+ *
13
+ * It lives in `@descryy/ir` because two repositories construct and assert
14
+ * against it — the pipeline that produces findings and the golden-report test
15
+ * that checks all fourteen render — and both plans put it here specifically so
16
+ * that a divergence is a compile error rather than a field quietly missing from
17
+ * a page.
18
+ *
19
+ * ## Five axes, and they are not interchangeable
20
+ *
21
+ * A finding says five separate things, and collapsing any pair of them loses a
22
+ * distinction the product exists to make:
23
+ *
24
+ * - **Confidence** — how sure we are (§19's five categories).
25
+ * - **Fault layer** — whose problem it is (DEC-273). A finding can be
26
+ * `confirmed` and `environment` at once: fully proven, and not the
27
+ * application's fault.
28
+ * - **Reliability class** — how certain the *category* of claim is (§15.2).
29
+ * - **Resolution level** — how well the *code was understood* (§11A.6).
30
+ * - **Environment level** — how faithful the environment that produced it was
31
+ * (architecture §16.3).
32
+ *
33
+ * **Coverage is a sixth thing and not a confidence value.** §19's adopted note
34
+ * moved untested scope out of the category list and into `Verification status`
35
+ * deliberately: *"we found nothing"* and *"we did not look"* are different
36
+ * statements, and the precision-over-recall rule is only defensible while the
37
+ * second one has an honest home.
38
+ */
39
+ import { reliabilityCap } from "./reliability.js";
40
+ import { RESOLUTION_LEVELS } from "./ir.js";
41
+ /**
42
+ * The one cast in this module, kept in one place so the brand has exactly one
43
+ * runtime origin. Not exported — {@link rederive} is the whole legitimate
44
+ * surface for producing one on the read side of a storage boundary; every
45
+ * other producer is one of the derivations below, computed fresh.
46
+ */
47
+ function derive(value) {
48
+ return value;
49
+ }
50
+ /**
51
+ * §19's five, adopted as superseding the architecture's previous four.
52
+ *
53
+ * `not analysable` and `unsupported` are refusals rather than readings of
54
+ * evidence — *"we cannot establish this here"* and *"we do not do this at
55
+ * all"* — and G4 below does not produce them; they are inherited from the
56
+ * static case unchanged.
57
+ */
58
+ export const REPORT_CATEGORIES = [
59
+ "confirmed",
60
+ "strongly supported",
61
+ "unconfirmed",
62
+ "not analysable",
63
+ "unsupported",
64
+ ];
65
+ /**
66
+ * The three of the five that G4's step 2 can return, ordered strongest first.
67
+ *
68
+ * A narrower return type than {@link ReportCategory} on purpose: the gate reads
69
+ * evidence, and no amount of evidence turns into *"we cannot analyse this"*.
70
+ */
71
+ export const EVIDENCE_CATEGORIES = ["confirmed", "strongly supported", "unconfirmed"];
72
+ /**
73
+ * DEC-273 — a second axis beside confidence, never a sixth category.
74
+ *
75
+ * Confidence asks *how sure are we*; fault layer asks *whose problem is this*.
76
+ * They are independent, and a finding is not more or less true for being an
77
+ * environment fault — it is attributable to a different owner.
78
+ *
79
+ * **Classification heuristics remain deliberately unspecified for `product`,
80
+ * and for the no-comparison-available fallback on `environment`/`dependency`.**
81
+ * DEC-273 rests on three real classifications, and three is not enough to
82
+ * generalize a pattern-match rule from — `descry-ai-layer.md` §20's own
83
+ * caution, unchanged by the ruling below. **`automation`, `dependency` and
84
+ * `environment` are the three exceptions** — see {@link classifyFaultLayer}.
85
+ * None of the three is a heuristic in the sense being deferred: each is a
86
+ * structural comparison a tool either reports or does not (a harness
87
+ * self-failure flag, a declared-vs-resolved dependency version, a
88
+ * declared-vs-observed runner version), not a pattern inferred from few
89
+ * examples. `automation` was unblocked 2026-08-24 by `descry-runtime`'s
90
+ * RT-219 self-observability channel; `dependency`/`environment`'s structural
91
+ * comparison is ruled in
92
+ * `DEC-NEXT-fault-layer-empirical-confirmation.md`, which also requires every
93
+ * classification to carry its own confidence marker — see
94
+ * {@link faultLayerConfidence}.
95
+ */
96
+ export const FAULT_LAYERS = [
97
+ /** The application's own code. */
98
+ "product",
99
+ /** Descry's own test or harness logic. See {@link classifyFaultLayer}. */
100
+ "automation",
101
+ /** Runner, interpreter version, CI configuration — a mismatch, not a bug. */
102
+ "environment",
103
+ /** A pinned or transitive dependency. */
104
+ "dependency",
105
+ /** Not yet classified. An honest value, not a placeholder for a missing one. */
106
+ "unknown",
107
+ ];
108
+ /**
109
+ * `DEC-NEXT-fault-layer-empirical-confirmation.md` — whether a fault-layer
110
+ * value rests on a structural comparison/rerun or a pattern-match with no
111
+ * comparison available. See {@link faultLayerConfidence}.
112
+ */
113
+ export const FAULT_LAYER_CONFIDENCES = ["structural", "heuristic"];
114
+ /**
115
+ * Architecture §16.3's fidelity levels — how real the environment that produced
116
+ * the finding was. Emitted on every finding as `Validated Against: Level N`.
117
+ *
118
+ * 1 stub · 2 real code + disposable seeded database (the preferred default) ·
119
+ * 3 real code + redacted production-shaped recordings · 4 real staging.
120
+ * Selection prefers the highest available, 4 → 1.
121
+ */
122
+ export const ENVIRONMENT_LEVELS = [1, 2, 3, 4];
123
+ /**
124
+ * The six independent observation channels, closed list —
125
+ * `ai-governance-spec.md` §5.2, ruled 2026-08-24 in
126
+ * `DEC-NEXT-independent-signal-type-inventory.md`, superseding DEC-276's
127
+ * original four.
128
+ *
129
+ * *"Two log lines are one signal type."* `E` counts **channels**, not
130
+ * observations, which is why {@link independentSignalTypes} derives it rather
131
+ * than trusting a caller's count.
132
+ *
133
+ * **Not a raw count over `descry-runtime`'s twenty-two runtime event types.**
134
+ * Each channel folds in the event types that belong to it (the full mapping
135
+ * is the spec's own table). `TEST_FAILED` — one of the four previously-open
136
+ * types — is deliberately **not** a seventh channel: its evidentiary weight
137
+ * belongs to `M` alone (see {@link directMechanismEvidence}), and counting it
138
+ * as one more corroborating channel would route a reproduced failure through
139
+ * the strictly weaker `E >= 1` path when it already qualifies for `M`'s
140
+ * direct path. `COLLECTOR_ERROR` and every harness-action type
141
+ * (`PROCESS_STARTED`, `NAVIGATION`, `CLICK`, ...) are excluded by name — they
142
+ * record that the observer failed, or what Descry did, never what the system
143
+ * under test did.
144
+ *
145
+ * **This list is deliberately narrow, and widening it must stay a compile
146
+ * error.** A new collector arriving quietly and re-tuning every confidence
147
+ * boundary with nothing failing is the exact defect the result-state enum next
148
+ * door exists to prevent, one level up. Typing `E` as a bare `number` would
149
+ * "fix" the friction by removing the alarm. An unrecognised event type must
150
+ * fail closed — {@link RuntimeEvidence.signal} of `null` — until this table is
151
+ * extended by the same process that built it.
152
+ */
153
+ export const RUNTIME_SIGNAL_TYPES = [
154
+ "browser-visual",
155
+ "browser-console",
156
+ "network",
157
+ "backend-log",
158
+ "database",
159
+ "external-service",
160
+ ];
161
+ /**
162
+ * Legal values of `E`. Bounded by the six channels above, hence 0–6.
163
+ */
164
+ export const SIGNAL_TYPE_COUNTS = [0, 1, 2, 3, 4, 5, 6];
165
+ /**
166
+ * §20's fourteen fields, in §20's order.
167
+ *
168
+ * Exported so the golden-report test can iterate the canonical list rather than
169
+ * restate it — the desktop plan's test previously asserted eleven fields and
170
+ * would have passed green while two axes vanished from the product. A list
171
+ * asserted against `Object.keys` of a real finding is what makes that a
172
+ * failure instead of a silence.
173
+ */
174
+ export const FINDING_FIELDS = [
175
+ "finding",
176
+ "scope",
177
+ "affectedComponents",
178
+ "evidence",
179
+ "graphPath",
180
+ "runtimeEvidence",
181
+ "relevantChange",
182
+ "confidence",
183
+ "faultLayer",
184
+ "reliabilityClass",
185
+ "resolutionLevel",
186
+ "environmentLevel",
187
+ "recommendedAction",
188
+ "verificationStatus",
189
+ ];
190
+ /**
191
+ * `E` — how many of the six channels named this finding, counting
192
+ * **channels** rather than observations, and ignoring any record whose
193
+ * `signal` is `null` (excluded by design — see {@link RuntimeEvidence}).
194
+ */
195
+ export function independentSignalTypes(evidence) {
196
+ const distinct = new Set(evidence.map((item) => item.signal).filter((signal) => signal !== null));
197
+ // Indexed rather than cast: the set cannot exceed the declared channels, and
198
+ // the clamp keeps that provable to the compiler instead of asserted. The
199
+ // bound is read off the array so widening the vocabulary does not need this
200
+ // line changed too.
201
+ return SIGNAL_TYPE_COUNTS[Math.min(distinct.size, SIGNAL_TYPE_COUNTS.length - 1)] ?? 0;
202
+ }
203
+ /**
204
+ * `M` — direct mechanism evidence, `ai-governance-spec.md` §5.3. `true` when
205
+ * at least one runtime evidence record's resolved graph node id equals
206
+ * `failurePoint` — the hypothesis's named failure site — and that record is
207
+ * itself marked {@link RuntimeEvidence.directMechanism}, not merely
208
+ * co-occurring with the failure in time.
209
+ *
210
+ * A plain node-id match, the same identity mechanism §10 already requires for
211
+ * graph/runtime contradiction detection — deterministic, never a model
212
+ * judgement, which is what makes it a gate input rather than a slot output.
213
+ */
214
+ export function directMechanismEvidence(evidence, failurePoint) {
215
+ return evidence.some((item) => item.directMechanism === true && item.nodeId === failurePoint);
216
+ }
217
+ /**
218
+ * `G3` — the evidence precondition, `ai-governance-spec.md` §4. `true` iff at
219
+ * least one runtime evidence record exists at all for this hypothesis,
220
+ * however it classifies under `E`/`M`. Restated from `root-cause-score.ts`'s
221
+ * own module doc: `RuntimeEvidence > 0` is a precondition on promotion above
222
+ * `unconfirmed`, never an addend folded into {@link reportCategory}'s step 2
223
+ * arithmetic.
224
+ */
225
+ export function evidencePrecondition(evidence) {
226
+ return evidence.length > 0;
227
+ }
228
+ /**
229
+ * P11 — `ai-layer-plan.md` §11, `ai-governance-spec.md` §7. DEC-273's fault
230
+ * layer, computed rather than authored, for the one value that is currently
231
+ * groundable: {@link RuntimeEvidence.harnessFailure} is a structural fact
232
+ * (`descry-runtime` RT-219's self-observability channel either reported the
233
+ * harness dying or it did not) rather than a pattern inferred from a small
234
+ * number of examples, so classifying `"automation"` from it is not the kind
235
+ * of heuristic {@link FAULT_LAYERS}'s doc defers.
236
+ *
237
+ * **`product` / `environment` / `dependency` are deliberately not
238
+ * classified here.** No mechanism this codebase has built yet can positively
239
+ * distinguish them from evidence alone, and DEC-273's own three real
240
+ * classifications are explicitly too few to generalize a rule from. Every
241
+ * finding that is not an `automation` case is `"unknown"` — the honest
242
+ * default, not a placeholder for a missing case.
243
+ *
244
+ * A harness failure classifies `"automation"` even when domain evidence is
245
+ * also present: a collector that died mid-run can leave partial application
246
+ * evidence behind, and that evidence does not make the underlying failure
247
+ * any less Descry's own.
248
+ */
249
+ export function classifyFaultLayer(evidence) {
250
+ // Priority order is not specified by the ruling for the three structural
251
+ // checks; automation goes first because a harness self-failure explains why
252
+ // nothing else on the run can be trusted, and dependency before environment
253
+ // because a pinned-dependency mismatch is the narrower, more specific fact
254
+ // of the two.
255
+ if (evidence.some((item) => item.harnessFailure === true))
256
+ return "automation";
257
+ if (evidence.some((item) => item.dependencyVersionMismatch === true))
258
+ return "dependency";
259
+ if (evidence.some((item) => item.environmentVersionMismatch === true))
260
+ return "environment";
261
+ return "unknown";
262
+ }
263
+ /**
264
+ * DEC-NEXT-fault-layer-empirical-confirmation.md — every fault-layer value
265
+ * carries its own confidence marker, independent of the finding's
266
+ * `Confidence`: `structural` (a comparison or rerun confirmed it) vs.
267
+ * `heuristic` (pattern-matched, no comparison run). *"A bare `environment` or
268
+ * `dependency` label must never be presented at the same visual weight as a
269
+ * `confirmed` finding unless it earned `structural`."*
270
+ *
271
+ * **Every path {@link classifyFaultLayer} can produce today is `structural`.**
272
+ * `product`'s heuristic and the no-comparison-available fallback for
273
+ * `dependency`/`environment` are the ones the ruling explicitly defers
274
+ * (`descry-ai-layer.md` §20's caution against generalizing from three
275
+ * classifications) and are not built — `heuristic` is reserved for whichever
276
+ * future function writes that fallback, not returned by anything here.
277
+ */
278
+ export function faultLayerConfidence(_layer) {
279
+ return "structural";
280
+ }
281
+ /**
282
+ * `R` — the **minimum** resolution across the path.
283
+ *
284
+ * Minimum rather than average, first or last, and that single choice is what
285
+ * makes NOTE-4's control #6 (*a five-hop path inherits its worst hop's cap*)
286
+ * true by construction rather than by a second rule that could disagree.
287
+ *
288
+ * **An empty path derives R0**, the conservative floor: no hop resolved
289
+ * anything, so nothing above R0 has been established, and rule 2 says omit
290
+ * rather than overstate. Whether a purely runtime-observed finding should
291
+ * instead carry a synthetic R4 hop — so that a symptom confirmed on two
292
+ * channels is not capped to `unconfirmed` for having no traced cause — is a
293
+ * real question and is **not** decided here; filed as
294
+ * `documents/decisions-inbox/DEC-NEXT-empty-graph-path-resolution-floor.md`.
295
+ */
296
+ export function minimumResolution(path) {
297
+ let lowest = 0;
298
+ let seen = false;
299
+ for (const hop of path) {
300
+ if (!seen || hop.resolution < lowest)
301
+ lowest = hop.resolution;
302
+ seen = true;
303
+ }
304
+ return derive(lowest);
305
+ }
306
+ /**
307
+ * `H` — testimony anywhere under the finding.
308
+ *
309
+ * Any load-bearing hop is enough: DEC-223's cap is not diluted by the number of
310
+ * honest hops beside it, because one unaudited assertion is one unaudited
311
+ * assertion however much resolved structure surrounds it.
312
+ */
313
+ export function humanAssertedOnPath(path) {
314
+ return path.some((hop) => hop.humanAsserted === true);
315
+ }
316
+ /** Strongest first, so a cap is a `Math.min` over the rank. */
317
+ const CATEGORY_RANK = {
318
+ confirmed: 2,
319
+ "strongly supported": 1,
320
+ unconfirmed: 0,
321
+ };
322
+ const BY_RANK = ["unconfirmed", "strongly supported", "confirmed"];
323
+ /** Step 3's table — the highest category each class permits. */
324
+ const CLASS_CEILING = {
325
+ A: "confirmed",
326
+ B: "strongly supported",
327
+ C: "unconfirmed",
328
+ };
329
+ /**
330
+ * **G3 + G4 — the evidence precondition and the category gate.**
331
+ * `ai-governance-spec.md` §4–§5, stage 14 of the V1 loop. G3 fires first; G4
332
+ * is three steps.
333
+ *
334
+ * ```
335
+ * G3 no runtime evidence at all -> unconfirmed, steps 1-3 below never run
336
+ * step 1 class = reliabilityCap(R, N, H) -> "A" | "B" | "C"
337
+ * step 2 M -> confirmed | !M && E >= 1 -> strongly supported | E == 0 -> unconfirmed
338
+ * step 3 A -> stands | B -> at most strongly supported | C -> at most unconfirmed
339
+ * ```
340
+ *
341
+ * **Resolution is an input to step 1 and appears nowhere in step 2.** An
342
+ * earlier form of this gate granted `confirmed` on *"the structural path
343
+ * resolves R2+ throughout"*, which reads resolution directly as a category
344
+ * threshold — re-implementing rule 3 in a second place, at a different
345
+ * threshold, with nothing tying the two together. `reliabilityCap()` is the one
346
+ * function that turns a resolution into a class, so step 1 calls it and never
347
+ * restates its table.
348
+ *
349
+ * The step 3 mapping is corroborated rather than invented: DEC-223 reached the
350
+ * class-B row independently, in its own words — *"a finding resting on
351
+ * testimony is at most strongly supported, never confirmed."*
352
+ *
353
+ * **`M` dominates step 2, not channel count** — `ai-governance-spec.md` §5.1.
354
+ * One piece of direct mechanism evidence — a reproduced test failure, an
355
+ * exception whose stack frame resolves to the hypothesis's node, a network
356
+ * response matching the predicted defect on the exact route — reaches
357
+ * `confirmed` regardless of how many channels agree. Two channels that merely
358
+ * co-occur without naming the node (§5.3's own example: a console error and a
359
+ * failed network call, neither correlated to the hypothesis) are `E = 2,
360
+ * M = false`: `strongly supported`, never `confirmed`. This is what the
361
+ * earlier `E >= 2 -> confirmed` form left open — §41 rule 12 (correlation must
362
+ * not become causation) re-entering through the gate built to enforce it.
363
+ *
364
+ * Two consequences worth stating because they are the point: `confirmed` at
365
+ * class B is unreachable, so the two labels can never disagree about one
366
+ * finding; and G3's precondition survives even inside step 2 alone, because
367
+ * zero runtime evidence forces both `M = false` and `E = 0` by construction
368
+ * (both are computed from the same evidence array) — the explicit
369
+ * short-circuit below is a second, independent enforcement of that same
370
+ * invariant, not a case step 2 would otherwise get wrong.
371
+ *
372
+ * **Ruled 2026-08-24 — `ai-governance-spec.md` §5, closing
373
+ * `documents/decisions-inbox/DEC-NEXT-hypothesis-states-vs-g4.md` and
374
+ * `documents/decisions-inbox/DEC-NEXT-independent-signal-type-inventory.md`.**
375
+ * `documents/architecture/descry-ai-layer.md` §12's hypothesis-state
376
+ * thresholds and this gate now agree on identical evidence — there is only
377
+ * one function; §12's states are machine states, never a label directly
378
+ * (`ai-layer-plan.md` §6 rule 2).
379
+ *
380
+ * Takes an object rather than positionals because several of the fields are
381
+ * adjacent booleans, and a swapped pair is a silent wrong answer rather than a
382
+ * compile error.
383
+ */
384
+ export function reportCategory(inputs) {
385
+ // Step 1 — the structural ceiling. Computed unconditionally: it is a pure
386
+ // function of the graph path alone (R, N, H), so it stays well-defined —
387
+ // and useful, e.g. for display — even when G3 below short-circuits the
388
+ // category.
389
+ const reliability = reliabilityCap(inputs.resolution, inputs.nameLevel, inputs.humanAsserted);
390
+ // G3 — the evidence precondition, evaluated before step 2's arithmetic ever
391
+ // runs. Zero runtime evidence records at all means steps 2/3 are not
392
+ // consulted: a purely static finding cannot buy its way to `confirmed` by
393
+ // scoring well on the structural terms alone.
394
+ if (!inputs.hasRuntimeEvidence) {
395
+ return { category: "unconfirmed", reliabilityClass: derive(reliability) };
396
+ }
397
+ // Step 2 — evidence category. Resolution plays no part here. `M` dominates:
398
+ // one piece of direct mechanism evidence reaches `confirmed` regardless of
399
+ // channel count; channel count only decides among the cases `M` does not
400
+ // settle.
401
+ const observed = inputs.directMechanismEvidence
402
+ ? "confirmed"
403
+ : inputs.independentSignalTypes >= 1
404
+ ? "strongly supported"
405
+ : "unconfirmed";
406
+ // Step 3 — rule 3 applied one layer up. A cap, so it can only ever lower.
407
+ const ceiling = CLASS_CEILING[reliability];
408
+ const rank = Math.min(CATEGORY_RANK[observed], CATEGORY_RANK[ceiling]);
409
+ return {
410
+ category: BY_RANK[rank] ?? "unconfirmed",
411
+ reliabilityClass: derive(reliability),
412
+ };
413
+ }
414
+ /**
415
+ * Thrown by {@link rederive} when a record cannot be trusted to re-derive
416
+ * from — a missing, mistyped or out-of-range G4 input, or a finding whose
417
+ * `confidence` was never G4-gated in the first place.
418
+ *
419
+ * A thrown error rather than a best-effort fallback, because the alternative
420
+ * is a *silently* wrong reliability class — the one field the whole cap in
421
+ * rule 3 rests on — which is worse than none at all.
422
+ */
423
+ export class RederivationError extends Error {
424
+ name = "RederivationError";
425
+ }
426
+ function fail(detail) {
427
+ throw new RederivationError(detail);
428
+ }
429
+ /**
430
+ * Runtime-validate one G4 input, regardless of what its static type claims.
431
+ *
432
+ * `record.inputs` is typed `G4Inputs` on {@link FindingRecord}, which is true
433
+ * for a caller holding a value this module produced — but the entire reason
434
+ * this validation exists is the storage boundary in between: a `JSON.parse`
435
+ * result is `any`, and nothing stops a writer from dropping a field, and
436
+ * nothing about TypeScript's guarantee survives that round trip. This is the
437
+ * one place that gap is closed.
438
+ */
439
+ function checked(value, field, isValid) {
440
+ if (!isValid(value))
441
+ fail(`"${field}" is missing or invalid — got ${JSON.stringify(value)}`);
442
+ return value;
443
+ }
444
+ const isSignalTypeCount = (v) => typeof v === "number" && SIGNAL_TYPE_COUNTS.includes(v);
445
+ const isResolutionLevel = (v) => typeof v === "number" && RESOLUTION_LEVELS.includes(v);
446
+ const isBoolean = (v) => typeof v === "boolean";
447
+ function validatedG4Inputs(inputs) {
448
+ return {
449
+ hasRuntimeEvidence: checked(inputs.hasRuntimeEvidence, "hasRuntimeEvidence", isBoolean),
450
+ independentSignalTypes: checked(inputs.independentSignalTypes, "independentSignalTypes", isSignalTypeCount),
451
+ directMechanismEvidence: checked(inputs.directMechanismEvidence, "directMechanismEvidence", isBoolean),
452
+ resolution: checked(inputs.resolution, "resolution", isResolutionLevel),
453
+ nameLevel: checked(inputs.nameLevel, "nameLevel", isBoolean),
454
+ humanAsserted: checked(inputs.humanAsserted, "humanAsserted", isBoolean),
455
+ };
456
+ }
457
+ /**
458
+ * Read a {@link FindingRecord} back as a `Finding` — the legitimate producer
459
+ * of `Derived<T>` on the read side of a storage boundary. The provisional
460
+ * escape hatch this module used to carry for the same purpose is gone; this
461
+ * is what made it deletable rather than permanent.
462
+ *
463
+ * Recomputes `reliabilityClass`, `resolutionLevel` and `confidence` from
464
+ * `record.inputs` by calling {@link reportCategory} — the one function that
465
+ * turns `E`/`R`/`N`/`H` into a class, never restated here. The recomputed
466
+ * values are authoritative; `record.finding`'s own copies of these three
467
+ * fields are not read at all, only overwritten, because the entire point is
468
+ * that this is a *re-derivation*, not a restoration of whatever the record
469
+ * happened to say.
470
+ *
471
+ * Throws {@link RederivationError} — rather than returning a plausible but
472
+ * wrong value — when `record.inputs` cannot be trusted (a missing, mistyped
473
+ * or out-of-range field) or when `record.finding.confidence` is not one of
474
+ * {@link EVIDENCE_CATEGORIES} (the finding was never G4-gated, so it has no
475
+ * G4 inputs to rederive from regardless of what `record.inputs` claims).
476
+ */
477
+ export function rederive(record) {
478
+ if (!EVIDENCE_CATEGORIES.includes(record.finding.confidence)) {
479
+ fail(`cannot rederive confidence "${record.finding.confidence}": only the three G4-gated ` +
480
+ `categories (${EVIDENCE_CATEGORIES.join(", ")}) have recorded G4 inputs to rederive from.`);
481
+ }
482
+ const inputs = validatedG4Inputs(record.inputs);
483
+ const { category, reliabilityClass } = reportCategory(inputs);
484
+ return {
485
+ ...record.finding,
486
+ confidence: category,
487
+ reliabilityClass,
488
+ resolutionLevel: derive(inputs.resolution),
489
+ };
490
+ }
491
+ //# sourceMappingURL=finding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding.js","sourceRoot":"","sources":["../src/finding.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * The hypothesis state machine — `descry-ai-layer.md` §12, P19 of
3
+ * `ai-layer-plan.md` §11 (Phase 4).
4
+ *
5
+ * Six states, four transitions, all evaluated outside the model: *"Every
6
+ * transition is a deterministic rule evaluated outside the model. The model
7
+ * proposes; a rule disposes."* (`ai-layer-plan.md` §6). Same posture as
8
+ * G1–G4 (`../governance/*.ts`, `./finding.ts`) — a pure function over
9
+ * caller-supplied facts, never a prompt instruction.
10
+ *
11
+ * ## This is not a report category
12
+ *
13
+ * §12's own ruling, 2026-08-24 (Option B, `ai-governance-spec.md` §9): these
14
+ * six are *machine states*. `supported`/`confirmed` are not §19's
15
+ * `strongly supported`/`confirmed` report categories — only G4
16
+ * ({@link "./finding.ts".reportCategory}) assigns those, and it reads
17
+ * different inputs (resolution, name-level-ness, humanAsserted) that this
18
+ * module does not see at all. Do not compare a `HypothesisState` against a
19
+ * `ReportCategory` anywhere upstream of G4.
20
+ *
21
+ * ## What this module does not compute
22
+ *
23
+ * Three of the five inputs below are facts only the caller (the investigation
24
+ * loop, stage 12, P20) can supply, and are deliberately *not* derived here:
25
+ *
26
+ * - **`contradicted`** — whether evidence directly contradicts the
27
+ * hypothesis. Detecting a contradiction is §10's own machinery (graph vs.
28
+ * runtime disagreement, P21) — a larger, separate mechanism this module
29
+ * does not reimplement.
30
+ * - **`roundLimitReached`** — whether this hypothesis has exhausted its
31
+ * per-hypothesis tool-call budget. That accounting is `../governance/budget.ts`
32
+ * (P18)'s job; this module only reacts to the boolean it produces.
33
+ * - **`toolCallsIssued`** — the raw count of tool calls issued so far.
34
+ * `candidate → investigating` fires the instant the first call is issued,
35
+ * before any result — even an empty or refused one — comes back, so this
36
+ * cannot be derived from the evidence array's shape.
37
+ */
38
+ import type { RuntimeEvidence } from "./finding.ts";
39
+ /** §12's six states, in the order the architecture lists them. */
40
+ export declare const HYPOTHESIS_STATES: readonly ["candidate", "investigating", "supported", "confirmed", "rejected", "unconfirmed"];
41
+ export type HypothesisState = (typeof HYPOTHESIS_STATES)[number];
42
+ /** States §12 marks terminal outright — the loop stops touching this hypothesis. */
43
+ export declare const TERMINAL_HYPOTHESIS_STATES: readonly ["confirmed", "rejected", "unconfirmed"];
44
+ export declare function isTerminalHypothesisState(state: HypothesisState): boolean;
45
+ /**
46
+ * Why an `unconfirmed` result was reached. `ai-layer-plan.md` §6 point 4 names
47
+ * three: `round-limit` is the one transition §12 actually defines
48
+ * (`any state → unconfirmed`, the round limit reached before `confirmed` or
49
+ * `rejected`); `superseded` and `unreachable` are named by the plan as
50
+ * additional reasons but have no defined trigger here — **disclosed, not
51
+ * modeled**, same as every other gap this project leaves rather than guesses
52
+ * a rule for.
53
+ */
54
+ export declare const UNCONFIRMED_REASONS: readonly ["round-limit"];
55
+ export type UnconfirmedReason = (typeof UNCONFIRMED_REASONS)[number];
56
+ /**
57
+ * Everything {@link deriveHypothesisState} reads. Computed fresh each round
58
+ * from the hypothesis's accumulated evidence — this is a derivation, not an
59
+ * incremental transition-application function, matching G3/G4's own shape
60
+ * (`../governance/*.ts` calls this the same way `reportCategory` is called:
61
+ * full inputs in, one output out, every time).
62
+ */
63
+ export interface HypothesisStateInput {
64
+ /** How many tool calls have been issued investigating this hypothesis. `0` means still `candidate`. */
65
+ readonly toolCallsIssued: number;
66
+ /**
67
+ * The failure-site node id this hypothesis names — what
68
+ * {@link directMechanismEvidence} matches evidence against. `hash(repo,
69
+ * kind, qualified symbol path)`, never a file path.
70
+ */
71
+ readonly failurePoint: string;
72
+ /** Every runtime evidence record gathered for this hypothesis so far. */
73
+ readonly runtimeEvidence: readonly RuntimeEvidence[];
74
+ /**
75
+ * Whether a graph-structural fact — independent of any runtime
76
+ * observation — supports this hypothesis. One of §12's two named
77
+ * `supported` sources (*"a graph fact plus a runtime observation"*); the
78
+ * other is two distinct runtime channels, already counted via
79
+ * `runtimeEvidence`.
80
+ */
81
+ readonly hasGraphFact: boolean;
82
+ /**
83
+ * Caller-supplied: at least one gathered evidence record directly
84
+ * contradicts the hypothesis. Not computed here — see the module doc.
85
+ */
86
+ readonly contradicted: boolean;
87
+ /**
88
+ * Caller-supplied: this hypothesis has reached its round limit (§41 rule
89
+ * 17) without reaching `confirmed` or `rejected`. Not computed here — see
90
+ * the module doc.
91
+ */
92
+ readonly roundLimitReached: boolean;
93
+ }
94
+ export interface HypothesisStateResult {
95
+ readonly state: HypothesisState;
96
+ /** Present only when `state === "unconfirmed"`. */
97
+ readonly unconfirmedReason?: UnconfirmedReason;
98
+ }
99
+ /**
100
+ * How many independent evidence sources name this hypothesis — a graph fact
101
+ * counts as one, and each of the six {@link RuntimeSignalType} channels
102
+ * counts once however many records carry it (the same dedup-by-channel
103
+ * discipline `independentSignalTypes` uses for G4's `E`, applied to a wider
104
+ * source set).
105
+ *
106
+ * **Not `E`.** G4's `E` is a closed count over exactly the six runtime
107
+ * channels (`ai-governance-spec.md` §5.2) and a graph fact is not one of
108
+ * them by design — conflating the two is exactly the "machine state leaking
109
+ * into report vocabulary" failure §12's own ruling exists to prevent. This
110
+ * function answers a different question — *how many independent sources
111
+ * back this hypothesis, for the state machine's own `supported` transition*
112
+ * — and must never be passed anywhere G4 reads.
113
+ */
114
+ export declare function independentEvidenceSources(runtimeEvidence: readonly RuntimeEvidence[], hasGraphFact: boolean): number;
115
+ /**
116
+ * Derive this hypothesis's current state from its accumulated evidence.
117
+ * Never throws — this is a check, not an execution of the thing it is
118
+ * checking.
119
+ *
120
+ * Precedence, most decisive first: a caller-flagged contradiction always
121
+ * rejects, ahead of mechanism evidence — a sane caller never sets both, but
122
+ * rejection winning is the conservative reading. Direct mechanism evidence
123
+ * confirms next, regardless of round limit — §12's `confirmed` transition
124
+ * names no round bound. Only after both are checked does the round limit
125
+ * force `unconfirmed`. Below that, two or more independent sources reach
126
+ * `supported`; otherwise the hypothesis is still `investigating` once at
127
+ * least one call has been issued, or `candidate` before the first.
128
+ */
129
+ export declare function deriveHypothesisState(input: HypothesisStateInput): HypothesisStateResult;
130
+ //# sourceMappingURL=hypothesis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hypothesis.d.ts","sourceRoot":"","sources":["../src/hypothesis.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAqB,MAAM,cAAc,CAAC;AAGvE,kEAAkE;AAClE,eAAO,MAAM,iBAAiB,8FAOpB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjE,oFAAoF;AACpF,eAAO,MAAM,0BAA0B,mDAAoD,CAAC;AAE5F,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAEzE;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,0BAA2B,CAAC;AAC5D,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAErE;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC,uGAAuG;IACvG,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,yEAAyE;IACzE,QAAQ,CAAC,eAAe,EAAE,SAAS,eAAe,EAAE,CAAC;IACrD;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B;;;OAGG;IACH,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;CACrC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,mDAAmD;IACnD,QAAQ,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CAChD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,0BAA0B,CACxC,eAAe,EAAE,SAAS,eAAe,EAAE,EAC3C,YAAY,EAAE,OAAO,GACpB,MAAM,CAOR;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,oBAAoB,GAAG,qBAAqB,CAsBxF"}