progmune-runtime 2.1.5 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +326 -59
- 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,386 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Progmune V3.3 — State Mining Engine
|
|
4
|
+
* ====================================
|
|
5
|
+
* 从代码中恢复业务状态机,而非函数调用序列。
|
|
6
|
+
*
|
|
7
|
+
* 核心理念:
|
|
8
|
+
* Policy 真正约束的是 State Transition(状态变化),不是 Function Order(函数顺序)。
|
|
9
|
+
* "Created → Paid → Shipped" 合法,"Created → Shipped" 非法。
|
|
10
|
+
* 这不是函数顺序问题,是状态机问题。
|
|
11
|
+
*
|
|
12
|
+
* 三层挖掘:
|
|
13
|
+
* L1: State Discovery — 从枚举、常量、类型定义中提取状态值
|
|
14
|
+
* L2: State Transition Recovery — 从行为图中恢复状态转换
|
|
15
|
+
* L3: State Machine Assembly — 将状态和转换组装成完整状态机
|
|
16
|
+
*
|
|
17
|
+
* 输入: Behavior Graph (V3.2) + 项目路径
|
|
18
|
+
* 输出: State Machines — 每个业务实体的完整生命周期
|
|
19
|
+
*/
|
|
20
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
23
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
24
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
25
|
+
}
|
|
26
|
+
Object.defineProperty(o, k2, desc);
|
|
27
|
+
}) : (function(o, m, k, k2) {
|
|
28
|
+
if (k2 === undefined) k2 = k;
|
|
29
|
+
o[k2] = m[k];
|
|
30
|
+
}));
|
|
31
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
32
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
33
|
+
}) : function(o, v) {
|
|
34
|
+
o["default"] = v;
|
|
35
|
+
});
|
|
36
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
37
|
+
var ownKeys = function(o) {
|
|
38
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
39
|
+
var ar = [];
|
|
40
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
41
|
+
return ar;
|
|
42
|
+
};
|
|
43
|
+
return ownKeys(o);
|
|
44
|
+
};
|
|
45
|
+
return function (mod) {
|
|
46
|
+
if (mod && mod.__esModule) return mod;
|
|
47
|
+
var result = {};
|
|
48
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
49
|
+
__setModuleDefault(result, mod);
|
|
50
|
+
return result;
|
|
51
|
+
};
|
|
52
|
+
})();
|
|
53
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
|
+
exports.mineStates = mineStates;
|
|
55
|
+
const ts_morph_1 = require("ts-morph");
|
|
56
|
+
const path = __importStar(require("path"));
|
|
57
|
+
const fs = __importStar(require("fs"));
|
|
58
|
+
// ══════════════════════════════════════════════
|
|
59
|
+
// L1: STATE DISCOVERY
|
|
60
|
+
// ══════════════════════════════════════════════
|
|
61
|
+
/**
|
|
62
|
+
* Extract all state-like definitions from the codebase.
|
|
63
|
+
* States come from:
|
|
64
|
+
* 1. TypeScript enums with Status/State/Stage/Type names
|
|
65
|
+
* 2. Drizzle mysqlEnum column definitions
|
|
66
|
+
* 3. String literal union types (type Status = "active" | "inactive")
|
|
67
|
+
*/
|
|
68
|
+
function discoverStates(projectPath) {
|
|
69
|
+
const states = [];
|
|
70
|
+
// ── Direct regex extraction from Drizzle/Prisma schema files ──
|
|
71
|
+
// ts-morph can fail parsing runtime imports. Regex is more robust.
|
|
72
|
+
for (const schemaDir of ["drizzle", "prisma", "db", "database"]) {
|
|
73
|
+
const schemaPath = path.join(projectPath, schemaDir);
|
|
74
|
+
if (!fs.existsSync(schemaPath))
|
|
75
|
+
continue;
|
|
76
|
+
for (const entry of fs.readdirSync(schemaPath, { recursive: true })) {
|
|
77
|
+
const fullPath = path.join(schemaPath, entry);
|
|
78
|
+
if (!fullPath.endsWith(".ts"))
|
|
79
|
+
continue;
|
|
80
|
+
const content = fs.readFileSync(fullPath, "utf-8");
|
|
81
|
+
const fileName = path.relative(projectPath, fullPath);
|
|
82
|
+
// Extract: mysqlTable("tableName", { ... status: mysqlEnum("status", ["a","b"]) ... })
|
|
83
|
+
// Pattern: mysqlTable("name", { ... colName: mysqlEnum("col", [...values]) ... })
|
|
84
|
+
const tableRegex = /mysqlTable\s*\(\s*["']([^"']+)["']\s*,\s*\{([^}]+(?:\{[^}]*\}[^}]*)*)\}\s*\)/gs;
|
|
85
|
+
let tableMatch;
|
|
86
|
+
while ((tableMatch = tableRegex.exec(content)) !== null) {
|
|
87
|
+
const tableName = tableMatch[1];
|
|
88
|
+
const columnsBlock = tableMatch[2];
|
|
89
|
+
// Extract: colName: mysqlEnum("col", ["val1", "val2", ...])
|
|
90
|
+
const enumRegex = /(\w+)\s*:\s*mysqlEnum\s*\(\s*["'][^"']*["']\s*,\s*\[([^\]]+)\]/g;
|
|
91
|
+
let enumMatch;
|
|
92
|
+
while ((enumMatch = enumRegex.exec(columnsBlock)) !== null) {
|
|
93
|
+
const colName = enumMatch[1];
|
|
94
|
+
const valuesStr = enumMatch[2];
|
|
95
|
+
const values = valuesStr.match(/["']([^"']+)["']/g)?.map(v => v.replace(/["']/g, "")) || [];
|
|
96
|
+
const isStateColumn = ["status", "state", "type", "stage", "phase", "role", "paymentMethod"].includes(colName);
|
|
97
|
+
if (isStateColumn && values.length >= 2) {
|
|
98
|
+
for (const val of values) {
|
|
99
|
+
states.push({
|
|
100
|
+
name: val,
|
|
101
|
+
entity: tableName,
|
|
102
|
+
source: "enum",
|
|
103
|
+
file: fileName,
|
|
104
|
+
lineNumber: 0,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// ── TypeScript enums from server source files ──
|
|
113
|
+
if (fs.existsSync(path.join(projectPath, "server"))) {
|
|
114
|
+
for (const entry of fs.readdirSync(path.join(projectPath, "server"), { recursive: true })) {
|
|
115
|
+
const fullPath = path.join(projectPath, "server", entry);
|
|
116
|
+
if (!fullPath.endsWith(".ts"))
|
|
117
|
+
continue;
|
|
118
|
+
const content = fs.readFileSync(fullPath, "utf-8");
|
|
119
|
+
const fileName = path.relative(projectPath, fullPath);
|
|
120
|
+
// Extract: enum OrderStatus { CREATED = "created", ... }
|
|
121
|
+
const enumRegex = /enum\s+(\w+)\s*\{([^}]+)\}/g;
|
|
122
|
+
let enumMatch;
|
|
123
|
+
while ((enumMatch = enumRegex.exec(content)) !== null) {
|
|
124
|
+
const enumName = enumMatch[1];
|
|
125
|
+
const body = enumMatch[2];
|
|
126
|
+
const isStateEnum = /status|state|stage|type|step|phase/i.test(enumName);
|
|
127
|
+
if (isStateEnum) {
|
|
128
|
+
const entityName = enumName.replace(/status|state|stage|type|step|phase/gi, "").trim() || enumName;
|
|
129
|
+
const members = body.match(/(\w+)\s*(?:=\s*["']?(\w+)["']?)?/g) || [];
|
|
130
|
+
for (const m of members) {
|
|
131
|
+
const name = m.split("=")[0].trim();
|
|
132
|
+
if (name && !name.startsWith("//")) {
|
|
133
|
+
states.push({ name, entity: entityName, source: "enum", file: fileName, lineNumber: 0 });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return states;
|
|
141
|
+
}
|
|
142
|
+
// ══════════════════════════════════════════════
|
|
143
|
+
// L2: STATE TRANSITION RECOVERY
|
|
144
|
+
// ══════════════════════════════════════════════
|
|
145
|
+
/**
|
|
146
|
+
* Map function names to state transitions using heuristics.
|
|
147
|
+
* e.g. "updateOrderStatus" + args containing "SHIPPED" → transition to SHIPPED
|
|
148
|
+
* e.g. "createOrder" → transition to CREATED
|
|
149
|
+
* e.g. "payOrder" → transition to PAID
|
|
150
|
+
*/
|
|
151
|
+
function recoverTransitions(projectPath, states, behaviorEdges) {
|
|
152
|
+
const project = new ts_morph_1.Project({
|
|
153
|
+
tsConfigFilePath: path.join(projectPath, "tsconfig.json"),
|
|
154
|
+
skipAddingFilesFromTsConfig: false,
|
|
155
|
+
});
|
|
156
|
+
// Also add drizzle/prisma schema files (often excluded from tsconfig includes)
|
|
157
|
+
for (const schemaDir of ["drizzle", "prisma", "db", "database"]) {
|
|
158
|
+
const schemaPath = path.join(projectPath, schemaDir);
|
|
159
|
+
if (fs.existsSync(schemaPath)) {
|
|
160
|
+
project.addSourceFilesAtPaths(path.join(schemaPath, "**/*.ts"));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const transitions = [];
|
|
164
|
+
const stateNamesByEntity = new Map();
|
|
165
|
+
for (const s of states) {
|
|
166
|
+
if (!stateNamesByEntity.has(s.entity)) {
|
|
167
|
+
stateNamesByEntity.set(s.entity, new Set());
|
|
168
|
+
}
|
|
169
|
+
stateNamesByEntity.get(s.entity).add(s.name);
|
|
170
|
+
}
|
|
171
|
+
for (const sourceFile of project.getSourceFiles()) {
|
|
172
|
+
const filePath = sourceFile.getFilePath().replace(projectPath + "/", "");
|
|
173
|
+
if (filePath.includes("node_modules") || filePath.includes(".test."))
|
|
174
|
+
continue;
|
|
175
|
+
// Walk all function calls
|
|
176
|
+
sourceFile.forEachDescendant((node) => {
|
|
177
|
+
if (!ts_morph_1.Node.isCallExpression(node))
|
|
178
|
+
return;
|
|
179
|
+
const callText = node.getExpression().getText().toLowerCase();
|
|
180
|
+
// Heuristic: if a function call contains a state name + verb,
|
|
181
|
+
// it's likely a state transition
|
|
182
|
+
for (const [entity, stateNames] of stateNamesByEntity) {
|
|
183
|
+
const entityLower = entity.toLowerCase();
|
|
184
|
+
// Check if this call is related to the entity
|
|
185
|
+
if (!callText.includes(entityLower) && !callText.includes("status") && !callText.includes("state")) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
// Look for state values in the function call or its arguments
|
|
189
|
+
for (const stateName of stateNames) {
|
|
190
|
+
const stateLower = stateName.toLowerCase();
|
|
191
|
+
const args = node.getArguments().map(a => a.getText().toLowerCase()).join(" ");
|
|
192
|
+
// Does this call set or transition to this state?
|
|
193
|
+
const isTransition = callText.includes(stateLower) ||
|
|
194
|
+
args.includes(stateLower) ||
|
|
195
|
+
args.includes(`"${stateLower}"`) ||
|
|
196
|
+
args.includes(`'${stateLower}'`);
|
|
197
|
+
if (isTransition) {
|
|
198
|
+
// Determine the transition direction:
|
|
199
|
+
// - "create" or "insert" → entering this state
|
|
200
|
+
// - "update" or "set" → transitioning TO this state (from unknown previous state)
|
|
201
|
+
// - "delete" or "remove" → leaving this state
|
|
202
|
+
const isCreation = callText.includes("create") || callText.includes("insert") || callText.includes("init");
|
|
203
|
+
const isMutation = callText.includes("update") || callText.includes("set") || callText.includes("change");
|
|
204
|
+
// Try to find the previous state from the call context
|
|
205
|
+
// Look at enclosing function for "if (status === X)" patterns
|
|
206
|
+
let fromState = "UNKNOWN";
|
|
207
|
+
const parent = node.getParent();
|
|
208
|
+
if (parent) {
|
|
209
|
+
const parentText = parent.getText().toLowerCase();
|
|
210
|
+
for (const prevState of stateNames) {
|
|
211
|
+
if (parentText.includes(`"${prevState.toLowerCase()}"`) ||
|
|
212
|
+
parentText.includes(`'${prevState.toLowerCase()}'`) ||
|
|
213
|
+
parentText.includes(`=== "${prevState.toLowerCase()}"`)) {
|
|
214
|
+
fromState = prevState;
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (isCreation) {
|
|
220
|
+
fromState = "INITIAL";
|
|
221
|
+
}
|
|
222
|
+
const existingTransition = transitions.find(t => t.from === fromState && t.to === stateName && t.entity === entity);
|
|
223
|
+
if (existingTransition) {
|
|
224
|
+
existingTransition.frequency++;
|
|
225
|
+
existingTransition.confidence = Math.min(100, existingTransition.confidence + 5);
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
transitions.push({
|
|
229
|
+
from: fromState,
|
|
230
|
+
to: stateName,
|
|
231
|
+
entity,
|
|
232
|
+
viaFunction: callText,
|
|
233
|
+
frequency: 1,
|
|
234
|
+
topologyConfirmed: isMutation || isCreation,
|
|
235
|
+
confidence: isCreation ? 70 : 40,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
return transitions;
|
|
244
|
+
}
|
|
245
|
+
// ══════════════════════════════════════════════
|
|
246
|
+
// L3: STATE MACHINE ASSEMBLY
|
|
247
|
+
// ══════════════════════════════════════════════
|
|
248
|
+
/**
|
|
249
|
+
* From states and transitions, assemble complete business state machines.
|
|
250
|
+
*/
|
|
251
|
+
function assembleMachines(states, transitions) {
|
|
252
|
+
const machines = [];
|
|
253
|
+
const entities = [...new Set(states.map(s => s.entity))];
|
|
254
|
+
for (const entity of entities) {
|
|
255
|
+
const entityStates = states.filter(s => s.entity === entity);
|
|
256
|
+
const entityTransitions = transitions.filter(t => t.entity === entity);
|
|
257
|
+
if (entityStates.length < 2)
|
|
258
|
+
continue;
|
|
259
|
+
// Sort states: INITIAL first, then by transition frequency, then terminal
|
|
260
|
+
const stateNames = [...new Set(entityStates.map(s => s.name))];
|
|
261
|
+
const initialState = entityTransitions.find(t => t.from === "INITIAL")?.to || stateNames[0];
|
|
262
|
+
// Terminal states: states that appear as "to" but not as "from"
|
|
263
|
+
const fromSet = new Set(entityTransitions.map(t => t.from));
|
|
264
|
+
const toSet = new Set(entityTransitions.map(t => t.to));
|
|
265
|
+
const terminalStates = stateNames.filter(s => toSet.has(s) && !fromSet.has(s));
|
|
266
|
+
// Valid transitions: the ones we've observed
|
|
267
|
+
const validTransitions = entityTransitions.map(t => `${t.from}→${t.to}`);
|
|
268
|
+
// Invalid transitions: all other possible pairs (for validation)
|
|
269
|
+
const invalidTransitions = [];
|
|
270
|
+
for (const from of stateNames) {
|
|
271
|
+
for (const to of stateNames) {
|
|
272
|
+
if (from === to)
|
|
273
|
+
continue;
|
|
274
|
+
const key = `${from}→${to}`;
|
|
275
|
+
if (!validTransitions.includes(key)) {
|
|
276
|
+
invalidTransitions.push(key);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
// Coverage: what % of possible transitions have we observed?
|
|
281
|
+
const totalPossible = stateNames.length * (stateNames.length - 1);
|
|
282
|
+
const coverage = totalPossible > 0 ? Math.round((validTransitions.length / totalPossible) * 100) : 0;
|
|
283
|
+
// Overall confidence: average of transition confidences
|
|
284
|
+
const avgConf = entityTransitions.length > 0
|
|
285
|
+
? Math.round(entityTransitions.reduce((s, t) => s + t.confidence, 0) / entityTransitions.length)
|
|
286
|
+
: 0;
|
|
287
|
+
machines.push({
|
|
288
|
+
entity,
|
|
289
|
+
states: stateNames,
|
|
290
|
+
initialState,
|
|
291
|
+
terminalStates,
|
|
292
|
+
transitions: entityTransitions,
|
|
293
|
+
validTransitions,
|
|
294
|
+
invalidTransitions: invalidTransitions.slice(0, 20), // Don't blow up
|
|
295
|
+
coverage,
|
|
296
|
+
confidence: avgConf,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
// Sort by confidence descending
|
|
300
|
+
machines.sort((a, b) => b.confidence - a.confidence);
|
|
301
|
+
return machines;
|
|
302
|
+
}
|
|
303
|
+
// ══════════════════════════════════════════════
|
|
304
|
+
// MAIN PIPELINE
|
|
305
|
+
// ══════════════════════════════════════════════
|
|
306
|
+
function mineStates(projectPath) {
|
|
307
|
+
console.log("🔬 Progmune State Mining Engine — V3.3");
|
|
308
|
+
console.log(" Project:", projectPath);
|
|
309
|
+
// L1: State Discovery
|
|
310
|
+
console.log("\nL1 · State Discovery...");
|
|
311
|
+
const states = discoverStates(projectPath);
|
|
312
|
+
const entityCount = new Set(states.map(s => s.entity)).size;
|
|
313
|
+
console.log(` ${states.length} states found across ${entityCount} entities`);
|
|
314
|
+
// Group by entity and show
|
|
315
|
+
const byEntity = new Map();
|
|
316
|
+
for (const s of states) {
|
|
317
|
+
if (!byEntity.has(s.entity))
|
|
318
|
+
byEntity.set(s.entity, []);
|
|
319
|
+
byEntity.get(s.entity).push(s);
|
|
320
|
+
}
|
|
321
|
+
for (const [entity, entityStates] of byEntity) {
|
|
322
|
+
const stateNames = entityStates.map(s => s.name);
|
|
323
|
+
console.log(` ${entity}: [${stateNames.join(", ")}]`);
|
|
324
|
+
}
|
|
325
|
+
// L2: State Transition Recovery
|
|
326
|
+
console.log("\nL2 · Transition Recovery...");
|
|
327
|
+
const transitions = recoverTransitions(projectPath, states);
|
|
328
|
+
const confTransitions = transitions.filter(t => t.topologyConfirmed);
|
|
329
|
+
console.log(` ${transitions.length} transitions (${confTransitions.length} topology-confirmed)`);
|
|
330
|
+
// L3: State Machine Assembly
|
|
331
|
+
console.log("\nL3 · State Machine Assembly...");
|
|
332
|
+
const machines = assembleMachines(states, transitions);
|
|
333
|
+
console.log(` ${machines.length} state machines assembled`);
|
|
334
|
+
for (const m of machines) {
|
|
335
|
+
console.log(`\n 🏗️ ${m.entity} State Machine [confidence: ${m.confidence}%]`);
|
|
336
|
+
console.log(` States: ${m.states.join(" → ")}`);
|
|
337
|
+
console.log(` Initial: ${m.initialState} | Terminal: ${m.terminalStates.join(", ") || "(none)"}`);
|
|
338
|
+
console.log(` Coverage: ${m.coverage}% (${m.validTransitions.length} observed / ${m.validTransitions.length + m.invalidTransitions.length} possible)`);
|
|
339
|
+
if (m.transitions.length > 0) {
|
|
340
|
+
console.log(` Transitions:`);
|
|
341
|
+
for (const t of m.transitions.slice(0, 5)) {
|
|
342
|
+
console.log(` ${t.from.padEnd(15)} → ${t.to.padEnd(15)} via ${t.viaFunction} [${t.confidence}%]`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
// Show violations this machine would catch
|
|
346
|
+
if (m.invalidTransitions.length > 0) {
|
|
347
|
+
console.log(` Would catch these violations:`);
|
|
348
|
+
for (const inv of m.invalidTransitions.slice(0, 3)) {
|
|
349
|
+
console.log(` ❌ ${inv} — would be blocked`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
const report = {
|
|
354
|
+
projectPath,
|
|
355
|
+
timestamp: new Date().toISOString(),
|
|
356
|
+
states,
|
|
357
|
+
transitions,
|
|
358
|
+
machines,
|
|
359
|
+
summary: {
|
|
360
|
+
statesFound: states.length,
|
|
361
|
+
transitionsFound: transitions.length,
|
|
362
|
+
machinesFound: machines.length,
|
|
363
|
+
completeMachines: machines.filter(m => m.coverage >= 50).length,
|
|
364
|
+
},
|
|
365
|
+
};
|
|
366
|
+
return report;
|
|
367
|
+
}
|
|
368
|
+
// ══════════════════════════════════════════════
|
|
369
|
+
// CLI
|
|
370
|
+
// ══════════════════════════════════════════════
|
|
371
|
+
if (require.main === module) {
|
|
372
|
+
const targetProject = process.argv[2];
|
|
373
|
+
if (!targetProject) {
|
|
374
|
+
console.error("Usage: npx ts-node src/state-miner.ts <project-path>");
|
|
375
|
+
process.exit(1);
|
|
376
|
+
}
|
|
377
|
+
const report = mineStates(targetProject);
|
|
378
|
+
const outputPath = path.join(targetProject, ".progmune_state_machines.json");
|
|
379
|
+
fs.writeFileSync(outputPath, JSON.stringify(report, null, 2));
|
|
380
|
+
console.log(`\n✅ State machines saved to: ${outputPath}`);
|
|
381
|
+
console.log("\n═══ State Mining Summary ═══");
|
|
382
|
+
console.log(` States: ${report.summary.statesFound}`);
|
|
383
|
+
console.log(` Transitions: ${report.summary.transitionsFound}`);
|
|
384
|
+
console.log(` Machines: ${report.summary.machinesFound} (${report.summary.completeMachines} complete)`);
|
|
385
|
+
console.log();
|
|
386
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* P6.8: State Name Semantic Inference
|
|
4
|
+
*
|
|
5
|
+
* Maps synthesized generic state names (C0_S1, C1_S2) to
|
|
6
|
+
* hand-written semantic names (FILE_OPEN, DB_CONNECTED, etc.)
|
|
7
|
+
* using action signature matching.
|
|
8
|
+
*
|
|
9
|
+
* Approach:
|
|
10
|
+
* 1. Build a "dictionary" of hand-written states with their
|
|
11
|
+
* action signatures (inbound + outbound function sets).
|
|
12
|
+
* 2. For each synthesized state, compute Jaccard similarity
|
|
13
|
+
* against all hand-written state signatures.
|
|
14
|
+
* 3. Map to the best-matching semantic name.
|
|
15
|
+
* 4. Replace generic names in synthesized rules.
|
|
16
|
+
*
|
|
17
|
+
* Target: bootstrap function overlap 12% → 60%+
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.alignSynthesizedProtocols = alignSynthesizedProtocols;
|
|
21
|
+
exports.runStateAlignment = runStateAlignment;
|
|
22
|
+
exports.printAlignmentReport = printAlignmentReport;
|
|
23
|
+
const protocol_coverage_1 = require("./protocol-coverage");
|
|
24
|
+
const auto_protocol_synthesizer_1 = require("./auto-protocol-synthesizer");
|
|
25
|
+
const bootstrap_validation_1 = require("./bootstrap-validation");
|
|
26
|
+
/** Build state signatures from protocol rules. */
|
|
27
|
+
function buildStateSignatures(rules) {
|
|
28
|
+
const sigs = [];
|
|
29
|
+
const seen = new Set();
|
|
30
|
+
for (const [fn, rule] of rules) {
|
|
31
|
+
// For each post_state, record fn as inbound
|
|
32
|
+
for (const ps of rule.post_states) {
|
|
33
|
+
if (ps.length === 0 || seen.has(ps))
|
|
34
|
+
continue;
|
|
35
|
+
seen.add(ps);
|
|
36
|
+
const inbound = new Set();
|
|
37
|
+
const outbound = new Set();
|
|
38
|
+
// Find all functions that produce this state (inbound)
|
|
39
|
+
for (const [otherFn, otherRule] of rules) {
|
|
40
|
+
if (otherRule.post_states.includes(ps))
|
|
41
|
+
inbound.add(otherFn);
|
|
42
|
+
if (otherRule.pre_states.includes(ps))
|
|
43
|
+
outbound.add(otherFn);
|
|
44
|
+
}
|
|
45
|
+
if (inbound.size > 0 || outbound.size > 0) {
|
|
46
|
+
sigs.push({ name: ps, inbound, outbound });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Also add pre_states as signatures
|
|
50
|
+
for (const ps of rule.pre_states) {
|
|
51
|
+
if (ps.length === 0 || seen.has(ps))
|
|
52
|
+
continue;
|
|
53
|
+
seen.add(ps);
|
|
54
|
+
const inbound = new Set();
|
|
55
|
+
const outbound = new Set();
|
|
56
|
+
for (const [otherFn, otherRule] of rules) {
|
|
57
|
+
if (otherRule.post_states.includes(ps))
|
|
58
|
+
inbound.add(otherFn);
|
|
59
|
+
if (otherRule.pre_states.includes(ps))
|
|
60
|
+
outbound.add(otherFn);
|
|
61
|
+
}
|
|
62
|
+
if (inbound.size > 0 || outbound.size > 0) {
|
|
63
|
+
sigs.push({ name: ps, inbound, outbound });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return sigs;
|
|
68
|
+
}
|
|
69
|
+
// ═══════════════════════════════════════════════════════════════
|
|
70
|
+
// Jaccard Similarity
|
|
71
|
+
// ═══════════════════════════════════════════════════════════════
|
|
72
|
+
function jaccard(a, b) {
|
|
73
|
+
const intersection = [...a].filter(x => b.has(x)).length;
|
|
74
|
+
const union = new Set([...a, ...b]).size;
|
|
75
|
+
return union > 0 ? intersection / union : 0;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Find the best semantic name for a synthetic state.
|
|
79
|
+
*
|
|
80
|
+
* Computes Jaccard similarity between the synthetic state's
|
|
81
|
+
* action signature and each hand-written state's signature.
|
|
82
|
+
* Returns the name with the highest combined similarity.
|
|
83
|
+
*/
|
|
84
|
+
function inferStateName(syntheticState, synthRules, handSignatures) {
|
|
85
|
+
// Build action signature for this synthetic state
|
|
86
|
+
const inbound = new Set();
|
|
87
|
+
const outbound = new Set();
|
|
88
|
+
for (const sr of synthRules) {
|
|
89
|
+
if (sr.post_states.includes(syntheticState))
|
|
90
|
+
inbound.add(sr.function);
|
|
91
|
+
if (sr.pre_states.includes(syntheticState))
|
|
92
|
+
outbound.add(sr.function);
|
|
93
|
+
}
|
|
94
|
+
// Fallback: try to derive name from function names
|
|
95
|
+
if (inbound.size === 0 && outbound.size === 0) {
|
|
96
|
+
return syntheticState; // keep original
|
|
97
|
+
}
|
|
98
|
+
let bestName = syntheticState;
|
|
99
|
+
let bestScore = 0;
|
|
100
|
+
for (const hs of handSignatures) {
|
|
101
|
+
const inSim = jaccard(inbound, hs.inbound);
|
|
102
|
+
const outSim = jaccard(outbound, hs.outbound);
|
|
103
|
+
const score = inSim * 0.5 + outSim * 0.5;
|
|
104
|
+
if (score > bestScore && score > 0.1) {
|
|
105
|
+
bestScore = score;
|
|
106
|
+
bestName = hs.name;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// If no good match found, try name-based inference
|
|
110
|
+
if (bestName === syntheticState) {
|
|
111
|
+
const allActions = [...inbound, ...outbound];
|
|
112
|
+
const prefixes = allActions.map(fn => {
|
|
113
|
+
const parts = fn.split("_");
|
|
114
|
+
// Extract domain prefix: "DB_Open" → "DB", "open_file" → "FILE"
|
|
115
|
+
if (parts.length > 1)
|
|
116
|
+
return parts[0].toUpperCase();
|
|
117
|
+
return "";
|
|
118
|
+
}).filter(p => p.length > 0);
|
|
119
|
+
if (prefixes.length > 0) {
|
|
120
|
+
// Most common prefix becomes the state name prefix
|
|
121
|
+
const prefixCounts = new Map();
|
|
122
|
+
for (const p of prefixes) {
|
|
123
|
+
prefixCounts.set(p, (prefixCounts.get(p) || 0) + 1);
|
|
124
|
+
}
|
|
125
|
+
const topPrefix = [...prefixCounts.entries()].sort((a, b) => b[1] - a[1])[0][0];
|
|
126
|
+
bestName = `${topPrefix}_${syntheticState.replace(/C\d+_/, "")}`;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return bestName;
|
|
130
|
+
}
|
|
131
|
+
// ═══════════════════════════════════════════════════════════════
|
|
132
|
+
// Protocol Alignment
|
|
133
|
+
// ═══════════════════════════════════════════════════════════════
|
|
134
|
+
/**
|
|
135
|
+
* Align synthesized protocol rules with hand-written state names.
|
|
136
|
+
*
|
|
137
|
+
* Replaces generic state names (C0_S1) with semantically meaningful
|
|
138
|
+
* names inferred from action context.
|
|
139
|
+
*/
|
|
140
|
+
function alignSynthesizedProtocols(synthesized, handRules) {
|
|
141
|
+
const handSigs = buildStateSignatures(handRules);
|
|
142
|
+
return synthesized.map(sp => {
|
|
143
|
+
const allSynthRules = sp.rules;
|
|
144
|
+
const stateMap = new Map();
|
|
145
|
+
// Map each synthetic state to its best semantic name
|
|
146
|
+
const allStates = new Set();
|
|
147
|
+
for (const sr of allSynthRules) {
|
|
148
|
+
for (const s of sr.pre_states)
|
|
149
|
+
if (s !== "INIT")
|
|
150
|
+
allStates.add(s);
|
|
151
|
+
for (const s of sr.post_states)
|
|
152
|
+
if (s !== "INIT")
|
|
153
|
+
allStates.add(s);
|
|
154
|
+
}
|
|
155
|
+
for (const s of allStates) {
|
|
156
|
+
stateMap.set(s, inferStateName(s, allSynthRules, handSigs));
|
|
157
|
+
}
|
|
158
|
+
// Replace state names in rules
|
|
159
|
+
const alignedRules = allSynthRules.map(sr => ({
|
|
160
|
+
...sr,
|
|
161
|
+
pre_states: sr.pre_states.map(s => s === "INIT" ? "INIT" : (stateMap.get(s) || s)),
|
|
162
|
+
post_states: sr.post_states.map(s => (stateMap.get(s) || s)),
|
|
163
|
+
invalidate: sr.invalidate?.map(s => (stateMap.get(s) || s)),
|
|
164
|
+
}));
|
|
165
|
+
return { ...sp, rules: alignedRules };
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Run the full state name alignment pipeline and measure bootstrap improvement.
|
|
170
|
+
*/
|
|
171
|
+
async function runStateAlignment() {
|
|
172
|
+
// Baseline
|
|
173
|
+
const baseline = await (0, bootstrap_validation_1.runBootstrapValidation)();
|
|
174
|
+
const beforeOverlap = baseline.functionOverlap;
|
|
175
|
+
// Get synthesized protocols
|
|
176
|
+
const synthesized = (0, auto_protocol_synthesizer_1.synthesizeAllKnownProtocols)();
|
|
177
|
+
// Get hand-written rules
|
|
178
|
+
const defs = (0, protocol_coverage_1.loadDefaultProtocolDefinitions)();
|
|
179
|
+
const handRules = new Map();
|
|
180
|
+
for (const p of defs)
|
|
181
|
+
for (const [fn, rule] of p.rules)
|
|
182
|
+
handRules.set(fn, rule);
|
|
183
|
+
// Align
|
|
184
|
+
const aligned = alignSynthesizedProtocols(synthesized, handRules);
|
|
185
|
+
// Count state alignments
|
|
186
|
+
let statesAligned = 0;
|
|
187
|
+
for (let i = 0; i < synthesized.length; i++) {
|
|
188
|
+
const orig = synthesized[i];
|
|
189
|
+
const algn = aligned[i];
|
|
190
|
+
for (let j = 0; j < orig.rules.length; j++) {
|
|
191
|
+
if (orig.rules[j].post_states[0] !== algn.rules[j].post_states[0]) {
|
|
192
|
+
statesAligned++;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// Re-run bootstrap (uses the aligned rules via synthesizeAllKnownProtocols)
|
|
197
|
+
const after = await (0, bootstrap_validation_1.runBootstrapValidation)();
|
|
198
|
+
const afterOverlap = after.functionOverlap;
|
|
199
|
+
return {
|
|
200
|
+
beforeOverlap,
|
|
201
|
+
afterOverlap,
|
|
202
|
+
improvement: afterOverlap - beforeOverlap,
|
|
203
|
+
statesAligned,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
function printAlignmentReport(report) {
|
|
207
|
+
console.log("\n─── P6.8 State Name Alignment ───");
|
|
208
|
+
console.log(` States Aligned: ${report.statesAligned}`);
|
|
209
|
+
console.log(` Before Overlap: ${(report.beforeOverlap * 100).toFixed(0)}%`);
|
|
210
|
+
console.log(` After Overlap: ${(report.afterOverlap * 100).toFixed(0)}%`);
|
|
211
|
+
console.log(` Improvement: ${(report.improvement > 0 ? "+" : "")}${(report.improvement * 100).toFixed(0)}%`);
|
|
212
|
+
console.log();
|
|
213
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* P6.8: State Name Inference Tests
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const vitest_1 = require("vitest");
|
|
7
|
+
const state_name_inference_1 = require("./state-name-inference");
|
|
8
|
+
const auto_protocol_synthesizer_1 = require("./auto-protocol-synthesizer");
|
|
9
|
+
const protocol_coverage_1 = require("./protocol-coverage");
|
|
10
|
+
(0, vitest_1.describe)("P6.8 State Name Inference", () => {
|
|
11
|
+
(0, vitest_1.it)("synthesizer generates semantic state names (integrated pipeline)", () => {
|
|
12
|
+
// Use the integrated synthesizer which now calls inferStateName internally
|
|
13
|
+
const protocols = (0, auto_protocol_synthesizer_1.synthesizeAllKnownProtocols)();
|
|
14
|
+
(0, vitest_1.expect)(protocols.length).toBeGreaterThan(0);
|
|
15
|
+
for (const sp of protocols) {
|
|
16
|
+
for (const sr of sp.rules) {
|
|
17
|
+
// Post states should NOT be generic C0_S1 format
|
|
18
|
+
for (const ps of sr.post_states) {
|
|
19
|
+
if (ps !== "INIT" && !ps.includes("_DONE")) {
|
|
20
|
+
// Semantic names come from inferStateName: FILE_OPEN, DB_CONNECTED, etc.
|
|
21
|
+
// or name-derived: DB_S1 (when no hand-written match found)
|
|
22
|
+
(0, vitest_1.expect)(ps).not.toMatch(/^C\d+_S\d+$/);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// Last rule's invalidate should contain semantic state names
|
|
26
|
+
if (sr.invalidate && sr.invalidate.length > 1) {
|
|
27
|
+
const hasSemantic = sr.invalidate.some(s => s.includes("_OPEN") || s.includes("_CLOSED") || s.includes("_CONNECTED") ||
|
|
28
|
+
s.includes("_VERIFIED") || s.includes("_ISSUED") || s.includes("_CREATED") ||
|
|
29
|
+
s.includes("_PRODUCED") || s.includes("_COMPLETED") || s.includes("_READY"));
|
|
30
|
+
(0, vitest_1.expect)(hasSemantic).toBe(true);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
(0, vitest_1.it)("aligned states use semantically meaningful names", () => {
|
|
36
|
+
const dbSynth = {
|
|
37
|
+
clusterId: "DB",
|
|
38
|
+
prototype: ["open_file", "write_file", "close_file"],
|
|
39
|
+
rules: [
|
|
40
|
+
{ function: "open_file", pre_states: ["INIT"], post_states: ["DB_S1"] },
|
|
41
|
+
{ function: "write_file", pre_states: ["DB_S1"], post_states: ["DB_S2"] },
|
|
42
|
+
{ function: "close_file", pre_states: ["DB_S2"], post_states: ["DB_DONE"], invalidate: ["DB_S1", "DB_S2"] },
|
|
43
|
+
],
|
|
44
|
+
stateCount: 4,
|
|
45
|
+
inferredPattern: "RESOURCE_ACQUIRE",
|
|
46
|
+
confidence: 1.0,
|
|
47
|
+
};
|
|
48
|
+
const defs = (0, protocol_coverage_1.loadDefaultProtocolDefinitions)();
|
|
49
|
+
const handRules = new Map();
|
|
50
|
+
for (const p of defs)
|
|
51
|
+
for (const [fn, rule] of p.rules)
|
|
52
|
+
handRules.set(fn, rule);
|
|
53
|
+
const aligned = (0, state_name_inference_1.alignSynthesizedProtocols)([dbSynth], handRules);
|
|
54
|
+
const rules = aligned[0].rules;
|
|
55
|
+
// open_file's post_state → FILE_OPEN (matches hand-written open_file rule)
|
|
56
|
+
(0, vitest_1.expect)(rules[0].post_states[0]).toBe("FILE_OPEN");
|
|
57
|
+
// close_file: should invalidate FILE_OPEN (matches hand-written close_file invalidation)
|
|
58
|
+
const closeRule = rules[2];
|
|
59
|
+
(0, vitest_1.expect)(closeRule.invalidate).toBeDefined();
|
|
60
|
+
(0, vitest_1.expect)(closeRule.invalidate.some(s => s.includes("FILE_OPEN") || s.includes("FILE_CLOSED"))).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
(0, vitest_1.it)("full alignment pipeline improves bootstrap overlap", async () => {
|
|
63
|
+
const report = await (0, state_name_inference_1.runStateAlignment)();
|
|
64
|
+
(0, vitest_1.expect)(report.statesAligned).toBeGreaterThan(0);
|
|
65
|
+
// Alignment should maintain or improve function overlap
|
|
66
|
+
(0, vitest_1.expect)(report.afterOverlap).toBeGreaterThanOrEqual(report.beforeOverlap);
|
|
67
|
+
(0, state_name_inference_1.printAlignmentReport)(report);
|
|
68
|
+
}, 30000);
|
|
69
|
+
});
|