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,283 @@
1
+ "use strict";
2
+ /**
3
+ * Phase 4: Deterministic Replay (P1)
4
+ *
5
+ * Verifies that replaying a session's ledger against current rules
6
+ * produces the same result as the stored execution certificate.
7
+ *
8
+ * Answers: "Would this execution produce the same result today?"
9
+ * Not just "read the log" — actively re-validates every transition.
10
+ */
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || (function () {
28
+ var ownKeys = function(o) {
29
+ ownKeys = Object.getOwnPropertyNames || function (o) {
30
+ var ar = [];
31
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
+ return ar;
33
+ };
34
+ return ownKeys(o);
35
+ };
36
+ return function (mod) {
37
+ if (mod && mod.__esModule) return mod;
38
+ var result = {};
39
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
+ __setModuleDefault(result, mod);
41
+ return result;
42
+ };
43
+ })();
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.replaySession = replaySession;
46
+ exports.replayLedger = replayLedger;
47
+ exports.replayWithDetail = replayWithDetail;
48
+ const fs = __importStar(require("fs"));
49
+ const path = __importStar(require("path"));
50
+ const ledger_registry_1 = require("./ledger-registry");
51
+ const protocol_registry_1 = require("./protocol-registry");
52
+ const ssg_validator_1 = require("./ssg-validator");
53
+ const branch_ledger_1 = require("./branch-ledger");
54
+ // ── Core Replay ──
55
+ /** Replay a session from disk, comparing against its stored fingerprint. */
56
+ /** Deterministically replay a session ledger and verify against stored fingerprint. */
57
+ /** @requires SESSION_DATA @produces REPLAY_RESULT */
58
+ function replaySession(sessionId, currentRules, namespaceInitialStates = (0, protocol_registry_1.getNsInit)()) {
59
+ // Load session
60
+ const sessionsDir = path.resolve(process.env.PROGMUNE_PROJECT_DIR || process.cwd(), ".progmune_corpus/sessions");
61
+ const sessionFile = path.join(sessionsDir, `${sessionId}.json`);
62
+ if (!fs.existsSync(sessionFile)) {
63
+ return {
64
+ success: false,
65
+ sessionId,
66
+ finalState: {},
67
+ replayedHash: "",
68
+ storedHash: "",
69
+ ruleHashMatch: false,
70
+ ledgerHashMatch: false,
71
+ totalTransitions: 0,
72
+ validTransitions: 0,
73
+ invalidTransitions: 0,
74
+ divergencePoint: 0,
75
+ divergenceDetail: `Session file not found: ${sessionFile}`,
76
+ };
77
+ }
78
+ const session = JSON.parse(fs.readFileSync(sessionFile, "utf-8"));
79
+ // Collect all transitions
80
+ let transitions = [];
81
+ let sessionRuleHash = session.ruleHash || "";
82
+ // Handle branch tree if present (Phase 4)
83
+ if (session.branchTree && session.branchTree.length > 0) {
84
+ const branches = session.branchTree;
85
+ const map = (0, branch_ledger_1.buildBranchMap)(branches);
86
+ const root = (0, branch_ledger_1.findRootBranch)(branches);
87
+ if (root) {
88
+ transitions = (0, branch_ledger_1.flattenBranch)(root, map);
89
+ }
90
+ // Use first attempt's ruleHash as session hash
91
+ if (!sessionRuleHash && branches[0]?.transitions[0]?.ruleHash) {
92
+ sessionRuleHash = branches[0].transitions[0].ruleHash;
93
+ }
94
+ }
95
+ else {
96
+ // Legacy: collect from attempts
97
+ for (const attempt of (session.attempts || [])) {
98
+ if (attempt.transitions) {
99
+ transitions = transitions.concat(attempt.transitions);
100
+ }
101
+ if (!sessionRuleHash && (attempt.ruleHash || session.ruleHash)) {
102
+ sessionRuleHash = attempt.ruleHash || session.ruleHash;
103
+ }
104
+ }
105
+ }
106
+ // Load fingerprint
107
+ const fingerprint = (0, ledger_registry_1.getFingerprint)(sessionId);
108
+ const storedHash = fingerprint?.ledgerHash || "";
109
+ // Compute current rule hash
110
+ let currentRuleHash = "";
111
+ if (currentRules && currentRules.size > 0) {
112
+ currentRuleHash = (0, ssg_validator_1.hashRules)(currentRules);
113
+ }
114
+ return replayLedger(sessionId, transitions, sessionRuleHash, storedHash, currentRuleHash, currentRules, namespaceInitialStates);
115
+ }
116
+ /** Core replay logic: replay transitions against (optional) current rules. */
117
+ /** Replay a ledger of transitions against current rules and verify consistency. */
118
+ /** @requires LEDGER_DATA @produces REPLAY_RESULT */
119
+ function replayLedger(sessionId, transitions, storedRuleHash, storedLedgerHash, currentRuleHash, currentRules, namespaceInitialStates = (0, protocol_registry_1.getNsInit)()) {
120
+ const replayedHash = transitions.length > 0 ? (0, ssg_validator_1.hashLedger)(transitions) : "";
121
+ const ruleHashMatch = !currentRuleHash || !storedRuleHash
122
+ ? true // can't compare if either is missing
123
+ : currentRuleHash === storedRuleHash;
124
+ const ledgerHashMatch = !storedLedgerHash
125
+ ? true // no fingerprint to compare against
126
+ : replayedHash === storedLedgerHash;
127
+ // Replay each transition if we have current rules
128
+ let validTransitions = 0;
129
+ let invalidTransitions = 0;
130
+ let divergencePoint;
131
+ let divergenceDetail;
132
+ if (currentRules && currentRules.size > 0) {
133
+ const ctx = {
134
+ ledger: [],
135
+ currentState: (0, ssg_validator_1.rebuildState)([], namespaceInitialStates),
136
+ };
137
+ for (let i = 0; i < transitions.length; i++) {
138
+ const t = transitions[i];
139
+ if (!t.valid) {
140
+ invalidTransitions++;
141
+ continue;
142
+ }
143
+ try {
144
+ // Re-validate against current rules
145
+ const { valid, transition } = (0, ssg_validator_1.validateTransition)(ctx, t.function, i, currentRules, namespaceInitialStates, currentRuleHash || "");
146
+ if (!valid && divergencePoint === undefined) {
147
+ divergencePoint = i;
148
+ divergenceDetail = `Transition[${i}] "${t.function}" is no longer valid under current rules.`;
149
+ }
150
+ // Check statesBefore matches rebuilt state
151
+ const rebuiltBefore = ctx.currentState;
152
+ const beforeMatch = deepEqualSnapshots(rebuiltBefore, t.statesBefore);
153
+ if (!beforeMatch && divergencePoint === undefined) {
154
+ divergencePoint = i;
155
+ divergenceDetail = `Transition[${i}] "${t.function}" statesBefore mismatch: rebuilt=${JSON.stringify(rebuiltBefore[t.namespace] || [])} recorded=${JSON.stringify(t.statesBefore[t.namespace] || [])}`;
156
+ }
157
+ if (valid) {
158
+ validTransitions++;
159
+ ctx.ledger.push(transition);
160
+ ctx.currentState = transition.statesAfter;
161
+ }
162
+ else {
163
+ invalidTransitions++;
164
+ ctx.ledger.push({ ...t, valid: false });
165
+ }
166
+ }
167
+ catch {
168
+ invalidTransitions++;
169
+ if (divergencePoint === undefined) {
170
+ divergencePoint = i;
171
+ divergenceDetail = `Transition[${i}] "${t.function}" threw an error during replay. Rule may have changed.`;
172
+ }
173
+ }
174
+ }
175
+ }
176
+ else {
177
+ // No current rules: just verify structural consistency
178
+ const consistency = (0, ssg_validator_1.checkLedgerConsistency)(transitions, namespaceInitialStates);
179
+ if (!consistency.consistent) {
180
+ divergencePoint = consistency.violations[0]?.index;
181
+ divergenceDetail = `Structural inconsistency: ${consistency.violations.length} violation(s). First: [${consistency.violations[0]?.invariant}] at index ${consistency.violations[0]?.index}`;
182
+ }
183
+ validTransitions = transitions.filter(t => t.valid).length;
184
+ invalidTransitions = transitions.filter(t => !t.valid).length;
185
+ }
186
+ // Compute final state
187
+ const finalState = transitions.length > 0
188
+ ? (0, ssg_validator_1.rebuildState)(transitions.filter(t => t.valid), namespaceInitialStates)
189
+ : {};
190
+ const success = ruleHashMatch && ledgerHashMatch && divergencePoint === undefined;
191
+ return {
192
+ success,
193
+ sessionId,
194
+ finalState,
195
+ divergencePoint,
196
+ divergenceDetail,
197
+ replayedHash,
198
+ storedHash: storedLedgerHash,
199
+ ruleHashMatch,
200
+ ledgerHashMatch,
201
+ totalTransitions: transitions.length,
202
+ validTransitions,
203
+ invalidTransitions,
204
+ };
205
+ }
206
+ /** Replay with per-transition detail — for debugging and UI. */
207
+ /** Replay transitions with per-step detail for debugging. */
208
+ /** @requires TRANSITIONS @produces DETAIL_RESULT */
209
+ function replayWithDetail(transitions, currentRules, namespaceInitialStates = (0, protocol_registry_1.getNsInit)()) {
210
+ if (!currentRules || currentRules.size === 0) {
211
+ return transitions.map(t => ({
212
+ index: t.actionIndex,
213
+ function: t.function,
214
+ namespace: t.namespace,
215
+ originalValid: t.valid,
216
+ replayValid: t.valid,
217
+ statesBeforeMatch: true,
218
+ statesAfterMatch: true,
219
+ }));
220
+ }
221
+ const ctx = {
222
+ ledger: [],
223
+ currentState: (0, ssg_validator_1.rebuildState)([], namespaceInitialStates),
224
+ };
225
+ const currentRuleHash = (0, ssg_validator_1.hashRules)(currentRules);
226
+ const results = [];
227
+ for (let i = 0; i < transitions.length; i++) {
228
+ const t = transitions[i];
229
+ const result = {
230
+ index: t.actionIndex,
231
+ function: t.function,
232
+ namespace: t.namespace,
233
+ originalValid: t.valid,
234
+ replayValid: false,
235
+ statesBeforeMatch: false,
236
+ statesAfterMatch: false,
237
+ };
238
+ if (!t.valid) {
239
+ result.detail = "Originally invalid";
240
+ results.push(result);
241
+ continue;
242
+ }
243
+ try {
244
+ const { valid, transition } = (0, ssg_validator_1.validateTransition)(ctx, t.function, i, currentRules, namespaceInitialStates, currentRuleHash);
245
+ result.replayValid = valid;
246
+ result.statesBeforeMatch = deepEqualSnapshots(ctx.currentState, t.statesBefore);
247
+ result.statesAfterMatch = valid
248
+ ? deepEqualSnapshots(transition.statesAfter, t.statesAfter)
249
+ : false;
250
+ if (!result.statesBeforeMatch) {
251
+ result.detail = `statesBefore mismatch`;
252
+ }
253
+ else if (!result.statesAfterMatch) {
254
+ result.detail = `statesAfter mismatch`;
255
+ }
256
+ else if (!valid) {
257
+ result.detail = `No longer valid under current rules`;
258
+ }
259
+ ctx.ledger.push(valid ? transition : { ...t, valid: false });
260
+ ctx.currentState = valid ? transition.statesAfter : ctx.currentState;
261
+ }
262
+ catch (e) {
263
+ result.replayValid = false;
264
+ result.detail = `Error: ${e.message?.slice(0, 80)}`;
265
+ }
266
+ results.push(result);
267
+ }
268
+ return results;
269
+ }
270
+ // ── Helpers ──
271
+ function deepEqualSnapshots(a, b) {
272
+ const keysA = Object.keys(a).sort();
273
+ const keysB = Object.keys(b).sort();
274
+ if (keysA.length !== keysB.length)
275
+ return false;
276
+ for (const k of keysA) {
277
+ if (!b[k])
278
+ return false;
279
+ if (a[k].join(",") !== b[k].join(","))
280
+ return false;
281
+ }
282
+ return true;
283
+ }
package/dist/emitter.js CHANGED
@@ -39,11 +39,46 @@ const BASIC_TYPES = new Set([
39
39
  "string", "number", "boolean", "any", "void", "undefined", "null",
40
40
  "str", "int", "float", "bool", "list", "dict", "tuple", "bytes",
41
41
  ]);
42
+ /** Normalize a type string to an importable type name.
43
+ * Strips [], <...>, unions, and typeof expressions.
44
+ * e.g. "StateTransition[]" → "StateTransition"
45
+ * "Map<string, string>" → "Map"
46
+ * "Action | null" → "Action"
47
+ * "ReturnType<typeof fn>" → null (complex, skip) */
48
+ function normalizeTypeName(raw) {
49
+ let t = raw.trim();
50
+ // Skip function types and complex expressions
51
+ if (t.includes("=>") || t.includes("typeof"))
52
+ return null;
53
+ // Strip array brackets
54
+ t = t.replace(/\[\]/g, "");
55
+ // Strip generic parameters
56
+ t = t.replace(/<[^>]*>/g, "");
57
+ // Take first part of union/intersection
58
+ t = t.split("|")[0].split("&")[0].trim();
59
+ // Must be a simple identifier
60
+ if (/^[A-Z][a-zA-Z0-9_]*$/.test(t))
61
+ return t;
62
+ return null;
63
+ }
42
64
  function getImportPath(file) {
43
- return "./" + file.replace(/\.ts$|\.tsx$/, "");
65
+ // Normalize: strip common src/ prefix since generated code lives in src/
66
+ let clean = file.replace(/\.ts$|\.tsx$/, "");
67
+ if (clean.startsWith("src/"))
68
+ clean = clean.slice(4);
69
+ return "./" + clean;
44
70
  }
45
- function emitCode(actions) {
46
- const ir = JSON.parse(fs.readFileSync("ir.json", "utf-8"));
71
+ /**
72
+ * 将动作序列编译为目标语言代码。
73
+ * @protocol namespace=dev_pipeline pre_states=["SEQUENCE_VALIDATED"] post_states=["CODE_EMITTED"] invalidate=["SEQUENCE_VALIDATED"]
74
+ */
75
+ /** @requires ACTIONS @produces TYPESCRIPT_CODE */
76
+ /** @requires ACTIONS @produces TYPESCRIPT_CODE */
77
+ function emitCode(actions, meta) {
78
+ const irRaw = JSON.parse(fs.readFileSync("ir.json", "utf-8"));
79
+ // Support both old format (array) and new format ({typeMap, functions})
80
+ const typeMap = irRaw.typeMap || {};
81
+ const ir = irRaw.functions || irRaw;
47
82
  const fnIndex = new Map();
48
83
  const fnMeta = new Map();
49
84
  for (const f of ir) {
@@ -56,16 +91,21 @@ function emitCode(actions) {
56
91
  if (action.kind === "call" && action.function) {
57
92
  const file = fnIndex.get(action.function);
58
93
  const meta = fnMeta.get(action.function);
59
- if (file) {
94
+ // Skip external functions — they are global/standard library (fs, path, etc.)
95
+ // Skip non-exported functions — can't import what isn't exported
96
+ if (file && file !== "(external)" && meta?.exported !== false) {
60
97
  if (!imports.has(file))
61
98
  imports.set(file, new Set());
62
99
  imports.get(file).add(action.function);
63
100
  if (meta?.params) {
64
101
  for (const p of meta.params) {
65
- if (!BASIC_TYPES.has(p.type)) {
66
- if (!typeImports.has(file))
67
- typeImports.set(file, new Set());
68
- typeImports.get(file).add(p.type);
102
+ const normalized = normalizeTypeName(p.type);
103
+ if (normalized && !BASIC_TYPES.has(normalized)) {
104
+ // Use typeMap to find the correct module for this type
105
+ const typeFile = typeMap[normalized] || file;
106
+ if (!typeImports.has(typeFile))
107
+ typeImports.set(typeFile, new Set());
108
+ typeImports.get(typeFile).add(normalized);
69
109
  }
70
110
  }
71
111
  }
@@ -81,14 +121,63 @@ function emitCode(actions) {
81
121
  };
82
122
  for (const a of actions)
83
123
  collect(a);
124
+ // ── Generation marker (provable usage) ──
84
125
  let code = "";
126
+ if (meta?.sessionId) {
127
+ const ts = new Date().toISOString();
128
+ code += `// @progmune-generated session=${meta.sessionId} timestamp=${ts}`;
129
+ if (meta.ruleHash)
130
+ code += ` ruleHash=${meta.ruleHash}`;
131
+ code += `\n`;
132
+ const funcs = meta.irFunctionCount ?? ir.length;
133
+ const rules = meta.protocolRuleCount ?? 0;
134
+ code += `// Generated with IR constraint: ${funcs} functions`;
135
+ if (rules > 0)
136
+ code += `, ${rules} protocol rules`;
137
+ code += `\n`;
138
+ }
85
139
  for (const [file, funcSet] of imports)
86
140
  code += `import { ${[...funcSet].join(", ")} } from "${getImportPath(file)}";\n`;
87
141
  for (const [file, typeSet] of typeImports)
88
142
  code += `import type { ${[...typeSet].join(", ")} } from "${getImportPath(file)}";\n`;
89
- code += "\nexport function main() {\n";
143
+ // Pre-scan: find input variables (referenced but never assigned via call/assign)
90
144
  const declared = new Set();
145
+ const referenced = new Set();
91
146
  let counter = 0;
147
+ // First pass: collect declared and referenced variables
148
+ for (const action of actions) {
149
+ if (action.kind === "call" && action.assignTo) {
150
+ declared.add(action.assignTo);
151
+ }
152
+ else if (action.kind === "assign" && action.target) {
153
+ declared.add(action.target);
154
+ }
155
+ // Collect arg value references
156
+ if (action.kind === "call" && action.args) {
157
+ for (const arg of action.args) {
158
+ const v = typeof arg === "object" ? arg?.value : arg;
159
+ if (typeof v === "string" && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(v) && v !== "") {
160
+ referenced.add(v);
161
+ }
162
+ }
163
+ }
164
+ }
165
+ // Inputs = referenced but not declared — add as typed parameters
166
+ const inputs = [...referenced].filter(v => !declared.has(v) && v !== "");
167
+ const inputTypes = new Map();
168
+ for (const action of actions) {
169
+ if (action.kind === "call" && action.args) {
170
+ for (const arg of action.args) {
171
+ const v = typeof arg === "object" ? arg?.value : arg;
172
+ const t = typeof arg === "object" ? arg?.type : "string";
173
+ if (typeof v === "string" && inputs.includes(v) && t && t !== "any") {
174
+ inputTypes.set(v, t);
175
+ }
176
+ }
177
+ }
178
+ }
179
+ const paramList = inputs.map(v => `${v}: ${inputTypes.get(v) || "string"}`).join(", ");
180
+ code += `\nexport function main(${paramList}) {\n`;
92
181
  const convert = (action, indent = " ") => {
93
182
  if (!action || !action.kind)
94
183
  return "";
@@ -103,16 +192,37 @@ function emitCode(actions) {
103
192
  const val = a?.value;
104
193
  if (typeof val === "string" && declared.has(val))
105
194
  return val;
195
+ // If value looks like a variable name (valid JS identifier), pass it through
196
+ if (typeof val === "string" && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(val) && val !== "") {
197
+ declared.add(val);
198
+ return val;
199
+ }
106
200
  const paramType = meta?.params?.[i]?.type || "any";
107
201
  if (BASIC_TYPES.has(paramType)) {
108
202
  if (paramType === "string" || paramType === "str")
109
- return `"defaultStr"`;
203
+ return `""`;
110
204
  if (paramType === "number" || paramType === "int" || paramType === "float")
111
205
  return "0";
112
206
  if (paramType === "boolean" || paramType === "bool")
113
207
  return "false";
114
- return `"default"`;
208
+ return `""`;
115
209
  }
210
+ // String enum defaults — match param type to sensible value
211
+ const STRING_ENUMS = {
212
+ "SVL": '"SVL-4"', "RootCause": '"F01"', "BranchReason": '"repair_attempt"',
213
+ "RepairStrategy": '"insert"', "ConstraintType": '"protocol"',
214
+ "SVLString": '"SVL-4"', "BranchOutcome": '"success"',
215
+ };
216
+ if (STRING_ENUMS[paramType])
217
+ return STRING_ENUMS[paramType];
218
+ // Array types: use empty array
219
+ if (paramType.endsWith("[]"))
220
+ return "[]";
221
+ // Generic Map/Set types: use empty constructor
222
+ if (paramType.startsWith("Map<"))
223
+ return `new Map()`;
224
+ if (paramType.startsWith("Set<"))
225
+ return `new Set()`;
116
226
  if (paramType === "UserPayload")
117
227
  return `{ id: 1, role: "user" } as UserPayload`;
118
228
  if (paramType === "PasswordHash")
@@ -161,6 +271,27 @@ function emitCode(actions) {
161
271
  };
162
272
  for (const a of actions)
163
273
  code += convert(a) + "\n";
164
- code += "}\nmain();\n";
274
+ // Ensure last action is a return — inject one if LLM forgot
275
+ const lastAction = actions[actions.length - 1];
276
+ if (lastAction && lastAction.kind !== "return") {
277
+ const allCalls = actions.filter(a => a.kind === "call" && a.assignTo);
278
+ if (allCalls.length === 1) {
279
+ code += ` return ${allCalls[0].assignTo};\n`;
280
+ }
281
+ else if (allCalls.length > 1) {
282
+ // Multiple calls: return all results as an object
283
+ const vars = allCalls.map(c => c.assignTo).join(", ");
284
+ code += ` return { ${vars} };\n`;
285
+ }
286
+ }
287
+ code += "}\n";
288
+ // Call main with params if it has inputs, otherwise no-arg call
289
+ if (inputs.length > 0) {
290
+ // Don't call — the user provides the arguments
291
+ // Just export the function for external use
292
+ }
293
+ else {
294
+ code += "main();\n";
295
+ }
165
296
  return code;
166
297
  }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.main = main;
4
+ // @progmune-generated session=sess_1780343438004_ux6bt timestamp=2026-06-01T19:50:39.415Z ruleHash=9dec68bc2995e92a
5
+ // Generated with IR constraint: 385 functions, 17 protocol rules
6
+ const execute_1 = require("./execute");
7
+ function main() {
8
+ const metrics = (0, execute_1.getExecutionMetrics)();
9
+ return metrics;
10
+ }
11
+ main();