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.
- package/README.md +108 -468
- package/dist/ablation-study.js +144 -0
- package/dist/ablation-study.test.js +18 -0
- package/dist/action-runtime.js +3 -1
- package/dist/active-learning.js +211 -0
- package/dist/analytics.js +139 -0
- package/dist/asset-factory.js +309 -0
- package/dist/asset-growth.js +244 -0
- package/dist/asset-promotion.js +382 -0
- package/dist/asset-quality.js +550 -0
- package/dist/audit/business-translator.js +285 -0
- package/dist/audit/cli.js +66 -0
- package/dist/audit/formatters/html.js +379 -0
- package/dist/audit/formatters/json.js +11 -0
- package/dist/audit/formatters/markdown.js +192 -0
- package/dist/audit/formatters/terminal.js +189 -0
- package/dist/audit/index.js +25 -0
- package/dist/audit/report-builder.js +318 -0
- package/dist/audit/types.js +8 -0
- package/dist/audit.js +3 -3
- package/dist/auto-benchmark-generator.js +137 -0
- package/dist/auto-benchmark-generator.test.js +45 -0
- package/dist/auto-protocol-synthesizer.js +362 -0
- package/dist/auto-protocol-synthesizer.test.js +82 -0
- package/dist/autonomous-patch.js +175 -0
- package/dist/autonomous-patch.test.js +128 -0
- package/dist/badge/badge-server.js +98 -0
- package/dist/behavior-miner.js +442 -0
- package/dist/belief-layer.js +475 -0
- package/dist/benchmark-count.js +5 -0
- package/dist/benchmark-generator.js +211 -0
- package/dist/benchmark-harness.js +201 -0
- package/dist/benchmark-pass-rate.js +7 -0
- package/dist/benchmark-report.js +8 -3
- package/dist/benchmark-save.js +14 -1
- package/dist/bootstrap-validation.js +197 -0
- package/dist/bootstrap-validation.test.js +51 -0
- package/dist/branch-ledger.js +1 -1
- package/dist/capability-gap.js +130 -0
- package/dist/certify-html.js +351 -0
- package/dist/certify.js +326 -0
- package/dist/check.js +4 -4
- package/dist/compliance-miner.js +447 -0
- package/dist/continuous-benchmark.js +194 -0
- package/dist/continuous-benchmark.test.js +116 -0
- package/dist/corpus-stats.js +173 -0
- package/dist/counterfactual-engine.js +288 -0
- package/dist/coverage-dashboard.js +109 -0
- package/dist/coverage-system.test.js +205 -0
- package/dist/cross-repo-precision.js +352 -0
- package/dist/cve-benchmark.js +180 -0
- package/dist/cve-benchmark.test.js +28 -0
- package/dist/cve-collector.js +73 -0
- package/dist/data-quality.js +141 -0
- package/dist/decision-engine.js +388 -0
- package/dist/derive-metadata.js +250 -0
- package/dist/difficulty-active.test.js +198 -0
- package/dist/difficulty-map.js +244 -0
- package/dist/discovery-analytics.js +125 -0
- package/dist/discovery-model.js +149 -0
- package/dist/discovery-optimize.test.js +199 -0
- package/dist/discovery-trace.js +276 -0
- package/dist/discovery-trace.test.js +97 -0
- package/dist/emitter.js +83 -1
- package/dist/enterprise-dashboard.js +405 -0
- package/dist/eval-hardening.js +297 -0
- package/dist/eval-hardening.test.js +85 -0
- package/dist/evaluation-campaign.js +359 -0
- package/dist/evaluation-campaign.test.js +181 -0
- package/dist/evidence-growth.js +143 -0
- package/dist/evidence-repository.js +209 -0
- package/dist/evidence-system.js +441 -0
- package/dist/execute.js +15 -7
- package/dist/experimental/software-physics.js +291 -0
- package/dist/experimental/state-inference.js +516 -0
- package/dist/experimental/unsupervised-physics.js +230 -0
- package/dist/extract-ir-python.js +54 -7
- package/dist/extract-ir.js +376 -12
- package/dist/failure-collector.js +2 -2
- package/dist/failure-corpus.js +322 -9
- package/dist/feedback.js +16 -5
- package/dist/feedback.test.js +49 -0
- package/dist/file-lock.js +1 -1
- package/dist/flywheel-batch.js +292 -0
- package/dist/frameworks/express-cli.js +237 -0
- package/dist/frameworks/express-detector.js +445 -0
- package/dist/frameworks/express-detector.test.js +206 -0
- package/dist/frameworks/index.js +30 -0
- package/dist/frameworks/nestjs-detector.js +302 -0
- package/dist/frameworks/trpc-detector.js +161 -0
- package/dist/frameworks/version-awareness.js +179 -0
- package/dist/function-synonyms.js +164 -0
- package/dist/function-synonyms.test.js +68 -0
- package/dist/generalization.test.js +352 -0
- package/dist/goal-annotator.js +113 -0
- package/dist/goal-planner.js +563 -0
- package/dist/gold-cve.js +164 -0
- package/dist/gold-cve.test.js +104 -0
- package/dist/gold-quality.js +206 -0
- package/dist/gold-tiers.js +241 -0
- package/dist/governance-dashboard.js +327 -0
- package/dist/graph-viz.js +240 -0
- package/dist/guided-frontier.js +195 -0
- package/dist/hierarchical-planner.js +148 -0
- package/dist/identifier-parser.js +260 -0
- package/dist/immune-metrics.js +93 -0
- package/dist/immune-receiver.js +158 -0
- package/dist/immune-reporter.js +1 -1
- package/dist/improvement-orchestrator.js +206 -0
- package/dist/inject-p0-vocabulary.js +300 -0
- package/dist/intent-parser.js +218 -0
- package/dist/invariant-algebra.js +476 -0
- package/dist/invariant-calculus.js +533 -0
- package/dist/ir-utils.js +70 -0
- package/dist/ir-utils.test.js +50 -0
- package/dist/knowledge-api.js +312 -0
- package/dist/knowledge-evolution.js +452 -0
- package/dist/knowledge-explorer.js +506 -0
- package/dist/knowledge-flywheel.js +274 -0
- package/dist/knowledge-governance.js +338 -0
- package/dist/knowledge-governance.test.js +150 -0
- package/dist/knowledge-graph.js +181 -0
- package/dist/knowledge-guided-synth.js +246 -0
- package/dist/knowledge-loop.test.js +77 -0
- package/dist/knowledge-object.js +316 -0
- package/dist/knowledge-package.js +98 -0
- package/dist/kpi-dashboard.js +561 -0
- package/dist/l3-cross-function.js +280 -0
- package/dist/learning-ranker.js +148 -0
- package/dist/learning-ranker.test.js +291 -0
- package/dist/ledger/accountability.js +322 -0
- package/dist/ledger/chain-builder.js +185 -0
- package/dist/ledger/cli.js +222 -0
- package/dist/ledger/index.js +13 -0
- package/dist/ledger/signatures.js +193 -0
- package/dist/ledger/types.js +9 -0
- package/dist/llm.js +74 -3
- package/dist/load-benchmarks.js +8 -3
- package/dist/logger.js +66 -0
- package/dist/logger.test.js +37 -0
- package/dist/logistic-reward.js +339 -0
- package/dist/logistic-reward.test.js +180 -0
- package/dist/macro-graph.js +193 -0
- package/dist/macro-repair.js +183 -0
- package/dist/mcp-server.mjs +1202 -483
- package/dist/memory-layer.js +42 -5
- package/dist/multi-repo-precision.js +422 -0
- package/dist/name-free-protocol.js +425 -0
- package/dist/name-free-protocol.test.js +170 -0
- package/dist/name-scrambling.js +138 -0
- package/dist/name-scrambling.test.js +16 -0
- package/dist/p3-observability.test.js +281 -0
- package/dist/p5-orchestrator.test.js +225 -0
- package/dist/pairwise-preference.js +294 -0
- package/dist/pairwise-preference.test.js +140 -0
- package/dist/planner-constraints.js +104 -0
- package/dist/planner-prompts.js +155 -0
- package/dist/planner-telemetry.js +415 -0
- package/dist/planner-trace.js +214 -0
- package/dist/planner.js +162 -167
- package/dist/plsb/artifact.js +116 -0
- package/dist/plsb/cli.js +71 -0
- package/dist/plsb/index.js +19 -0
- package/dist/plsb/leaderboard.js +249 -0
- package/dist/plsb/report-md.js +156 -0
- package/dist/plsb/schema.js +179 -0
- package/dist/plsb-benchmark.js +284 -0
- package/dist/plsb-benchmark.test.js +119 -0
- package/dist/policy/cli.js +134 -0
- package/dist/policy/engine.js +333 -0
- package/dist/policy/index.js +12 -0
- package/dist/policy/types.js +59 -0
- package/dist/policy-miner.js +505 -0
- package/dist/precision-analyze.js +229 -0
- package/dist/precision-benchmark.js +147 -0
- package/dist/precision-label-c.js +134 -0
- package/dist/precision-label.js +193 -0
- package/dist/precision-report-c.js +149 -0
- package/dist/precision-report.js +246 -0
- package/dist/progmune-status.js +108 -0
- package/dist/proof-engine.js +479 -0
- package/dist/proof-provenance.js +315 -0
- package/dist/protocol-coverage.js +294 -0
- package/dist/protocol-detector.js +1189 -0
- package/dist/protocol-embedding-expanded.js +297 -0
- package/dist/protocol-embedding-expanded.test.js +97 -0
- package/dist/protocol-embedding.js +195 -0
- package/dist/protocol-embedding.test.js +82 -0
- package/dist/protocol-extractor-v2.js +354 -0
- package/dist/protocol-extractor-v2.test.js +140 -0
- package/dist/protocol-extractor.js +310 -0
- package/dist/protocol-extractor.test.js +113 -0
- package/dist/protocol-foundation.js +322 -0
- package/dist/protocol-foundation.test.js +163 -0
- package/dist/protocol-frontier.js +243 -0
- package/dist/protocol-frontier.test.js +92 -0
- package/dist/protocol-gap-analyzer.js +228 -0
- package/dist/protocol-gap-analyzer.test.js +49 -0
- package/dist/protocol-invariants.js +276 -0
- package/dist/protocol-invariants.test.js +111 -0
- package/dist/protocol-knowledge.js +464 -0
- package/dist/protocol-miner.js +343 -0
- package/dist/protocol-mining.js +207 -0
- package/dist/protocol-mining.test.js +37 -0
- package/dist/protocol-registry.js +1 -1
- package/dist/protocol-security-benchmark.js +222 -0
- package/dist/protocol-vulnerability.js +257 -0
- package/dist/protocol-vulnerability.test.js +60 -0
- package/dist/python-benchmark.js +120 -0
- package/dist/python-emitter.js +163 -45
- package/dist/python-protocol-extractor.js +187 -0
- package/dist/python-protocol-extractor.test.js +116 -0
- package/dist/realworld-benchmark.js +646 -0
- package/dist/realworld-benchmark.test.js +36 -0
- package/dist/repair-arch.test.js +411 -0
- package/dist/repair-evolution.test.js +454 -0
- package/dist/repair-executor.js +719 -0
- package/dist/repair-proposal.js +4 -4
- package/dist/repair-ranker.js +141 -0
- package/dist/repair-strategies.js +419 -0
- package/dist/repair-taxonomy.js +234 -0
- package/dist/repair-types.js +12 -0
- package/dist/repo-evaluator.js +250 -0
- package/dist/repo-evaluator.test.js +128 -0
- package/dist/resource-abstraction.js +242 -0
- package/dist/resource-detector.js +211 -0
- package/dist/result.test.js +43 -0
- package/dist/reward-system.js +411 -0
- package/dist/reward-system.test.js +175 -0
- package/dist/risk-model.js +215 -0
- package/dist/rule-miner.js +234 -7
- package/dist/rule-specificity.js +254 -0
- package/dist/runtime-types.js +27 -0
- package/dist/scaffold.js +208 -0
- package/dist/scale-collector.test.js +101 -0
- package/dist/scale-trajectory-collector.js +128 -0
- package/dist/sdk.js +250 -0
- package/dist/search-planner.js +4 -41
- package/dist/semantic-snapshot.js +1 -1
- package/dist/semantic-topology.js +121 -0
- package/dist/semantic-trace.js +310 -317
- package/dist/sequence-extractor.js +343 -0
- package/dist/skill-library.js +245 -0
- package/dist/skill-planner.test.js +189 -0
- package/dist/software-physics.js +291 -0
- package/dist/software-physics.test.js +81 -0
- package/dist/ssg-precision.js +478 -0
- package/dist/ssg-validator.js +71 -21
- package/dist/state-inference-doubleblind.test.js +160 -0
- package/dist/state-inference.js +516 -0
- package/dist/state-inference.test.js +115 -0
- package/dist/state-machine-fingerprint.js +345 -0
- package/dist/state-machine-fingerprint.test.js +120 -0
- package/dist/state-miner.js +386 -0
- package/dist/state-name-inference.js +213 -0
- package/dist/state-name-inference.test.js +69 -0
- package/dist/strategy-planner.js +262 -96
- package/dist/strategy-planner.test.js +135 -0
- package/dist/telemetry-analytics.test.js +402 -0
- package/dist/terminal-format.js +68 -0
- package/dist/terminal-format.test.js +83 -0
- package/dist/topology-factory.js +196 -0
- package/dist/topology-representation.js +242 -0
- package/dist/topology-representation.test.js +27 -0
- package/dist/trajectory-augmentation.js +254 -0
- package/dist/trajectory-augmentation.test.js +63 -0
- package/dist/trajectory-corpus.js +440 -0
- package/dist/trajectory-corpus.test.js +32 -0
- package/dist/trajectory-feedback.test.js +116 -0
- package/dist/transition-synthesizer.js +286 -0
- package/dist/transition-synthesizer.test.js +123 -0
- package/dist/trust/api-semantic-mapper.js +809 -0
- package/dist/trust/call-graph-propagator.js +225 -0
- package/dist/trust/cli.js +122 -0
- package/dist/trust/compliance-scorer.js +283 -0
- package/dist/trust/confidence-calculator.js +261 -0
- package/dist/trust/engine.js +1145 -0
- package/dist/trust/explainability.js +85 -0
- package/dist/trust/formatters/ci.js +42 -0
- package/dist/trust/formatters/json.js +11 -0
- package/dist/trust/formatters/terminal.js +152 -0
- package/dist/trust/index.js +39 -0
- package/dist/trust/phase1-verify.js +171 -0
- package/dist/trust/protocol-domain-validator.js +697 -0
- package/dist/trust/score-calculator.js +282 -0
- package/dist/trust/ssg-bridge.js +641 -0
- package/dist/trust/ssg-bridge.test.js +269 -0
- package/dist/trust/types.js +67 -0
- package/dist/trust/violation-trace.js +335 -0
- package/dist/trust-api.js +179 -0
- package/dist/trust-calibration.js +279 -0
- package/dist/unknown-protocol-discovery.js +339 -0
- package/dist/unknown-protocol-discovery.test.js +102 -0
- package/dist/unsupervised-physics.js +230 -0
- package/dist/unsupervised-physics.test.js +95 -0
- package/dist/utils.test.js +37 -0
- package/dist/validator.js +187 -10
- package/dist/verification-intelligence.js +475 -0
- package/dist/verify-api.js +432 -0
- package/dist/vi-impact-report.js +293 -0
- package/dist/wl-fingerprint.js +162 -0
- package/dist/wl-fingerprint.test.js +130 -0
- package/dist/zeroshot-strategy.js +139 -0
- 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
- package/docs/Progmune_/351/241/271/347/233/256/345/205/250/350/247/243.html +710 -0
- package/package.json +74 -7
- package/protocols.json +1956 -50
- package/.dockerignore +0 -14
- package/.mcp.json +0 -11
- package/.progmune_allowlist +0 -50
- package/.test_report/test_report.md +0 -87
- package/Dockerfile +0 -9
- package/FAQ.md +0 -167
- package/WHITEPAPER.md +0 -540
- package/demo-project/auth.ts +0 -55
- package/demo-project/tsconfig.json +0 -8
- package/dist/acl-breakdown.js +0 -13
- package/dist/all-sessions.js +0 -11
- package/dist/antibody-stats.js +0 -11
- package/dist/branch-tree-count.js +0 -14
- package/dist/common-fixpath.js +0 -12
- package/dist/constraint-types.js +0 -12
- package/dist/exec-metrics.js +0 -11
- package/dist/failure-report.js +0 -11
- package/dist/fast-path-hits.js +0 -13
- package/dist/fingerprint-list.js +0 -15
- package/dist/gen-history-log.js +0 -13
- package/dist/heatmap-data.js +0 -11
- package/dist/recent-session.js +0 -12
- package/dist/svl-distribution.js +0 -11
- package/dist/terminal-status.js +0 -11
- package/dist/token-savings.js +0 -11
- package/dist/total-repairs.js +0 -12
- package/dist/unresolved-count.js +0 -12
- package/dist/valid-fingerprints.js +0 -13
- package/dist/verify-ledgers.js +0 -11
- package/docs/whitepaper-style.css +0 -77
- package/docs/whitepaper-v2.1.md +0 -609
- package/docs/whitepaper-v2.2.md +0 -1064
- package/docs/whitepaper-v2.2.pdf +0 -0
- package/fly.toml +0 -31
- package/public/dashboard.html +0 -119
- package/server/hub.js +0 -116
- package/test/replay-golden/sess_1780063202050_mgeld.json +0 -9
- package/test/replay-golden/sess_1780064032560_gocld.json +0 -354
- package/test/replay-golden/sess_1780064413331_s2709.json +0 -606
- package/test/replay-golden/sess_1780064792710_y3avo.json +0 -614
- package/test/replay-golden.ts +0 -84
- package/test_benchmark.js +0 -165
- package/test_comprehensive.mjs +0 -638
- package/test_concurrency.js +0 -129
- package/test_ir_robustness.js +0 -85
- package/test_semantic_contracts.js +0 -269
- package/test_ssg_stress.js +0 -156
- package/test_svl3.js +0 -58
- 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
|
+
});
|