progmune-runtime 2.0.4 → 2.1.1

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