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,291 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* P2.6: Ranking Evolution Test — Self-Improving Loop Verification
|
|
4
|
+
*
|
|
5
|
+
* Proves that the feedback loop works:
|
|
6
|
+
* Repair A accepted 90 times, Repair B accepted 10 times
|
|
7
|
+
* → LearningRanker ranks A > B
|
|
8
|
+
* → Then Repair B accepted 100 more times
|
|
9
|
+
* → LearningRanker now ranks B > A
|
|
10
|
+
*
|
|
11
|
+
* This is the first proof that Progmune is a Learning System,
|
|
12
|
+
* not just a Rule System.
|
|
13
|
+
*/
|
|
14
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
17
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
18
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
19
|
+
}
|
|
20
|
+
Object.defineProperty(o, k2, desc);
|
|
21
|
+
}) : (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
o[k2] = m[k];
|
|
24
|
+
}));
|
|
25
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
26
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
27
|
+
}) : function(o, v) {
|
|
28
|
+
o["default"] = v;
|
|
29
|
+
});
|
|
30
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
31
|
+
var ownKeys = function(o) {
|
|
32
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
33
|
+
var ar = [];
|
|
34
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
35
|
+
return ar;
|
|
36
|
+
};
|
|
37
|
+
return ownKeys(o);
|
|
38
|
+
};
|
|
39
|
+
return function (mod) {
|
|
40
|
+
if (mod && mod.__esModule) return mod;
|
|
41
|
+
var result = {};
|
|
42
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
43
|
+
__setModuleDefault(result, mod);
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
46
|
+
})();
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
const vitest_1 = require("vitest");
|
|
49
|
+
const fs = __importStar(require("fs"));
|
|
50
|
+
const path = __importStar(require("path"));
|
|
51
|
+
const planner_telemetry_1 = require("./planner-telemetry");
|
|
52
|
+
const learning_ranker_1 = require("./learning-ranker");
|
|
53
|
+
const repair_ranker_1 = require("./repair-ranker");
|
|
54
|
+
const analytics_1 = require("./analytics");
|
|
55
|
+
// ── Test setup ──
|
|
56
|
+
const EVO_DIR = path.resolve(__dirname, "..", "test-ranking-evolution");
|
|
57
|
+
process.env.PROGMUNE_PROJECT_DIR = EVO_DIR;
|
|
58
|
+
fs.mkdirSync(EVO_DIR, { recursive: true });
|
|
59
|
+
fs.mkdirSync(path.join(EVO_DIR, ".progmune_corpus", "telemetry"), { recursive: true });
|
|
60
|
+
fs.mkdirSync(path.join(EVO_DIR, ".progmune_corpus", "lifecycles"), { recursive: true });
|
|
61
|
+
const ctx = {
|
|
62
|
+
protocol: "FileProtocol",
|
|
63
|
+
currentState: ["FILE_OPEN"],
|
|
64
|
+
targetState: [],
|
|
65
|
+
violationType: "resource_leak",
|
|
66
|
+
constraints: [],
|
|
67
|
+
rules: new Map(),
|
|
68
|
+
};
|
|
69
|
+
function makeCandidate(id, source, actions) {
|
|
70
|
+
return {
|
|
71
|
+
id,
|
|
72
|
+
source,
|
|
73
|
+
actions: actions.map(fn => ({ kind: "call", function: fn, args: [] })),
|
|
74
|
+
explanation: `${id}: ${actions.join(" → ")}`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
(0, vitest_1.describe)("Ranking Evolution", () => {
|
|
78
|
+
(0, vitest_1.it)("accepted repairs rise over time (self-improving loop)", () => {
|
|
79
|
+
const runId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
80
|
+
const evoPath = path.join(EVO_DIR, ".progmune_corpus", "telemetry", `evo-${runId}.jsonl`);
|
|
81
|
+
const t = new planner_telemetry_1.PlannerTelemetry(evoPath);
|
|
82
|
+
const fpA = (0, planner_telemetry_1.candidateFingerprint)(`fp-${runId}`, ["open_file", "write_file", "close_file"], "resource_leak");
|
|
83
|
+
const fpB = (0, planner_telemetry_1.candidateFingerprint)(`fp-${runId}`, ["atomic_write"], "resource_leak");
|
|
84
|
+
// Use weights that favor historical success (learning test — not goal-match)
|
|
85
|
+
const base = (0, repair_ranker_1.createLinearRanker)({ safety: 0.4, successRate: 0.3, performance: 0.2, auditability: 0.1, goalMatch: 0 });
|
|
86
|
+
const learner = new learning_ranker_1.LearningRanker(base, t);
|
|
87
|
+
// Two candidates: A (safe) and B (fast)
|
|
88
|
+
const safeA = makeCandidate("safe", "protocol", ["open_file", "write_file", "close_file"]);
|
|
89
|
+
const fastB = makeCandidate("fast", "corpus", ["atomic_write"]);
|
|
90
|
+
const safeFeatures = {
|
|
91
|
+
protocolSafety: 1.0, historicalSuccessRate: 0.5, actionCount: 3,
|
|
92
|
+
latencyCost: 0.6, auditability: 0.8, corpusEvidence: 0, source: "protocol",
|
|
93
|
+
goalMatch: 0,
|
|
94
|
+
};
|
|
95
|
+
const fastFeatures = {
|
|
96
|
+
protocolSafety: 0.7, historicalSuccessRate: 0.99, actionCount: 1,
|
|
97
|
+
latencyCost: 0.1, auditability: 0.5, corpusEvidence: 42, source: "corpus",
|
|
98
|
+
goalMatch: 0,
|
|
99
|
+
};
|
|
100
|
+
// ── Phase 1: A accepted 90 times, B accepted 10 times ──
|
|
101
|
+
for (let i = 0; i < 90; i++) {
|
|
102
|
+
const id = t.recordDecision({
|
|
103
|
+
goal: "safely write config file",
|
|
104
|
+
protocol: "FileProtocol",
|
|
105
|
+
violationType: "resource_leak",
|
|
106
|
+
candidates: [
|
|
107
|
+
{ candidateId: fpA, source: "protocol", evidenceSources: ["protocol"], actions: ["open_file", "write_file", "close_file"], explanation: "safe" },
|
|
108
|
+
{ candidateId: fpB, source: "corpus", evidenceSources: ["corpus"], actions: ["atomic_write"], explanation: "fast" },
|
|
109
|
+
],
|
|
110
|
+
selectedCandidateId: fpA,
|
|
111
|
+
});
|
|
112
|
+
t.recordFeedback(id, {
|
|
113
|
+
decision: "accepted",
|
|
114
|
+
executionResult: { success: true, violations: [] },
|
|
115
|
+
timestamp: Date.now(),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
for (let i = 0; i < 10; i++) {
|
|
119
|
+
const id = t.recordDecision({
|
|
120
|
+
goal: "quick config update",
|
|
121
|
+
protocol: "FileProtocol",
|
|
122
|
+
violationType: "resource_leak",
|
|
123
|
+
candidates: [
|
|
124
|
+
{ candidateId: fpA, source: "protocol", evidenceSources: ["protocol"], actions: ["open_file", "write_file", "close_file"], explanation: "safe" },
|
|
125
|
+
{ candidateId: fpB, source: "corpus", evidenceSources: ["corpus"], actions: ["atomic_write"], explanation: "fast" },
|
|
126
|
+
],
|
|
127
|
+
selectedCandidateId: fpB,
|
|
128
|
+
});
|
|
129
|
+
// B accepted but only 30% execution success → effective reward penalized
|
|
130
|
+
const execOk = i < 3;
|
|
131
|
+
t.recordFeedback(id, {
|
|
132
|
+
decision: "accepted",
|
|
133
|
+
executionResult: { success: execOk, violations: execOk ? [] : ["resource_leak"] },
|
|
134
|
+
timestamp: Date.now(),
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
// After phase 1: A should rank above B (A has higher effective reward)
|
|
138
|
+
const ctx1 = { protocol: `fp-${runId}`, violationType: "resource_leak" };
|
|
139
|
+
const ranked1 = learner.rank([safeA, fastB], [safeFeatures, fastFeatures], ctx1);
|
|
140
|
+
(0, vitest_1.expect)(ranked1[0].id).toBe("safe");
|
|
141
|
+
(0, vitest_1.expect)(ranked1[0].acceptance).toBeGreaterThan(0.8);
|
|
142
|
+
(0, vitest_1.expect)(ranked1[0].effectiveReward).toBe(1.0);
|
|
143
|
+
// Verify TelemetryIndex stats
|
|
144
|
+
const statsA1 = t.getCandidateStats(fpA);
|
|
145
|
+
(0, vitest_1.expect)(statsA1.accepted).toBe(90);
|
|
146
|
+
(0, vitest_1.expect)(statsA1.executionSuccess).toBe(90);
|
|
147
|
+
const statsB1 = t.getCandidateStats(fpB);
|
|
148
|
+
(0, vitest_1.expect)(statsB1.accepted).toBe(10);
|
|
149
|
+
(0, vitest_1.expect)(statsB1.executionFailure).toBe(7);
|
|
150
|
+
// ── Phase 2: B gets 100 more accepts → overtakes A ──
|
|
151
|
+
for (let i = 0; i < 100; i++) {
|
|
152
|
+
const id = t.recordDecision({
|
|
153
|
+
goal: "quick config update",
|
|
154
|
+
protocol: "FileProtocol",
|
|
155
|
+
violationType: "resource_leak",
|
|
156
|
+
candidates: [
|
|
157
|
+
{ candidateId: fpA, source: "protocol", evidenceSources: ["protocol"], actions: ["open_file", "write_file", "close_file"], explanation: "safe" },
|
|
158
|
+
{ candidateId: fpB, source: "corpus", evidenceSources: ["corpus"], actions: ["atomic_write"], explanation: "fast" },
|
|
159
|
+
],
|
|
160
|
+
selectedCandidateId: fpB,
|
|
161
|
+
});
|
|
162
|
+
t.recordFeedback(id, {
|
|
163
|
+
decision: "accepted",
|
|
164
|
+
executionResult: { success: true, violations: [] },
|
|
165
|
+
timestamp: Date.now(),
|
|
166
|
+
});
|
|
167
|
+
// also record execution results for B
|
|
168
|
+
t.recordExecutionResult(id, true, [], 3);
|
|
169
|
+
}
|
|
170
|
+
// After phase 2: B should overtake A (110 accepts, 103 execution successes)
|
|
171
|
+
const ranked2 = learner.rank([safeA, fastB], [safeFeatures, fastFeatures], { protocol: `fp-${runId}`, violationType: "resource_leak" });
|
|
172
|
+
(0, vitest_1.expect)(ranked2[0].id).toBe("fast");
|
|
173
|
+
(0, vitest_1.expect)(ranked2[0].acceptance).toBeGreaterThan(0.9);
|
|
174
|
+
(0, vitest_1.expect)(ranked2[0].effectiveReward).toBeGreaterThan(0.9);
|
|
175
|
+
// Verify stats after evolution (>= because disk persistence may accumulate)
|
|
176
|
+
const statsB2 = t.getCandidateStats(fpB);
|
|
177
|
+
(0, vitest_1.expect)(statsB2.accepted).toBeGreaterThanOrEqual(110);
|
|
178
|
+
(0, vitest_1.expect)(statsB2.executionSuccess).toBeGreaterThanOrEqual(103);
|
|
179
|
+
// Adoption rate KPI
|
|
180
|
+
const adoptionRate = (0, analytics_1.getRepairAdoptionRate)(t);
|
|
181
|
+
(0, vitest_1.expect)(adoptionRate).toBe(1.0); // 200/200
|
|
182
|
+
});
|
|
183
|
+
(0, vitest_1.it)("rejected repairs sink in ranking", () => {
|
|
184
|
+
const t = new planner_telemetry_1.PlannerTelemetry(path.join(EVO_DIR, ".progmune_corpus", "telemetry", `sink-${Date.now()}.jsonl`));
|
|
185
|
+
const learner = new learning_ranker_1.LearningRanker((0, repair_ranker_1.createLinearRanker)(), t);
|
|
186
|
+
const good = makeCandidate("good", "protocol", ["verify_password", "generate_jwt"]);
|
|
187
|
+
const bad = makeCandidate("bad", "antibody", ["skip_auth"]);
|
|
188
|
+
const goodFp = (0, planner_telemetry_1.candidateFingerprint)("AuthProtocol", ["verify_password", "generate_jwt"], "missing_prerequisite");
|
|
189
|
+
const badFp = (0, planner_telemetry_1.candidateFingerprint)("AuthProtocol", ["skip_auth"], "missing_prerequisite");
|
|
190
|
+
const features = {
|
|
191
|
+
protocolSafety: 0.8, historicalSuccessRate: 0.5, actionCount: 2,
|
|
192
|
+
latencyCost: 0.3, auditability: 0.7, corpusEvidence: 0, source: "protocol",
|
|
193
|
+
};
|
|
194
|
+
// Good: accepted 50 times
|
|
195
|
+
for (let i = 0; i < 50; i++) {
|
|
196
|
+
const id = t.recordDecision({
|
|
197
|
+
goal: "authenticate user",
|
|
198
|
+
protocol: "AuthProtocol",
|
|
199
|
+
violationType: "missing_prerequisite",
|
|
200
|
+
candidates: [
|
|
201
|
+
{ candidateId: goodFp, source: "protocol", evidenceSources: ["protocol"], actions: ["verify_password", "generate_jwt"], explanation: "proper auth" },
|
|
202
|
+
{ candidateId: badFp, source: "antibody", evidenceSources: ["antibody"], actions: ["skip_auth"], explanation: "skip auth" },
|
|
203
|
+
],
|
|
204
|
+
selectedCandidateId: goodFp,
|
|
205
|
+
});
|
|
206
|
+
t.recordFeedback(id, { decision: "accepted", timestamp: Date.now() });
|
|
207
|
+
}
|
|
208
|
+
// Bad: rejected 50 times
|
|
209
|
+
for (let i = 0; i < 50; i++) {
|
|
210
|
+
const id = t.recordDecision({
|
|
211
|
+
goal: "authenticate user",
|
|
212
|
+
protocol: "AuthProtocol",
|
|
213
|
+
violationType: "missing_prerequisite",
|
|
214
|
+
candidates: [
|
|
215
|
+
{ candidateId: goodFp, source: "protocol", evidenceSources: ["protocol"], actions: ["verify_password", "generate_jwt"], explanation: "proper auth" },
|
|
216
|
+
{ candidateId: badFp, source: "antibody", evidenceSources: ["antibody"], actions: ["skip_auth"], explanation: "skip auth" },
|
|
217
|
+
],
|
|
218
|
+
selectedCandidateId: badFp,
|
|
219
|
+
});
|
|
220
|
+
t.recordFeedback(id, { decision: "rejected", timestamp: Date.now() });
|
|
221
|
+
}
|
|
222
|
+
const ranked = learner.rank([good, bad], [features, { ...features, source: "antibody" }], { protocol: "AuthProtocol", violationType: "missing_prerequisite" });
|
|
223
|
+
(0, vitest_1.expect)(ranked[0].id).toBe("good");
|
|
224
|
+
(0, vitest_1.expect)(ranked[1].acceptance).toBeLessThan(0.2); // ~0%
|
|
225
|
+
// Verify rejection stats
|
|
226
|
+
(0, vitest_1.expect)(t.getCandidateStats(badFp).rejected).toBe(50);
|
|
227
|
+
});
|
|
228
|
+
(0, vitest_1.it)("execution failure reduces effective reward", () => {
|
|
229
|
+
const t = new planner_telemetry_1.PlannerTelemetry(path.join(EVO_DIR, ".progmune_corpus", "telemetry", `execfail-${Date.now()}.jsonl`));
|
|
230
|
+
const learner = new learning_ranker_1.LearningRanker((0, repair_ranker_1.createLinearRanker)(), t);
|
|
231
|
+
const safe = makeCandidate("safe", "protocol", ["open_file", "write_file", "close_file"]);
|
|
232
|
+
const leaky = makeCandidate("leaky", "corpus", ["open_file", "write_file"]); // missing close
|
|
233
|
+
const safeFp = (0, planner_telemetry_1.candidateFingerprint)("FileProtocol", ["open_file", "write_file", "close_file"], "resource_leak");
|
|
234
|
+
const leakyFp = (0, planner_telemetry_1.candidateFingerprint)("FileProtocol", ["open_file", "write_file"], "resource_leak");
|
|
235
|
+
const features = {
|
|
236
|
+
protocolSafety: 0.8, historicalSuccessRate: 0.5, actionCount: 2,
|
|
237
|
+
latencyCost: 0.3, auditability: 0.7, corpusEvidence: 0, source: "protocol",
|
|
238
|
+
};
|
|
239
|
+
// Both accepted 50 times, but leaky always fails at execution
|
|
240
|
+
for (let i = 0; i < 50; i++) {
|
|
241
|
+
const safeId = t.recordDecision({
|
|
242
|
+
goal: "write file",
|
|
243
|
+
protocol: "FileProtocol",
|
|
244
|
+
violationType: "resource_leak",
|
|
245
|
+
candidates: [
|
|
246
|
+
{ candidateId: safeFp, source: "protocol", evidenceSources: ["protocol"], actions: ["open_file", "write_file", "close_file"], explanation: "safe" },
|
|
247
|
+
{ candidateId: leakyFp, source: "corpus", evidenceSources: ["corpus"], actions: ["open_file", "write_file"], explanation: "fast but leaky" },
|
|
248
|
+
],
|
|
249
|
+
selectedCandidateId: safeFp,
|
|
250
|
+
});
|
|
251
|
+
t.recordFeedback(safeId, {
|
|
252
|
+
decision: "accepted",
|
|
253
|
+
executionResult: { success: true, violations: [] },
|
|
254
|
+
timestamp: Date.now(),
|
|
255
|
+
});
|
|
256
|
+
const leakyId = t.recordDecision({
|
|
257
|
+
goal: "write file",
|
|
258
|
+
protocol: "FileProtocol",
|
|
259
|
+
violationType: "resource_leak",
|
|
260
|
+
candidates: [
|
|
261
|
+
{ candidateId: safeFp, source: "protocol", evidenceSources: ["protocol"], actions: ["open_file", "write_file", "close_file"], explanation: "safe" },
|
|
262
|
+
{ candidateId: leakyFp, source: "corpus", evidenceSources: ["corpus"], actions: ["open_file", "write_file"], explanation: "fast but leaky" },
|
|
263
|
+
],
|
|
264
|
+
selectedCandidateId: leakyFp,
|
|
265
|
+
});
|
|
266
|
+
t.recordFeedback(leakyId, {
|
|
267
|
+
decision: "accepted",
|
|
268
|
+
executionResult: { success: false, violations: ["resource_leak"] },
|
|
269
|
+
timestamp: Date.now(),
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
const ranked = learner.rank([safe, leaky], [features, features], { protocol: "FileProtocol", violationType: "resource_leak" });
|
|
273
|
+
// Safe ranks higher despite equal acceptance (execution success matters)
|
|
274
|
+
(0, vitest_1.expect)(ranked[0].id).toBe("safe");
|
|
275
|
+
// Safe has higher effectiveReward
|
|
276
|
+
(0, vitest_1.expect)(ranked[0].effectiveReward).toBeGreaterThan(ranked[1].effectiveReward);
|
|
277
|
+
// Safe: acceptance=1.0, execution=1.0 → effectiveReward=1.0
|
|
278
|
+
// Leaky: acceptance=1.0, execution=0.0 → effectiveReward=0.5
|
|
279
|
+
(0, vitest_1.expect)(ranked[0].effectiveReward).toBe(1.0);
|
|
280
|
+
(0, vitest_1.expect)(ranked[1].effectiveReward).toBe(0.5);
|
|
281
|
+
// Verify stats
|
|
282
|
+
const safeStats = t.getCandidateStats(safeFp);
|
|
283
|
+
(0, vitest_1.expect)(safeStats.accepted).toBe(50);
|
|
284
|
+
(0, vitest_1.expect)(safeStats.executionSuccess).toBe(50);
|
|
285
|
+
(0, vitest_1.expect)(safeStats.executionFailure).toBe(0);
|
|
286
|
+
const leakyStats = t.getCandidateStats(leakyFp);
|
|
287
|
+
(0, vitest_1.expect)(leakyStats.accepted).toBe(50);
|
|
288
|
+
(0, vitest_1.expect)(leakyStats.executionSuccess).toBe(0);
|
|
289
|
+
(0, vitest_1.expect)(leakyStats.executionFailure).toBe(50);
|
|
290
|
+
});
|
|
291
|
+
});
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Phase 10: Accountability Ledger
|
|
4
|
+
*
|
|
5
|
+
* Builds an actor-attributed chain of custody on top of the
|
|
6
|
+
* provenance chain. Maps technical events to accountable actors,
|
|
7
|
+
* identifies custody gaps, and computes the full AI supply chain
|
|
8
|
+
* from human intent through AI generation to production deployment.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* buildAccountabilityChain(sessionId, humanActor?)
|
|
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.buildAccountabilityChain = buildAccountabilityChain;
|
|
48
|
+
exports.verifyAccountabilityChain = verifyAccountabilityChain;
|
|
49
|
+
const crypto = __importStar(require("crypto"));
|
|
50
|
+
const chain_builder_1 = require("./chain-builder");
|
|
51
|
+
const signatures_1 = require("./signatures");
|
|
52
|
+
// ── Core Builder ──
|
|
53
|
+
function shortHash(data) {
|
|
54
|
+
return crypto.createHash("sha256").update(data).digest("hex").slice(0, 16);
|
|
55
|
+
}
|
|
56
|
+
function hashAccountabilityEvent(index, actorId, actorType, step, artifact, prevHash, timestamp, result) {
|
|
57
|
+
return shortHash(`${index}|${actorId}|${actorType}|${step}|${artifact}|${prevHash}|${timestamp}|${result}`);
|
|
58
|
+
}
|
|
59
|
+
function computeActorType(provEvent) {
|
|
60
|
+
const actor = provEvent.actor || "";
|
|
61
|
+
if (actor === "human")
|
|
62
|
+
return "human";
|
|
63
|
+
if (actor === "llm")
|
|
64
|
+
return "llm";
|
|
65
|
+
if (actor === "validator" || actor === "system")
|
|
66
|
+
return "validator";
|
|
67
|
+
if (actor === "planner")
|
|
68
|
+
return "llm"; // planner = LLM-driven
|
|
69
|
+
if (actor === "antibody")
|
|
70
|
+
return "validator"; // antibody = automated
|
|
71
|
+
return "validator";
|
|
72
|
+
}
|
|
73
|
+
function computeActorLabel(provEvent, options) {
|
|
74
|
+
const step = provEvent.step;
|
|
75
|
+
const actor = provEvent.actor || "";
|
|
76
|
+
const timestamp = provEvent.timestamp || "";
|
|
77
|
+
// Human actors from options
|
|
78
|
+
if (step === "generation" && options.author) {
|
|
79
|
+
return {
|
|
80
|
+
actorId: options.author.id,
|
|
81
|
+
actorType: "human",
|
|
82
|
+
actorLabel: `${options.author.name} (${options.author.id}, ${options.author.role})`,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (step === "approval" && options.approver) {
|
|
86
|
+
return {
|
|
87
|
+
actorId: options.approver.id,
|
|
88
|
+
actorType: "reviewer",
|
|
89
|
+
actorLabel: `${options.approver.name} (${options.approver.id}, approved)`,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
if (step === "deploy" && options.deployer) {
|
|
93
|
+
return {
|
|
94
|
+
actorId: options.deployer.id,
|
|
95
|
+
actorType: "deployer",
|
|
96
|
+
actorLabel: `${options.deployer.name} (${options.deployer.id})`,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
// Fallback: infer from provenance actor string
|
|
100
|
+
const actorType = computeActorType(provEvent);
|
|
101
|
+
let actorId = "";
|
|
102
|
+
let actorLabel = "";
|
|
103
|
+
switch (actorType) {
|
|
104
|
+
case "llm":
|
|
105
|
+
actorId = `llm:${actor}@${timestamp.slice(0, 10)}`;
|
|
106
|
+
actorLabel = `${actor} (LLM)`;
|
|
107
|
+
break;
|
|
108
|
+
case "validator":
|
|
109
|
+
actorId = `system:progmune-ssg@${timestamp.slice(0, 10)}`;
|
|
110
|
+
actorLabel = `Progmune Validator (SSG)`;
|
|
111
|
+
break;
|
|
112
|
+
case "human":
|
|
113
|
+
actorId = `human:${actor}@${timestamp.slice(0, 10)}`;
|
|
114
|
+
actorLabel = `${actor} (Human)`;
|
|
115
|
+
break;
|
|
116
|
+
case "reviewer":
|
|
117
|
+
actorId = `reviewer:${actor}@${timestamp.slice(0, 10)}`;
|
|
118
|
+
actorLabel = `${actor} (Reviewer)`;
|
|
119
|
+
break;
|
|
120
|
+
case "deployer":
|
|
121
|
+
actorId = `deployer:${actor}@${timestamp.slice(0, 10)}`;
|
|
122
|
+
actorLabel = `${actor} (Deployer)`;
|
|
123
|
+
break;
|
|
124
|
+
default:
|
|
125
|
+
actorId = `unknown:${actor}@${timestamp.slice(0, 10)}`;
|
|
126
|
+
actorLabel = `${actor}`;
|
|
127
|
+
}
|
|
128
|
+
return { actorId, actorType, actorLabel };
|
|
129
|
+
}
|
|
130
|
+
// ── Main Entry Point ──
|
|
131
|
+
function buildAccountabilityChain(sessionId, options = {}) {
|
|
132
|
+
// 1. Build the underlying provenance chain
|
|
133
|
+
const provChain = (0, chain_builder_1.buildProvenanceChain)(sessionId);
|
|
134
|
+
// 2. Convert each provenance event to an accountability event
|
|
135
|
+
const events = [];
|
|
136
|
+
let prevHash = "";
|
|
137
|
+
const custodyGaps = [];
|
|
138
|
+
for (const provEvent of provChain.events) {
|
|
139
|
+
const { actorId, actorType, actorLabel } = computeActorLabel(provEvent, options);
|
|
140
|
+
const action = describeAction(provEvent);
|
|
141
|
+
const eventHash = hashAccountabilityEvent(provEvent.index, actorId, actorType, provEvent.step, provEvent.artifact, prevHash, provEvent.timestamp, provEvent.result);
|
|
142
|
+
// Check custody gap: any event without verified actor identity
|
|
143
|
+
const hasGap = actorId.startsWith("unknown:") ||
|
|
144
|
+
(actorType === "human" && !options.author && !options.approver);
|
|
145
|
+
custodyGaps.push(hasGap);
|
|
146
|
+
// Try to sign the event
|
|
147
|
+
let signature;
|
|
148
|
+
try {
|
|
149
|
+
const payload = (0, signatures_1.buildSignPayload)(provEvent.index, actorId, provEvent.step, provEvent.artifact, provEvent.timestamp, provEvent.result, prevHash);
|
|
150
|
+
const sigResult = (0, signatures_1.trySign)(actorId, payload);
|
|
151
|
+
if (sigResult)
|
|
152
|
+
signature = sigResult.signature;
|
|
153
|
+
}
|
|
154
|
+
catch { /* signing is best-effort */ }
|
|
155
|
+
const aev = {
|
|
156
|
+
index: provEvent.index,
|
|
157
|
+
step: provEvent.step,
|
|
158
|
+
timestamp: provEvent.timestamp,
|
|
159
|
+
actorId,
|
|
160
|
+
actorType,
|
|
161
|
+
actorLabel,
|
|
162
|
+
artifact: provEvent.artifact,
|
|
163
|
+
action,
|
|
164
|
+
hash: eventHash,
|
|
165
|
+
prevHash,
|
|
166
|
+
result: provEvent.result,
|
|
167
|
+
signature,
|
|
168
|
+
detail: provEvent.detail,
|
|
169
|
+
};
|
|
170
|
+
events.push(aev);
|
|
171
|
+
prevHash = eventHash;
|
|
172
|
+
}
|
|
173
|
+
// 3. Add explicit human events from options (if not already in chain)
|
|
174
|
+
if (options.author && events.length > 0) {
|
|
175
|
+
// Prepend human author event
|
|
176
|
+
const humanEvent = makeHumanEvent("author", options.author, events[0].hash, events.length);
|
|
177
|
+
events.unshift(humanEvent);
|
|
178
|
+
// Recompute hashes forward
|
|
179
|
+
recomputeChainHashes(events);
|
|
180
|
+
}
|
|
181
|
+
if (options.reviewers && events.length > 0) {
|
|
182
|
+
// Insert reviewer events after validation
|
|
183
|
+
let valIdx = -1;
|
|
184
|
+
for (let i = events.length - 1; i >= 0; i--) {
|
|
185
|
+
if (events[i].step === "validation" || events[i].step === "repair") {
|
|
186
|
+
valIdx = i;
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const insertIdx = valIdx >= 0 ? valIdx + 1 : events.length;
|
|
191
|
+
for (const reviewer of options.reviewers) {
|
|
192
|
+
const prev = insertIdx > 0 ? events[insertIdx - 1].hash : "";
|
|
193
|
+
const re = makeHumanEvent("reviewer", reviewer, prev, insertIdx);
|
|
194
|
+
events.splice(insertIdx, 0, re);
|
|
195
|
+
}
|
|
196
|
+
recomputeChainHashes(events);
|
|
197
|
+
}
|
|
198
|
+
if (options.approver && events.length > 0) {
|
|
199
|
+
// Add approval event before deploy
|
|
200
|
+
const depIdx = events.findIndex((e) => e.step === "deploy");
|
|
201
|
+
const insertIdx = depIdx >= 0 ? depIdx : events.length;
|
|
202
|
+
const prev = insertIdx > 0 ? events[insertIdx - 1].hash : "";
|
|
203
|
+
const ae = makeHumanEvent("approver", options.approver, prev, insertIdx);
|
|
204
|
+
events.splice(insertIdx, 0, ae);
|
|
205
|
+
recomputeChainHashes(events);
|
|
206
|
+
}
|
|
207
|
+
// 4. Compute chain hash and stats
|
|
208
|
+
const chainHash = computeAccountabilityChainHash(events);
|
|
209
|
+
const humanEvents = events.filter((e) => e.actorType === "human" || e.actorType === "reviewer").length;
|
|
210
|
+
const aiEvents = events.filter((e) => e.actorType === "llm").length;
|
|
211
|
+
const autoEvents = events.filter((e) => e.actorType === "validator" || e.actorType === "deployer").length;
|
|
212
|
+
const hasGap = custodyGaps.some(Boolean);
|
|
213
|
+
return {
|
|
214
|
+
sessionId: provChain.sessionId,
|
|
215
|
+
intent: provChain.intent,
|
|
216
|
+
events,
|
|
217
|
+
chainHash,
|
|
218
|
+
integrity: provChain.integrity,
|
|
219
|
+
custodyGap: hasGap,
|
|
220
|
+
totalEvents: events.length,
|
|
221
|
+
humanEvents,
|
|
222
|
+
aiEvents,
|
|
223
|
+
automatedEvents: autoEvents,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
// ── Helpers ──
|
|
227
|
+
function describeAction(ev) {
|
|
228
|
+
switch (ev.step) {
|
|
229
|
+
case "generation":
|
|
230
|
+
return `Generated ${ev.artifact}`;
|
|
231
|
+
case "validation":
|
|
232
|
+
return `Validated ${ev.artifact}`;
|
|
233
|
+
case "repair":
|
|
234
|
+
return `Repaired ${ev.artifact}`;
|
|
235
|
+
case "approval":
|
|
236
|
+
return `Approved ${ev.artifact}`;
|
|
237
|
+
case "deploy":
|
|
238
|
+
return `Deployed ${ev.artifact}`;
|
|
239
|
+
default:
|
|
240
|
+
return ev.step;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function makeHumanEvent(role, human, prevHash, index) {
|
|
244
|
+
const step = role === "author" ? "generation" : role === "reviewer" ? "approval" : "approval";
|
|
245
|
+
const timestamp = new Date().toISOString();
|
|
246
|
+
const eventHash = hashAccountabilityEvent(index, human.id, "human", step, `intent: ${human.role}`, prevHash, timestamp, "approved");
|
|
247
|
+
return {
|
|
248
|
+
index,
|
|
249
|
+
step: step,
|
|
250
|
+
timestamp,
|
|
251
|
+
actorId: human.id,
|
|
252
|
+
actorType: role === "author" ? "human" : "reviewer",
|
|
253
|
+
actorLabel: `${human.name} (${human.id}, ${human.role})`,
|
|
254
|
+
artifact: `Intent from ${human.name}`,
|
|
255
|
+
action: role === "author" ? "Initiated code generation"
|
|
256
|
+
: role === "reviewer" ? "Reviewed and approved"
|
|
257
|
+
: "Approved deployment",
|
|
258
|
+
hash: eventHash,
|
|
259
|
+
prevHash,
|
|
260
|
+
result: "approved",
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function recomputeChainHashes(events) {
|
|
264
|
+
let ph = "";
|
|
265
|
+
for (let i = 0; i < events.length; i++) {
|
|
266
|
+
events[i].index = i;
|
|
267
|
+
events[i].prevHash = ph;
|
|
268
|
+
events[i].hash = hashAccountabilityEvent(i, events[i].actorId, events[i].actorType, events[i].step, events[i].artifact, ph, events[i].timestamp, events[i].result);
|
|
269
|
+
ph = events[i].hash;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
function computeAccountabilityChainHash(events) {
|
|
273
|
+
return shortHash(events.map((e) => e.hash).join(""));
|
|
274
|
+
}
|
|
275
|
+
// ── Verify accountability chain integrity ──
|
|
276
|
+
function verifyAccountabilityChain(chain) {
|
|
277
|
+
let ph = "";
|
|
278
|
+
let unsignedCount = 0;
|
|
279
|
+
let signedCount = 0;
|
|
280
|
+
for (const e of chain.events) {
|
|
281
|
+
// 1. Verify hash chain
|
|
282
|
+
const recomputed = hashAccountabilityEvent(e.index, e.actorId, e.actorType, e.step, e.artifact, ph, e.timestamp, e.result);
|
|
283
|
+
if (recomputed !== e.hash) {
|
|
284
|
+
return {
|
|
285
|
+
valid: false,
|
|
286
|
+
brokenAt: e.index,
|
|
287
|
+
detail: `Hash mismatch at event ${e.index}: expected ${recomputed}, got ${e.hash}`,
|
|
288
|
+
unsignedCount, signedCount,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
// 2. Verify signature (if present)
|
|
292
|
+
if (e.signature) {
|
|
293
|
+
try {
|
|
294
|
+
const { verifySignature, loadPublicKey } = require("./signatures");
|
|
295
|
+
const payload = (0, signatures_1.buildSignPayload)(e.index, e.actorId, e.step, e.artifact, e.timestamp, e.result, e.prevHash);
|
|
296
|
+
const pubKey = loadPublicKey(e.actorId);
|
|
297
|
+
if (pubKey) {
|
|
298
|
+
const verification = verifySignature(payload, e.signature, pubKey);
|
|
299
|
+
e.signatureVerified = verification.valid;
|
|
300
|
+
if (!verification.valid) {
|
|
301
|
+
return {
|
|
302
|
+
valid: false,
|
|
303
|
+
brokenAt: e.index,
|
|
304
|
+
detail: `Signature verification failed at event ${e.index}: ${verification.detail}`,
|
|
305
|
+
unsignedCount, signedCount,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
signedCount++;
|
|
310
|
+
}
|
|
311
|
+
catch { /* skip signature check if verification fails */ }
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
unsignedCount++;
|
|
315
|
+
}
|
|
316
|
+
ph = e.hash;
|
|
317
|
+
}
|
|
318
|
+
if (computeAccountabilityChainHash(chain.events) !== chain.chainHash) {
|
|
319
|
+
return { valid: false, detail: "Chain root hash mismatch", unsignedCount, signedCount };
|
|
320
|
+
}
|
|
321
|
+
return { valid: true, unsignedCount, signedCount };
|
|
322
|
+
}
|