progmune-runtime 2.0.4 → 2.1.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.
Files changed (106) hide show
  1. package/.mcp.json +11 -0
  2. package/.progmune_allowlist +50 -0
  3. package/.test_report/test_report.md +87 -0
  4. package/Dockerfile +2 -10
  5. package/FAQ.md +167 -0
  6. package/demo-project/auth.ts +55 -0
  7. package/demo-project/tsconfig.json +8 -0
  8. package/dist/ab-stats.js +11 -0
  9. package/dist/acl-breakdown.js +13 -0
  10. package/dist/all-sessions.js +11 -0
  11. package/dist/antibody-stats.js +11 -0
  12. package/dist/audit.js +218 -0
  13. package/dist/benchmark-count.js +7 -0
  14. package/dist/benchmark-full.js +17 -0
  15. package/dist/benchmark-pass-rate.js +54 -0
  16. package/dist/benchmark-report.js +67 -0
  17. package/dist/benchmark-save.js +62 -0
  18. package/dist/benchmark-status.js +15 -0
  19. package/dist/branch-ledger.js +365 -0
  20. package/dist/branch-tree-count.js +14 -0
  21. package/dist/check.js +506 -0
  22. package/dist/common-fixpath.js +12 -0
  23. package/dist/constraint-types.js +12 -0
  24. package/dist/deterministic-replay.js +277 -0
  25. package/dist/emitter.js +112 -12
  26. package/dist/exec-metrics.js +11 -0
  27. package/dist/execute.js +242 -0
  28. package/dist/extract-ir.js +550 -5
  29. package/dist/failure-collector.js +143 -0
  30. package/dist/failure-corpus.js +481 -35
  31. package/dist/failure-report.js +11 -0
  32. package/dist/failures.js +11 -0
  33. package/dist/fast-path-hits.js +13 -0
  34. package/dist/feedback.js +6 -3
  35. package/dist/file-lock.js +82 -0
  36. package/dist/find-session.js +10 -0
  37. package/dist/fingerprint-list.js +15 -0
  38. package/dist/gen-history-log.js +13 -0
  39. package/dist/generate.js +2 -1
  40. package/dist/generate_500.js +4 -2
  41. package/dist/genome.js +11 -0
  42. package/dist/heatmap-data.js +11 -0
  43. package/dist/heatmap.js +11 -0
  44. package/dist/immune-reporter.js +33 -22
  45. package/dist/learned.js +11 -0
  46. package/dist/ledger-registry.js +241 -0
  47. package/dist/llm.js +43 -2
  48. package/dist/load-benchmarks.js +47 -0
  49. package/dist/main.js +2 -1
  50. package/dist/mcp-server.mjs +445 -43
  51. package/dist/memory-layer.js +51 -13
  52. package/dist/metrics.js +11 -0
  53. package/dist/obs-web.js +561 -0
  54. package/dist/p0_ssg_demo.js +255 -40
  55. package/dist/planner.js +905 -80
  56. package/dist/protocol-registry.js +105 -0
  57. package/dist/recent-session.js +12 -0
  58. package/dist/repair-proposal.js +353 -0
  59. package/dist/runtime-invariants.js +161 -0
  60. package/dist/runtime-types.js +117 -0
  61. package/dist/search-planner.js +38 -9
  62. package/dist/semantic-snapshot.js +155 -0
  63. package/dist/semantic-trace.js +1497 -0
  64. package/dist/semantic-validator.js +3 -2
  65. package/dist/semantic_guard_test.js +2 -1
  66. package/dist/sessions.js +11 -0
  67. package/dist/ssg-validator.js +658 -20
  68. package/dist/svl-distribution.js +11 -0
  69. package/dist/terminal-status.js +11 -0
  70. package/dist/test_failure_corpus.js +3 -1
  71. package/dist/token-savings.js +11 -0
  72. package/dist/total-repairs.js +12 -0
  73. package/dist/unresolved-count.js +12 -0
  74. package/dist/valid-fingerprints.js +13 -0
  75. package/dist/validator.js +116 -60
  76. package/dist/verify-fps.js +11 -0
  77. package/dist/verify-ledgers.js +11 -0
  78. package/docs/whitepaper-style.css +77 -0
  79. package/docs/whitepaper-v2.1.md +609 -0
  80. package/docs/whitepaper-v2.2.md +1064 -0
  81. package/docs/whitepaper-v2.2.pdf +0 -0
  82. package/fly.toml +1 -1
  83. package/package.json +13 -4
  84. package/protocols.json +131 -11
  85. package/public/dashboard.html +119 -0
  86. package/readme.md +829 -0
  87. package/server/hub.js +84 -12
  88. package/test/replay-golden/sess_1780063202050_mgeld.json +9 -0
  89. package/test/replay-golden/sess_1780064032560_gocld.json +354 -0
  90. package/test/replay-golden/sess_1780064413331_s2709.json +606 -0
  91. package/test/replay-golden/sess_1780064792710_y3avo.json +614 -0
  92. package/test/replay-golden.ts +84 -0
  93. package/test_benchmark.js +165 -0
  94. package/test_comprehensive.mjs +638 -0
  95. package/test_concurrency.js +129 -0
  96. package/test_ir_robustness.js +85 -0
  97. package/test_semantic_contracts.js +269 -0
  98. package/test_ssg_stress.js +156 -0
  99. package/test_svl3.js +58 -0
  100. package/tsconfig.json +1 -1
  101. package/.env.example +0 -3
  102. package/.progmune_memory/episodic.json +0 -186
  103. package/.progmune_memory/fingerprints.json +0 -7
  104. package/.progmune_memory/opt_in.json +0 -4
  105. package/README.md +0 -118
  106. package/immune_hub_data/2026-05-14.json +0 -50
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ /**
3
+ * Phase 6C: Single Source of Protocol Truth
4
+ *
5
+ * ALL components read namespaceInitialStates, rules, ruleHash, and version
6
+ * from here. No component may hardcode default states or read protocols.json directly.
7
+ *
8
+ * Usage:
9
+ * import { getProtocolConfig } from "./protocol-registry";
10
+ * const { nsInit, rules, ruleHash, version } = getProtocolConfig();
11
+ */
12
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ var desc = Object.getOwnPropertyDescriptor(m, k);
15
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
+ desc = { enumerable: true, get: function() { return m[k]; } };
17
+ }
18
+ Object.defineProperty(o, k2, desc);
19
+ }) : (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ o[k2] = m[k];
22
+ }));
23
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
24
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
25
+ }) : function(o, v) {
26
+ o["default"] = v;
27
+ });
28
+ var __importStar = (this && this.__importStar) || (function () {
29
+ var ownKeys = function(o) {
30
+ ownKeys = Object.getOwnPropertyNames || function (o) {
31
+ var ar = [];
32
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
33
+ return ar;
34
+ };
35
+ return ownKeys(o);
36
+ };
37
+ return function (mod) {
38
+ if (mod && mod.__esModule) return mod;
39
+ var result = {};
40
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
41
+ __setModuleDefault(result, mod);
42
+ return result;
43
+ };
44
+ })();
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.invalidateProtocolCache = invalidateProtocolCache;
47
+ exports.getProtocolConfig = getProtocolConfig;
48
+ exports.getNsInit = getNsInit;
49
+ exports.getRuleHash = getRuleHash;
50
+ const fs = __importStar(require("fs"));
51
+ const path = __importStar(require("path"));
52
+ const ssg_validator_1 = require("./ssg-validator");
53
+ // ── Singleton cache ──
54
+ let cached = null;
55
+ /** Invalidate the cache (call after protocols.json changes). */
56
+ function invalidateProtocolCache() {
57
+ cached = null;
58
+ }
59
+ /** Get the authoritative protocol configuration.
60
+ * Cached after first call; call invalidateProtocolCache() to force reload. */
61
+ function getProtocolConfig() {
62
+ if (cached)
63
+ return cached;
64
+ const nsInit = new Map();
65
+ let rules = [];
66
+ let version = "1.0";
67
+ const protoPath = path.resolve(process.env.PROGMUNE_PROJECT_DIR || process.cwd(), "protocols.json");
68
+ if (fs.existsSync(protoPath)) {
69
+ try {
70
+ const proto = JSON.parse(fs.readFileSync(protoPath, "utf-8"));
71
+ version = proto.$schema || proto.version || "1.0";
72
+ // Load namespace initial states
73
+ nsInit.set("_global", "UNAUTHENTICATED");
74
+ if (proto.namespaceInitialStates) {
75
+ for (const [ns, state] of Object.entries(proto.namespaceInitialStates)) {
76
+ nsInit.set(ns, state);
77
+ }
78
+ }
79
+ // Parse rules
80
+ rules = (0, ssg_validator_1.parseProtocolsFromJSON)(proto);
81
+ }
82
+ catch { }
83
+ }
84
+ else {
85
+ // Fallback: minimal defaults (no protocols.json found)
86
+ nsInit.set("_global", "UNAUTHENTICATED");
87
+ }
88
+ // Compute rule hash
89
+ const ruleMap = new Map();
90
+ for (const r of rules) {
91
+ ruleMap.set(r.function, r.protocol);
92
+ }
93
+ const ruleHash = (0, ssg_validator_1.hashRules)(ruleMap);
94
+ cached = { nsInit, rules, ruleHash, version };
95
+ return cached;
96
+ }
97
+ // ── Convenience re-exports ──
98
+ /** Get namespace initial states only (most common need). */
99
+ function getNsInit() {
100
+ return new Map(getProtocolConfig().nsInit);
101
+ }
102
+ /** Get current rule hash without loading full config. */
103
+ function getRuleHash() {
104
+ return getProtocolConfig().ruleHash;
105
+ }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.main = main;
4
+ // @progmune-generated session=sess_1780343925827_zga2j timestamp=2026-06-01T19:58:47.097Z ruleHash=9dec68bc2995e92a
5
+ // Generated with IR constraint: 398 functions, 17 protocol rules
6
+ const failure_corpus_1 = require("./failure-corpus");
7
+ function main() {
8
+ const sessions = (0, failure_corpus_1.getAllSessions)();
9
+ const idx = findIndex();
10
+ return sessions;
11
+ }
12
+ main();
@@ -0,0 +1,353 @@
1
+ "use strict";
2
+ /**
3
+ * Phase 4: Repair Proposal Engine (P2)
4
+ *
5
+ * Suggests repairs for detected violations — but NEVER auto-writes to the Ledger.
6
+ * Ledger = Evidence, not Cache.
7
+ *
8
+ * Three proposal strategies:
9
+ * insert — missing step detected, insert function call before violation
10
+ * replace — transition has incorrect state data, replace it
11
+ * reorder — wrong call order, propose reordering
12
+ *
13
+ * The engine produces Proposal JSON. The caller (Planner, human, future Repair Planner)
14
+ * decides whether to accept and create a Branch.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.suggestRepairs = suggestRepairs;
18
+ exports.suggestProtocolRepair = suggestProtocolRepair;
19
+ exports.suggestInvariantRepair = suggestInvariantRepair;
20
+ exports.applyProposalAsBranch = applyProposalAsBranch;
21
+ exports.validateProposal = validateProposal;
22
+ exports.generateRepairSummary = generateRepairSummary;
23
+ exports.getMinimalFixSet = getMinimalFixSet;
24
+ const ssg_validator_1 = require("./ssg-validator");
25
+ const runtime_invariants_1 = require("./runtime-invariants");
26
+ const protocol_registry_1 = require("./protocol-registry");
27
+ const branch_ledger_1 = require("./branch-ledger");
28
+ // ── Proposal Generation ──
29
+ /** Generate repair proposals for all detected violations in a ledger. */
30
+ function suggestRepairs(violations, ir, protocols) {
31
+ const proposals = [];
32
+ for (const v of violations) {
33
+ if (v.violatedConstraint === "protocol") {
34
+ const rejection = tryParseRejection(v);
35
+ if (rejection) {
36
+ proposals.push(...suggestProtocolRepair(rejection, ir));
37
+ continue;
38
+ }
39
+ }
40
+ // Generic violation: suggest based on SVL level
41
+ proposals.push(suggestGenericRepair(v, ir));
42
+ }
43
+ return proposals;
44
+ }
45
+ /** Protocol violation repair: use SSG fixPath to suggest insertions. */
46
+ function suggestProtocolRepair(rejection, ir) {
47
+ const proposals = [];
48
+ if (rejection.fixPath && rejection.fixPath.length > 0) {
49
+ // Each missing function in the fix path is a separate insert proposal
50
+ for (let i = 0; i < rejection.fixPath.length; i++) {
51
+ const fnName = rejection.fixPath[i];
52
+ const def = ir.find((f) => f.name === fnName);
53
+ const actions = [];
54
+ if (def) {
55
+ const args = (def.params || []).map((p, j) => ({
56
+ name: p.name || `p${j}`,
57
+ type: p.type || "any",
58
+ value: "",
59
+ }));
60
+ const action = {
61
+ kind: "call",
62
+ function: fnName,
63
+ args,
64
+ };
65
+ if (def.returnType && def.returnType !== "void" && def.returnType !== "undefined") {
66
+ action.assignTo = `${fnName}_result`;
67
+ }
68
+ actions.push(action);
69
+ }
70
+ proposals.push({
71
+ id: `rp_${Date.now()}_${Math.random().toString(36).slice(2, 5)}`,
72
+ violationIndex: 0, // protocol violations don't have a specific transition index
73
+ reason: `Protocol violation: ${rejection.blocked} requires ${fnName}`,
74
+ strategy: "insert",
75
+ insertBefore: undefined, // caller decides exact position
76
+ proposedActions: actions,
77
+ confidence: 0.85 + (i * 0.05), // later steps in fixPath have higher confidence
78
+ explanation: `Insert ${fnName} before ${rejection.blocked}. Missing states: [${(rejection.missingFunctions || []).join(", ")}]. Fix path: ${rejection.fixPath.join(" → ")}`,
79
+ });
80
+ }
81
+ }
82
+ // If no fixPath but missing functions are known, propose searching
83
+ if (proposals.length === 0 && rejection.missingFunctions && rejection.missingFunctions.length > 0) {
84
+ proposals.push({
85
+ id: `rp_${Date.now()}_${Math.random().toString(36).slice(2, 5)}`,
86
+ violationIndex: 0,
87
+ reason: `Protocol violation: ${rejection.blocked} blocked`,
88
+ strategy: "insert",
89
+ proposedActions: [],
90
+ confidence: 0.5,
91
+ explanation: `Cannot determine fix path. Blocked function: ${rejection.blocked}. Required states: [${(rejection.requiredState || []).join(", ")}]. Current states: [${(rejection.currentState || []).join(", ")}].`,
92
+ });
93
+ }
94
+ return proposals;
95
+ }
96
+ /** Invariant violation repair: use rebuildState to compute correct transition data. */
97
+ function suggestInvariantRepair(violation, ledger, namespaceInitialStates = (0, protocol_registry_1.getNsInit)()) {
98
+ const proposals = [];
99
+ if (violation.invariant === "before-consistency") {
100
+ // statesBefore is wrong — can be recomputed from rebuildState
101
+ const correctStatesBefore = violation.expected;
102
+ const t = ledger[violation.index];
103
+ if (!t)
104
+ return [];
105
+ const correctedTransition = {
106
+ ...t,
107
+ statesBefore: correctStatesBefore,
108
+ };
109
+ // Ontology: verify corrected transition passes delta consistency
110
+ try {
111
+ (0, runtime_invariants_1.assertDeltaConsistency)(correctedTransition);
112
+ }
113
+ catch { }
114
+ proposals.push({
115
+ id: `rp_${Date.now()}_${Math.random().toString(36).slice(2, 5)}`,
116
+ violationIndex: violation.index,
117
+ reason: `Invariant-0 (before-consistency): statesBefore mismatch at index ${violation.index}`,
118
+ strategy: "replace",
119
+ replacement: correctedTransition,
120
+ proposedActions: [], // no new actions needed — just correct the record
121
+ confidence: 0.95,
122
+ explanation: `Transition[${violation.index}] "${t.function}" has incorrect statesBefore. Expected: ${JSON.stringify(correctStatesBefore[t.namespace] || [])}. This is likely a recording error, not a code error.`,
123
+ });
124
+ }
125
+ if (violation.invariant === "delta-consistency") {
126
+ // statesAfter doesn't match applyDelta(statesBefore, acquired, invalidated)
127
+ const correctStatesAfter = violation.expected;
128
+ const t = ledger[violation.index];
129
+ if (!t)
130
+ return [];
131
+ const correctedTransition = {
132
+ ...t,
133
+ statesAfter: correctStatesAfter,
134
+ };
135
+ try {
136
+ (0, runtime_invariants_1.assertDeltaConsistency)(correctedTransition);
137
+ }
138
+ catch { }
139
+ proposals.push({
140
+ id: `rp_${Date.now()}_${Math.random().toString(36).slice(2, 5)}`,
141
+ violationIndex: violation.index,
142
+ reason: `Invariant-1 (delta-consistency): statesAfter mismatch at index ${violation.index}`,
143
+ strategy: "replace",
144
+ replacement: correctedTransition,
145
+ proposedActions: [],
146
+ confidence: 0.95,
147
+ explanation: `Transition[${violation.index}] "${t.function}" statesAfter does not match applyDelta(statesBefore). Expected: ${JSON.stringify(correctStatesAfter[t.namespace] || [])}. The acquire/invalidate fields may also need recomputation.`,
148
+ });
149
+ }
150
+ return proposals;
151
+ }
152
+ /** Generic repair for SVL-1/SVL-2/SVL-3 violations. */
153
+ function suggestGenericRepair(violation, ir) {
154
+ const base = {
155
+ id: `rp_${Date.now()}_${Math.random().toString(36).slice(2, 5)}`,
156
+ violationIndex: violation.actionIndex,
157
+ proposedActions: [],
158
+ };
159
+ switch (violation.violatedConstraint) {
160
+ case "symbol_existence": {
161
+ // Find closest matching function in IR
162
+ const missingName = violation.missingStates?.[0] || "unknown";
163
+ let bestMatch = "";
164
+ let bestScore = 0;
165
+ for (const fn of ir) {
166
+ const score = jaccardSimilarity(missingName.toLowerCase(), fn.name.toLowerCase());
167
+ if (score > bestScore && score >= 0.3) {
168
+ bestScore = score;
169
+ bestMatch = fn.name;
170
+ }
171
+ }
172
+ if (bestMatch) {
173
+ const def = ir.find((f) => f.name === bestMatch);
174
+ const args = (def?.params || []).map((p, j) => ({
175
+ name: p.name || `p${j}`,
176
+ type: p.type || "any",
177
+ value: "",
178
+ }));
179
+ return {
180
+ ...base,
181
+ reason: `SVL-1: function "${missingName}" does not exist`,
182
+ strategy: "replace",
183
+ proposedActions: [{ kind: "call", function: bestMatch, args }],
184
+ confidence: 0.7 + bestScore * 0.3,
185
+ explanation: `Replace "${missingName}" with the closest real function "${bestMatch}" (similarity: ${(bestScore * 100).toFixed(0)}%).`,
186
+ };
187
+ }
188
+ return {
189
+ ...base,
190
+ reason: `SVL-1: function "${missingName}" does not exist`,
191
+ strategy: "replace",
192
+ confidence: 0.1,
193
+ explanation: `Cannot find a matching function for "${missingName}" in the IR. Manual intervention required.`,
194
+ };
195
+ }
196
+ case "type_mismatch":
197
+ return {
198
+ ...base,
199
+ reason: `SVL-2: type/parameter mismatch`,
200
+ strategy: "replace",
201
+ confidence: 0.6,
202
+ explanation: `Action[${violation.actionIndex}] has incorrect parameter types or count. Check the function signature in IR and adjust args accordingly.`,
203
+ };
204
+ case "dataflow":
205
+ return {
206
+ ...base,
207
+ reason: `SVL-3: dataflow violation`,
208
+ strategy: "reorder",
209
+ confidence: 0.5,
210
+ explanation: `Action[${violation.actionIndex}] uses a variable before it's defined, or creates a circular reference. Reorder actions or add an assignment.`,
211
+ };
212
+ default:
213
+ return {
214
+ ...base,
215
+ reason: `Unknown violation: ${violation.violatedConstraint}`,
216
+ strategy: "replace",
217
+ confidence: 0.1,
218
+ explanation: violation.description,
219
+ };
220
+ }
221
+ }
222
+ // ── Proposal → Branch Bridge ──
223
+ /** Convert an accepted repair proposal into a new Branch.
224
+ * Creates a child branch with the proposed fix applied.
225
+ * The original ledger is never modified. */
226
+ function applyProposalAsBranch(proposal, parentBranch, currentLedger, ir) {
227
+ const branch = (0, branch_ledger_1.createBranch)(parentBranch, "repair_attempt");
228
+ switch (proposal.strategy) {
229
+ case "insert": {
230
+ if (proposal.insertBefore !== undefined && proposal.insertBefore < currentLedger.length) {
231
+ // Insert proposed actions before the violation point
232
+ const before = currentLedger.slice(0, proposal.insertBefore);
233
+ const after = currentLedger.slice(proposal.insertBefore);
234
+ // Create placeholder transitions for inserted actions
235
+ const newTransitions = proposal.proposedActions.map((a, i) => ({
236
+ actionIndex: proposal.insertBefore + i,
237
+ function: a.kind === "call" ? a.function : a.kind,
238
+ namespace: "_global",
239
+ acquired: [],
240
+ invalidated: [],
241
+ statesBefore: {},
242
+ statesAfter: {},
243
+ valid: true,
244
+ }));
245
+ branch.transitions = [...before, ...newTransitions, ...after];
246
+ }
247
+ else {
248
+ branch.transitions = [...currentLedger];
249
+ }
250
+ break;
251
+ }
252
+ case "replace": {
253
+ if (proposal.replacement && proposal.violationIndex < currentLedger.length) {
254
+ branch.transitions = currentLedger.map((t, i) => i === proposal.violationIndex ? proposal.replacement : t);
255
+ }
256
+ else {
257
+ branch.transitions = [...currentLedger];
258
+ }
259
+ break;
260
+ }
261
+ case "reorder": {
262
+ if (proposal.newOrder) {
263
+ branch.transitions = proposal.newOrder.map(i => currentLedger[i]);
264
+ }
265
+ else {
266
+ branch.transitions = [...currentLedger];
267
+ }
268
+ break;
269
+ }
270
+ default:
271
+ branch.transitions = [...currentLedger];
272
+ }
273
+ return branch;
274
+ }
275
+ // ── Validation ──
276
+ /** Validate a repair proposal: does applying it fix the violation?
277
+ * Returns true if a re-check passes after applying the proposal. */
278
+ function validateProposal(proposal, currentLedger, namespaceInitialStates = (0, protocol_registry_1.getNsInit)()) {
279
+ let proposedLedger;
280
+ switch (proposal.strategy) {
281
+ case "replace":
282
+ proposedLedger = currentLedger.map((t, i) => i === proposal.violationIndex && proposal.replacement ? proposal.replacement : t);
283
+ break;
284
+ case "reorder":
285
+ proposedLedger = proposal.newOrder
286
+ ? proposal.newOrder.map(i => currentLedger[i])
287
+ : [...currentLedger];
288
+ break;
289
+ case "insert":
290
+ default:
291
+ proposedLedger = [...currentLedger];
292
+ }
293
+ const result = (0, ssg_validator_1.checkLedgerConsistency)(proposedLedger, namespaceInitialStates);
294
+ return {
295
+ valid: result.consistent,
296
+ remainingViolations: result.violations,
297
+ };
298
+ }
299
+ // ── Summary ──
300
+ /** Generate a comprehensive repair summary from a ledger and IR context. */
301
+ function generateRepairSummary(ledger, ir, protocols, namespaceInitialStates = (0, protocol_registry_1.getNsInit)()) {
302
+ const consistency = (0, ssg_validator_1.checkLedgerConsistency)(ledger, namespaceInitialStates);
303
+ const allProposals = [];
304
+ // Generate invariant repair proposals
305
+ for (const v of consistency.violations) {
306
+ allProposals.push(...suggestInvariantRepair(v, ledger, namespaceInitialStates));
307
+ }
308
+ const minimalFixSet = getMinimalFixSet(allProposals);
309
+ return {
310
+ totalViolations: consistency.violations.length,
311
+ proposals: allProposals,
312
+ minimalFixSet,
313
+ };
314
+ }
315
+ /**
316
+ * Deduplicate repair proposals: pick the highest-confidence proposal per violation index.
317
+ * Returns proposals sorted by violationIndex (ascending).
318
+ *
319
+ * This is the authoritative "minimal fix set" — applying these proposals in order
320
+ * should resolve all detected violations without redundant fixes.
321
+ */
322
+ function getMinimalFixSet(proposals) {
323
+ const seen = new Map();
324
+ for (const p of proposals) {
325
+ const existing = seen.get(p.violationIndex);
326
+ if (!existing || p.confidence > existing.confidence) {
327
+ seen.set(p.violationIndex, p);
328
+ }
329
+ }
330
+ return [...seen.values()].sort((a, b) => a.violationIndex - b.violationIndex);
331
+ }
332
+ // ── Helpers ──
333
+ function tryParseRejection(v) {
334
+ try {
335
+ if (v.description) {
336
+ const parsed = JSON.parse(v.description);
337
+ if (parsed.protocol_violation) {
338
+ return parsed.protocol_violation;
339
+ }
340
+ }
341
+ }
342
+ catch {
343
+ // Not JSON — try to extract from description string
344
+ }
345
+ return null;
346
+ }
347
+ function jaccardSimilarity(a, b) {
348
+ const setA = new Set(a.split(""));
349
+ const setB = new Set(b.split(""));
350
+ const intersection = new Set([...setA].filter(x => setB.has(x)));
351
+ const union = new Set([...setA, ...setB]);
352
+ return union.size === 0 ? 0 : intersection.size / union.size;
353
+ }
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ /**
3
+ * Phase 4: Runtime Invariant Assertion Layer
4
+ *
5
+ * Centralized invariant enforcement. All invariant checks → throw on failure.
6
+ * Replaces scattered console.error() with structured InvariantViolationError.
7
+ *
8
+ * Controlled by PROGMUNE_STRICT env var (default: true in dev/ci).
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.InvariantViolationError = void 0;
12
+ exports.assertLedgerConsistency = assertLedgerConsistency;
13
+ exports.assertDeltaConsistency = assertDeltaConsistency;
14
+ exports.assertRuleHashMatch = assertRuleHashMatch;
15
+ exports.assertTransitionOrder = assertTransitionOrder;
16
+ exports.assertLedgerInvariants = assertLedgerInvariants;
17
+ const ssg_validator_1 = require("./ssg-validator");
18
+ const protocol_registry_1 = require("./protocol-registry");
19
+ // Re-export for convenient import
20
+ var ssg_validator_2 = require("./ssg-validator");
21
+ Object.defineProperty(exports, "InvariantViolationError", { enumerable: true, get: function () { return ssg_validator_2.InvariantViolationError; } });
22
+ // ── Config ──
23
+ const STRICT = process.env.PROGMUNE_STRICT !== "false";
24
+ const isStrict = () => STRICT;
25
+ // ── Assertions ──
26
+ /** Assert full ledger passes Invariant-0 + Invariant-1.
27
+ * Throws InvariantViolationError with the first violation's details. */
28
+ function assertLedgerConsistency(ledger, namespaceInitialStates = (0, protocol_registry_1.getNsInit)()) {
29
+ if (ledger.length === 0)
30
+ return;
31
+ const result = (0, ssg_validator_1.checkLedgerConsistency)(ledger, namespaceInitialStates);
32
+ if (result.consistent)
33
+ return;
34
+ if (!isStrict()) {
35
+ console.error(`[Invariant] Ledger consistency: ${result.violations.length} violation(s) detected (PROGMUNE_STRICT=false — not throwing)`);
36
+ for (const v of result.violations) {
37
+ console.error(` [${v.invariant}] index=${v.index}: ${v.detail || ""}`);
38
+ }
39
+ return;
40
+ }
41
+ const first = result.violations[0];
42
+ const msg = result.violations.length === 1
43
+ ? `Ledger invariant violation: [${first.invariant}] at index ${first.index}`
44
+ : `Ledger invariant violation: ${result.violations.length} violations, first: [${first.invariant}] at index ${first.index}`;
45
+ throw new ssg_validator_1.InvariantViolationError(msg, {
46
+ invariant: first.invariant,
47
+ index: first.index,
48
+ expected: first.expected,
49
+ actual: first.actual,
50
+ });
51
+ }
52
+ /** Assert a single transition's delta consistency.
53
+ * Checks that applying acquire/invalidate to statesBefore produces statesAfter. */
54
+ function assertDeltaConsistency(transition) {
55
+ if (!transition.valid)
56
+ return;
57
+ // Replay: apply the delta to statesBefore, compare with statesAfter
58
+ const fromSnapshot = (snap) => {
59
+ const map = new Map();
60
+ for (const [ns, states] of Object.entries(snap)) {
61
+ map.set(ns, new Set(states));
62
+ }
63
+ return map;
64
+ };
65
+ const toSnapshot = (stateMap) => {
66
+ const snap = {};
67
+ for (const [ns, states] of stateMap) {
68
+ snap[ns] = [...states].sort();
69
+ }
70
+ return snap;
71
+ };
72
+ const deltaMap = fromSnapshot(transition.statesBefore);
73
+ for (const s of transition.acquired) {
74
+ const ns = transition.namespace;
75
+ if (!deltaMap.has(ns))
76
+ deltaMap.set(ns, new Set());
77
+ deltaMap.get(ns).add(s);
78
+ }
79
+ for (const s of transition.invalidated) {
80
+ const ns = transition.namespace;
81
+ deltaMap.get(ns)?.delete(s);
82
+ }
83
+ const computedAfter = toSnapshot(deltaMap);
84
+ const deepEqual = (a, b) => {
85
+ const keysA = Object.keys(a).sort();
86
+ const keysB = Object.keys(b).sort();
87
+ if (keysA.length !== keysB.length)
88
+ return false;
89
+ for (const k of keysA) {
90
+ if (!b[k])
91
+ return false;
92
+ if (a[k].join(",") !== b[k].join(","))
93
+ return false;
94
+ }
95
+ return true;
96
+ };
97
+ if (!deepEqual(computedAfter, transition.statesAfter)) {
98
+ const msg = `[Invariant-1] Delta inconsistency in transition[${transition.actionIndex}] "${transition.function}": statesAfter mismatch`;
99
+ if (!isStrict()) {
100
+ console.error(msg);
101
+ return;
102
+ }
103
+ throw new ssg_validator_1.InvariantViolationError(msg, {
104
+ invariant: "delta-consistency",
105
+ namespace: transition.namespace,
106
+ function: transition.function,
107
+ index: transition.actionIndex,
108
+ expected: computedAfter,
109
+ actual: transition.statesAfter,
110
+ });
111
+ }
112
+ }
113
+ /** Assert rule hashes match — detects when validation rules changed under a ledger. */
114
+ function assertRuleHashMatch(expected, actual, context) {
115
+ if (expected === actual)
116
+ return;
117
+ const msg = context
118
+ ? `Rule hash mismatch in ${context}: expected ${expected.slice(0, 16)}, got ${actual.slice(0, 16)}`
119
+ : `Rule hash mismatch: expected ${expected.slice(0, 16)}, got ${actual.slice(0, 16)}`;
120
+ if (!isStrict()) {
121
+ console.error(msg);
122
+ return;
123
+ }
124
+ throw new ssg_validator_1.InvariantViolationError(msg, {
125
+ invariant: "rule-hash-mismatch",
126
+ });
127
+ }
128
+ /** Assert transition indices are strictly monotonic (no duplicates, non-decreasing). */
129
+ function assertTransitionOrder(ledger) {
130
+ if (ledger.length <= 1)
131
+ return;
132
+ for (let i = 1; i < ledger.length; i++) {
133
+ if (ledger[i].actionIndex <= ledger[i - 1].actionIndex) {
134
+ const msg = `[Transition Order] Non-monotonic indices at position ${i}: index[${i - 1}]=${ledger[i - 1].actionIndex}, index[${i}]=${ledger[i].actionIndex}`;
135
+ if (!isStrict()) {
136
+ console.error(msg);
137
+ return;
138
+ }
139
+ throw new ssg_validator_1.InvariantViolationError(msg, {
140
+ invariant: "transition-order",
141
+ index: i,
142
+ });
143
+ }
144
+ }
145
+ }
146
+ /** Convenience: run all invariant checks on a ledger. Does not throw if all pass. */
147
+ function assertLedgerInvariants(ledger, namespaceInitialStates = (0, protocol_registry_1.getNsInit)(), expectedRuleHash) {
148
+ assertTransitionOrder(ledger);
149
+ assertLedgerConsistency(ledger, namespaceInitialStates);
150
+ for (const t of ledger) {
151
+ if (t.valid)
152
+ assertDeltaConsistency(t);
153
+ }
154
+ if (expectedRuleHash) {
155
+ const actualRuleHash = (0, ssg_validator_1.hashRules)(new Map()); // ledger's first transition carries ruleHash
156
+ const ledgerRuleHash = ledger[0]?.ruleHash;
157
+ if (ledgerRuleHash) {
158
+ assertRuleHashMatch(expectedRuleHash, ledgerRuleHash, "ledger invariants check");
159
+ }
160
+ }
161
+ }