progmune-runtime 2.1.6 → 3.2.1
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 +119 -461
- 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 +721 -0
- package/package.json +73 -6
- 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,478 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* P9.2p-SSG: Precision benchmark using REAL SSG state machine validation.
|
|
4
|
+
*
|
|
5
|
+
* Key difference from precision-benchmark.ts:
|
|
6
|
+
* precision-benchmark.ts: structural comparison (state count diff)
|
|
7
|
+
* ssg-precision.ts: SSG validation (pre_state → fn → post_state)
|
|
8
|
+
*
|
|
9
|
+
* Pipeline:
|
|
10
|
+
* 1. Extract call sequences from repo
|
|
11
|
+
* 2. Auto-discover protocol rules (auto-protocol-synthesizer)
|
|
12
|
+
* 3. Feed rules into SSG validator (ssg-validator)
|
|
13
|
+
* 4. Validate each sequence against discovered rules
|
|
14
|
+
* 5. Measure precision against labeled ground truth
|
|
15
|
+
*/
|
|
16
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
19
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
20
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
21
|
+
}
|
|
22
|
+
Object.defineProperty(o, k2, desc);
|
|
23
|
+
}) : (function(o, m, k, k2) {
|
|
24
|
+
if (k2 === undefined) k2 = k;
|
|
25
|
+
o[k2] = m[k];
|
|
26
|
+
}));
|
|
27
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
28
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
29
|
+
}) : function(o, v) {
|
|
30
|
+
o["default"] = v;
|
|
31
|
+
});
|
|
32
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
33
|
+
var ownKeys = function(o) {
|
|
34
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
35
|
+
var ar = [];
|
|
36
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
37
|
+
return ar;
|
|
38
|
+
};
|
|
39
|
+
return ownKeys(o);
|
|
40
|
+
};
|
|
41
|
+
return function (mod) {
|
|
42
|
+
if (mod && mod.__esModule) return mod;
|
|
43
|
+
var result = {};
|
|
44
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
45
|
+
__setModuleDefault(result, mod);
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
})();
|
|
49
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
+
exports.validateSequenceWithSSG = validateSequenceWithSSG;
|
|
51
|
+
exports.loadHandWrittenRules = loadHandWrittenRules;
|
|
52
|
+
exports.discoverRulesFromSequences = discoverRulesFromSequences;
|
|
53
|
+
exports.runSSGPrecisionBenchmark = runSSGPrecisionBenchmark;
|
|
54
|
+
exports.runHandWrittenPrecisionBenchmark = runHandWrittenPrecisionBenchmark;
|
|
55
|
+
const fs = __importStar(require("fs"));
|
|
56
|
+
const auto_protocol_synthesizer_1 = require("./auto-protocol-synthesizer");
|
|
57
|
+
const ssg_validator_1 = require("./ssg-validator");
|
|
58
|
+
// ═══════════════════════════════════════════════════════════════
|
|
59
|
+
// SSG Validation Engine
|
|
60
|
+
// ═══════════════════════════════════════════════════════════════
|
|
61
|
+
/**
|
|
62
|
+
* Validate a single call sequence against SSG protocol rules.
|
|
63
|
+
*
|
|
64
|
+
* Unlike detectStructuralViolations (which compares SM topology),
|
|
65
|
+
* this does proper state-machine validation:
|
|
66
|
+
* 1. Start with initial states (namespace defaults)
|
|
67
|
+
* 2. For each function call, check if pre_states are satisfied
|
|
68
|
+
* 3. Apply state changes (invalidate old, add new)
|
|
69
|
+
* 4. A sequence is "clean" if ALL function calls pass pre_state checks
|
|
70
|
+
* 5. A sequence is "violation" if ANY function call fails pre_state checks
|
|
71
|
+
*
|
|
72
|
+
* @returns true if ALL calls pass validation, false if any call fails
|
|
73
|
+
*/
|
|
74
|
+
function validateSequenceWithSSG(sequence, rules, nsInit = new Map([["_global", "INIT"]]), trainingSeqs) {
|
|
75
|
+
// Initialize per-namespace state sets
|
|
76
|
+
const nsStates = new Map();
|
|
77
|
+
for (const [ns, initState] of nsInit) {
|
|
78
|
+
nsStates.set(ns, new Set([initState]));
|
|
79
|
+
}
|
|
80
|
+
for (let i = 0; i < sequence.length; i++) {
|
|
81
|
+
const fn = sequence[i];
|
|
82
|
+
const rule = rules.get(fn);
|
|
83
|
+
// No rule for this function → no constraint → skip (not a violation)
|
|
84
|
+
if (!rule)
|
|
85
|
+
continue;
|
|
86
|
+
const ns = rule.namespace || "_global";
|
|
87
|
+
const states = nsStates.get(ns) || new Set(["INIT"]);
|
|
88
|
+
// Check preconditions
|
|
89
|
+
if (rule.pre_states.length > 0) {
|
|
90
|
+
const preMet = rule.pre_states.every(s => states.has(s));
|
|
91
|
+
if (!preMet) {
|
|
92
|
+
const missing = rule.pre_states.filter(s => !states.has(s));
|
|
93
|
+
return {
|
|
94
|
+
valid: false,
|
|
95
|
+
failingStep: i,
|
|
96
|
+
failingFunction: fn,
|
|
97
|
+
reason: `${fn} requires [${missing.join(",")}] but state has [${[...states].join(",")}] (namespace: ${ns})`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Apply state changes
|
|
102
|
+
if (rule.invalidate)
|
|
103
|
+
rule.invalidate.forEach(s => states.delete(s));
|
|
104
|
+
for (const s of rule.post_states)
|
|
105
|
+
states.add(s);
|
|
106
|
+
}
|
|
107
|
+
// P9.2p: Check for incomplete lifecycle.
|
|
108
|
+
// Only flag if ALL of these conditions are met:
|
|
109
|
+
// 1. The state was PRODUCED by a function in this sequence (not inherited from INIT)
|
|
110
|
+
// 2. A closer function EXISTS in the rule set that can invalidate this state
|
|
111
|
+
// 3. That closer function appears in the CLEAN training data as a frequent terminal call
|
|
112
|
+
// This prevents false positives on sequences that naturally end without a closer.
|
|
113
|
+
const lingering = [];
|
|
114
|
+
for (const [ns, st] of nsStates) {
|
|
115
|
+
if (ns === "_global")
|
|
116
|
+
continue;
|
|
117
|
+
for (const s of st) {
|
|
118
|
+
if (s === "INIT" || s === "IDLE")
|
|
119
|
+
continue;
|
|
120
|
+
// Condition 1: Was this state produced during this sequence?
|
|
121
|
+
let producedInSequence = false;
|
|
122
|
+
for (const fn of sequence) {
|
|
123
|
+
const r = rules.get(fn);
|
|
124
|
+
if (r && r.post_states.includes(s)) {
|
|
125
|
+
producedInSequence = true;
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (!producedInSequence)
|
|
130
|
+
continue;
|
|
131
|
+
// Condition 2: Does a closer function exist that can invalidate this state?
|
|
132
|
+
let closerFn = "";
|
|
133
|
+
for (const [fn, rule] of rules) {
|
|
134
|
+
if (rule.invalidate && rule.invalidate.includes(s)) {
|
|
135
|
+
closerFn = fn;
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (!closerFn)
|
|
140
|
+
continue;
|
|
141
|
+
// Condition 3: Has this closer been called in this sequence?
|
|
142
|
+
if (sequence.includes(closerFn))
|
|
143
|
+
continue; // already called — no violation
|
|
144
|
+
// Condition 4: Is this closer a frequent terminal call?
|
|
145
|
+
// Only flag if ≥50% of sequences containing the state's producer
|
|
146
|
+
// also call the closer as the last step.
|
|
147
|
+
if (trainingSeqs && !isFrequentTerminal(closerFn, s, trainingSeqs, rules))
|
|
148
|
+
continue;
|
|
149
|
+
lingering.push(`${ns}:${s}→${closerFn}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (lingering.length > 0) {
|
|
153
|
+
return {
|
|
154
|
+
valid: false,
|
|
155
|
+
failingStep: sequence.length - 1,
|
|
156
|
+
failingFunction: sequence[sequence.length - 1] || "",
|
|
157
|
+
reason: `Incomplete lifecycle: acquired states [${lingering.join(",")}] not released — missing closer call`,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
// P9.2p: Check for reordering violations.
|
|
161
|
+
// If function A always precedes function B in the clean training data,
|
|
162
|
+
// but B precedes A in this test sequence, that's a potential violation.
|
|
163
|
+
const orderViolations = checkOrderViolations(sequence, rules, nsInit);
|
|
164
|
+
if (orderViolations.length > 0) {
|
|
165
|
+
return {
|
|
166
|
+
valid: false,
|
|
167
|
+
failingStep: orderViolations[0].pos,
|
|
168
|
+
failingFunction: orderViolations[0].fn,
|
|
169
|
+
reason: `Order violation: ${orderViolations[0].reason}`,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return { valid: true, failingStep: -1, failingFunction: "", reason: "" };
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Check if any functions appear in the wrong order compared to
|
|
176
|
+
* the discovered protocol rules' state machine graph.
|
|
177
|
+
*
|
|
178
|
+
* Builds a partial order from the state machine topology:
|
|
179
|
+
* if fnA's post_states include S, and fnB's pre_states include S,
|
|
180
|
+
* then fnA must precede fnB. If fnB appears before fnA, it's a violation.
|
|
181
|
+
*/
|
|
182
|
+
function checkOrderViolations(sequence, rules, nsInit) {
|
|
183
|
+
const violations = [];
|
|
184
|
+
// Build must-precede graph from rules
|
|
185
|
+
for (const [fnA, ruleA] of rules) {
|
|
186
|
+
if (ruleA.post_states.length === 0)
|
|
187
|
+
continue;
|
|
188
|
+
for (const [fnB, ruleB] of rules) {
|
|
189
|
+
if (fnA === fnB)
|
|
190
|
+
continue;
|
|
191
|
+
// fnA produces a state that fnB requires → fnA must precede fnB
|
|
192
|
+
const shared = ruleA.post_states.filter(s => ruleB.pre_states.includes(s));
|
|
193
|
+
if (shared.length === 0)
|
|
194
|
+
continue;
|
|
195
|
+
// Check if fnB appears before fnA in the sequence
|
|
196
|
+
const posA = sequence.indexOf(fnA);
|
|
197
|
+
const posB = sequence.indexOf(fnB);
|
|
198
|
+
if (posB >= 0 && posA >= 0 && posB < posA) {
|
|
199
|
+
violations.push({
|
|
200
|
+
pos: posB,
|
|
201
|
+
fn: fnB,
|
|
202
|
+
reason: `${fnB} should come AFTER ${fnA} (state [${shared[0]}] must be produced first)`,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return violations;
|
|
208
|
+
}
|
|
209
|
+
// ═══════════════════════════════════════════════════════════════
|
|
210
|
+
// Auto-discover protocol rules from sequences
|
|
211
|
+
// ═══════════════════════════════════════════════════════════════
|
|
212
|
+
/**
|
|
213
|
+
* Load hand-written protocol rules from a JSON file (like protocols.json format).
|
|
214
|
+
*/
|
|
215
|
+
function loadHandWrittenRules(protoPath) {
|
|
216
|
+
const protoDef = JSON.parse(fs.readFileSync(protoPath, "utf-8"));
|
|
217
|
+
const fns = (0, ssg_validator_1.parseProtocolsFromJSON)(protoDef);
|
|
218
|
+
const rules = new Map();
|
|
219
|
+
for (const p of fns)
|
|
220
|
+
rules.set(p.function, p.protocol);
|
|
221
|
+
const nsInit = new Map();
|
|
222
|
+
nsInit.set("_global", "INIT");
|
|
223
|
+
if (protoDef.namespaceInitialStates) {
|
|
224
|
+
for (const [ns, init] of Object.entries(protoDef.namespaceInitialStates)) {
|
|
225
|
+
nsInit.set(ns, init);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return { rules, nsInit };
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Build ordering constraints from clean sequences.
|
|
232
|
+
* If fnA appears before fnB in ≥80% of sequences where both appear,
|
|
233
|
+
* then fnA must precede fnB.
|
|
234
|
+
*/
|
|
235
|
+
function buildOrderConstraints(sequences) {
|
|
236
|
+
const pairs = new Map();
|
|
237
|
+
for (const seq of sequences) {
|
|
238
|
+
for (let i = 0; i < seq.length; i++) {
|
|
239
|
+
for (let j = i + 1; j < seq.length; j++) {
|
|
240
|
+
const a = seq[i], b = seq[j];
|
|
241
|
+
if (a === b)
|
|
242
|
+
continue;
|
|
243
|
+
if (!pairs.has(a))
|
|
244
|
+
pairs.set(a, new Map());
|
|
245
|
+
if (!pairs.get(a).has(b))
|
|
246
|
+
pairs.get(a).set(b, { aBeforeB: 0, bBeforeA: 0 });
|
|
247
|
+
pairs.get(a).get(b).aBeforeB++;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
const constraints = new Map();
|
|
252
|
+
for (const [fnA, pbs] of pairs) {
|
|
253
|
+
for (const [fnB, counts] of pbs) {
|
|
254
|
+
const total = counts.aBeforeB + counts.bBeforeA;
|
|
255
|
+
// Require ≥2 co-occurrences AND ≥80% ordering confidence.
|
|
256
|
+
// Lower threshold catches more violations; higher precision trades off recall.
|
|
257
|
+
if (total >= 2 && counts.aBeforeB / total >= 0.8) {
|
|
258
|
+
if (!constraints.has(fnA))
|
|
259
|
+
constraints.set(fnA, new Set());
|
|
260
|
+
constraints.get(fnA).add(fnB);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return constraints;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Check if a closer function is a frequent terminal call for sequences
|
|
268
|
+
* that produce a given state. Prevents flagging sequences that naturally
|
|
269
|
+
* end without a closer.
|
|
270
|
+
*/
|
|
271
|
+
function isFrequentTerminal(closerFn, targetState, sequences, rules) {
|
|
272
|
+
let producesState = 0;
|
|
273
|
+
let closerAsLast = 0;
|
|
274
|
+
for (const seq of sequences) {
|
|
275
|
+
let hasProducer = false;
|
|
276
|
+
for (const fn of seq) {
|
|
277
|
+
const r = rules.get(fn);
|
|
278
|
+
if (r && r.post_states.includes(targetState)) {
|
|
279
|
+
hasProducer = true;
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if (!hasProducer)
|
|
284
|
+
continue;
|
|
285
|
+
producesState++;
|
|
286
|
+
if (seq.length > 0 && seq[seq.length - 1] === closerFn)
|
|
287
|
+
closerAsLast++;
|
|
288
|
+
}
|
|
289
|
+
// Closer must appear as last call in ≥50% of sequences that produce this state
|
|
290
|
+
return producesState >= 2 && closerAsLast / producesState >= 0.5;
|
|
291
|
+
}
|
|
292
|
+
/** Validate a sequence against ordering constraints + SSG rules. */
|
|
293
|
+
function validateWithOrdering(sequence, rules, nsInit, orderConstraints, trainingSeqs) {
|
|
294
|
+
// First: SSG validation
|
|
295
|
+
const ssgResult = validateSequenceWithSSG(sequence, rules, nsInit, trainingSeqs);
|
|
296
|
+
if (!ssgResult.valid)
|
|
297
|
+
return ssgResult;
|
|
298
|
+
// Second: ordering constraint check
|
|
299
|
+
for (let i = 0; i < sequence.length; i++) {
|
|
300
|
+
const fnA = sequence[i];
|
|
301
|
+
const mustFollow = orderConstraints.get(fnA);
|
|
302
|
+
if (!mustFollow)
|
|
303
|
+
continue;
|
|
304
|
+
// Check if any required follower is BEFORE fnA in this sequence
|
|
305
|
+
for (let j = 0; j < i; j++) {
|
|
306
|
+
const fnB = sequence[j];
|
|
307
|
+
if (mustFollow.has(fnB)) {
|
|
308
|
+
return {
|
|
309
|
+
valid: false, failingStep: j, failingFunction: fnB,
|
|
310
|
+
reason: `Order violation: ${fnB} should come AFTER ${fnA} (appears before in ${Math.round(sequence.length / 2)} of clean sequences)`,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return { valid: true, failingStep: -1, failingFunction: "", reason: "" };
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Discover protocol rules from call sequences using the auto-protocol-synthesizer.
|
|
319
|
+
* Returns SSG-compatible StateAnnotation rules ready for validation.
|
|
320
|
+
*/
|
|
321
|
+
function discoverRulesFromSequences(sequences) {
|
|
322
|
+
const synthesized = (0, auto_protocol_synthesizer_1.synthesizeProtocols)(sequences);
|
|
323
|
+
const rules = new Map();
|
|
324
|
+
const namespaces = new Set();
|
|
325
|
+
for (const proto of synthesized) {
|
|
326
|
+
for (const r of proto.rules) {
|
|
327
|
+
// Infer namespace from prototype pattern
|
|
328
|
+
const ns = proto.inferredPattern || "discovered";
|
|
329
|
+
namespaces.add(ns);
|
|
330
|
+
rules.set(r.function, {
|
|
331
|
+
pre_states: r.pre_states,
|
|
332
|
+
post_states: r.post_states,
|
|
333
|
+
invalidate: r.invalidate,
|
|
334
|
+
namespace: ns,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
// Build namespace initial states
|
|
339
|
+
const nsInit = new Map();
|
|
340
|
+
nsInit.set("_global", "INIT");
|
|
341
|
+
for (const ns of namespaces) {
|
|
342
|
+
nsInit.set(ns, "INIT");
|
|
343
|
+
}
|
|
344
|
+
return { rules, nsInit };
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Run precision benchmark using SSG validation with auto-discovered rules.
|
|
348
|
+
*
|
|
349
|
+
* @param sequences Labeled call sequences
|
|
350
|
+
* @param labels Index → 'clean' | 'violation'
|
|
351
|
+
* @returns SSGPrecisionResult with TP/FP/TN/FN breakdown
|
|
352
|
+
*/
|
|
353
|
+
function runSSGPrecisionBenchmark(sequences, labels) {
|
|
354
|
+
// 1. Build rule set from clean-labeled sequences
|
|
355
|
+
const cleanSeqs = sequences
|
|
356
|
+
.filter(s => labels[s.index] === "clean" && s.calls.length >= 2)
|
|
357
|
+
.map(s => s.calls);
|
|
358
|
+
if (cleanSeqs.length === 0) {
|
|
359
|
+
return { total: 0, truePositive: 0, falsePositive: 0, trueNegative: 0, falseNegative: 0, precision: 0, recall: 0, f1: 0, discoveredRules: 0, mismatches: [] };
|
|
360
|
+
}
|
|
361
|
+
// 2. Auto-discover protocol rules
|
|
362
|
+
const { rules, nsInit } = discoverRulesFromSequences(cleanSeqs);
|
|
363
|
+
// 2b. Build ordering constraints from clean data
|
|
364
|
+
const orderConstraints = buildOrderConstraints(cleanSeqs);
|
|
365
|
+
// 3. Validate each sequence
|
|
366
|
+
let tp = 0, fp = 0, tn = 0, fn = 0;
|
|
367
|
+
const mismatches = [];
|
|
368
|
+
for (const seq of sequences) {
|
|
369
|
+
const expected = labels[seq.index];
|
|
370
|
+
if (!expected)
|
|
371
|
+
continue;
|
|
372
|
+
const result = validateWithOrdering(seq.calls, rules, nsInit, orderConstraints, cleanSeqs);
|
|
373
|
+
const detected = result.valid ? "clean" : "violation";
|
|
374
|
+
if (expected === "clean" && detected === "clean") {
|
|
375
|
+
tn++;
|
|
376
|
+
}
|
|
377
|
+
else if (expected === "violation" && detected === "violation") {
|
|
378
|
+
tp++;
|
|
379
|
+
}
|
|
380
|
+
else if (expected === "clean" && detected === "violation") {
|
|
381
|
+
fp++;
|
|
382
|
+
mismatches.push({ index: seq.index, functionName: seq.functionName, expected, detected, reason: result.reason });
|
|
383
|
+
}
|
|
384
|
+
else if (expected === "violation" && detected === "clean") {
|
|
385
|
+
fn++;
|
|
386
|
+
mismatches.push({ index: seq.index, functionName: seq.functionName, expected, detected, reason: "SSG passed — no pre_state violation detected" });
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
const total = tp + fp + tn + fn;
|
|
390
|
+
const precision = total > 0 ? tp / (tp + fp) : 0;
|
|
391
|
+
const recall = total > 0 ? tp / (tp + fn) : 0;
|
|
392
|
+
const f1 = precision + recall > 0 ? 2 * precision * recall / (precision + recall) : 0;
|
|
393
|
+
return { total, truePositive: tp, falsePositive: fp, trueNegative: tn, falseNegative: fn, precision, recall, f1, discoveredRules: rules.size, mismatches };
|
|
394
|
+
}
|
|
395
|
+
// ═══════════════════════════════════════════════════════════════
|
|
396
|
+
// Hand-written rule precision benchmark
|
|
397
|
+
// ═══════════════════════════════════════════════════════════════
|
|
398
|
+
/**
|
|
399
|
+
* Run precision benchmark using HAND-WRITTEN protocol rules instead of auto-discovered ones.
|
|
400
|
+
* This measures the UPPER BOUND of what the SSG validator can achieve.
|
|
401
|
+
*/
|
|
402
|
+
function runHandWrittenPrecisionBenchmark(sequences, labels, protoPath) {
|
|
403
|
+
const { rules, nsInit } = loadHandWrittenRules(protoPath);
|
|
404
|
+
let tp = 0, fp = 0, tn = 0, fn = 0;
|
|
405
|
+
const mismatches = [];
|
|
406
|
+
for (const seq of sequences) {
|
|
407
|
+
const expected = labels[seq.index];
|
|
408
|
+
if (!expected)
|
|
409
|
+
continue;
|
|
410
|
+
const result = validateSequenceWithSSG(seq.calls, rules, nsInit);
|
|
411
|
+
const detected = result.valid ? "clean" : "violation";
|
|
412
|
+
if (expected === "clean" && detected === "clean") {
|
|
413
|
+
tn++;
|
|
414
|
+
}
|
|
415
|
+
else if (expected === "violation" && detected === "violation") {
|
|
416
|
+
tp++;
|
|
417
|
+
}
|
|
418
|
+
else if (expected === "clean" && detected === "violation") {
|
|
419
|
+
fp++;
|
|
420
|
+
mismatches.push({ index: seq.index, functionName: seq.functionName, expected, detected, reason: result.reason });
|
|
421
|
+
}
|
|
422
|
+
else if (expected === "violation" && detected === "clean") {
|
|
423
|
+
fn++;
|
|
424
|
+
mismatches.push({ index: seq.index, functionName: seq.functionName, expected, detected, reason: "SSG passed — no violation detected" });
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
const total = tp + fp + tn + fn;
|
|
428
|
+
const precision = total > 0 ? tp / (tp + fp) : 0;
|
|
429
|
+
const recall = total > 0 ? tp / (tp + fn) : 0;
|
|
430
|
+
const f1 = precision + recall > 0 ? 2 * precision * recall / (precision + recall) : 0;
|
|
431
|
+
return { total, truePositive: tp, falsePositive: fp, trueNegative: tn, falseNegative: fn, precision, recall, f1, discoveredRules: rules.size, mismatches };
|
|
432
|
+
}
|
|
433
|
+
// ═══════════════════════════════════════════════════════════════
|
|
434
|
+
// CLI — only runs when executed directly (not required from tests)
|
|
435
|
+
// ═══════════════════════════════════════════════════════════════
|
|
436
|
+
if (require.main === module) {
|
|
437
|
+
const args = process.argv.slice(2);
|
|
438
|
+
const seqFile = args[0];
|
|
439
|
+
const labelFile = args[1];
|
|
440
|
+
if (!seqFile || !labelFile) {
|
|
441
|
+
console.error("Usage: npx tsx src/ssg-precision.ts <sequences.json> <labels.json>");
|
|
442
|
+
process.exit(1);
|
|
443
|
+
}
|
|
444
|
+
const seqData = JSON.parse(fs.readFileSync(seqFile, "utf-8"));
|
|
445
|
+
const rawSeqs = seqData.sequences || seqData;
|
|
446
|
+
// Add index to each sequence if missing
|
|
447
|
+
const sequences = rawSeqs.map((s, i) => ({
|
|
448
|
+
index: s.index ?? i,
|
|
449
|
+
functionName: s.functionName || s.functionName || (`seq_${i}`),
|
|
450
|
+
calls: s.calls || [],
|
|
451
|
+
expected: 'clean',
|
|
452
|
+
}));
|
|
453
|
+
const labels = JSON.parse(fs.readFileSync(labelFile, "utf-8"));
|
|
454
|
+
console.log("Running SSG precision benchmark...");
|
|
455
|
+
const result = runSSGPrecisionBenchmark(sequences, labels);
|
|
456
|
+
console.log("\n=== SSG Precision Benchmark Results ===");
|
|
457
|
+
console.log(`Discovered rules: ${result.discoveredRules}`);
|
|
458
|
+
console.log(`Sequences evaluated: ${result.total}`);
|
|
459
|
+
console.log(`True Positives: ${result.truePositive}`);
|
|
460
|
+
console.log(`False Positives: ${result.falsePositive}`);
|
|
461
|
+
console.log(`True Negatives: ${result.trueNegative}`);
|
|
462
|
+
console.log(`False Negatives: ${result.falseNegative}`);
|
|
463
|
+
console.log(`\nPrecision: ${(result.precision * 100).toFixed(1)}%`);
|
|
464
|
+
console.log(`Recall: ${(result.recall * 100).toFixed(1)}%`);
|
|
465
|
+
console.log(`F1 Score: ${(result.f1 * 100).toFixed(1)}%`);
|
|
466
|
+
if (result.mismatches.length > 0) {
|
|
467
|
+
console.log(`\n--- Mismatches (${result.mismatches.length}) ---`);
|
|
468
|
+
for (const m of result.mismatches.slice(0, 20)) {
|
|
469
|
+
console.log(` [${m.index}] ${m.functionName}: expected=${m.expected} detected=${m.detected}`);
|
|
470
|
+
console.log(` ${m.reason}`);
|
|
471
|
+
}
|
|
472
|
+
if (result.mismatches.length > 20) {
|
|
473
|
+
console.log(` ... and ${result.mismatches.length - 20} more`);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
fs.writeFileSync("benchmarks/ssg-precision-report.json", JSON.stringify({ timestamp: new Date().toISOString(), result }, null, 2));
|
|
477
|
+
console.log("\nReport saved to benchmarks/ssg-precision-report.json");
|
|
478
|
+
} // end if (require.main === module)
|
package/dist/ssg-validator.js
CHANGED
|
@@ -260,32 +260,34 @@ function validateTransition(ctx, candidateFunctionName, actionIndex, rules, name
|
|
|
260
260
|
valid: true,
|
|
261
261
|
ruleHash,
|
|
262
262
|
};
|
|
263
|
-
// Invariant-1: delta consistency
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
`
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
263
|
+
// Invariant-1: delta consistency (skip in fast mode for performance)
|
|
264
|
+
if (process.env.PROGMUNE_FAST_VALIDATE !== "true") {
|
|
265
|
+
const deltaMap = fromSnapshot(currentState);
|
|
266
|
+
applyTransitionDelta(deltaMap, transition);
|
|
267
|
+
const computedAfter = toSnapshot(deltaMap);
|
|
268
|
+
if (!deepEqualSnapshots(computedAfter, statesAfter)) {
|
|
269
|
+
const detail = `[Invariant-1] Delta consistency violation in validateTransition for "${candidateFunctionName}":\n` +
|
|
270
|
+
` acquired: [${acquired.join(", ")}] invalidated: [${invalidated.join(", ")}]\n` +
|
|
271
|
+
` expected after: ${JSON.stringify(computedAfter)}\n` +
|
|
272
|
+
` actual after: ${JSON.stringify(statesAfter)}`;
|
|
273
|
+
// P0 Strict Mode: fail fast on kernel corruption
|
|
274
|
+
if (process.env.PROGMUNE_STRICT !== "false") {
|
|
275
|
+
throw new InvariantViolationError(detail, {
|
|
276
|
+
invariant: "delta-consistency",
|
|
277
|
+
namespace: ns,
|
|
278
|
+
function: candidateFunctionName,
|
|
279
|
+
expected: computedAfter,
|
|
280
|
+
actual: statesAfter,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
console.error(detail);
|
|
281
284
|
}
|
|
282
|
-
|
|
283
|
-
}
|
|
285
|
+
} // PROGMUNE_FAST_VALIDATE guard
|
|
284
286
|
return { valid: true, transition };
|
|
285
287
|
}
|
|
286
288
|
// ── checkLedgerConsistency: Invariant-0 + Invariant-1 over full ledger ──
|
|
287
289
|
/** @requires LEDGER_DATA @produces CONSISTENCY_RESULT */
|
|
288
|
-
function checkLedgerConsistency(ledger, namespaceInitialStates = new Map([["_global", "INIT"]])) {
|
|
290
|
+
function checkLedgerConsistency(ledger, namespaceInitialStates = new Map([["_global", "INIT"]]), protocolRules) {
|
|
289
291
|
const violations = [];
|
|
290
292
|
const running = fromSnapshot({});
|
|
291
293
|
// Pre-scan: collect all namespaces referenced in any transition
|
|
@@ -344,6 +346,53 @@ function checkLedgerConsistency(ledger, namespaceInitialStates = new Map([["_glo
|
|
|
344
346
|
});
|
|
345
347
|
}
|
|
346
348
|
}
|
|
349
|
+
// Invariant-2: delta legality — acquired/invalidated must match protocol rules
|
|
350
|
+
if (t.valid && protocolRules && protocolRules.size > 0) {
|
|
351
|
+
const rule = protocolRules.get(t.function);
|
|
352
|
+
if (rule) {
|
|
353
|
+
// Check acquired states are in the function's post_states
|
|
354
|
+
const beforeStates = t.statesBefore[t.namespace] || [];
|
|
355
|
+
for (const a of (t.acquired || [])) {
|
|
356
|
+
if (!rule.post_states.includes(a)) {
|
|
357
|
+
violations.push({
|
|
358
|
+
index: i,
|
|
359
|
+
invariant: "delta-legality",
|
|
360
|
+
detail: `Transition[${i}] "${t.function}": acquired state "${a}" is not in protocol post_states [${rule.post_states.join(", ")}]`,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
// Reject acquiring a state that already exists in statesBefore
|
|
364
|
+
if (beforeStates.includes(a)) {
|
|
365
|
+
violations.push({
|
|
366
|
+
index: i,
|
|
367
|
+
invariant: "delta-legality",
|
|
368
|
+
detail: `Transition[${i}] "${t.function}": acquired state "${a}" already exists in statesBefore [${beforeStates.join(", ")}]`,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
// Check invalidated states are in the function's invalidate list
|
|
373
|
+
for (const inv of (t.invalidated || [])) {
|
|
374
|
+
if (!(rule.invalidate || []).includes(inv)) {
|
|
375
|
+
violations.push({
|
|
376
|
+
index: i,
|
|
377
|
+
invariant: "delta-legality",
|
|
378
|
+
detail: `Transition[${i}] "${t.function}": invalidated state "${inv}" is not in protocol invalidate [${(rule.invalidate || []).join(", ")}]`,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
// Check no missing expected invalidations (unless the function has no invalidate rule)
|
|
383
|
+
if (rule.invalidate && rule.invalidate.length > 0) {
|
|
384
|
+
for (const expectedInv of rule.invalidate) {
|
|
385
|
+
if (!(t.invalidated || []).includes(expectedInv)) {
|
|
386
|
+
violations.push({
|
|
387
|
+
index: i,
|
|
388
|
+
invariant: "delta-legality",
|
|
389
|
+
detail: `Transition[${i}] "${t.function}": expected invalidation of "${expectedInv}" per protocol, but not present in transition`,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
347
396
|
// Advance running state
|
|
348
397
|
if (t.valid) {
|
|
349
398
|
applyTransitionDelta(running, t);
|
|
@@ -671,6 +720,7 @@ function parseProtocolsFromJSON(protocolDef) {
|
|
|
671
720
|
post_states: rule.post_states,
|
|
672
721
|
invalidate: rule.invalidate,
|
|
673
722
|
namespace: rule.namespace,
|
|
723
|
+
aliases: rule.aliases,
|
|
674
724
|
},
|
|
675
725
|
});
|
|
676
726
|
}
|