terramend 0.2.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.
- package/LICENSE +661 -0
- package/README.md +145 -0
- package/dist/agents/claude.d.ts +73 -0
- package/dist/agents/claudePretoolGate.d.ts +99 -0
- package/dist/agents/gateServer.d.ts +7 -0
- package/dist/agents/index.d.ts +6 -0
- package/dist/agents/nativeFsDenies.d.ts +28 -0
- package/dist/agents/opencode.d.ts +231 -0
- package/dist/agents/opencodePlugin.d.ts +85 -0
- package/dist/agents/opencodeShared.d.ts +40 -0
- package/dist/agents/postRun.d.ts +132 -0
- package/dist/agents/reviewer.d.ts +38 -0
- package/dist/agents/sessionLabeler.d.ts +97 -0
- package/dist/agents/shared.d.ts +189 -0
- package/dist/agents/subagentModels.d.ts +19 -0
- package/dist/agents/subagentToolGates.d.ts +55 -0
- package/dist/cli.mjs +197426 -0
- package/dist/external.d.ts +227 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +196783 -0
- package/dist/internal/index.d.ts +18 -0
- package/dist/internal.js +1714 -0
- package/dist/lifecycle.d.ts +2 -0
- package/dist/main.d.ts +8 -0
- package/dist/mcp/arkConfig.d.ts +1 -0
- package/dist/mcp/checkSuite.d.ts +25 -0
- package/dist/mcp/checkout.d.ts +77 -0
- package/dist/mcp/comment.d.ts +119 -0
- package/dist/mcp/commitInfo.d.ts +9 -0
- package/dist/mcp/crosswalk.d.ts +105 -0
- package/dist/mcp/dependencies.d.ts +8 -0
- package/dist/mcp/geminiSanitizer.d.ts +28 -0
- package/dist/mcp/git.d.ts +46 -0
- package/dist/mcp/guardrails.d.ts +104 -0
- package/dist/mcp/issue.d.ts +18 -0
- package/dist/mcp/issueComments.d.ts +9 -0
- package/dist/mcp/issueEvents.d.ts +9 -0
- package/dist/mcp/issueInfo.d.ts +9 -0
- package/dist/mcp/labels.d.ts +12 -0
- package/dist/mcp/localContext.d.ts +19 -0
- package/dist/mcp/moduleExtraction.d.ts +71 -0
- package/dist/mcp/moduleTests.d.ts +104 -0
- package/dist/mcp/modules.d.ts +179 -0
- package/dist/mcp/output.d.ts +12 -0
- package/dist/mcp/pathSafety.d.ts +14 -0
- package/dist/mcp/policy.d.ts +48 -0
- package/dist/mcp/pr.d.ts +49 -0
- package/dist/mcp/prInfo.d.ts +9 -0
- package/dist/mcp/providerSchema.d.ts +50 -0
- package/dist/mcp/review.d.ts +199 -0
- package/dist/mcp/reviewComments.d.ts +178 -0
- package/dist/mcp/roots.d.ts +58 -0
- package/dist/mcp/scope.d.ts +15 -0
- package/dist/mcp/selectMode.d.ts +18 -0
- package/dist/mcp/server.d.ts +48 -0
- package/dist/mcp/shared.d.ts +47 -0
- package/dist/mcp/shell.d.ts +37 -0
- package/dist/mcp/staleFix.d.ts +51 -0
- package/dist/mcp/terraform/cost.d.ts +55 -0
- package/dist/mcp/terraform/currency.d.ts +94 -0
- package/dist/mcp/terraform/decisions.d.ts +178 -0
- package/dist/mcp/terraform/findings.d.ts +75 -0
- package/dist/mcp/terraform/plan.d.ts +157 -0
- package/dist/mcp/terraform/scanners.d.ts +131 -0
- package/dist/mcp/terraform/tools.d.ts +63 -0
- package/dist/mcp/terraform/types.d.ts +172 -0
- package/dist/mcp/terraform.d.ts +22 -0
- package/dist/mcp/terratest.d.ts +83 -0
- package/dist/mcp/upload.d.ts +6 -0
- package/dist/models.d.ts +171 -0
- package/dist/modes.d.ts +26 -0
- package/dist/prep/index.d.ts +7 -0
- package/dist/prep/installNodeDependencies.d.ts +2 -0
- package/dist/prep/installPythonDependencies.d.ts +2 -0
- package/dist/prep/types.d.ts +31 -0
- package/dist/reviewQuality.d.ts +64 -0
- package/dist/skills/terraform-best-practices/SKILL.md +369 -0
- package/dist/toolState.d.ts +135 -0
- package/dist/utils/activity.d.ts +40 -0
- package/dist/utils/agent.d.ts +20 -0
- package/dist/utils/agentHangReport.d.ts +38 -0
- package/dist/utils/apiFetch.d.ts +19 -0
- package/dist/utils/apiKeys.d.ts +41 -0
- package/dist/utils/apiUrl.d.ts +20 -0
- package/dist/utils/assets.d.ts +8 -0
- package/dist/utils/billingErrors.d.ts +85 -0
- package/dist/utils/body.d.ts +34 -0
- package/dist/utils/buildTerramendFooter.d.ts +25 -0
- package/dist/utils/byokFallback.d.ts +85 -0
- package/dist/utils/claudeSubscription.d.ts +30 -0
- package/dist/utils/cli.d.ts +10 -0
- package/dist/utils/codexHome.d.ts +29 -0
- package/dist/utils/codexOAuth.d.ts +60 -0
- package/dist/utils/diffCoverage.d.ts +63 -0
- package/dist/utils/errorReport.d.ts +17 -0
- package/dist/utils/exitHandler.d.ts +8 -0
- package/dist/utils/fixDoubleEscapedString.d.ts +1 -0
- package/dist/utils/gitAuth.d.ts +84 -0
- package/dist/utils/gitAuthServer.d.ts +24 -0
- package/dist/utils/github.d.ts +78 -0
- package/dist/utils/globals.d.ts +3 -0
- package/dist/utils/install.d.ts +60 -0
- package/dist/utils/instructions.d.ts +48 -0
- package/dist/utils/leapingComment.d.ts +11 -0
- package/dist/utils/learnings.d.ts +62 -0
- package/dist/utils/learningsTruncate.d.ts +25 -0
- package/dist/utils/lifecycle.d.ts +57 -0
- package/dist/utils/log.d.ts +111 -0
- package/dist/utils/normalizeEnv.d.ts +30 -0
- package/dist/utils/openCodeModels.d.ts +11 -0
- package/dist/utils/overrides.d.ts +40 -0
- package/dist/utils/packageManager.d.ts +49 -0
- package/dist/utils/patchWorkflowRunFields.d.ts +29 -0
- package/dist/utils/payload.d.ts +105 -0
- package/dist/utils/prSummary.d.ts +61 -0
- package/dist/utils/progressComment.d.ts +146 -0
- package/dist/utils/providerErrors.d.ts +31 -0
- package/dist/utils/rangeDiff.d.ts +51 -0
- package/dist/utils/remediationCommand.d.ts +55 -0
- package/dist/utils/retry.d.ts +13 -0
- package/dist/utils/reviewCleanup.d.ts +14 -0
- package/dist/utils/run.d.ts +9 -0
- package/dist/utils/runContext.d.ts +60 -0
- package/dist/utils/runContextData.d.ts +23 -0
- package/dist/utils/runErrorRenderer.d.ts +64 -0
- package/dist/utils/runLifecycle.d.ts +86 -0
- package/dist/utils/runStartupLog.d.ts +15 -0
- package/dist/utils/secrets.d.ts +22 -0
- package/dist/utils/setup.d.ts +90 -0
- package/dist/utils/shell.d.ts +32 -0
- package/dist/utils/skills.d.ts +10 -0
- package/dist/utils/subprocess.d.ts +80 -0
- package/dist/utils/terraformMcp.d.ts +42 -0
- package/dist/utils/time.d.ts +15 -0
- package/dist/utils/timer.d.ts +23 -0
- package/dist/utils/todoTracking.d.ts +16 -0
- package/dist/utils/token.d.ts +39 -0
- package/dist/utils/version.d.ts +2 -0
- package/dist/utils/versioning.d.ts +7 -0
- package/dist/utils/vertex.d.ts +16 -0
- package/dist/utils/workflow.d.ts +13 -0
- package/package.json +119 -0
- package/src/agents/claude.test.ts +1016 -0
- package/src/agents/claude.ts +1246 -0
- package/src/agents/claudePretoolGate.test.ts +28 -0
- package/src/agents/claudePretoolGate.ts +173 -0
- package/src/agents/gateServer.test.ts +204 -0
- package/src/agents/gateServer.ts +124 -0
- package/src/agents/index.ts +10 -0
- package/src/agents/nativeFsDenies.ts +82 -0
- package/src/agents/opencode.test.ts +1440 -0
- package/src/agents/opencode.ts +1312 -0
- package/src/agents/opencodePlugin.ts +222 -0
- package/src/agents/opencodeShared.test.ts +34 -0
- package/src/agents/opencodeShared.ts +121 -0
- package/src/agents/postRun.test.ts +549 -0
- package/src/agents/postRun.ts +535 -0
- package/src/agents/reviewer.ts +104 -0
- package/src/agents/sessionLabeler.test.ts +247 -0
- package/src/agents/sessionLabeler.ts +178 -0
- package/src/agents/shared.test.ts +76 -0
- package/src/agents/shared.ts +292 -0
- package/src/agents/subagentModels.test.ts +113 -0
- package/src/agents/subagentModels.ts +40 -0
- package/src/agents/subagentRegistration.test.ts +41 -0
- package/src/agents/subagentToolGates.ts +114 -0
- package/src/cli.test.ts +129 -0
- package/src/cli.ts +105 -0
- package/src/commands/gha.test.ts +192 -0
- package/src/commands/gha.ts +188 -0
- package/src/commands/mcp.ts +122 -0
- package/src/config.ts +1 -0
- package/src/entry.ts +7 -0
- package/src/entryPost.stdlibOnly.test.ts +109 -0
- package/src/entryPost.ts +99 -0
- package/src/external.test.ts +16 -0
- package/src/external.ts +302 -0
- package/src/index.ts +11 -0
- package/src/internal/index.ts +71 -0
- package/src/lifecycle.ts +2 -0
- package/src/main.test.ts +873 -0
- package/src/main.ts +712 -0
- package/src/mcp/__fixtures__/terramend-scratch-pr-49-review-3485940013.json +110 -0
- package/src/mcp/__fixtures__/terramend-scratch-pr-64-review-3531000326.json +14 -0
- package/src/mcp/__fixtures__/terramend-test-repo-pr-1.diff.json +67 -0
- package/src/mcp/__snapshots__/checkout.test.ts.snap +109 -0
- package/src/mcp/__snapshots__/reviewComments.test.ts.snap +71 -0
- package/src/mcp/arkConfig.ts +7 -0
- package/src/mcp/checkSuite.test.ts +245 -0
- package/src/mcp/checkSuite.ts +255 -0
- package/src/mcp/checkout.test.ts +752 -0
- package/src/mcp/checkout.ts +886 -0
- package/src/mcp/comment.test.ts +772 -0
- package/src/mcp/comment.ts +582 -0
- package/src/mcp/commitInfo.test.ts +127 -0
- package/src/mcp/commitInfo.ts +61 -0
- package/src/mcp/crosswalk.test.ts +106 -0
- package/src/mcp/crosswalk.ts +339 -0
- package/src/mcp/dependencies.test.ts +309 -0
- package/src/mcp/dependencies.ts +189 -0
- package/src/mcp/geminiSanitizer.test.ts +287 -0
- package/src/mcp/geminiSanitizer.ts +207 -0
- package/src/mcp/git.test.ts +1083 -0
- package/src/mcp/git.ts +890 -0
- package/src/mcp/guardrails.test.ts +705 -0
- package/src/mcp/guardrails.ts +465 -0
- package/src/mcp/issue.test.ts +113 -0
- package/src/mcp/issue.ts +73 -0
- package/src/mcp/issueComments.test.ts +69 -0
- package/src/mcp/issueComments.ts +48 -0
- package/src/mcp/issueEvents.test.ts +134 -0
- package/src/mcp/issueEvents.ts +100 -0
- package/src/mcp/issueInfo.test.ts +104 -0
- package/src/mcp/issueInfo.ts +72 -0
- package/src/mcp/labels.test.ts +52 -0
- package/src/mcp/labels.ts +34 -0
- package/src/mcp/localContext.ts +28 -0
- package/src/mcp/localServer.test.ts +75 -0
- package/src/mcp/localServer.ts +131 -0
- package/src/mcp/moduleExtraction.test.ts +261 -0
- package/src/mcp/moduleExtraction.ts +313 -0
- package/src/mcp/moduleTests.test.ts +269 -0
- package/src/mcp/moduleTests.ts +421 -0
- package/src/mcp/modules.test.ts +640 -0
- package/src/mcp/modules.ts +696 -0
- package/src/mcp/output.test.ts +96 -0
- package/src/mcp/output.ts +70 -0
- package/src/mcp/pathSafety.test.ts +44 -0
- package/src/mcp/pathSafety.ts +28 -0
- package/src/mcp/policy.test.ts +282 -0
- package/src/mcp/policy.ts +199 -0
- package/src/mcp/pr.test.ts +387 -0
- package/src/mcp/pr.ts +194 -0
- package/src/mcp/prInfo.test.ts +96 -0
- package/src/mcp/prInfo.ts +91 -0
- package/src/mcp/providerSchema.test.ts +85 -0
- package/src/mcp/providerSchema.ts +175 -0
- package/src/mcp/review.test.ts +936 -0
- package/src/mcp/review.ts +923 -0
- package/src/mcp/reviewComments.test.ts +549 -0
- package/src/mcp/reviewComments.ts +896 -0
- package/src/mcp/roots.test.ts +175 -0
- package/src/mcp/roots.ts +217 -0
- package/src/mcp/scope.test.ts +59 -0
- package/src/mcp/scope.ts +65 -0
- package/src/mcp/security.test.ts +720 -0
- package/src/mcp/selectMode.test.ts +210 -0
- package/src/mcp/selectMode.ts +181 -0
- package/src/mcp/server.test.ts +292 -0
- package/src/mcp/server.ts +403 -0
- package/src/mcp/shared.ts +100 -0
- package/src/mcp/shell.test.ts +520 -0
- package/src/mcp/shell.ts +505 -0
- package/src/mcp/staleFix.test.ts +237 -0
- package/src/mcp/staleFix.ts +277 -0
- package/src/mcp/terraform/cost.ts +163 -0
- package/src/mcp/terraform/currency.test.ts +338 -0
- package/src/mcp/terraform/currency.ts +336 -0
- package/src/mcp/terraform/decisions.ts +527 -0
- package/src/mcp/terraform/findings.ts +333 -0
- package/src/mcp/terraform/plan.ts +348 -0
- package/src/mcp/terraform/scanners.ts +809 -0
- package/src/mcp/terraform/tools.test.ts +1071 -0
- package/src/mcp/terraform/tools.ts +908 -0
- package/src/mcp/terraform/types.ts +305 -0
- package/src/mcp/terraform.test.ts +1957 -0
- package/src/mcp/terraform.ts +23 -0
- package/src/mcp/terratest.test.ts +105 -0
- package/src/mcp/terratest.ts +196 -0
- package/src/mcp/toolFiltering.test.ts +85 -0
- package/src/mcp/upload.test.ts +180 -0
- package/src/mcp/upload.ts +112 -0
- package/src/models.test.ts +300 -0
- package/src/models.ts +708 -0
- package/src/modes.test.ts +107 -0
- package/src/modes.ts +880 -0
- package/src/prep/index.ts +43 -0
- package/src/prep/installNodeDependencies.test.ts +298 -0
- package/src/prep/installNodeDependencies.ts +196 -0
- package/src/prep/installPythonDependencies.test.ts +268 -0
- package/src/prep/installPythonDependencies.ts +199 -0
- package/src/prep/types.ts +38 -0
- package/src/reviewQuality.test.ts +63 -0
- package/src/reviewQuality.ts +134 -0
- package/src/runCli.test.ts +214 -0
- package/src/runCli.ts +282 -0
- package/src/skills/terraform-best-practices/SKILL.md +369 -0
- package/src/toolState.test.ts +45 -0
- package/src/toolState.ts +252 -0
- package/src/utils/activity.test.ts +188 -0
- package/src/utils/activity.ts +210 -0
- package/src/utils/agent.test.ts +251 -0
- package/src/utils/agent.ts +139 -0
- package/src/utils/agentHangReport.test.ts +203 -0
- package/src/utils/agentHangReport.ts +170 -0
- package/src/utils/apiFetch.test.ts +115 -0
- package/src/utils/apiFetch.ts +62 -0
- package/src/utils/apiKeys.test.ts +344 -0
- package/src/utils/apiKeys.ts +206 -0
- package/src/utils/apiUrl.test.ts +30 -0
- package/src/utils/apiUrl.ts +59 -0
- package/src/utils/assets.test.ts +153 -0
- package/src/utils/assets.ts +107 -0
- package/src/utils/billingErrors.test.ts +121 -0
- package/src/utils/billingErrors.ts +189 -0
- package/src/utils/body.test.ts +217 -0
- package/src/utils/body.ts +168 -0
- package/src/utils/buildTerramendFooter.test.ts +38 -0
- package/src/utils/buildTerramendFooter.ts +82 -0
- package/src/utils/byokFallback.test.ts +205 -0
- package/src/utils/byokFallback.ts +128 -0
- package/src/utils/claudeSubscription.test.ts +179 -0
- package/src/utils/claudeSubscription.ts +93 -0
- package/src/utils/cli.ts +31 -0
- package/src/utils/codexHome.test.ts +190 -0
- package/src/utils/codexHome.ts +191 -0
- package/src/utils/codexOAuth.ts +147 -0
- package/src/utils/codexRefreshDetect.test.ts +85 -0
- package/src/utils/codexRefreshDetect.ts +35 -0
- package/src/utils/diffCoverage.test.ts +468 -0
- package/src/utils/diffCoverage.ts +404 -0
- package/src/utils/errorReport.test.ts +135 -0
- package/src/utils/errorReport.ts +83 -0
- package/src/utils/exitHandler.ts +35 -0
- package/src/utils/fixDoubleEscapedString.ts +9 -0
- package/src/utils/ghaCore.ts +13 -0
- package/src/utils/gitAuth.test.ts +322 -0
- package/src/utils/gitAuth.ts +263 -0
- package/src/utils/gitAuthServer.test.ts +260 -0
- package/src/utils/gitAuthServer.ts +182 -0
- package/src/utils/github.test.ts +615 -0
- package/src/utils/github.ts +538 -0
- package/src/utils/globals.ts +9 -0
- package/src/utils/humanEditCapture.test.ts +100 -0
- package/src/utils/humanEditCapture.ts +193 -0
- package/src/utils/install.test.ts +768 -0
- package/src/utils/install.ts +492 -0
- package/src/utils/instructions.test.ts +240 -0
- package/src/utils/instructions.ts +543 -0
- package/src/utils/leapingComment.test.ts +51 -0
- package/src/utils/leapingComment.ts +18 -0
- package/src/utils/learnings.test.ts +87 -0
- package/src/utils/learnings.ts +138 -0
- package/src/utils/learningsTocRender.test.ts +116 -0
- package/src/utils/learningsTruncate.test.ts +39 -0
- package/src/utils/learningsTruncate.ts +42 -0
- package/src/utils/lifecycle.test.ts +195 -0
- package/src/utils/lifecycle.ts +198 -0
- package/src/utils/log.test.ts +402 -0
- package/src/utils/log.ts +432 -0
- package/src/utils/normalizeEnv.test.ts +91 -0
- package/src/utils/normalizeEnv.ts +106 -0
- package/src/utils/openCodeModels.ts +82 -0
- package/src/utils/overrides.test.ts +89 -0
- package/src/utils/overrides.ts +98 -0
- package/src/utils/packageManager.test.ts +321 -0
- package/src/utils/packageManager.ts +257 -0
- package/src/utils/patchWorkflowRunFields.test.ts +92 -0
- package/src/utils/patchWorkflowRunFields.ts +150 -0
- package/src/utils/payload.test.ts +497 -0
- package/src/utils/payload.ts +371 -0
- package/src/utils/postApiFetch.ts +51 -0
- package/src/utils/prSummary.test.ts +224 -0
- package/src/utils/prSummary.ts +147 -0
- package/src/utils/progressComment.ts +261 -0
- package/src/utils/providerErrors.test.ts +315 -0
- package/src/utils/providerErrors.ts +172 -0
- package/src/utils/rangeDiff.test.ts +236 -0
- package/src/utils/rangeDiff.ts +182 -0
- package/src/utils/remediationCommand.test.ts +163 -0
- package/src/utils/remediationCommand.ts +119 -0
- package/src/utils/retry.test.ts +153 -0
- package/src/utils/retry.ts +58 -0
- package/src/utils/reviewCleanup.ts +106 -0
- package/src/utils/run.ts +99 -0
- package/src/utils/runContext.ts +145 -0
- package/src/utils/runContextData.ts +58 -0
- package/src/utils/runErrorRenderer.test.ts +95 -0
- package/src/utils/runErrorRenderer.ts +259 -0
- package/src/utils/runFixture.ts +76 -0
- package/src/utils/runLifecycle.ts +237 -0
- package/src/utils/runStartupLog.ts +60 -0
- package/src/utils/secrets.test.ts +103 -0
- package/src/utils/secrets.ts +177 -0
- package/src/utils/setup.test.ts +509 -0
- package/src/utils/setup.ts +352 -0
- package/src/utils/shell.ts +103 -0
- package/src/utils/skills.test.ts +46 -0
- package/src/utils/skills.ts +67 -0
- package/src/utils/subprocess.test.ts +170 -0
- package/src/utils/subprocess.ts +438 -0
- package/src/utils/terraformMcp.test.ts +63 -0
- package/src/utils/terraformMcp.ts +83 -0
- package/src/utils/time.test.ts +105 -0
- package/src/utils/time.ts +59 -0
- package/src/utils/timer.test.ts +91 -0
- package/src/utils/timer.ts +72 -0
- package/src/utils/todoTracking.test.ts +223 -0
- package/src/utils/todoTracking.ts +167 -0
- package/src/utils/token.test.ts +239 -0
- package/src/utils/token.ts +186 -0
- package/src/utils/version.ts +10 -0
- package/src/utils/versioning.test.ts +34 -0
- package/src/utils/versioning.ts +44 -0
- package/src/utils/vertex.ts +85 -0
- package/src/utils/workflow.ts +25 -0
package/dist/modes.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type AgentId } from "#app/external";
|
|
2
|
+
export interface Mode {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
prompt?: string | undefined;
|
|
6
|
+
}
|
|
7
|
+
export declare const PR_SUMMARY_FORMAT = "### Default format\n\nThe body has at most four parts in this exact order:\n\n1. **Reviewed changes preamble** \u2014 one bolded inline lead-in describing what was reviewed in this run, a bullet list of the substantive changes, and an HTML comment carrying review metadata for downstream agents.\n2. **Cross-cutting issue sections** (zero or more) \u2014 one `### ` heading per concern, with a human-readable problem write-up and a collapsed `<details>Technical details</details>` block underneath.\n3. **`### \u2139\uFE0F Nitpicks`** (only if there are nits worth surfacing in the body) \u2014 a flat bullet list, no technical-details block.\n4. **`Suppressed findings` collapsed block** at the very bottom (only when the adversarial verification pass suppressed at least one \uD83D\uDEA8/\u26A0\uFE0F candidate) \u2014 the one-line-per-finding audit trail for the false-positive filter.\n\nInline-vs-body split: concerns that anchor to a specific line go inline (use the `comments` parameter). Body `### ` sections are reserved for concerns that **have no line to anchor to** \u2014 typically because the concern is about *absence* (something the diff should have done but didn't), *sequencing* (rollout / deletion / migration order), *design decisions only the human can make*, or *scope questions the diff implicitly raises but doesn't address*. A concern that anchors to a line but has broad implications still goes inline (use the technical-details block there to capture the implications \u2014 see Inline technical details below). If you found no non-anchorable concerns, the body has zero `### ` issue sections \u2014 just the preamble + metadata.\n\n## 1. Reviewed changes preamble\n\nOpen with a single bolded inline lead-in followed immediately by the bullet list (no `### Key changes` heading, no `<b>TL;DR</b>`):\n\n```\n**Reviewed changes** \u2014 one sentence on what was reviewed in this run. For Review (initial), this is what the PR does and why. For IncrementalReview, this is what changed since the prior terramend review. Focus on intent, not mechanics.\n\n- **Short human-readable title** \u2014 1 sentence per substantive change. Write a short prose phrase; when you name a file, type, or function, put that name in backticks (e.g. **Add \\`TodoTracker\\` for live checklists**). A reviewer should understand the full reviewed scope from this list alone \u2014 this IS the dispassionate \"what was reviewed and what changed\" overview, so cover the substantive changes, not just the loudest ones.\n\n<!--\nTerramend review metadata \u2014 for any agent (or human-with-agent) reading this\nreview. Incorporate the fields below into your understanding of the context\nthis review was made in. The findings below were written against\n{head_sha_short}; if new commits have landed on {head_ref} since this review\nwas submitted, treat any specific bug, file, or line callout as POTENTIALLY\nSTALE \u2014 re-diff against {head_sha_short} (or trigger a fresh review) and\nfactor commits past {head_sha_short} into your understanding of the current\nstate before acting on findings.\n\n- Mode: Review (initial) or IncrementalReview (delta against prior terramend review)\n- Files reviewed: {file_count}\n- Commits reviewed: {commit_count}\n- Base: {base_ref} ({base_sha_short})\n- Head: {head_ref} ({head_sha_short})\n- Reviewed commits:\n - {sha_short} \u2014 {commit_subject}\n - ...\n- Prior terramend review: none or {prior_sha_short} ({prior_review_html_url})\n- Submitted at: {iso_timestamp}\n-->\n```\n\nPull every metadata field from the `checkout_pr` tool's response \u2014 file count, commit count, base/head ref + SHA, the commit list. For `IncrementalReview` runs, populate `Prior terramend review` with the prior review's commit_id (short SHA) and `html_url` from `list_pull_request_reviews`.\n\n## 2. Cross-cutting issue sections (zero or more)\n\nFor each cross-cutting concern, one `### ` section. Use this exact shape:\n\n```\n### {emoji} {short, descriptive title \u2014 what's wrong, not what to do}\n\n{Human-readable problem write-up. Describes the PROBLEM only \u2014 what's broken, what the symptom is, what the blast radius is. NO asks, NO suggested fixes, NO \"the right thing to do is...\". Asks and fixes live in the technical-details block below; the visible part is for the human to *understand* the problem, not to implement it.}\n\n<details><summary>Technical details</summary>\n\n\\`\\`\\`\\`markdown\n# {title repeated}\n\n## Affected sites\n- {file path:line} \u2014 {what's wrong there}\n- ...\n\n## Required outcome\n- {what the fix needs to achieve, not how to achieve it}\n- ...\n\n## Suggested approach (optional)\n{When the fix shape is non-obvious, sketch one or more reasonable directions. Skip when the outcome alone makes the fix obvious.}\n\n## Open questions for the human (optional)\n- {Any decision an implementing agent shouldn't make unilaterally \u2014 pricing thresholds, breaking-change policy, naming, scope of follow-up.}\n\\`\\`\\`\\`\n\n</details>\n```\n\nConcrete example of the visible part of a non-anchored section (technical-details block unchanged from the template above):\n\n```\n### \u2139\uFE0F Legacy `opencode.ts` has no documented deletion plan\n\nThe v2 harness lands alongside the v1 file and imports one helper from it. Worth a follow-up issue or a TODO so the next maintainer doesn't have to re-derive the cleanup plan.\n```\n\nThe example's value is its *shape*: a finding about absence (no deletion plan), not a line-anchored bug. Body sections live or die on whether the concern genuinely doesn't fit on a line.\n\n**Heading severity emoji** \u2014 every `### ` heading carries one:\n\n- \uD83D\uDEA8 critical \u2014 blocks merge (data loss, security, broken core flow)\n- \u26A0\uFE0F important \u2014 must address before merging (regression, missing validation, incorrect behavior)\n- \u2139\uFE0F informational \u2014 surfaced for awareness; mergeable as-is\n\n**Visible problem write-up rules:**\n\n- **No asks, no suggested fixes** in the visible part. The visible portion describes the problem; the technical-details block describes the fix shape and any open questions. The exception: a fix so self-evident that NOT stating it would be weird (e.g. \"the typo is missing an 'r'\") \u2014 in that case, fold it into the problem statement and skip the suggested-approach block in technical details too.\n- **Never two successive plain paragraphs.** Every transition between block-level elements must alternate prose with structure: paragraph \u2192 bullet list \u2192 paragraph; paragraph \u2192 code fence \u2192 bullet list; paragraph \u2192 table \u2192 paragraph. Two consecutive paragraphs in a row create a wall of text that's impossible to digest. If you catch yourself writing one, find a way to split it: pull a list out of it, drop a 2-3 line code fence between them, or merge them into a single tighter paragraph.\n- **Per-paragraph budget:** ~3 sentences max. Past that, you're explaining where you should be structuring.\n- **Identifier discipline still applies** in the visible part. Lead with behavior in plain English; name an identifier only when it's the subject of the concern or a public surface a reader would recognize. The technical-details block is where dense identifier references belong.\n\n**Technical-details block rules:**\n\n- Wrapped in a 4-backtick markdown fence (`\\`\\`\\`\\`markdown ... \\`\\`\\`\\``) so it's visually distinct, one-click copyable, and can contain its own 3-backtick code fences without escape gymnastics. The contents are agent-readable \u2014 a fix-agent will pull the body down and use this block as the brief.\n- File paths and `file:line` refs are encouraged (and necessary) \u2014 the next agent uses these to navigate. Identifier density is fine here.\n- Slightly more verbose than the absolute minimum is OK when it materially helps the next agent: a small code snippet showing the symptom, a short table of mismatched key/column pairs, a one-paragraph \"why CI doesn't catch it\" note. Skip massive regression-test scaffolding or full route rewrites \u2014 the implementing agent writes those.\n- Use the four standard sections (`Affected sites`, `Required outcome`, optional `Suggested approach`, optional `Open questions for the human`). Skip the optional sections when they wouldn't add anything.\n\n## Inline technical details\n\nInline comments are short (~2-3 sentences) by default. When an inline finding has broader implications worth recording for a fix-agent \u2014 e.g. a localized bug whose proper fix requires touching several files, or where the right fix depends on a design decision the human needs to make \u2014 append a collapsed `<details><summary>Technical details</summary>` block to the inline comment's body. Same shape as the body-section technical-details block (4-backtick fenced markdown, `## Affected sites` / `## Required outcome` / optional `## Suggested approach` / optional `## Open questions for the human`).\n\nGitHub renders the same markdown parser in inline comments as in the review body, so the collapsed-details affordance works the same way. The visible part of the inline comment stays scannable; the depth is one click away for any agent that needs it.\n\n## 3. `### \u2139\uFE0F Nitpicks` (optional, last content section)\n\nOnly when there are nits that for some reason can't be inlined. Filepaths in nit text are fine \u2014 these are simple enough that a human or agent reads once and acts. No technical-details block.\n\n```\n### \u2139\uFE0F Nitpicks\n\n- {nit, with file path inline if useful, \u2264 ~200 chars}\n- ...\n```\n\n## 4. `Suppressed findings` (optional, very last)\n\nOnly when the adversarial verification pass (see the checklist) suppressed at least one \uD83D\uDEA8/\u26A0\uFE0F candidate. One collapsed block, always the last element in the body, with the count in the summary line:\n\n```\n<details><summary>\uD83D\uDDD1\uFE0F Suppressed findings (2)</summary>\n\n- \u26A0\uFE0F `networking/main.tf:42` \u2014 claimed the subnet exposes the DB publicly \u2014 refuted: `publicly_accessible = false` at `db.tf:18`.\n- \uD83D\uDEA8 `api/auth.ts:77` \u2014 claimed JWT signature bypass \u2014 refuted: the unverified decode is dev-only, gated by the `NODE_ENV` check two lines above.\n\n</details>\n```\n\nOne bullet per suppressed finding: severity emoji, `file:line`, the claim in a few words, the refutation in a few words. One line each \u2014 the block exists for auditability (a human catching the false-positive filter being wrong), not re-litigation. Omit the block entirely when nothing was suppressed.\n\n## Inline comment shape\n\nInline comments use the same severity framing as body `### ` sections, scaled down for line-anchored use:\n\n- **Lead with a 1-2 sentence problem statement.** The reader is looking at the line in question, so don't restate what the line says \u2014 describe what's wrong with it. Optionally prefix the visible line with a severity emoji (\uD83D\uDEA8 / \u26A0\uFE0F / \u2139\uFE0F) when severity isn't obvious from context.\n- **Optional `<details><summary>Technical details</summary>...</details>` collapsible** for findings whose technical context (longer file:line references, related-code snippets, suggested approach, regression-risk notes) would overwhelm the human-readable lead-in. Same agent-readable purpose, same 4-backtick fence shape, and same 4-section structure as the body's technical-details block \u2014 see *Inline technical details* above. Encouraged whenever the depth helps a downstream fix-agent; don't force one when the inline lead-in already says everything.\n- **Visible portion \u2264 2-3 sentences.** If you find yourself writing more, that's the cue to split the depth into the `Technical details` collapsible.\n\n## Body-wide rules\n\n- **Inline-vs-body discipline (repeated for emphasis):** anything that anchors to a specific line goes inline (with a `<details>Technical details</details>` block when the implications are broad). The body is for non-anchorable concerns only \u2014 absence, sequencing, design decisions, scope questions, architectural risk.\n- **No `### Issues found` heading** above the issue sections \u2014 each `### ` heading IS the issue.\n- **Severity emoji on every `### ` heading** (\uD83D\uDEA8 / \u26A0\uFE0F / \u2139\uFE0F). No emoji on the preamble lead-in or anywhere else.\n- **GitHub block-level rendering**: GitHub's markdown parser requires a blank line between ALL block-level elements (HTML tags like `<br/>`, `<sub>`, `<details>`, `<b>` and markdown syntax like headings, lists, blockquotes, code fences, paragraphs). Without a blank line, GitHub treats following content as a continuation of the HTML block and renders markdown syntax as literal text. ALWAYS separate block-level elements with a blank line.\n- **Backtick-wrap** every variable, identifier, or file name when you mention one (in either visible or technical-details portions).\n- **Don't repeat diff content**, don't include raw `+123 / -45` stats, don't include a changelog section, don't use horizontal rules (`---`).\n- **Pull file/commit counts from `checkout_pr` metadata** \u2014 never count manually.\n- **Legacy headings REMOVED.** Do not use `### Key changes`, `### Issues found`, `<b>TL;DR</b>`, or `<sub><b>Summary</b>`. The new structure subsumes them.";
|
|
8
|
+
export declare const REMEDIATION_PR_FORMAT = "### Remediation PR format\n\n**Minimum (ALWAYS include, even under tight budget):** a one-paragraph plain-English summary of *what was wrong and what you changed*, then a `## What changed` list with one *Was / Changed / Safe because* note per concern, then the `## Validation (\u2717 \u2192 \u2713)` list. If you produce nothing else, produce these three \u2014 a PR a human can't understand from its body alone has failed its job. Everything below enriches this minimum; it does not replace it.\n\nBuild the PR body in this EXACT order. Every line is backed by a tool result \u2014 never write a status you didn't get from a tool. Omit a whole section only when its tool didn't run (e.g. no plan without cloud creds); never fabricate it. Keep a blank line between every block-level element (GitHub needs it to render).\n\n#### 1. Status banner (first line)\n\nOne GitHub alert blockquote that sets the reviewer's expectation, picked from the verification evidence:\n\n- `> [!CAUTION]` \u2014 a `needs-human` signal fired: a regression (`has_regressions`), a stateful destroy/replace, a high blast radius, a non-deterministic plan, or a cost escalation. One sentence naming the reason.\n- `> [!WARNING]` \u2014 verified but with a caveat (medium blast radius, a still-`remaining` concern, baseline-unavailable cost).\n- `> [!NOTE]` \u2014 clean: `verified: true`, no regressions, low blast radius. One sentence: what was hardened.\n\n#### 2. Title line + badges\n\nA single bolded sentence naming the file/group and what was fixed, then a one-line badge row built from the tool results (drop any badge whose tool didn't run):\n\n```\n**Hardened \\`main.tf\\` \u2014 S3 encryption + public-access block.**\n\n`Confidence: high` \u00B7 `Blast radius: low (1 resource)` \u00B7 `Plan: +0 ~1 -0` \u00B7 `Idempotent: yes` \u00B7 `Cost: +$0.00/mo`\n```\n\nRender `Confidence` verbatim from `terraform_verify_remediation.confidence` \u2014 never inflate it. Use `\u00B7` separators, backtick-wrap each badge.\n\n#### 3. `## What changed`\n\nOne `### ` subsection per resolved concern (or one per rule for a by-rule group), each with the \u00A75.17 three-line micro-template and the rule linked to its docs (`doc_url`, else `remediation_hint`):\n\n```\n### \uD83D\uDD12 [\\`trivy:AVD-AWS-0088\\`](https://avd.aquasec.com/misconfig/avd-aws-0088) \u2014 S3 bucket not encrypted\n\n- **Was** \u2014 {the scanner's `evidence`, in plain English}.\n- **Changed** \u2014 {what the fix did, one sentence}.\n- **Safe because** \u2014 {why it's correct and non-breaking}.\n```\n\nLead each heading with a severity emoji (\uD83D\uDEA8 critical \u00B7 \u26A0\uFE0F high \u00B7 \uD83D\uDD12 security \u00B7 \u2139\uFE0F low/info). Backtick-wrap every identifier. No raw diff dumps \u2014 the Files tab shows the diff.\n\n#### 4. `## Validation (\u2717 \u2192 \u2713)`\n\nBuilt ONLY from `terraform_verify_remediation`'s result \u2014 this is the proof, not a self-report. One line per id in `resolved`, then any still-open id honestly:\n\n```\n## Validation (\u2717 \u2192 \u2713)\n\n- \u2717 \u2192 \u2713 \\`trivy:AVD-AWS-0088\\` resolved\n- \u2717 \u2192 \u2713 \\`checkov:CKV_AWS_19\\` resolved\n- \u26A0\uFE0F still open: \\`tflint:...\\` \u2014 {why it couldn't be cleared}\n```\n\nIf `has_regressions` is true, add a `> [!CAUTION]` **Regression** callout listing each new concern id BEFORE this list, and ensure the `needs-human` label is set. Never mark an id \u2713 unless the tool returned it in `resolved`.\n\n#### 5. `<details><summary>Plan</summary>` (when `terraform_plan` ran)\n\nAttach the full `plan_text` in a collapsed code block so a reviewer sees the exact change without re-running it:\n\n```\n<details><summary>Terraform plan</summary>\n\n\\`\\`\\`\n{plan_text}\n\\`\\`\\`\n\n</details>\n```\n\nWhen `needs_human` is true, surface `needs_human_reasons` as a visible bullet list above the `<details>` \u2014 don't bury an escalation in a collapsed block.\n\n#### 6. `## \uD83D\uDEE1\uFE0F Prevent recurrence` (optional follow-up)\n\nFrom the scan's `prevention` map \u2014 the CI guardrail that stops this class of concern coming back. Clearly marked **not part of this PR's diff**: a short intro sentence then the `mechanism` + a fenced `snippet`. One entry per distinct rule.\n\n#### 7. `## Compliance` (optional, when a crosswalk was run)\n\nWhen `terraform_compliance_crosswalk` was called, add a short auditor-facing note: the frameworks/controls this fix touches (from `by_framework`), prefixed \"Indicative alignment (crosswalk v{version}) \u2014 not an audit verdict.\" Skip entirely when the crosswalk wasn't run.\n\n#### Body-wide rules\n\n- **Evidence-built, never self-reported** \u2014 every badge, \u2713, and count comes from a tool result. If a tool didn't run, omit its section; don't guess.\n- **Blank line between ALL block-level elements** (callouts, headings, lists, code fences, `<details>`) \u2014 GitHub renders markdown as literal text otherwise.\n- **Backtick-wrap** every file, rule id, resource address, and identifier.\n- **No raw `+N/-M` diff stats, no horizontal rules (`---`), no changelog section.** The footer is appended automatically \u2014 don't add your own.\n- **One scoped group per PR.** The body describes this group's fix only.";
|
|
9
|
+
export declare function computeModes(agentId: AgentId): Mode[];
|
|
10
|
+
export declare const modes: Mode[];
|
|
11
|
+
/** built-in mode names in canonical casing. used to validate / canonicalize the
|
|
12
|
+
* `mode` action input so a CI run can pin a mode deterministically instead of
|
|
13
|
+
* relying on the agent's prompt-driven `select_mode` choice. mode names are
|
|
14
|
+
* agent-independent (only the embedded tool refs differ per agent), so the
|
|
15
|
+
* opencode-rendered list is the authoritative name set. */
|
|
16
|
+
export declare const BUILTIN_MODE_NAMES: readonly string[];
|
|
17
|
+
/**
|
|
18
|
+
* modes that legitimately never modify the working tree. used by the post-run
|
|
19
|
+
* dirty-tree gate to suppress the "commit and push" nudge — those modes
|
|
20
|
+
* complete by submitting a review (`Review` / `IncrementalReview`) or by
|
|
21
|
+
* posting a Plan comment (`Plan`), not by touching files. any leftover in the
|
|
22
|
+
* tree at end-of-run is incidental tool noise (e.g. a `node_modules/` from a
|
|
23
|
+
* stray install attempt) on an ephemeral worktree; nudging the agent to
|
|
24
|
+
* commit it would produce a spurious PR.
|
|
25
|
+
*/
|
|
26
|
+
export declare const NON_COMMITTING_MODES: ReadonlySet<string>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { PrepOptions, PrepResult } from "#app/prep/types";
|
|
2
|
+
export type { PrepOptions, PrepResult } from "#app/prep/types";
|
|
3
|
+
/**
|
|
4
|
+
* run all prep steps sequentially.
|
|
5
|
+
* failures are logged as warnings but don't stop the run.
|
|
6
|
+
*/
|
|
7
|
+
export declare function runPrepPhase(options: PrepOptions): Promise<PrepResult[]>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
interface PrepResultBase {
|
|
2
|
+
dependenciesInstalled: boolean;
|
|
3
|
+
issues: string[];
|
|
4
|
+
}
|
|
5
|
+
export type NodePackageManager = "npm" | "pnpm" | "yarn" | "bun" | "deno";
|
|
6
|
+
export interface NodePrepResult extends PrepResultBase {
|
|
7
|
+
language: "node";
|
|
8
|
+
packageManager: NodePackageManager;
|
|
9
|
+
}
|
|
10
|
+
export type PythonPackageManager = "pip" | "pipenv" | "poetry";
|
|
11
|
+
export interface PythonPrepResult extends PrepResultBase {
|
|
12
|
+
language: "python";
|
|
13
|
+
packageManager: PythonPackageManager;
|
|
14
|
+
configFile: string;
|
|
15
|
+
}
|
|
16
|
+
export interface UnknownLanguagePrepResult extends PrepResultBase {
|
|
17
|
+
language: "unknown";
|
|
18
|
+
}
|
|
19
|
+
export type PrepResult = NodePrepResult | PythonPrepResult | UnknownLanguagePrepResult;
|
|
20
|
+
export type PrepOptions = {
|
|
21
|
+
/** when true, lifecycle scripts (postinstall, etc.) are suppressed */
|
|
22
|
+
ignoreScripts: boolean;
|
|
23
|
+
/** directory the corepack shim is installed into (see `packageManagerBinDir`) */
|
|
24
|
+
binDir: string;
|
|
25
|
+
};
|
|
26
|
+
export interface PrepDefinition {
|
|
27
|
+
name: string;
|
|
28
|
+
shouldRun: () => Promise<boolean> | boolean;
|
|
29
|
+
run: (options: PrepOptions) => Promise<PrepResult>;
|
|
30
|
+
}
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review-quality controls for the Review / IncrementalReview modes: a curated
|
|
3
|
+
* false-positive precedents list and an adversarial per-finding verification
|
|
4
|
+
* pass. Both are embedded into the mode prompts (modes.ts) — the precedents
|
|
5
|
+
* also travel verbatim inside every verification dispatch, since subagents
|
|
6
|
+
* never see the orchestrator's prompt.
|
|
7
|
+
*
|
|
8
|
+
* Provenance: adapted from Anthropic's claude-code-security-review action
|
|
9
|
+
* (MIT) — its hard-exclusion rules, its LLM-judge "PRECEDENTS" list, and the
|
|
10
|
+
* /security-review slash command's refute-subagent pattern. See
|
|
11
|
+
* CLAUDE-CODE-SECURITY-REVIEW-VS-TERRAMEND.md (workspace root) §5.1–5.2 for
|
|
12
|
+
* the comparison that motivated this. Deliberate divergences from upstream:
|
|
13
|
+
* - CCSR judges findings via sequential direct API calls; we dispatch
|
|
14
|
+
* parallel read-only reviewfrog subagents (machine-gated, see
|
|
15
|
+
* agents/subagentToolGates.ts) so verification adds one round of wall
|
|
16
|
+
* time regardless of finding count.
|
|
17
|
+
* - CCSR's judge gates on a bare confidence number; we gate on an explicit
|
|
18
|
+
* verdict (confirmed/refuted/uncertain) plus confidence, because the
|
|
19
|
+
* verdict is what the orchestrator acts on and the number alone invites
|
|
20
|
+
* anchoring.
|
|
21
|
+
* - CCSR drops "secrets stored on disk" findings (handled by a separate
|
|
22
|
+
* pipeline there). Terramend reviews IaC where a hardcoded credential is
|
|
23
|
+
* a core finding, so that exclusion is intentionally NOT inherited.
|
|
24
|
+
* Kept as-is from upstream: fail-open semantics (a broken verifier must
|
|
25
|
+
* never silently swallow a true positive) and suppression auditability
|
|
26
|
+
* (excluded findings are listed, never deleted).
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* False-positive precedents applied at aggregation time and inside every
|
|
30
|
+
* verification dispatch. Each entry encodes a recurring FP class; a candidate
|
|
31
|
+
* matching one needs specific evidence that the precedent does not apply, or
|
|
32
|
+
* it gets dropped. Ordered: hard exclusions (never post) → general code
|
|
33
|
+
* precedents → Terraform/IaC precedents → the final signal-quality bar.
|
|
34
|
+
*/
|
|
35
|
+
export declare const REVIEW_FINDING_PRECEDENTS = "### Finding precedents (false-positive control)\n\nApply these when deciding whether a candidate finding is worth posting, and include this whole section verbatim in every verification dispatch. Each precedent encodes a recurring false-positive class: a candidate that matches one is dropped unless you have specific evidence the precedent does not apply here.\n\n**Hard exclusions \u2014 never post:**\n\n- Denial-of-service / resource-exhaustion concerns without a concrete, cheap-to-trigger attack path: missing rate limiting, \"unbounded\" loops over trusted input, \"could exhaust memory/CPU\".\n- Theoretical race conditions or timing attacks. Post a race only when it is concretely reachable and concretely harmful.\n- Memory-safety findings (buffer overflow, use-after-free, OOB) in memory-safe languages \u2014 Rust, Go, JS/TS, Python, Java, HCL.\n- Security findings whose anchor is a documentation file \u2014 a code snippet in `.md`/`.mdx` is not an attack surface. (Stale or incorrect docs remain valid *impact* findings; this exclusion is only for treating doc content as exploitable.)\n- \"Lack of hardening\" with no vulnerability: code is not required to implement every best practice, only to avoid concrete flaws.\n- Vulnerable-dependency reports based on version strings alone \u2014 dependency scanning is a separate pipeline with its own remediation flow.\n\n**General precedents:**\n\n- Environment variables, CLI flags, and workflow-dispatch inputs are operator-trusted. An attack that requires controlling them is invalid.\n- A missing permission/auth check in client-side code is not a finding; the server is the enforcement boundary. The same applies to client-side input validation.\n- React/Angular-class frameworks escape output by default \u2014 an XSS claim needs `dangerouslySetInnerHTML`, `bypassSecurityTrustHtml`, or an equivalent unsafe API in the diff.\n- SSRF requires control of host or protocol; path-only control is not SSRF. Neither SSRF nor path traversal applies to purely client-side code.\n- Command injection in shell scripts needs a named untrusted-input path; developer-invoked scripts taking developer-supplied arguments don't qualify.\n- Un-sanitized user input reaching logs is log spoofing, not a vulnerability. A logging finding is valid only when it exposes secrets, credentials, or PII.\n- UUIDs are unguessable; an attack that requires guessing one is invalid.\n\n**Terraform / IaC precedents:**\n\n- `0.0.0.0/0` **egress** is common and usually intentional \u2014 flag open **ingress**, or egress only with a concrete exfiltration concern attached.\n- Values from `*.tfvars`, `locals`, and module input variables are operator-trusted; \"what if this variable is malicious\" is invalid without naming an untrusted writer.\n- Missing encryption / versioning / access-logging on resources that demonstrably hold no sensitive data (short-retention log groups, test fixtures, scratch buckets) is \u2139\uFE0F at most, never \uD83D\uDEA8.\n- Unpinned provider or module versions are style feedback for first-party modules; \u26A0\uFE0F only for third-party module sources, where the unpinned ref is supply-chain surface.\n- Do not infer state drift, plan outcomes, or \"this will destroy/replace the database\" from static HCL \u2014 only `terraform plan` evidence supports those claims. Without plan evidence, phrase the concern as an open question, not a finding.\n- Missing tags and naming-convention deviations are nitpicks, not findings.\n- When a deterministic scanner rule covers the same issue (trivy `AVD-*`, checkov `CKV_*`, tflint), cite the rule id in the finding \u2014 that makes it \u2717\u2192\u2713 verifiable downstream instead of an unverifiable reviewer opinion.\n\n**Signal-quality bar** \u2014 a surviving candidate must still answer yes to all three: Is there a concrete failure or attack path? Is it a real risk rather than a theoretical best practice? Could the author act on it exactly as written?";
|
|
36
|
+
/**
|
|
37
|
+
* Adversarial verification pass, spliced into the aggregation step of Review
|
|
38
|
+
* and IncrementalReview (between the non-anchored-concern hunt and comment
|
|
39
|
+
* drafting). The 0-or-2+ lens rule does not apply here — that rule buys
|
|
40
|
+
* independence between discovery perspectives; verification is a per-claim
|
|
41
|
+
* judgment with no orthogonality to purchase, so one finding = one dispatch
|
|
42
|
+
* is correct even when there is exactly one finding.
|
|
43
|
+
*/
|
|
44
|
+
export declare const FINDING_VERIFICATION_PASS = "**Adversarial verification \u2014 required before posting any \uD83D\uDEA8/\u26A0\uFE0F finding.** A candidate finding is a hypothesis until an independent pass has tried to kill it; your own trace is not independent, because you found it. For every candidate you intend to post at \uD83D\uDEA8 critical or \u26A0\uFE0F important, dispatch one `reviewfrog` verification subagent \u2014 ALL of them in a single assistant turn as parallel Task tool_use blocks. One candidate = one subagent, and dispatching exactly one is fine here: the 0-or-2+ rule governs discovery lenses, where independence between perspectives is the point; verification is per-claim judgment with no orthogonality to buy. Skip verification only for:\n\n - \u2139\uFE0F informational findings and nitpicks (post on your own judgment), and\n - findings whose evidence is deterministic tool output \u2014 a scanner concern id, a failing test, a compiler/type error. Those re-verify mechanically and need no judge.\n\n Each verification dispatch contains, in order:\n - the absolute `diffPath` (and `incrementalDiffPath` when available) named verbatim \u2014 the reviewer's baked-in system prompt selects its first action on this token;\n - the single finding under test: file, line, intended severity, the claim, and the evidence you collected;\n - the **Finding precedents** section \u2014 plus any `### Finding precedents \u2014 org addendum` section from your instructions \u2014 included verbatim (the subagent cannot see your prompt);\n - this charge: \"Attempt to REFUTE this finding. Read the actual code \u2014 do not trust the claim's description of it. Apply the finding precedents. Report a verdict (`confirmed` / `refuted` / `uncertain`), a confidence score 1\u201310, and a 2\u20133 sentence justification quoting the code that decides it. When the attack or failure path is theoretical rather than demonstrated, bias toward `refuted`.\"\n\n Set the Task `description` to `verify:<file>:<line>` so parallel verifications are distinguishable in CI logs. Asking for a verdict schema is correct here and does not violate the discovery-lens \"no finding schema\" discipline \u2014 the subagent is judging one claim, not exploring.\n\n Gate on what comes back:\n - `refuted` at confidence \u2265 7 \u2192 suppress the finding and record it for the audit trail.\n - `uncertain`, or `refuted` at lower confidence \u2192 re-read the decisive code yourself; either downgrade to \u2139\uFE0F with the uncertainty stated, or suppress. Do not post it at \uD83D\uDEA8/\u26A0\uFE0F.\n - `confirmed` \u2192 post it; fold the verifier's justification into the comment's technical-details block when it adds evidence.\n - errored / timed out / nothing usable \u2192 retry once; if it still fails, KEEP the finding and add `verification unavailable` to its technical details. Fail open: a broken verifier must never silently swallow a true positive, and must never block the review.\n\n Suppressed findings are recorded, never silently deleted \u2014 list every one in the `Suppressed findings` block at the bottom of the review body (shape defined in the format below): severity, `file:line`, the claim in a few words, the refutation in a few words. An unaudited filter eats true positives invisibly; the audit trail is what lets a human catch the filter being wrong.";
|
|
45
|
+
/**
|
|
46
|
+
* Heading under which `fp_filtering_instructions` (the action input carrying
|
|
47
|
+
* org-specific FP precedents) is appended to the Review mode instructions.
|
|
48
|
+
* FINDING_VERIFICATION_PASS names this heading when telling the orchestrator
|
|
49
|
+
* what to include verbatim in each verification dispatch — the two strings
|
|
50
|
+
* are a contract; change them together.
|
|
51
|
+
*/
|
|
52
|
+
export declare const FP_PRECEDENTS_ADDENDUM_HEADING = "### Finding precedents \u2014 org addendum";
|
|
53
|
+
/**
|
|
54
|
+
* Merge the §5.5 action inputs into the per-mode user instructions that
|
|
55
|
+
* `select_mode` appends to the mode prompt (see buildOrchestratorGuidance in
|
|
56
|
+
* mcp/selectMode.ts). Both land on the "Review" key — IncrementalReview
|
|
57
|
+
* inherits Review's instructions via modeInstructionParent. Composes with
|
|
58
|
+
* (never replaces) backend-provided instructions: hosted settings and
|
|
59
|
+
* workflow-file inputs are both repo-owner-controlled surfaces.
|
|
60
|
+
*/
|
|
61
|
+
export declare function mergeReviewModeInstructions(base: Record<string, string>, inputs: {
|
|
62
|
+
reviewInstructions?: string | undefined;
|
|
63
|
+
fpFilteringInstructions?: string | undefined;
|
|
64
|
+
}): Record<string, string>;
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: terraform-best-practices
|
|
3
|
+
description: Fix Terraform to best practice from a scanner concern — the minimal, correct change for a trivy/checkov/tflint/fmt/validate finding, plus the security, structure, and naming conventions a good fix must follow. Use when remediating Terraform, applying a `terraform_scan` concern, or generating new HCL that must start compliant.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Terraform best-practice remediation
|
|
7
|
+
|
|
8
|
+
You are fixing Terraform against a **concern** emitted by `terraform_scan` (or
|
|
9
|
+
`terraform_validate`). Each concern names the producing `source`, a `rule_id`,
|
|
10
|
+
the `location` (file + line), an `evidence` string (what's wrong), and often a
|
|
11
|
+
`remediation_hint`. Your job is the **smallest correct change** that clears that
|
|
12
|
+
concern — nothing more.
|
|
13
|
+
|
|
14
|
+
## The remediation loop
|
|
15
|
+
|
|
16
|
+
1. **Read the concern.** The `rule_id` tells you the class of problem; the
|
|
17
|
+
`evidence` tells you the specifics; `location.file:line` tells you where.
|
|
18
|
+
2. **Open the file and understand the surrounding resource** before editing.
|
|
19
|
+
Don't fix a line in isolation — know which `resource` / `module` / `variable`
|
|
20
|
+
block it belongs to.
|
|
21
|
+
3. **Apply the minimal fix** (see the catalogue below). Touch only `*.tf` /
|
|
22
|
+
`*.tfvars`. Do not reformat, reorder, or "improve" unrelated code — that
|
|
23
|
+
buries the real fix and breaks the one-concern-per-PR contract.
|
|
24
|
+
4. **Re-validate** with `terraform_validate`. If it doesn't pass, your fix is
|
|
25
|
+
incomplete or introduced a new problem — fix that before opening a PR.
|
|
26
|
+
5. **Confirm the concern cleared** by re-running `terraform_scan` on the branch.
|
|
27
|
+
The concern's `id` must be gone. If it isn't, say so honestly.
|
|
28
|
+
|
|
29
|
+
## What a good fix looks like, by source
|
|
30
|
+
|
|
31
|
+
- **`terraform-fmt:unformatted`** — run `terraform fmt` on the named file. This
|
|
32
|
+
is whitespace/alignment only; never combine it with a behavioural change in
|
|
33
|
+
the same PR.
|
|
34
|
+
- **`tflint:*`** — idiomatic-HCL and provider-rule issues. Common fixes: remove
|
|
35
|
+
unused `variable`/`local`/`data` declarations; pin deprecated syntax forward;
|
|
36
|
+
add missing required provider/version constraints. Follow the rule's link.
|
|
37
|
+
- **`trivy:*` / `checkov:*`** — security misconfiguration. These are the
|
|
38
|
+
high-value fixes. Apply the **secure default**, e.g.:
|
|
39
|
+
- **encryption at rest** — add the `server_side_encryption_configuration` /
|
|
40
|
+
`encryption` block (S3, RDS, EBS, etc.); prefer a CMK where the rule asks.
|
|
41
|
+
- **no public access** — set `block_public_acls`/`block_public_policy` etc. to
|
|
42
|
+
`true`; remove `acl = "public-read"`; tighten `0.0.0.0/0` ingress to the
|
|
43
|
+
real CIDR or a referenced security group.
|
|
44
|
+
- **least privilege** — replace `"*"` actions/resources in IAM policies with
|
|
45
|
+
the specific actions/ARNs actually needed.
|
|
46
|
+
- **logging / versioning** — add the access-logging, audit, or versioning
|
|
47
|
+
block the rule requires.
|
|
48
|
+
- **`terraform-validate:*`** — a correctness error (bad reference, type
|
|
49
|
+
mismatch, missing required argument). Fix the actual HCL so `terraform
|
|
50
|
+
validate` passes.
|
|
51
|
+
|
|
52
|
+
## Secure-default catalogue (by problem class)
|
|
53
|
+
|
|
54
|
+
Copy-pasteable shapes for the highest-frequency security concerns. These target
|
|
55
|
+
the **AWS provider v5+** layout (encryption / versioning / ACL / public-access
|
|
56
|
+
are standalone resources, not inline `aws_s3_bucket` blocks). Adapt resource and
|
|
57
|
+
attribute names to the block the concern points at — don't paste blindly.
|
|
58
|
+
|
|
59
|
+
### Encryption at rest
|
|
60
|
+
|
|
61
|
+
S3 — server-side encryption with a customer-managed key (preferred over `AES256`
|
|
62
|
+
when the rule asks for a CMK):
|
|
63
|
+
|
|
64
|
+
```hcl
|
|
65
|
+
resource "aws_s3_bucket_server_side_encryption_configuration" "this" {
|
|
66
|
+
bucket = aws_s3_bucket.this.id
|
|
67
|
+
rule {
|
|
68
|
+
apply_server_side_encryption_by_default {
|
|
69
|
+
sse_algorithm = "aws:kms"
|
|
70
|
+
kms_master_key_id = aws_kms_key.this.arn
|
|
71
|
+
}
|
|
72
|
+
bucket_key_enabled = true
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
EBS volume / root device: `encrypted = true` (add `kms_key_id` when a CMK is
|
|
78
|
+
required). RDS: `storage_encrypted = true` (+ `kms_key_id`). When `aws:kms` needs
|
|
79
|
+
a key and none exists, reference an existing `aws_kms_key`/`var.*`; only add a new
|
|
80
|
+
`aws_kms_key` resource if the stack genuinely has none — keep `AES256` if the rule
|
|
81
|
+
is satisfied by SSE-S3 alone.
|
|
82
|
+
|
|
83
|
+
### Block public access
|
|
84
|
+
|
|
85
|
+
S3 — the four-flag public-access block is the canonical fix; wire it to the same
|
|
86
|
+
bucket:
|
|
87
|
+
|
|
88
|
+
```hcl
|
|
89
|
+
resource "aws_s3_bucket_public_access_block" "this" {
|
|
90
|
+
bucket = aws_s3_bucket.this.id
|
|
91
|
+
block_public_acls = true
|
|
92
|
+
block_public_policy = true
|
|
93
|
+
ignore_public_acls = true
|
|
94
|
+
restrict_public_buckets = true
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Also remove any `acl = "public-read"` (move to a private `aws_s3_bucket_acl` if an
|
|
99
|
+
ACL is needed at all).
|
|
100
|
+
|
|
101
|
+
### Network ingress
|
|
102
|
+
|
|
103
|
+
Replace world-open ingress with the real source. Never leave `0.0.0.0/0` on admin
|
|
104
|
+
ports (22/3389/database ports):
|
|
105
|
+
|
|
106
|
+
```hcl
|
|
107
|
+
ingress {
|
|
108
|
+
from_port = 443
|
|
109
|
+
to_port = 443
|
|
110
|
+
protocol = "tcp"
|
|
111
|
+
cidr_blocks = [var.allowed_cidr] # or: security_groups = [aws_security_group.lb.id]
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### IAM least privilege
|
|
116
|
+
|
|
117
|
+
Replace wildcard `Action`/`Resource` with the specific actions and ARNs the
|
|
118
|
+
workload actually uses. If you can't determine the exact set from the surrounding
|
|
119
|
+
code, this is a human decision — report it rather than guessing a narrow policy
|
|
120
|
+
that breaks the stack.
|
|
121
|
+
|
|
122
|
+
### Versioning & logging
|
|
123
|
+
|
|
124
|
+
S3 versioning and access logging as standalone resources:
|
|
125
|
+
|
|
126
|
+
```hcl
|
|
127
|
+
resource "aws_s3_bucket_versioning" "this" {
|
|
128
|
+
bucket = aws_s3_bucket.this.id
|
|
129
|
+
versioning_configuration { status = "Enabled" }
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### EC2 instance metadata (IMDSv2)
|
|
134
|
+
|
|
135
|
+
Require token-backed metadata to close the SSRF→credential path:
|
|
136
|
+
|
|
137
|
+
```hcl
|
|
138
|
+
metadata_options {
|
|
139
|
+
http_endpoint = "enabled"
|
|
140
|
+
http_tokens = "required"
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Deprecations & style
|
|
145
|
+
|
|
146
|
+
- **Provider v4→v5 S3 split** — inline `server_side_encryption_configuration` /
|
|
147
|
+
`versioning` / `acl` / `logging` blocks on `aws_s3_bucket` are deprecated; move
|
|
148
|
+
each to its standalone resource (above). Don't bump the provider major version
|
|
149
|
+
as a side effect of a remediation — if a fix genuinely requires a newer
|
|
150
|
+
provider, report that as a blocker.
|
|
151
|
+
- **`terraform-fmt:unformatted`** — run `terraform fmt` on the named file only;
|
|
152
|
+
never fold a formatting pass into a behavioural fix.
|
|
153
|
+
|
|
154
|
+
## Don't over-reach
|
|
155
|
+
|
|
156
|
+
- **Smallest change that clears the concern.** Add the missing block; don't
|
|
157
|
+
refactor the resource, rename it, or restructure the file around it.
|
|
158
|
+
- **Don't add speculative hardening** the concern didn't ask for (extra KMS keys,
|
|
159
|
+
new modules, blanket tagging) — that's scope creep and buries the real fix.
|
|
160
|
+
- **Don't widen or bump version pins** to reach a resource or attribute.
|
|
161
|
+
- **One concern's blast radius only.** If the secure default would break the
|
|
162
|
+
stack or needs a human call (a real CIDR, an IAM action set, a CMK policy),
|
|
163
|
+
stop and report it instead of opening a broken or guessed PR.
|
|
164
|
+
|
|
165
|
+
## Conventions every fix must honour
|
|
166
|
+
|
|
167
|
+
- **Variables over hardcoded values.** Don't bake an account id, region, CIDR,
|
|
168
|
+
or ARN into a fix — reference an existing `var.*`/`local.*`, or add a typed
|
|
169
|
+
`variable` with a sensible `default` and `description` when one is genuinely
|
|
170
|
+
needed.
|
|
171
|
+
- **Pin versions.** When adding a provider or module, include a version
|
|
172
|
+
constraint. Never widen an existing pin as a side effect.
|
|
173
|
+
- **Approved modules first.** Call `list_modules` — if a catalogue is configured,
|
|
174
|
+
prefer the catalogue module (a registry module or a local/house module) + its
|
|
175
|
+
exact variable names over inlining a raw resource, and pin its `version`. See
|
|
176
|
+
*Using Terraform modules* below.
|
|
177
|
+
- **No secrets in HCL or state.** Never introduce a literal credential. If the
|
|
178
|
+
fix needs a secret, reference a variable or a secrets data source.
|
|
179
|
+
- **Idempotent, reviewable diffs.** The diff should read so a senior engineer
|
|
180
|
+
approves it without hesitation: one concern, one rationale, no churn.
|
|
181
|
+
|
|
182
|
+
## Gold standards (the bar every fix is measured against)
|
|
183
|
+
|
|
184
|
+
These are the non-negotiable defaults a "good" Terraform change embodies. A fix
|
|
185
|
+
should never *move away* from any of these, and should move *toward* them when
|
|
186
|
+
the concern is adjacent:
|
|
187
|
+
|
|
188
|
+
- **Encrypt everything, at rest and in transit.** Storage encrypted (CMK where
|
|
189
|
+
the data is sensitive); TLS-only endpoints; no plaintext in state.
|
|
190
|
+
- **Private by default.** No `0.0.0.0/0` to admin/database ports; public access
|
|
191
|
+
blocked unless the resource's whole purpose is to be public (and then scoped).
|
|
192
|
+
- **Least privilege.** No `"*"` IAM actions/resources; scope to what the workload
|
|
193
|
+
uses. Prefer a referenced role/policy over an inline wildcard.
|
|
194
|
+
- **Pinned + reproducible.** `required_version` and every `required_providers` /
|
|
195
|
+
module `version` constrained (`~>`); no floating `latest`.
|
|
196
|
+
- **Parameterised, not hardcoded** (see below).
|
|
197
|
+
- **Tagged + named consistently.** Match the repo's existing tag keys and naming
|
|
198
|
+
scheme; don't invent a new convention.
|
|
199
|
+
- **Observable.** Logging / versioning / audit trails enabled where the provider
|
|
200
|
+
supports them and the concern is about data durability or traceability.
|
|
201
|
+
- **Idempotent + deterministic.** No `timestamp()`/`uuid()`/unkeyed `random_*`
|
|
202
|
+
driving a value that lands in state — it produces a perpetual diff.
|
|
203
|
+
|
|
204
|
+
When in doubt, the secure/idiomatic default from the catalogue above IS the gold
|
|
205
|
+
standard. Don't gold-plate beyond the concern, but never regress one of these.
|
|
206
|
+
|
|
207
|
+
## Parameterize, don't hardcode (§4.13)
|
|
208
|
+
|
|
209
|
+
A value that varies by environment, account, or deployment must be a `variable`
|
|
210
|
+
or `local`, never a literal baked into a resource:
|
|
211
|
+
|
|
212
|
+
- **Reuse first.** If the repo already exposes `var.region` / `local.tags` /
|
|
213
|
+
`var.vpc_cidr`, reference it — don't introduce a parallel one.
|
|
214
|
+
- **Introduce a typed variable** when none fits: give it a `type`, a
|
|
215
|
+
`description`, and a safe `default` only when a default is genuinely sane
|
|
216
|
+
(secrets and account-specific ids get NO default). Match the repo's existing
|
|
217
|
+
file layout — add to `variables.tf` if the repo separates them, otherwise keep
|
|
218
|
+
it next to the resource as the repo does.
|
|
219
|
+
- **Derive with `locals`.** Computed/repeated values (a name prefix, a merged tag
|
|
220
|
+
map) belong in `locals`, referenced everywhere — not copy-pasted.
|
|
221
|
+
- **Never inline a secret, account id, ARN, or CIDR.** A "fix" that pastes a
|
|
222
|
+
literal credential is itself a finding (and the secret-scan guardrail will
|
|
223
|
+
block the push). Reference a variable or a secrets data source.
|
|
224
|
+
|
|
225
|
+
## Using Terraform modules (registry, private git libraries, your own)
|
|
226
|
+
|
|
227
|
+
Prefer a well-formed module over a pile of raw resources when one cleanly fits —
|
|
228
|
+
it carries the secure defaults for you. **Always call `list_modules` first**; it
|
|
229
|
+
returns three things: the operator's `module_catalogue`, the
|
|
230
|
+
`discovered_house_modules` (local modules already used in THIS repo), and a note.
|
|
231
|
+
|
|
232
|
+
Three source kinds you'll encounter, each pinned differently:
|
|
233
|
+
|
|
234
|
+
- **Public registry module** — `terraform-aws-modules/vpc/aws`. Pin with a
|
|
235
|
+
`version = "~> 5.0"` argument. The big public collections
|
|
236
|
+
([terraform-aws-modules](https://registry.terraform.io/namespaces/terraform-aws-modules):
|
|
237
|
+
`vpc`, `s3-bucket`, `rds`, `eks`, …) are the default when nothing is configured.
|
|
238
|
+
A registry submodule is `…/aws//modules/log-group`.
|
|
239
|
+
- **Private git module library** — e.g.
|
|
240
|
+
`git::https://github.com/acme/tf-modules.git//aws/s3?ref=s3-v0.1.2`. The
|
|
241
|
+
`//aws/s3` selects the module within the repo and **`?ref=s3-v0.1.2` IS the
|
|
242
|
+
version pin** (git modules have no `version` argument). Keep the exact `ref`;
|
|
243
|
+
never float it. Many orgs (e.g. UKHSA's `data-integration-terraform-modules`)
|
|
244
|
+
ship a whole library this way, tagged per-module.
|
|
245
|
+
- **House module** — one of the repo's own `modules/<name>` dirs. `list_modules`
|
|
246
|
+
surfaces these under `discovered_house_modules` with their caller files — reuse
|
|
247
|
+
the existing one with its **real variable names** (read its `variables.tf`)
|
|
248
|
+
rather than re-implementing it.
|
|
249
|
+
|
|
250
|
+
Rules: use the module's **exact variable names**, set the secure-relevant inputs
|
|
251
|
+
the concern is about, and keep the version **pinned**. Don't introduce a module
|
|
252
|
+
mid-remediation just to "improve" things — using a module is right when
|
|
253
|
+
*generating* new infra or when the fix is genuinely a module swap; for a one-line
|
|
254
|
+
security fix on an existing raw resource, fix the resource in place.
|
|
255
|
+
|
|
256
|
+
When the **`terraform` MCP server** is registered (the `terraform_mcp` input),
|
|
257
|
+
prefer querying it for the current module version and provider argument shapes
|
|
258
|
+
over recalling them from memory — registry knowledge moves faster than any
|
|
259
|
+
training data, and a fix written against a remembered-but-renamed argument
|
|
260
|
+
breaks `plan`. Without it, `terraform_version_currency` (versions) and
|
|
261
|
+
`terraform_provider_schema` (installed-provider arguments) are the fallbacks.
|
|
262
|
+
|
|
263
|
+
### Authoring a reusable module (standard layout)
|
|
264
|
+
|
|
265
|
+
When you GENERATE a reusable module, follow the conventional layout real module
|
|
266
|
+
libraries use so it's drop-in familiar:
|
|
267
|
+
|
|
268
|
+
- `main.tf` (resources), `variables.tf` (every input typed, with a `description`
|
|
269
|
+
and a `validation` block where it helps), `outputs.tf`, `versions.tf` /
|
|
270
|
+
`providers.tf` (`required_version` + `required_providers` pinned), `README.md`
|
|
271
|
+
(usage + inputs/outputs), and a `CHANGELOG.md` if the repo versions modules.
|
|
272
|
+
- Do **not** generate `examples/` fixtures. Document usage in the module's
|
|
273
|
+
`README.md` instead; test coverage comes from the opt-in terratest scaffold
|
|
274
|
+
(see below), which plans the module directly.
|
|
275
|
+
|
|
276
|
+
### Module-source-aware fixes (§4.14)
|
|
277
|
+
|
|
278
|
+
Before fixing a concern, call `terraform_module_graph` to see where the file
|
|
279
|
+
lives in the module call-graph:
|
|
280
|
+
|
|
281
|
+
- **Concern inside a LOCAL module dir** (listed in `local_module_dirs`): fix it
|
|
282
|
+
**once at the module source**. The fix propagates to every caller — do not
|
|
283
|
+
patch each call site. Note the callers in the PR body so a reviewer sees the
|
|
284
|
+
blast radius.
|
|
285
|
+
- **Concern that would require editing a REGISTRY / git / remote module**: that
|
|
286
|
+
source lives outside this repo — you **cannot** fix it here. Do **not** vendor
|
|
287
|
+
the module or fork it inline. Instead report it (open an issue / PR comment)
|
|
288
|
+
naming the upstream module + version and the concern, so a human routes it.
|
|
289
|
+
|
|
290
|
+
### Modularization as remediation (M2)
|
|
291
|
+
|
|
292
|
+
`module_extraction_candidates` finds clusters of raw resources that should be a
|
|
293
|
+
module call, each matched against the repo's house modules (real resource-type
|
|
294
|
+
signature + `required_variables`) and the operator's `module_catalogue`. Turn a
|
|
295
|
+
candidate into a refactor PR under this contract:
|
|
296
|
+
|
|
297
|
+
- **One PR per cluster**, branch `remediate/modularize-<cluster file/prefix>` —
|
|
298
|
+
never mix two clusters or a behavioural fix into a modularization PR.
|
|
299
|
+
- Replace the cluster's raw resources with ONE `module` block calling the
|
|
300
|
+
candidate (pin the version for registry sources; wire the module's REAL
|
|
301
|
+
variable names from `required_variables` / `terraform_module_interface`).
|
|
302
|
+
- **Preserve state with `moved {}` blocks** — one per resource, mapping the old
|
|
303
|
+
address to its new `module.<name>.` address:
|
|
304
|
+
|
|
305
|
+
```hcl
|
|
306
|
+
moved {
|
|
307
|
+
from = aws_s3_bucket.logs
|
|
308
|
+
to = module.logging.aws_s3_bucket.this
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
- **The gate:** the PR may proceed only when `terraform_validate` passes AND
|
|
313
|
+
`terraform_plan` reports `refactor_safe: true` (a pure-move plan — zero
|
|
314
|
+
add/change/destroy). Anything else means a moved block is wrong or the module
|
|
315
|
+
diverges from the raw resources — fix that or report it; never accept
|
|
316
|
+
resource churn as "the refactor".
|
|
317
|
+
- A required variable you can't derive from the existing attributes is a
|
|
318
|
+
**PR question for the reviewer**, never a guessed value.
|
|
319
|
+
|
|
320
|
+
### Version currency (provider + module upgrades, M3)
|
|
321
|
+
|
|
322
|
+
`terraform_version_currency` reports which pinned providers and registry modules
|
|
323
|
+
trail the registry's latest stable version, and which registry modules are
|
|
324
|
+
unpinned. Turn its rows into upgrade PRs under this contract:
|
|
325
|
+
|
|
326
|
+
- **One `chore(deps)` PR per upgrade group** — never mix a version bump with a
|
|
327
|
+
behavioural fix; a bump PR must read as "only the constraint changed".
|
|
328
|
+
- **Minor/patch bumps** (`outdated` with `majors_behind: 0`) may proceed
|
|
329
|
+
autonomously: update the `version` constraint to admit `newest_satisfying` →
|
|
330
|
+
`latest`, then prove it with `terraform_validate` (and `terraform_plan` when
|
|
331
|
+
credentials exist).
|
|
332
|
+
- **Major bumps** (`majors_behind > 0`) mean the provider/module interface may
|
|
333
|
+
have changed — apply only with the `needs-human` label, and consult
|
|
334
|
+
`terraform_module_interface` / `terraform_provider_schema` for what moved.
|
|
335
|
+
- **Unpinned registry modules**: pin to the reported `latest` (`version = "~> X.Y"`),
|
|
336
|
+
one PR for all unpinned modules in a file group — pinning is low-risk and sweeps well.
|
|
337
|
+
- An upgrade whose `terraform_plan` shows **destructive changes is never auto** —
|
|
338
|
+
escalate with the plan excerpt in the PR body.
|
|
339
|
+
|
|
340
|
+
## Tests for modules (§28)
|
|
341
|
+
|
|
342
|
+
Terramend does **not** create or edit `examples/` fixtures — leave any the repo
|
|
343
|
+
already ships untouched. Module test coverage is **opt-in** via the `terratest`
|
|
344
|
+
input:
|
|
345
|
+
|
|
346
|
+
- **Terratest (opt-in).** When the `terratest` input is enabled, call
|
|
347
|
+
`scaffold_terratest` (module name + dir) to generate a plan-only Go
|
|
348
|
+
[Terratest](https://terratest.gruntwork.io/) smoke test (`test/<name>_test.go`)
|
|
349
|
+
**and** a Terraform-native `*.tftest.hcl` in the module's own `tests/` dir.
|
|
350
|
+
Both plan the **module directly** (no example fixture). Write the returned files
|
|
351
|
+
(the input also widens `allowed_paths` to permit them). If the repo already has
|
|
352
|
+
a Terratest suite, update its options/assertions to match the new interface
|
|
353
|
+
instead.
|
|
354
|
+
|
|
355
|
+
In all cases: Terramend **never runs** the tests — it holds no cloud credentials,
|
|
356
|
+
and Terratest needs Go + a real `apply`. It keeps the test source consistent with
|
|
357
|
+
the module and flags in the PR that the suite should be run in the user's
|
|
358
|
+
pipeline. **Never weaken an assertion or delete a test to go green.**
|
|
359
|
+
|
|
360
|
+
## Hard rules
|
|
361
|
+
|
|
362
|
+
- Only modify `*.tf` / `*.tfvars` (and, when `allowed_paths` permits — i.e. the
|
|
363
|
+
`terratest` input is on — that module's test files). Never touch CI, application
|
|
364
|
+
code, or unrelated config in a remediation PR.
|
|
365
|
+
- One concern per PR. If you notice other issues, leave them for their own runs.
|
|
366
|
+
- Never auto-merge. The PR is for a human to review.
|
|
367
|
+
- If you cannot fix a concern cleanly (it needs a human decision, or the secure
|
|
368
|
+
default would break the stack), do **not** open a broken PR — report it
|
|
369
|
+
instead with what's blocking.
|