martin-loop 0.1.4 → 1.3.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 (286) hide show
  1. package/CODE_OF_CONDUCT.md +32 -0
  2. package/README.md +172 -227
  3. package/demo/seeded-workspace/README.md +35 -0
  4. package/demo/seeded-workspace/TASKS.md +29 -0
  5. package/demo/seeded-workspace/martin.config.yaml +11 -0
  6. package/demo/seeded-workspace/package.json +8 -0
  7. package/demo/seeded-workspace/src/invoice-summary.js +11 -0
  8. package/demo/seeded-workspace/test/invoice-summary.test.js +20 -0
  9. package/dist/bin/martin-loop.js +0 -0
  10. package/dist/vendor/adapters/claude-cli.d.ts +19 -4
  11. package/dist/vendor/adapters/claude-cli.js +55 -24
  12. package/dist/vendor/adapters/cli-bridge.d.ts +1 -0
  13. package/dist/vendor/adapters/cli-bridge.js +154 -28
  14. package/dist/vendor/adapters/counter.d.ts +1 -0
  15. package/dist/vendor/adapters/counter.js +4 -0
  16. package/dist/vendor/adapters/git-baseline.d.ts +50 -0
  17. package/dist/vendor/adapters/git-baseline.js +233 -0
  18. package/dist/vendor/adapters/index.d.ts +1 -0
  19. package/dist/vendor/adapters/index.js +1 -0
  20. package/dist/vendor/adapters/openrouter-adapter.d.ts +15 -0
  21. package/dist/vendor/adapters/openrouter-adapter.js +302 -0
  22. package/dist/vendor/adapters/usage.d.ts +48 -0
  23. package/dist/vendor/adapters/usage.js +66 -0
  24. package/dist/vendor/adapters/verifier-only.d.ts +7 -0
  25. package/dist/vendor/adapters/verifier-only.js +57 -0
  26. package/dist/vendor/cli/bin/exit.d.ts +12 -0
  27. package/dist/vendor/cli/bin/exit.js +28 -0
  28. package/dist/vendor/cli/commands/analyze.d.ts +5 -0
  29. package/dist/vendor/cli/commands/analyze.js +58 -0
  30. package/dist/vendor/cli/commands/audit-log-verify.d.ts +34 -0
  31. package/dist/vendor/cli/commands/audit-log-verify.js +99 -0
  32. package/dist/vendor/cli/commands/audit.d.ts +8 -0
  33. package/dist/vendor/cli/commands/audit.js +199 -0
  34. package/dist/vendor/cli/commands/corpus.d.ts +5 -0
  35. package/dist/vendor/cli/commands/corpus.js +60 -0
  36. package/dist/vendor/cli/commands/doctor.d.ts +8 -0
  37. package/dist/vendor/cli/commands/doctor.js +219 -0
  38. package/dist/vendor/cli/commands/explain.d.ts +17 -0
  39. package/dist/vendor/cli/commands/explain.js +176 -0
  40. package/dist/vendor/cli/commands/export.d.ts +5 -0
  41. package/dist/vendor/cli/commands/export.js +60 -0
  42. package/dist/vendor/cli/commands/governance.d.ts +8 -0
  43. package/dist/vendor/cli/commands/governance.js +95 -0
  44. package/dist/vendor/cli/commands/improve.d.ts +18 -0
  45. package/dist/vendor/cli/commands/improve.js +396 -0
  46. package/dist/vendor/cli/commands/init.d.ts +8 -0
  47. package/dist/vendor/cli/commands/init.js +281 -0
  48. package/dist/vendor/cli/commands/migration.d.ts +8 -0
  49. package/dist/vendor/cli/commands/migration.js +67 -0
  50. package/dist/vendor/cli/commands/prior.d.ts +23 -0
  51. package/dist/vendor/cli/commands/prior.js +145 -0
  52. package/dist/vendor/cli/commands/resume.d.ts +21 -0
  53. package/dist/vendor/cli/commands/resume.js +73 -0
  54. package/dist/vendor/cli/commands/verify.d.ts +6 -0
  55. package/dist/vendor/cli/commands/verify.js +43 -0
  56. package/dist/vendor/cli/index.d.ts +6 -1
  57. package/dist/vendor/cli/index.js +124 -7
  58. package/dist/vendor/cli/research/public-corpus.d.ts +43 -0
  59. package/dist/vendor/cli/research/public-corpus.js +151 -0
  60. package/dist/vendor/cli/ui/error-card.d.ts +38 -0
  61. package/dist/vendor/cli/ui/error-card.js +103 -0
  62. package/dist/vendor/cli/ui/mission-brief.d.ts +41 -0
  63. package/dist/vendor/cli/ui/mission-brief.js +173 -0
  64. package/dist/vendor/cli/ui/summary-card.d.ts +34 -0
  65. package/dist/vendor/cli/ui/summary-card.js +102 -0
  66. package/dist/vendor/contracts/audit.d.ts +46 -0
  67. package/dist/vendor/contracts/audit.js +360 -0
  68. package/dist/vendor/contracts/index.d.ts +3 -1
  69. package/dist/vendor/contracts/post-phase15.d.ts +240 -0
  70. package/dist/vendor/contracts/post-phase15.js +166 -0
  71. package/dist/vendor/core/agent/mandates.d.ts +46 -0
  72. package/dist/vendor/core/agent/mandates.js +178 -0
  73. package/dist/vendor/core/agent/receipts.d.ts +38 -0
  74. package/dist/vendor/core/agent/receipts.js +131 -0
  75. package/dist/vendor/core/agent/signing.d.ts +17 -0
  76. package/dist/vendor/core/agent/signing.js +91 -0
  77. package/dist/vendor/core/attestation/sign.d.ts +25 -0
  78. package/dist/vendor/core/attestation/sign.js +216 -0
  79. package/dist/vendor/core/autonomy/autonomous-promotion.d.ts +120 -0
  80. package/dist/vendor/core/autonomy/autonomous-promotion.js +346 -0
  81. package/dist/vendor/core/autonomy/envelope-v2.d.ts +29 -0
  82. package/dist/vendor/core/autonomy/envelope-v2.js +60 -0
  83. package/dist/vendor/core/autonomy/envelope.d.ts +17 -0
  84. package/dist/vendor/core/autonomy/envelope.js +27 -0
  85. package/dist/vendor/core/autonomy/escalation-ledger.d.ts +20 -0
  86. package/dist/vendor/core/autonomy/escalation-ledger.js +18 -0
  87. package/dist/vendor/core/autonomy/resume.d.ts +15 -0
  88. package/dist/vendor/core/autonomy/resume.js +23 -0
  89. package/dist/vendor/core/circuit/circuit-breaker.d.ts +60 -0
  90. package/dist/vendor/core/circuit/circuit-breaker.js +143 -0
  91. package/dist/vendor/core/compiler.d.ts +2 -0
  92. package/dist/vendor/core/compiler.js +10 -4
  93. package/dist/vendor/core/context-distillation.d.ts +3 -0
  94. package/dist/vendor/core/context-distillation.js +44 -0
  95. package/dist/vendor/core/context-flow/compile-context.d.ts +8 -0
  96. package/dist/vendor/core/context-flow/compile-context.js +111 -0
  97. package/dist/vendor/core/context-flow/entities.d.ts +2 -0
  98. package/dist/vendor/core/context-flow/entities.js +44 -0
  99. package/dist/vendor/core/context-flow/evaluate-policy.d.ts +2 -0
  100. package/dist/vendor/core/context-flow/evaluate-policy.js +42 -0
  101. package/dist/vendor/core/context-flow/index.d.ts +11 -0
  102. package/dist/vendor/core/context-flow/index.js +24 -0
  103. package/dist/vendor/core/context-flow/labels.d.ts +3 -0
  104. package/dist/vendor/core/context-flow/labels.js +17 -0
  105. package/dist/vendor/core/context-flow/normalizer.d.ts +9 -0
  106. package/dist/vendor/core/context-flow/normalizer.js +69 -0
  107. package/dist/vendor/core/context-flow/profiles.d.ts +33 -0
  108. package/dist/vendor/core/context-flow/profiles.js +36 -0
  109. package/dist/vendor/core/context-flow/redaction.d.ts +1 -0
  110. package/dist/vendor/core/context-flow/redaction.js +6 -0
  111. package/dist/vendor/core/context-flow/sensitivity.d.ts +2 -0
  112. package/dist/vendor/core/context-flow/sensitivity.js +27 -0
  113. package/dist/vendor/core/context-flow/sync-preview.d.ts +2 -0
  114. package/dist/vendor/core/context-flow/sync-preview.js +22 -0
  115. package/dist/vendor/core/context-flow/token-estimator.d.ts +3 -0
  116. package/dist/vendor/core/context-flow/token-estimator.js +13 -0
  117. package/dist/vendor/core/context-flow/types.d.ts +91 -0
  118. package/dist/vendor/core/context-flow/types.js +2 -0
  119. package/dist/vendor/core/context-integrity.d.ts +26 -0
  120. package/dist/vendor/core/context-integrity.js +56 -0
  121. package/dist/vendor/core/context-utility.d.ts +47 -0
  122. package/dist/vendor/core/context-utility.js +405 -0
  123. package/dist/vendor/core/cost/pipeline.d.ts +92 -0
  124. package/dist/vendor/core/cost/pipeline.js +141 -0
  125. package/dist/vendor/core/cost/tagged-cost.d.ts +27 -0
  126. package/dist/vendor/core/cost/tagged-cost.js +55 -0
  127. package/dist/vendor/core/cost-governor.d.ts +2 -0
  128. package/dist/vendor/core/cost-governor.js +50 -0
  129. package/dist/vendor/core/cve/cve-check.d.ts +80 -0
  130. package/dist/vendor/core/cve/cve-check.js +172 -0
  131. package/dist/vendor/core/digital-twin/index.d.ts +27 -0
  132. package/dist/vendor/core/digital-twin/index.js +90 -0
  133. package/dist/vendor/core/drift/drift-graph.d.ts +47 -0
  134. package/dist/vendor/core/drift/drift-graph.js +100 -0
  135. package/dist/vendor/core/drift/objective-lock.d.ts +69 -0
  136. package/dist/vendor/core/drift/objective-lock.js +88 -0
  137. package/dist/vendor/core/drift/scope.d.ts +46 -0
  138. package/dist/vendor/core/drift/scope.js +102 -0
  139. package/dist/vendor/core/drift/signature-lock.d.ts +48 -0
  140. package/dist/vendor/core/drift/signature-lock.js +202 -0
  141. package/dist/vendor/core/drift/stale-proof-gate.d.ts +21 -0
  142. package/dist/vendor/core/drift/stale-proof-gate.js +19 -0
  143. package/dist/vendor/core/eval/known-bad-world-runner.d.ts +24 -0
  144. package/dist/vendor/core/eval/known-bad-world-runner.js +256 -0
  145. package/dist/vendor/core/evidence/claim-audit.d.ts +18 -0
  146. package/dist/vendor/core/evidence/claim-audit.js +89 -0
  147. package/dist/vendor/core/exit-intelligence.d.ts +2 -0
  148. package/dist/vendor/core/exit-intelligence.js +58 -0
  149. package/dist/vendor/core/explain/formatter.d.ts +42 -0
  150. package/dist/vendor/core/explain/formatter.js +171 -0
  151. package/dist/vendor/core/explain/timeline.d.ts +29 -0
  152. package/dist/vendor/core/explain/timeline.js +213 -0
  153. package/dist/vendor/core/failure-taxonomy.d.ts +2 -0
  154. package/dist/vendor/core/failure-taxonomy.js +76 -0
  155. package/dist/vendor/core/gateway/index.d.ts +10 -0
  156. package/dist/vendor/core/gateway/index.js +12 -0
  157. package/dist/vendor/core/gateway/registry.d.ts +40 -0
  158. package/dist/vendor/core/gateway/registry.js +97 -0
  159. package/dist/vendor/core/gateway/transport.d.ts +31 -0
  160. package/dist/vendor/core/gateway/transport.js +82 -0
  161. package/dist/vendor/core/gateway/vault.d.ts +19 -0
  162. package/dist/vendor/core/gateway/vault.js +29 -0
  163. package/dist/vendor/core/graph/adapters.d.ts +43 -0
  164. package/dist/vendor/core/graph/adapters.js +91 -0
  165. package/dist/vendor/core/graph/hotspots.d.ts +22 -0
  166. package/dist/vendor/core/graph/hotspots.js +30 -0
  167. package/dist/vendor/core/graph/index.d.ts +1 -0
  168. package/dist/vendor/core/graph/index.js +2 -0
  169. package/dist/vendor/core/honey/honey-tokens.d.ts +32 -0
  170. package/dist/vendor/core/honey/honey-tokens.js +44 -0
  171. package/dist/vendor/core/index.d.ts +7 -4
  172. package/dist/vendor/core/index.js +222 -64
  173. package/dist/vendor/core/learning/bayesian-update.d.ts +31 -0
  174. package/dist/vendor/core/learning/bayesian-update.js +60 -0
  175. package/dist/vendor/core/learning/prior-sets.d.ts +42 -0
  176. package/dist/vendor/core/learning/prior-sets.js +111 -0
  177. package/dist/vendor/core/learning/promotion-gate.d.ts +17 -0
  178. package/dist/vendor/core/learning/promotion-gate.js +23 -0
  179. package/dist/vendor/core/leash/blast-radius.d.ts +42 -0
  180. package/dist/vendor/core/leash/blast-radius.js +156 -0
  181. package/dist/vendor/core/leash/policy-leash.d.ts +31 -0
  182. package/dist/vendor/core/leash/policy-leash.js +117 -0
  183. package/dist/vendor/core/memo/memo.d.ts +63 -0
  184. package/dist/vendor/core/memo/memo.js +97 -0
  185. package/dist/vendor/core/memory/learning-pipeline.d.ts +154 -0
  186. package/dist/vendor/core/memory/learning-pipeline.js +391 -0
  187. package/dist/vendor/core/memory/palace.d.ts +84 -0
  188. package/dist/vendor/core/memory/palace.js +379 -0
  189. package/dist/vendor/core/merge/ast-merge.d.ts +22 -0
  190. package/dist/vendor/core/merge/ast-merge.js +350 -0
  191. package/dist/vendor/core/merge/text-merge.d.ts +12 -0
  192. package/dist/vendor/core/merge/text-merge.js +182 -0
  193. package/dist/vendor/core/otel/tracer.d.ts +45 -0
  194. package/dist/vendor/core/otel/tracer.js +116 -0
  195. package/dist/vendor/core/parallel/parallel-attempts.d.ts +28 -0
  196. package/dist/vendor/core/parallel/parallel-attempts.js +41 -0
  197. package/dist/vendor/core/parallel/scorer.d.ts +24 -0
  198. package/dist/vendor/core/parallel/scorer.js +65 -0
  199. package/dist/vendor/core/pattern-detection.d.ts +64 -0
  200. package/dist/vendor/core/pattern-detection.js +108 -0
  201. package/dist/vendor/core/persistence/checkpoint.d.ts +44 -0
  202. package/dist/vendor/core/persistence/checkpoint.js +156 -0
  203. package/dist/vendor/core/persistence/cleanup.d.ts +22 -0
  204. package/dist/vendor/core/persistence/cleanup.js +131 -0
  205. package/dist/vendor/core/persistence/index.d.ts +2 -0
  206. package/dist/vendor/core/persistence/index.js +1 -0
  207. package/dist/vendor/core/persistence/runs-reader.d.ts +52 -0
  208. package/dist/vendor/core/persistence/runs-reader.js +84 -0
  209. package/dist/vendor/core/persistence/store.d.ts +6 -1
  210. package/dist/vendor/core/persistence/store.js +5 -0
  211. package/dist/vendor/core/policy/file-touch-quota.d.ts +60 -0
  212. package/dist/vendor/core/policy/file-touch-quota.js +105 -0
  213. package/dist/vendor/core/policy/policy-loader.d.ts +30 -0
  214. package/dist/vendor/core/policy/policy-loader.js +170 -0
  215. package/dist/vendor/core/policy/policy-schema.d.ts +55 -0
  216. package/dist/vendor/core/policy/policy-schema.js +78 -0
  217. package/dist/vendor/core/policy.d.ts +6 -0
  218. package/dist/vendor/core/probe/probe.d.ts +49 -0
  219. package/dist/vendor/core/probe/probe.js +115 -0
  220. package/dist/vendor/core/proof/patch-proof.d.ts +58 -0
  221. package/dist/vendor/core/proof/patch-proof.js +84 -0
  222. package/dist/vendor/core/proof/semantic-probe.d.ts +25 -0
  223. package/dist/vendor/core/proof/semantic-probe.js +82 -0
  224. package/dist/vendor/core/recovery/failure-mode-runner.d.ts +29 -0
  225. package/dist/vendor/core/recovery/failure-mode-runner.js +39 -0
  226. package/dist/vendor/core/red-blue/red-phase.d.ts +64 -0
  227. package/dist/vendor/core/red-blue/red-phase.js +141 -0
  228. package/dist/vendor/core/red-blue/risk-tiers.d.ts +22 -0
  229. package/dist/vendor/core/red-blue/risk-tiers.js +33 -0
  230. package/dist/vendor/core/replay/replay.d.ts +85 -0
  231. package/dist/vendor/core/replay/replay.js +109 -0
  232. package/dist/vendor/core/router/engine.d.ts +54 -0
  233. package/dist/vendor/core/router/engine.js +131 -0
  234. package/dist/vendor/core/router/index.d.ts +1 -0
  235. package/dist/vendor/core/router/index.js +2 -0
  236. package/dist/vendor/core/router/trust-calibration.d.ts +57 -0
  237. package/dist/vendor/core/router/trust-calibration.js +127 -0
  238. package/dist/vendor/core/run-martin.d.ts +2 -0
  239. package/dist/vendor/core/run-martin.js +287 -0
  240. package/dist/vendor/core/security/cve-scanner.d.ts +62 -0
  241. package/dist/vendor/core/security/cve-scanner.js +178 -0
  242. package/dist/vendor/core/sentinel/efficiency-sentinel.d.ts +29 -0
  243. package/dist/vendor/core/sentinel/efficiency-sentinel.js +30 -0
  244. package/dist/vendor/core/sentinel/progress-guard.d.ts +35 -0
  245. package/dist/vendor/core/sentinel/progress-guard.js +46 -0
  246. package/dist/vendor/core/siem/siem-emitter.d.ts +49 -0
  247. package/dist/vendor/core/siem/siem-emitter.js +157 -0
  248. package/dist/vendor/core/strategy/attempt-brief.d.ts +22 -0
  249. package/dist/vendor/core/strategy/attempt-brief.js +89 -0
  250. package/dist/vendor/core/summarize/diff-summary.d.ts +35 -0
  251. package/dist/vendor/core/summarize/diff-summary.js +204 -0
  252. package/dist/vendor/core/surface-signals.d.ts +21 -0
  253. package/dist/vendor/core/surface-signals.js +139 -0
  254. package/dist/vendor/core/truth/truth-wall.d.ts +51 -0
  255. package/dist/vendor/core/truth/truth-wall.js +69 -0
  256. package/dist/vendor/core/truth-spine.d.ts +26 -0
  257. package/dist/vendor/core/truth-spine.js +62 -0
  258. package/dist/vendor/core/types.d.ts +115 -0
  259. package/dist/vendor/core/types.js +2 -0
  260. package/dist/vendor/core/verification/tiered-verify.d.ts +17 -0
  261. package/dist/vendor/core/verification/tiered-verify.js +29 -0
  262. package/dist/vendor/core/verifier-pyramid.d.ts +32 -0
  263. package/dist/vendor/core/verifier-pyramid.js +111 -0
  264. package/dist/vendor/core/workflow-artifacts.d.ts +99 -0
  265. package/dist/vendor/core/workflow-artifacts.js +668 -0
  266. package/dist/vendor/core/wrap/supervised-run.d.ts +96 -0
  267. package/dist/vendor/core/wrap/supervised-run.js +178 -0
  268. package/docs/assets/cli-animated.svg +139 -0
  269. package/docs/assets/cli-static.svg +34 -0
  270. package/docs/assets/github-hero-v2.svg +23 -0
  271. package/docs/assets/martin-raplph.png.jpg +0 -0
  272. package/docs/assets/martinloop-logo.png +0 -0
  273. package/docs/assets/nvidia-inception-program-light.png +0 -0
  274. package/docs/assets/nvidia-inception-program.png +0 -0
  275. package/docs/assets/phase3c-sidesidebyside-demo.html +228 -0
  276. package/docs/assets/side-by-side.svg +134 -0
  277. package/docs/oss/CLAUDE-CODE-WALKTHROUGH.md +142 -0
  278. package/docs/oss/EXAMPLES.md +9 -1
  279. package/docs/oss/OSS-BOUNDARY-REPORT.json +109 -113
  280. package/docs/oss/OSS-BOUNDARY-REPORT.md +48 -48
  281. package/docs/oss/QUICKSTART.md +39 -4
  282. package/docs/oss/RALPH-LOOP-SAFETY.md +113 -0
  283. package/docs/oss/README.md +7 -4
  284. package/docs/oss/RELEASE-SURFACE-REPORT.json +46 -45
  285. package/docs/oss/RELEASE-SURFACE-REPORT.md +36 -35
  286. package/package.json +129 -49
@@ -0,0 +1,204 @@
1
+ /**
2
+ * diff-summary.ts
3
+ *
4
+ * Produces a one-line human-readable summary of a unified diff relative to the
5
+ * loop objective. Uses a cheap haiku-tier LLM call when an adapter is available;
6
+ * falls back to a deterministic heuristic when no adapter is configured or the
7
+ * model call fails.
8
+ *
9
+ * Quality gate: the generated one-liner MUST contain at least one concrete
10
+ * identifier (file name, function name, symbol) extracted from the diff itself.
11
+ * If the model output fails that gate the fallback is used instead.
12
+ */
13
+ // ---------------------------------------------------------------------------
14
+ // Public entry point
15
+ // ---------------------------------------------------------------------------
16
+ /**
17
+ * Summarize a unified diff in the context of a loop objective.
18
+ *
19
+ * @param input Diff text + objective string.
20
+ * @param callModelFn Optional async function that accepts a prompt string and
21
+ * returns a model response string. When omitted, or when
22
+ * the call throws, the deterministic heuristic is used.
23
+ */
24
+ export async function summarizeDiff(input, callModelFn) {
25
+ const anchor = extractAnchorIdentifier(input.diff);
26
+ if (callModelFn && input.diff.trim().length > 0) {
27
+ try {
28
+ const raw = await callModelFn(buildPrompt(input, anchor));
29
+ const cleaned = cleanModelOutput(raw);
30
+ if (isQualityGatePassing(cleaned, anchor)) {
31
+ return { oneLiner: cleaned, method: "model", anchorIdentifier: anchor };
32
+ }
33
+ // Model output failed quality gate — fall through to heuristic
34
+ }
35
+ catch {
36
+ // Non-fatal — fall through to heuristic
37
+ }
38
+ }
39
+ return {
40
+ oneLiner: buildHeuristicSummary(input, anchor),
41
+ method: "heuristic",
42
+ anchorIdentifier: anchor
43
+ };
44
+ }
45
+ // ---------------------------------------------------------------------------
46
+ // Prompt construction
47
+ // ---------------------------------------------------------------------------
48
+ function buildPrompt(input, anchor) {
49
+ // Keep the diff excerpt short — we only need enough for the model to name
50
+ // the primary change. Hard cap at 3000 chars to stay within haiku context.
51
+ const diffExcerpt = input.diff.length > 3000
52
+ ? `${input.diff.slice(0, 2900)}\n... (truncated)`
53
+ : input.diff;
54
+ return [
55
+ "You are a precise technical writer generating a single-line git commit summary.",
56
+ "",
57
+ `Objective: ${input.objective}`,
58
+ "",
59
+ "Diff:",
60
+ "```",
61
+ diffExcerpt,
62
+ "```",
63
+ "",
64
+ `The summary MUST mention "${anchor}" or a closely related identifier.`,
65
+ "Respond with ONLY the one-line summary — no preamble, no punctuation at the end.",
66
+ "Keep it under 72 characters.",
67
+ "Format: <verb in imperative mood> <what changed> in <where>",
68
+ "Example: add retry guard to fetchWithTimeout in http-client.ts"
69
+ ].join("\n");
70
+ }
71
+ // ---------------------------------------------------------------------------
72
+ // Quality gate
73
+ // ---------------------------------------------------------------------------
74
+ /**
75
+ * Returns true when the model output contains the anchor identifier or a
76
+ * plausible stem of it (e.g. "retry" from "retryCount").
77
+ */
78
+ function isQualityGatePassing(summary, anchor) {
79
+ if (summary.trim().length === 0)
80
+ return false;
81
+ if (summary.length > 120)
82
+ return false; // unreasonably long
83
+ const lowerSummary = summary.toLowerCase();
84
+ const lowerAnchor = anchor.toLowerCase();
85
+ // Direct match
86
+ if (lowerSummary.includes(lowerAnchor))
87
+ return true;
88
+ // Stem match: try the first 6+ chars of the anchor
89
+ const stem = lowerAnchor.slice(0, Math.max(6, Math.floor(lowerAnchor.length * 0.6)));
90
+ if (stem.length >= 4 && lowerSummary.includes(stem))
91
+ return true;
92
+ return false;
93
+ }
94
+ // ---------------------------------------------------------------------------
95
+ // Deterministic heuristic
96
+ // ---------------------------------------------------------------------------
97
+ /**
98
+ * Builds a summary without a model call by inspecting the diff structure.
99
+ * Always passes the quality gate because we embed the anchor directly.
100
+ */
101
+ function buildHeuristicSummary(input, anchor) {
102
+ const stats = parseDiffStats(input.diff);
103
+ const verb = selectVerb(stats);
104
+ const filesChanged = stats.files.length;
105
+ if (filesChanged === 0) {
106
+ return `${verb} changes related to ${anchor}`;
107
+ }
108
+ if (filesChanged === 1) {
109
+ const file = stats.files[0] ?? "change";
110
+ const location = stripPath(file);
111
+ return `${verb} ${anchor} in ${location}`;
112
+ }
113
+ const primary = stripPath(stats.files[0] ?? "change");
114
+ return `${verb} ${anchor} across ${filesChanged} files (primary: ${primary})`;
115
+ }
116
+ // ---------------------------------------------------------------------------
117
+ // Anchor identifier extraction
118
+ // ---------------------------------------------------------------------------
119
+ /**
120
+ * Extracts the most informative concrete identifier from a unified diff.
121
+ *
122
+ * Priority order:
123
+ * 1. First function/method name from a hunk header (@@...@@ <name>)
124
+ * 2. First added symbol-looking token from a + line
125
+ * 3. First changed file base name (no extension)
126
+ * 4. Literal "change" as a safe fallback
127
+ */
128
+ function extractAnchorIdentifier(diff) {
129
+ // 1. Hunk header function hint:
130
+ // @@ -l,n +l,n @@ function validateToken(
131
+ const hunkHeaderFn = diff.match(/@@[^@]+@@\s+(?:(?:export|async)\s+)*(?:function\s+)?(\w[\w.]*)\s*\(/u);
132
+ if (hunkHeaderFn?.[1] && hunkHeaderFn[1].length >= 3) {
133
+ return hunkHeaderFn[1];
134
+ }
135
+ // 2. First added function/method-like declaration. This intentionally
136
+ // precedes local const extraction so a method body's first local variable
137
+ // does not become the summary anchor.
138
+ const addedCallable = diff.match(/^\+[^+].*?\b(?:export\s+)?(?:async\s+)?(?:function\s+)?(\w[\w]*)\s*\(/mu);
139
+ if (addedCallable?.[1] && addedCallable[1].length >= 3) {
140
+ return addedCallable[1];
141
+ }
142
+ // 3. First export/const/class/type on an added line
143
+ const addedSymbol = diff.match(/^\+[^+].*?\b(?:export\s+)?(?:const|let|var|class|interface|type)\s+(\w[\w]*)/mu);
144
+ if (addedSymbol?.[1] && addedSymbol[1].length >= 3) {
145
+ return addedSymbol[1];
146
+ }
147
+ // 4. First changed file name from diff --git a/<path> b/<path>
148
+ const fileMatch = diff.match(/^diff --git a\/(.+?) b\//mu);
149
+ if (fileMatch?.[1]) {
150
+ const base = fileMatch[1].split("/").pop() ?? fileMatch[1];
151
+ return base.replace(/\.\w+$/, ""); // strip extension
152
+ }
153
+ // 5. First +++ b/ file name
154
+ const plusFile = diff.match(/^\+\+\+ b\/(.+)$/mu);
155
+ if (plusFile?.[1]) {
156
+ const base = plusFile[1].split("/").pop() ?? plusFile[1];
157
+ return base.replace(/\.\w+$/, "");
158
+ }
159
+ return "change";
160
+ }
161
+ function parseDiffStats(diff) {
162
+ const files = [];
163
+ let addedLines = 0;
164
+ let removedLines = 0;
165
+ for (const line of diff.split("\n")) {
166
+ if (line.startsWith("diff --git ")) {
167
+ const match = line.match(/^diff --git a\/(.+?) b\//u);
168
+ if (match?.[1])
169
+ files.push(match[1]);
170
+ }
171
+ else if (line.startsWith("+") && !line.startsWith("+++")) {
172
+ addedLines += 1;
173
+ }
174
+ else if (line.startsWith("-") && !line.startsWith("---")) {
175
+ removedLines += 1;
176
+ }
177
+ }
178
+ return { files, addedLines, removedLines };
179
+ }
180
+ function selectVerb(stats) {
181
+ if (stats.addedLines > 0 && stats.removedLines === 0)
182
+ return "add";
183
+ if (stats.removedLines > 0 && stats.addedLines === 0)
184
+ return "remove";
185
+ if (stats.addedLines > stats.removedLines * 3)
186
+ return "add";
187
+ if (stats.removedLines > stats.addedLines * 3)
188
+ return "remove";
189
+ return "update";
190
+ }
191
+ function stripPath(filePath) {
192
+ return filePath.split("/").pop() ?? filePath;
193
+ }
194
+ // ---------------------------------------------------------------------------
195
+ // Model output cleanup
196
+ // ---------------------------------------------------------------------------
197
+ function cleanModelOutput(raw) {
198
+ return raw
199
+ .trim()
200
+ .replace(/^["']|["']$/g, "") // strip surrounding quotes
201
+ .replace(/\.+$/, "") // strip trailing dots
202
+ .replace(/\s+/g, " "); // collapse whitespace
203
+ }
204
+ //# sourceMappingURL=diff-summary.js.map
@@ -0,0 +1,21 @@
1
+ import type { LoopAttempt } from "../contracts/index.js";
2
+ export interface SurfaceSignalInput {
3
+ objective?: string;
4
+ verificationPlan?: string[];
5
+ summary?: string;
6
+ failureMessage?: string;
7
+ diff?: string;
8
+ changedFiles?: string[];
9
+ previousAttempts?: Array<Pick<LoopAttempt, "summary">>;
10
+ }
11
+ export interface SurfaceSignals {
12
+ dependencyTouchedFileCount: number;
13
+ migrationTouchedFileCount: number;
14
+ mergeConflictSignalCount: number;
15
+ ambiguitySignalCount: number;
16
+ observabilityNoiseSignalCount: number;
17
+ workspaceGraphRiskScore: number;
18
+ crossBoundaryRiskScore: number;
19
+ }
20
+ export declare function analyzeLoopSurface(input: SurfaceSignalInput): SurfaceSignals;
21
+ export declare function buildSurfaceGuidance(input: SurfaceSignalInput): string[];
@@ -0,0 +1,139 @@
1
+ export function analyzeLoopSurface(input) {
2
+ const changedFiles = (input.changedFiles ?? []).map(normalizePath);
3
+ const narrative = [
4
+ input.objective,
5
+ input.summary,
6
+ input.failureMessage,
7
+ input.diff,
8
+ ...(input.verificationPlan ?? []),
9
+ ...(input.previousAttempts?.map((attempt) => attempt.summary) ?? [])
10
+ ]
11
+ .filter(Boolean)
12
+ .join("\n")
13
+ .toLowerCase();
14
+ const dependencyTouchedFileCount = changedFiles.filter(isDependencySurface).length;
15
+ const migrationTouchedFileCount = changedFiles.filter(isMigrationSurface).length;
16
+ const mergeConflictSignalCount = countPhraseMatches(narrative, [
17
+ "merge conflict",
18
+ "conflict marker",
19
+ "conflict markers",
20
+ "rebase",
21
+ "cherry-pick",
22
+ "<<<<<<<",
23
+ ">>>>>>>"
24
+ ]) + (input.diff?.match(/^(<{7}|={7}|>{7})/gmu)?.length ?? 0);
25
+ const workspaceGraphSignals = countPhraseMatches(narrative, [
26
+ "monorepo",
27
+ "workspace graph",
28
+ "dependency graph",
29
+ "graph-wide",
30
+ "cross-package",
31
+ "package boundaries",
32
+ "downstream packages",
33
+ "transitive dependency",
34
+ "transitive toolchain",
35
+ "pnpm -r",
36
+ "workspace:",
37
+ "package manager",
38
+ "lockfile"
39
+ ]) + dependencyTouchedFileCount;
40
+ const crossBoundarySignals = countPhraseMatches(narrative, [
41
+ "cross-system",
42
+ "cross-package",
43
+ "cross-service",
44
+ "service boundary",
45
+ "producer-consumer",
46
+ "contract boundary",
47
+ "data contract",
48
+ "schema drift",
49
+ "external dependency",
50
+ "package boundaries"
51
+ ]) + migrationTouchedFileCount;
52
+ const ambiguitySignalCount = countPhraseMatches(narrative, [
53
+ "ambiguous",
54
+ "acceptance target",
55
+ "moving target",
56
+ "moving expectation",
57
+ "underspecified",
58
+ "seed-data divergence",
59
+ "oauth callback",
60
+ "callback loop",
61
+ "unclear ground truth",
62
+ "requirement boundary"
63
+ ]);
64
+ const observabilityNoiseSignalCount = countPhraseMatches(narrative, [
65
+ "alert-threshold",
66
+ "alert threshold",
67
+ "threshold-driven",
68
+ "flapping alert",
69
+ "flapping alerts",
70
+ "alert storm",
71
+ "signal-to-noise",
72
+ "noisy alert",
73
+ "noise"
74
+ ]);
75
+ return {
76
+ dependencyTouchedFileCount,
77
+ migrationTouchedFileCount,
78
+ mergeConflictSignalCount,
79
+ ambiguitySignalCount,
80
+ observabilityNoiseSignalCount,
81
+ workspaceGraphRiskScore: roundScore(Math.min(1, workspaceGraphSignals / 4)),
82
+ crossBoundaryRiskScore: roundScore(Math.min(1, crossBoundarySignals / 4))
83
+ };
84
+ }
85
+ export function buildSurfaceGuidance(input) {
86
+ const signals = analyzeLoopSurface(input);
87
+ const guidance = [];
88
+ if (signals.dependencyTouchedFileCount > 0 || signals.workspaceGraphRiskScore >= 0.5) {
89
+ guidance.push("Treat this as a dependency or workspace-graph boundary issue. Prefer one package or contract seam at a time, verify install/typecheck before broad build fan-out, and avoid graph-wide surgery unless the verifier proves the boundary is local.");
90
+ }
91
+ if (signals.migrationTouchedFileCount > 0 || signals.crossBoundaryRiskScore >= 0.5) {
92
+ guidance.push("Treat migration and data-contract edits as boundary work. Change one producer/consumer or migration seam at a time and verify the contract boundary before broad cleanup.");
93
+ }
94
+ if (signals.mergeConflictSignalCount > 0) {
95
+ guidance.push("Resolve the conflict surface first. Do not stack new edits on top of unresolved conflict markers; refresh the base, resolve the minimal conflicting files, then rerun verification.");
96
+ }
97
+ if (signals.ambiguitySignalCount > 0) {
98
+ guidance.push("Treat this as an acceptance-boundary issue. Freeze one explicit expected behavior, verify the smallest canonical case, and escalate once the target keeps moving.");
99
+ }
100
+ if (signals.observabilityNoiseSignalCount > 0) {
101
+ guidance.push("Treat this as an alert-noise issue. Adjust the smallest threshold seam or noise filter first, then re-verify signal quality before widening the observability patch.");
102
+ }
103
+ return guidance;
104
+ }
105
+ function normalizePath(file) {
106
+ return file.replace(/\\/gu, "/").toLowerCase();
107
+ }
108
+ function isDependencySurface(file) {
109
+ return (file === "package.json" ||
110
+ file.endsWith("/package.json") ||
111
+ file.endsWith("/pnpm-lock.yaml") ||
112
+ file === "pnpm-lock.yaml" ||
113
+ file.endsWith("/package-lock.json") ||
114
+ file === "package-lock.json" ||
115
+ file.endsWith("/yarn.lock") ||
116
+ file === "yarn.lock" ||
117
+ file.endsWith("/turbo.json") ||
118
+ file === "turbo.json");
119
+ }
120
+ function isMigrationSurface(file) {
121
+ return (file.includes("/migrations/") ||
122
+ file.startsWith("migrations/") ||
123
+ file.includes("prisma/migrations/") ||
124
+ file.endsWith("/schema.prisma") ||
125
+ file.endsWith("/supabase/config.toml"));
126
+ }
127
+ function countPhraseMatches(haystack, phrases) {
128
+ return phrases.reduce((count, phrase) => {
129
+ const matches = haystack.match(new RegExp(escapeRegExp(phrase), "gu"));
130
+ return count + (matches?.length ?? 0);
131
+ }, 0);
132
+ }
133
+ function escapeRegExp(value) {
134
+ return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
135
+ }
136
+ function roundScore(value) {
137
+ return Math.round(value * 100) / 100;
138
+ }
139
+ //# sourceMappingURL=surface-signals.js.map
@@ -0,0 +1,51 @@
1
+ export interface SettledFact {
2
+ factId: string;
3
+ content: string;
4
+ settledAt: string;
5
+ }
6
+ export interface TruthWall {
7
+ readonly runId: string;
8
+ readonly builtAt: string;
9
+ readonly facts: readonly SettledFact[];
10
+ }
11
+ export interface Hypothesis {
12
+ hypothesisId: string;
13
+ runId: string;
14
+ rootCause: string;
15
+ recordedAt: string;
16
+ testResult: "validated" | "invalidated" | "pending";
17
+ confidenceDelta: number;
18
+ }
19
+ export interface HypothesisLedger {
20
+ hypotheses: Hypothesis[];
21
+ }
22
+ export interface TruthWallInput {
23
+ runId: string;
24
+ facts: SettledFact[];
25
+ runDir: string;
26
+ }
27
+ /**
28
+ * Builds an immutable TruthWall from settled ledger facts at run start.
29
+ * Writes truth-wall.json to the run directory and freezes the returned object.
30
+ */
31
+ export declare function buildTruthWall(input: TruthWallInput): Promise<TruthWall>;
32
+ /**
33
+ * Creates an ephemeral mutable Map for scratch-space state during a run.
34
+ * Writes do NOT propagate to the truth wall.
35
+ */
36
+ export declare function createWorkingMemory(): Map<string, unknown>;
37
+ /**
38
+ * Loads an existing hypothesis ledger from the run directory, or returns an empty one.
39
+ */
40
+ export declare function loadHypothesisLedger(runDir: string): Promise<HypothesisLedger>;
41
+ /**
42
+ * Records a hypothesis in the ledger and persists hypothesis-ledger.json.
43
+ */
44
+ export declare function recordHypothesis(ledger: HypothesisLedger, input: Omit<Hypothesis, "recordedAt">, runDir: string): Promise<Hypothesis>;
45
+ /**
46
+ * Emits all three required run artifacts:
47
+ * - truth-wall.json (immutable settled facts)
48
+ * - hypothesis-ledger.json (per-attempt hypotheses)
49
+ * - working-memory.json (mutable scratch, never promoted to truth wall)
50
+ */
51
+ export declare function emitRunArtifacts(runDir: string, wall: TruthWall, ledger: HypothesisLedger, workingMemory: Map<string, unknown>): Promise<void>;
@@ -0,0 +1,69 @@
1
+ import { readFile, writeFile } from "node:fs/promises";
2
+ import { existsSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ // ─── Truth Wall ───────────────────────────────────────────────────────────────
5
+ /**
6
+ * Builds an immutable TruthWall from settled ledger facts at run start.
7
+ * Writes truth-wall.json to the run directory and freezes the returned object.
8
+ */
9
+ export async function buildTruthWall(input) {
10
+ const wall = Object.freeze({
11
+ runId: input.runId,
12
+ builtAt: new Date().toISOString(),
13
+ facts: Object.freeze([...input.facts.map(f => Object.freeze({ ...f }))])
14
+ });
15
+ await writeFile(join(input.runDir, "truth-wall.json"), JSON.stringify(wall, null, 2), "utf8");
16
+ return wall;
17
+ }
18
+ // ─── Working Memory ───────────────────────────────────────────────────────────
19
+ /**
20
+ * Creates an ephemeral mutable Map for scratch-space state during a run.
21
+ * Writes do NOT propagate to the truth wall.
22
+ */
23
+ export function createWorkingMemory() {
24
+ return new Map();
25
+ }
26
+ // ─── Hypothesis Ledger ────────────────────────────────────────────────────────
27
+ /**
28
+ * Loads an existing hypothesis ledger from the run directory, or returns an empty one.
29
+ */
30
+ export async function loadHypothesisLedger(runDir) {
31
+ const ledgerPath = join(runDir, "hypothesis-ledger.json");
32
+ if (existsSync(ledgerPath)) {
33
+ const raw = await readFile(ledgerPath, "utf8");
34
+ return JSON.parse(raw);
35
+ }
36
+ return { hypotheses: [] };
37
+ }
38
+ /**
39
+ * Records a hypothesis in the ledger and persists hypothesis-ledger.json.
40
+ */
41
+ export async function recordHypothesis(ledger, input, runDir) {
42
+ const hypothesis = {
43
+ ...input,
44
+ recordedAt: new Date().toISOString()
45
+ };
46
+ ledger.hypotheses.push(hypothesis);
47
+ await writeFile(join(runDir, "hypothesis-ledger.json"), JSON.stringify(ledger, null, 2), "utf8");
48
+ return hypothesis;
49
+ }
50
+ // ─── Run Artifacts Emitter ────────────────────────────────────────────────────
51
+ /**
52
+ * Emits all three required run artifacts:
53
+ * - truth-wall.json (immutable settled facts)
54
+ * - hypothesis-ledger.json (per-attempt hypotheses)
55
+ * - working-memory.json (mutable scratch, never promoted to truth wall)
56
+ */
57
+ export async function emitRunArtifacts(runDir, wall, ledger, workingMemory) {
58
+ // truth-wall.json was already written by buildTruthWall — only write if missing
59
+ if (!existsSync(join(runDir, "truth-wall.json"))) {
60
+ await writeFile(join(runDir, "truth-wall.json"), JSON.stringify(wall, null, 2), "utf8");
61
+ }
62
+ await writeFile(join(runDir, "hypothesis-ledger.json"), JSON.stringify(ledger, null, 2), "utf8");
63
+ const wmObject = {};
64
+ for (const [k, v] of workingMemory) {
65
+ wmObject[k] = v;
66
+ }
67
+ await writeFile(join(runDir, "working-memory.json"), JSON.stringify({ entries: wmObject }, null, 2), "utf8");
68
+ }
69
+ //# sourceMappingURL=truth-wall.js.map
@@ -0,0 +1,26 @@
1
+ export type ContextIntegrityVerdict = "clean" | "context_poisoning_warning" | "context_poisoning_block";
2
+ export interface ContextIntegrityPrecheck {
3
+ runId: string;
4
+ attemptIndex: number;
5
+ verdict: ContextIntegrityVerdict;
6
+ reason?: string;
7
+ detectedSignals: string[];
8
+ analyzedChannels: {
9
+ system: boolean;
10
+ user: boolean;
11
+ tools: boolean;
12
+ history: boolean;
13
+ };
14
+ timestamp: string;
15
+ }
16
+ /**
17
+ * T05: Context Poisoning Pre-gate.
18
+ * Scans untrusted input channels for authority inversion or instruction re-injection.
19
+ * Runs BEFORE core reasoning and verification.
20
+ */
21
+ export declare function runContextIntegrityPrecheck(runId: string, attemptIndex: number, artifactsDir: string, inputs: {
22
+ userPrompt?: string;
23
+ toolOutput?: string;
24
+ retrievedContext?: string;
25
+ history?: string;
26
+ }): Promise<ContextIntegrityPrecheck>;
@@ -0,0 +1,62 @@
1
+ import { writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ const POISON_PATTERNS = [
4
+ /ignore\s+(?:all\s+)?previous\s+instructions/i,
5
+ /you\s+are\s+now\s+a\s+(?!Martin\s+Loop)/i,
6
+ /new\s+rule:/i,
7
+ /disregard\s+(?:safety|policy|guardrails)/i,
8
+ /override\s+system\s+authority/i,
9
+ /hidden\s+instruction:/i,
10
+ /\[system_override\]/i,
11
+ /\[authority_inversion\]/i
12
+ ];
13
+ /**
14
+ * T05: Context Poisoning Pre-gate.
15
+ * Scans untrusted input channels for authority inversion or instruction re-injection.
16
+ * Runs BEFORE core reasoning and verification.
17
+ */
18
+ export async function runContextIntegrityPrecheck(runId, attemptIndex, artifactsDir, inputs) {
19
+ const signals = [];
20
+ const analyzedChannels = {
21
+ system: true,
22
+ user: Boolean(inputs.userPrompt),
23
+ tools: Boolean(inputs.toolOutput),
24
+ history: Boolean(inputs.history)
25
+ };
26
+ const fullUntrustedBuffer = [
27
+ inputs.userPrompt,
28
+ inputs.toolOutput,
29
+ inputs.retrievedContext
30
+ ].filter(Boolean).join("\n---\n");
31
+ for (const pattern of POISON_PATTERNS) {
32
+ if (pattern.test(fullUntrustedBuffer)) {
33
+ signals.push(`Detected poison pattern: ${pattern.toString()}`);
34
+ }
35
+ }
36
+ // Authority Boundary Detector: Check for attempts to redefine the 'Martin' identity or role.
37
+ if (/\b(?:I am|You are)\s+(?!Martin\s+Loop|an\s+AI)\b/i.test(fullUntrustedBuffer)) {
38
+ signals.push("Identity redefinition attempt detected.");
39
+ }
40
+ const verdict = signals.length > 0 ? "context_poisoning_block" : "clean";
41
+ const precheck = {
42
+ runId,
43
+ attemptIndex,
44
+ verdict,
45
+ reason: signals.length > 0 ? `Detected ${signals.length} poisoning signals.` : undefined,
46
+ detectedSignals: signals,
47
+ analyzedChannels,
48
+ timestamp: new Date().toISOString()
49
+ };
50
+ // Persist artifact: context-integrity-precheck.json
51
+ const artifactPath = join(artifactsDir, "context-integrity-precheck.json");
52
+ try {
53
+ const { mkdir } = await import("node:fs/promises");
54
+ await mkdir(artifactsDir, { recursive: true });
55
+ await writeFile(artifactPath, JSON.stringify(precheck, null, 2), "utf8");
56
+ }
57
+ catch (err) {
58
+ console.error(`Failed to persist context-integrity-precheck: ${err instanceof Error ? err.message : String(err)}`);
59
+ }
60
+ return precheck;
61
+ }
62
+ //# sourceMappingURL=truth-spine.js.map