progmune-runtime 2.1.6 → 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 +262 -96
  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,115 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * P8.1: State Inference Tests — The decisive structure learning benchmark
5
+ *
6
+ * Three experiments:
7
+ * A: Name-Scramble — inferred state machine must survive function renaming
8
+ * B: State-Discrimination — different topologies must produce different fingerprints
9
+ * C: Real-world: infer state machines from Redis/SQLite sequences
10
+ */
11
+ const vitest_1 = require("vitest");
12
+ const state_inference_1 = require("./experimental/state-inference");
13
+ // ── Test data: structurally distinct protocols ──
14
+ const ACQUIRE_USE_RELEASE = [
15
+ ["open", "read", "close"],
16
+ ["open", "write", "close"],
17
+ ["open", "read", "write", "close"],
18
+ ];
19
+ const ACQUIRE_USE_RELEASE_B = [
20
+ ["sql_open", "sql_read", "sql_close"],
21
+ ["sql_open", "sql_write", "sql_close"],
22
+ ];
23
+ const TRANSACTION = [
24
+ ["begin_tx", "insert", "commit_tx"],
25
+ ["begin_tx", "update", "commit_tx"],
26
+ ["begin_tx", "delete", "rollback_tx"],
27
+ ["begin_tx", "savepoint", "update", "release", "commit_tx"],
28
+ ];
29
+ const LOOP = [
30
+ ["init", "fetch", "process", "next", "fetch", "process", "exit"],
31
+ ["init", "fetch", "process", "exit"],
32
+ ["init", "fetch", "process", "timeout"],
33
+ ];
34
+ const STAR = [
35
+ ["hub", "leaf_a", "hub", "leaf_b", "hub", "destroy"],
36
+ ["hub", "leaf_c", "hub", "leaf_a", "hub", "destroy"],
37
+ ];
38
+ (0, vitest_1.describe)("P8.1 State Inference", () => {
39
+ (0, vitest_1.it)("infers state machine from call sequences", () => {
40
+ const sm = (0, state_inference_1.inferStateMachine)(ACQUIRE_USE_RELEASE);
41
+ (0, state_inference_1.printInferredStateMachine)(sm);
42
+ (0, vitest_1.expect)(sm.fnCount).toBeGreaterThan(1);
43
+ (0, vitest_1.expect)(sm.stateCount).toBeGreaterThan(0);
44
+ (0, vitest_1.expect)(sm.states.some(s => s.role === "entry")).toBe(true);
45
+ (0, vitest_1.expect)(sm.states.some(s => s.role === "exit")).toBe(true);
46
+ });
47
+ (0, vitest_1.it)("NAME-SCRAMBLE: identical topology, different names → identical state machine", () => {
48
+ // This is the P8.1 decisive test:
49
+ // If state inference truly ignores function names,
50
+ // two repos with identical structure but different names
51
+ // must produce structurally identical state machines.
52
+ const smA = (0, state_inference_1.inferStateMachine)(ACQUIRE_USE_RELEASE);
53
+ const smB = (0, state_inference_1.inferStateMachine)(ACQUIRE_USE_RELEASE_B);
54
+ const fpA = (0, state_inference_1.extractStateFingerprint)(smA);
55
+ const fpB = (0, state_inference_1.extractStateFingerprint)(smB);
56
+ console.log(`\n ═══ P8.1 NAME-SCRAMBLE TEST ═══`);
57
+ console.log(` States A: ${fpA.stateCount} States B: ${fpB.stateCount}`);
58
+ console.log(` Trans A: ${fpA.transitionCount} Trans B: ${fpB.transitionCount}`);
59
+ console.log(` Entries: ${fpA.entryCount}/${fpB.entryCount} Exits: ${fpA.exitCount}/${fpB.exitCount}`);
60
+ console.log(` DAG: ${fpA.isDAG}/${fpB.isDAG}`);
61
+ const sim = (0, state_inference_1.stateFingerprintSimilarity)(fpA, fpB);
62
+ console.log(` Similarity: ${(sim * 100).toFixed(1)}%`);
63
+ console.log(` Target: >90%`);
64
+ // Must be >90% similar — state inference should ignore function names
65
+ (0, vitest_1.expect)(sim).toBeGreaterThan(0.9);
66
+ });
67
+ (0, vitest_1.it)("SAME-TOPOLOGY: structurally identical repos produce near-identical fingerprints (>90%)", () => {
68
+ const acquire = (0, state_inference_1.extractStateFingerprint)((0, state_inference_1.inferStateMachine)(ACQUIRE_USE_RELEASE));
69
+ const acquireB = (0, state_inference_1.extractStateFingerprint)((0, state_inference_1.inferStateMachine)(ACQUIRE_USE_RELEASE_B));
70
+ const sameSim = (0, state_inference_1.stateFingerprintSimilarity)(acquire, acquireB);
71
+ console.log(` Same topo (acquire vs acquire_B): ${(sameSim * 100).toFixed(0)}%`);
72
+ (0, vitest_1.expect)(sameSim).toBeGreaterThan(0.9);
73
+ });
74
+ (0, vitest_1.it)("CROSS-TOPOLOGY: topologies with different node counts are distinguishable", () => {
75
+ // Small (3-state chain) vs larger (5-10 state transaction)
76
+ const cfg = [["a", "b", "c"], ["a", "c"]]; // 3-node chain
77
+ const largeCfg = [];
78
+ for (let i = 0; i < 20; i++) {
79
+ largeCfg.push(["init", "fetch", "process", "next", "fetch", "process", "exit"]);
80
+ }
81
+ const small = (0, state_inference_1.extractStateFingerprint)((0, state_inference_1.inferStateMachine)(cfg));
82
+ const large = (0, state_inference_1.extractStateFingerprint)((0, state_inference_1.inferStateMachine)(largeCfg));
83
+ const sim = (0, state_inference_1.stateFingerprintSimilarity)(small, large);
84
+ console.log(` Small (3-node) vs Large (7-fn loop): ${(sim * 100).toFixed(0)}%`);
85
+ // Different-sized state machines should NOT be identical
86
+ (0, vitest_1.expect)(sim).toBeLessThan(0.99);
87
+ // Should still show some structural similarity (both are chains)
88
+ (0, vitest_1.expect)(sim).toBeGreaterThan(0.3);
89
+ });
90
+ (0, vitest_1.it)("cross-repo: Redis and SQLite have similar state structures", () => {
91
+ const redisSeqs = [
92
+ ["createClient", "sendCommand", "closeClient"],
93
+ ["selectDB", "getKey"],
94
+ ["createClient", "sendCommand", "readReply", "closeClient"],
95
+ ];
96
+ const sqliteSeqs = [
97
+ ["sqlite3_open", "sqlite3_exec", "sqlite3_close"],
98
+ ["sqlite3_prepare", "sqlite3_step", "sqlite3_finalize"],
99
+ ["sqlite3_open", "sqlite3_prepare", "sqlite3_step", "sqlite3_finalize", "sqlite3_close"],
100
+ ];
101
+ const redisFp = (0, state_inference_1.extractStateFingerprint)((0, state_inference_1.inferStateMachine)(redisSeqs));
102
+ const sqliteFp = (0, state_inference_1.extractStateFingerprint)((0, state_inference_1.inferStateMachine)(sqliteSeqs));
103
+ const sim = (0, state_inference_1.stateFingerprintSimilarity)(redisFp, sqliteFp);
104
+ console.log(`\n Redis ↔ SQLite state similarity: ${(sim * 100).toFixed(0)}%`);
105
+ // Both are acquire→use→release — should be structurally similar
106
+ (0, vitest_1.expect)(sim).toBeGreaterThan(0.5);
107
+ });
108
+ (0, vitest_1.it)("empty input produces empty state machine", () => {
109
+ const sm = (0, state_inference_1.inferStateMachine)([]);
110
+ (0, vitest_1.expect)(sm.fnCount).toBe(0);
111
+ (0, vitest_1.expect)(sm.stateCount).toBe(0);
112
+ const fp = (0, state_inference_1.extractStateFingerprint)(sm);
113
+ (0, vitest_1.expect)(fp.stateCount).toBe(0);
114
+ });
115
+ });
@@ -0,0 +1,345 @@
1
+ "use strict";
2
+ /**
3
+ * P8.1: State Machine Learning — From static call graphs to dynamic state machines
4
+ *
5
+ * Core insight: protocol semantics live in STATE SPACE, not function space.
6
+ * "Authenticate before query" is a state-machine constraint, not a naming convention.
7
+ *
8
+ * Unlike P8.0 (which compares function call graphs), P8.1 compares protocol
9
+ * STATE MACHINES — states + allowed transitions + entry/exit nodes.
10
+ * State machines inherently capture ordering, branching, and lifecycle.
11
+ *
12
+ * Three operations:
13
+ * 1. extractStateMachine: SSG rules → StateMachineFingerprint (name-free)
14
+ * 2. compareStateMachines: structural similarity between two state machines
15
+ * 3. renameStates: scramble state names (for State-Scramble test)
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.extractStateMachine = extractStateMachine;
19
+ exports.renameStates = renameStates;
20
+ exports.compareStateMachines = compareStateMachines;
21
+ exports.extractAllProtocolStateMachines = extractAllProtocolStateMachines;
22
+ exports.compareAllProtocolStateMachines = compareAllProtocolStateMachines;
23
+ exports.printStateMachineComparison = printStateMachineComparison;
24
+ const protocol_coverage_1 = require("./protocol-coverage");
25
+ /**
26
+ * Extract a name-free state machine fingerprint from protocol rules.
27
+ *
28
+ * States are identified by their STRUCTURAL ROLE (entry, exit, branch, merge),
29
+ * not by their semantic names (UNAUTHENTICATED, SESSION_ACTIVE, etc.).
30
+ * Function names (verify_password, close_file) are ENTIRELY IGNORED.
31
+ *
32
+ * @param rules Map of function → StateAnnotation (from protocols.json)
33
+ * @returns Name-free StateMachineFingerprint
34
+ */
35
+ function extractStateMachine(rules) {
36
+ const stateSet = new Set();
37
+ const edgeSet = new Set(); // "from→to"
38
+ const transitions = [];
39
+ const inDegree = new Map();
40
+ const outDegree = new Map();
41
+ // Collect all states from pre_states, post_states, and invalidate arrays
42
+ for (const [, rule] of rules) {
43
+ for (const s of rule.pre_states) {
44
+ stateSet.add(s);
45
+ outDegree.set(s, (outDegree.get(s) || 0));
46
+ }
47
+ for (const s of rule.post_states) {
48
+ stateSet.add(s);
49
+ inDegree.set(s, (inDegree.get(s) || 0));
50
+ }
51
+ if (rule.invalidate) {
52
+ for (const s of rule.invalidate) {
53
+ stateSet.add(s);
54
+ }
55
+ }
56
+ }
57
+ // Add INIT as a synthetic state for rules with empty pre_states
58
+ stateSet.add("INIT");
59
+ for (const [, rule] of rules) {
60
+ const pres = rule.pre_states.length > 0 ? rule.pre_states : ["INIT"];
61
+ // Acquire transitions: pre → post
62
+ for (const pre of pres) {
63
+ for (const post of rule.post_states) {
64
+ const key = `${pre}→${post}`;
65
+ if (!edgeSet.has(key)) {
66
+ edgeSet.add(key);
67
+ transitions.push({ from: pre, to: post });
68
+ inDegree.set(post, (inDegree.get(post) || 0) + 1);
69
+ outDegree.set(pre, (outDegree.get(pre) || 0) + 1);
70
+ }
71
+ }
72
+ }
73
+ // Invalidation transitions: state → ∅ (modeled as self-removal)
74
+ if (rule.invalidate) {
75
+ for (const inv of rule.invalidate) {
76
+ const key = `${inv}→∅`;
77
+ if (!edgeSet.has(key)) {
78
+ edgeSet.add(key);
79
+ transitions.push({ from: inv, to: "∅" });
80
+ outDegree.set(inv, (outDegree.get(inv) || 0) + 1);
81
+ }
82
+ }
83
+ }
84
+ }
85
+ // Ensure all states have entries in both degree maps
86
+ for (const s of stateSet) {
87
+ if (!inDegree.has(s))
88
+ inDegree.set(s, 0);
89
+ if (!outDegree.has(s))
90
+ outDegree.set(s, 0);
91
+ }
92
+ // Classify states by structural role
93
+ const entryStates = [];
94
+ const exitStates = [];
95
+ const selfLoopStates = [];
96
+ const branchStates = [];
97
+ const mergeStates = [];
98
+ for (const s of stateSet) {
99
+ const inDeg = inDegree.get(s) || 0;
100
+ const outDeg = outDegree.get(s) || 0;
101
+ if (inDeg === 0)
102
+ entryStates.push(s);
103
+ if (outDeg === 0)
104
+ exitStates.push(s);
105
+ if (outDeg >= 2)
106
+ branchStates.push(s);
107
+ if (inDeg >= 2)
108
+ mergeStates.push(s);
109
+ // Self-loop check
110
+ if (edgeSet.has(`${s}→${s}`))
111
+ selfLoopStates.push(s);
112
+ }
113
+ // DAG check: Kahn's algorithm
114
+ const tempInDeg = new Map(inDegree);
115
+ const kahnQueue = [...stateSet].filter(s => (tempInDeg.get(s) || 0) === 0);
116
+ let visitedCount = 0;
117
+ while (kahnQueue.length > 0) {
118
+ const node = kahnQueue.shift();
119
+ visitedCount++;
120
+ for (const t of transitions) {
121
+ if (t.from === node) {
122
+ const newDeg = (tempInDeg.get(t.to) || 1) - 1;
123
+ tempInDeg.set(t.to, newDeg);
124
+ if (newDeg === 0)
125
+ kahnQueue.push(t.to);
126
+ }
127
+ }
128
+ }
129
+ const isDAG = visitedCount >= stateSet.size;
130
+ // Diameter: BFS from each node
131
+ let diameter = 0;
132
+ const stateList = [...stateSet];
133
+ const adj = new Map();
134
+ for (const s of stateList)
135
+ adj.set(s, []);
136
+ for (const t of transitions) {
137
+ adj.get(t.from)?.push(t.to);
138
+ }
139
+ for (const start of stateList) {
140
+ const dist = new Map();
141
+ const q = [start];
142
+ dist.set(start, 0);
143
+ while (q.length > 0) {
144
+ const node = q.shift();
145
+ const d = dist.get(node) + 1;
146
+ for (const next of (adj.get(node) || [])) {
147
+ if (!dist.has(next)) {
148
+ dist.set(next, d);
149
+ diameter = Math.max(diameter, d);
150
+ q.push(next);
151
+ }
152
+ }
153
+ }
154
+ }
155
+ // Components: DFS on undirected version
156
+ const undirected = new Map();
157
+ for (const s of stateList)
158
+ undirected.set(s, []);
159
+ for (const t of transitions) {
160
+ undirected.get(t.from)?.push(t.to);
161
+ undirected.get(t.to)?.push(t.from);
162
+ }
163
+ const compVisited = new Set();
164
+ let componentCount = 0;
165
+ for (const s of stateList) {
166
+ if (!compVisited.has(s)) {
167
+ componentCount++;
168
+ const q = [s];
169
+ compVisited.add(s);
170
+ while (q.length > 0) {
171
+ for (const n of (undirected.get(q.shift()) || [])) {
172
+ if (!compVisited.has(n)) {
173
+ compVisited.add(n);
174
+ q.push(n);
175
+ }
176
+ }
177
+ }
178
+ }
179
+ }
180
+ // Average branching factor
181
+ const nonExits = stateList.filter(s => (outDegree.get(s) || 0) > 0);
182
+ const avgBF = nonExits.length > 0
183
+ ? nonExits.reduce((s, x) => s + (outDegree.get(x) || 0), 0) / nonExits.length
184
+ : 0;
185
+ return {
186
+ stateCount: stateSet.size,
187
+ transitions,
188
+ inDegrees: inDegree,
189
+ outDegrees: outDegree,
190
+ entryStates,
191
+ exitStates,
192
+ selfLoopStates,
193
+ branchStates,
194
+ mergeStates,
195
+ isDAG,
196
+ diameter,
197
+ componentCount,
198
+ largestSCCSize: 0, // simplified — SCC detection would need Tarjan's
199
+ avgBranchingFactor: Math.round(avgBF * 100) / 100,
200
+ };
201
+ }
202
+ /**
203
+ * Rename all states to opaque IDs (S0, S1, S2...).
204
+ * Used for the State-Scramble test: if similarity survives state renaming,
205
+ * the system has genuinely learned state TRANSITION PATTERNS.
206
+ */
207
+ function renameStates(fp) {
208
+ const nameMap = new Map();
209
+ let counter = 0;
210
+ const rename = (s) => {
211
+ if (s === "INIT" || s === "∅" || s === "INIT→")
212
+ return s; // preserve synthetic markers
213
+ if (!nameMap.has(s))
214
+ nameMap.set(s, `S${counter++}`);
215
+ return nameMap.get(s);
216
+ };
217
+ const renameMap = (m) => {
218
+ const result = new Map();
219
+ for (const [k, v] of m)
220
+ result.set(rename(k), v);
221
+ return result;
222
+ };
223
+ return {
224
+ ...fp,
225
+ transitions: fp.transitions.map(t => ({ from: rename(t.from), to: rename(t.to) })),
226
+ inDegrees: renameMap(fp.inDegrees),
227
+ outDegrees: renameMap(fp.outDegrees),
228
+ entryStates: fp.entryStates.map(rename),
229
+ exitStates: fp.exitStates.map(rename),
230
+ selfLoopStates: fp.selfLoopStates.map(rename),
231
+ branchStates: fp.branchStates.map(rename),
232
+ mergeStates: fp.mergeStates.map(rename),
233
+ };
234
+ }
235
+ /**
236
+ * Compare two state machine fingerprints.
237
+ *
238
+ * Comparison is ENTIRELY structural — state names are never used.
239
+ * Two machines are similar if they have:
240
+ * - Similar numbers of states (±20%)
241
+ * - Similar transition patterns (edge count, Jaccard on degree profiles)
242
+ * - Similar structural roles (entry/exit/branch/merge counts)
243
+ * - Same DAG property
244
+ */
245
+ function compareStateMachines(a, b) {
246
+ // 1. State count ratio (penalize large differences)
247
+ const maxSC = Math.max(a.stateCount, b.stateCount);
248
+ const minSC = Math.min(a.stateCount, b.stateCount);
249
+ const stateCountRatio = maxSC > 0 ? minSC / maxSC : 1;
250
+ // 2. Transition pattern: compare edge count
251
+ const maxEdge = Math.max(a.transitions.length, b.transitions.length);
252
+ const minEdge = Math.min(a.transitions.length, b.transitions.length);
253
+ const transitionPatternSimilarity = maxEdge > 0 ? minEdge / maxEdge : 1;
254
+ // 3. Degree profile similarity: compare distributions of in/out degrees
255
+ const aInVals = [...a.inDegrees.values()].sort((x, y) => x - y);
256
+ const bInVals = [...b.inDegrees.values()].sort((x, y) => x - y);
257
+ const aOutVals = [...a.outDegrees.values()].sort((x, y) => x - y);
258
+ const bOutVals = [...b.outDegrees.values()].sort((x, y) => x - y);
259
+ const mean = (arr) => arr.length > 0 ? arr.reduce((s, x) => s + x, 0) / arr.length : 0;
260
+ const maxv = (arr) => arr.length > 0 ? Math.max(...arr) : 0;
261
+ const minv = (arr) => arr.length > 0 ? Math.min(...arr) : 0;
262
+ const distSim = (valsA, valsB) => {
263
+ const mA = mean(valsA), mB = mean(valsB);
264
+ const maxA = maxv(valsA), maxB = maxv(valsB);
265
+ const minA = minv(valsA), minB = minv(valsB);
266
+ const meanSim = Math.max(mA, mB) > 0 ? Math.min(mA, mB) / Math.max(mA, mB) : 1;
267
+ const rangeSim = Math.max(maxA, maxB) > 0 ? Math.min(maxA, maxB) / Math.max(maxA, maxB) : 1;
268
+ return (meanSim + rangeSim) / 2;
269
+ };
270
+ const inSim = distSim(aInVals, bInVals);
271
+ const outSim = distSim(aOutVals, bOutVals);
272
+ const degreeProfileSimilarity = (inSim + outSim) / 2;
273
+ // 4. Structural role counts
274
+ const roles = [
275
+ a.entryStates.length, a.exitStates.length,
276
+ a.branchStates.length, a.mergeStates.length, a.selfLoopStates.length,
277
+ b.entryStates.length, b.exitStates.length,
278
+ b.branchStates.length, b.mergeStates.length, b.selfLoopStates.length,
279
+ ];
280
+ let roleDot = 0, roleNormA = 0, roleNormB = 0;
281
+ for (let i = 0; i < 5; i++) {
282
+ roleDot += roles[i] * roles[i + 5];
283
+ roleNormA += roles[i] * roles[i];
284
+ roleNormB += roles[i + 5] * roles[i + 5];
285
+ }
286
+ const roleProfileSimilarity = (roleNormA > 0 && roleNormB > 0)
287
+ ? roleDot / (Math.sqrt(roleNormA) * Math.sqrt(roleNormB))
288
+ : 1;
289
+ // 5. DAG match
290
+ const dagMatch = a.isDAG === b.isDAG;
291
+ // Overall: weighted combination
292
+ const similarity = stateCountRatio * 0.20 +
293
+ transitionPatternSimilarity * 0.25 +
294
+ degreeProfileSimilarity * 0.25 +
295
+ roleProfileSimilarity * 0.25 +
296
+ (dagMatch ? 0.05 : 0);
297
+ return {
298
+ similarity: Math.round(similarity * 10000) / 10000,
299
+ transitionPatternSimilarity: Math.round(transitionPatternSimilarity * 10000) / 10000,
300
+ stateCountRatio: Math.round(stateCountRatio * 10000) / 10000,
301
+ degreeProfileSimilarity: Math.round(degreeProfileSimilarity * 10000) / 10000,
302
+ roleProfileSimilarity: Math.round(roleProfileSimilarity * 10000) / 10000,
303
+ dagMatch,
304
+ };
305
+ }
306
+ // ═══════════════════════════════════════════════════════════════
307
+ // Convenience: extract from protocol definitions
308
+ // ═══════════════════════════════════════════════════════════════
309
+ /**
310
+ * Extract state machine fingerprints for all 9 protocol groups.
311
+ */
312
+ function extractAllProtocolStateMachines() {
313
+ const defs = (0, protocol_coverage_1.loadDefaultProtocolDefinitions)();
314
+ const result = new Map();
315
+ for (const p of defs) {
316
+ result.set(p.name, extractStateMachine(p.rules));
317
+ }
318
+ return result;
319
+ }
320
+ /**
321
+ * Compare all protocol pairs and return sorted results.
322
+ */
323
+ function compareAllProtocolStateMachines() {
324
+ const machines = extractAllProtocolStateMachines();
325
+ const results = [];
326
+ const names = [...machines.keys()];
327
+ for (let i = 0; i < names.length; i++) {
328
+ for (let j = i + 1; j < names.length; j++) {
329
+ results.push({
330
+ protocolA: names[i],
331
+ protocolB: names[j],
332
+ comparison: compareStateMachines(machines.get(names[i]), machines.get(names[j])),
333
+ });
334
+ }
335
+ }
336
+ return results.sort((a, b) => b.comparison.similarity - a.comparison.similarity);
337
+ }
338
+ function printStateMachineComparison(comparison) {
339
+ console.log(` similarity: ${(comparison.similarity * 100).toFixed(1)}%`);
340
+ console.log(` transition pattern: ${(comparison.transitionPatternSimilarity * 100).toFixed(0)}%`);
341
+ console.log(` state count ratio: ${(comparison.stateCountRatio * 100).toFixed(0)}%`);
342
+ console.log(` degree profile: ${(comparison.degreeProfileSimilarity * 100).toFixed(0)}%`);
343
+ console.log(` role profile: ${(comparison.roleProfileSimilarity * 100).toFixed(0)}%`);
344
+ console.log(` DAG match: ${comparison.dagMatch}`);
345
+ }
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * P8.1: State Machine Fingerprint Tests
5
+ *
6
+ * Three decisive experiments:
7
+ * A: State-Scramble — renaming states must preserve similarity (100%)
8
+ * B: Protocol discrimination — auth vs file must differ from auth vs auth
9
+ * C: All 9 protocols — structural grouping by state machine topology
10
+ */
11
+ const vitest_1 = require("vitest");
12
+ const state_machine_fingerprint_1 = require("./state-machine-fingerprint");
13
+ const protocol_coverage_1 = require("./protocol-coverage");
14
+ (0, vitest_1.describe)("P8.1 State Machine Fingerprint", () => {
15
+ (0, vitest_1.it)("extracts state machines from all 9 protocol groups", () => {
16
+ const machines = (0, state_machine_fingerprint_1.extractAllProtocolStateMachines)();
17
+ (0, vitest_1.expect)(machines.size).toBeGreaterThanOrEqual(9);
18
+ for (const [name, fp] of machines) {
19
+ (0, vitest_1.expect)(fp.stateCount).toBeGreaterThan(0);
20
+ // StatelessProtocol has empty pre/post → no transitions (expected)
21
+ if (name === "StatelessProtocol") {
22
+ (0, vitest_1.expect)(fp.transitions.length).toBeGreaterThanOrEqual(0);
23
+ }
24
+ else {
25
+ (0, vitest_1.expect)(fp.transitions.length).toBeGreaterThan(0);
26
+ }
27
+ (0, vitest_1.expect)(fp.entryStates.length).toBeGreaterThanOrEqual(0);
28
+ (0, vitest_1.expect)(fp.exitStates.length).toBeGreaterThanOrEqual(0);
29
+ }
30
+ });
31
+ (0, vitest_1.it)("STATE-SCRAMBLE: renaming states preserves similarity 100%", () => {
32
+ const defs = (0, protocol_coverage_1.loadDefaultProtocolDefinitions)();
33
+ for (const proto of defs) {
34
+ if (proto.rules.size === 0)
35
+ continue;
36
+ const original = (0, state_machine_fingerprint_1.extractStateMachine)(proto.rules);
37
+ const scrambled = (0, state_machine_fingerprint_1.renameStates)(original);
38
+ // Every state should be renamed (except INIT and ∅)
39
+ for (const s of scrambled.entryStates) {
40
+ if (s !== "INIT" && s !== "∅")
41
+ (0, vitest_1.expect)(s).toMatch(/^S\d+$/);
42
+ }
43
+ const comp = (0, state_machine_fingerprint_1.compareStateMachines)(original, scrambled);
44
+ (0, vitest_1.expect)(comp.similarity).toBe(1.0);
45
+ }
46
+ });
47
+ (0, vitest_1.it)("DISCRIMINATION: auth vs file differs from auth vs auth", () => {
48
+ const machines = (0, state_machine_fingerprint_1.extractAllProtocolStateMachines)();
49
+ const auth = machines.get("AuthProtocol");
50
+ const file = machines.get("FileProtocol");
51
+ const db = machines.get("DBProtocol");
52
+ // Same protocol → should be identical
53
+ const authVsAuth = (0, state_machine_fingerprint_1.compareStateMachines)(auth, auth);
54
+ (0, vitest_1.expect)(authVsAuth.similarity).toBe(1.0);
55
+ // Different protocols → should be distinguishable
56
+ const authVsFile = (0, state_machine_fingerprint_1.compareStateMachines)(auth, file);
57
+ const authVsDb = (0, state_machine_fingerprint_1.compareStateMachines)(auth, db);
58
+ const fileVsDb = (0, state_machine_fingerprint_1.compareStateMachines)(file, db);
59
+ console.log(`\n Auth ↔ Auth: ${(authVsAuth.similarity * 100).toFixed(0)}%`);
60
+ console.log(` Auth ↔ File: ${(authVsFile.similarity * 100).toFixed(0)}%`);
61
+ console.log(` Auth ↔ DB: ${(authVsDb.similarity * 100).toFixed(0)}%`);
62
+ console.log(` File ↔ DB: ${(fileVsDb.similarity * 100).toFixed(0)}%`);
63
+ // Auth vs File should be LESS similar than Auth vs Auth
64
+ const discriminationExists = authVsFile.similarity < authVsAuth.similarity;
65
+ (0, vitest_1.expect)(discriminationExists).toBe(true);
66
+ // File vs DB (both resource lifecycle) might be more similar than Auth vs File
67
+ // because they share acquire→use→release structure
68
+ console.log(`\n Auth-File gap: ${((authVsAuth.similarity - authVsFile.similarity) * 100).toFixed(0)}%`);
69
+ console.log(` File-DB (resource siblings): ${(fileVsDb.similarity * 100).toFixed(0)}%`);
70
+ });
71
+ (0, vitest_1.it)("PROTOCOL CLUSTERING: linear vs non-linear protocols separate", () => {
72
+ const machines = (0, state_machine_fingerprint_1.extractAllProtocolStateMachines)();
73
+ const names = [...machines.keys()];
74
+ // Linear protocols: auth, file, db, dev_pipeline, cross
75
+ const linear = ["AuthProtocol", "FileProtocol", "DBProtocol", "IRProtocol", "CrossProtocol"];
76
+ // Non-linear: transaction (DAG), conditional (tree), loop (self-loop)
77
+ const nonlinear = ["TransactionProtocol", "ConditionalProtocol", "LoopProtocol"];
78
+ let linearInternalSim = 0, linearCount = 0;
79
+ for (let i = 0; i < linear.length; i++) {
80
+ for (let j = i + 1; j < linear.length; j++) {
81
+ if (machines.has(linear[i]) && machines.has(linear[j])) {
82
+ linearInternalSim += (0, state_machine_fingerprint_1.compareStateMachines)(machines.get(linear[i]), machines.get(linear[j])).similarity;
83
+ linearCount++;
84
+ }
85
+ }
86
+ }
87
+ let nonlinearInternalSim = 0, nonlinearCount = 0;
88
+ for (let i = 0; i < nonlinear.length; i++) {
89
+ for (let j = i + 1; j < nonlinear.length; j++) {
90
+ if (machines.has(nonlinear[i]) && machines.has(nonlinear[j])) {
91
+ nonlinearInternalSim += (0, state_machine_fingerprint_1.compareStateMachines)(machines.get(nonlinear[i]), machines.get(nonlinear[j])).similarity;
92
+ nonlinearCount++;
93
+ }
94
+ }
95
+ }
96
+ const avgLinear = linearCount > 0 ? linearInternalSim / linearCount : 0;
97
+ const avgNonlinear = nonlinearCount > 0 ? nonlinearInternalSim / nonlinearCount : 0;
98
+ console.log(`\n Avg linear↔linear: ${(avgLinear * 100).toFixed(0)}%`);
99
+ console.log(` Avg nonlinear↔nonlinear: ${(avgNonlinear * 100).toFixed(0)}%`);
100
+ });
101
+ (0, vitest_1.it)("ALL 9 PROTOCOLS: ranked similarity matrix", () => {
102
+ const results = (0, state_machine_fingerprint_1.compareAllProtocolStateMachines)();
103
+ console.log(`\n Ranked by structural similarity:`);
104
+ console.log(` ${'Protocol A'.padEnd(22)} ${'Protocol B'.padEnd(22)} ${'Sim'.padEnd(6)} Trans State Deg Role`);
105
+ console.log(` ${'─'.repeat(80)}`);
106
+ for (const r of results.slice(0, 15)) {
107
+ console.log(` ${r.protocolA.padEnd(22)} ${r.protocolB.padEnd(22)} ` +
108
+ `${(r.comparison.similarity * 100).toFixed(0).padStart(3)}% ` +
109
+ `${(r.comparison.transitionPatternSimilarity * 100).toFixed(0).padStart(3)}% ` +
110
+ `${(r.comparison.stateCountRatio * 100).toFixed(0).padStart(3)}% ` +
111
+ `${(r.comparison.degreeProfileSimilarity * 100).toFixed(0).padStart(3)}% ` +
112
+ `${(r.comparison.roleProfileSimilarity * 100).toFixed(0).padStart(3)}%`);
113
+ }
114
+ // All results should be valid (0-1 range)
115
+ for (const r of results) {
116
+ (0, vitest_1.expect)(r.comparison.similarity).toBeGreaterThanOrEqual(0);
117
+ (0, vitest_1.expect)(r.comparison.similarity).toBeLessThanOrEqual(1.01);
118
+ }
119
+ });
120
+ });