infraweaver 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (524) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +244 -0
  3. package/dist/agents/claude.d.ts +113 -0
  4. package/dist/agents/claudePretoolGate.d.ts +137 -0
  5. package/dist/agents/dispatchMetrics.d.ts +77 -0
  6. package/dist/agents/gateServer.d.ts +7 -0
  7. package/dist/agents/index.d.ts +6 -0
  8. package/dist/agents/nativeFsDenies.d.ts +46 -0
  9. package/dist/agents/opencode.d.ts +284 -0
  10. package/dist/agents/opencodePlugin.d.ts +96 -0
  11. package/dist/agents/opencodeShared.d.ts +40 -0
  12. package/dist/agents/postRun.d.ts +126 -0
  13. package/dist/agents/reviewer.d.ts +41 -0
  14. package/dist/agents/sessionLabeler.d.ts +97 -0
  15. package/dist/agents/shared.d.ts +246 -0
  16. package/dist/agents/subagentModels.d.ts +19 -0
  17. package/dist/agents/tokenQuota.d.ts +118 -0
  18. package/dist/agents/writeGateSource.d.ts +22 -0
  19. package/dist/agents/writePolicy.d.ts +79 -0
  20. package/dist/brand.d.ts +67 -0
  21. package/dist/cli.mjs +247846 -0
  22. package/dist/external.d.ts +227 -0
  23. package/dist/i18n/scaffolding.d.ts +59 -0
  24. package/dist/index.d.ts +6 -0
  25. package/dist/index.js +247061 -0
  26. package/dist/internal/index.d.ts +18 -0
  27. package/dist/internal.js +2398 -0
  28. package/dist/lifecycle.d.ts +2 -0
  29. package/dist/main.d.ts +8 -0
  30. package/dist/mcp/arkConfig.d.ts +1 -0
  31. package/dist/mcp/assess.d.ts +157 -0
  32. package/dist/mcp/capabilityContext.d.ts +71 -0
  33. package/dist/mcp/changeSummary.d.ts +52 -0
  34. package/dist/mcp/checkSuite.d.ts +27 -0
  35. package/dist/mcp/checkout.d.ts +92 -0
  36. package/dist/mcp/comment.d.ts +127 -0
  37. package/dist/mcp/commitInfo.d.ts +11 -0
  38. package/dist/mcp/crosswalk.d.ts +178 -0
  39. package/dist/mcp/crosswalkDigest.d.ts +1 -0
  40. package/dist/mcp/cyberEssentials.d.ts +24 -0
  41. package/dist/mcp/dashboard.d.ts +125 -0
  42. package/dist/mcp/dependencies.d.ts +12 -0
  43. package/dist/mcp/frameworks.d.ts +74 -0
  44. package/dist/mcp/geminiSanitizer.d.ts +28 -0
  45. package/dist/mcp/git.d.ts +60 -0
  46. package/dist/mcp/guardrails.d.ts +222 -0
  47. package/dist/mcp/issue.d.ts +20 -0
  48. package/dist/mcp/issueComments.d.ts +11 -0
  49. package/dist/mcp/issueEvents.d.ts +11 -0
  50. package/dist/mcp/issueInfo.d.ts +11 -0
  51. package/dist/mcp/labels.d.ts +14 -0
  52. package/dist/mcp/localContext.d.ts +26 -0
  53. package/dist/mcp/moduleExtraction.d.ts +79 -0
  54. package/dist/mcp/moduleTests.d.ts +106 -0
  55. package/dist/mcp/modules.d.ts +198 -0
  56. package/dist/mcp/output.d.ts +16 -0
  57. package/dist/mcp/pathSafety.d.ts +14 -0
  58. package/dist/mcp/policy.d.ts +50 -0
  59. package/dist/mcp/pr.d.ts +64 -0
  60. package/dist/mcp/prInfo.d.ts +11 -0
  61. package/dist/mcp/providerSchema.d.ts +52 -0
  62. package/dist/mcp/review.d.ts +212 -0
  63. package/dist/mcp/reviewComments.d.ts +245 -0
  64. package/dist/mcp/roots.d.ts +60 -0
  65. package/dist/mcp/scope.d.ts +26 -0
  66. package/dist/mcp/selectMode.d.ts +20 -0
  67. package/dist/mcp/server.d.ts +61 -0
  68. package/dist/mcp/shared.d.ts +62 -0
  69. package/dist/mcp/shell.d.ts +58 -0
  70. package/dist/mcp/staleFix.d.ts +138 -0
  71. package/dist/mcp/terraform/azurePrices.d.ts +8 -0
  72. package/dist/mcp/terraform/baseline.d.ts +57 -0
  73. package/dist/mcp/terraform/concernResult.d.ts +38 -0
  74. package/dist/mcp/terraform/cost.d.ts +55 -0
  75. package/dist/mcp/terraform/cspm.d.ts +80 -0
  76. package/dist/mcp/terraform/currency.d.ts +110 -0
  77. package/dist/mcp/terraform/decisions.d.ts +178 -0
  78. package/dist/mcp/terraform/eidas.d.ts +100 -0
  79. package/dist/mcp/terraform/evidence.d.ts +343 -0
  80. package/dist/mcp/terraform/findings.d.ts +76 -0
  81. package/dist/mcp/terraform/fixMemory.d.ts +81 -0
  82. package/dist/mcp/terraform/governance.d.ts +56 -0
  83. package/dist/mcp/terraform/hcl.d.ts +115 -0
  84. package/dist/mcp/terraform/iacLanguages.d.ts +120 -0
  85. package/dist/mcp/terraform/idleFloor.d.ts +78 -0
  86. package/dist/mcp/terraform/keyless.d.ts +68 -0
  87. package/dist/mcp/terraform/moduleDocs.d.ts +56 -0
  88. package/dist/mcp/terraform/nativeRules.d.ts +38 -0
  89. package/dist/mcp/terraform/nativeScan.d.ts +11 -0
  90. package/dist/mcp/terraform/noiseProfile.d.ts +100 -0
  91. package/dist/mcp/terraform/normalization.d.ts +52 -0
  92. package/dist/mcp/terraform/oscal.d.ts +103 -0
  93. package/dist/mcp/terraform/packs/azurerm-3-4.d.ts +2 -0
  94. package/dist/mcp/terraform/packs/cloudflare-4-5.d.ts +2 -0
  95. package/dist/mcp/terraform/paths.d.ts +28 -0
  96. package/dist/mcp/terraform/plan.d.ts +157 -0
  97. package/dist/mcp/terraform/planDrift.d.ts +199 -0
  98. package/dist/mcp/terraform/policyAuthor.d.ts +191 -0
  99. package/dist/mcp/terraform/prowlerOcsf.d.ts +136 -0
  100. package/dist/mcp/terraform/refactor.d.ts +183 -0
  101. package/dist/mcp/terraform/registry.d.ts +108 -0
  102. package/dist/mcp/terraform/risk.d.ts +41 -0
  103. package/dist/mcp/terraform/scanSession.d.ts +116 -0
  104. package/dist/mcp/terraform/scannerCache.d.ts +15 -0
  105. package/dist/mcp/terraform/scannerEgress.d.ts +72 -0
  106. package/dist/mcp/terraform/scannerJson.d.ts +13 -0
  107. package/dist/mcp/terraform/scanners.d.ts +205 -0
  108. package/dist/mcp/terraform/signing.d.ts +61 -0
  109. package/dist/mcp/terraform/stateLocking.d.ts +18 -0
  110. package/dist/mcp/terraform/subprocess.d.ts +43 -0
  111. package/dist/mcp/terraform/suppressions.d.ts +90 -0
  112. package/dist/mcp/terraform/taxonomy.d.ts +39 -0
  113. package/dist/mcp/terraform/tfquery.d.ts +47 -0
  114. package/dist/mcp/terraform/toolchainIntegrity.d.ts +82 -0
  115. package/dist/mcp/terraform/tools/authorPolicy.d.ts +59 -0
  116. package/dist/mcp/terraform/tools/detectPlanDrift.d.ts +20 -0
  117. package/dist/mcp/terraform/tools/emitCost.d.ts +11 -0
  118. package/dist/mcp/terraform/tools/emitSarif.d.ts +14 -0
  119. package/dist/mcp/terraform/tools/fixMemory.d.ts +19 -0
  120. package/dist/mcp/terraform/tools/infracostDiff.d.ts +22 -0
  121. package/dist/mcp/terraform/tools/ingestExternalFindings.d.ts +21 -0
  122. package/dist/mcp/terraform/tools/moduleLookup.d.ts +14 -0
  123. package/dist/mcp/terraform/tools/moduleSearch.d.ts +23 -0
  124. package/dist/mcp/terraform/tools/plan.d.ts +59 -0
  125. package/dist/mcp/terraform/tools/providerUpgrade.d.ts +55 -0
  126. package/dist/mcp/terraform/tools/readFindings.d.ts +17 -0
  127. package/dist/mcp/terraform/tools/scan.d.ts +114 -0
  128. package/dist/mcp/terraform/tools/validate.d.ts +31 -0
  129. package/dist/mcp/terraform/tools/verifyRemediation.d.ts +31 -0
  130. package/dist/mcp/terraform/tools/versionCurrency.d.ts +5 -0
  131. package/dist/mcp/terraform/tools.d.ts +16 -0
  132. package/dist/mcp/terraform/transformPacks.d.ts +267 -0
  133. package/dist/mcp/terraform/types.d.ts +226 -0
  134. package/dist/mcp/terraform/verification.d.ts +81 -0
  135. package/dist/mcp/terraform/vex.d.ts +115 -0
  136. package/dist/mcp/terraform/writeOnly.d.ts +67 -0
  137. package/dist/mcp/terraform.d.ts +51 -0
  138. package/dist/mcp/terratest.d.ts +85 -0
  139. package/dist/mcp/untrustedContent.d.ts +76 -0
  140. package/dist/mcp/upload.d.ts +8 -0
  141. package/dist/models.d.ts +181 -0
  142. package/dist/modes/address-reviews.d.ts +2 -0
  143. package/dist/modes/assess.d.ts +2 -0
  144. package/dist/modes/build.d.ts +2 -0
  145. package/dist/modes/compliance-audit.d.ts +2 -0
  146. package/dist/modes/cost-optimization.d.ts +2 -0
  147. package/dist/modes/drift-detect.d.ts +2 -0
  148. package/dist/modes/fix.d.ts +2 -0
  149. package/dist/modes/incremental-review.d.ts +2 -0
  150. package/dist/modes/index.d.ts +24 -0
  151. package/dist/modes/modernize-deprecated.d.ts +2 -0
  152. package/dist/modes/plan.d.ts +2 -0
  153. package/dist/modes/policy-gate.d.ts +2 -0
  154. package/dist/modes/prFormats.d.ts +3 -0
  155. package/dist/modes/refactor.d.ts +2 -0
  156. package/dist/modes/refresh-remediation.d.ts +2 -0
  157. package/dist/modes/remediate-and-refactor.d.ts +2 -0
  158. package/dist/modes/remediate.d.ts +2 -0
  159. package/dist/modes/resolve-conflicts.d.ts +2 -0
  160. package/dist/modes/review.d.ts +2 -0
  161. package/dist/modes/summarize-pr.d.ts +2 -0
  162. package/dist/modes/task.d.ts +2 -0
  163. package/dist/modes/terraform-code-review.d.ts +2 -0
  164. package/dist/modes/types.d.ts +9 -0
  165. package/dist/modes/update-dependencies.d.ts +2 -0
  166. package/dist/prep/index.d.ts +7 -0
  167. package/dist/prep/installNodeDependencies.d.ts +2 -0
  168. package/dist/prep/installPythonDependencies.d.ts +2 -0
  169. package/dist/prep/types.d.ts +31 -0
  170. package/dist/reviewQuality.d.ts +107 -0
  171. package/dist/skills/terraform-best-practices/SKILL.md +379 -0
  172. package/dist/toolState.d.ts +123 -0
  173. package/dist/utils/activity.d.ts +40 -0
  174. package/dist/utils/agent.d.ts +47 -0
  175. package/dist/utils/agentHangReport.d.ts +38 -0
  176. package/dist/utils/annotations.d.ts +17 -0
  177. package/dist/utils/apiFetch.d.ts +11 -0
  178. package/dist/utils/apiKeys.d.ts +48 -0
  179. package/dist/utils/apiUrl.d.ts +28 -0
  180. package/dist/utils/assets.d.ts +8 -0
  181. package/dist/utils/baseRefConfig.d.ts +121 -0
  182. package/dist/utils/billingErrors.d.ts +85 -0
  183. package/dist/utils/body.d.ts +34 -0
  184. package/dist/utils/buildInfraweaverFooter.d.ts +29 -0
  185. package/dist/utils/byokFallback.d.ts +85 -0
  186. package/dist/utils/changeImpact.d.ts +101 -0
  187. package/dist/utils/claudeSubscription.d.ts +30 -0
  188. package/dist/utils/cli.d.ts +10 -0
  189. package/dist/utils/cloudClient.d.ts +33 -0
  190. package/dist/utils/cloudFetchCore.d.ts +42 -0
  191. package/dist/utils/cloudReport.d.ts +114 -0
  192. package/dist/utils/codexHome.d.ts +29 -0
  193. package/dist/utils/codexOAuth.d.ts +60 -0
  194. package/dist/utils/diffCoverage.d.ts +63 -0
  195. package/dist/utils/env.d.ts +46 -0
  196. package/dist/utils/errorReport.d.ts +17 -0
  197. package/dist/utils/exitHandler.d.ts +8 -0
  198. package/dist/utils/fixDoubleEscapedString.d.ts +1 -0
  199. package/dist/utils/gitAuth.d.ts +84 -0
  200. package/dist/utils/gitAuthServer.d.ts +24 -0
  201. package/dist/utils/github.d.ts +104 -0
  202. package/dist/utils/globals.d.ts +3 -0
  203. package/dist/utils/infraweaverConfig.d.ts +56 -0
  204. package/dist/utils/install.d.ts +37 -0
  205. package/dist/utils/instructions.d.ts +48 -0
  206. package/dist/utils/keylessOidc.d.ts +28 -0
  207. package/dist/utils/leapingComment.d.ts +11 -0
  208. package/dist/utils/learnings.d.ts +62 -0
  209. package/dist/utils/learningsTruncate.d.ts +25 -0
  210. package/dist/utils/lifecycle.d.ts +57 -0
  211. package/dist/utils/log.d.ts +111 -0
  212. package/dist/utils/modelResolver.d.ts +63 -0
  213. package/dist/utils/moduleFetch.d.ts +39 -0
  214. package/dist/utils/normalizeEnv.d.ts +30 -0
  215. package/dist/utils/openCodeModels.d.ts +11 -0
  216. package/dist/utils/openaiCompatible.d.ts +26 -0
  217. package/dist/utils/opentofuMcp.d.ts +50 -0
  218. package/dist/utils/overrides.d.ts +40 -0
  219. package/dist/utils/packageManager.d.ts +49 -0
  220. package/dist/utils/patchWorkflowRunFields.d.ts +29 -0
  221. package/dist/utils/payload.d.ts +249 -0
  222. package/dist/utils/prSummary.d.ts +61 -0
  223. package/dist/utils/presets.d.ts +36 -0
  224. package/dist/utils/progressComment.d.ts +146 -0
  225. package/dist/utils/providerErrors.d.ts +31 -0
  226. package/dist/utils/proxyModel.d.ts +76 -0
  227. package/dist/utils/rangeDiff.d.ts +51 -0
  228. package/dist/utils/redact.d.ts +33 -0
  229. package/dist/utils/registryAuth.d.ts +52 -0
  230. package/dist/utils/remediationCommand.d.ts +58 -0
  231. package/dist/utils/retry.d.ts +38 -0
  232. package/dist/utils/reviewCleanup.d.ts +14 -0
  233. package/dist/utils/run.d.ts +9 -0
  234. package/dist/utils/runContext.d.ts +112 -0
  235. package/dist/utils/runContextData.d.ts +42 -0
  236. package/dist/utils/runErrorRenderer.d.ts +64 -0
  237. package/dist/utils/runLifecycle.d.ts +89 -0
  238. package/dist/utils/runStartupLog.d.ts +15 -0
  239. package/dist/utils/secrets.d.ts +34 -0
  240. package/dist/utils/setup.d.ts +90 -0
  241. package/dist/utils/shell.d.ts +44 -0
  242. package/dist/utils/skills.d.ts +10 -0
  243. package/dist/utils/subprocess.d.ts +81 -0
  244. package/dist/utils/terraformMcp.d.ts +46 -0
  245. package/dist/utils/time.d.ts +15 -0
  246. package/dist/utils/timer.d.ts +23 -0
  247. package/dist/utils/todoTracking.d.ts +16 -0
  248. package/dist/utils/token.d.ts +52 -0
  249. package/dist/utils/toolLicensing.d.ts +56 -0
  250. package/dist/utils/toolSelection.d.ts +73 -0
  251. package/dist/utils/toon.d.ts +16 -0
  252. package/dist/utils/version.d.ts +2 -0
  253. package/dist/utils/versioning.d.ts +7 -0
  254. package/dist/utils/vertex.d.ts +16 -0
  255. package/dist/utils/workflow.d.ts +13 -0
  256. package/package.json +132 -0
  257. package/src/agents/claude.ts +1588 -0
  258. package/src/agents/claudePretoolGate.ts +286 -0
  259. package/src/agents/dispatchMetrics.ts +162 -0
  260. package/src/agents/gateServer.ts +178 -0
  261. package/src/agents/index.ts +10 -0
  262. package/src/agents/nativeFsDenies.ts +164 -0
  263. package/src/agents/opencode.ts +1600 -0
  264. package/src/agents/opencodePlugin.ts +304 -0
  265. package/src/agents/opencodeShared.ts +134 -0
  266. package/src/agents/postRun.ts +615 -0
  267. package/src/agents/reviewer.ts +134 -0
  268. package/src/agents/sessionLabeler.ts +185 -0
  269. package/src/agents/shared.ts +377 -0
  270. package/src/agents/subagentModels.ts +40 -0
  271. package/src/agents/tokenQuota.ts +203 -0
  272. package/src/agents/writeGateSource.ts +148 -0
  273. package/src/agents/writePolicy.ts +132 -0
  274. package/src/brand.ts +84 -0
  275. package/src/cli.ts +117 -0
  276. package/src/commands/gha.ts +188 -0
  277. package/src/commands/mcp.ts +126 -0
  278. package/src/commands/verifyEvidence.ts +100 -0
  279. package/src/entry.ts +7 -0
  280. package/src/entryPost.ts +109 -0
  281. package/src/external.ts +308 -0
  282. package/src/i18n/scaffolding.ts +191 -0
  283. package/src/index.ts +7 -0
  284. package/src/internal/index.ts +74 -0
  285. package/src/lifecycle.ts +2 -0
  286. package/src/main.ts +815 -0
  287. package/src/mcp/__fixtures__/infraweaver-scratch-pr-49-review-3485940013.json +110 -0
  288. package/src/mcp/__fixtures__/infraweaver-scratch-pr-64-review-3531000326.json +14 -0
  289. package/src/mcp/__fixtures__/infraweaver-test-repo-pr-1.diff.json +67 -0
  290. package/src/mcp/__snapshots__/checkout.test.ts.snap +109 -0
  291. package/src/mcp/__snapshots__/reviewComments.test.ts.snap +71 -0
  292. package/src/mcp/arkConfig.ts +7 -0
  293. package/src/mcp/assess.ts +723 -0
  294. package/src/mcp/capabilityContext.ts +80 -0
  295. package/src/mcp/changeSummary.ts +145 -0
  296. package/src/mcp/checkSuite.ts +261 -0
  297. package/src/mcp/checkout.ts +1015 -0
  298. package/src/mcp/comment.ts +683 -0
  299. package/src/mcp/commitInfo.ts +57 -0
  300. package/src/mcp/crosswalk.ts +1253 -0
  301. package/src/mcp/crosswalkDigest.ts +5 -0
  302. package/src/mcp/cyberEssentials.ts +60 -0
  303. package/src/mcp/dashboard.ts +232 -0
  304. package/src/mcp/dependencies.ts +190 -0
  305. package/src/mcp/frameworks.ts +236 -0
  306. package/src/mcp/geminiSanitizer.ts +212 -0
  307. package/src/mcp/git.ts +1116 -0
  308. package/src/mcp/guardrails.ts +771 -0
  309. package/src/mcp/issue.ts +74 -0
  310. package/src/mcp/issueComments.ts +48 -0
  311. package/src/mcp/issueEvents.ts +100 -0
  312. package/src/mcp/issueInfo.ts +72 -0
  313. package/src/mcp/labels.ts +35 -0
  314. package/src/mcp/localContext.ts +65 -0
  315. package/src/mcp/localServer.ts +217 -0
  316. package/src/mcp/moduleExtraction.ts +368 -0
  317. package/src/mcp/moduleTests.ts +421 -0
  318. package/src/mcp/modules.ts +752 -0
  319. package/src/mcp/output.ts +71 -0
  320. package/src/mcp/pathSafety.ts +28 -0
  321. package/src/mcp/policy.ts +226 -0
  322. package/src/mcp/pr.ts +238 -0
  323. package/src/mcp/prInfo.ts +91 -0
  324. package/src/mcp/providerSchema.ts +175 -0
  325. package/src/mcp/review.ts +1081 -0
  326. package/src/mcp/reviewComments.ts +1137 -0
  327. package/src/mcp/roots.ts +217 -0
  328. package/src/mcp/scope.ts +82 -0
  329. package/src/mcp/selectMode.ts +207 -0
  330. package/src/mcp/server.ts +498 -0
  331. package/src/mcp/shared.ts +120 -0
  332. package/src/mcp/shell.ts +631 -0
  333. package/src/mcp/staleFix.ts +557 -0
  334. package/src/mcp/terraform/__snapshots__/moduleDocs.test.ts.snap +40 -0
  335. package/src/mcp/terraform/azurePrices.ts +38 -0
  336. package/src/mcp/terraform/baseline.ts +172 -0
  337. package/src/mcp/terraform/concernResult.ts +111 -0
  338. package/src/mcp/terraform/cost.ts +175 -0
  339. package/src/mcp/terraform/cspm.ts +326 -0
  340. package/src/mcp/terraform/currency.ts +354 -0
  341. package/src/mcp/terraform/decisions.ts +590 -0
  342. package/src/mcp/terraform/eidas.ts +134 -0
  343. package/src/mcp/terraform/evidence.ts +771 -0
  344. package/src/mcp/terraform/findings.ts +383 -0
  345. package/src/mcp/terraform/fixMemory.ts +253 -0
  346. package/src/mcp/terraform/governance.ts +231 -0
  347. package/src/mcp/terraform/hcl.ts +273 -0
  348. package/src/mcp/terraform/iacLanguages.ts +431 -0
  349. package/src/mcp/terraform/idleFloor.ts +304 -0
  350. package/src/mcp/terraform/keyless.ts +123 -0
  351. package/src/mcp/terraform/moduleDocs.ts +303 -0
  352. package/src/mcp/terraform/nativeRules.ts +152 -0
  353. package/src/mcp/terraform/nativeScan.ts +67 -0
  354. package/src/mcp/terraform/noiseProfile.ts +183 -0
  355. package/src/mcp/terraform/normalization.ts +265 -0
  356. package/src/mcp/terraform/oscal.ts +283 -0
  357. package/src/mcp/terraform/packs/azurerm-3-4.ts +382 -0
  358. package/src/mcp/terraform/packs/cloudflare-4-5.ts +114 -0
  359. package/src/mcp/terraform/paths.ts +59 -0
  360. package/src/mcp/terraform/plan.ts +377 -0
  361. package/src/mcp/terraform/planDrift.ts +520 -0
  362. package/src/mcp/terraform/policyAuthor.ts +886 -0
  363. package/src/mcp/terraform/prowlerOcsf.ts +380 -0
  364. package/src/mcp/terraform/refactor.ts +879 -0
  365. package/src/mcp/terraform/registry.ts +317 -0
  366. package/src/mcp/terraform/risk.ts +99 -0
  367. package/src/mcp/terraform/scanSession.ts +242 -0
  368. package/src/mcp/terraform/scannerCache.ts +86 -0
  369. package/src/mcp/terraform/scannerEgress.ts +141 -0
  370. package/src/mcp/terraform/scannerJson.ts +22 -0
  371. package/src/mcp/terraform/scanners.ts +1134 -0
  372. package/src/mcp/terraform/signing.ts +151 -0
  373. package/src/mcp/terraform/stateLocking.ts +106 -0
  374. package/src/mcp/terraform/subprocess.ts +102 -0
  375. package/src/mcp/terraform/suppressions.ts +551 -0
  376. package/src/mcp/terraform/taxonomy.ts +0 -0
  377. package/src/mcp/terraform/tfquery.ts +159 -0
  378. package/src/mcp/terraform/toolchainIntegrity.ts +151 -0
  379. package/src/mcp/terraform/tools/authorPolicy.ts +185 -0
  380. package/src/mcp/terraform/tools/detectPlanDrift.ts +279 -0
  381. package/src/mcp/terraform/tools/emitCost.ts +130 -0
  382. package/src/mcp/terraform/tools/emitSarif.ts +108 -0
  383. package/src/mcp/terraform/tools/fixMemory.ts +82 -0
  384. package/src/mcp/terraform/tools/infracostDiff.ts +110 -0
  385. package/src/mcp/terraform/tools/ingestExternalFindings.ts +259 -0
  386. package/src/mcp/terraform/tools/moduleLookup.ts +86 -0
  387. package/src/mcp/terraform/tools/moduleSearch.ts +46 -0
  388. package/src/mcp/terraform/tools/plan.ts +384 -0
  389. package/src/mcp/terraform/tools/providerUpgrade.ts +217 -0
  390. package/src/mcp/terraform/tools/readFindings.ts +138 -0
  391. package/src/mcp/terraform/tools/scan.ts +303 -0
  392. package/src/mcp/terraform/tools/validate.ts +160 -0
  393. package/src/mcp/terraform/tools/verifyRemediation.ts +190 -0
  394. package/src/mcp/terraform/tools/versionCurrency.ts +64 -0
  395. package/src/mcp/terraform/tools.ts +62 -0
  396. package/src/mcp/terraform/transformPacks.ts +594 -0
  397. package/src/mcp/terraform/types.ts +382 -0
  398. package/src/mcp/terraform/verification.ts +150 -0
  399. package/src/mcp/terraform/vex.ts +367 -0
  400. package/src/mcp/terraform/writeOnly.ts +252 -0
  401. package/src/mcp/terraform.ts +52 -0
  402. package/src/mcp/terratest.ts +215 -0
  403. package/src/mcp/untrustedContent.ts +126 -0
  404. package/src/mcp/upload.ts +123 -0
  405. package/src/models.ts +753 -0
  406. package/src/modes/address-reviews.ts +35 -0
  407. package/src/modes/assess.ts +27 -0
  408. package/src/modes/build.ts +86 -0
  409. package/src/modes/compliance-audit.ts +25 -0
  410. package/src/modes/cost-optimization.ts +36 -0
  411. package/src/modes/drift-detect.ts +31 -0
  412. package/src/modes/fix.ts +29 -0
  413. package/src/modes/incremental-review.ts +103 -0
  414. package/src/modes/index.ts +94 -0
  415. package/src/modes/modernize-deprecated.ts +36 -0
  416. package/src/modes/plan.ts +21 -0
  417. package/src/modes/policy-gate.ts +27 -0
  418. package/src/modes/prFormats.ts +325 -0
  419. package/src/modes/refactor.ts +74 -0
  420. package/src/modes/refresh-remediation.ts +40 -0
  421. package/src/modes/remediate-and-refactor.ts +42 -0
  422. package/src/modes/remediate.ts +78 -0
  423. package/src/modes/resolve-conflicts.ts +32 -0
  424. package/src/modes/review.ts +135 -0
  425. package/src/modes/summarize-pr.ts +42 -0
  426. package/src/modes/task.ts +26 -0
  427. package/src/modes/terraform-code-review.ts +68 -0
  428. package/src/modes/types.ts +17 -0
  429. package/src/modes/update-dependencies.ts +44 -0
  430. package/src/prep/index.ts +96 -0
  431. package/src/prep/installNodeDependencies.ts +251 -0
  432. package/src/prep/installPythonDependencies.ts +235 -0
  433. package/src/prep/types.ts +38 -0
  434. package/src/reviewQuality.ts +237 -0
  435. package/src/runCli.ts +335 -0
  436. package/src/skills/terraform-best-practices/SKILL.md +379 -0
  437. package/src/toolState.ts +241 -0
  438. package/src/utils/activity.ts +210 -0
  439. package/src/utils/agent.ts +245 -0
  440. package/src/utils/agentHangReport.ts +180 -0
  441. package/src/utils/annotations.ts +56 -0
  442. package/src/utils/apiFetch.ts +19 -0
  443. package/src/utils/apiKeys.ts +305 -0
  444. package/src/utils/apiUrl.ts +42 -0
  445. package/src/utils/assets.ts +123 -0
  446. package/src/utils/baseRefConfig.ts +254 -0
  447. package/src/utils/billingErrors.ts +210 -0
  448. package/src/utils/body.ts +168 -0
  449. package/src/utils/buildInfraweaverFooter.ts +104 -0
  450. package/src/utils/byokFallback.ts +137 -0
  451. package/src/utils/changeImpact.ts +330 -0
  452. package/src/utils/claudeSubscription.ts +93 -0
  453. package/src/utils/cli.ts +36 -0
  454. package/src/utils/cloudClient.ts +57 -0
  455. package/src/utils/cloudFetchCore.ts +139 -0
  456. package/src/utils/cloudReport.ts +316 -0
  457. package/src/utils/codexHome.ts +204 -0
  458. package/src/utils/codexOAuth.ts +154 -0
  459. package/src/utils/codexRefreshDetect.ts +36 -0
  460. package/src/utils/diffCoverage.ts +404 -0
  461. package/src/utils/env.ts +84 -0
  462. package/src/utils/errorReport.ts +94 -0
  463. package/src/utils/exitHandler.ts +35 -0
  464. package/src/utils/fixDoubleEscapedString.ts +9 -0
  465. package/src/utils/ghaCore.ts +13 -0
  466. package/src/utils/gitAuth.ts +268 -0
  467. package/src/utils/gitAuthServer.ts +186 -0
  468. package/src/utils/github.ts +676 -0
  469. package/src/utils/globals.ts +9 -0
  470. package/src/utils/humanEditCapture.ts +198 -0
  471. package/src/utils/infraweaverConfig.ts +202 -0
  472. package/src/utils/install.ts +279 -0
  473. package/src/utils/instructions.ts +640 -0
  474. package/src/utils/keylessOidc.ts +47 -0
  475. package/src/utils/leapingComment.ts +20 -0
  476. package/src/utils/learnings.ts +145 -0
  477. package/src/utils/learningsTruncate.ts +42 -0
  478. package/src/utils/lifecycle.ts +198 -0
  479. package/src/utils/log.ts +452 -0
  480. package/src/utils/modelResolver.ts +105 -0
  481. package/src/utils/moduleFetch.ts +138 -0
  482. package/src/utils/normalizeEnv.ts +106 -0
  483. package/src/utils/openCodeModels.ts +86 -0
  484. package/src/utils/openaiCompatible.ts +52 -0
  485. package/src/utils/opentofuMcp.ts +61 -0
  486. package/src/utils/overrides.ts +100 -0
  487. package/src/utils/packageManager.ts +257 -0
  488. package/src/utils/patchWorkflowRunFields.ts +173 -0
  489. package/src/utils/payload.ts +1206 -0
  490. package/src/utils/prSummary.ts +148 -0
  491. package/src/utils/presets.ts +105 -0
  492. package/src/utils/progressComment.ts +266 -0
  493. package/src/utils/providerErrors.ts +189 -0
  494. package/src/utils/proxyModel.ts +239 -0
  495. package/src/utils/rangeDiff.ts +182 -0
  496. package/src/utils/redact.ts +86 -0
  497. package/src/utils/registryAuth.ts +115 -0
  498. package/src/utils/remediationCommand.ts +144 -0
  499. package/src/utils/retry.ts +180 -0
  500. package/src/utils/reviewCleanup.ts +116 -0
  501. package/src/utils/run.ts +100 -0
  502. package/src/utils/runContext.ts +276 -0
  503. package/src/utils/runContextData.ts +121 -0
  504. package/src/utils/runErrorRenderer.ts +282 -0
  505. package/src/utils/runFixture.ts +91 -0
  506. package/src/utils/runLifecycle.ts +360 -0
  507. package/src/utils/runStartupLog.ts +67 -0
  508. package/src/utils/secrets.ts +208 -0
  509. package/src/utils/setup.ts +368 -0
  510. package/src/utils/shell.ts +132 -0
  511. package/src/utils/skills.ts +67 -0
  512. package/src/utils/subprocess.ts +474 -0
  513. package/src/utils/terraformMcp.ts +99 -0
  514. package/src/utils/time.ts +59 -0
  515. package/src/utils/timer.ts +72 -0
  516. package/src/utils/todoTracking.ts +168 -0
  517. package/src/utils/token.ts +294 -0
  518. package/src/utils/toolLicensing.ts +152 -0
  519. package/src/utils/toolSelection.ts +239 -0
  520. package/src/utils/toon.ts +76 -0
  521. package/src/utils/version.ts +10 -0
  522. package/src/utils/versioning.ts +44 -0
  523. package/src/utils/vertex.ts +94 -0
  524. package/src/utils/workflow.ts +25 -0
@@ -0,0 +1,78 @@
1
+ import { COMMENT_COMMAND, PRODUCT_NAME, SARIF_FILE } from "#app/brand";
2
+ import { REMEDIATION_ADDENDUM_HEADING } from "#app/reviewQuality";
3
+ import { REMEDIATION_PR_FORMAT } from "./prFormats.ts";
4
+ import type { Mode, ToolRef } from "./types.ts";
5
+
6
+ export function remediateMode(t: ToolRef): Mode {
7
+ return {
8
+ name: "Remediate",
9
+ description:
10
+ "Bring a repository's Terraform up to best practice: scan with the deterministic check tools, then open one scoped, reviewable PR per concern that fixes it and proves each fix by re-scanning (✅).",
11
+ prompt: `### Checklist
12
+
13
+ 1. **task list**: create your task list for this run as your first action.
14
+
15
+ 2. **scan**: call \`${t("terraform_scan")}\` to get the best-practice \`concerns\` plus \`groups\` — one group per file, each with a stable \`id\`, its \`file\`, the group \`severity\` (highest in the file), the distinct \`rule_ids\`, and the \`concern_ids\` it covers. If it reports zero concerns, call \`${t("report_progress")}\` with "Terraform already follows best practice — nothing to remediate." and **stop**.
16
+
17
+ *Alternative concern source:* if the Assessor (terraform-reviewer) has already produced a \`findings.json\` **or a SARIF report** (Trivy/Checkov/tflint \`-o sarif\`) for this repo (one exists in the workspace, or \`$INFRAWEAVER_FINDINGS_PATH\` is set), call \`${t("read_findings")}\` instead — it auto-detects the format and returns the **same** \`{concerns, groups}\` shape, so the rest of this checklist is unchanged. Default to \`${t("terraform_scan")}\`; only use \`${t("read_findings")}\` when such a file is present (it returns \`found: false\` otherwise). Note that reviewer-exclusive findings (source \`reviewer\`) can't be re-verified by ${PRODUCT_NAME}'s scanners — see the prove-it step.
18
+
19
+ *External / CSPM feed:* if the customer's cloud-security tool (Wiz / Orca / Prisma, or any CSPM) has produced an external-findings feed (a JSON file in the workspace, or \`$INFRAWEAVER_EXTERNAL_FINDINGS_PATH\` is set), call \`${t("ingest_external_findings")}\` — it maps each cloud alert back to the offending Terraform resource (by address, then type+name, then by correlating a cloud id / ARN against the resource block) and returns the **same** \`{concerns, groups}\` shape, so this checklist proceeds unchanged. ${PRODUCT_NAME} never touches the cloud here — the CSPM already holds the creds and emitted the JSON; we only read it. Mapped alerts are \`reviewer\`-source (so, like other reviewer findings, not re-verifiable by our scanners — see the prove-it step); any alert with no matching resource in code comes back under \`unmapped\` (report it, don't invent a fix). Returns \`found: false\` when no feed is present.
20
+
21
+ *Multi-root repos:* \`${t("terraform_validate")}\` and \`${t("terraform_plan")}\` are now **multi-root aware** — they automatically validate/plan **every** Terraform root in the repo (e.g. hepcare's \`terraform/\` + \`terraform/core/\`) and aggregate the result (see \`roots_validated\` / \`roots_planned\`), so you do **not** need to loop per-root yourself. Call \`${t("terraform_roots")}\` only when you want to see the root layout (or fix a concern that lives in one specific root).
22
+
23
+ 3. **pick scope**: act on **one group per PR** (a group is all of one file's concerns — different scanners flag the same defect under different rules, so fixing per-file avoids a flood of near-duplicate PRs). Take the **highest-severity group first**. **Targeted findings override this:** if YOUR TASK carries a \`TARGETED FINDINGS\` directive (a Cloud dashboard "Remediate" dispatch naming specific concern ids), act ONLY on the group(s) containing those ids — never the highest-severity group instead, and never widen to other groups. Unless the task explicitly asks for more, open **at most one PR this run**. Skip groups whose severity is only \`info\` unless asked. Use the \`terraform-best-practices\` skill for how to read each concern and apply the *minimal* fix.
24
+
25
+ **Autonomy**: each group carries an \`autonomy\` field — \`auto\` (fix and open a normal PR) or \`needs-human\` (a security finding at/above the \`autonomy_threshold\`, or — once plan runs — a high blast radius). You still fix and open a PR for a \`needs-human\` group, but you MUST add the \`needs-human\` label (\`${t("add_labels")}\`), open the PR with \`approved: false\` framing, and put a prominent **⚠️ Needs human review** callout at the top of the body listing the group's \`autonomy_reasons\`. Never batch a \`needs-human\` group with others.
26
+
27
+ **Dependency order & environment twins**: when a repo has local modules, \`${t("terraform_module_graph")}\` returns \`dependency_order\` — fix a shared/depended-on module BEFORE its dependents so sequenced PRs don't conflict. \`${t("terraform_roots")}\` returns \`environment_twins\` — parallel \`dev\`/\`staging\`/\`prod\` (or per-region) stacks that differ only by an environment segment; when the fixed file is one twin, note in the PR that the same fix should be offered for its twins (a separate PR per twin, honouring \`max_prs\`).
28
+
29
+ **Grouping & batching**: groups default to one-per-file. When a single rule dominates across many files (e.g. "add \`tags\` everywhere"), re-scan with \`group_by: "rule"\` so it becomes ONE coherent group/PR instead of many. The scan's \`batch_plan\` tells you which low-risk groups are \`batchable\` (combine them into the single \`batch_plan.batch_branch\` PR when \`max_prs\` would otherwise be exceeded) and which are \`isolated\` (each gets its own PR for independent review/revert). Still honour \`max_prs\` and never batch a \`needs-human\` group.
30
+
31
+ **Comment command**: when this run was triggered by a \`${COMMENT_COMMAND} fix …\` comment, the triggering body is in your prompt — honour the requested scope INSTEAD of "highest-severity group": \`fix #<concern-id>\` → act only on the group containing that concern id; \`fix all <severity>-severity\` → set the scan \`severity_threshold\` to that level and act on those groups (up to \`max_prs\`); \`fix <file>.tf\` → act on that file's group; \`fix all\` → act on the highest-severity groups up to \`max_prs\`. If the comment isn't a recognised fix command, fall back to the default scope. A **strategy suffix** — \`fix #<concern-id> with strategy B\` (or a bare \`strategy B\` reply on a proposal thread) — additionally tells you **which** fix to apply: see the propose-then-steer guidance in step 4.
32
+
33
+ **Bulk remediation (\`fix rule <rule-id>\` / \`fix all rule <rule-id>\`)**: a request to fix ONE scanner rule everywhere it fires (e.g. \`${COMMENT_COMMAND} fix rule CKV_AWS_23\` — "add a description to every security group", or \`fix rule terraform_required_version\`). Re-scan with \`group_by: "rule"\` so that rule becomes ONE group spanning every file, then act on the single group whose \`rule_ids\` include \`<rule-id>\` — apply the SAME minimal fix at every site in its \`files\` and open ONE coherent PR (not one per file). This is the sweep path; still honour \`max_prs\` and never batch a \`needs-human\` group. Cite each fixed site in the PR body.
34
+
35
+ 4. **for the chosen group**:
36
+ - **base branch**: this run's base branch is resolved deterministically — \`${t("create_pull_request")}\` targets the \`base_branch\` input if set, else the repository's default branch (\`main\`, or \`master\`). You do not choose it; just **omit** the \`base\` argument when opening the PR (below) and it is filled in.
37
+ - **idempotency**: the remediation branch is \`remediate/<group-id>\`. Before doing anything, check whether that branch or an open PR for it already exists (\`${t("git")}\` / \`${t("get_pull_request")}\`). If one exists, update it rather than opening a duplicate.
38
+ - **branch**: create \`remediate/<group-id>\` from the **current HEAD** (the checkout that was just scanned) via \`${t("git")}\` (\`git checkout -b remediate/<group-id>\`). Do NOT switch to a different base first — branching from the scanned checkout keeps the PR diff to exactly your fix.
39
+ - **honest refusal (decide BEFORE fixing)**: if the group's concerns appear in the scan's \`refusal_candidates\` (the fix needs a human decision — narrowing an IAM wildcard, a KMS key policy, a real ingress CIDR), do **not** guess a fix that could break the stack. Instead open a structured issue (\`${t("create_issue")}\`) describing the concern, why it isn't auto-fixed, and what a human should do, and skip the PR for that group. A proven fix or an honest refusal — never a guessed, unverifiable PR.
40
+ - **propose, then let me steer (when there's no single right fix)**: distinct from honest refusal (which refuses a fix a human must *decide*), this is for a finding with **2–3 genuinely distinct, defensible fixes** that differ in trade-offs, not correctness (e.g. encrypt with an AWS-managed key **vs** a customer-managed KMS key; a narrow security-group rule **vs** a prefix list **vs** a VPC endpoint). When such a fork exists **and the triggering comment did not already select a strategy**, do **not** silently pick for the reviewer: via \`${t("create_issue_comment")}\` post one short comment listing the options as **A / B / C** — each a single line (what it does + its trade-off) — and ask the reviewer to reply \`${COMMENT_COMMAND} fix #<concern-id> with strategy <A|B|C>\`. Then **skip the PR for this group** this run and note it in your final report (it resumes when the reviewer replies). When the comment **did** select one (\`fix #<id> with strategy B\`, or a bare \`strategy B\` reply on the proposal thread), apply **exactly** that strategy — don't second-guess it. Reserve this for real forks in the road; a fix with one obvious correct answer just gets made.
41
+ - **reuse a proven fix (optional, do this BEFORE editing)**: call \`${t("terraform_fix_memory")}\`. When proven-fix memory is enabled it returns any patterns whose \`finding_type\` matches this group's concerns — each a transformation that already passed a \`✗→✓\` proof on this repo or elsewhere in the fleet, with its \`remediation_hint\` + a \`before_example\`. Use a matching pattern as your STARTING POINT (it saves you rediscovering the approach), but ADAPT it to this repo's own resources — it is a prior, never a patch to paste. You MUST still run \`${t("terraform_verify_remediation")}\` to prove \`✗→✓\` here; a pattern is never blind-applied and never substitutes for the per-target proof. Returns an empty list (never an error) when memory is off or nothing matches — then fix from first principles as usual.
42
+ - **fix**: edit the group's file(s), using your native file tools. For a by-file group that's the single \`file\`; for a **by-rule group** it's every entry in \`files\` (fix the one rule everywhere it fires). Resolve **every** concern in the group — when the scan's \`co_located\` shows several scanners flagged the same \`file:line\`, they're one underlying defect: write ONE canonical fix and one explanation, not separate edits. **Only touch \`*.tf\` / \`*.tfvars\` files.** Make the smallest changes that clear the concerns — do NOT reformat or refactor unrelated code (see *SYSTEM* surgical-change rules). **Match the file's existing formatting in your edit, and never run \`terraform fmt\` on a file that already has pre-existing formatting drift** — \`terraform fmt\` rewrites the WHOLE file (every interpolation + alignment), burying your one-line fix under dozens of cosmetic lines and making the PR unreviewable. \`${t("terraform_validate")}\` reports that pre-existing drift as advisory \`preexisting_fmt_drift\` and does NOT gate on it, so leave it alone; repo-wide reformatting is a separate \`${COMMENT_COMMAND} fix rule terraform-fmt\` PR, never a rider on a security fix. **Module-source awareness:** call \`${t("terraform_module_graph")}\` first — if the concern's file is inside a \`local_module_dir\`, fix it ONCE at the module source (it propagates to all callers; note them in the PR); if the fix would require editing a registry/git/remote module, you can't fix it here — report it (open an issue naming the upstream module + version) instead. **Approved modules:** call \`${t("list_modules")}\` and prefer a catalogue module (registry or house, pinned) when the fix is genuinely a module swap — but for a one-line fix on an existing raw resource, fix it in place. **Provider-major awareness:** before introducing an argument or block, check \`terraform_validate\`'s \`providers\` list for the pinned \`major\` — argument names and valid blocks differ across majors. After the dir is init-ed (validate/plan ran), you can **verify an argument exists** for the installed provider with \`${t("terraform_provider_schema")}\` (pass the resource type + the arg names you added; it returns any \`unknown_args\` that would break \`plan\`). If your fix means RAISING a provider major (rare in Remediate — that is \`UpdateDependencies\`' job), call \`${t("terraform_provider_upgrade")}\` first: it names the codified breaking changes for that boundary and, critically, which of them MOVE STATE and therefore must never be applied autonomously. **Reusing a module?** call \`${t("terraform_module_interface")}\` on its dir to get its real \`variable\` names + which are required, so the \`module\` block you write is correct.
43
+ - **fix QUALITY — a real fix, not a scanner-silencer (enterprise bar)**: the goal is infrastructure that is *actually* safer, not Terraform that merely stops tripping the scanner. Three rules:
44
+ - **Secure defaults, never a hidden-insecure one.** When you parameterise a hardcoded value, the new \`variable\`'s \`default\` must be the SECURE choice, or have **no \`default\`** (forcing the operator to set it). NEVER preserve the insecure value as the default — e.g. replacing \`cidr_blocks = ["0.0.0.0/0"]\` with \`var.allowed_cidr_blocks\` *defaulting to \`["0.0.0.0/0"]\`* is not a fix: the deployed behaviour is identical and you've just moved the problem somewhere a scanner may not see it. If the secure value genuinely needs a human decision, this is an honest-refusal / propose-then-steer case, not a default-to-insecure.
45
+ - **Optional-input resources must be conditional.** If a fix adds a resource or block that only works when an OPTIONAL input is set (e.g. an HTTPS listener that needs \`var.ssl_certificate_arn\`, which defaults to \`null\`), gate it with \`count\`/\`for_each\` or a \`dynamic\` block so it is NOT emitted — and cannot break \`plan\`/\`apply\` — when the input is unset. Do not write an always-present resource that references a null/empty value, and never claim in the PR that something is "only active when set" unless the HCL actually makes it conditional. Remember \`${t("terraform_validate")}\` can pass on HCL that still fails at \`plan\`/\`apply\` — your claim of conditionality must be in the code, not just the prose.
46
+ - **Modernise, don't perpetuate.** Call \`${t("terraform_version_currency")}\` and, when you must add a \`required_providers\`/\`required_version\` block, pin a CURRENT supported major, not an ancient one chosen only to match legacy code. Flag (in the PR body, as a follow-up — not necessarily fixed in this scoped PR) deprecated patterns the scanners don't encode: the archived \`hashicorp/template\` provider + \`data "template_file"\` (modern: the built-in \`templatefile()\` function), \`aws_launch_configuration\` (→ \`aws_launch_template\`), and any provider/module pin that is several majors behind. A best-practice fix should not entrench an EOL provider. **State locking:** when a fix touches (or would add) an S3 backend, prefer native locking — \`use_lockfile = true\` — over the deprecated \`dynamodb_table\` argument, and never introduce DynamoDB locking into a backend that doesn't already have it. ⚠️ Changing an EXISTING backend block is not a code-only edit: it requires \`terraform init -migrate-state\` against real state, so report it for a human instead of pushing it in a remediation PR.
47
+ - **keep the module's tests/examples consistent (only when you fixed a reusable module)**: if the file(s) you changed live inside a \`local_module_dir\` (from \`${t("terraform_module_graph")}\`) AND your fix changed the module's public interface (added/removed/renamed a \`variable\`, tightened a type), call \`${t("terraform_module_tests")}\` with that module dir. It returns the module's existing \`examples/\` fixtures + \`terraform test\` (\`*.tftest.hcl\`) / Go Terratest files and the \`drift\` per asset — \`missing_required\` (a variable the asset must now set) and \`unknown_set\` (a variable the asset references that no longer exists). Update **exactly** the drifting assets so they match the new interface; **never weaken, delete, or comment out an assertion just to make a test pass** — a fix that breaks a module's contract is the test doing its job, so correct the fix or the fixture, not the assertion. \`examples/\` are \`*.tf\` (always within the push allow-list); native \`*.tftest.hcl\` / Go \`*_test.go\` files are only pushable when the \`terratest\` input is enabled — when it isn't and only those drift, note the needed test update in the PR body for a human rather than leaving the module's own tests broken. Skip this entirely for a one-off raw-resource fix that doesn't touch a module interface.
48
+ - **validate**: call \`${t("terraform_validate")}\`. If it does not pass, fix what it reports or abandon this group — **never open a PR whose validate did not pass**. \`passed\` excludes pre-existing \`terraform-fmt\` drift (surfaced separately as advisory \`preexisting_fmt_drift\`); only formatting drift YOUR edit introduced counts — so a failing \`passed\` is a real validate/lint error to fix, never a cue to reformat untouched code. Its \`providers\` field carries the pinned provider majors (use them as above). It also returns \`unknown_arguments\`: arguments you wrote that are NOT in the installed provider's schema and would break \`plan\` — treat any entry as a must-fix (correct the argument for the pinned major) even though \`passed\` doesn't gate on it. \`schema_checked: false\` means the schema wasn't available (rely on \`${t("terraform_plan")}\` then).
49
+ - **policy gate (optional)**: if the repo ships policy-as-code (a \`policy/\`, \`policies/\`, or \`.conftest\` dir of Rego), call \`${t("policy_check")}\` — it runs \`conftest\` against the plan JSON. It degrades green (\`ok: false\`) when conftest or a policy dir is absent. When it returns \`passed: false\`, treat it exactly like a failed validate: fix the violation (listed in \`failures\`) or label the PR \`needs-human\` and surface it — never push past a policy denial.
50
+ - **plan (safety gate — do this BEFORE pushing)**: call \`${t("terraform_plan")}\`. It auto-skips (returns \`ran: false\`) when no cloud credentials / the terraform CLI are present, or init/plan can't complete — then carry on. When it returns \`ran: true\`, add a one-line **Plan** note to the PR body (e.g. \`Plan: +0 ~1 -0\`) and act on three signals:
51
+ - **destroy/replace — blast-radius pre-flight (\`push_will_block\` / \`blocked_destructive\`)**: treat any destroy/replace as a stop sign — a best-practice remediation should rarely destroy or replace a resource. The plan does the \`allow_replace\` cross-check FOR you: \`blocked_destructive\` lists the stateful resources (RDS, S3, EBS, a SQL database, …) NOT covered by the operator's \`allow_replace\`, and **\`push_will_block: true\` means \`${t("push_branch")}\` WILL be hard-blocked** by the destroy guardrail. When \`push_will_block\` is true, **abandon this group now — before you commit or push** — and report it (don't waste a commit/push round-trip discovering the block at push). Only proceed past a stateful destroy/replace when \`allow_replace\` genuinely covers it (then \`push_will_block\` is false) AND the replacement is clearly intended. List the \`destructive\` resources in your report either way.
52
+ - **blast radius (\`blast_radius.tier\`)**: add it to the PR body (e.g. \`Blast radius: low (1 resource)\`). When the tier is \`high\` (more than 10 resources, or the change spans more than one module), add a prominent **⚠️ Large blast radius — review carefully** callout to the PR body so a reviewer knows this is not a one-line change.
53
+ - **idempotency (\`idempotent\`)**: when it is \`false\`, the second plan disagreed with the first — a perpetual-diff smell (a non-deterministic value such as \`timestamp()\`/\`uuid()\`/an unkeyed \`random_*\`). **Prefer to fix the non-determinism or abandon the group**; if you still open the PR, surface \`idempotency_warning\` prominently as a **⚠️ Non-deterministic plan** caveat. (Note: this catches in-config non-determinism only — ${PRODUCT_NAME} never applies, so a provider-normalisation perpetual diff can't be detected here.)
54
+ - **needs-human (\`needs_human\`)**: when \`true\`, the plan crossed a deterministic escalation line (high blast radius, a stateful destroy/replace, or a non-deterministic plan — see \`needs_human_reasons\`). Add the \`needs-human\` label (\`${t("add_labels")}\`) and a louder callout.
55
+ - **full plan (\`plan_text\`)**: when present, attach it to the PR body as a collapsed \`<details><summary>Plan</summary>\\n\\n\\\`\\\`\\\`\\n…\\n\\\`\\\`\\\`\\n</details>\` block so a reviewer can see the exact planned change without re-running it.
56
+ - **commit + push**: \`git add\` only the file you changed, commit with a message naming the file and the key rules (e.g. \`fix(tf): harden main.tf — S3 encryption + block public access\`), then \`${t("push_branch")}\` (same push/prepush guidance as Build mode in *SYSTEM*).
57
+ - **open PR — with a COMPLETE body (MANDATORY)**: \`${t("create_pull_request")}\` (omit \`base\` — it resolves to the run's base branch above). The PR body is the primary deliverable a human reviews. At an absolute minimum the body MUST explain, in plain English: (a) **what was wrong** — each concern by \`rule_id\` + its \`evidence\`; and (b) **what you changed** to fix each one, and why it's safe. Build it with the **Remediation PR format** at the end of this checklist (status banner → title + badges → \`## What changed\` with the *Was / Changed / Safe because* note per concern). Two parts of the format depend on the verification tool that runs in the NEXT step, so at open time they are provisional: open with a \`> [!NOTE]\` **verification pending** status banner (not a clean/caution verdict you haven't earned yet) and omit the \`## Validation\` section — the next step's \`${t("update_pull_request_body")}\` fills both from the tool result. Never write a verified/clean/regression status before the tool returned it. ⚠️ \`${t("report_progress")}\` writes the GitHub Actions **job summary**, which is NOT the PR body — a good job summary does **not** substitute for a complete PR body. If you only have time/budget for one, the PR body wins.
58
+ - **prove it (re-scan)**: call \`${t("terraform_verify_remediation")}\` with the group's \`concern_ids\`. It re-runs the scanners and returns the authoritative \`resolved\` / \`remaining\` sets and a \`verified\` flag — this is the proof, do NOT eyeball a scan or self-report. Then \`${t("update_pull_request_body")}\` to add a "Validation" section built **from that result**: one \`✅ <rule_id> resolved\` line per id in \`resolved\`, and list every id in \`remaining\` honestly as still-open. Never put a ✅ on a concern unless the tool returned it in \`resolved\`. Act on two more fields it returns:
59
+ - **regressions**: when \`has_regressions\` is true, the fix INTRODUCED new concerns (listed in \`regressions\`) that weren't there before — it traded one defect for another. Add a prominent **⚠️ Regression** callout listing them, add the \`needs-human\` label (\`${t("add_labels")}\`), and prefer reworking the fix to remove the regression before relying on the PR.
60
+ - **confidence**: render the returned \`confidence\` (high/medium/low) as a one-line badge in the PR body (e.g. \`Confidence: high\`) with its \`confidence_reasons\`. It is computed deterministically from the verification evidence (verified + no regressions + plan idempotency + blast radius + cost) — report it verbatim, do NOT inflate it.
61
+ - **per-finding explanation**: in the PR body, give each resolved concern a short three-line note — **Was** (what the scanner flagged, from its \`evidence\`), **Changed** (what your fix did), **Safe because** (why it's correct/non-breaking) — and hyperlink the \`rule_id\` to its documentation. The scan output carries a \`doc_url\` per concern (and \`doc_urls\` per group); use it, falling back to the concern's \`remediation_hint\` when no \`doc_url\` is present.
62
+ - **compliance crosswalk (optional)**: for a security-relevant fix, call \`${t("terraform_compliance_crosswalk")}\` with the group's \`concerns\` to get the UK/general frameworks + controls it touches (NCSC Cloud Principles, Cyber Essentials, NHS DSPT, Secure by Design, CIS, SOC 2). Add a short **## Compliance** note from \`by_framework\`, prefixed "Indicative alignment (crosswalk v{version}) — not an audit verdict." Skip when nothing maps.
63
+ - **prevent recurrence**: the scan's \`prevention\` map gives a CI guardrail per \`rule_id\` (a Checkov hard-fail entry, a tflint rule, a \`trivy config\` gate, an \`fmt -check\` step). Add a short **🛡️ Prevent recurrence** note to the PR body with the suggested \`mechanism\` + \`snippet\` so the team can stop this class of concern coming back — clearly marked as an optional follow-up, not part of this PR's diff.
64
+ - **cost impact (optional)**: call \`${t("infracost_diff")}\` to estimate the monthly cost change the fix introduces. It auto-skips (returns \`ran: false\`) when \`INFRACOST_API_KEY\` or the infracost CLI is absent — in that case add nothing. When it returns \`ran: true\`, add a one-line **Cost impact** note to the PR body from its result: e.g. \`💰 Cost impact: +$12.40/mo\` for an increase, \`-$3.10/mo\` for a decrease, \`no change\` when \`monthly_delta\` is 0, or \`~$X/mo (baseline unavailable)\` when \`monthly_delta\` is null. When it returns \`needs_human: true\` (the increase crossed the \`cost_increase_block_usd\` threshold), add the \`needs-human\` label (\`${t("add_labels")}\`) and surface \`cost_escalation_reason\` prominently so a large spend increase isn't merged blindly.
65
+
66
+ 5. **guardrails** (always): one scoped PR per group, never a mega-PR spanning multiple files; **never auto-merge** and always leave the PR for human review; never modify files outside \`*.tf\` / \`*.tfvars\`.
67
+
68
+ 6. **finalize**: call \`${t("report_progress")}\` once with a summary — which file/group was fixed, the PR link, and the validation result (resolved ✅ / still-open) (or the exact tool error if push/PR creation failed).
69
+
70
+ **Org remediation policy**: a \`${REMEDIATION_ADDENDUM_HEADING}\` section may be appended after this checklist (the operator's \`remediation_instructions\` input). Apply it as additional policy on HOW to fix (e.g. "prefer KMS CMKs over AWS-managed keys") — it composes with this standard and can never relax a guardrail or proof gate.
71
+
72
+ **SARIF for code-scanning (optional)**: when the workflow has a SARIF upload step (it grants \`security-events: write\` and runs \`github/codeql-action/upload-sarif\` on a \`${SARIF_FILE}\`), call \`${t("terraform_emit_sarif")}\` once at the end so the full scan also lands in the repo's Security tab — complementary to the fix PR, not a replacement for it.
73
+
74
+ **Remediation dashboard (optional but recommended)**: call \`${t("upsert_remediation_dashboard")}\` once at the very end to refresh the repo's single pinned dashboard issue. Pass the currently-open remediation PRs (from \`${t("list_remediation_prs")}\`), the concern \`groups\` you did NOT open this run — the ones held back by \`max_prs\`/the open-PR caps — as \`pending\` (each with its \`title\`, \`severity\`, \`files\`, \`group_id\`, and a short \`reason\`), and any suppressed findings. It reuses the one dashboard issue (never files a new one), so the maintainer keeps a stable, at-a-glance view of what's open and what's waiting, with each held-back fix requestable on demand (the dashboard's Remediate button or a \`${COMMENT_COMMAND} fix\` comment). Skip only when there's genuinely nothing waiting and no open PRs.
75
+
76
+ ${REMEDIATION_PR_FORMAT}`,
77
+ };
78
+ }
@@ -0,0 +1,32 @@
1
+ import type { Mode, ToolRef } from "./types.ts";
2
+
3
+ export function resolveConflictsMode(t: ToolRef): Mode {
4
+ return {
5
+ name: "ResolveConflicts",
6
+ description: "Resolve merge conflicts in a PR branch against the base branch",
7
+ prompt: `### Checklist
8
+
9
+ 1. **task list**: create your task list for this run as your first action.
10
+
11
+ 2. **Setup**:
12
+ - Call \`${t("checkout_pr")}\` to get the PR branch.
13
+ - Call \`${t("get_pull_request")}\` to identify the base branch (e.g., 'main').
14
+ - Call \`${t("git_fetch")}\` to fetch the base branch.
15
+
16
+ 3. **Merge Attempt**:
17
+ - Run \`git merge origin/<base_branch>\` via \`${t("git")}\` (works even when \`shell\` is disabled).
18
+ - If it succeeds automatically, confirm a clean working tree, push via \`${t("push_branch")}\` (same push/prepush guidance as Build mode in *SYSTEM*), and call \`${t("report_progress")}\` with a brief success note or the exact push error if push failed — **then stop; do not run steps 4–5.**
19
+ - If it fails (conflicts), resolve them manually (continue to steps 4–5).
20
+
21
+ 4. **Resolve Conflicts**:
22
+ - Run \`git status\` or parse the merge output to find the list of conflicting files.
23
+ - For each conflicting file: read it, find the conflict markers (\`<<<<<<<\`, \`=======\`, \`>>>>>>>\`), understand the code context, and rewrite the file with the correct resolution. Remove all markers.
24
+ - Verify the file syntax is correct after resolution.
25
+
26
+ 5. **Finalize**:
27
+ - Run a final verification (build/test) to ensure the resolution works.
28
+ - via \`${t("git")}\`: \`git add\` the resolved files, then \`git commit -m "resolve merge conflicts"\` (works even when \`shell\` is disabled)
29
+ - confirm a clean working tree, then push via \`${t("push_branch")}\` (same push/prepush guidance as Build mode in *SYSTEM*)
30
+ - Call \`${t("report_progress")}\` with a summary of what was resolved (or the exact push error if push failed)`,
31
+ };
32
+ }
@@ -0,0 +1,135 @@
1
+ import { REVIEWER_AGENT_NAME } from "#app/agents/reviewer";
2
+ import {
3
+ FINDING_VERIFICATION_PASS,
4
+ REVIEW_FINDING_PRECEDENTS,
5
+ } from "#app/reviewQuality";
6
+ import { PR_SUMMARY_FORMAT } from "./prFormats.ts";
7
+ import type { Mode, ToolRef } from "./types.ts";
8
+
9
+ export function reviewMode(t: ToolRef): Mode {
10
+ return {
11
+ name: "Review",
12
+ nonCommitting: true,
13
+ description:
14
+ "Review code, PRs, or implementations; provide feedback or suggestions; identify issues; or check code quality, style, and correctness",
15
+ prompt: `### Checklist
16
+
17
+ 1. **task list**: create your task list for this run as your first action.
18
+
19
+ 2. **checkout**: call \`${t("checkout_pr")}\` — this returns PR metadata, a \`diffPath\`, and (when TF declarations changed) a supplemental \`impactPath\`. read the diff TOC end-to-end and treat its file line ranges as your coverage checklist. only AFTER the raw-diff read, use \`impactPath\` as an explicitly-incomplete list of reference leads (where the PR's changed var/resource/module/output blocks are used elsewhere) — it never replaces raw-diff reading or establishes coverage.
20
+
21
+ 3. **triage**: orient yourself on the PR — identify *what kind of thing this is* (domain it touches, seams it crosses, external contracts it depends on, user-facing surfaces it changes). pull as much context as you need to render a confident, well-grounded review: read related files, grep for callers of changed symbols, check tests that exercise the touched paths, fetch related GitHub state. **you are the synthesizer** — never delegate understanding to subagents.
22
+
23
+ if the PR is **genuinely trivial**, skip the fan-out entirely and submit a \`No new issues found.\` review per step 7.
24
+
25
+ "Genuinely trivial" (skip):
26
+ - single-word doc typo, whitespace/format-only, comment-only across any number of files
27
+ - lockfile or generated-code regeneration (size of diff is irrelevant — read the *shape*)
28
+ - mechanical rename whose only effect is import-path updates
29
+ - low-risk dep patch bump
30
+
31
+ "Looks trivial but isn't" (do **NOT** skip — small diff, big blast radius):
32
+ - any 1-line change to SQL / regex / auth / billing / permission / signature-verification code
33
+ - flipping a feature-flag default, default config value, or retry/timeout constant
34
+ - changing a money/tax/currency/fee constant by any amount
35
+ - changing an HTTP method, redirect URL, response code, or status enum
36
+ - tightening or loosening a comparison operator (\`<\` ↔ \`<=\`, \`==\` ↔ \`!=\`)
37
+ - renaming a public API surface (still trivial in shape, but needs an impact lens)
38
+ - adding a new direct dependency (supply-chain surface)
39
+ - any "typo fix" in user-facing copy that changes meaning ("approved" → "denied")
40
+ - mixed diffs where a semantic 1-liner is buried in whitespace/formatting changes
41
+
42
+ 4. **lens dispatch — one lens per unresolved, load-bearing hypothesis (0, 1, or 2+)**.
43
+
44
+ You review the PR yourself first — read the whole diff, pull context, trace the changes. A lens is not a second opinion for its own sake; it is an independent pass at a *specific, falsifiable question you could not confidently resolve on your own*. After your own pass, keep only the open questions that meet ALL THREE bars:
45
+ - **falsifiable** — you can state in one line what evidence confirms it and what refutes it. "is the auth correct?" fails this; "does the new middleware run *before* the tenant-scoping guard on \`/export\`?" passes.
46
+ - **load-bearing** — if it resolves the wrong way, the review's verdict or a specific finding changes. A question whose answer doesn't move the review is not worth a dispatch.
47
+ - **beyond your own reach** — you tried and the code alone doesn't settle it: it needs an external-contract check, a cross-file trace too wide to hold confidently, or an independent adversarial read of genuinely subtle logic. "I'd like a second look to be thorough" does NOT qualify — that is doing your own job on a weaker model.
48
+
49
+ Dispatch **one \`${REVIEWER_AGENT_NAME}\` lens per surviving hypothesis**: **0** for most PRs (you resolved everything yourself — still the common outcome), **1** when a single real question remains, **2+** when several do. The count follows the questions — do NOT manufacture a hypothesis to reach two, and do NOT drop a real one to avoid dispatching alone. When you dispatch 2+, they go out together in parallel (step 5); a lone lens is the same mechanism at n=1, and is legitimate precisely because it answers a question you genuinely could not resolve — not because a second model looked.
50
+
51
+ Where load-bearing hypotheses cluster: high-stakes subsystems (auth, billing, payments, schema migration, webhooks, secrets, RBAC, multi-tenant isolation, cron/scheduling) and substantive diffs (>5 files, >200 net lines) are where an unresolved question is most likely genuinely load-bearing — but a 1-line change to a security boundary can raise exactly one hypothesis worth a lens, and a 500-line mechanical rename can raise none. Size is a hint about where to look, never the gate.
52
+
53
+ Lens framings come in two flavors:
54
+ - **themed lenses** — a perspective applied across the whole diff (correctness, security, user-journey, performance, etc.).
55
+ - **subsystem lenses** — a domain-scoped frame for high-stakes subsystems the PR touches (e.g. "the auth lens", "the billing lens", "the schema-migration lens"). **for high-stakes domains, lead with the subsystem lens rather than the generic themed equivalent** — "billing-subsystem" outperforms "correctness on billing code" because the framing primes the subagent to remember domain-specific failure modes (double-charges, refund races, currency rounding, dispute flows) the generic lens misses.
56
+
57
+ starter menu (each entry is a *source* of hypotheses, not a checklist to run — pick one only when it names a falsifiable question you actually could not resolve yourself):
58
+ - **correctness & invariants** — bugs, races, error handling, edge cases, state-machine boundaries
59
+ - **impact** — stale references in code/tests/docs/configs/UI after rename/remove
60
+ - **research-validated assumptions** — third-party API contracts, SDK semantics, framework directives, version-gated behavior. **only pick when the PR's correctness depends on the contract behaving a specific way** — not when the API is merely used. The bar is "if the third-party contract differs from what the diff assumes, the PR is incorrect." When dispatched, the subagent must verify load-bearing claims via web search and quote source URLs.
61
+ - **security** — new endpoints, authZ, input validation, secrets handling, replay/CSRF/injection, cross-tenant isolation
62
+ - **user-journey** — UX-touching flows: walk through happy path and failure modes as a user
63
+ - **operational readiness** — observability, alerting, migrations (forward + rollback), feature flags, on-call burden
64
+ - **integration & cross-cutting** — API contracts between modules, backward-compat of public surfaces, multi-service ordering
65
+ - **test integrity** — meaningful coverage for the changed behavior; deterministic; no shared-state pollution
66
+ - **performance** — N+1 queries, hot-path allocation, latency budgets, index coverage
67
+ - **holistic** — does the PR make sense as a whole? symmetric flows (delete for every create, rollback for every migration)?
68
+ - **subsystem lenses** (invent as the PR demands) — auth, billing, payments, schema migration, webhooks, secrets, RBAC, multi-tenant isolation, cron/scheduling, etc.
69
+
70
+ The only subagent type is \`${REVIEWER_AGENT_NAME}\` — used for lens judgment work ("is this safe / correct / well-tested?"), runs on a mid-tier model.
71
+
72
+ 5. **fan out (only if step 4 raised ≥1 hypothesis)**: dispatch every \`${REVIEWER_AGENT_NAME}\` lens for this run **IN A SINGLE ASSISTANT TURN.** One hypothesis is a single Task block; **2+ hypotheses are MULTIPLE PARALLEL TASK TOOL_USE BLOCKS IN ONE MESSAGE.**
73
+
74
+ ⚠️ CRITICAL — WHEN YOU DISPATCH 2+ LENSES, PARALLELISM IS MANDATORY. ⚠️
75
+ The default tool-call behavior of Claude Code (and most agent runtimes) is **serial dispatch**: emit one Task call, await result, emit next, await, etc. With multiple lenses this collapses your fan-out into a sequential review where each lens adds N × (orchestrator-think-time + lens-execution-time) to wall time. **YOU MUST OVERRIDE THIS DEFAULT.** Emit ALL of your Task tool_use blocks in the SAME assistant message, BEFORE you read ANY result from ANY of them. If you find yourself emitting one Task call, then thinking about the result, then emitting another — STOP and re-issue them all together. (A lone lens is trivially "parallel" — it is one block in one turn.)
76
+
77
+ ✅ Right pattern: one assistant turn with N Task tool_use blocks → wait → N results arrive together → aggregate.
78
+ ❌ Wrong pattern: turn 1 = Task(lens A) → turn 2 (after A's result) = Task(lens B) → turn 3 (after B's result) = Task(lens C). This is the failure mode. Do not do this.
79
+
80
+ You can also include your own \`read\` / \`grep\` / \`webfetch\` calls in the SAME turn as the parallel \`${REVIEWER_AGENT_NAME}\` dispatches — concurrent context-pulling on the orchestrator side runs in parallel with the lens fan-out and costs zero extra wall time.
81
+
82
+ if a subagent errors out, times out, or returns nothing usable, retry once with the same lens; if it still fails, proceed with partial coverage and note the missing lens in the review body — do not skip the fan-out entirely on a single subagent failure. each subagent gets:
83
+ - **the absolute \`diffPath\` (and \`incrementalDiffPath\` if available) from step 2's \`${t("checkout_pr")}\` return, named verbatim in the dispatch prompt** (e.g. \`diffPath: /tmp/infraweaver-XXXX/pr-NNN-SHA.diff\`). the reviewer's baked-in system prompt selects its FIRST action on this token — paraphrasing ("review the diff", "look at this PR") sends it down the \`git diff origin/<base>\` fallback, which fails on shallow GHA checkouts. the subagent \`read\`s those files for scope; it must NOT re-derive the diff via \`git diff\` (bare \`git diff origin/<base>\` is symmetric and pulls in the inverse of any commits that landed on \`<base>\` since the branch forked — pure noise, and the git tool rejects it). reading and codebase exploration are still its job.
84
+ - **only one lens = one hypothesis** — state it as the falsifiable question, name its scope boundary (which files/paths/contract it covers), and say what evidence would confirm vs refute it; never a multi-section "review for X, Y, and Z" prompt, and never a vague "review for security" with no question the answer would change
85
+ - **a Task \`description\` set to the lens name** (e.g. \`"security"\`, \`"correctness"\`, \`"billing-subsystem"\`) — the harness reads this field to label the subagent's log lines so parallel runs can be told apart in CI output. without it, every subagent shows up as \`subagent#N\`.
86
+ - if the lens touches external contracts, instruct the subagent to verify load-bearing claims via web search rather than trust training data, and to quote source URLs in its reasoning. action runs are non-interactive — there's no human in the loop to catch "I'm pretty sure Stripe does X."
87
+ - ask the subagent to report findings with file paths and NEW line numbers from the diff so you can anchor inline comments without re-reading the entire diff.
88
+
89
+ delegation discipline:
90
+ - do NOT summarize the PR for them (biases toward a validation frame)
91
+ - do NOT hand them a curated reading list (let them discover scope)
92
+ - do NOT pre-shape their output with a finding schema
93
+ - do NOT mention the other lenses (independence is the point — overlapping findings are a strong signal)
94
+
95
+ 6. **aggregate & draft**: when the fan-out lands, merge findings; de-dup overlaps (two lenses catching the same issue = higher-confidence signal); trace each finding yourself before accepting it. drop praise, style preferences, speculative/unverified claims, findings about pre-existing code unrelated to the PR (heuristic: if the finding's root cause lives in lines this PR added or modified, it's in scope; otherwise drop unless the PR plausibly introduced or amplified the regression), and anything not actionable. also drop **bloat-shaped findings** — proposed fixes that would add defensive checks for cases that can't happen, abstractions used once, comments restating obvious code, tests asserting tautologies, or "just-in-case" guards. subagents are fallible and bias toward recommending changes; the bar for an actionable inline comment is sound + correct + elegant. recommending a change that improves only one of the three (or worse, degrades elegance to nominally improve correctness) makes the codebase worse, not better.
96
+
97
+ Apply the **Finding precedents** (defined after this checklist, before the body format) to every candidate — a precedent match means drop, unless you have specific evidence the precedent does not apply here.
98
+
99
+ **Hunt for non-anchored concerns before drafting.** After collecting your anchored findings, deliberately scan for concerns that have no specific line to point at — typically: deletion / cleanup plans for code the diff replaces or shadows; rollout sequencing (what happens to in-flight state during deploy / revert?); coverage gaps the diff implies but doesn't add; scope questions that only the human can answer (e.g. is the legacy path going away or is this a long-term dual track?); architectural risks the diff opens up that aren't a single-line bug. On substantial PRs (migrations, refactors, multi-file rewrites, version bumps that change runtime semantics), at least one such concern almost always exists; if you can't think of any, your bar is probably too high.
100
+
101
+ ${FINDING_VERIFICATION_PASS}
102
+
103
+ for surviving findings, draft inline comments with NEW line numbers from the diff — attach a \`<details>Technical details</details>\` block to any inline comment whose fix is non-trivial or has cross-file implications (see Inline technical details in the format below). every comment must be actionable, 2-3 sentences max in the visible part. use GitHub permalink format for code references. for impact-analysis findings (stale references after rename/remove), report them in the review body ordered by severity (runtime breakage > incorrect docs > stale comments) rather than as inline comments unless they're anchored to a specific line.
104
+
105
+ 7. **submit**: ALWAYS submit exactly one review via \`${t("create_pull_request_review")}\`. Do NOT call \`report_progress\` — the review is the final record and the progress comment will be cleaned up automatically.
106
+
107
+ note: the first create_pull_request_review submission may error with a one-time diff-coverage nudge listing unread TOC regions. retry the same call to proceed — optionally after reading the listed ranges. the pre-flight will not block again this session.
108
+
109
+ The review body is structured as: \`[optional alert blockquote]\` → \`[PR summary using the default format below]\`. Inline comments are passed via the \`comments\` parameter, not in the body.
110
+
111
+ The opening callout is what the author sees first — pick the one that matches what you want them to do. Four tiers, from loudest to friendliest:
112
+
113
+ - \`[!CAUTION]\` — large red banner. Reads as "this will break something."
114
+ - \`[!IMPORTANT]\` — large purple banner. Reads as "you need to look at this before merging."
115
+ - \`> ℹ️ ...\` — informational blockquote. Reads as "minor suggestions, nothing blocking."
116
+ - \`> ✅ ...\` — green friendly blockquote. Reads as "no concerns, mergeable."
117
+
118
+ Two reinforcing levers: callout intensity (above) and \`approved\` (which gates the footer Fix-button affordance — Fix renders on every non-approving review, so \`approved: true\` suppresses it). Wrapping mergeable feedback in \`[!IMPORTANT]\` trains users to click Fix on reviews that don't need fixing. Pick the tier the author's actual next action justifies.
119
+
120
+ - **critical issues** (blocks merge — bugs, security, data loss, broken core flows):
121
+ \`approved: false\`. Body opens with \`> [!CAUTION]\\n> This PR introduces ...\`, followed by the PR summary. Include all inline comments via \`comments\`.
122
+ - **must-address non-critical findings** (real consequences if shipped — incorrect behavior in non-critical paths, missing validation on user input, regressions the author should fix before merge):
123
+ \`approved: false\`. Body opens with \`> [!IMPORTANT]\\n> ...\`, followed by the PR summary. Reserve this tier for findings with concrete fallout — do NOT use \`[!IMPORTANT]\` for nits, style preferences, or "consider also" suggestions. Include all inline comments via \`comments\`.
124
+ - **minor suggestions only** (single-line nits, doc/comment polish, defer-able observations, "rough edges"):
125
+ \`approved: false\`. Body opens with \`> ℹ️ No critical issues — minor suggestions inline.\\n\\n\` followed by the PR summary. Include all inline comments via \`comments\`. Vary the wording after the emoji to fit the review (e.g. "Minor suggestions only.", "Two rough edges worth a look."), but always keep the ℹ️ prefix and keep it short.
126
+ - **informational observations** (mergeable as-is, nothing actionable — e.g. prior feedback addressed cleanly, surfacing a minor stale doc reference, calling out something noteworthy without recommending a change):
127
+ \`approved: true\`. Body opens with \`> ✅ No new issues found.\\n\\n\` followed by the PR summary. Do NOT include inline \`comments\` — the ✅ signals "no action needed", which contradicts an actionable anchor; if a point is concrete enough to anchor to a line, downgrade the whole review to "minor suggestions only" (\`approved: false\`) instead.
128
+ - **no actionable issues**:
129
+ \`approved: true\`. Body opens with \`> ✅ No new issues found.\\n\\n\` followed by the PR summary.
130
+
131
+ ${REVIEW_FINDING_PRECEDENTS}
132
+
133
+ ${PR_SUMMARY_FORMAT}`,
134
+ };
135
+ }
@@ -0,0 +1,42 @@
1
+ import type { Mode, ToolRef } from "./types.ts";
2
+
3
+ export function summarizePrMode(t: ToolRef): Mode {
4
+ return {
5
+ name: "SummarizePr",
6
+ nonCommitting: true,
7
+ description:
8
+ "Summarize a pull request's changes in a single structured comment — what it does, the key changes, and any areas worth a closer look. Does NOT review, approve, or change code (use Review for a verdict).",
9
+ prompt: `### Checklist
10
+
11
+ This mode posts ONE plain-English summary of what a PR does — an orientation aid, not a verdict. Do NOT approve, request changes, leave inline review comments, or modify any code. If a real review is wanted, that's the Review mode.
12
+
13
+ 1. **task list**: create your task list for this run as your first action.
14
+
15
+ 2. **checkout**: call \`${t("checkout_pr")}\` — this returns PR metadata and a \`diffPath\`. Read the diff TOC so you understand the scope.
16
+
17
+ 3. **Terraform anchor (when relevant)**: call \`${t("terraform_change_summary")}\` — it returns the DETERMINISTIC Terraform block changes (resource/module/data/variable/output addresses ADDED and REMOVED, plus the Terraform files touched) vs the base. It degrades green (\`ok: false\`) when git can't resolve the base — run \`${t("git_fetch")}\` on the base ref first and retry — or when the PR has no Terraform changes (then it's a general summary). Use its counts as the factual backbone of the Terraform part of your summary instead of counting by eye.
18
+
19
+ 4. **read for intent**: read the \`diffPath\` (and related files as needed) to understand WHAT the PR does and WHY — not just the mechanics. Pull as much context as you need; you are the synthesizer.
20
+
21
+ 5. **post the summary**: call \`${t("create_issue_comment")}\` ONCE on the PR with a structured summary in this shape (omit a section when it has nothing):
22
+
23
+ \`\`\`
24
+ ## Summary
25
+ {1–2 sentences: what this PR does and why, in plain English.}
26
+
27
+ ### Key changes
28
+ - **{short title}** — {one sentence}; backtick-wrap files/identifiers you name.
29
+ - ...
30
+
31
+ ### Terraform changes
32
+ {only when terraform_change_summary returned data — e.g. "Adds \\\`module.vpc\\\` and \\\`aws_s3_bucket.logs\\\`; removes \\\`aws_launch_configuration.web\\\`; edits 3 files." Use its real addresses/counts.}
33
+
34
+ ### Worth a closer look (optional)
35
+ - {non-blocking observations a reviewer might want to focus on — risk areas, sequencing, things the diff implies but doesn't address. Phrase as orientation, not findings — this is not a review.}
36
+ \`\`\`
37
+
38
+ Keep it scannable: lead with intent, alternate prose with structure, backtick-wrap identifiers, no raw diff dumps, no \`+N/-M\` stats. NEVER fabricate a change — every claim must be in the diff (the Terraform counts come from the tool).
39
+
40
+ 6. **finalize**: call \`${t("report_progress")}\` with a one-line note that the summary was posted (or the exact error if the comment failed). Do NOT call \`${t("create_pull_request_review")}\` — this mode summarizes, it does not review.`,
41
+ };
42
+ }
@@ -0,0 +1,26 @@
1
+ import { infraweaverMcpName } from "#app/external";
2
+ import type { Mode, ToolRef } from "./types.ts";
3
+
4
+ export function taskMode(t: ToolRef): Mode {
5
+ return {
6
+ name: "Task",
7
+ description:
8
+ "General-purpose tasks that don't fit other modes: answering questions, adding comments, labeling, running ad-hoc commands, or any direct request",
9
+ prompt: `### Checklist
10
+
11
+ 1. **task list**: create your task list for this run as your first action.
12
+
13
+ 2. Analyze the task. For simple operations (labeling, commenting, answering questions, running a single command), handle directly — but your answer only reaches the user through \`${t("report_progress")}\` (step 4); raw assistant text is discarded.
14
+
15
+ 3. For substantial work — code changes across multiple files, multi-step investigations:
16
+ - plan your approach before starting
17
+ - use native file and shell tools for local operations
18
+ - use ${infraweaverMcpName} MCP tools for GitHub/git operations
19
+ - if code changes are needed: review your own diff before committing — verify only intended changes are present, no debug artifacts remain, and the changes are clean enough that a senior engineer would approve without hesitation
20
+
21
+ 4. Finalize:
22
+ - if code changes were made, push to a pull request (new or existing) using \`${t("push_branch")}\` and \`${t("create_pull_request")}\` as needed. \`git status\` must be clean before you finish (see *SYSTEM* Git rules if push fails).
23
+ - call \`${t("report_progress")}\` once with results — include exact tool errors if push or PR creation failed
24
+ - if the task involved labeling, commenting, or other GitHub operations, perform those directly`,
25
+ };
26
+ }
@@ -0,0 +1,68 @@
1
+ import { REVIEWER_AGENT_NAME } from "#app/agents/reviewer";
2
+ import {
3
+ FINDING_VERIFICATION_PASS,
4
+ REVIEW_FINDING_PRECEDENTS,
5
+ TERRAFORM_SECURITY_REFUTE_LENS,
6
+ } from "#app/reviewQuality";
7
+ import { PR_SUMMARY_FORMAT } from "./prFormats.ts";
8
+ import type { Mode, ToolRef } from "./types.ts";
9
+
10
+ export function terraformCodeReviewMode(t: ToolRef): Mode {
11
+ return {
12
+ name: "TerraformCodeReview",
13
+ nonCommitting: true,
14
+ description:
15
+ "Review the Terraform changes in a HUMAN pull request — security misconfig, state-destroying edits, version-pin drift, module hygiene, idiomatic HCL — and submit one PR review. A lighter, *.tf-only counterpart to Review: it skips the app-code, test-integrity, and user-journey lenses and never modifies files or opens a PR.",
16
+ prompt: `### Checklist
17
+
18
+ This mode reviews the **Terraform** in a human PR and submits ONE review. It is read-only — never edits files, commits, pushes, or opens a PR. Scope is \`*.tf\` / \`*.tfvars\` / \`*.tf.json\` / \`*.tfvars.json\` ONLY; non-Terraform files in the diff are out of scope (do not review them — a one-line "N non-Terraform files not reviewed" note in the body is enough).
19
+
20
+ 1. **task list**: create your task list for this run as your first action.
21
+
22
+ 2. **checkout**: call \`${t("checkout_pr")}\` — it returns PR metadata, a \`diffPath\`, and (when TF declarations changed) a supplemental \`impactPath\`. read the diff TOC end-to-end; treat the \`*.tf\`/\`*.tfvars\` file ranges as your coverage checklist and ignore the rest. only AFTER the raw-diff read, use \`impactPath\` as an explicitly-incomplete list of blast-radius leads (where the PR's changed var/resource/module/output blocks are referenced elsewhere) — it never establishes coverage.
23
+
24
+ 3. **triage**: orient on what the Terraform change does — which resources/modules/providers it touches, whether it changes a stateful resource, whether it crosses a security boundary (IAM, networking, encryption, public exposure). pull context as needed: read the surrounding \`.tf\`, the module being called, \`versions.tf\`/\`required_providers\`, and any \`.tfvars\` defaults. **you are the synthesizer** — never delegate understanding.
25
+
26
+ if the Terraform change is **genuinely trivial** (a comment/whitespace-only edit, a tag value, a description string), skip the fan-out and submit a \`✅ No new issues found.\` review per step 6.
27
+
28
+ "Looks trivial but isn't" (do **NOT** skip — small diff, big blast radius):
29
+ - any change to an IAM policy/role/statement, security-group/NACL rule, or \`*_public_access*\` flag
30
+ - any change to encryption settings (KMS key, \`encrypted\`, \`*_sse_*\`), or a secret/credential value
31
+ - flipping a \`force_destroy\`, \`deletion_protection\`, \`prevent_destroy\`, or \`skip_final_snapshot\`
32
+ - a resource \`type\` swap, a \`count\`/\`for_each\` change, or anything that renames/moves a stateful resource address (destroy-and-recreate risk)
33
+ - a provider/module version constraint change (supply-chain + behaviour surface)
34
+
35
+ 4. **lens dispatch — one lens per unresolved, load-bearing hypothesis (0, 1, or 2+)** (same discipline as Review). Review the Terraform yourself first. A lens is an independent pass at a *specific, falsifiable Terraform question you could not confidently resolve on your own* — not a second opinion for its own sake. The default is **0 lenses**: most Terraform PRs resolve fully yourself. After your own pass, keep only the open questions that are **falsifiable** (you can state what confirms/refutes), **load-bearing** (a wrong answer changes the review), and **beyond your own reach** (the HCL alone doesn't settle it — needs a provider-contract check, a cross-module trace too wide to hold, or an independent adversarial read).
36
+
37
+ Dispatch **one \`${REVIEWER_AGENT_NAME}\` lens per surviving hypothesis** — **0** for most PRs, **1** when a single real question remains, **2+** when several do. The count follows the questions: never manufacture a hypothesis to reach two, never drop a real one to avoid dispatching alone. When you dispatch 2+, they go out together in parallel (step 5). A substantive diff (multiple resources/modules AND >150 net lines of Terraform) or a high-stakes boundary (IAM, networking, encryption, stateful data stores, multi-account/workspace wiring) is where an unresolved question is most likely load-bearing — but a 1-line IAM or CIDR change can raise exactly one lens-worthy hypothesis while a large mechanical reformat raises none. Size is a hint, never the gate.
38
+
39
+ Terraform-scoped lens menu (each entry is a source of hypotheses, not a checklist — pick one only when it names a question you could not resolve yourself; invent as the diff demands):
40
+ - **security & exposure** — IAM least-privilege (no wildcard actions/resources), public exposure (S3/SG/RDS), encryption at rest/in transit, IMDSv2, plaintext secrets in \`.tf\`/\`.tfvars\`.
41
+ - **state safety & blast radius** — does the change force a destroy/replace of a stateful resource? is a \`moved {}\` block needed for a relocated address? \`create_before_destroy\` where a replace is unavoidable?
42
+ - **correctness** — would this pass \`terraform validate\`? deprecated syntax/arguments, wrong types, unresolved interpolations, \`count\`/\`for_each\` index churn.
43
+ - **version & supply-chain** — provider/module pins (exact vs floating), a bumped major's breaking changes, an unpinned remote module source.
44
+ - **module & interface hygiene** — variable validation/defaults (secure-by-default), unused/undocumented variables, sensitive outputs not marked \`sensitive\`.
45
+
46
+ The only subagent type is \`${REVIEWER_AGENT_NAME}\`. Do NOT use the app-code, test-integrity, performance, or user-journey lenses from Review — they don't apply to IaC.
47
+
48
+ 5. **fan out (only if step 4 raised ≥1 hypothesis)**: dispatch every \`${REVIEWER_AGENT_NAME}\` lens **IN A SINGLE ASSISTANT TURN** — one hypothesis is a single Task block; **2+ are MULTIPLE PARALLEL TASK TOOL_USE BLOCKS IN ONE MESSAGE** (when you dispatch 2+, serial dispatch defeats the only reason to fan out). Each subagent gets: the absolute \`diffPath\` from step 2 named verbatim; **only one lens = one hypothesis** (the falsifiable question with its scope boundary and what would confirm vs refute it, never a vague "review for security"); a Task \`description\` set to the lens name; and an instruction to verify any version/provider-contract claim via web search and quote source URLs (action runs are non-interactive). Do NOT summarise the diff for them, hand them a reading list, pre-shape their output, or mention the other lenses. On a subagent failure, retry once, then proceed with partial coverage and note the missing lens in the body.
49
+
50
+ 6. **aggregate, draft & submit**: merge findings; de-dup overlaps (two lenses catching the same issue = higher confidence); trace each finding yourself against the diff before accepting it. drop praise, style preferences, unverified claims, findings rooted in pre-existing \`.tf\` the PR didn't touch, and **bloat-shaped findings** (defensive guards for impossible cases, abstractions used once, comments restating code, "just-in-case" variables). Apply the **Finding precedents** below; a match means drop unless you have specific evidence it doesn't apply.
51
+
52
+ ${FINDING_VERIFICATION_PASS}
53
+
54
+ ${TERRAFORM_SECURITY_REFUTE_LENS}
55
+
56
+ When the finding under test is a **cloud-misconfig** (public exposure, missing encryption, over-broad IAM, open ingress), the verification dispatch MUST include the Terraform-security refute lens above in addition to the generic charge — these IaC false-positive patterns (a separate hardening resource, a disabled resource, a call-site override) are exactly what a generic code lens misses.
57
+
58
+ Hunt for non-anchored concerns too — an absent \`moved {}\` block, a missing \`prevent_destroy\` on a data store, a destroy/replace the human must sign off, a version bump whose changelog they should read. Then ALWAYS submit exactly one review via \`${t("create_pull_request_review")}\` (never \`report_progress\` for review output). Inline comments anchor to NEW line numbers via the \`comments\` parameter; non-anchorable concerns go in the body \`### \` sections.
59
+
60
+ Same opening-callout ladder + \`approved\` lever as Review — \`[!CAUTION]\` (a state-destroying or security-breaking change) → \`[!IMPORTANT]\` (a real misconfig to fix before merge) → \`> ℹ️ ...\` (minor HCL/style nits) → \`> ✅ No new issues found.\` (mergeable, \`approved: true\`, no inline comments). Pick the tier the author's actual next action justifies; do not wrap mergeable feedback in \`[!IMPORTANT]\`. note: the first \`create_pull_request_review\` submission may error with a one-time diff-coverage nudge — retry the same call to proceed.
61
+
62
+ 7. **guardrails**: never modify \`*.tf\`/\`*.tfvars\`, never commit/push, never open a PR or issue. Only Terraform files are in scope. The review is the only deliverable.
63
+
64
+ ${REVIEW_FINDING_PRECEDENTS}
65
+
66
+ ${PR_SUMMARY_FORMAT}`,
67
+ };
68
+ }
@@ -0,0 +1,17 @@
1
+ export interface Mode {
2
+ name: string;
3
+ description: string;
4
+ // step-by-step guidance returned when the agent calls select_mode.
5
+ // custom user-defined modes supply this; built-in modes define it here.
6
+ prompt?: string | undefined;
7
+ // true for modes that legitimately never modify the working tree — they
8
+ // complete by submitting a review, posting a comment, or reporting a
9
+ // verdict. drives NON_COMMITTING_MODES (the post-run dirty-tree gate);
10
+ // declared here, next to the mode it describes, so a new read-only mode
11
+ // can't forget to update a hand-maintained list elsewhere.
12
+ nonCommitting?: boolean | undefined;
13
+ }
14
+
15
+ /** binds a bare MCP tool name to its agent-specific reference (per-run, via
16
+ * formatMcpToolRef) — the single closure each mode factory needs from computeModes. */
17
+ export type ToolRef = (toolName: string) => string;
@@ -0,0 +1,44 @@
1
+ import { REFACTOR_PR_FORMAT } from "./prFormats.ts";
2
+ import type { Mode, ToolRef } from "./types.ts";
3
+
4
+ export function updateDependenciesMode(t: ToolRef): Mode {
5
+ return {
6
+ name: "UpdateDependencies",
7
+ description:
8
+ "Upgrade outdated or unpinned Terraform provider and module versions to current, supported pins, then PROVE the bump is safe — config-preserving bumps via the equivalence check (same resource set + arguments), behaviour-affecting major bumps via a reviewed plan delta (or shipped `proposed — unproven`). One scoped PR per provider/module.",
9
+ prompt: `### Checklist
10
+
11
+ This mode turns the version-currency ADVISORY into action: it bumps stale/unpinned provider + module versions and PROVES the bump is safe before a human sees it, reusing the Refactor proof machinery. A pin bump that needs no code change is behaviour-preserving (proven by \`${t("terraform_equivalence_check")}\`); a major bump that changes resource shape ALTERS behaviour and is proven by a reviewed plan delta (or shipped \`proposed — unproven\`) — never a false equivalence claim.
12
+
13
+ 1. **task list**: create your task list for this run as your first action.
14
+
15
+ 2. **detect**: call \`${t("terraform_version_currency")}\` — it returns provider/module pins that are outdated or unpinned, each with the current constraint, the latest version, and how many majors behind it is.
16
+
17
+ 3. **pick scope — ONE provider or module per PR**: take the most impactful safe upgrade first (a security-relevant provider bump, or pinning an unpinned pin to an exact constraint). **Targeted directive override:** if YOUR TASK names a specific provider/module, act only on it. Unless the task asks for more, open **at most one PR this run**; list deferred upgrades in the final report (never skip silently).
18
+
19
+ 4. **classify the bump (this decides the proof)**:
20
+ - **config-preserving** — a version-constraint change only (a patch/minor bump, or pinning a floating version) with NO change to any resource or argument. This is behaviour-preserving: prove it with \`${t("terraform_equivalence_check")}\` exactly as Refactor does — it must return \`equivalent: true\` (same resource set, identical arguments, validate + fmt clean). The push hard-fails an unproven one.
21
+ - **behaviour-affecting** — a major bump that requires resource/argument changes (renamed args, new required blocks, changed defaults). Equivalence is FALSE and must never be claimed. Make the minimal code changes the new major requires — use \`${t("terraform_validate")}\`'s \`providers\` majors and \`${t("terraform_provider_schema")}\` to get the new argument shape — then prove via the reviewed-plan path in step 6.
22
+
23
+ 4b. **for a provider MAJOR bump, call \`${t("terraform_provider_upgrade")}\` BEFORE editing.** It returns the codified breaking changes for that exact major boundary, each with the file it is in. Three things in its output decide what you do next, and none of them can be inferred from the diff:
24
+ - \`needs_human: true\` — the transform MOVES STATE (a removed resource type, or a rename the vendor marks manual). A config-only edit there DESTROYS AND RECREATES the live resource. Never apply one autonomously: put it in its own PR, label it \`needs-human\` (\`${t("add_labels")}\`), and name the affected resources in the body.
25
+ - \`value_change\` — the change is NOT a pure rename; the value must be inverted or retyped. Mechanically renaming these is how a live network silently gets its routing flipped while everything stays green.
26
+ - \`schema_agreement: "unverified"\` — the installed provider schema did not corroborate that entry. Treat it as a hint to check, never as fact.
27
+ The packs are explicitly NOT exhaustive; step 5's \`unknown_arguments\` is the authoritative completeness check. If the output names an official vendor migration tool, prefer it and review its diff rather than hand-migrating what it already handles.
28
+
29
+ 5. **validate**: \`${t("terraform_validate")}\` — never push a bump whose validate didn't pass. Use its \`unknown_arguments\` to catch arguments the new major dropped or renamed — including any the transform pack missed.
30
+
31
+ 6. **prove safety**:
32
+ - config-preserving → \`${t("terraform_equivalence_check")}\` is the proof (\`equivalent: true\`).
33
+ - behaviour-affecting → call \`${t("terraform_plan")}\` when cloud credentials are present and attach the **reviewed plan delta** as the proof; treat \`has_destroy_or_replace\`/\`stateful_destructive\` exactly as Remediate (a stateful destroy/replace is hard-blocked at push unless \`allow_replace\` covers that address — abandon otherwise). With NO creds, do not guess: ship the PR labelled \`proposed — unproven\` (\`${t("add_labels")}\`) with the proposed-unproven banner, stating the major bump ALTERS behaviour and the plan must be reviewed.
34
+ - record either proof with \`${t("terraform_emit_evidence")}\`.
35
+
36
+ 7. **keep module tests consistent** (only if the bump changed a local module's interface): \`${t("terraform_module_tests")}\` — update the drifting \`examples/\`/tests, never weaken an assertion.
37
+
38
+ 8. **open the PR (MANDATORY body)**: branch \`infraweaver/update-deps-<slug>\` from the scanned HEAD, \`git add\` only the changed files, commit naming the provider/module + old → new version, \`${t("push_branch")}\`, then \`${t("create_pull_request")}\` (omit \`base\`). Build the body with the **Refactor PR format** below — the equivalence proof for a config-preserving bump, or the reviewed-plan / proposed-unproven section for a major bump — stating old → new and linking the provider/module changelog. Never auto-merge.
39
+
40
+ 9. **finalize**: \`${t("report_progress")}\` once — which pin was bumped (old → new), the proof verdict, the PR link, and the deferred upgrades (or the exact tool error).
41
+
42
+ ${REFACTOR_PR_FORMAT}`,
43
+ };
44
+ }