wicked-crew 0.7.25 → 0.7.27

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 (131) hide show
  1. package/dist/api/endpoint-manifest-live.d.ts.map +1 -1
  2. package/dist/api/endpoint-manifest-live.js +5 -0
  3. package/dist/api/endpoint-manifest-live.js.map +1 -1
  4. package/dist/api/eval-compare.d.ts +306 -0
  5. package/dist/api/eval-compare.d.ts.map +1 -0
  6. package/dist/api/eval-compare.js +610 -0
  7. package/dist/api/eval-compare.js.map +1 -0
  8. package/dist/api/eval-sample.d.ts +158 -0
  9. package/dist/api/eval-sample.d.ts.map +1 -0
  10. package/dist/api/eval-sample.js +88 -0
  11. package/dist/api/eval-sample.js.map +1 -0
  12. package/dist/api/routes.d.ts +5 -0
  13. package/dist/api/routes.d.ts.map +1 -1
  14. package/dist/api/routes.js +31 -0
  15. package/dist/api/routes.js.map +1 -1
  16. package/dist/api/run-files.d.ts +12 -2
  17. package/dist/api/run-files.d.ts.map +1 -1
  18. package/dist/api/run-files.js +17 -5
  19. package/dist/api/run-files.js.map +1 -1
  20. package/dist/api/server.d.ts +20 -0
  21. package/dist/api/server.d.ts.map +1 -1
  22. package/dist/api/server.js +62 -9
  23. package/dist/api/server.js.map +1 -1
  24. package/dist/api/skills.d.ts +88 -0
  25. package/dist/api/skills.d.ts.map +1 -0
  26. package/dist/api/skills.js +264 -0
  27. package/dist/api/skills.js.map +1 -0
  28. package/dist/api/testing.d.ts +2 -75
  29. package/dist/api/testing.d.ts.map +1 -1
  30. package/dist/api/testing.js +14 -28
  31. package/dist/api/testing.js.map +1 -1
  32. package/dist/core/adapter.d.ts +42 -0
  33. package/dist/core/adapter.d.ts.map +1 -1
  34. package/dist/core/adapter.js +83 -12
  35. package/dist/core/adapter.js.map +1 -1
  36. package/dist/interactive/bridge-root.d.ts +117 -7
  37. package/dist/interactive/bridge-root.d.ts.map +1 -1
  38. package/dist/interactive/bridge-root.js +229 -9
  39. package/dist/interactive/bridge-root.js.map +1 -1
  40. package/dist/interactive/doc-delete-routes.d.ts +2 -0
  41. package/dist/interactive/doc-delete-routes.d.ts.map +1 -1
  42. package/dist/interactive/doc-delete-routes.js +3 -19
  43. package/dist/interactive/doc-delete-routes.js.map +1 -1
  44. package/dist/interactive/doc-list-routes.d.ts +42 -0
  45. package/dist/interactive/doc-list-routes.d.ts.map +1 -0
  46. package/dist/interactive/doc-list-routes.js +165 -0
  47. package/dist/interactive/doc-list-routes.js.map +1 -0
  48. package/dist/interactive/project-root.d.ts +36 -0
  49. package/dist/interactive/project-root.d.ts.map +1 -0
  50. package/dist/interactive/project-root.js +45 -0
  51. package/dist/interactive/project-root.js.map +1 -0
  52. package/dist/interactive/proxy-routes.d.ts +4 -1
  53. package/dist/interactive/proxy-routes.d.ts.map +1 -1
  54. package/dist/interactive/proxy-routes.js +4 -22
  55. package/dist/interactive/proxy-routes.js.map +1 -1
  56. package/dist/projects/default-project.d.ts +10 -0
  57. package/dist/projects/default-project.d.ts.map +1 -0
  58. package/dist/projects/default-project.js +10 -0
  59. package/dist/projects/default-project.js.map +1 -0
  60. package/dist/projects/routes.d.ts +0 -2
  61. package/dist/projects/routes.d.ts.map +1 -1
  62. package/dist/projects/routes.js +1 -2
  63. package/dist/projects/routes.js.map +1 -1
  64. package/dist/skills/bundle.d.ts +94 -0
  65. package/dist/skills/bundle.d.ts.map +1 -0
  66. package/dist/skills/bundle.js +187 -0
  67. package/dist/skills/bundle.js.map +1 -0
  68. package/dist/skills/contain.d.ts +57 -0
  69. package/dist/skills/contain.d.ts.map +1 -0
  70. package/dist/skills/contain.js +90 -0
  71. package/dist/skills/contain.js.map +1 -0
  72. package/dist/skills/core-closure.d.ts +83 -0
  73. package/dist/skills/core-closure.d.ts.map +1 -0
  74. package/dist/skills/core-closure.js +130 -0
  75. package/dist/skills/core-closure.js.map +1 -0
  76. package/dist/skills/engine-env.d.ts +48 -0
  77. package/dist/skills/engine-env.d.ts.map +1 -0
  78. package/dist/skills/engine-env.js +69 -0
  79. package/dist/skills/engine-env.js.map +1 -0
  80. package/dist/skills/frontmatter.d.ts +77 -0
  81. package/dist/skills/frontmatter.d.ts.map +1 -0
  82. package/dist/skills/frontmatter.js +190 -0
  83. package/dist/skills/frontmatter.js.map +1 -0
  84. package/dist/skills/guards.d.ts +72 -0
  85. package/dist/skills/guards.d.ts.map +1 -0
  86. package/dist/skills/guards.js +147 -0
  87. package/dist/skills/guards.js.map +1 -0
  88. package/dist/skills/live-generations.d.ts +80 -0
  89. package/dist/skills/live-generations.d.ts.map +1 -0
  90. package/dist/skills/live-generations.js +163 -0
  91. package/dist/skills/live-generations.js.map +1 -0
  92. package/dist/skills/plugin-source.d.ts +226 -0
  93. package/dist/skills/plugin-source.d.ts.map +1 -0
  94. package/dist/skills/plugin-source.js +467 -0
  95. package/dist/skills/plugin-source.js.map +1 -0
  96. package/dist/skills/refs.d.ts +58 -0
  97. package/dist/skills/refs.d.ts.map +1 -0
  98. package/dist/skills/refs.js +133 -0
  99. package/dist/skills/refs.js.map +1 -0
  100. package/dist/skills/root-fence.d.ts +53 -0
  101. package/dist/skills/root-fence.d.ts.map +1 -0
  102. package/dist/skills/root-fence.js +109 -0
  103. package/dist/skills/root-fence.js.map +1 -0
  104. package/dist/skills/root-names.d.ts +42 -0
  105. package/dist/skills/root-names.d.ts.map +1 -0
  106. package/dist/skills/root-names.js +44 -0
  107. package/dist/skills/root-names.js.map +1 -0
  108. package/dist/skills/runtime.d.ts +147 -0
  109. package/dist/skills/runtime.d.ts.map +1 -0
  110. package/dist/skills/runtime.js +320 -0
  111. package/dist/skills/runtime.js.map +1 -0
  112. package/dist/skills/store.d.ts +895 -0
  113. package/dist/skills/store.d.ts.map +1 -0
  114. package/dist/skills/store.js +3596 -0
  115. package/dist/skills/store.js.map +1 -0
  116. package/dist/skills/tree.d.ts +292 -0
  117. package/dist/skills/tree.d.ts.map +1 -0
  118. package/dist/skills/tree.js +645 -0
  119. package/dist/skills/tree.js.map +1 -0
  120. package/dist/skills/venv.d.ts +52 -0
  121. package/dist/skills/venv.d.ts.map +1 -0
  122. package/dist/skills/venv.js +80 -0
  123. package/dist/skills/venv.js.map +1 -0
  124. package/dist/studio/assets/index-CK1uJWs2.css +32 -0
  125. package/dist/studio/assets/index-FcZg3WDR.js +539 -0
  126. package/dist/studio/index.html +2 -2
  127. package/dist/studio/testid-inventory.json +605 -7
  128. package/endpoint-manifest.json +183 -1
  129. package/package.json +4 -3
  130. package/dist/studio/assets/index-C4iz4uAw.js +0 -537
  131. package/dist/studio/assets/index-DEEQcRQ_.css +0 -32
@@ -1 +1 @@
1
- {"version":3,"file":"endpoint-manifest-live.d.ts","sourceRoot":"","sources":["../../src/api/endpoint-manifest-live.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAQH,OAAO,EAAyB,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAEtF,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAkC7E"}
1
+ {"version":3,"file":"endpoint-manifest-live.d.ts","sourceRoot":"","sources":["../../src/api/endpoint-manifest-live.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAQH,OAAO,EAAyB,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAEtF,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAuC7E"}
@@ -40,6 +40,7 @@ export async function collectLiveEndpointManifest() {
40
40
  projectsSupported: () => false,
41
41
  getSettings: async () => ({}),
42
42
  // The daemon's single CoreEvent fan-out subscribes at boot; no event ever arrives here.
43
+ onLaunch: () => () => undefined, // the launch hook createServer registers (skills keystone, codex round 4)
43
44
  onEvent: () => () => { },
44
45
  };
45
46
  try {
@@ -49,6 +50,10 @@ export async function collectLiveEndpointManifest() {
49
50
  projectEvents: { disabled: true },
50
51
  interactiveWsRelay: { disabled: true },
51
52
  stallWatchdog: { enabled: false },
53
+ // No skills seam: the manifest documents the routes, and an armed seam would seed the
54
+ // generating machine's REAL skills root from its installed plugin. The routes register
55
+ // regardless (they answer 503 unconfigured).
56
+ skills: { disabled: true },
52
57
  // Nonexistent on purpose — headless boot; see the module header.
53
58
  studioRoot: join(scratch, 'no-studio-bundle'),
54
59
  });
@@ -1 +1 @@
1
- {"version":3,"file":"endpoint-manifest-live.js","sourceRoot":"","sources":["../../src/api/endpoint-manifest-live.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,qBAAqB,EAAyB,MAAM,wBAAwB,CAAC;AAEtF,MAAM,CAAC,KAAK,UAAU,2BAA2B;IAC/C,4FAA4F;IAC5F,kFAAkF;IAClF,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,QAAQ,CAAC;IACpC,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,yBAAyB,CAAC,CAAC,CAAC;IACvE,MAAM,OAAO,GAAG;QACd,IAAI,EAAE,IAAI;QACV,iBAAiB,EAAE,GAAG,EAAE,CAAC,KAAK;QAC9B,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QAC7B,wFAAwF;QACxF,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,GAAE,CAAC;KACE,CAAC;IAC5B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE;YACtC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;YACrB,SAAS,EAAE,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC;YACrC,aAAa,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;YACjC,kBAAkB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;YACtC,aAAa,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;YACjC,iEAAiE;YACjE,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE,kBAAkB,CAAC;SAC9C,CAAC,CAAC;QACH,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;YAClB,OAAO,qBAAqB,CAAC,GAAG,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC;QAC3D,CAAC;gBAAS,CAAC;YACT,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC;IACH,CAAC;YAAS,CAAC;QACT,IAAI,aAAa,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;;YAC5D,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,aAAa,CAAC;QAC9C,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"endpoint-manifest-live.js","sourceRoot":"","sources":["../../src/api/endpoint-manifest-live.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,qBAAqB,EAAyB,MAAM,wBAAwB,CAAC;AAEtF,MAAM,CAAC,KAAK,UAAU,2BAA2B;IAC/C,4FAA4F;IAC5F,kFAAkF;IAClF,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,QAAQ,CAAC;IACpC,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,yBAAyB,CAAC,CAAC,CAAC;IACvE,MAAM,OAAO,GAAG;QACd,IAAI,EAAE,IAAI;QACV,iBAAiB,EAAE,GAAG,EAAE,CAAC,KAAK;QAC9B,WAAW,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QAC7B,wFAAwF;QACxF,QAAQ,EAAE,GAAiB,EAAE,CAAC,GAAG,EAAE,CAAC,SAAS,EAAE,0EAA0E;QACzH,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,GAAE,CAAC;KACE,CAAC;IAC5B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE;YACtC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;YACrB,SAAS,EAAE,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC;YACrC,aAAa,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;YACjC,kBAAkB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;YACtC,aAAa,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;YACjC,sFAAsF;YACtF,uFAAuF;YACvF,6CAA6C;YAC7C,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;YAC1B,iEAAiE;YACjE,UAAU,EAAE,IAAI,CAAC,OAAO,EAAE,kBAAkB,CAAC;SAC9C,CAAC,CAAC;QACH,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;YAClB,OAAO,qBAAqB,CAAC,GAAG,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC;QAC3D,CAAC;gBAAS,CAAC;YACT,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC;IACH,CAAC;YAAS,CAAC;QACT,IAAI,aAAa,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;;YAC5D,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,aAAa,CAAC;QAC9C,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;AACH,CAAC"}
@@ -0,0 +1,306 @@
1
+ /**
2
+ * Cross-run eval comparison (docs/testing/evals-test-plan.md S17) — the OFFLINE, deterministic diff
3
+ * of two recorded {@link EvalRunDetail}s over the same corpus: which per-sample verdicts flipped,
4
+ * whether each flip is the kind a tightened store is allowed to produce (`permitted`) or the kind a
5
+ * release has to explain (`flagged`), whether the two stored summaries reconcile to the per-sample
6
+ * accounting, and which rules gained or lost exercise (core #394 `rule_coverage`).
7
+ *
8
+ * Pure over the two records: no store, no engine, no clock, no I/O — two persisted drilldowns in,
9
+ * one report out, byte-stable for the same inputs (every list is codepoint-sorted by id).
10
+ *
11
+ * # What "comparable" means here
12
+ *
13
+ * Plan §3 keys a comparison by five identities. The records carry four of them today (`corpus`,
14
+ * `rule_store`, `type_filter`, `degraded`; the rule snapshot and the engine build are §5 follow-ups),
15
+ * so `comparable` is derived from CONTENT, never from a name alone: the same corpus name AND the same
16
+ * type filter AND the same sample ids AND, per id, the same `kind` AND the same sample PAYLOAD
17
+ * identity — the `sample.payload_hash` a producer stamped on each result row (`eval-sample.js`
18
+ * `samplePayloadHash`: sha256 over the canonical JSON of id, description, kind, steering_type,
19
+ * signals). The engine echoes only id/description/kind/steering_type per row, never the input
20
+ * `signals`, so a row WITHOUT a payload hash cannot be proven to be the same action as its
21
+ * namesake in the other run: such a pair is `comparable: false` with `comparable_reason` starting
22
+ * `unverified: no sample identity` — unverified, which is not the same as "different". A sample
23
+ * present on one side only, one whose kind changed, or one whose payload hash changed means the
24
+ * corpus changed under the same name — reported, and the pair is not comparable release over
25
+ * release. Two runs under DIFFERENT type filters judged different slices of the corpus over
26
+ * different coverage denominators — not comparable either (`differing-type-filter`, see below).
27
+ * `comparable_reason` is `null` exactly when `comparable` is true.
28
+ *
29
+ * # Flip classification (the S17 rule table)
30
+ *
31
+ * bad gap → caught permitted a rule tightened: a bad behavior is now caught
32
+ * bad caught → gap flagged regression: a bad behavior is no longer caught
33
+ * good false_positive → caught permitted a good sample is no longer denied
34
+ * good caught → false_positive flagged a good sample is newly denied
35
+ * anything else flagged not a verdict pair that kind can take (evals.rs
36
+ * `evaluate_sample`: a bad sample is caught|gap, a
37
+ * good sample is caught|false_positive)
38
+ *
39
+ * `good` is a sample KIND, not a verdict — the draft plan's `good → false_positive` spelling was
40
+ * corrected in revision 3; this module is the executable form of that correction.
41
+ *
42
+ * # Coverage transitions vs. rule-set changes — what a record CANNOT tell (codex rounds 6 and 8)
43
+ *
44
+ * The wire carries `rule_coverage.exercised` as a COUNT and `unexercised` as a LIST of ids
45
+ * (api-types `GovernanceEvalRuleCoverage`; wicked-core `crates/wicked-governance/src/evals.rs` on
46
+ * branch `feat/evals-effect-and-coverage` (core #394/#395, PR #398) at `a87e461` — `RuleCoverage`
47
+ * lines 323-328: `exercised: usize`, `unexercised: Vec<UnexercisedRule>`, `recall_only: usize`,
48
+ * `per_type: BTreeMap<String, TypeCoverage>`). The partition is over the ELIGIBLE rules — the
49
+ * active, effect-bearing rules of the slice (`decide_lane_rules` lines 816-843: `effect.is_some()
50
+ * && !retired && in_slice`); `recall_only` counts the effect-less active rules OUTSIDE it (docs
51
+ * lines 317-319, computed at 875-878). A rule is EXERCISED when it appeared in ANY evaluated claim's
52
+ * `policy_ids`, whatever its effect (`RuleCoverage` docs lines 312-314; `rule_coverage()` lines
53
+ * 849-880 partitions the eligible rules by `triggered`, the union `run_evals` collects of every
54
+ * claim's `policy_ids`). A result row's `fired`, however, is the BLOCKING subset only:
55
+ * `evaluate_sample` lines 906-916 keep the ids whose effect is `Deny`. Hence, for an UNFILTERED run,
56
+ *
57
+ * fired(run) ⊆ exercised(run), and `exercised − |fired|` rules were exercised by a non-blocking
58
+ * (`warn`) effect ALONE — counted, but NEVER named anywhere in the record.
59
+ *
60
+ * NO field of the wire lists a run's rule set: `exercised` and `recall_only` are counts,
61
+ * `unexercised` names only the rules nothing exercised, `fired` only the blocking firings. The rule
62
+ * identities a record enumerates are therefore exactly `unexercised ∪ fired`, and an id one record
63
+ * enumerates and the other does not is NOT thereby absent from the other's store: it may be one of
64
+ * that side's unnamed warn-exercised rules, an effect-less (recall-only) or retired rule outside the
65
+ * eligible partition, under a type filter a rule of another type outside the denominator — or a rule
66
+ * the store really gained or lost. Round 6 replaced a reconstruction of "the inventory" as
67
+ * `unexercised ∪ fired` with a completeness inference: `exercised === |fired|` ⇒ every exercised rule
68
+ * is named ⇒ the silent side's absence was asserted as `added_rules`/`removed_rules`. Round 8 deleted
69
+ * that inference too — under a type filter a fired id is typed into the slice only by the OTHER run's
70
+ * `unexercised` row, and a rule's type in run A does not establish its type in run B (codex's
71
+ * reproduction: A lists R and Q unexercised under `development`; B moves R to `security`, fires R
72
+ * blocking and exercises Q by `warn`; the cross-run intersection named R, declared B complete and
73
+ * reported Q — still present and exercised — as `removed_rules`). The delta asserts ONLY what the
74
+ * records state:
75
+ *
76
+ * gained unexercised in A (listed) AND blocking-fired in B (listed): a sample now exercises
77
+ * the rule — a statement about the RULE, both ends named, certain
78
+ * lost blocking-fired in A AND unexercised in B (listed): certain
79
+ * unidentified PER RECORD, how many exercised rules of its denominator it does not name:
80
+ * unfiltered `exercised − |fired|`; under a type filter the whole `exercised` (a
81
+ * fired id carries no steering_type, so a record types none of its own firings into
82
+ * the slice) — never reduced by what the OTHER run lists
83
+ * added_rules / asserted only from an explicit rule inventory on BOTH records — the wire carries
84
+ * removed_rules none, so both are EMPTY for every daemon-recorded run and `inventory` is `partial`
85
+ * transitions_withheld
86
+ * every id enumerated by one record and not the other, one reason per id saying what
87
+ * it may be besides a rule-set change — WITHHELD, never guessed
88
+ *
89
+ * `exercised_delta` is always the difference of the two counts. Reconciliation errors on coverage are
90
+ * the record contradicting ITSELF (or being malformed — below); two valid reports always `reconcile`.
91
+ *
92
+ * # Type filters (codex round 7) — the denominator is the SLICE; the firings are not
93
+ *
94
+ * A run recorded under `type_filter: T` was produced by `rules eval --type T`. In the engine that
95
+ * filter slices the SAMPLES (`run_evals` lines 974-977: only samples of type T are judged) and the
96
+ * COVERAGE DENOMINATOR (`decide_lane_rules` lines 816-844, `in_slice` at 820: only type-T rules are
97
+ * eligible; `rule_coverage` 849-880 partitions exactly those; `recall_only` 875-878 is sliced too)
98
+ * but NOT the gate: `evaluate_sample` runs `select_any` over every active rule whatever its type
99
+ * (line 901) and a row's `fired` keeps every blocking id it produced (906-916). So under a filter a
100
+ * row may fire a rule OUTSIDE the denominator — codex's reproduction: a development-filtered run
101
+ * with `fired: ["SECURITY-DENY"]` and `exercised: 0` is a VALID record (the security rule is not a
102
+ * development rule), and the unfiltered invariant `exercised ≥ |fired|` does not hold. Hence, per
103
+ * record ({@link EvalCoverageReconciliation}, reported per side in `coverage_reconciliation`):
104
+ *
105
+ * type_filter null `'rows'` — every active rule is in the denominator, so the rows'
106
+ * blocking-fired ids are exercised rules: `exercised ≥ |fired|`, and
107
+ * the exercised set is complete iff `exercised === |fired|`.
108
+ * type_filter T, per_type `'per_type'` — reconciled against the engine's OWN row for the slice
109
+ * present (`rule_coverage.per_type[T]`, `TypeCoverage` lines 300-303):
110
+ * `per_type[T].exercised === exercised`; the rows' fired ids carry no
111
+ * steering type and are NOT a denominator check.
112
+ * type_filter T, no `'n/a (engine reports no per-type coverage)'` — the record carries
113
+ * per_type nothing to check its slice against; nothing fired-based is asserted.
114
+ *
115
+ * Whatever the filter: `per_type`, when present, must sum to `exercised` and agree per type with the
116
+ * listed `unexercised` rows; under a filter every listed row is of the filter's type; no id is
117
+ * listed twice; and no listed-unexercised id fired — that row TYPES the rule into the slice, and a
118
+ * blocking firing is a firing, which makes an eligible rule exercised (862-873) — so this last check
119
+ * is sound under a filter too (the fix brief's floor was "unfiltered only"; the engine's definition
120
+ * makes it general).
121
+ *
122
+ * Two runs under DIFFERENT filters get NO `rule_coverage_delta` (an inventory diff across
123
+ * denominators would report the other slice's rules as changes) and are not comparable. Under the
124
+ * SAME filter T the delta is computed exactly as unfiltered — `gained`/`lost` from ids listed on both
125
+ * ends (the unexercised row carries `steering_type: T`, the firing is a firing), every one-sided id
126
+ * withheld — except that `unidentified` is the whole `exercised` count (see above) and each withheld
127
+ * reason adds that the id may be a rule of another type outside the denominator.
128
+ *
129
+ * # Malformed persisted coverage (codex round 8)
130
+ *
131
+ * The daemon persists a run's `rule_coverage` verbatim and validates none of it (the script's
132
+ * `verifyEngineReport` gates only its own reports). A recorded `rule_coverage` that is not the wire
133
+ * shape — `null`; `unexercised` not an array; `per_type: null`, a row that is not `{ exercised,
134
+ * unexercised }` of non-negative integers, a key that is no steering type … — cannot be reconciled:
135
+ * that side is `coverage_reconciliation: 'unverified (malformed rule_coverage)'`
136
+ * ({@link MALFORMED_RULE_COVERAGE}), a reconciliation error names the run and the defect, no
137
+ * `rule_coverage_delta` is computed, and the comparison never throws over persisted data.
138
+ *
139
+ * # Malformed persisted result rows (Copilot on #475)
140
+ *
141
+ * The same store validates a detail's `results` only as "an array" (`EvalRunStore.get()`), so a row
142
+ * that is not the wire shape (`GovernanceEvalResult` — no `sample`, `sample.id` not a non-empty
143
+ * string, a `kind` or `verdict` outside its union, `fired` not an array of strings, a row that is no
144
+ * object at all) can reach the comparison from a corrupted or hand-edited file. Every row is checked
145
+ * ({@link resultRowProblem}) BEFORE it is indexed, tallied or iterated: a malformed one is ONE
146
+ * reconciliation error naming the run, the index and the defect, and is EXCLUDED from everything —
147
+ * the id index, the identity counts, the tally, the coverage `fired` set. The pair is then not
148
+ * comparable ({@link MALFORMED_RESULT_ROWS}), and that side's stored summary is NOT checked against a
149
+ * results list the comparison could not fully read (the shortfall is the excluded rows, not a summary
150
+ * defect — reporting it as one would misattribute it). Never a throw.
151
+ */
152
+ import type { EvalRunDetail, GovernanceEvalResult, GovernanceEvalSummary, SteeringType } from '../core/types.js';
153
+ export type EvalVerdict = GovernanceEvalResult['verdict'];
154
+ export type EvalSampleKind = GovernanceEvalResult['sample']['kind'];
155
+ export type FlipClass = 'permitted' | 'flagged';
156
+ /** The exact prefix of `comparable_reason` when a side carries result rows without a payload hash. */
157
+ export declare const UNVERIFIED_NO_SAMPLE_IDENTITY = "unverified: no sample identity";
158
+ /** The exact prefix of `comparable_reason` when the two runs were produced under different type filters. */
159
+ export declare const DIFFERING_TYPE_FILTER = "differing-type-filter";
160
+ /** One identity dimension, side by side. */
161
+ export interface IdentityPair<T> {
162
+ a: T;
163
+ b: T;
164
+ same: boolean;
165
+ }
166
+ /** One sample whose verdict differs between the two runs. */
167
+ export interface EvalVerdictFlip {
168
+ sample_id: string;
169
+ /** The sample's kind in run B (equal to run A's unless the id is also in `kind_changed`). */
170
+ kind: EvalSampleKind;
171
+ from: EvalVerdict;
172
+ to: EvalVerdict;
173
+ classification: FlipClass;
174
+ reason: string;
175
+ }
176
+ /** Whether the two records LIST their rule sets. `complete` requires an explicit rule inventory on
177
+ * BOTH records; no field of `GovernanceEvalRuleCoverage` (api-types 0.27.0) carries one — `exercised`
178
+ * and `recall_only` are counts, `unexercised` names only the unexercised rules, a row's `fired` only
179
+ * the blocking firings — so every comparison of daemon-recorded runs is `partial` and the rule-set
180
+ * transitions are withheld by name. See the module doc. */
181
+ export type EvalRuleInventory = 'complete' | 'partial';
182
+ /** The exact `coverage_reconciliation` value of a side whose persisted `rule_coverage` is not the wire
183
+ * shape — unverified, not reconciled, no delta computed over it (module doc, "Malformed persisted
184
+ * coverage"). */
185
+ export declare const MALFORMED_RULE_COVERAGE = "unverified (malformed rule_coverage)";
186
+ /** The `comparable_reason` prefix of a pair one side of which persisted result rows that are not the
187
+ * wire shape (api-types `GovernanceEvalResult`, module doc "Malformed persisted result rows"): each
188
+ * is named in `reconciliation_errors` and EXCLUDED, never thrown over — and a comparison over a
189
+ * record it could not fully read asserts nothing (Copilot on #475). */
190
+ export declare const MALFORMED_RESULT_ROWS = "unverified: malformed result row(s)";
191
+ /**
192
+ * How one record's `rule_coverage` was reconciled — which denominator its numbers were checked
193
+ * against (module doc, "Type filters"):
194
+ * - `'rows'` — `type_filter` null: every active rule is in the denominator, so the rows' distinct
195
+ * blocking-fired ids are exercised rules and `exercised ≥ |fired|` is checked;
196
+ * - `'per_type'` — `type_filter` set and `per_type` present: checked against the engine's own row
197
+ * for that type, `per_type[<filter>]`; the rows' fired ids may belong to other types;
198
+ * - `'n/a (engine reports no per-type coverage)'` — `type_filter` set and no `per_type`: the
199
+ * denominator is the filter's slice and the record carries no number to check it against;
200
+ * - {@link MALFORMED_RULE_COVERAGE} — the persisted value is not the wire shape: nothing was
201
+ * checked, a reconciliation error names the defect, no delta is computed over this side.
202
+ */
203
+ export type EvalCoverageReconciliation = 'rows' | 'per_type' | 'n/a (engine reports no per-type coverage)' | typeof MALFORMED_RULE_COVERAGE;
204
+ /** Which rules gained or lost exercise between the runs (present only when BOTH carry WELL-FORMED
205
+ * coverage AND both were produced under the same `type_filter`). */
206
+ export interface EvalRuleCoverageDelta {
207
+ /** `b.rule_coverage.exercised − a.rule_coverage.exercised` — the counts, always. */
208
+ exercised_delta: number;
209
+ /** `partial` for every comparison of daemon-recorded runs: the wire carries no rule inventory, so
210
+ * `added_rules`/`removed_rules` are never asserted and every one-sided id is in
211
+ * `transitions_withheld`. `complete` is reserved for two records that LIST their rule sets. */
212
+ inventory: EvalRuleInventory;
213
+ /** Per side, how many exercised rules of its denominator the record counts but does not name —
214
+ * a fact about THAT record alone, never reduced by what the other run lists. Unfiltered:
215
+ * `rule_coverage.exercised − |distinct blocking-fired ids|` (the rules exercised by a `warn`
216
+ * effect alone). Under a type filter: `rule_coverage.exercised` — a fired id carries no
217
+ * steering_type, so the record types none of its own firings into the slice. */
218
+ unidentified: {
219
+ a: number;
220
+ b: number;
221
+ };
222
+ /** Unexercised in A (listed) AND blocking-fired in B (listed) — a sample now exercises the rule.
223
+ * A statement about the RULE, certain on both ends whatever the filter (under one, the id is typed
224
+ * into the slice by A's row alone — B's `unidentified` does not subtract it). */
225
+ gained: string[];
226
+ /** Blocking-fired in A AND unexercised in B (listed) — a rule nothing exercises any more. Certain. */
227
+ lost: string[];
228
+ /** Enumerated by B and not by A AND absent from A's explicit rule inventory — the wire carries no
229
+ * inventory, so this is EMPTY for every daemon-recorded run (the candidates are in
230
+ * `transitions_withheld`). */
231
+ added_rules: string[];
232
+ /** Enumerated by A and not by B AND absent from B's explicit rule inventory — EMPTY for the same
233
+ * reason. */
234
+ removed_rules: string[];
235
+ /** Every id enumerated (unexercised or blocking-fired) by one record and not the other, one reason
236
+ * per id, codepoint-sorted — what the id may be besides a rule-set change: an exercised rule the
237
+ * silent side never names (a `warn`-only firing; under a type filter any of its firings), an
238
+ * effect-less (recall-only) or retired rule outside the eligible partition, under a type filter a
239
+ * rule of another type outside the denominator. WITHHELD, never guessed. */
240
+ transitions_withheld: string[];
241
+ }
242
+ export interface EvalRunComparison {
243
+ /** The two run ids, in the order given (A = the baseline, B = the candidate). */
244
+ a: string;
245
+ b: string;
246
+ identity: {
247
+ corpus: IdentityPair<string | null>;
248
+ rule_store: IdentityPair<string>;
249
+ type_filter: IdentityPair<SteeringType | null>;
250
+ degraded: IdentityPair<'facet-only' | null>;
251
+ };
252
+ /** Same corpus name, same type filter, same sample ids, same kinds, same payload identities on
253
+ * every row — the same actions were judged over the same slice, provably. */
254
+ comparable: boolean;
255
+ /** Why `comparable` is false (`;`-joined when several apply; starts with
256
+ * {@link UNVERIFIED_NO_SAMPLE_IDENTITY} when a side carries rows without a payload hash, contains
257
+ * {@link DIFFERING_TYPE_FILTER} when the filters differ), or `null` when it is true. */
258
+ comparable_reason: string | null;
259
+ /** Every per-sample verdict change, codepoint-sorted by sample id, each classified. */
260
+ flips: EvalVerdictFlip[];
261
+ /** Shared samples with the same kind AND the same verdict on both sides, minus those whose payload
262
+ * identity is VERIFIED to differ (`payload_changed`). A row unverified on either side (no
263
+ * well-formed `payload_hash`) still counts when kind and verdict agree — `unchanged` is a verdict
264
+ * statement, not proof of payload identity; `comparable` / `unverified_rows` carry that claim. */
265
+ unchanged: number;
266
+ only_in_a: string[];
267
+ only_in_b: string[];
268
+ /** Shared ids whose `kind` differs — the corpus changed under this name (always flagged). */
269
+ kind_changed: string[];
270
+ /** Shared ids whose `payload_hash` differs on the two sides (both present) — the description,
271
+ * steering type or signals changed under the same id and kind: the corpus changed under this
272
+ * name (a flip on such an id is always flagged). */
273
+ payload_changed: string[];
274
+ /** Result rows WITHOUT a `sample.payload_hash`, per side — the rows whose identity is unverified. */
275
+ unverified_rows: {
276
+ a: number;
277
+ b: number;
278
+ };
279
+ /** `b.summary − a.summary`, field by field. */
280
+ summary_delta: GovernanceEvalSummary;
281
+ /** Each run's summary equals its own results' tally AND the delta equals the flip/add/remove
282
+ * accounting AND each run's coverage is well-formed and does not contradict itself (see
283
+ * `coverage_reconciliation` for what it was checked against) — a stored record that disagrees
284
+ * with ITSELF, or carries a `rule_coverage` that is not the wire shape, is a defect, not a flip.
285
+ * Two valid records always reconcile; an exercised count ABOVE the fired ids is valid (warn-only
286
+ * exercise), and so is a fired id outside a filtered run's denominator. */
287
+ reconciles: boolean;
288
+ reconciliation_errors: string[];
289
+ /** Per side, how the record's `rule_coverage` was reconciled ({@link EvalCoverageReconciliation};
290
+ * {@link MALFORMED_RULE_COVERAGE} when the persisted value is not the wire shape), or `null` when
291
+ * that side carries no coverage (a pre-#394 engine). */
292
+ coverage_reconciliation: {
293
+ a: EvalCoverageReconciliation | null;
294
+ b: EvalCoverageReconciliation | null;
295
+ };
296
+ /** Present only when BOTH sides carry WELL-FORMED coverage AND `identity.type_filter.same`. */
297
+ rule_coverage_delta?: EvalRuleCoverageDelta;
298
+ }
299
+ /** The S17 rule table, as a function. Exported so a caller can classify a single flip. */
300
+ export declare function classifyFlip(kind: EvalSampleKind, from: EvalVerdict, to: EvalVerdict): {
301
+ classification: FlipClass;
302
+ reason: string;
303
+ };
304
+ /** Compare two recorded runs — A is the baseline, B the candidate. See the module doc. */
305
+ export declare function compareEvalRuns(a: EvalRunDetail, b: EvalRunDetail): EvalRunComparison;
306
+ //# sourceMappingURL=eval-compare.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eval-compare.d.ts","sourceRoot":"","sources":["../../src/api/eval-compare.ts"],"names":[],"mappings":"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"}