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,475 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* P5: Verification Intelligence — the 5th paradigm shift.
|
|
4
|
+
*
|
|
5
|
+
* Moves Progmune from a Pattern Matcher to a Verification Decision Engine.
|
|
6
|
+
*
|
|
7
|
+
* The core question changes from:
|
|
8
|
+
* "Does this code violate protocol X?"
|
|
9
|
+
* to:
|
|
10
|
+
* "Should I alert on this? Why? Why not? What did I learn?"
|
|
11
|
+
*
|
|
12
|
+
* Architecture:
|
|
13
|
+
*
|
|
14
|
+
* Verification → Decision (alert? suppress?) → FP detected →
|
|
15
|
+
* Classify FP reason → Profile context → Adjust rule confidence →
|
|
16
|
+
* → Next time: better decision
|
|
17
|
+
*
|
|
18
|
+
* This closes the loop that turns every false positive into
|
|
19
|
+
* Verification Intelligence.
|
|
20
|
+
*
|
|
21
|
+
* FP Taxonomy (why did we wrongly alert?):
|
|
22
|
+
* FP-1: RULE_TOO_BROAD — Rule matches too many patterns (overfitting to clean seqs)
|
|
23
|
+
* FP-2: CONTEXT_MISMATCH — Rule is valid but wrong context (e.g., internal/init code)
|
|
24
|
+
* FP-3: NAMESPACE_LEAK — Rule from one namespace applied to another
|
|
25
|
+
* FP-4: ORDER_INSENSITIVE — Rule requires strict ordering but code is order-flexible
|
|
26
|
+
* FP-5: INCOMPLETE_RULE — Rule is too specific (missing valid alternative paths)
|
|
27
|
+
* FP-6: DOMAIN_IRRELEVANT — Rule is technically correct but irrelevant to this domain
|
|
28
|
+
* FP-7: LEGACY_COMPAT — Code follows legacy pattern that protocol doesn't cover
|
|
29
|
+
*/
|
|
30
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
31
|
+
if (k2 === undefined) k2 = k;
|
|
32
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
33
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
34
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
35
|
+
}
|
|
36
|
+
Object.defineProperty(o, k2, desc);
|
|
37
|
+
}) : (function(o, m, k, k2) {
|
|
38
|
+
if (k2 === undefined) k2 = k;
|
|
39
|
+
o[k2] = m[k];
|
|
40
|
+
}));
|
|
41
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
42
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
43
|
+
}) : function(o, v) {
|
|
44
|
+
o["default"] = v;
|
|
45
|
+
});
|
|
46
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
47
|
+
var ownKeys = function(o) {
|
|
48
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
49
|
+
var ar = [];
|
|
50
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
51
|
+
return ar;
|
|
52
|
+
};
|
|
53
|
+
return ownKeys(o);
|
|
54
|
+
};
|
|
55
|
+
return function (mod) {
|
|
56
|
+
if (mod && mod.__esModule) return mod;
|
|
57
|
+
var result = {};
|
|
58
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
59
|
+
__setModuleDefault(result, mod);
|
|
60
|
+
return result;
|
|
61
|
+
};
|
|
62
|
+
})();
|
|
63
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
64
|
+
exports.VerificationIntelligence = exports.FP_REASON_LABELS = void 0;
|
|
65
|
+
exports.getVerificationIntelligence = getVerificationIntelligence;
|
|
66
|
+
exports.formatVILearningReport = formatVILearningReport;
|
|
67
|
+
const fs = __importStar(require("fs"));
|
|
68
|
+
const path = __importStar(require("path"));
|
|
69
|
+
exports.FP_REASON_LABELS = {
|
|
70
|
+
RULE_TOO_BROAD: {
|
|
71
|
+
label: "Rule too broad",
|
|
72
|
+
fix: "Narrow the rule by adding more specific pre/post states",
|
|
73
|
+
},
|
|
74
|
+
CONTEXT_MISMATCH: {
|
|
75
|
+
label: "Context mismatch",
|
|
76
|
+
fix: "Add context filter — not all call sites require the same protocol",
|
|
77
|
+
},
|
|
78
|
+
NAMESPACE_LEAK: {
|
|
79
|
+
label: "Namespace leak",
|
|
80
|
+
fix: "Restrict rule to its namespace; don't cross-apply",
|
|
81
|
+
},
|
|
82
|
+
ORDER_INSENSITIVE: {
|
|
83
|
+
label: "Order insensitive",
|
|
84
|
+
fix: "Relax ordering constraint — this protocol allows flexible ordering",
|
|
85
|
+
},
|
|
86
|
+
INCOMPLETE_RULE: {
|
|
87
|
+
label: "Incomplete rule",
|
|
88
|
+
fix: "Add missing alternative paths to the rule definition",
|
|
89
|
+
},
|
|
90
|
+
DOMAIN_IRRELEVANT: {
|
|
91
|
+
label: "Domain irrelevant",
|
|
92
|
+
fix: "Exclude this domain/codebase from this rule",
|
|
93
|
+
},
|
|
94
|
+
LEGACY_COMPAT: {
|
|
95
|
+
label: "Legacy compatibility",
|
|
96
|
+
fix: "Add legacy pattern as an accepted alternative",
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
// ═══════════════════════════════════════════════════════════════
|
|
100
|
+
// Verification Decision Engine
|
|
101
|
+
// ═══════════════════════════════════════════════════════════════
|
|
102
|
+
const VI_DIR = ".progmune_corpus/verification-intelligence";
|
|
103
|
+
class VerificationIntelligence {
|
|
104
|
+
constructor() {
|
|
105
|
+
this.ruleConfidences = new Map();
|
|
106
|
+
this.fpRecords = [];
|
|
107
|
+
this.contextFilters = new Map(); // rule → excluded contexts
|
|
108
|
+
this.load();
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Make a verification decision: should we alert on this rule match?
|
|
112
|
+
*
|
|
113
|
+
* This REPLACES the binary valid/invalid with a calibrated decision.
|
|
114
|
+
*/
|
|
115
|
+
decide(ruleName, protocol, context) {
|
|
116
|
+
const key = `${protocol}:${ruleName}`;
|
|
117
|
+
let rc = this.ruleConfidences.get(key);
|
|
118
|
+
if (!rc) {
|
|
119
|
+
// New rule — start with high confidence
|
|
120
|
+
rc = {
|
|
121
|
+
ruleName,
|
|
122
|
+
protocol,
|
|
123
|
+
initialConfidence: 0.9,
|
|
124
|
+
currentConfidence: 0.9,
|
|
125
|
+
totalAlerts: 0,
|
|
126
|
+
truePositives: 0,
|
|
127
|
+
falsePositives: 0,
|
|
128
|
+
suppressionThreshold: 0.3,
|
|
129
|
+
suppressed: false,
|
|
130
|
+
fpHistory: [],
|
|
131
|
+
lastAdjusted: new Date().toISOString(),
|
|
132
|
+
};
|
|
133
|
+
this.ruleConfidences.set(key, rc);
|
|
134
|
+
}
|
|
135
|
+
// Check context filters
|
|
136
|
+
if (this.isContextFiltered(ruleName, context)) {
|
|
137
|
+
return {
|
|
138
|
+
alert: false,
|
|
139
|
+
confidence: 0,
|
|
140
|
+
reason: `Rule ${ruleName} suppressed by context filter`,
|
|
141
|
+
triggeredRules: [],
|
|
142
|
+
suppressedRules: [ruleName],
|
|
143
|
+
recommendation: "SUPPRESS",
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
// Check if rule is suppressed (confidence too low)
|
|
147
|
+
if (rc.suppressed || rc.currentConfidence < rc.suppressionThreshold) {
|
|
148
|
+
return {
|
|
149
|
+
alert: false,
|
|
150
|
+
confidence: rc.currentConfidence,
|
|
151
|
+
reason: `Rule ${ruleName} suppressed (confidence ${(rc.currentConfidence * 100).toFixed(0)}% < threshold ${(rc.suppressionThreshold * 100).toFixed(0)}%)`,
|
|
152
|
+
triggeredRules: [],
|
|
153
|
+
suppressedRules: [ruleName],
|
|
154
|
+
recommendation: "SUPPRESS",
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
// Determine recommendation level based on confidence
|
|
158
|
+
let recommendation;
|
|
159
|
+
if (rc.currentConfidence >= 0.8)
|
|
160
|
+
recommendation = "BLOCK";
|
|
161
|
+
else if (rc.currentConfidence >= 0.6)
|
|
162
|
+
recommendation = "WARN";
|
|
163
|
+
else if (rc.currentConfidence >= 0.4)
|
|
164
|
+
recommendation = "INFO";
|
|
165
|
+
else
|
|
166
|
+
recommendation = "SUPPRESS";
|
|
167
|
+
return {
|
|
168
|
+
alert: true,
|
|
169
|
+
confidence: rc.currentConfidence,
|
|
170
|
+
reason: `Rule ${ruleName} triggered with ${(rc.currentConfidence * 100).toFixed(0)}% confidence (${rc.truePositives} TP, ${rc.falsePositives} FP)`,
|
|
171
|
+
triggeredRules: [ruleName],
|
|
172
|
+
suppressedRules: [],
|
|
173
|
+
recommendation,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Record a false positive and adjust rule confidence DOWN.
|
|
178
|
+
*
|
|
179
|
+
* This is the core learning mechanism: every FP reduces confidence,
|
|
180
|
+
* eventually suppressing the rule if it's too noisy.
|
|
181
|
+
*/
|
|
182
|
+
recordFP(params) {
|
|
183
|
+
const key = `${params.protocol}:${params.ruleName}`;
|
|
184
|
+
let rc = this.ruleConfidences.get(key);
|
|
185
|
+
const record = {
|
|
186
|
+
id: `FP-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`,
|
|
187
|
+
timestamp: new Date().toISOString(),
|
|
188
|
+
repo: params.repo,
|
|
189
|
+
protocol: params.protocol,
|
|
190
|
+
sequence: params.sequence,
|
|
191
|
+
triggeredRules: [params.ruleName],
|
|
192
|
+
reason: params.reason,
|
|
193
|
+
context: params.context || {},
|
|
194
|
+
action: this.determineAction(params.reason),
|
|
195
|
+
labeledBy: params.labeledBy || "auto",
|
|
196
|
+
confidenceDelta: 0,
|
|
197
|
+
};
|
|
198
|
+
// Create rule if it doesn't exist yet
|
|
199
|
+
if (!rc) {
|
|
200
|
+
rc = {
|
|
201
|
+
ruleName: params.ruleName,
|
|
202
|
+
protocol: params.protocol,
|
|
203
|
+
initialConfidence: 0.9,
|
|
204
|
+
currentConfidence: 0.9,
|
|
205
|
+
totalAlerts: 0,
|
|
206
|
+
truePositives: 0,
|
|
207
|
+
falsePositives: 0,
|
|
208
|
+
suppressionThreshold: 0.3,
|
|
209
|
+
suppressed: false,
|
|
210
|
+
fpHistory: [],
|
|
211
|
+
lastAdjusted: new Date().toISOString(),
|
|
212
|
+
};
|
|
213
|
+
this.ruleConfidences.set(key, rc);
|
|
214
|
+
}
|
|
215
|
+
// Adjust confidence
|
|
216
|
+
rc.falsePositives++;
|
|
217
|
+
rc.totalAlerts++;
|
|
218
|
+
// Confidence decay: each FP reduces confidence by a factor
|
|
219
|
+
// New confidence = TP / (TP + FP) with Laplace smoothing
|
|
220
|
+
const effectiveTP = rc.truePositives + 1; // Laplace prior
|
|
221
|
+
const effectiveFP = rc.falsePositives + 1;
|
|
222
|
+
const oldConf = rc.currentConfidence;
|
|
223
|
+
rc.currentConfidence = effectiveTP / (effectiveTP + effectiveFP);
|
|
224
|
+
record.confidenceDelta = rc.currentConfidence - oldConf;
|
|
225
|
+
// Suppress if too noisy
|
|
226
|
+
if (rc.currentConfidence < rc.suppressionThreshold && rc.falsePositives >= 5) {
|
|
227
|
+
rc.suppressed = true;
|
|
228
|
+
}
|
|
229
|
+
rc.fpHistory.push(record.id);
|
|
230
|
+
rc.lastAdjusted = record.timestamp;
|
|
231
|
+
this.ruleConfidences.set(key, rc);
|
|
232
|
+
this.fpRecords.push(record);
|
|
233
|
+
this.save();
|
|
234
|
+
return record;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Record a true positive — confirms the rule is working.
|
|
238
|
+
*/
|
|
239
|
+
recordTP(ruleName, protocol) {
|
|
240
|
+
const key = `${protocol}:${ruleName}`;
|
|
241
|
+
let rc = this.ruleConfidences.get(key);
|
|
242
|
+
if (!rc) {
|
|
243
|
+
rc = {
|
|
244
|
+
ruleName, protocol,
|
|
245
|
+
initialConfidence: 0.9, currentConfidence: 0.9,
|
|
246
|
+
totalAlerts: 0, truePositives: 0, falsePositives: 0,
|
|
247
|
+
suppressionThreshold: 0.3, suppressed: false,
|
|
248
|
+
fpHistory: [], lastAdjusted: new Date().toISOString(),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
rc.truePositives++;
|
|
252
|
+
rc.totalAlerts++;
|
|
253
|
+
// Confidence boost from TP
|
|
254
|
+
const effectiveTP = rc.truePositives;
|
|
255
|
+
const effectiveFP = rc.falsePositives + 1; // Laplace
|
|
256
|
+
rc.currentConfidence = effectiveTP / (effectiveTP + effectiveFP);
|
|
257
|
+
// Un-suppress if confidence recovers
|
|
258
|
+
if (rc.suppressed && rc.currentConfidence >= rc.suppressionThreshold + 0.1) {
|
|
259
|
+
rc.suppressed = false;
|
|
260
|
+
}
|
|
261
|
+
rc.lastAdjusted = new Date().toISOString();
|
|
262
|
+
this.ruleConfidences.set(key, rc);
|
|
263
|
+
this.save();
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Auto-classify a false positive based on sequence and rule characteristics.
|
|
267
|
+
*/
|
|
268
|
+
autoClassifyFP(params) {
|
|
269
|
+
const { ruleName, sequence, context } = params;
|
|
270
|
+
// Heuristic 1: Context mismatch (test/init code) — most common FP source
|
|
271
|
+
if (context?.isTestCode || context?.isInitCode) {
|
|
272
|
+
return "CONTEXT_MISMATCH";
|
|
273
|
+
}
|
|
274
|
+
// Heuristic 2: Legacy/deprecated patterns — check BEFORE short-sequence heuristic
|
|
275
|
+
if (sequence.some(fn => /legacy|deprecated|old_|_v1|_compat/i.test(fn))) {
|
|
276
|
+
return "LEGACY_COMPAT";
|
|
277
|
+
}
|
|
278
|
+
// Heuristic 3: Internal/private functions (start with _ or contain "internal")
|
|
279
|
+
if (sequence.some(fn => fn.startsWith("_") || fn.includes("internal"))) {
|
|
280
|
+
return "DOMAIN_IRRELEVANT";
|
|
281
|
+
}
|
|
282
|
+
// Heuristic 4: Very long sequences (10+) → RULE_TOO_BROAD
|
|
283
|
+
if (sequence.length >= 10) {
|
|
284
|
+
return "RULE_TOO_BROAD";
|
|
285
|
+
}
|
|
286
|
+
// Heuristic 5: Short sequence with no resource management → RULE_TOO_BROAD
|
|
287
|
+
if (sequence.length <= 3 && !sequence.some(fn => /init|open|close|free|alloc|create|destroy/i.test(fn))) {
|
|
288
|
+
return "RULE_TOO_BROAD";
|
|
289
|
+
}
|
|
290
|
+
return "RULE_TOO_BROAD"; // Default
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Get all rules that should currently be suppressed.
|
|
294
|
+
*/
|
|
295
|
+
getSuppressedRules() {
|
|
296
|
+
return [...this.ruleConfidences.values()]
|
|
297
|
+
.filter(rc => rc.suppressed || rc.currentConfidence < rc.suppressionThreshold)
|
|
298
|
+
.sort((a, b) => a.currentConfidence - b.currentConfidence);
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Get the FP learning report — what have we learned?
|
|
302
|
+
*/
|
|
303
|
+
getFPLearningReport() {
|
|
304
|
+
const byReason = {};
|
|
305
|
+
const byRule = {};
|
|
306
|
+
for (const fp of this.fpRecords) {
|
|
307
|
+
byReason[fp.reason] = (byReason[fp.reason] || 0) + 1;
|
|
308
|
+
const ruleKey = `${fp.protocol}:${fp.triggeredRules[0]}`;
|
|
309
|
+
byRule[ruleKey] = (byRule[ruleKey] || 0) + 1;
|
|
310
|
+
}
|
|
311
|
+
const allConfidences = [...this.ruleConfidences.values()];
|
|
312
|
+
const avgConfidence = allConfidences.length > 0
|
|
313
|
+
? allConfidences.reduce((s, rc) => s + rc.currentConfidence, 0) / allConfidences.length
|
|
314
|
+
: 0;
|
|
315
|
+
const topFPRules = Object.entries(byRule)
|
|
316
|
+
.sort((a, b) => b[1] - a[1])
|
|
317
|
+
.slice(0, 10)
|
|
318
|
+
.map(([rule, fps]) => {
|
|
319
|
+
const rc = this.ruleConfidences.get(rule);
|
|
320
|
+
return { rule, fps, confidence: rc?.currentConfidence || 0 };
|
|
321
|
+
});
|
|
322
|
+
return {
|
|
323
|
+
totalFPs: this.fpRecords.length,
|
|
324
|
+
byReason,
|
|
325
|
+
byRule,
|
|
326
|
+
suppressedRules: this.getSuppressedRules().length,
|
|
327
|
+
avgConfidence,
|
|
328
|
+
topFPRules,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
// ═════════════════════════════════════════════════════════════
|
|
332
|
+
// Context Filters
|
|
333
|
+
// ═════════════════════════════════════════════════════════════
|
|
334
|
+
isContextFiltered(ruleName, context) {
|
|
335
|
+
if (!context)
|
|
336
|
+
return false;
|
|
337
|
+
const filter = this.contextFilters.get(ruleName);
|
|
338
|
+
if (!filter)
|
|
339
|
+
return false;
|
|
340
|
+
if (context.isTestCode && filter.has("test"))
|
|
341
|
+
return true;
|
|
342
|
+
if (context.isInitCode && filter.has("init"))
|
|
343
|
+
return true;
|
|
344
|
+
if (context.isInternal && filter.has("internal"))
|
|
345
|
+
return true;
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
addContextFilter(ruleName, filterType) {
|
|
349
|
+
if (!this.contextFilters.has(ruleName)) {
|
|
350
|
+
this.contextFilters.set(ruleName, new Set());
|
|
351
|
+
}
|
|
352
|
+
this.contextFilters.get(ruleName).add(filterType);
|
|
353
|
+
this.save();
|
|
354
|
+
}
|
|
355
|
+
// ═════════════════════════════════════════════════════════════
|
|
356
|
+
// Helpers
|
|
357
|
+
// ═════════════════════════════════════════════════════════════
|
|
358
|
+
determineAction(reason) {
|
|
359
|
+
switch (reason) {
|
|
360
|
+
case "CONTEXT_MISMATCH": return "context_filter_added";
|
|
361
|
+
case "NAMESPACE_LEAK": return "namespace_restricted";
|
|
362
|
+
case "RULE_TOO_BROAD":
|
|
363
|
+
case "INCOMPLETE_RULE": return "rule_adjusted";
|
|
364
|
+
case "DOMAIN_IRRELEVANT":
|
|
365
|
+
case "LEGACY_COMPAT": return "exception_added";
|
|
366
|
+
default: return "deferred";
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
// ═════════════════════════════════════════════════════════════
|
|
370
|
+
// Persistence
|
|
371
|
+
// ═════════════════════════════════════════════════════════════
|
|
372
|
+
load() {
|
|
373
|
+
const dir = path.resolve(process.cwd(), VI_DIR);
|
|
374
|
+
if (!fs.existsSync(dir))
|
|
375
|
+
return;
|
|
376
|
+
try {
|
|
377
|
+
const confFile = path.join(dir, "rule-confidences.json");
|
|
378
|
+
if (fs.existsSync(confFile)) {
|
|
379
|
+
const data = JSON.parse(fs.readFileSync(confFile, "utf-8"));
|
|
380
|
+
for (const [key, val] of Object.entries(data)) {
|
|
381
|
+
this.ruleConfidences.set(key, val);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
const fpFile = path.join(dir, "fp-records.json");
|
|
385
|
+
if (fs.existsSync(fpFile)) {
|
|
386
|
+
this.fpRecords = JSON.parse(fs.readFileSync(fpFile, "utf-8"));
|
|
387
|
+
}
|
|
388
|
+
const filterFile = path.join(dir, "context-filters.json");
|
|
389
|
+
if (fs.existsSync(filterFile)) {
|
|
390
|
+
const data = JSON.parse(fs.readFileSync(filterFile, "utf-8"));
|
|
391
|
+
for (const [key, val] of Object.entries(data)) {
|
|
392
|
+
this.contextFilters.set(key, new Set(val));
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
catch { /* start fresh */ }
|
|
397
|
+
}
|
|
398
|
+
save() {
|
|
399
|
+
const dir = path.resolve(process.cwd(), VI_DIR);
|
|
400
|
+
if (!fs.existsSync(dir))
|
|
401
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
402
|
+
const confObj = {};
|
|
403
|
+
for (const [key, val] of this.ruleConfidences) {
|
|
404
|
+
confObj[key] = val;
|
|
405
|
+
}
|
|
406
|
+
fs.writeFileSync(path.join(dir, "rule-confidences.json"), JSON.stringify(confObj, null, 2));
|
|
407
|
+
// Only save last 1000 FP records to avoid file bloat
|
|
408
|
+
const recent = this.fpRecords.slice(-1000);
|
|
409
|
+
fs.writeFileSync(path.join(dir, "fp-records.json"), JSON.stringify(recent, null, 2));
|
|
410
|
+
const filterObj = {};
|
|
411
|
+
for (const [key, val] of this.contextFilters) {
|
|
412
|
+
filterObj[key] = [...val];
|
|
413
|
+
}
|
|
414
|
+
fs.writeFileSync(path.join(dir, "context-filters.json"), JSON.stringify(filterObj, null, 2));
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
exports.VerificationIntelligence = VerificationIntelligence;
|
|
418
|
+
// ═══════════════════════════════════════════════════════════════
|
|
419
|
+
// Singleton
|
|
420
|
+
// ═══════════════════════════════════════════════════════════════
|
|
421
|
+
let _vi = null;
|
|
422
|
+
function getVerificationIntelligence() {
|
|
423
|
+
if (!_vi)
|
|
424
|
+
_vi = new VerificationIntelligence();
|
|
425
|
+
return _vi;
|
|
426
|
+
}
|
|
427
|
+
// ═══════════════════════════════════════════════════════════════
|
|
428
|
+
// Report Formatter
|
|
429
|
+
// ═══════════════════════════════════════════════════════════════
|
|
430
|
+
function formatVILearningReport() {
|
|
431
|
+
const vi = getVerificationIntelligence();
|
|
432
|
+
const report = vi.getFPLearningReport();
|
|
433
|
+
const suppressed = vi.getSuppressedRules();
|
|
434
|
+
const lines = [];
|
|
435
|
+
lines.push("");
|
|
436
|
+
lines.push("╔══════════════════════════════════════════════════════════════╗");
|
|
437
|
+
lines.push("║ Verification Intelligence — Learning Report ║");
|
|
438
|
+
lines.push("╠══════════════════════════════════════════════════════════════╣");
|
|
439
|
+
lines.push(`║ FP Records: ${String(report.totalFPs).padStart(5)} | Suppressed Rules: ${String(report.suppressedRules).padStart(3)} | Avg Confidence: ${(report.avgConfidence * 100).toFixed(0)}% ║`);
|
|
440
|
+
lines.push("╚══════════════════════════════════════════════════════════════╝");
|
|
441
|
+
lines.push("");
|
|
442
|
+
if (report.totalFPs === 0) {
|
|
443
|
+
lines.push(" No false positives recorded yet.");
|
|
444
|
+
lines.push(" The engine will learn as verification runs across repos.");
|
|
445
|
+
lines.push("");
|
|
446
|
+
return lines.join("\n");
|
|
447
|
+
}
|
|
448
|
+
// FP by reason
|
|
449
|
+
lines.push("── FP by Reason ──");
|
|
450
|
+
for (const [reason, count] of Object.entries(report.byReason).sort((a, b) => b[1] - a[1])) {
|
|
451
|
+
const label = exports.FP_REASON_LABELS[reason]?.label || reason;
|
|
452
|
+
const bar = "█".repeat(Math.min(30, count));
|
|
453
|
+
lines.push(` ${label.padEnd(25)} ${String(count).padStart(4)} ${bar}`);
|
|
454
|
+
}
|
|
455
|
+
lines.push("");
|
|
456
|
+
// Top noisy rules
|
|
457
|
+
if (report.topFPRules.length > 0) {
|
|
458
|
+
lines.push("── Top Noisy Rules ──");
|
|
459
|
+
for (const r of report.topFPRules.slice(0, 5)) {
|
|
460
|
+
const conf = (r.confidence * 100).toFixed(0);
|
|
461
|
+
const status = r.confidence < 0.3 ? "🔇 SUPPRESSED" : r.confidence < 0.6 ? "⚠️ LOW" : "✓ OK";
|
|
462
|
+
lines.push(` ${r.rule.padEnd(35)} ${String(r.fps).padStart(3)} FPs conf=${conf}% ${status}`);
|
|
463
|
+
}
|
|
464
|
+
lines.push("");
|
|
465
|
+
}
|
|
466
|
+
// Suppressed rules
|
|
467
|
+
if (suppressed.length > 0) {
|
|
468
|
+
lines.push("── Suppressed Rules ──");
|
|
469
|
+
for (const rc of suppressed) {
|
|
470
|
+
lines.push(` 🔇 ${rc.protocol}:${rc.ruleName} — ${rc.falsePositives} FPs, confidence ${(rc.currentConfidence * 100).toFixed(0)}%`);
|
|
471
|
+
}
|
|
472
|
+
lines.push("");
|
|
473
|
+
}
|
|
474
|
+
return lines.join("\n");
|
|
475
|
+
}
|