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,388 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* P7.5: Unified Decision Engine — the "operating system kernel" of Progmune.
|
|
4
|
+
*
|
|
5
|
+
* All decisions in the system — verification, promotion, repair, policy —
|
|
6
|
+
* are driven by ONE engine with ONE consistent model.
|
|
7
|
+
*
|
|
8
|
+
* The three core concepts:
|
|
9
|
+
* 1. Verification Asset — the data (knowledge, rules, benchmarks, protocols)
|
|
10
|
+
* 2. Promotion Pipeline — the lifecycle (evidence → candidate → ... → stable)
|
|
11
|
+
* 3. Decision Engine — the kernel (this file)
|
|
12
|
+
*
|
|
13
|
+
* Everything else is just an Asset Type.
|
|
14
|
+
*
|
|
15
|
+
* Decision model:
|
|
16
|
+
* Input: Asset + Context → Decision { action, confidence, reason, provenance }
|
|
17
|
+
* Output: BLOCK/WARN/ALLOW | promote/demote/hold | fix/retry/rollback | accept/reject
|
|
18
|
+
*
|
|
19
|
+
* Importance is COMPUTED, not configured:
|
|
20
|
+
* Importance = Impact × Universality × Criticality
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.DecisionEngine = void 0;
|
|
24
|
+
exports.computeImportance = computeImportance;
|
|
25
|
+
exports.classifyCodeContext = classifyCodeContext;
|
|
26
|
+
exports.getDecisionEngine = getDecisionEngine;
|
|
27
|
+
// ═══════════════════════════════════════════════════════════════
|
|
28
|
+
// Importance Computation
|
|
29
|
+
// ═══════════════════════════════════════════════════════════════
|
|
30
|
+
/**
|
|
31
|
+
* Impact: how severe are the consequences of getting this wrong?
|
|
32
|
+
*
|
|
33
|
+
* 1.0 = security/critical (TLS Finished, password verify)
|
|
34
|
+
* 0.7 = resource management (file close, memory free)
|
|
35
|
+
* 0.4 = correctness (data validation, type checking)
|
|
36
|
+
* 0.1 = informational (logging, formatting)
|
|
37
|
+
*/
|
|
38
|
+
function computeImpact(asset) {
|
|
39
|
+
const domain = asset.domain.toLowerCase();
|
|
40
|
+
const name = asset.name.toLowerCase();
|
|
41
|
+
// Security-critical
|
|
42
|
+
if (domain === "tls" || domain === "ssl" || domain === "auth" ||
|
|
43
|
+
/tls|ssl|auth|encrypt|decrypt|verify|cert|key|token|password/i.test(name)) {
|
|
44
|
+
return 1.0;
|
|
45
|
+
}
|
|
46
|
+
// Resource management
|
|
47
|
+
if (/close|free|destroy|release|cleanup|disconnect|logout/i.test(name)) {
|
|
48
|
+
return 0.7;
|
|
49
|
+
}
|
|
50
|
+
// Correctness
|
|
51
|
+
if (/validate|check|assert|verify|compare|match/i.test(name)) {
|
|
52
|
+
return 0.4;
|
|
53
|
+
}
|
|
54
|
+
// Informational
|
|
55
|
+
return 0.1;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Universality: how broadly does this apply across codebases?
|
|
59
|
+
*
|
|
60
|
+
* 1.0 = universal (memory management, error handling)
|
|
61
|
+
* 0.7 = common (HTTP, file I/O, auth)
|
|
62
|
+
* 0.4 = domain-specific (TLS, SSH, DB protocols)
|
|
63
|
+
* 0.1 = repo-specific (custom internal patterns)
|
|
64
|
+
*/
|
|
65
|
+
function computeUniversality(asset) {
|
|
66
|
+
const crossRepo = asset.evidence.crossRepoCount;
|
|
67
|
+
if (crossRepo >= 5)
|
|
68
|
+
return 1.0;
|
|
69
|
+
if (crossRepo >= 3)
|
|
70
|
+
return 0.7;
|
|
71
|
+
if (crossRepo >= 2)
|
|
72
|
+
return 0.4;
|
|
73
|
+
return 0.1;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Criticality: how essential is this to correct program behavior?
|
|
77
|
+
*
|
|
78
|
+
* 1.0 = program fails without this (init, close, free)
|
|
79
|
+
* 0.7 = security/privacy risk (auth, encrypt)
|
|
80
|
+
* 0.4 = correctness risk (validate, check)
|
|
81
|
+
* 0.1 = quality risk (style, format)
|
|
82
|
+
*/
|
|
83
|
+
function computeCriticality(asset) {
|
|
84
|
+
const name = asset.name.toLowerCase();
|
|
85
|
+
// Program fails without this
|
|
86
|
+
if (/^(init_|create_|open_|close_|free_|destroy_)/i.test(name) ||
|
|
87
|
+
/_(init|cleanup|setup|teardown)$/i.test(name)) {
|
|
88
|
+
return 1.0;
|
|
89
|
+
}
|
|
90
|
+
// Security risk
|
|
91
|
+
if (/auth|encrypt|decrypt|token|password|verify|ssl|tls/i.test(name)) {
|
|
92
|
+
return 0.7;
|
|
93
|
+
}
|
|
94
|
+
// Correctness risk
|
|
95
|
+
if (/validate|check|assert|enforce|policy/i.test(name)) {
|
|
96
|
+
return 0.4;
|
|
97
|
+
}
|
|
98
|
+
return 0.1;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Compute Importance from Impact × Universality × Criticality.
|
|
102
|
+
* This is NOT configured — it's derived from evidence and semantics.
|
|
103
|
+
*/
|
|
104
|
+
function computeImportance(asset) {
|
|
105
|
+
const impact = computeImpact(asset);
|
|
106
|
+
const universality = computeUniversality(asset);
|
|
107
|
+
const criticality = computeCriticality(asset);
|
|
108
|
+
// Weighted product: Impact is most important, Universality is secondary
|
|
109
|
+
return Math.round((impact * 0.4 + universality * 0.25 + criticality * 0.35) * 100);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Sprint 14: Classify code context from function names and patterns.
|
|
113
|
+
*
|
|
114
|
+
* Production code gets strict thresholds.
|
|
115
|
+
* Test/example/benchmark code gets relaxed thresholds.
|
|
116
|
+
* Enterprises only care about Production FP.
|
|
117
|
+
*/
|
|
118
|
+
function classifyCodeContext(functions) {
|
|
119
|
+
const all = functions.join(" ").toLowerCase();
|
|
120
|
+
// Test patterns
|
|
121
|
+
if (/test|_test|testing|mock|stub|fake|assert|expect/i.test(all))
|
|
122
|
+
return "test";
|
|
123
|
+
// Example patterns
|
|
124
|
+
if (/example|demo|sample|tutorial|showcase/i.test(all))
|
|
125
|
+
return "example";
|
|
126
|
+
// Benchmark patterns
|
|
127
|
+
if (/bench|benchmark|perf|performance|stress|soak|load/i.test(all))
|
|
128
|
+
return "benchmark";
|
|
129
|
+
// Init/setup patterns
|
|
130
|
+
if (/^(init_|setup|config|bootstrap|startup)/i.test(all))
|
|
131
|
+
return "init";
|
|
132
|
+
return "production";
|
|
133
|
+
}
|
|
134
|
+
/** Sprint 14: Per-context confidence thresholds. */
|
|
135
|
+
const CONTEXT_THRESHOLDS = {
|
|
136
|
+
production: { block: 0.80, warn: 0.60, allow: 0.40 },
|
|
137
|
+
test: { block: 0.95, warn: 0.85, allow: 0.70 },
|
|
138
|
+
example: { block: 0.95, warn: 0.85, allow: 0.70 },
|
|
139
|
+
benchmark: { block: 0.90, warn: 0.80, allow: 0.60 },
|
|
140
|
+
init: { block: 0.85, warn: 0.70, allow: 0.50 },
|
|
141
|
+
unknown: { block: 0.80, warn: 0.60, allow: 0.40 },
|
|
142
|
+
};
|
|
143
|
+
class DecisionEngine {
|
|
144
|
+
constructor() {
|
|
145
|
+
this.history = [];
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Make a verification decision: should we alert?
|
|
149
|
+
*/
|
|
150
|
+
decideVerification(asset, context = {}) {
|
|
151
|
+
const importance = computeImportance(asset);
|
|
152
|
+
const fpRate = (context.recentFPs || 0) / Math.max(1, (context.recentFPs || 0) + (context.recentTPs || 0));
|
|
153
|
+
// Confidence = asset confidence × (1 - FP rate) × environment factor
|
|
154
|
+
let envFactor = 1.0;
|
|
155
|
+
if (context.environment === "production")
|
|
156
|
+
envFactor = 0.7; // More conservative in prod
|
|
157
|
+
if (context.environment === "test")
|
|
158
|
+
envFactor = 1.0;
|
|
159
|
+
const confidence = asset.confidence * (1 - fpRate) * envFactor;
|
|
160
|
+
// Sprint 14: Context-aware thresholds
|
|
161
|
+
const ctx = context.codeContext || "production";
|
|
162
|
+
const thresholds = CONTEXT_THRESHOLDS[ctx];
|
|
163
|
+
let action;
|
|
164
|
+
if (confidence >= thresholds.block)
|
|
165
|
+
action = "BLOCK";
|
|
166
|
+
else if (confidence >= thresholds.warn)
|
|
167
|
+
action = "WARN";
|
|
168
|
+
else if (confidence >= thresholds.allow)
|
|
169
|
+
action = "ALLOW";
|
|
170
|
+
else
|
|
171
|
+
action = "SUPPRESS";
|
|
172
|
+
// Importance override: high-importance assets alert even at lower confidence
|
|
173
|
+
if (importance >= 70 && action === "ALLOW")
|
|
174
|
+
action = "WARN";
|
|
175
|
+
if (importance >= 85 && action === "WARN")
|
|
176
|
+
action = "BLOCK";
|
|
177
|
+
return this.recordDecision({
|
|
178
|
+
kind: "verification",
|
|
179
|
+
action,
|
|
180
|
+
confidence,
|
|
181
|
+
reason: action === "BLOCK"
|
|
182
|
+
? `High-confidence violation (${(confidence * 100).toFixed(0)}%, importance ${importance})`
|
|
183
|
+
: action === "SUPPRESS"
|
|
184
|
+
? `Low confidence (${(confidence * 100).toFixed(0)}%), ${context.recentFPs || 0} recent FPs`
|
|
185
|
+
: `Moderate confidence (${(confidence * 100).toFixed(0)}%) — ${action}`,
|
|
186
|
+
provenance: {
|
|
187
|
+
assets: [asset.id],
|
|
188
|
+
evidence: [
|
|
189
|
+
...asset.evidence.rfcRefs.map(r => `RFC ${r}`),
|
|
190
|
+
...asset.evidence.repos,
|
|
191
|
+
`importance=${importance}`,
|
|
192
|
+
],
|
|
193
|
+
chain: [],
|
|
194
|
+
decider: "system",
|
|
195
|
+
timestamp: new Date().toISOString(),
|
|
196
|
+
},
|
|
197
|
+
recommendations: action === "SUPPRESS"
|
|
198
|
+
? ["Add RFC reference to increase confidence", "Validate across more repos"]
|
|
199
|
+
: action === "WARN"
|
|
200
|
+
? ["Review manually before blocking", "Check deployment observations"]
|
|
201
|
+
: [],
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Make a promotion decision: should this asset move to the next stage?
|
|
206
|
+
*/
|
|
207
|
+
decidePromotion(asset, context = {}) {
|
|
208
|
+
const importance = computeImportance(asset);
|
|
209
|
+
const hasDeployment = (context.deploymentObservations || 0) > 0;
|
|
210
|
+
// Promotion confidence depends on:
|
|
211
|
+
// 1. Asset evidence strength (cross-repo, RFC, sequence count)
|
|
212
|
+
// 2. Importance (high-importance assets are promoted faster)
|
|
213
|
+
// 3. Deployment observations (real-world validation)
|
|
214
|
+
let confidence = asset.confidence;
|
|
215
|
+
// Deployment bonus: real-world observation significantly boosts confidence
|
|
216
|
+
if (hasDeployment)
|
|
217
|
+
confidence = Math.min(1.0, confidence + 0.15);
|
|
218
|
+
// Importance bonus: high-importance assets get promotion priority
|
|
219
|
+
if (importance >= 70)
|
|
220
|
+
confidence = Math.min(1.0, confidence + 0.05);
|
|
221
|
+
let action;
|
|
222
|
+
if (confidence >= 0.80 && hasDeployment)
|
|
223
|
+
action = "promote";
|
|
224
|
+
else if (confidence >= 0.60)
|
|
225
|
+
action = "promote";
|
|
226
|
+
else if (confidence >= 0.30)
|
|
227
|
+
action = "hold";
|
|
228
|
+
else
|
|
229
|
+
action = "demote";
|
|
230
|
+
return this.recordDecision({
|
|
231
|
+
kind: "promotion",
|
|
232
|
+
action,
|
|
233
|
+
confidence,
|
|
234
|
+
reason: action === "promote"
|
|
235
|
+
? `Ready for promotion: ${asset.evidence.crossRepoCount} repos, importance ${importance}${hasDeployment ? ", deployment-validated" : ""}`
|
|
236
|
+
: action === "hold"
|
|
237
|
+
? `Holding at ${asset.stage}: need more evidence${!hasDeployment ? " (no deployment data)" : ""}`
|
|
238
|
+
: `Demoting: confidence ${(confidence * 100).toFixed(0)}% below threshold`,
|
|
239
|
+
provenance: {
|
|
240
|
+
assets: [asset.id],
|
|
241
|
+
evidence: [
|
|
242
|
+
`${asset.evidence.crossRepoCount} repos`,
|
|
243
|
+
`${asset.evidence.sequenceCount} sequences`,
|
|
244
|
+
...asset.evidence.rfcRefs.map(r => `RFC ${r}`),
|
|
245
|
+
hasDeployment ? `${context.deploymentObservations} deployments` : "no deployment data",
|
|
246
|
+
],
|
|
247
|
+
chain: asset.history.map(h => `${h.from}→${h.to}: ${h.reason}`),
|
|
248
|
+
decider: "system",
|
|
249
|
+
timestamp: new Date().toISOString(),
|
|
250
|
+
},
|
|
251
|
+
recommendations: !hasDeployment
|
|
252
|
+
? ["Deploy to staging for real-world validation", "Collect production observations"]
|
|
253
|
+
: action === "hold"
|
|
254
|
+
? ["Add cross-repo evidence", "Seek human review"]
|
|
255
|
+
: [],
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Make a repair decision: should we fix this, retry, or rollback?
|
|
260
|
+
*/
|
|
261
|
+
decideRepair(asset, context = {}) {
|
|
262
|
+
const importance = computeImportance(asset);
|
|
263
|
+
// Repair is only worth attempting for high-importance assets
|
|
264
|
+
// AND when confidence is high enough
|
|
265
|
+
let action;
|
|
266
|
+
let confidence = asset.confidence;
|
|
267
|
+
if (importance >= 70 && confidence >= 0.60) {
|
|
268
|
+
action = "fix";
|
|
269
|
+
}
|
|
270
|
+
else if (importance >= 50 && confidence >= 0.40) {
|
|
271
|
+
action = "retry";
|
|
272
|
+
}
|
|
273
|
+
else if (context.environment === "production") {
|
|
274
|
+
action = "rollback";
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
action = "report";
|
|
278
|
+
}
|
|
279
|
+
if (context.humanOverride) {
|
|
280
|
+
action = context.humanOverride;
|
|
281
|
+
}
|
|
282
|
+
return this.recordDecision({
|
|
283
|
+
kind: "repair",
|
|
284
|
+
action,
|
|
285
|
+
confidence,
|
|
286
|
+
reason: action === "fix"
|
|
287
|
+
? `Auto-repair recommended: importance ${importance}, confidence ${(confidence * 100).toFixed(0)}%`
|
|
288
|
+
: action === "rollback"
|
|
289
|
+
? `Rollback: too risky for production (importance ${importance}, confidence ${(confidence * 100).toFixed(0)}%)`
|
|
290
|
+
: `Report-only: insufficient confidence for auto-repair`,
|
|
291
|
+
provenance: {
|
|
292
|
+
assets: [asset.id],
|
|
293
|
+
evidence: [`importance=${importance}`, `confidence=${(confidence * 100).toFixed(0)}%`],
|
|
294
|
+
chain: [],
|
|
295
|
+
decider: context.humanOverride ? "human" : "system",
|
|
296
|
+
timestamp: new Date().toISOString(),
|
|
297
|
+
},
|
|
298
|
+
recommendations: action === "fix"
|
|
299
|
+
? ["Apply fix and re-verify", "Record outcome for learning"]
|
|
300
|
+
: action === "report"
|
|
301
|
+
? ["Flag for human review", "Collect more evidence before auto-repair"]
|
|
302
|
+
: [],
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Make a deployment decision: is this asset ready for production?
|
|
307
|
+
*/
|
|
308
|
+
decideDeployment(asset, context = {}) {
|
|
309
|
+
const importance = computeImportance(asset);
|
|
310
|
+
// Deployment requires:
|
|
311
|
+
// 1. Asset at "validated" stage or higher
|
|
312
|
+
// 2. Deployment observations (staging OK)
|
|
313
|
+
// 3. Acceptable FP rate (< 30% in staging)
|
|
314
|
+
const stageOk = ["validated", "stable"].includes(asset.stage);
|
|
315
|
+
const hasDeployment = (context.deploymentObservations || 0) > 0;
|
|
316
|
+
const fpRate = (context.recentFPs || 0) / Math.max(1, (context.recentFPs || 0) + (context.recentTPs || 0));
|
|
317
|
+
const fpOk = fpRate < 0.30;
|
|
318
|
+
let action;
|
|
319
|
+
let confidence = asset.confidence;
|
|
320
|
+
if (stageOk && hasDeployment && fpOk) {
|
|
321
|
+
action = "deploy";
|
|
322
|
+
confidence = Math.min(1.0, confidence + 0.1);
|
|
323
|
+
}
|
|
324
|
+
else if (stageOk && !hasDeployment) {
|
|
325
|
+
action = "hold";
|
|
326
|
+
}
|
|
327
|
+
else if (!fpOk) {
|
|
328
|
+
action = "rollback";
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
action = "hold";
|
|
332
|
+
}
|
|
333
|
+
return this.recordDecision({
|
|
334
|
+
kind: "deployment",
|
|
335
|
+
action,
|
|
336
|
+
confidence,
|
|
337
|
+
reason: action === "deploy"
|
|
338
|
+
? `Production-ready: stage=${asset.stage}, ${context.deploymentObservations} deployments, FP rate ${(fpRate * 100).toFixed(0)}%`
|
|
339
|
+
: action === "hold"
|
|
340
|
+
? !hasDeployment ? "Needs staging deployment first" : "Not ready for production"
|
|
341
|
+
: `Rollback: FP rate ${(fpRate * 100).toFixed(0)}% exceeds 30% threshold`,
|
|
342
|
+
provenance: {
|
|
343
|
+
assets: [asset.id],
|
|
344
|
+
evidence: [
|
|
345
|
+
`stage=${asset.stage}`,
|
|
346
|
+
`deployments=${context.deploymentObservations || 0}`,
|
|
347
|
+
`FP rate=${(fpRate * 100).toFixed(0)}%`,
|
|
348
|
+
],
|
|
349
|
+
chain: asset.history.map(h => `${h.from}→${h.to}`),
|
|
350
|
+
decider: "system",
|
|
351
|
+
timestamp: new Date().toISOString(),
|
|
352
|
+
},
|
|
353
|
+
recommendations: !hasDeployment
|
|
354
|
+
? ["Deploy to staging environment", "Collect FP/TP metrics"]
|
|
355
|
+
: !fpOk
|
|
356
|
+
? ["Investigate FP sources", "Suppress noisy rules before re-deploying"]
|
|
357
|
+
: [],
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Get full decision history for provenance tracking.
|
|
362
|
+
*/
|
|
363
|
+
getHistory() {
|
|
364
|
+
return [...this.history];
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Get decision history for a specific asset.
|
|
368
|
+
*/
|
|
369
|
+
getAssetDecisions(assetId) {
|
|
370
|
+
return this.history.filter(d => d.provenance.assets.includes(assetId));
|
|
371
|
+
}
|
|
372
|
+
recordDecision(partial) {
|
|
373
|
+
const id = `DEC-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
374
|
+
const decision = { id, ...partial };
|
|
375
|
+
this.history.push(decision);
|
|
376
|
+
return decision;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
exports.DecisionEngine = DecisionEngine;
|
|
380
|
+
// ═══════════════════════════════════════════════════════════════
|
|
381
|
+
// Singleton
|
|
382
|
+
// ═══════════════════════════════════════════════════════════════
|
|
383
|
+
let _decisionEngine = null;
|
|
384
|
+
function getDecisionEngine() {
|
|
385
|
+
if (!_decisionEngine)
|
|
386
|
+
_decisionEngine = new DecisionEngine();
|
|
387
|
+
return _decisionEngine;
|
|
388
|
+
}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Auto-derive capability metadata (@requires, @produces, @purpose, @tags)
|
|
4
|
+
* from existing code patterns:
|
|
5
|
+
* 1. @protocol pre_states → @requires, post_states → @produces
|
|
6
|
+
* 2. Function name → @purpose (humanized)
|
|
7
|
+
* 3. File path → @tags
|
|
8
|
+
* 4. Import chain → @requires (dependency)
|
|
9
|
+
*
|
|
10
|
+
* This bridges the gap from 30% → 80% metadata coverage without
|
|
11
|
+
* requiring manual annotation of every function.
|
|
12
|
+
*/
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.deriveMetadata = deriveMetadata;
|
|
48
|
+
exports.applyDerivedMetadata = applyDerivedMetadata;
|
|
49
|
+
const fs = __importStar(require("fs"));
|
|
50
|
+
const path = __importStar(require("path"));
|
|
51
|
+
/**
|
|
52
|
+
* Humanize a camelCase/PascalCase function name into a readable purpose.
|
|
53
|
+
*/
|
|
54
|
+
function humanize(name) {
|
|
55
|
+
return name
|
|
56
|
+
.replace(/([A-Z])/g, " $1")
|
|
57
|
+
.replace(/_/g, " ")
|
|
58
|
+
.replace(/\s+/g, " ")
|
|
59
|
+
.trim()
|
|
60
|
+
.toLowerCase();
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Infer tags from file path.
|
|
64
|
+
*/
|
|
65
|
+
function inferTags(filePath) {
|
|
66
|
+
const dir = path.dirname(filePath).replace(/^src\//, "");
|
|
67
|
+
const parts = dir.split("/").filter(Boolean);
|
|
68
|
+
return parts.length > 0 ? parts : ["core"];
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Parse @protocol annotations from raw source text.
|
|
72
|
+
*/
|
|
73
|
+
function parseProtocolAnnotation(source) {
|
|
74
|
+
const match = source.match(/@protocol\s+.*?pre_states\s*=\s*\[(.*?)\]\s*.*?post_states\s*=\s*\[(.*?)\]/s);
|
|
75
|
+
if (!match)
|
|
76
|
+
return null;
|
|
77
|
+
const pre = match[1].split(",").map(s => s.replace(/["'\s]/g, "")).filter(Boolean);
|
|
78
|
+
const post = match[2].split(",").map(s => s.replace(/["'\s]/g, "")).filter(Boolean);
|
|
79
|
+
return { pre, post };
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Scan source files and auto-derive capability metadata for functions that lack it.
|
|
83
|
+
*/
|
|
84
|
+
function deriveMetadata(srcDir = "src") {
|
|
85
|
+
const results = [];
|
|
86
|
+
function walk(dir) {
|
|
87
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
88
|
+
const fullPath = path.join(dir, entry.name);
|
|
89
|
+
if (entry.isDirectory()) {
|
|
90
|
+
if (entry.name === "node_modules" || entry.name === "dist" || entry.name.startsWith("."))
|
|
91
|
+
continue;
|
|
92
|
+
walk(fullPath);
|
|
93
|
+
}
|
|
94
|
+
else if (entry.name.endsWith(".ts") && !entry.name.endsWith(".test.ts")) {
|
|
95
|
+
const source = fs.readFileSync(fullPath, "utf-8");
|
|
96
|
+
processFile(fullPath, source, results);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
walk(srcDir);
|
|
101
|
+
return results;
|
|
102
|
+
}
|
|
103
|
+
function processFile(filePath, source, results) {
|
|
104
|
+
// Match: export function name | export async function name
|
|
105
|
+
const funcRegex = /\/\*\*[\s\S]*?\*\/\s*(?:export\s+)?(?:async\s+)?function\s+(\w+)/g;
|
|
106
|
+
const protocolRegex = /@protocol\s+.*?pre_states\s*=\s*\[([^\]]*)\].*?post_states\s*=\s*\[([^\]]*)\]/g;
|
|
107
|
+
let match;
|
|
108
|
+
// Strategy 1: Extract from @protocol annotations
|
|
109
|
+
// Reset regex
|
|
110
|
+
while ((match = protocolRegex.exec(source)) !== null) {
|
|
111
|
+
// Find the nearest function name before or after this annotation
|
|
112
|
+
const before = source.slice(0, match.index);
|
|
113
|
+
const after = source.slice(match.index);
|
|
114
|
+
const funcMatch = after.match(/function\s+(\w+)/);
|
|
115
|
+
if (!funcMatch)
|
|
116
|
+
continue;
|
|
117
|
+
const name = funcMatch[1];
|
|
118
|
+
const pre = match[1].split(",").map(s => s.replace(/["'\s]/g, "")).filter(Boolean);
|
|
119
|
+
const post = match[2].split(",").map(s => s.replace(/["'\s]/g, "")).filter(Boolean);
|
|
120
|
+
// Don't overwrite existing metadata
|
|
121
|
+
const existing = results.find(r => r.function === name);
|
|
122
|
+
if (existing) {
|
|
123
|
+
if (existing.requires.length === 0)
|
|
124
|
+
existing.requires = pre;
|
|
125
|
+
if (existing.produces.length === 0)
|
|
126
|
+
existing.produces = post;
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
results.push({
|
|
130
|
+
function: name,
|
|
131
|
+
file: filePath,
|
|
132
|
+
requires: pre,
|
|
133
|
+
produces: post,
|
|
134
|
+
purpose: humanize(name),
|
|
135
|
+
tags: inferTags(filePath),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Strategy 2: Extract from @requires/@produces tags in JSDoc
|
|
140
|
+
while ((match = funcRegex.exec(source)) !== null) {
|
|
141
|
+
const name = match[1];
|
|
142
|
+
const jsdoc = match[0];
|
|
143
|
+
// Skip if already covered by Strategy 1
|
|
144
|
+
const existing = results.find(r => r.function === name);
|
|
145
|
+
const requires = existing?.requires || [];
|
|
146
|
+
const produces = existing?.produces || [];
|
|
147
|
+
// Parse @requires tag
|
|
148
|
+
const reqMatch = jsdoc.match(/@requires\s+(\S+)/g);
|
|
149
|
+
if (reqMatch) {
|
|
150
|
+
for (const r of reqMatch) {
|
|
151
|
+
const val = r.replace("@requires ", "").trim();
|
|
152
|
+
if (val && !requires.includes(val))
|
|
153
|
+
requires.push(val);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Parse @produces tag
|
|
157
|
+
const prodMatch = jsdoc.match(/@produces\s+(\S+)/g);
|
|
158
|
+
if (prodMatch) {
|
|
159
|
+
for (const p of prodMatch) {
|
|
160
|
+
const val = p.replace("@produces ", "").trim();
|
|
161
|
+
if (val && !produces.includes(val))
|
|
162
|
+
produces.push(val);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Parse @purpose
|
|
166
|
+
const purposeMatch = jsdoc.match(/@purpose\s+(.+?)(?:\n|\*\/)/);
|
|
167
|
+
const purpose = purposeMatch
|
|
168
|
+
? purposeMatch[1].trim()
|
|
169
|
+
: humanize(name);
|
|
170
|
+
// Parse @tags
|
|
171
|
+
const tagsMatch = jsdoc.match(/@tags?\s+(.+?)(?:\n|\*\/)/);
|
|
172
|
+
const tags = tagsMatch
|
|
173
|
+
? tagsMatch[1].split(/[,\s]+/).map(s => s.trim()).filter(Boolean)
|
|
174
|
+
: inferTags(filePath);
|
|
175
|
+
if (existing) {
|
|
176
|
+
if (existing.requires.length === 0)
|
|
177
|
+
existing.requires = requires;
|
|
178
|
+
if (existing.produces.length === 0)
|
|
179
|
+
existing.produces = produces;
|
|
180
|
+
if (!existing.purpose || existing.purpose === humanize(name))
|
|
181
|
+
existing.purpose = purpose;
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
results.push({ function: name, file: filePath, requires, produces, purpose, tags });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Apply derived metadata back to source files.
|
|
190
|
+
* Inserts @requires/@produces/@purpose/@tags tags into existing JSDoc comments.
|
|
191
|
+
*/
|
|
192
|
+
function applyDerivedMetadata(dryRun = true) {
|
|
193
|
+
const derived = deriveMetadata();
|
|
194
|
+
let updated = 0;
|
|
195
|
+
let skipped = 0;
|
|
196
|
+
for (const meta of derived) {
|
|
197
|
+
const filePath = meta.file;
|
|
198
|
+
if (!fs.existsSync(filePath))
|
|
199
|
+
continue;
|
|
200
|
+
let source = fs.readFileSync(filePath, "utf-8");
|
|
201
|
+
// Find the function's JSDoc block
|
|
202
|
+
const funcPattern = new RegExp(`(\\/\\*\\*[\\s\\S]*?\\*\\/)\\s*(?:export\\s+)?(?:async\\s+)?function\\s+${meta.function}\\b`);
|
|
203
|
+
const match = funcPattern.exec(source);
|
|
204
|
+
if (!match) {
|
|
205
|
+
skipped++;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
const existingJSDoc = match[1];
|
|
209
|
+
let newJSDoc = existingJSDoc;
|
|
210
|
+
// Add missing tags
|
|
211
|
+
if (!existingJSDoc.includes("@requires") && meta.requires.length > 0) {
|
|
212
|
+
newJSDoc = newJSDoc.replace(/\*\/$/, ` * @requires ${meta.requires.join(" ")}\n */`);
|
|
213
|
+
}
|
|
214
|
+
if (!existingJSDoc.includes("@produces") && meta.produces.length > 0) {
|
|
215
|
+
newJSDoc = newJSDoc.replace(/\*\/$/, ` * @produces ${meta.produces.join(" ")}\n */`);
|
|
216
|
+
}
|
|
217
|
+
if (!existingJSDoc.includes("@purpose") && meta.purpose) {
|
|
218
|
+
newJSDoc = newJSDoc.replace(/\*\/$/, ` * @purpose ${meta.purpose}\n */`);
|
|
219
|
+
}
|
|
220
|
+
if (!existingJSDoc.includes("@tags") && meta.tags.length > 0) {
|
|
221
|
+
newJSDoc = newJSDoc.replace(/\*\/$/, ` * @tags ${meta.tags.join(", ")}\n */`);
|
|
222
|
+
}
|
|
223
|
+
if (newJSDoc !== existingJSDoc) {
|
|
224
|
+
if (!dryRun) {
|
|
225
|
+
source = source.replace(existingJSDoc, newJSDoc);
|
|
226
|
+
fs.writeFileSync(filePath, source, "utf-8");
|
|
227
|
+
}
|
|
228
|
+
updated++;
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
skipped++;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return { updated, skipped };
|
|
235
|
+
}
|
|
236
|
+
/** CLI: preview derivable metadata */
|
|
237
|
+
if (require.main === module) {
|
|
238
|
+
const derived = deriveMetadata();
|
|
239
|
+
console.log(`Derived metadata for ${derived.length} functions:\n`);
|
|
240
|
+
const withR = derived.filter(d => d.requires.length > 0);
|
|
241
|
+
const withP = derived.filter(d => d.produces.length > 0);
|
|
242
|
+
const withPurpose = derived.filter(d => d.purpose && d.purpose !== humanize(d.function));
|
|
243
|
+
console.log(` requires: ${withR.length} produces: ${withP.length} purpose: ${withPurpose.length}`);
|
|
244
|
+
// Show sample
|
|
245
|
+
console.log("\nSample derived metadata:");
|
|
246
|
+
for (const d of derived.filter(d => d.requires.length > 0 && d.produces.length > 0).slice(0, 5)) {
|
|
247
|
+
console.log(` ${d.function}: ${d.requires.join(",")} → ${d.produces.join(",")}`);
|
|
248
|
+
console.log(` purpose: ${d.purpose} tags: [${d.tags.join(", ")}]`);
|
|
249
|
+
}
|
|
250
|
+
}
|