progmune-runtime 2.1.5 → 3.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 (356) hide show
  1. package/README.md +108 -468
  2. package/dist/ablation-study.js +144 -0
  3. package/dist/ablation-study.test.js +18 -0
  4. package/dist/action-runtime.js +3 -1
  5. package/dist/active-learning.js +211 -0
  6. package/dist/analytics.js +139 -0
  7. package/dist/asset-factory.js +309 -0
  8. package/dist/asset-growth.js +244 -0
  9. package/dist/asset-promotion.js +382 -0
  10. package/dist/asset-quality.js +550 -0
  11. package/dist/audit/business-translator.js +285 -0
  12. package/dist/audit/cli.js +66 -0
  13. package/dist/audit/formatters/html.js +379 -0
  14. package/dist/audit/formatters/json.js +11 -0
  15. package/dist/audit/formatters/markdown.js +192 -0
  16. package/dist/audit/formatters/terminal.js +189 -0
  17. package/dist/audit/index.js +25 -0
  18. package/dist/audit/report-builder.js +318 -0
  19. package/dist/audit/types.js +8 -0
  20. package/dist/audit.js +3 -3
  21. package/dist/auto-benchmark-generator.js +137 -0
  22. package/dist/auto-benchmark-generator.test.js +45 -0
  23. package/dist/auto-protocol-synthesizer.js +362 -0
  24. package/dist/auto-protocol-synthesizer.test.js +82 -0
  25. package/dist/autonomous-patch.js +175 -0
  26. package/dist/autonomous-patch.test.js +128 -0
  27. package/dist/badge/badge-server.js +98 -0
  28. package/dist/behavior-miner.js +442 -0
  29. package/dist/belief-layer.js +475 -0
  30. package/dist/benchmark-count.js +5 -0
  31. package/dist/benchmark-generator.js +211 -0
  32. package/dist/benchmark-harness.js +201 -0
  33. package/dist/benchmark-pass-rate.js +7 -0
  34. package/dist/benchmark-report.js +8 -3
  35. package/dist/benchmark-save.js +14 -1
  36. package/dist/bootstrap-validation.js +197 -0
  37. package/dist/bootstrap-validation.test.js +51 -0
  38. package/dist/branch-ledger.js +1 -1
  39. package/dist/capability-gap.js +130 -0
  40. package/dist/certify-html.js +351 -0
  41. package/dist/certify.js +326 -0
  42. package/dist/check.js +4 -4
  43. package/dist/compliance-miner.js +447 -0
  44. package/dist/continuous-benchmark.js +194 -0
  45. package/dist/continuous-benchmark.test.js +116 -0
  46. package/dist/corpus-stats.js +173 -0
  47. package/dist/counterfactual-engine.js +288 -0
  48. package/dist/coverage-dashboard.js +109 -0
  49. package/dist/coverage-system.test.js +205 -0
  50. package/dist/cross-repo-precision.js +352 -0
  51. package/dist/cve-benchmark.js +180 -0
  52. package/dist/cve-benchmark.test.js +28 -0
  53. package/dist/cve-collector.js +73 -0
  54. package/dist/data-quality.js +141 -0
  55. package/dist/decision-engine.js +388 -0
  56. package/dist/derive-metadata.js +250 -0
  57. package/dist/difficulty-active.test.js +198 -0
  58. package/dist/difficulty-map.js +244 -0
  59. package/dist/discovery-analytics.js +125 -0
  60. package/dist/discovery-model.js +149 -0
  61. package/dist/discovery-optimize.test.js +199 -0
  62. package/dist/discovery-trace.js +276 -0
  63. package/dist/discovery-trace.test.js +97 -0
  64. package/dist/emitter.js +83 -1
  65. package/dist/enterprise-dashboard.js +405 -0
  66. package/dist/eval-hardening.js +297 -0
  67. package/dist/eval-hardening.test.js +85 -0
  68. package/dist/evaluation-campaign.js +359 -0
  69. package/dist/evaluation-campaign.test.js +181 -0
  70. package/dist/evidence-growth.js +143 -0
  71. package/dist/evidence-repository.js +209 -0
  72. package/dist/evidence-system.js +441 -0
  73. package/dist/execute.js +15 -7
  74. package/dist/experimental/software-physics.js +291 -0
  75. package/dist/experimental/state-inference.js +516 -0
  76. package/dist/experimental/unsupervised-physics.js +230 -0
  77. package/dist/extract-ir-python.js +54 -7
  78. package/dist/extract-ir.js +376 -12
  79. package/dist/failure-collector.js +2 -2
  80. package/dist/failure-corpus.js +322 -9
  81. package/dist/feedback.js +16 -5
  82. package/dist/feedback.test.js +49 -0
  83. package/dist/file-lock.js +1 -1
  84. package/dist/flywheel-batch.js +292 -0
  85. package/dist/frameworks/express-cli.js +237 -0
  86. package/dist/frameworks/express-detector.js +445 -0
  87. package/dist/frameworks/express-detector.test.js +206 -0
  88. package/dist/frameworks/index.js +30 -0
  89. package/dist/frameworks/nestjs-detector.js +302 -0
  90. package/dist/frameworks/trpc-detector.js +161 -0
  91. package/dist/frameworks/version-awareness.js +179 -0
  92. package/dist/function-synonyms.js +164 -0
  93. package/dist/function-synonyms.test.js +68 -0
  94. package/dist/generalization.test.js +352 -0
  95. package/dist/goal-annotator.js +113 -0
  96. package/dist/goal-planner.js +563 -0
  97. package/dist/gold-cve.js +164 -0
  98. package/dist/gold-cve.test.js +104 -0
  99. package/dist/gold-quality.js +206 -0
  100. package/dist/gold-tiers.js +241 -0
  101. package/dist/governance-dashboard.js +327 -0
  102. package/dist/graph-viz.js +240 -0
  103. package/dist/guided-frontier.js +195 -0
  104. package/dist/hierarchical-planner.js +148 -0
  105. package/dist/identifier-parser.js +260 -0
  106. package/dist/immune-metrics.js +93 -0
  107. package/dist/immune-receiver.js +158 -0
  108. package/dist/immune-reporter.js +1 -1
  109. package/dist/improvement-orchestrator.js +206 -0
  110. package/dist/inject-p0-vocabulary.js +300 -0
  111. package/dist/intent-parser.js +218 -0
  112. package/dist/invariant-algebra.js +476 -0
  113. package/dist/invariant-calculus.js +533 -0
  114. package/dist/ir-utils.js +70 -0
  115. package/dist/ir-utils.test.js +50 -0
  116. package/dist/knowledge-api.js +312 -0
  117. package/dist/knowledge-evolution.js +452 -0
  118. package/dist/knowledge-explorer.js +506 -0
  119. package/dist/knowledge-flywheel.js +274 -0
  120. package/dist/knowledge-governance.js +338 -0
  121. package/dist/knowledge-governance.test.js +150 -0
  122. package/dist/knowledge-graph.js +181 -0
  123. package/dist/knowledge-guided-synth.js +246 -0
  124. package/dist/knowledge-loop.test.js +77 -0
  125. package/dist/knowledge-object.js +316 -0
  126. package/dist/knowledge-package.js +98 -0
  127. package/dist/kpi-dashboard.js +561 -0
  128. package/dist/l3-cross-function.js +280 -0
  129. package/dist/learning-ranker.js +148 -0
  130. package/dist/learning-ranker.test.js +291 -0
  131. package/dist/ledger/accountability.js +322 -0
  132. package/dist/ledger/chain-builder.js +185 -0
  133. package/dist/ledger/cli.js +222 -0
  134. package/dist/ledger/index.js +13 -0
  135. package/dist/ledger/signatures.js +193 -0
  136. package/dist/ledger/types.js +9 -0
  137. package/dist/llm.js +74 -3
  138. package/dist/load-benchmarks.js +8 -3
  139. package/dist/logger.js +66 -0
  140. package/dist/logger.test.js +37 -0
  141. package/dist/logistic-reward.js +339 -0
  142. package/dist/logistic-reward.test.js +180 -0
  143. package/dist/macro-graph.js +193 -0
  144. package/dist/macro-repair.js +183 -0
  145. package/dist/mcp-server.mjs +1202 -483
  146. package/dist/memory-layer.js +42 -5
  147. package/dist/multi-repo-precision.js +422 -0
  148. package/dist/name-free-protocol.js +425 -0
  149. package/dist/name-free-protocol.test.js +170 -0
  150. package/dist/name-scrambling.js +138 -0
  151. package/dist/name-scrambling.test.js +16 -0
  152. package/dist/p3-observability.test.js +281 -0
  153. package/dist/p5-orchestrator.test.js +225 -0
  154. package/dist/pairwise-preference.js +294 -0
  155. package/dist/pairwise-preference.test.js +140 -0
  156. package/dist/planner-constraints.js +104 -0
  157. package/dist/planner-prompts.js +155 -0
  158. package/dist/planner-telemetry.js +415 -0
  159. package/dist/planner-trace.js +214 -0
  160. package/dist/planner.js +162 -167
  161. package/dist/plsb/artifact.js +116 -0
  162. package/dist/plsb/cli.js +71 -0
  163. package/dist/plsb/index.js +19 -0
  164. package/dist/plsb/leaderboard.js +249 -0
  165. package/dist/plsb/report-md.js +156 -0
  166. package/dist/plsb/schema.js +179 -0
  167. package/dist/plsb-benchmark.js +284 -0
  168. package/dist/plsb-benchmark.test.js +119 -0
  169. package/dist/policy/cli.js +134 -0
  170. package/dist/policy/engine.js +333 -0
  171. package/dist/policy/index.js +12 -0
  172. package/dist/policy/types.js +59 -0
  173. package/dist/policy-miner.js +505 -0
  174. package/dist/precision-analyze.js +229 -0
  175. package/dist/precision-benchmark.js +147 -0
  176. package/dist/precision-label-c.js +134 -0
  177. package/dist/precision-label.js +193 -0
  178. package/dist/precision-report-c.js +149 -0
  179. package/dist/precision-report.js +246 -0
  180. package/dist/progmune-status.js +108 -0
  181. package/dist/proof-engine.js +479 -0
  182. package/dist/proof-provenance.js +315 -0
  183. package/dist/protocol-coverage.js +294 -0
  184. package/dist/protocol-detector.js +1189 -0
  185. package/dist/protocol-embedding-expanded.js +297 -0
  186. package/dist/protocol-embedding-expanded.test.js +97 -0
  187. package/dist/protocol-embedding.js +195 -0
  188. package/dist/protocol-embedding.test.js +82 -0
  189. package/dist/protocol-extractor-v2.js +354 -0
  190. package/dist/protocol-extractor-v2.test.js +140 -0
  191. package/dist/protocol-extractor.js +310 -0
  192. package/dist/protocol-extractor.test.js +113 -0
  193. package/dist/protocol-foundation.js +322 -0
  194. package/dist/protocol-foundation.test.js +163 -0
  195. package/dist/protocol-frontier.js +243 -0
  196. package/dist/protocol-frontier.test.js +92 -0
  197. package/dist/protocol-gap-analyzer.js +228 -0
  198. package/dist/protocol-gap-analyzer.test.js +49 -0
  199. package/dist/protocol-invariants.js +276 -0
  200. package/dist/protocol-invariants.test.js +111 -0
  201. package/dist/protocol-knowledge.js +464 -0
  202. package/dist/protocol-miner.js +343 -0
  203. package/dist/protocol-mining.js +207 -0
  204. package/dist/protocol-mining.test.js +37 -0
  205. package/dist/protocol-registry.js +1 -1
  206. package/dist/protocol-security-benchmark.js +222 -0
  207. package/dist/protocol-vulnerability.js +257 -0
  208. package/dist/protocol-vulnerability.test.js +60 -0
  209. package/dist/python-benchmark.js +120 -0
  210. package/dist/python-emitter.js +163 -45
  211. package/dist/python-protocol-extractor.js +187 -0
  212. package/dist/python-protocol-extractor.test.js +116 -0
  213. package/dist/realworld-benchmark.js +646 -0
  214. package/dist/realworld-benchmark.test.js +36 -0
  215. package/dist/repair-arch.test.js +411 -0
  216. package/dist/repair-evolution.test.js +454 -0
  217. package/dist/repair-executor.js +719 -0
  218. package/dist/repair-proposal.js +4 -4
  219. package/dist/repair-ranker.js +141 -0
  220. package/dist/repair-strategies.js +419 -0
  221. package/dist/repair-taxonomy.js +234 -0
  222. package/dist/repair-types.js +12 -0
  223. package/dist/repo-evaluator.js +250 -0
  224. package/dist/repo-evaluator.test.js +128 -0
  225. package/dist/resource-abstraction.js +242 -0
  226. package/dist/resource-detector.js +211 -0
  227. package/dist/result.test.js +43 -0
  228. package/dist/reward-system.js +411 -0
  229. package/dist/reward-system.test.js +175 -0
  230. package/dist/risk-model.js +215 -0
  231. package/dist/rule-miner.js +234 -7
  232. package/dist/rule-specificity.js +254 -0
  233. package/dist/runtime-types.js +27 -0
  234. package/dist/scaffold.js +208 -0
  235. package/dist/scale-collector.test.js +101 -0
  236. package/dist/scale-trajectory-collector.js +128 -0
  237. package/dist/sdk.js +250 -0
  238. package/dist/search-planner.js +4 -41
  239. package/dist/semantic-snapshot.js +1 -1
  240. package/dist/semantic-topology.js +121 -0
  241. package/dist/semantic-trace.js +310 -317
  242. package/dist/sequence-extractor.js +343 -0
  243. package/dist/skill-library.js +245 -0
  244. package/dist/skill-planner.test.js +189 -0
  245. package/dist/software-physics.js +291 -0
  246. package/dist/software-physics.test.js +81 -0
  247. package/dist/ssg-precision.js +478 -0
  248. package/dist/ssg-validator.js +71 -21
  249. package/dist/state-inference-doubleblind.test.js +160 -0
  250. package/dist/state-inference.js +516 -0
  251. package/dist/state-inference.test.js +115 -0
  252. package/dist/state-machine-fingerprint.js +345 -0
  253. package/dist/state-machine-fingerprint.test.js +120 -0
  254. package/dist/state-miner.js +386 -0
  255. package/dist/state-name-inference.js +213 -0
  256. package/dist/state-name-inference.test.js +69 -0
  257. package/dist/strategy-planner.js +326 -59
  258. package/dist/strategy-planner.test.js +135 -0
  259. package/dist/telemetry-analytics.test.js +402 -0
  260. package/dist/terminal-format.js +68 -0
  261. package/dist/terminal-format.test.js +83 -0
  262. package/dist/topology-factory.js +196 -0
  263. package/dist/topology-representation.js +242 -0
  264. package/dist/topology-representation.test.js +27 -0
  265. package/dist/trajectory-augmentation.js +254 -0
  266. package/dist/trajectory-augmentation.test.js +63 -0
  267. package/dist/trajectory-corpus.js +440 -0
  268. package/dist/trajectory-corpus.test.js +32 -0
  269. package/dist/trajectory-feedback.test.js +116 -0
  270. package/dist/transition-synthesizer.js +286 -0
  271. package/dist/transition-synthesizer.test.js +123 -0
  272. package/dist/trust/api-semantic-mapper.js +809 -0
  273. package/dist/trust/call-graph-propagator.js +225 -0
  274. package/dist/trust/cli.js +122 -0
  275. package/dist/trust/compliance-scorer.js +283 -0
  276. package/dist/trust/confidence-calculator.js +261 -0
  277. package/dist/trust/engine.js +1145 -0
  278. package/dist/trust/explainability.js +85 -0
  279. package/dist/trust/formatters/ci.js +42 -0
  280. package/dist/trust/formatters/json.js +11 -0
  281. package/dist/trust/formatters/terminal.js +152 -0
  282. package/dist/trust/index.js +39 -0
  283. package/dist/trust/phase1-verify.js +171 -0
  284. package/dist/trust/protocol-domain-validator.js +697 -0
  285. package/dist/trust/score-calculator.js +282 -0
  286. package/dist/trust/ssg-bridge.js +641 -0
  287. package/dist/trust/ssg-bridge.test.js +269 -0
  288. package/dist/trust/types.js +67 -0
  289. package/dist/trust/violation-trace.js +335 -0
  290. package/dist/trust-api.js +179 -0
  291. package/dist/trust-calibration.js +279 -0
  292. package/dist/unknown-protocol-discovery.js +339 -0
  293. package/dist/unknown-protocol-discovery.test.js +102 -0
  294. package/dist/unsupervised-physics.js +230 -0
  295. package/dist/unsupervised-physics.test.js +95 -0
  296. package/dist/utils.test.js +37 -0
  297. package/dist/validator.js +187 -10
  298. package/dist/verification-intelligence.js +475 -0
  299. package/dist/verify-api.js +432 -0
  300. package/dist/vi-impact-report.js +293 -0
  301. package/dist/wl-fingerprint.js +162 -0
  302. package/dist/wl-fingerprint.test.js +130 -0
  303. package/dist/zeroshot-strategy.js +139 -0
  304. package/docs/Progmune_/346/212/225/350/265/204/344/272/272/347/231/275/347/232/256/344/271/246_v2.0.html +576 -0
  305. package/docs/Progmune_/351/241/271/347/233/256/345/205/250/350/247/243.html +710 -0
  306. package/package.json +74 -7
  307. package/protocols.json +1956 -50
  308. package/.dockerignore +0 -14
  309. package/.mcp.json +0 -11
  310. package/.progmune_allowlist +0 -50
  311. package/.test_report/test_report.md +0 -87
  312. package/Dockerfile +0 -9
  313. package/FAQ.md +0 -167
  314. package/WHITEPAPER.md +0 -540
  315. package/demo-project/auth.ts +0 -55
  316. package/demo-project/tsconfig.json +0 -8
  317. package/dist/acl-breakdown.js +0 -13
  318. package/dist/all-sessions.js +0 -11
  319. package/dist/antibody-stats.js +0 -11
  320. package/dist/branch-tree-count.js +0 -14
  321. package/dist/common-fixpath.js +0 -12
  322. package/dist/constraint-types.js +0 -12
  323. package/dist/exec-metrics.js +0 -11
  324. package/dist/failure-report.js +0 -11
  325. package/dist/fast-path-hits.js +0 -13
  326. package/dist/fingerprint-list.js +0 -15
  327. package/dist/gen-history-log.js +0 -13
  328. package/dist/heatmap-data.js +0 -11
  329. package/dist/recent-session.js +0 -12
  330. package/dist/svl-distribution.js +0 -11
  331. package/dist/terminal-status.js +0 -11
  332. package/dist/token-savings.js +0 -11
  333. package/dist/total-repairs.js +0 -12
  334. package/dist/unresolved-count.js +0 -12
  335. package/dist/valid-fingerprints.js +0 -13
  336. package/dist/verify-ledgers.js +0 -11
  337. package/docs/whitepaper-style.css +0 -77
  338. package/docs/whitepaper-v2.1.md +0 -609
  339. package/docs/whitepaper-v2.2.md +0 -1064
  340. package/docs/whitepaper-v2.2.pdf +0 -0
  341. package/fly.toml +0 -31
  342. package/public/dashboard.html +0 -119
  343. package/server/hub.js +0 -116
  344. package/test/replay-golden/sess_1780063202050_mgeld.json +0 -9
  345. package/test/replay-golden/sess_1780064032560_gocld.json +0 -354
  346. package/test/replay-golden/sess_1780064413331_s2709.json +0 -606
  347. package/test/replay-golden/sess_1780064792710_y3avo.json +0 -614
  348. package/test/replay-golden.ts +0 -84
  349. package/test_benchmark.js +0 -165
  350. package/test_comprehensive.mjs +0 -638
  351. package/test_concurrency.js +0 -129
  352. package/test_ir_robustness.js +0 -85
  353. package/test_semantic_contracts.js +0 -269
  354. package/test_ssg_stress.js +0 -156
  355. package/test_svl3.js +0 -58
  356. package/tsconfig.json +0 -17
@@ -0,0 +1,291 @@
1
+ "use strict";
2
+ /**
3
+ * P2.6: Ranking Evolution Test — Self-Improving Loop Verification
4
+ *
5
+ * Proves that the feedback loop works:
6
+ * Repair A accepted 90 times, Repair B accepted 10 times
7
+ * → LearningRanker ranks A > B
8
+ * → Then Repair B accepted 100 more times
9
+ * → LearningRanker now ranks B > A
10
+ *
11
+ * This is the first proof that Progmune is a Learning System,
12
+ * not just a Rule System.
13
+ */
14
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ var desc = Object.getOwnPropertyDescriptor(m, k);
17
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
18
+ desc = { enumerable: true, get: function() { return m[k]; } };
19
+ }
20
+ Object.defineProperty(o, k2, desc);
21
+ }) : (function(o, m, k, k2) {
22
+ if (k2 === undefined) k2 = k;
23
+ o[k2] = m[k];
24
+ }));
25
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
26
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
27
+ }) : function(o, v) {
28
+ o["default"] = v;
29
+ });
30
+ var __importStar = (this && this.__importStar) || (function () {
31
+ var ownKeys = function(o) {
32
+ ownKeys = Object.getOwnPropertyNames || function (o) {
33
+ var ar = [];
34
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
35
+ return ar;
36
+ };
37
+ return ownKeys(o);
38
+ };
39
+ return function (mod) {
40
+ if (mod && mod.__esModule) return mod;
41
+ var result = {};
42
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
43
+ __setModuleDefault(result, mod);
44
+ return result;
45
+ };
46
+ })();
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ const vitest_1 = require("vitest");
49
+ const fs = __importStar(require("fs"));
50
+ const path = __importStar(require("path"));
51
+ const planner_telemetry_1 = require("./planner-telemetry");
52
+ const learning_ranker_1 = require("./learning-ranker");
53
+ const repair_ranker_1 = require("./repair-ranker");
54
+ const analytics_1 = require("./analytics");
55
+ // ── Test setup ──
56
+ const EVO_DIR = path.resolve(__dirname, "..", "test-ranking-evolution");
57
+ process.env.PROGMUNE_PROJECT_DIR = EVO_DIR;
58
+ fs.mkdirSync(EVO_DIR, { recursive: true });
59
+ fs.mkdirSync(path.join(EVO_DIR, ".progmune_corpus", "telemetry"), { recursive: true });
60
+ fs.mkdirSync(path.join(EVO_DIR, ".progmune_corpus", "lifecycles"), { recursive: true });
61
+ const ctx = {
62
+ protocol: "FileProtocol",
63
+ currentState: ["FILE_OPEN"],
64
+ targetState: [],
65
+ violationType: "resource_leak",
66
+ constraints: [],
67
+ rules: new Map(),
68
+ };
69
+ function makeCandidate(id, source, actions) {
70
+ return {
71
+ id,
72
+ source,
73
+ actions: actions.map(fn => ({ kind: "call", function: fn, args: [] })),
74
+ explanation: `${id}: ${actions.join(" → ")}`,
75
+ };
76
+ }
77
+ (0, vitest_1.describe)("Ranking Evolution", () => {
78
+ (0, vitest_1.it)("accepted repairs rise over time (self-improving loop)", () => {
79
+ const runId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
80
+ const evoPath = path.join(EVO_DIR, ".progmune_corpus", "telemetry", `evo-${runId}.jsonl`);
81
+ const t = new planner_telemetry_1.PlannerTelemetry(evoPath);
82
+ const fpA = (0, planner_telemetry_1.candidateFingerprint)(`fp-${runId}`, ["open_file", "write_file", "close_file"], "resource_leak");
83
+ const fpB = (0, planner_telemetry_1.candidateFingerprint)(`fp-${runId}`, ["atomic_write"], "resource_leak");
84
+ // Use weights that favor historical success (learning test — not goal-match)
85
+ const base = (0, repair_ranker_1.createLinearRanker)({ safety: 0.4, successRate: 0.3, performance: 0.2, auditability: 0.1, goalMatch: 0 });
86
+ const learner = new learning_ranker_1.LearningRanker(base, t);
87
+ // Two candidates: A (safe) and B (fast)
88
+ const safeA = makeCandidate("safe", "protocol", ["open_file", "write_file", "close_file"]);
89
+ const fastB = makeCandidate("fast", "corpus", ["atomic_write"]);
90
+ const safeFeatures = {
91
+ protocolSafety: 1.0, historicalSuccessRate: 0.5, actionCount: 3,
92
+ latencyCost: 0.6, auditability: 0.8, corpusEvidence: 0, source: "protocol",
93
+ goalMatch: 0,
94
+ };
95
+ const fastFeatures = {
96
+ protocolSafety: 0.7, historicalSuccessRate: 0.99, actionCount: 1,
97
+ latencyCost: 0.1, auditability: 0.5, corpusEvidence: 42, source: "corpus",
98
+ goalMatch: 0,
99
+ };
100
+ // ── Phase 1: A accepted 90 times, B accepted 10 times ──
101
+ for (let i = 0; i < 90; i++) {
102
+ const id = t.recordDecision({
103
+ goal: "safely write config file",
104
+ protocol: "FileProtocol",
105
+ violationType: "resource_leak",
106
+ candidates: [
107
+ { candidateId: fpA, source: "protocol", evidenceSources: ["protocol"], actions: ["open_file", "write_file", "close_file"], explanation: "safe" },
108
+ { candidateId: fpB, source: "corpus", evidenceSources: ["corpus"], actions: ["atomic_write"], explanation: "fast" },
109
+ ],
110
+ selectedCandidateId: fpA,
111
+ });
112
+ t.recordFeedback(id, {
113
+ decision: "accepted",
114
+ executionResult: { success: true, violations: [] },
115
+ timestamp: Date.now(),
116
+ });
117
+ }
118
+ for (let i = 0; i < 10; i++) {
119
+ const id = t.recordDecision({
120
+ goal: "quick config update",
121
+ protocol: "FileProtocol",
122
+ violationType: "resource_leak",
123
+ candidates: [
124
+ { candidateId: fpA, source: "protocol", evidenceSources: ["protocol"], actions: ["open_file", "write_file", "close_file"], explanation: "safe" },
125
+ { candidateId: fpB, source: "corpus", evidenceSources: ["corpus"], actions: ["atomic_write"], explanation: "fast" },
126
+ ],
127
+ selectedCandidateId: fpB,
128
+ });
129
+ // B accepted but only 30% execution success → effective reward penalized
130
+ const execOk = i < 3;
131
+ t.recordFeedback(id, {
132
+ decision: "accepted",
133
+ executionResult: { success: execOk, violations: execOk ? [] : ["resource_leak"] },
134
+ timestamp: Date.now(),
135
+ });
136
+ }
137
+ // After phase 1: A should rank above B (A has higher effective reward)
138
+ const ctx1 = { protocol: `fp-${runId}`, violationType: "resource_leak" };
139
+ const ranked1 = learner.rank([safeA, fastB], [safeFeatures, fastFeatures], ctx1);
140
+ (0, vitest_1.expect)(ranked1[0].id).toBe("safe");
141
+ (0, vitest_1.expect)(ranked1[0].acceptance).toBeGreaterThan(0.8);
142
+ (0, vitest_1.expect)(ranked1[0].effectiveReward).toBe(1.0);
143
+ // Verify TelemetryIndex stats
144
+ const statsA1 = t.getCandidateStats(fpA);
145
+ (0, vitest_1.expect)(statsA1.accepted).toBe(90);
146
+ (0, vitest_1.expect)(statsA1.executionSuccess).toBe(90);
147
+ const statsB1 = t.getCandidateStats(fpB);
148
+ (0, vitest_1.expect)(statsB1.accepted).toBe(10);
149
+ (0, vitest_1.expect)(statsB1.executionFailure).toBe(7);
150
+ // ── Phase 2: B gets 100 more accepts → overtakes A ──
151
+ for (let i = 0; i < 100; i++) {
152
+ const id = t.recordDecision({
153
+ goal: "quick config update",
154
+ protocol: "FileProtocol",
155
+ violationType: "resource_leak",
156
+ candidates: [
157
+ { candidateId: fpA, source: "protocol", evidenceSources: ["protocol"], actions: ["open_file", "write_file", "close_file"], explanation: "safe" },
158
+ { candidateId: fpB, source: "corpus", evidenceSources: ["corpus"], actions: ["atomic_write"], explanation: "fast" },
159
+ ],
160
+ selectedCandidateId: fpB,
161
+ });
162
+ t.recordFeedback(id, {
163
+ decision: "accepted",
164
+ executionResult: { success: true, violations: [] },
165
+ timestamp: Date.now(),
166
+ });
167
+ // also record execution results for B
168
+ t.recordExecutionResult(id, true, [], 3);
169
+ }
170
+ // After phase 2: B should overtake A (110 accepts, 103 execution successes)
171
+ const ranked2 = learner.rank([safeA, fastB], [safeFeatures, fastFeatures], { protocol: `fp-${runId}`, violationType: "resource_leak" });
172
+ (0, vitest_1.expect)(ranked2[0].id).toBe("fast");
173
+ (0, vitest_1.expect)(ranked2[0].acceptance).toBeGreaterThan(0.9);
174
+ (0, vitest_1.expect)(ranked2[0].effectiveReward).toBeGreaterThan(0.9);
175
+ // Verify stats after evolution (>= because disk persistence may accumulate)
176
+ const statsB2 = t.getCandidateStats(fpB);
177
+ (0, vitest_1.expect)(statsB2.accepted).toBeGreaterThanOrEqual(110);
178
+ (0, vitest_1.expect)(statsB2.executionSuccess).toBeGreaterThanOrEqual(103);
179
+ // Adoption rate KPI
180
+ const adoptionRate = (0, analytics_1.getRepairAdoptionRate)(t);
181
+ (0, vitest_1.expect)(adoptionRate).toBe(1.0); // 200/200
182
+ });
183
+ (0, vitest_1.it)("rejected repairs sink in ranking", () => {
184
+ const t = new planner_telemetry_1.PlannerTelemetry(path.join(EVO_DIR, ".progmune_corpus", "telemetry", `sink-${Date.now()}.jsonl`));
185
+ const learner = new learning_ranker_1.LearningRanker((0, repair_ranker_1.createLinearRanker)(), t);
186
+ const good = makeCandidate("good", "protocol", ["verify_password", "generate_jwt"]);
187
+ const bad = makeCandidate("bad", "antibody", ["skip_auth"]);
188
+ const goodFp = (0, planner_telemetry_1.candidateFingerprint)("AuthProtocol", ["verify_password", "generate_jwt"], "missing_prerequisite");
189
+ const badFp = (0, planner_telemetry_1.candidateFingerprint)("AuthProtocol", ["skip_auth"], "missing_prerequisite");
190
+ const features = {
191
+ protocolSafety: 0.8, historicalSuccessRate: 0.5, actionCount: 2,
192
+ latencyCost: 0.3, auditability: 0.7, corpusEvidence: 0, source: "protocol",
193
+ };
194
+ // Good: accepted 50 times
195
+ for (let i = 0; i < 50; i++) {
196
+ const id = t.recordDecision({
197
+ goal: "authenticate user",
198
+ protocol: "AuthProtocol",
199
+ violationType: "missing_prerequisite",
200
+ candidates: [
201
+ { candidateId: goodFp, source: "protocol", evidenceSources: ["protocol"], actions: ["verify_password", "generate_jwt"], explanation: "proper auth" },
202
+ { candidateId: badFp, source: "antibody", evidenceSources: ["antibody"], actions: ["skip_auth"], explanation: "skip auth" },
203
+ ],
204
+ selectedCandidateId: goodFp,
205
+ });
206
+ t.recordFeedback(id, { decision: "accepted", timestamp: Date.now() });
207
+ }
208
+ // Bad: rejected 50 times
209
+ for (let i = 0; i < 50; i++) {
210
+ const id = t.recordDecision({
211
+ goal: "authenticate user",
212
+ protocol: "AuthProtocol",
213
+ violationType: "missing_prerequisite",
214
+ candidates: [
215
+ { candidateId: goodFp, source: "protocol", evidenceSources: ["protocol"], actions: ["verify_password", "generate_jwt"], explanation: "proper auth" },
216
+ { candidateId: badFp, source: "antibody", evidenceSources: ["antibody"], actions: ["skip_auth"], explanation: "skip auth" },
217
+ ],
218
+ selectedCandidateId: badFp,
219
+ });
220
+ t.recordFeedback(id, { decision: "rejected", timestamp: Date.now() });
221
+ }
222
+ const ranked = learner.rank([good, bad], [features, { ...features, source: "antibody" }], { protocol: "AuthProtocol", violationType: "missing_prerequisite" });
223
+ (0, vitest_1.expect)(ranked[0].id).toBe("good");
224
+ (0, vitest_1.expect)(ranked[1].acceptance).toBeLessThan(0.2); // ~0%
225
+ // Verify rejection stats
226
+ (0, vitest_1.expect)(t.getCandidateStats(badFp).rejected).toBe(50);
227
+ });
228
+ (0, vitest_1.it)("execution failure reduces effective reward", () => {
229
+ const t = new planner_telemetry_1.PlannerTelemetry(path.join(EVO_DIR, ".progmune_corpus", "telemetry", `execfail-${Date.now()}.jsonl`));
230
+ const learner = new learning_ranker_1.LearningRanker((0, repair_ranker_1.createLinearRanker)(), t);
231
+ const safe = makeCandidate("safe", "protocol", ["open_file", "write_file", "close_file"]);
232
+ const leaky = makeCandidate("leaky", "corpus", ["open_file", "write_file"]); // missing close
233
+ const safeFp = (0, planner_telemetry_1.candidateFingerprint)("FileProtocol", ["open_file", "write_file", "close_file"], "resource_leak");
234
+ const leakyFp = (0, planner_telemetry_1.candidateFingerprint)("FileProtocol", ["open_file", "write_file"], "resource_leak");
235
+ const features = {
236
+ protocolSafety: 0.8, historicalSuccessRate: 0.5, actionCount: 2,
237
+ latencyCost: 0.3, auditability: 0.7, corpusEvidence: 0, source: "protocol",
238
+ };
239
+ // Both accepted 50 times, but leaky always fails at execution
240
+ for (let i = 0; i < 50; i++) {
241
+ const safeId = t.recordDecision({
242
+ goal: "write file",
243
+ protocol: "FileProtocol",
244
+ violationType: "resource_leak",
245
+ candidates: [
246
+ { candidateId: safeFp, source: "protocol", evidenceSources: ["protocol"], actions: ["open_file", "write_file", "close_file"], explanation: "safe" },
247
+ { candidateId: leakyFp, source: "corpus", evidenceSources: ["corpus"], actions: ["open_file", "write_file"], explanation: "fast but leaky" },
248
+ ],
249
+ selectedCandidateId: safeFp,
250
+ });
251
+ t.recordFeedback(safeId, {
252
+ decision: "accepted",
253
+ executionResult: { success: true, violations: [] },
254
+ timestamp: Date.now(),
255
+ });
256
+ const leakyId = t.recordDecision({
257
+ goal: "write file",
258
+ protocol: "FileProtocol",
259
+ violationType: "resource_leak",
260
+ candidates: [
261
+ { candidateId: safeFp, source: "protocol", evidenceSources: ["protocol"], actions: ["open_file", "write_file", "close_file"], explanation: "safe" },
262
+ { candidateId: leakyFp, source: "corpus", evidenceSources: ["corpus"], actions: ["open_file", "write_file"], explanation: "fast but leaky" },
263
+ ],
264
+ selectedCandidateId: leakyFp,
265
+ });
266
+ t.recordFeedback(leakyId, {
267
+ decision: "accepted",
268
+ executionResult: { success: false, violations: ["resource_leak"] },
269
+ timestamp: Date.now(),
270
+ });
271
+ }
272
+ const ranked = learner.rank([safe, leaky], [features, features], { protocol: "FileProtocol", violationType: "resource_leak" });
273
+ // Safe ranks higher despite equal acceptance (execution success matters)
274
+ (0, vitest_1.expect)(ranked[0].id).toBe("safe");
275
+ // Safe has higher effectiveReward
276
+ (0, vitest_1.expect)(ranked[0].effectiveReward).toBeGreaterThan(ranked[1].effectiveReward);
277
+ // Safe: acceptance=1.0, execution=1.0 → effectiveReward=1.0
278
+ // Leaky: acceptance=1.0, execution=0.0 → effectiveReward=0.5
279
+ (0, vitest_1.expect)(ranked[0].effectiveReward).toBe(1.0);
280
+ (0, vitest_1.expect)(ranked[1].effectiveReward).toBe(0.5);
281
+ // Verify stats
282
+ const safeStats = t.getCandidateStats(safeFp);
283
+ (0, vitest_1.expect)(safeStats.accepted).toBe(50);
284
+ (0, vitest_1.expect)(safeStats.executionSuccess).toBe(50);
285
+ (0, vitest_1.expect)(safeStats.executionFailure).toBe(0);
286
+ const leakyStats = t.getCandidateStats(leakyFp);
287
+ (0, vitest_1.expect)(leakyStats.accepted).toBe(50);
288
+ (0, vitest_1.expect)(leakyStats.executionSuccess).toBe(0);
289
+ (0, vitest_1.expect)(leakyStats.executionFailure).toBe(50);
290
+ });
291
+ });
@@ -0,0 +1,322 @@
1
+ "use strict";
2
+ /**
3
+ * Phase 10: Accountability Ledger
4
+ *
5
+ * Builds an actor-attributed chain of custody on top of the
6
+ * provenance chain. Maps technical events to accountable actors,
7
+ * identifies custody gaps, and computes the full AI supply chain
8
+ * from human intent through AI generation to production deployment.
9
+ *
10
+ * Usage:
11
+ * buildAccountabilityChain(sessionId, humanActor?)
12
+ */
13
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || (function () {
30
+ var ownKeys = function(o) {
31
+ ownKeys = Object.getOwnPropertyNames || function (o) {
32
+ var ar = [];
33
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
+ return ar;
35
+ };
36
+ return ownKeys(o);
37
+ };
38
+ return function (mod) {
39
+ if (mod && mod.__esModule) return mod;
40
+ var result = {};
41
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
42
+ __setModuleDefault(result, mod);
43
+ return result;
44
+ };
45
+ })();
46
+ Object.defineProperty(exports, "__esModule", { value: true });
47
+ exports.buildAccountabilityChain = buildAccountabilityChain;
48
+ exports.verifyAccountabilityChain = verifyAccountabilityChain;
49
+ const crypto = __importStar(require("crypto"));
50
+ const chain_builder_1 = require("./chain-builder");
51
+ const signatures_1 = require("./signatures");
52
+ // ── Core Builder ──
53
+ function shortHash(data) {
54
+ return crypto.createHash("sha256").update(data).digest("hex").slice(0, 16);
55
+ }
56
+ function hashAccountabilityEvent(index, actorId, actorType, step, artifact, prevHash, timestamp, result) {
57
+ return shortHash(`${index}|${actorId}|${actorType}|${step}|${artifact}|${prevHash}|${timestamp}|${result}`);
58
+ }
59
+ function computeActorType(provEvent) {
60
+ const actor = provEvent.actor || "";
61
+ if (actor === "human")
62
+ return "human";
63
+ if (actor === "llm")
64
+ return "llm";
65
+ if (actor === "validator" || actor === "system")
66
+ return "validator";
67
+ if (actor === "planner")
68
+ return "llm"; // planner = LLM-driven
69
+ if (actor === "antibody")
70
+ return "validator"; // antibody = automated
71
+ return "validator";
72
+ }
73
+ function computeActorLabel(provEvent, options) {
74
+ const step = provEvent.step;
75
+ const actor = provEvent.actor || "";
76
+ const timestamp = provEvent.timestamp || "";
77
+ // Human actors from options
78
+ if (step === "generation" && options.author) {
79
+ return {
80
+ actorId: options.author.id,
81
+ actorType: "human",
82
+ actorLabel: `${options.author.name} (${options.author.id}, ${options.author.role})`,
83
+ };
84
+ }
85
+ if (step === "approval" && options.approver) {
86
+ return {
87
+ actorId: options.approver.id,
88
+ actorType: "reviewer",
89
+ actorLabel: `${options.approver.name} (${options.approver.id}, approved)`,
90
+ };
91
+ }
92
+ if (step === "deploy" && options.deployer) {
93
+ return {
94
+ actorId: options.deployer.id,
95
+ actorType: "deployer",
96
+ actorLabel: `${options.deployer.name} (${options.deployer.id})`,
97
+ };
98
+ }
99
+ // Fallback: infer from provenance actor string
100
+ const actorType = computeActorType(provEvent);
101
+ let actorId = "";
102
+ let actorLabel = "";
103
+ switch (actorType) {
104
+ case "llm":
105
+ actorId = `llm:${actor}@${timestamp.slice(0, 10)}`;
106
+ actorLabel = `${actor} (LLM)`;
107
+ break;
108
+ case "validator":
109
+ actorId = `system:progmune-ssg@${timestamp.slice(0, 10)}`;
110
+ actorLabel = `Progmune Validator (SSG)`;
111
+ break;
112
+ case "human":
113
+ actorId = `human:${actor}@${timestamp.slice(0, 10)}`;
114
+ actorLabel = `${actor} (Human)`;
115
+ break;
116
+ case "reviewer":
117
+ actorId = `reviewer:${actor}@${timestamp.slice(0, 10)}`;
118
+ actorLabel = `${actor} (Reviewer)`;
119
+ break;
120
+ case "deployer":
121
+ actorId = `deployer:${actor}@${timestamp.slice(0, 10)}`;
122
+ actorLabel = `${actor} (Deployer)`;
123
+ break;
124
+ default:
125
+ actorId = `unknown:${actor}@${timestamp.slice(0, 10)}`;
126
+ actorLabel = `${actor}`;
127
+ }
128
+ return { actorId, actorType, actorLabel };
129
+ }
130
+ // ── Main Entry Point ──
131
+ function buildAccountabilityChain(sessionId, options = {}) {
132
+ // 1. Build the underlying provenance chain
133
+ const provChain = (0, chain_builder_1.buildProvenanceChain)(sessionId);
134
+ // 2. Convert each provenance event to an accountability event
135
+ const events = [];
136
+ let prevHash = "";
137
+ const custodyGaps = [];
138
+ for (const provEvent of provChain.events) {
139
+ const { actorId, actorType, actorLabel } = computeActorLabel(provEvent, options);
140
+ const action = describeAction(provEvent);
141
+ const eventHash = hashAccountabilityEvent(provEvent.index, actorId, actorType, provEvent.step, provEvent.artifact, prevHash, provEvent.timestamp, provEvent.result);
142
+ // Check custody gap: any event without verified actor identity
143
+ const hasGap = actorId.startsWith("unknown:") ||
144
+ (actorType === "human" && !options.author && !options.approver);
145
+ custodyGaps.push(hasGap);
146
+ // Try to sign the event
147
+ let signature;
148
+ try {
149
+ const payload = (0, signatures_1.buildSignPayload)(provEvent.index, actorId, provEvent.step, provEvent.artifact, provEvent.timestamp, provEvent.result, prevHash);
150
+ const sigResult = (0, signatures_1.trySign)(actorId, payload);
151
+ if (sigResult)
152
+ signature = sigResult.signature;
153
+ }
154
+ catch { /* signing is best-effort */ }
155
+ const aev = {
156
+ index: provEvent.index,
157
+ step: provEvent.step,
158
+ timestamp: provEvent.timestamp,
159
+ actorId,
160
+ actorType,
161
+ actorLabel,
162
+ artifact: provEvent.artifact,
163
+ action,
164
+ hash: eventHash,
165
+ prevHash,
166
+ result: provEvent.result,
167
+ signature,
168
+ detail: provEvent.detail,
169
+ };
170
+ events.push(aev);
171
+ prevHash = eventHash;
172
+ }
173
+ // 3. Add explicit human events from options (if not already in chain)
174
+ if (options.author && events.length > 0) {
175
+ // Prepend human author event
176
+ const humanEvent = makeHumanEvent("author", options.author, events[0].hash, events.length);
177
+ events.unshift(humanEvent);
178
+ // Recompute hashes forward
179
+ recomputeChainHashes(events);
180
+ }
181
+ if (options.reviewers && events.length > 0) {
182
+ // Insert reviewer events after validation
183
+ let valIdx = -1;
184
+ for (let i = events.length - 1; i >= 0; i--) {
185
+ if (events[i].step === "validation" || events[i].step === "repair") {
186
+ valIdx = i;
187
+ break;
188
+ }
189
+ }
190
+ const insertIdx = valIdx >= 0 ? valIdx + 1 : events.length;
191
+ for (const reviewer of options.reviewers) {
192
+ const prev = insertIdx > 0 ? events[insertIdx - 1].hash : "";
193
+ const re = makeHumanEvent("reviewer", reviewer, prev, insertIdx);
194
+ events.splice(insertIdx, 0, re);
195
+ }
196
+ recomputeChainHashes(events);
197
+ }
198
+ if (options.approver && events.length > 0) {
199
+ // Add approval event before deploy
200
+ const depIdx = events.findIndex((e) => e.step === "deploy");
201
+ const insertIdx = depIdx >= 0 ? depIdx : events.length;
202
+ const prev = insertIdx > 0 ? events[insertIdx - 1].hash : "";
203
+ const ae = makeHumanEvent("approver", options.approver, prev, insertIdx);
204
+ events.splice(insertIdx, 0, ae);
205
+ recomputeChainHashes(events);
206
+ }
207
+ // 4. Compute chain hash and stats
208
+ const chainHash = computeAccountabilityChainHash(events);
209
+ const humanEvents = events.filter((e) => e.actorType === "human" || e.actorType === "reviewer").length;
210
+ const aiEvents = events.filter((e) => e.actorType === "llm").length;
211
+ const autoEvents = events.filter((e) => e.actorType === "validator" || e.actorType === "deployer").length;
212
+ const hasGap = custodyGaps.some(Boolean);
213
+ return {
214
+ sessionId: provChain.sessionId,
215
+ intent: provChain.intent,
216
+ events,
217
+ chainHash,
218
+ integrity: provChain.integrity,
219
+ custodyGap: hasGap,
220
+ totalEvents: events.length,
221
+ humanEvents,
222
+ aiEvents,
223
+ automatedEvents: autoEvents,
224
+ };
225
+ }
226
+ // ── Helpers ──
227
+ function describeAction(ev) {
228
+ switch (ev.step) {
229
+ case "generation":
230
+ return `Generated ${ev.artifact}`;
231
+ case "validation":
232
+ return `Validated ${ev.artifact}`;
233
+ case "repair":
234
+ return `Repaired ${ev.artifact}`;
235
+ case "approval":
236
+ return `Approved ${ev.artifact}`;
237
+ case "deploy":
238
+ return `Deployed ${ev.artifact}`;
239
+ default:
240
+ return ev.step;
241
+ }
242
+ }
243
+ function makeHumanEvent(role, human, prevHash, index) {
244
+ const step = role === "author" ? "generation" : role === "reviewer" ? "approval" : "approval";
245
+ const timestamp = new Date().toISOString();
246
+ const eventHash = hashAccountabilityEvent(index, human.id, "human", step, `intent: ${human.role}`, prevHash, timestamp, "approved");
247
+ return {
248
+ index,
249
+ step: step,
250
+ timestamp,
251
+ actorId: human.id,
252
+ actorType: role === "author" ? "human" : "reviewer",
253
+ actorLabel: `${human.name} (${human.id}, ${human.role})`,
254
+ artifact: `Intent from ${human.name}`,
255
+ action: role === "author" ? "Initiated code generation"
256
+ : role === "reviewer" ? "Reviewed and approved"
257
+ : "Approved deployment",
258
+ hash: eventHash,
259
+ prevHash,
260
+ result: "approved",
261
+ };
262
+ }
263
+ function recomputeChainHashes(events) {
264
+ let ph = "";
265
+ for (let i = 0; i < events.length; i++) {
266
+ events[i].index = i;
267
+ events[i].prevHash = ph;
268
+ events[i].hash = hashAccountabilityEvent(i, events[i].actorId, events[i].actorType, events[i].step, events[i].artifact, ph, events[i].timestamp, events[i].result);
269
+ ph = events[i].hash;
270
+ }
271
+ }
272
+ function computeAccountabilityChainHash(events) {
273
+ return shortHash(events.map((e) => e.hash).join(""));
274
+ }
275
+ // ── Verify accountability chain integrity ──
276
+ function verifyAccountabilityChain(chain) {
277
+ let ph = "";
278
+ let unsignedCount = 0;
279
+ let signedCount = 0;
280
+ for (const e of chain.events) {
281
+ // 1. Verify hash chain
282
+ const recomputed = hashAccountabilityEvent(e.index, e.actorId, e.actorType, e.step, e.artifact, ph, e.timestamp, e.result);
283
+ if (recomputed !== e.hash) {
284
+ return {
285
+ valid: false,
286
+ brokenAt: e.index,
287
+ detail: `Hash mismatch at event ${e.index}: expected ${recomputed}, got ${e.hash}`,
288
+ unsignedCount, signedCount,
289
+ };
290
+ }
291
+ // 2. Verify signature (if present)
292
+ if (e.signature) {
293
+ try {
294
+ const { verifySignature, loadPublicKey } = require("./signatures");
295
+ const payload = (0, signatures_1.buildSignPayload)(e.index, e.actorId, e.step, e.artifact, e.timestamp, e.result, e.prevHash);
296
+ const pubKey = loadPublicKey(e.actorId);
297
+ if (pubKey) {
298
+ const verification = verifySignature(payload, e.signature, pubKey);
299
+ e.signatureVerified = verification.valid;
300
+ if (!verification.valid) {
301
+ return {
302
+ valid: false,
303
+ brokenAt: e.index,
304
+ detail: `Signature verification failed at event ${e.index}: ${verification.detail}`,
305
+ unsignedCount, signedCount,
306
+ };
307
+ }
308
+ }
309
+ signedCount++;
310
+ }
311
+ catch { /* skip signature check if verification fails */ }
312
+ }
313
+ else {
314
+ unsignedCount++;
315
+ }
316
+ ph = e.hash;
317
+ }
318
+ if (computeAccountabilityChainHash(chain.events) !== chain.chainHash) {
319
+ return { valid: false, detail: "Chain root hash mismatch", unsignedCount, signedCount };
320
+ }
321
+ return { valid: true, unsignedCount, signedCount };
322
+ }