infraweaver 0.3.7 → 0.3.9
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/README.md +2 -2
- package/dist/agents/claude.d.ts +1 -1
- package/dist/agents/claudePretoolGate.d.ts +0 -36
- package/dist/agents/toolPlaneWatch.d.ts +31 -0
- package/dist/cli.mjs +31908 -18736
- package/dist/external.d.ts +2 -1
- package/dist/index.js +31992 -18749
- package/dist/internal.js +133 -52
- package/dist/mcp/baseBranch.d.ts +41 -0
- package/dist/mcp/changeSummary.d.ts +1 -1
- package/dist/mcp/findingAudit.d.ts +2 -0
- package/dist/mcp/guardrails.d.ts +38 -0
- package/dist/mcp/localContext.d.ts +1 -1
- package/dist/mcp/moduleExtraction.d.ts +5 -0
- package/dist/mcp/pr.d.ts +0 -31
- package/dist/mcp/reproveSuggestions.d.ts +27 -0
- package/dist/mcp/review.d.ts +126 -2
- package/dist/mcp/reviewCommentLimit.d.ts +17 -0
- package/dist/mcp/reviewComments.d.ts +9 -5
- package/dist/mcp/reviewDedup.d.ts +1 -0
- package/dist/mcp/reviewMarkers.d.ts +10 -0
- package/dist/mcp/reviewProvenance.d.ts +20 -6
- package/dist/mcp/shared.d.ts +15 -0
- package/dist/mcp/suggestionProver.d.ts +3 -2
- package/dist/mcp/terraform/checkovSeverities.d.ts +7 -0
- package/dist/mcp/terraform/consolidation.d.ts +26 -0
- package/dist/mcp/terraform/cost.d.ts +2 -2
- package/dist/mcp/terraform/currency.d.ts +11 -0
- package/dist/mcp/terraform/deltaSummary.d.ts +11 -1
- package/dist/mcp/terraform/emittedArtefacts.d.ts +11 -0
- package/dist/mcp/terraform/evidence.d.ts +10 -0
- package/dist/mcp/terraform/gateScan.d.ts +2 -16
- package/dist/mcp/terraform/hcl.d.ts +15 -0
- package/dist/mcp/terraform/initArtefacts.d.ts +21 -0
- package/dist/mcp/terraform/localModules.d.ts +29 -0
- package/dist/mcp/terraform/moduleFindings.d.ts +38 -0
- package/dist/mcp/terraform/refactor/attribution.d.ts +1 -1
- package/dist/mcp/terraform/refactor/equivalence.d.ts +7 -0
- package/dist/mcp/terraform/refactor/expressions.d.ts +62 -0
- package/dist/mcp/terraform/refactor/references.d.ts +15 -0
- package/dist/mcp/terraform/refactor/resources.d.ts +11 -0
- package/dist/mcp/terraform/refactor/substitution.d.ts +55 -37
- package/dist/mcp/terraform/scanDelta.d.ts +28 -0
- package/dist/mcp/terraform/scanSession.d.ts +13 -0
- package/dist/mcp/terraform/scanners.d.ts +67 -18
- package/dist/mcp/terraform/secretScan.d.ts +19 -0
- package/dist/mcp/terraform/standardsReport.d.ts +1 -1
- package/dist/mcp/terraform/tools/plan.d.ts +1 -1
- package/dist/mcp/terraform/tools/scan.d.ts +7 -7
- package/dist/mcp/terraform/tools/validate.d.ts +7 -2
- package/dist/mcp/terraform/tools/verifyRemediation.d.ts +1 -1
- package/dist/mcp/terraform/treeLayout.d.ts +57 -0
- package/dist/mcp/terraform/types.d.ts +1 -1
- package/dist/phases/applyPlan.d.ts +0 -2
- package/dist/toolState.d.ts +3 -0
- package/dist/utils/agent/codexOAuth.d.ts +1 -1
- package/dist/utils/cli.d.ts +1 -7
- package/dist/utils/cloud/cloudReport.d.ts +1 -1
- package/dist/utils/config/baseRefConfig.d.ts +1 -1
- package/dist/utils/config/infraweaverConfig.d.ts +1 -1
- package/dist/utils/config/payload.d.ts +5 -0
- package/dist/utils/markdownTable.d.ts +6 -0
- package/dist/utils/setup/toolLicensing.d.ts +1 -1
- package/dist/utils/unifiedDiff.d.ts +69 -0
- package/package.json +34 -28
- package/src/agents/claude.ts +48 -13
- package/src/agents/claudePretoolGate.ts +9 -17
- package/src/agents/sessionLabeler.ts +3 -3
- package/src/agents/toolPlaneWatch.ts +71 -0
- package/src/external.ts +3 -1
- package/src/mcp/assess.ts +8 -8
- package/src/mcp/baseBranch.ts +96 -0
- package/src/mcp/changeSummary.ts +10 -11
- package/src/mcp/checkSuite.ts +7 -10
- package/src/mcp/checkout.ts +13 -40
- package/src/mcp/crosswalk.ts +5 -0
- package/src/mcp/findingAudit.ts +1 -1
- package/src/mcp/git.ts +44 -2
- package/src/mcp/guardrails.ts +188 -36
- package/src/mcp/localContext.ts +2 -0
- package/src/mcp/localServer.ts +1 -0
- package/src/mcp/moduleExtraction.ts +17 -5
- package/src/mcp/moduleTests.ts +9 -2
- package/src/mcp/modules.ts +10 -7
- package/src/mcp/pr.ts +17 -65
- package/src/mcp/reproveSuggestions.ts +86 -0
- package/src/mcp/review.ts +249 -75
- package/src/mcp/reviewAnchor.ts +11 -23
- package/src/mcp/reviewCommentLimit.ts +79 -0
- package/src/mcp/reviewComments.ts +34 -81
- package/src/mcp/reviewDedup.ts +1 -1
- package/src/mcp/reviewMarkers.ts +25 -2
- package/src/mcp/reviewProvenance.ts +56 -21
- package/src/mcp/roots.ts +3 -2
- package/src/mcp/server.ts +2 -1
- package/src/mcp/shared.ts +40 -1
- package/src/mcp/shell.ts +27 -28
- package/src/mcp/suggestionProver.ts +8 -4
- package/src/mcp/terraform/checkovSeverities.ts +1156 -0
- package/src/mcp/terraform/concernResult.ts +2 -1
- package/src/mcp/terraform/consolidation.ts +232 -19
- package/src/mcp/terraform/cost.ts +16 -7
- package/src/mcp/terraform/cspm.ts +8 -4
- package/src/mcp/terraform/currency.ts +28 -12
- package/src/mcp/terraform/decisions.ts +1 -1
- package/src/mcp/terraform/deltaSummary.ts +50 -1
- package/src/mcp/terraform/emittedArtefacts.ts +63 -0
- package/src/mcp/terraform/evidence.ts +23 -3
- package/src/mcp/terraform/gateScan.ts +191 -128
- package/src/mcp/terraform/hcl.ts +27 -2
- package/src/mcp/terraform/idleFloor.ts +2 -1
- package/src/mcp/terraform/initArtefacts.ts +76 -0
- package/src/mcp/terraform/localModules.ts +112 -0
- package/src/mcp/terraform/moduleDocs.ts +2 -5
- package/src/mcp/terraform/moduleFindings.ts +102 -0
- package/src/mcp/terraform/moduleVersionConstraints.ts +1 -1
- package/src/mcp/terraform/planPairs.ts +2 -20
- package/src/mcp/terraform/policyGate.ts +0 -15
- package/src/mcp/terraform/refactor/attribution.ts +39 -5
- package/src/mcp/terraform/refactor/equivalence.ts +49 -10
- package/src/mcp/terraform/refactor/expressions.ts +323 -0
- package/src/mcp/terraform/refactor/references.ts +101 -0
- package/src/mcp/terraform/refactor/resources.ts +79 -7
- package/src/mcp/terraform/refactor/substitution.ts +852 -256
- package/src/mcp/terraform/scanDelta.ts +41 -0
- package/src/mcp/terraform/scanSession.ts +30 -0
- package/src/mcp/terraform/scannerCache.ts +4 -0
- package/src/mcp/terraform/scanners.ts +553 -233
- package/src/mcp/terraform/secretScan.ts +131 -0
- package/src/mcp/terraform/standardsReport.ts +14 -13
- package/src/mcp/terraform/tools/consolidationCandidates.ts +3 -0
- package/src/mcp/terraform/tools/emitEvidence.ts +13 -5
- package/src/mcp/terraform/tools/emitOscal.ts +3 -2
- package/src/mcp/terraform/tools/emitVex.ts +10 -4
- package/src/mcp/terraform/tools/equivalenceCheck.ts +139 -21
- package/src/mcp/terraform/tools/infracostDiff.ts +2 -2
- package/src/mcp/terraform/tools/plan.ts +30 -17
- package/src/mcp/terraform/tools/reviewScanDelta.ts +3 -0
- package/src/mcp/terraform/tools/scan.ts +5 -3
- package/src/mcp/terraform/tools/standardsReport.ts +1 -1
- package/src/mcp/terraform/tools/validate.ts +20 -5
- package/src/mcp/terraform/tools/verifyRemediation.ts +14 -7
- package/src/mcp/terraform/tools/versionCurrency.ts +3 -1
- package/src/mcp/terraform/treeLayout.ts +378 -0
- package/src/mcp/terraform/types.ts +4 -1
- package/src/mcp/terraform/versionRequirements.ts +11 -2
- package/src/modes/__snapshots__/assemblePrompt.test.ts.snap +2 -0
- package/src/modes/modernize-deprecated.ts +2 -2
- package/src/modes/refactor.ts +9 -9
- package/src/modes/remediate.ts +1 -1
- package/src/modes/terraform-code-review.ts +7 -3
- package/src/modes/update-dependencies.ts +6 -6
- package/src/phases/applyPlan.ts +33 -14
- package/src/phases/applySuggestions.ts +2 -2
- package/src/phases/runAgentWithWatchdogs.ts +2 -1
- package/src/toolState.ts +7 -0
- package/src/utils/agent/codexOAuth.ts +7 -7
- package/src/utils/agent/openCodeModels.ts +4 -6
- package/src/utils/cli.ts +1 -31
- package/src/utils/cloud/runContext.ts +1 -8
- package/src/utils/config/baseRefConfig.ts +6 -0
- package/src/utils/config/infraweaverConfig.ts +2 -0
- package/src/utils/config/payload.ts +41 -0
- package/src/utils/github/assets.ts +1 -6
- package/src/utils/github/github.ts +42 -150
- package/src/utils/github/token.ts +6 -11
- package/src/utils/log.ts +4 -5
- package/src/utils/markdownTable.ts +11 -0
- package/src/utils/prompt/changeImpact.ts +18 -26
- package/src/utils/prompt/instructions.ts +1 -1
- package/src/utils/prompt/promptDirectives.ts +1 -1
- package/src/utils/prompt/toolSelection.ts +11 -2
- package/src/utils/setup/toolLicensing.ts +7 -0
- package/src/utils/telemetry.ts +2 -1
- package/src/utils/unifiedDiff.ts +120 -0
package/dist/internal.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from 'module'; import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __dirnameFn } from 'path'; const require = __createRequire(import.meta.url); const __filename = __fileURLToPath(import.meta.url); const __dirname = __dirnameFn(__filename);
|
|
2
2
|
|
|
3
|
-
// node_modules/.pnpm/@infraweaver-io+ci@0.1.
|
|
3
|
+
// node_modules/.pnpm/@infraweaver-io+ci@0.1.8/node_modules/@infraweaver-io/ci/dist/progressComment.js
|
|
4
4
|
async function getProgressComment(ctx, comment) {
|
|
5
5
|
const result = await (comment.type === "review" ? ctx.octokit.rest.pulls.getReviewComment({
|
|
6
6
|
owner: ctx.owner,
|
|
@@ -94,7 +94,7 @@ async function createLeapingProgressComment(ctx, target, body) {
|
|
|
94
94
|
};
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
// node_modules/.pnpm/@infraweaver-io+core@0.1.
|
|
97
|
+
// node_modules/.pnpm/@infraweaver-io+core@0.1.5/node_modules/@infraweaver-io/core/dist/time.js
|
|
98
98
|
var TIMEOUT_DISABLED = "none";
|
|
99
99
|
var TIME_STRING_REGEX = /^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/;
|
|
100
100
|
function parseTimeString(input) {
|
|
@@ -110,7 +110,7 @@ function isValidTimeString(input) {
|
|
|
110
110
|
return parseTimeString(input) !== null;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
// node_modules/.pnpm/@infraweaver-io+models@0.1.
|
|
113
|
+
// node_modules/.pnpm/@infraweaver-io+models@0.1.6/node_modules/@infraweaver-io/models/dist/catalog.js
|
|
114
114
|
function provider(config) {
|
|
115
115
|
return config;
|
|
116
116
|
}
|
|
@@ -121,32 +121,32 @@ var providers = {
|
|
|
121
121
|
models: {
|
|
122
122
|
// Claude Fable is access-gated on Anthropic: many BYOK keys can't invoke
|
|
123
123
|
// it, so auto-selecting it as the Anthropic default 404s on the first model
|
|
124
|
-
// call (matches the upstream pullfrog fix that demoted it).
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
//
|
|
124
|
+
// call (matches the upstream pullfrog fix that demoted it). So it is
|
|
125
|
+
// `hidden` — never auto-selected and absent from the pickers — but NOT
|
|
126
|
+
// deprecated: a `fallback` would also turn an explicit `claude-fable`
|
|
127
|
+
// request into Opus without a word, and a run must use the model it was
|
|
128
|
+
// asked for. Unhide it once access is broad. Zen meters Fable like any
|
|
129
|
+
// other model — `opencode/claude-fable` below reaches it today.
|
|
130
130
|
"claude-fable": {
|
|
131
131
|
displayName: "Claude Fable",
|
|
132
132
|
resolve: "anthropic/claude-fable-5-1",
|
|
133
133
|
// rolling alias: OpenRouter's catalog lags brand-new pinned versions
|
|
134
134
|
// (claude-fable-5-1 is not indexed), so track ~…-latest.
|
|
135
135
|
openRouterResolve: "openrouter/~anthropic/claude-fable-latest",
|
|
136
|
-
|
|
136
|
+
hidden: true,
|
|
137
137
|
subagentModel: "claude-sonnet"
|
|
138
138
|
},
|
|
139
139
|
"claude-opus": {
|
|
140
140
|
displayName: "Claude Opus",
|
|
141
|
-
resolve: "anthropic/claude-opus-5",
|
|
142
|
-
openRouterResolve: "openrouter/anthropic/claude-opus-5",
|
|
141
|
+
resolve: "anthropic/claude-opus-5-5",
|
|
142
|
+
openRouterResolve: "openrouter/anthropic/claude-opus-5.5",
|
|
143
143
|
preferred: true,
|
|
144
144
|
subagentModel: "claude-sonnet"
|
|
145
145
|
},
|
|
146
146
|
"claude-sonnet": {
|
|
147
147
|
displayName: "Claude Sonnet",
|
|
148
|
-
resolve: "anthropic/claude-sonnet-5",
|
|
149
|
-
openRouterResolve: "openrouter/anthropic/claude-sonnet-5"
|
|
148
|
+
resolve: "anthropic/claude-sonnet-5-5",
|
|
149
|
+
openRouterResolve: "openrouter/anthropic/claude-sonnet-5.5"
|
|
150
150
|
},
|
|
151
151
|
"claude-haiku": {
|
|
152
152
|
displayName: "Claude Haiku",
|
|
@@ -388,18 +388,19 @@ var providers = {
|
|
|
388
388
|
// opencode/claude-opus-5` emitting nothing for 240s in CI as late as
|
|
389
389
|
// 2026-09-03: opencode retries above the AI SDK with no part.updated, so
|
|
390
390
|
// a run just produces nothing until it is killed. A raw-endpoint 200 is
|
|
391
|
-
// not runtime availability.
|
|
391
|
+
// not runtime availability. The move to claude-opus-5-5 proves nothing
|
|
392
|
+
// either way: the fallback stays until a run completes on the new id.
|
|
392
393
|
"claude-opus": {
|
|
393
394
|
displayName: "Claude Opus",
|
|
394
|
-
resolve: "opencode/claude-opus-5",
|
|
395
|
-
openRouterResolve: "openrouter/anthropic/claude-opus-5",
|
|
395
|
+
resolve: "opencode/claude-opus-5-5",
|
|
396
|
+
openRouterResolve: "openrouter/anthropic/claude-opus-5.5",
|
|
396
397
|
subagentModel: "claude-sonnet",
|
|
397
398
|
fallback: "opencode/claude-sonnet"
|
|
398
399
|
},
|
|
399
400
|
"claude-sonnet": {
|
|
400
401
|
displayName: "Claude Sonnet",
|
|
401
|
-
resolve: "opencode/claude-sonnet-5",
|
|
402
|
-
openRouterResolve: "openrouter/anthropic/claude-sonnet-5"
|
|
402
|
+
resolve: "opencode/claude-sonnet-5-5",
|
|
403
|
+
openRouterResolve: "openrouter/anthropic/claude-sonnet-5.5"
|
|
403
404
|
},
|
|
404
405
|
"claude-haiku": {
|
|
405
406
|
displayName: "Claude Haiku",
|
|
@@ -587,15 +588,15 @@ var providers = {
|
|
|
587
588
|
models: {
|
|
588
589
|
"claude-opus": {
|
|
589
590
|
displayName: "Claude Opus",
|
|
590
|
-
resolve: "openrouter/anthropic/claude-opus-5",
|
|
591
|
-
openRouterResolve: "openrouter/anthropic/claude-opus-5",
|
|
591
|
+
resolve: "openrouter/anthropic/claude-opus-5.5",
|
|
592
|
+
openRouterResolve: "openrouter/anthropic/claude-opus-5.5",
|
|
592
593
|
preferred: true,
|
|
593
594
|
subagentModel: "claude-sonnet"
|
|
594
595
|
},
|
|
595
596
|
"claude-sonnet": {
|
|
596
597
|
displayName: "Claude Sonnet",
|
|
597
|
-
resolve: "openrouter/anthropic/claude-sonnet-5",
|
|
598
|
-
openRouterResolve: "openrouter/anthropic/claude-sonnet-5"
|
|
598
|
+
resolve: "openrouter/anthropic/claude-sonnet-5.5",
|
|
599
|
+
openRouterResolve: "openrouter/anthropic/claude-sonnet-5.5"
|
|
599
600
|
},
|
|
600
601
|
"claude-haiku": {
|
|
601
602
|
displayName: "Claude Haiku",
|
|
@@ -745,13 +746,13 @@ var providers = {
|
|
|
745
746
|
models: {
|
|
746
747
|
"claude-opus": {
|
|
747
748
|
displayName: "Claude Opus",
|
|
748
|
-
resolve: "vercel/anthropic/claude-opus-5",
|
|
749
|
+
resolve: "vercel/anthropic/claude-opus-5.5",
|
|
749
750
|
preferred: true,
|
|
750
751
|
subagentModel: "claude-sonnet"
|
|
751
752
|
},
|
|
752
753
|
"claude-sonnet": {
|
|
753
754
|
displayName: "Claude Sonnet",
|
|
754
|
-
resolve: "vercel/anthropic/claude-sonnet-5"
|
|
755
|
+
resolve: "vercel/anthropic/claude-sonnet-5.5"
|
|
755
756
|
},
|
|
756
757
|
"claude-haiku": {
|
|
757
758
|
displayName: "Claude Haiku",
|
|
@@ -1687,7 +1688,7 @@ ${PR_SUMMARY_FORMAT}`
|
|
|
1687
1688
|
function modernizeDeprecatedMode(t) {
|
|
1688
1689
|
return {
|
|
1689
1690
|
name: "ModernizeDeprecated",
|
|
1690
|
-
description: "Migrate deprecated Terraform patterns to their modern equivalents (aws_launch_configuration \u2192 aws_launch_template, the template_file data source \u2192 the templatefile() function, EOL provider pins), one PR per pattern class. These ALTER the resource set, so they are proven by a reviewed plan delta (or shipped
|
|
1691
|
+
description: "Migrate deprecated Terraform patterns to their modern equivalents (aws_launch_configuration \u2192 aws_launch_template, the template_file data source \u2192 the templatefile() function, EOL provider pins), one PR per pattern class. These ALTER the resource set, so they are proven by a reviewed plan delta (or shipped proposed \u2014 unproven) with a `moved {}` block wherever an address can be preserved; equivalence is never claimed.",
|
|
1691
1692
|
prompt: `### Checklist
|
|
1692
1693
|
|
|
1693
1694
|
This mode migrates DEPRECATED Terraform patterns to modern ones. Unlike Refactor, these migrations swap a resource TYPE / data source / function and therefore ALTER the resource set \u2014 so equivalence is FALSE and must NEVER be claimed (the equivalence guard would block such a claim anyway). The proof is a reviewed plan delta when cloud credentials exist, otherwise the change ships labelled \`proposed \u2014 unproven\`. Author a \`moved {}\` block wherever an address genuinely carries over, and use \`create_before_destroy\` where a replace is unavoidable.
|
|
@@ -1704,7 +1705,7 @@ This mode migrates DEPRECATED Terraform patterns to modern ones. Unlike Refactor
|
|
|
1704
1705
|
|
|
1705
1706
|
6. **prove via plan, never equivalence**: do NOT call \`${t("terraform_equivalence_check")}\` \u2014 this change is behaviour-altering and an equivalence claim would be false (and is hard-blocked at push). Instead:
|
|
1706
1707
|
- **cloud credentials present**: call \`${t("terraform_plan")}\` and attach the **reviewed plan delta**. Treat \`has_destroy_or_replace\`/\`stateful_destructive\` exactly as Remediate \u2014 a stateful destroy/replace is hard-blocked at push unless \`allow_replace\` covers it; a high \`blast_radius.tier\` or \`needs_human\` adds the \`needs-human\` label (\`${t("add_labels")}\`) + a prominent callout.
|
|
1707
|
-
- **no credentials**: ship
|
|
1708
|
+
- **no credentials**: ship it proposed \u2014 unproven \u2014 add the \`proposed-unproven\` label (exactly that name), put the proposed-unproven banner unmissably at the top of the body, and state plainly that this migration ALTERS resources and the plan must be reviewed before merge.
|
|
1708
1709
|
- record the outcome with \`${t("terraform_emit_evidence")}\`.
|
|
1709
1710
|
|
|
1710
1711
|
7. **keep module tests consistent** (if a local module changed): \`${t("terraform_module_tests")}\` \u2014 update drifting \`examples/\`/tests, never weaken an assertion.
|
|
@@ -1778,21 +1779,21 @@ This mode standardises STRUCTURE while preserving BEHAVIOUR \u2014 the equivalen
|
|
|
1778
1779
|
2. **detect (two kinds of behaviour-preserving refactor \u2014 call BOTH)**:
|
|
1779
1780
|
- \`${t("module_extraction_candidates")}\` \u2192 resource \`clusters\` (raw root resources that should likely be a module call), each with matched \`candidates\` (house modules by resource-type signature; catalogue modules by real signature when terraform init already fetched their content, else by keyword; plus 1\u20132-resource groups that EXACTLY duplicate an existing module) and per-candidate \`expected_moves\` (every cluster resource \u2192 its new module address).
|
|
1780
1781
|
- \`${t("terraform_normalization_candidates")}\` \u2192 in-place idiomatic cleanups that change SYNTAX, not behaviour (redundant whole-string \`"\${expr}"\` interpolation; legacy HCL0.11 map-argument block syntax like \`vars {\` on a \`template_file\`). These relocate NOTHING \u2014 zero \`moved {}\` blocks \u2014 and are proven by the same equivalence check.
|
|
1781
|
-
- \`${t("terraform_consolidation_candidates")}\` \u2192 the SAME resource shape declared in several root directories (\`env/dev\` + \`env/staging\` + \`env/prod\`), with the single parameterised module they could all call already derived: which attributes are identical (module content) and which differ (per-environment inputs). A member root of a group will ALSO show up as a zero-candidate cluster in \`${t("module_extraction_candidates")}\` \u2014 prefer the consolidation, which does that work once for every environment instead of once per environment.
|
|
1782
|
+
- \`${t("terraform_consolidation_candidates")}\` \u2192 the SAME resource shape declared in several root directories (\`env/dev\` + \`env/staging\` + \`env/prod\`), with the single parameterised module they could all call already derived: which attributes are identical (module content) and which differ (per-environment inputs). A member root of a group will ALSO show up as a zero-candidate cluster in \`${t("module_extraction_candidates")}\` \u2014 prefer the consolidation, which does that work once for every environment instead of once per environment. Its \`near_groups\` are the opposite case: roots recognisably the same stack that DIFFER (one carries a resource the other does not), where folding them together is a design decision this mode has no basis for. Do NOT extract a near-group root's shared resources into a module on its own either \u2014 a module one twin calls and the other does not widens exactly the drift the near-group reports. Leave those roots alone and name the near-group and its \`differences\` in your report.
|
|
1782
1783
|
|
|
1783
1784
|
**Before picking, check the interface.** \`${t("module_extraction_candidates")}\` reports \`unmapped_attributes\` per candidate and a top-level \`partial_interfaces\` list: arguments the raw resources SET that the target module does not. Adopting such a module DROPS them silently \u2014 and the equivalence check cannot catch it, because once the argument is gone it is absent from both sides. Two of these are unrecoverable rather than merely wrong (\`object_lock_enabled\` cannot be set after creation; \`force_destroy\` changes deletion semantics). So: **decline the candidate, or extend the module to expose the argument and say so in the PR body.** Deleting the argument to make the shapes match is never the fix. A candidate whose \`unmapped_attributes\` is \`null\` was NOT checked \u2014 the module is external and its code is not in this repo \u2014 which is not the same as clean.
|
|
1784
1785
|
|
|
1785
|
-
Pick the highest-value refactor across ALL THREE detectors. Only when they ALL come back empty is there genuinely nothing to do: call \`${t("report_progress")}\` with an ACCURATE message \u2014 e.g. "No behaviour-preserving refactor found: no extraction clusters and no idiomatic-normalisation candidates." \u2014 and **stop**. Two anti-patterns to avoid in that message: (i) never call a repo "already idiomatic" while normalisation candidates remain; (ii) when what's left is behaviour-CHANGING modernisation \u2014 a deprecated-resource swap (\`aws_launch_configuration\`\u2192\`aws_launch_template\`, \`aws_elb\`\u2192\`aws_lb\`, \`template_file\`\u2192\`templatefile()\`) \u2014 do NOT report "nothing to refactor": that is real work, but it ALTERS the resource set, so it belongs to Remediate. Say so explicitly rather than implying the code is clean.
|
|
1786
|
+
Pick the highest-value refactor across ALL THREE detectors. Only when they ALL come back empty \u2014 or all that remains is a cluster in a near-group root \u2014 is there genuinely nothing to do: call \`${t("report_progress")}\` with an ACCURATE message \u2014 e.g. "No behaviour-preserving refactor found: no extraction clusters and no idiomatic-normalisation candidates." \u2014 and **stop**. Two anti-patterns to avoid in that message: (i) never call a repo "already idiomatic" while normalisation candidates remain; (ii) when what's left is behaviour-CHANGING modernisation \u2014 a deprecated-resource swap (\`aws_launch_configuration\`\u2192\`aws_launch_template\`, \`aws_elb\`\u2192\`aws_lb\`, \`template_file\`\u2192\`templatefile()\`) \u2014 do NOT report "nothing to refactor": that is real work, but it ALTERS the resource set, so it belongs to Remediate. Say so explicitly rather than implying the code is clean.
|
|
1786
1787
|
|
|
1787
1788
|
3. **pick scope \u2014 ONE refactor per PR**: take the largest / highest-cohesion extraction cluster, the file(s) with the most normalisation sites, or ONE consolidation group (which is one refactor spanning its member roots \u2014 see b.5), first. **Targeted directive override:** if YOUR TASK names specific files/resources, act only on those. Unless the task explicitly asks for more, open **at most one PR this run**.
|
|
1788
1789
|
|
|
1789
1790
|
**If you chose an in-place NORMALISATION** (not a module extraction), nothing relocates: SKIP steps 4\u20136 (no module source, no wiring, no \`moved {}\` blocks). Apply the \`${t("terraform_normalization_candidates")}\` edits directly (rewrite \`"\${expr}"\` \u2192 \`expr\`, \`vars {\` \u2192 \`vars = {\`, etc.) \u2014 only the literal cleanups, never a resource-type swap \u2014 then jump to step 7. The equivalence check (step 8) proves the no-op: same resource set, identical argument names, zero moves.
|
|
1790
1791
|
|
|
1791
1792
|
4. **resolve the module source (risk order \u2014 PRESERVING variants only)**: choose where the module code comes from. This mode ships only behaviour-preserving refactors; pick the lowest-risk viable source:
|
|
1792
|
-
- **b.2 \u2014 module exists, same repo** (candidate \`kind: local\`):
|
|
1793
|
+
- **b.2 \u2014 module exists, same repo** (candidate \`kind: local\`): write the candidate's \`call_source\` as the module block's \`source\` \u2014 the path from the calling root, e.g. \`../../modules/x\` from \`env/dev\`. The candidate's \`source\` is repo-relative and resolves only from the repo root. No credential. *Prefer this.*
|
|
1793
1794
|
- **b.1 \u2014 module exists, separate org repo** (candidate \`kind: git\`): use a \`git::\u2026?ref=<pinned-sha>\` source (pin an exact commit, never a branch). The equivalence proof (step 8) attributes the module's resources from what \`terraform init\` fetched at that ref \u2014 the init runs inside \`${t("terraform_validate")}\` and the equivalence check itself \u2014 so a true relocation into an org module proves \`equivalent: true\` exactly like a local one. If the fetch fails (no access, offline), the module stays invisible to the proof and it DECLINES: fix access or abandon, never relabel. Access is via the GitHub App token (or the operator's read-only deploy key) and is **READ-ONLY** \u2014 \u26A0\uFE0F **NEVER open a write PR against the module repo.** If a concern lives inside the shared module, fix it at the CALL SITE via inputs, or note "fix needed in module repo \`<repo>\`" \u2014 do not edit vendored module code. A candidate's \`expected_moves\` for a git module is a TEMPLATE built from the root's own leaf names \u2014 correct each \`to\` against the module's REAL resource addresses (read the fetched module, or the equivalence check's \`uncovered_moves\`/\`resource_set_diff\` will name them).
|
|
1794
|
-
- **b.3 \u2014 no module exists, author it** (no candidate): MECHANICAL extraction. Take the cluster's existing resource blocks, move them verbatim into \`./modules/<name>/\`, parameterise each hardcoded value into a \`variable\` (with a SECURE \`default\`, or **no default** so the caller must set it \u2014 NEVER preserve an insecure value as a default), expose the needed \`output\`s, and replace the root resources with a \`module\` block passing the ORIGINAL values. You do the naming + structure; the equivalence check (step 8) is the guardrail that proves you changed nothing.
|
|
1795
|
-
- **b.5 \u2014 consolidation: one module, N environment roots** (a \`${t("terraform_consolidation_candidates")}\` group): the b.3 extraction done once for a shape several roots duplicate. Author \`./modules/<name>/\` from the group's resources, then convert EVERY member root in this same PR
|
|
1795
|
+
- **b.3 \u2014 no module exists, author it** (no candidate): MECHANICAL extraction. Take the cluster's existing resource blocks, move them verbatim into \`./modules/<name>/\`, parameterise each hardcoded value into a \`variable\` (with a SECURE \`default\`, or **no default** so the caller must set it \u2014 NEVER preserve an insecure value as a default), expose the needed \`output\`s, and replace the root resources with a \`module\` block passing the ORIGINAL values. Its \`source\` is relative to the root's directory: \`../../modules/<name>\` from \`env/dev\`, \`./modules/<name>\` only from the repo root. You do the naming + structure; the equivalence check (step 8) is the guardrail that proves you changed nothing.
|
|
1796
|
+
- **b.5 \u2014 consolidation: one module, N environment roots** (a \`${t("terraform_consolidation_candidates")}\` group): the b.3 extraction done once for a shape several roots duplicate. Author \`./modules/<name>/\` from the group's resources, then convert EVERY member root in this same PR; each member's call writes that member's \`call_source\` as its \`source\`. Rules that are not optional: (i) move each resource block **verbatim, keeping its resource name** \u2014 the group's \`expected_moves_by_member\` assumes the leaf name survives, and renaming it to \`this\` invalidates every move; a \`module\` entry in the group's \`resources\` is a module CALL the shape includes \u2014 move that call block too, keeping its name, and write its \`wrapped_calls[].source\` (relative to the NEW module, e.g. \`../s3\`) as its \`source\`, since the root's \`../../modules/s3\` no longer resolves from there; (ii) put each \`module_body_values\` entry in the module exactly as written \u2014 \`kind: "attribute"\` is a right-hand side (\`bucket = <value>\`), \`kind: "block"\` is a nested block's BODY and goes back inside its own braces (\`versioning_configuration { <value> }\`), never as \`versioning_configuration = { \u2026 }\`; (iii) replace ONLY the \`variables\` attributes with \`var.<name>\`, and copy each member's \`per_member.value\` into THAT member's call **verbatim \u2014 never retyped, never reformatted, never swapped between environments**. A \`classification: reference\` variable (identical text like \`var.bucket_name\` in every root) is still per-environment: the text matches, the value does not. Never fold differing values into one interpolated template (\`"acme-\${var.env}-logs"\`) \u2014 the job is to preserve N values, not to invent a rule that reproduces them. Paste each root's \`moved {}\` blocks, then prove ALL the roots with a single \`${t("terraform_equivalence_check")}\`.
|
|
1796
1797
|
- **b.4 \u2014 third-party module (behaviour-ALTERING)**: adopting a community/registry module changes defaults and often adds resources, so equivalence is FALSE and must **never** be claimed. This is a DIFFERENT proof (a reviewed plan delta, opt-in; \`proposed \u2014 unproven\` by default) \u2014 follow the **b.4 path** at the end of this checklist INSTEAD of steps 5\u201312's equivalence gate. Reserve it for a genuine module adoption; a plain relocation of your own resources is b.1/b.2/b.3.
|
|
1797
1798
|
|
|
1798
1799
|
5. **wire the module call**: get the chosen module's REAL interface so the \`module\` block passes its actual \`variable\` names (a missing required variable is a PR question for the reviewer, never a guessed value). For a LOCAL module dir (b.2/b.3) call \`${t("terraform_module_interface")}\`; for a REGISTRY-sourced module \u2014 a \`module_catalogue\` entry like \`terraform-aws-modules/vpc/aws\`, or the b.4 adoption \u2014 call \`${t("terraform_module_lookup")}\` to read its published \`required_inputs\`/\`outputs\` + resolved \`version\` from the registry. \`${t("terraform_module_graph")}\` shows existing local module dirs + callers. Apply the same secure-default discipline as Remediate: a parameterised value's default must be the secure choice or absent \u2014 never a hidden-insecure default.
|
|
@@ -1804,7 +1805,7 @@ This mode standardises STRUCTURE while preserving BEHAVIOUR \u2014 the equivalen
|
|
|
1804
1805
|
8. **PROVE equivalence (the gate)**: call \`${t("terraform_equivalence_check")}\`. It compares the working tree against the run-start commit and must return \`equivalent: true\` \u2014 meaning: same resource multiset (\`resource_set_diff\` empty), identical per-resource argument names, **zero \`uncovered_moves\`**, and validate + fmt clean. If \`equivalent\` is false:
|
|
1805
1806
|
- any \`uncovered_moves\` \u2192 add the missing \`moved {}\` block(s) for those addresses and re-run;
|
|
1806
1807
|
- a non-empty \`resource_set_diff.added\`/\`removed\` or \`arg_diffs\` \u2192 the refactor changed behaviour; fix it back to a true no-op, or ABANDON the cluster (it may be a behaviour-altering adoption \u2014 see step 4).
|
|
1807
|
-
- a non-empty \`value_mismatches\` (\`values_preserved: false\`) \u2192 a
|
|
1808
|
+
- a non-empty \`value_mismatches\` (\`values_preserved: false\`) \u2192 a VALUE changed: the check follows each value \u2014 \`var.<x>\` back through the call site, locals, module outputs, data sources, nested and \`dynamic\` blocks \u2014 and it no longer yields what the before tree had. This is the b.5 failure nothing else can see \u2014 the resource set, the argument names, the moves, validate and even a plan are all clean, and the apply applies another environment's configuration. Each row names the root, the resource, the argument path, the \`before\` value and what it now resolves to: put that root's OWN value back at its OWN call site, copied verbatim from the before tree. A row on a resource you did not move means a shared module's body or an untouched call site changed under it \u2014 undo that edit.
|
|
1808
1809
|
- a \`verdict: "unverifiable"\` naming a repetition that moved between levels \u2192 the per-key \`moved {}\` blocks are the right shape and the proof will not evaluate the \`for_each\` expression to confirm the key set is complete. This is the ONE case a plan settles: run \`${t("terraform_plan")}\` and reach \`refactor_safe: true\` on the tree you are pushing, and the push is allowed with the verdict left at \`unverifiable\` (never restated as proven). Without cloud credentials, escalate the cluster rather than pushing.
|
|
1809
1810
|
Do not proceed to push until \`equivalent: true\`, or until that one \`unverifiable\` is plan-corroborated. **Optional stronger proof:** when cloud credentials are present, call \`${t("terraform_plan")}\` \u2014 a \`refactor_safe: true\` (pure-move) plan corroborates the keys-free proof; attach the plan to the PR. Then **re-run \`${t("terraform_equivalence_check")}\`** so the plan's own reading of what moved folds into the recorded proof (\`plan_corroborated: true\`). Make no edits between the two calls \u2014 the pairing is tied to the exact tree the plan saw and is discarded if the tree changed.
|
|
1810
1811
|
|
|
@@ -1814,7 +1815,7 @@ This mode standardises STRUCTURE while preserving BEHAVIOUR \u2014 the equivalen
|
|
|
1814
1815
|
|
|
1815
1816
|
11. **branch + commit + push**: create \`infraweaver/refactor-<slug>\` from the **current HEAD** (the scanned checkout) via \`${t("git")}\` \u2014 do NOT switch base first. \`git add\` only the files you changed, commit with a message naming the module + cluster (e.g. \`refactor(tf): extract S3 logging resources into module.logging\`), then \`${t("push_branch")}\` (same push/prepush guidance as Build mode in *SYSTEM*). The push guardrails enforce Terraform-only paths, no inlined secrets, AND the equivalence hard-fail \u2014 which refuses an unproven refactor AND one that recorded no proof at all, so skipping step 8 blocks the push rather than slipping past it.
|
|
1816
1817
|
|
|
1817
|
-
12. **open the PR (MANDATORY body)**: \`${t("create_pull_request")}\` (omit \`base\` \u2014 it resolves to the run's base branch). Build the body with the **Refactor PR format** at the end of this checklist \u2014 status banner \u2192 title \u2192 \`## What changed\` (include the adopted candidate's \`selection_reason\` VERBATIM; if you omit it, \`${t("create_pull_request")}\` appends it under \`### Module selection\`) \u2192 \`## Equivalence proof\` (built ONLY from \`${t("terraform_equivalence_check")}\`, the proof not a self-report) \u2192 composition note. Optionally call \`${t("terraform_emit_evidence")}\` \u2014 it records the equivalence statement with the proof behind it; when its result says \`signed: true\`, add a one-line evidence note to the PR body naming the artefact and the signing key fingerprint (\`key_fingerprint_sha256\`) so a reviewer can verify with \`infraweaver verify-evidence\`. Never auto-merge.
|
|
1818
|
+
12. **open the PR (MANDATORY body)**: \`${t("create_pull_request")}\` (omit \`base\` \u2014 it resolves to the run's base branch). Build the body with the **Refactor PR format** at the end of this checklist \u2014 status banner \u2192 title \u2192 \`## What changed\` (include the adopted candidate's \`selection_reason\` VERBATIM; if you omit it, \`${t("create_pull_request")}\` appends it under \`### Module selection\`) \u2192 \`## Equivalence proof\` (built ONLY from \`${t("terraform_equivalence_check")}\`, the proof not a self-report) \u2192 composition note. Optionally call \`${t("terraform_emit_evidence")}\` \u2014 it records the equivalence statement with the proof behind it; when its result says \`signed: true\`, add a one-line evidence note to the PR body naming the artefact and the signing key fingerprint (\`key_fingerprint_sha256\`) so a reviewer can verify with \`infraweaver verify-evidence\`. Never auto-merge. If \`${t("create_pull_request")}\` refuses because an open PR already changes the same files, that PR is this refactor waiting for review: delete the branch you pushed and report it \u2014 never re-open under another branch name.
|
|
1818
1819
|
|
|
1819
1820
|
13. **finalize**: call \`${t("report_progress")}\` once with a summary \u2014 which cluster was modularised, into which module, the PR link, and the equivalence verdict (or the exact tool error if a step failed).
|
|
1820
1821
|
|
|
@@ -1823,11 +1824,11 @@ This mode standardises STRUCTURE while preserving BEHAVIOUR \u2014 the equivalen
|
|
|
1823
1824
|
Use this ONLY when the chosen source is a community/registry module that changes behaviour \u2014 equivalence is false here and must NEVER be claimed. The proof is a reviewed plan delta, gated behind opt-in read creds; by default the change ships labelled \`proposed \u2014 unproven\` and asserts nothing about runtime.
|
|
1824
1825
|
|
|
1825
1826
|
- **Find + read the module**: when the operator's \`module_catalogue\` / \`refactor_source\` doesn't already name the module, call \`${t("terraform_module_search")}\` to find a published one \u2014 pass your org's \`namespace\` to prefer your OWN vetted modules over a stranger's. Search covers the PUBLIC registry only: a private-registry module must be named in \`module_catalogue\` / \`refactor_source\`, or looked up directly by its \`host/namespace/name/provider\` address. Then call \`${t("terraform_module_lookup")}\` on the chosen \`namespace/name/provider\` to read its \`required_inputs\` / \`outputs\` + latest \`version\`, so the \`module\` block wires the real interface and pins an exact version.
|
|
1826
|
-
- **Scan with external-module resolution ON**: call \`${t("terraform_scan")}\` with \`download_external_modules: true\` (and verify with \`${t("terraform_verify_remediation")}\`'s \`download_external_modules: true\`) so the adopted module's INTERNALS stay in scope. Skipping it would let the module's findings vanish unfixed \u2014 a false \u2717\u2192\u2713 ("finding-laundering"). Non-negotiable.
|
|
1827
|
+
- **Scan with external-module resolution ON**: call \`${t("terraform_scan")}\` with \`download_external_modules: true\` (and verify with \`${t("terraform_verify_remediation")}\`'s \`download_external_modules: true\`) so the adopted module's INTERNALS stay in scope. Skipping it would let the module's findings vanish unfixed \u2014 a false \u2717\u2192\u2713 ("finding-laundering"). Non-negotiable. A finding inside the module is reported on the line of the \`module\` block that calls it, with the module resource it is about named in the evidence ("\u2014 inside the module: module.data.aws_s3_bucket.this (main.tf:25)").
|
|
1827
1828
|
- **Remediate module-sourced findings via INPUTS**: set the hardening variable at the call site \u2014 never edit vendored module code. Where the module exposes no knob for a control, label it \`needs-fork\` and surface it; never a silent pass.
|
|
1828
1829
|
- **Pin the exact version**: a registry \`version\` or a git \`?ref=<sha>\` (provenance discipline \u2014 the analog to a SHA-pinned action); it is recorded in the evidence bundle.
|
|
1829
1830
|
- **Proof**:
|
|
1830
|
-
- **Default (no read creds)**: do NOT run a plan. Call \`${t("terraform_emit_evidence")}\` with \`proposed_unproven: true\` **before you push** \u2014 the bundle records that the change asserts nothing about runtime, and \`${t("push_branch")}\` refuses a refactor that reaches a reviewer with neither a proof nor that marker. Then add the \`proposed-unproven\` label (\`${t("add_labels")}\`) and put the proposed-unproven banner unmissably at the top of the body. It must NEVER read as a proven / equivalence change.
|
|
1831
|
+
- **Default (no read creds)**: do NOT run a plan. Call \`${t("terraform_emit_evidence")}\` with \`proposed_unproven: true\` **before you push** \u2014 the bundle records that the change asserts nothing about runtime (commit the file it writes with the change, unedited: the push admits exactly those bytes), and \`${t("push_branch")}\` refuses a refactor that reaches a reviewer with neither a proof nor that marker. Then add the \`proposed-unproven\` label (\`${t("add_labels")}\`) and put the proposed-unproven banner unmissably at the top of the body. It must NEVER read as a proven / equivalence change.
|
|
1831
1832
|
- **Opt-in (read-only backend/state supplied)**: call \`${t("terraform_plan")}\` **before you push**, then record the outcome with \`${t("terraform_emit_evidence")}\` passing \`reviewed_plan: true\` (refused if no plan ran) \u2014 that marker is what the push gate accepts for a behaviour-altering adoption; a plan alone no longer discharges it. Emit the **reviewed plan delta** in the PR body as the proof (replacing the proposed-unproven section). No supplied credential is stored, and no plan attribute VALUES are kept \u2014 only the aggregate totals and affected addresses the push gates read.
|
|
1832
1833
|
- Same guardrails: one cluster per PR, never auto-merge, Terraform-only, never write-PR a read-only module repo.
|
|
1833
1834
|
|
|
@@ -1910,7 +1911,7 @@ function remediateMode(t) {
|
|
|
1910
1911
|
**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\` \u2014 "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>\` \u2014 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.
|
|
1911
1912
|
|
|
1912
1913
|
4. **for the chosen group**:
|
|
1913
|
-
- **base branch**: this run's base branch is resolved deterministically \u2014 \`${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.
|
|
1914
|
+
- **base branch**: this run's base branch is resolved deterministically \u2014 \`${t("create_pull_request")}\` targets the \`base_branch\` input if set, else the branch the run started on, 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.
|
|
1914
1915
|
- **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.
|
|
1915
1916
|
- **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 \u2014 branching from the scanned checkout keeps the PR diff to exactly your fix.
|
|
1916
1917
|
- **honest refusal (decide BEFORE fixing)**: if the group's concerns appear in the scan's \`refusal_candidates\` (the fix needs a human decision \u2014 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 \u2014 never a guessed, unverifiable PR.
|
|
@@ -2237,9 +2238,10 @@ This mode reviews the **Terraform** in a human PR and submits ONE review. It is
|
|
|
2237
2238
|
|
|
2238
2239
|
4. **introduced findings \u2014 comment or dismiss each one**: for EVERY introduced finding the delta lists (the most severe first; past its limit it only counts the rest, which need nothing from you), read the cited code and decide:
|
|
2239
2240
|
- **comment**: an inline comment on the changed line, with \`finding_id\` set to that finding's id and an \`audit\` filled from your verification pass. Explain the concrete consequence in this configuration, not the rule's generic text. The tool adds the scanner, rule and mapped controls itself \u2014 do not restate them.
|
|
2240
|
-
- **dismiss**: list it in \`dismissed\` with the specific reason it needs no comment here \u2014 a named sibling resource or module default already satisfies the control, the resource is not instantiated (\`count = 0\`), or the rule does not fit this configuration for a stated reason. "Not important" is not a reason. A finding marked \`possibly_preexisting\` is usually a rename, or a move into another module, without a \`moved\` block:
|
|
2241
|
+
- **dismiss**: list it in \`dismissed\` with the specific reason it needs no comment here \u2014 a named sibling resource or module default already satisfies the control, the resource is not instantiated (\`count = 0\`), or the rule does not fit this configuration for a stated reason. "Not important" is not a reason. A finding marked \`possibly_preexisting\` is usually a rename, or a move into another module, without a \`moved\` block (\`renamed_from\` names the resource it pairs with): the review's verdict already names each such resource once, so make ONE comment per renamed resource giving the \`moved\` block it needs, and dismiss its other findings as part of that rename rather than treating each misconfiguration as new.
|
|
2241
2242
|
**One comment per root cause.** When several introduced findings are the same problem on the same lines (two scanners flagging one open ingress rule), comment once with the most specific \`finding_id\` and dismiss the others as duplicates of that comment.
|
|
2242
2243
|
The review tool refuses once if any introduced finding is neither commented nor dismissed; on the retry it names the rest to the author as unaddressed.
|
|
2244
|
+
**A comment limit is not yours to apply.** The repo may set \`review_comment_limit\`; the tool enforces it after you submit, keeping the most severe comments inline and listing the rest in the review body. Comment on or dismiss every introduced finding exactly as you would without one \u2014 never leave a finding unaddressed, or fold it into prose, because of a limit.
|
|
2243
2245
|
|
|
2244
2246
|
5. **suggestions**: offer a \`suggestion\` only on a scanner-backed comment whose fix fits inside that comment's own line range [start_line, line], preserving exact indentation. The tool applies it to a scratch copy, re-runs fmt, validate and every scanner, and makes it committable only if the finding is gone and nothing new appeared ("Proven by re-scan"); otherwise it is shown as plain code marked "Unproven". A fix that needs another resource, a new variable or lines outside the range is described in prose instead \u2014 it cannot be proven from a line range. A judgement's suggestion is always shown as unproven code.
|
|
2245
2247
|
|
|
@@ -2249,6 +2251,7 @@ This mode reviews the **Terraform** in a human PR and submits ONE review. It is
|
|
|
2249
2251
|
- **module & interface hygiene** \u2014 a variable losing its validation or its secure default, a sensitive output not marked \`sensitive\`.
|
|
2250
2252
|
- **correctness** \u2014 would this pass \`terraform validate\`? deprecated arguments, wrong types, \`count\`/\`for_each\` index churn.
|
|
2251
2253
|
Do NOT re-raise as a judgement anything an introduced finding already covers, and do not comment on pre-existing code the PR did not touch.
|
|
2254
|
+
When one judgement applies at several sites (the same constraint loosened in every root, the same block missing from each copy), post it ONCE, on the first site, and name the other \`file:line\` sites in that comment \u2014 never the same point once per file.
|
|
2252
2255
|
|
|
2253
2256
|
If the Terraform change is **genuinely trivial** (a comment/whitespace-only edit, a tag value, a description string) AND there are no introduced findings, skip the lenses and submit a \`> \u2705 No new issues found.\` review.
|
|
2254
2257
|
|
|
@@ -2262,9 +2265,11 @@ This mode reviews the **Terraform** in a human PR and submits ONE review. It is
|
|
|
2262
2265
|
|
|
2263
2266
|
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 \u2014 these IaC false-positive patterns (a separate hardening resource, a disabled resource, a call-site override) are exactly what a generic code lens misses. A scanner-backed finding the refute lens knocks down is DISMISSED with that reason, not silently dropped.
|
|
2264
2267
|
|
|
2265
|
-
|
|
2268
|
+
A \`betterleaks:\` finding is a secret the PR's commits add, in any file, Terraform or not: answer it like any scanner finding, and never quote, decode or guess its value \u2014 say which file and line holds it and that it must be removed from the history and rotated.
|
|
2266
2269
|
|
|
2267
|
-
|
|
2270
|
+
Every \u{1F6A8}/\u26A0\uFE0F comment \u2014 and every scanner-backed comment \u2014 carries an \`audit\`; one without is withheld. Then ALWAYS submit exactly one review via \`${t("create_pull_request_review")}\` (never \`report_progress\` for review output), with \`comments\` (anchored to NEW line numbers) and \`dismissed\`. Non-anchorable concerns go in the body \`### \` sections. With more than about 10 comments and dismissals in all, stage them first with \`${t("stage_review")}\` in calls of up to 10 \u2014 one dismissal entry can carry several \`finding_ids\` that share a reason \u2014 and then submit \`${t("create_pull_request_review")}\` with the body alone: one oversized call can be cut off mid-way and cost the whole review. The tool opens the body with the verdict (merge-blocking or not, and the author's next steps) and closes it with the scan delta \u2014 counts, gate, controls, dismissals and suggestion proofs \u2014 so do not restate those yourself.
|
|
2271
|
+
|
|
2272
|
+
Same opening-callout ladder + \`approved\` lever as Review \u2014 \`[!CAUTION]\` (a state-destroying or security-breaking change) \u2192 \`[!IMPORTANT]\` (a real misconfig to fix before merge; at least this tier whenever an introduced high/critical finding stands after verification) \u2192 \`> \u2139\uFE0F ...\` (minor HCL/style nits) \u2192 \`> \u2705 No new issues found.\` (mergeable, \`approved: true\`, no inline comments). Pick the tier the author's actual next action justifies. note: the first \`create_pull_request_review\` submission may error with a one-time diff-coverage, audit or scan-delta nudge \u2014 fix what it names (stage what it asks for) and retry.
|
|
2268
2273
|
|
|
2269
2274
|
9. **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.
|
|
2270
2275
|
|
|
@@ -2278,20 +2283,20 @@ ${PR_SUMMARY_FORMAT}`
|
|
|
2278
2283
|
function updateDependenciesMode(t) {
|
|
2279
2284
|
return {
|
|
2280
2285
|
name: "UpdateDependencies",
|
|
2281
|
-
description: "Upgrade outdated or unpinned Terraform provider and module versions to current, supported pins, then PROVE the bump is safe \u2014 config-preserving bumps via the equivalence check (same resource set + arguments), behaviour-affecting major bumps via a reviewed plan delta (or shipped
|
|
2286
|
+
description: "Upgrade outdated or unpinned Terraform provider and module versions to current, supported pins, then PROVE the bump is safe \u2014 config-preserving bumps via the equivalence check (same resource set + arguments), behaviour-affecting major bumps via a reviewed plan delta (or shipped proposed \u2014 unproven). One scoped PR per provider/module.",
|
|
2282
2287
|
prompt: `### Checklist
|
|
2283
2288
|
|
|
2284
2289
|
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 \u2014 unproven\`) \u2014 never a false equivalence claim.
|
|
2285
2290
|
|
|
2286
2291
|
1. **task list**: create your task list for this run as your first action.
|
|
2287
2292
|
|
|
2288
|
-
2. **detect**: call \`${t("terraform_version_currency")}\` \u2014 it returns provider/module pins that are outdated or unpinned, each with the current constraint, the latest version,
|
|
2293
|
+
2. **detect**: call \`${t("terraform_version_currency")}\` \u2014 it returns provider/module pins that are outdated or unpinned, each with the current constraint, the latest version, how many majors behind it is, and \`latest_in_major\` \u2014 the newest release in the major it already sits on.
|
|
2289
2294
|
|
|
2290
|
-
3. **pick scope \u2014 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).
|
|
2295
|
+
3. **pick scope \u2014 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). A row with \`latest_in_major\` has an upgrade that crosses no major \u2014 take that one when the major is not allowed (\`allow_major_upgrade\`), rather than reporting that nothing could be done. **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).
|
|
2291
2296
|
|
|
2292
2297
|
4. **classify the bump (this decides the proof)**:
|
|
2293
|
-
- **config-preserving** \u2014 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 \u2014 it must return \`equivalent: true\` (same resource set, identical arguments, validate + fmt clean). The push hard-fails an unproven one.
|
|
2294
|
-
- **behaviour-affecting** \u2014 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 \u2014 use \`${t("terraform_validate")}\`'s \`providers\` majors and \`${t("terraform_provider_schema")}\` to get the new argument shape \u2014 then prove via the reviewed-plan path in step 6.
|
|
2298
|
+
- **config-preserving** \u2014 a PROVIDER 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 \u2014 it must return \`equivalent: true\` (same resource set, identical arguments, validate + fmt clean). The push hard-fails an unproven one.
|
|
2299
|
+
- **behaviour-affecting** \u2014 a major bump that requires resource/argument changes (renamed args, new required blocks, changed defaults), AND every registry MODULE version change, patch included: the module's resources are its code, which the bump replaces outside this tree, so \`${t("terraform_equivalence_check")}\` cannot see what changed and will not claim it. Equivalence is FALSE and must never be claimed. Make the minimal code changes the new major requires \u2014 use \`${t("terraform_validate")}\`'s \`providers\` majors and \`${t("terraform_provider_schema")}\` to get the new argument shape \u2014 then prove via the reviewed-plan path in step 6.
|
|
2295
2300
|
|
|
2296
2301
|
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:
|
|
2297
2302
|
- \`needs_human: true\` \u2014 the transform MOVES STATE (a removed resource type, or a rename the vendor marks manual), forces replacement, or needs the VALUE changed and not just the name. 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. Read \`needs_human_reason\` \u2014 the three causes need different PR bodies.
|
|
@@ -2303,7 +2308,7 @@ This mode turns the version-currency ADVISORY into action: it bumps stale/unpinn
|
|
|
2303
2308
|
|
|
2304
2309
|
6. **prove safety**:
|
|
2305
2310
|
- config-preserving \u2192 \`${t("terraform_equivalence_check")}\` is the proof (\`equivalent: true\`).
|
|
2306
|
-
- behaviour-affecting \u2192 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 \u2014 abandon otherwise). With NO creds, do not guess: ship the PR labelled \`proposed
|
|
2311
|
+
- behaviour-affecting \u2192 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 \u2014 abandon otherwise). With NO creds, do not guess: ship the PR labelled \`proposed-unproven\` (exactly that name, \`${t("add_labels")}\`) with the proposed-unproven banner, stating the major bump ALTERS behaviour and the plan must be reviewed.
|
|
2307
2312
|
- record either proof with \`${t("terraform_emit_evidence")}\`.
|
|
2308
2313
|
|
|
2309
2314
|
7. **keep module tests consistent** (only if the bump changed a local module's interface): \`${t("terraform_module_tests")}\` \u2014 update the drifting \`examples/\`/tests, never weaken an assertion.
|
|
@@ -2349,6 +2354,84 @@ var NON_COMMITTING_MODES = new Set(
|
|
|
2349
2354
|
modes.filter((m) => m.nonCommitting).map((m) => m.name)
|
|
2350
2355
|
);
|
|
2351
2356
|
|
|
2357
|
+
// node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/lib/buffer_utils.js
|
|
2358
|
+
var encoder = new TextEncoder();
|
|
2359
|
+
var decoder = new TextDecoder();
|
|
2360
|
+
var strictDecoder = new TextDecoder("utf-8", { fatal: true });
|
|
2361
|
+
var MAX_INT32 = 2 ** 32;
|
|
2362
|
+
function decodeBase64(encoded, url = false) {
|
|
2363
|
+
if (Uint8Array.fromBase64)
|
|
2364
|
+
return Uint8Array.fromBase64(encoded, { alphabet: url ? "base64url" : "base64" });
|
|
2365
|
+
if (url) {
|
|
2366
|
+
if (encoded.includes("+") || encoded.includes("/"))
|
|
2367
|
+
throw new TypeError("Invalid base64url");
|
|
2368
|
+
encoded = encoded.replace(/-/g, "+").replace(/_/g, "/");
|
|
2369
|
+
}
|
|
2370
|
+
const binary = atob(encoded), bytes = new Uint8Array(binary.length);
|
|
2371
|
+
for (let i = 0; i < binary.length; i++)
|
|
2372
|
+
bytes[i] = binary.charCodeAt(i);
|
|
2373
|
+
return bytes;
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
// node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/util/errors.js
|
|
2377
|
+
var JOSEError = class extends Error {
|
|
2378
|
+
static code = "ERR_JOSE_GENERIC";
|
|
2379
|
+
code = "ERR_JOSE_GENERIC";
|
|
2380
|
+
constructor(message, options) {
|
|
2381
|
+
super(message, options), this.name = this.constructor.name, Error.captureStackTrace?.(this, this.constructor);
|
|
2382
|
+
}
|
|
2383
|
+
};
|
|
2384
|
+
var JWTInvalid = class extends JOSEError {
|
|
2385
|
+
static code = "ERR_JWT_INVALID";
|
|
2386
|
+
code = "ERR_JWT_INVALID";
|
|
2387
|
+
};
|
|
2388
|
+
|
|
2389
|
+
// node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/util/base64url.js
|
|
2390
|
+
var invalid = "The input to be decoded is not correctly encoded.";
|
|
2391
|
+
function decode(input) {
|
|
2392
|
+
try {
|
|
2393
|
+
return decodeBase64(typeof input == "string" ? input : decoder.decode(input), true);
|
|
2394
|
+
} catch (cause) {
|
|
2395
|
+
throw new TypeError(invalid, { cause });
|
|
2396
|
+
}
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2399
|
+
// node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/lib/validate.js
|
|
2400
|
+
function isObject(input) {
|
|
2401
|
+
if (typeof input != "object" || input === null || Object.prototype.toString.call(input) !== "[object Object]")
|
|
2402
|
+
return false;
|
|
2403
|
+
const prototype = Object.getPrototypeOf(input);
|
|
2404
|
+
return prototype === null || Object.getPrototypeOf(prototype) === null;
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
// node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/util/decode_jwt.js
|
|
2408
|
+
function decodeJwt(jwt) {
|
|
2409
|
+
if (typeof jwt != "string")
|
|
2410
|
+
throw new JWTInvalid("JWTs must use Compact JWS serialization, JWT must be a string");
|
|
2411
|
+
const { 1: payload, length } = jwt.split(".");
|
|
2412
|
+
if (length === 5)
|
|
2413
|
+
throw new JWTInvalid("Only JWTs using Compact JWS serialization can be decoded");
|
|
2414
|
+
if (length !== 3)
|
|
2415
|
+
throw new JWTInvalid("Invalid JWT");
|
|
2416
|
+
if (!payload)
|
|
2417
|
+
throw new JWTInvalid("JWTs must contain a payload");
|
|
2418
|
+
let decoded;
|
|
2419
|
+
try {
|
|
2420
|
+
decoded = decode(payload);
|
|
2421
|
+
} catch {
|
|
2422
|
+
throw new JWTInvalid("Failed to base64url decode the payload");
|
|
2423
|
+
}
|
|
2424
|
+
let result;
|
|
2425
|
+
try {
|
|
2426
|
+
result = JSON.parse(strictDecoder.decode(decoded));
|
|
2427
|
+
} catch {
|
|
2428
|
+
throw new JWTInvalid("Failed to parse the decoded payload as JSON");
|
|
2429
|
+
}
|
|
2430
|
+
if (!isObject(result))
|
|
2431
|
+
throw new JWTInvalid("Invalid JWT Claims Set");
|
|
2432
|
+
return result;
|
|
2433
|
+
}
|
|
2434
|
+
|
|
2352
2435
|
// src/utils/agent/codexOAuth.ts
|
|
2353
2436
|
var CODEX_OAUTH_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
2354
2437
|
var CODEX_OAUTH_TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
@@ -2393,16 +2476,14 @@ async function refreshCodexAuthBody(body) {
|
|
|
2393
2476
|
};
|
|
2394
2477
|
}
|
|
2395
2478
|
function decodeJwtExpMs(token) {
|
|
2396
|
-
|
|
2397
|
-
if (parts.length !== 3) return null;
|
|
2398
|
-
let payload;
|
|
2479
|
+
let exp;
|
|
2399
2480
|
try {
|
|
2400
|
-
|
|
2481
|
+
exp = decodeJwt(token).exp;
|
|
2401
2482
|
} catch {
|
|
2402
2483
|
return null;
|
|
2403
2484
|
}
|
|
2404
|
-
if (typeof
|
|
2405
|
-
return
|
|
2485
|
+
if (typeof exp !== "number" || !Number.isFinite(exp)) return null;
|
|
2486
|
+
return exp * 1e3;
|
|
2406
2487
|
}
|
|
2407
2488
|
function parseCodexAuthBody(raw) {
|
|
2408
2489
|
let parsed;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ToolState } from "../toolState.js";
|
|
2
|
+
import type { RunContextData } from "../utils/cloud/runContextData.js";
|
|
3
|
+
import type { ResolvedPayload } from "../utils/config/payload.js";
|
|
4
|
+
/**
|
|
5
|
+
* The branch a run's change is proposed into. Its own module because three
|
|
6
|
+
* places need the same answer and must never disagree: the PR tools (where the
|
|
7
|
+
* PR goes), `terraform_change_summary` (what the change is measured against)
|
|
8
|
+
* and the push guardrail that refuses a change an open PR already proposes.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* The slice `resolveBaseBranch` reads: the declared base-branch input, the
|
|
12
|
+
* branch the run started on and the repository's default branch.
|
|
13
|
+
*/
|
|
14
|
+
export interface BaseBranchContext {
|
|
15
|
+
payload: ResolvedPayload;
|
|
16
|
+
repo: RunContextData["repo"];
|
|
17
|
+
toolState: Pick<ToolState, "initialHead">;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Pure base-branch picker. Precedence: an explicit `base_branch` declaration
|
|
21
|
+
* always wins; then the branch the run started on, so a run dispatched on
|
|
22
|
+
* `develop` proposes into `develop`; then the repository's default branch
|
|
23
|
+
* (GitHub reports it — normally `main`, sometimes `master`); otherwise prefer
|
|
24
|
+
* `main`, then `master`; final fallback `main`. Split out from git/ctx so it's
|
|
25
|
+
* exhaustively testable.
|
|
26
|
+
*/
|
|
27
|
+
export declare function pickBaseBranch(opts: {
|
|
28
|
+
declared?: string | undefined;
|
|
29
|
+
startBranch?: string | undefined;
|
|
30
|
+
defaultBranch?: string | undefined;
|
|
31
|
+
mainExists: boolean;
|
|
32
|
+
masterExists: boolean;
|
|
33
|
+
}): string;
|
|
34
|
+
/**
|
|
35
|
+
* Deterministically resolve the branch a PR targets, so the base is never left
|
|
36
|
+
* to the agent's guess: the `base_branch` input, else the branch the run
|
|
37
|
+
* started on, else the repository's default branch (`main`, or `master`). Git
|
|
38
|
+
* is only probed for main/master in the last-resort case where none of those is
|
|
39
|
+
* available (essentially never).
|
|
40
|
+
*/
|
|
41
|
+
export declare function resolveBaseBranch(ctx: BaseBranchContext): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BaseBranchContext } from "./
|
|
1
|
+
import type { BaseBranchContext } from "./baseBranch.js";
|
|
2
2
|
/**
|
|
3
3
|
* AI PR summaries — the deterministic Terraform-change anchor. A PR summary
|
|
4
4
|
* written purely by the model drifts (miscounts resources, invents changes). This
|
|
@@ -89,6 +89,8 @@ export interface CitedCode {
|
|
|
89
89
|
}
|
|
90
90
|
/** Evaluate one audited finding. Pure. */
|
|
91
91
|
export declare function evaluateFindingAudit(audit: FindingAudit, cited: CitedCode): AuditOutcome;
|
|
92
|
+
/** The severity marker a comment body opens with, if any. */
|
|
93
|
+
export declare function leadingMarker(body: string): string | null;
|
|
92
94
|
/** Replace the body's leading severity marker with `marker`, or prefix it. */
|
|
93
95
|
export declare function withMarker(body: string, marker: string): string;
|
|
94
96
|
interface AuditableComment {
|
package/dist/mcp/guardrails.d.ts
CHANGED
|
@@ -394,6 +394,17 @@ export declare function resolveMaxPrs(ctx: GuardrailContext): number;
|
|
|
394
394
|
* PRs instead of fanning out.
|
|
395
395
|
*/
|
|
396
396
|
export declare function assertUnderPrCap(ctx: GuardrailContext): void;
|
|
397
|
+
/**
|
|
398
|
+
* Refuse a guarded-mode PR whose head is not a commit this run pushed.
|
|
399
|
+
*
|
|
400
|
+
* Every content guardrail — the path allow-list, the secret scan, the rename
|
|
401
|
+
* and equivalence gates — runs in `push_branch`, so a PR is only as safe as the
|
|
402
|
+
* push behind it. A head this run never pushed went through none of them: a
|
|
403
|
+
* branch another run (or a person) left on the remote, adopted after this
|
|
404
|
+
* run's own push was rejected, opens a PR whose content nothing here checked,
|
|
405
|
+
* under a body describing work it does not contain.
|
|
406
|
+
*/
|
|
407
|
+
export declare function assertHeadPushedThisRun(ctx: GuardrailContext, branch: string, headSha: string): void;
|
|
397
408
|
/** record that a guarded-mode PR was opened (after create_pull_request succeeds). */
|
|
398
409
|
export declare function recordRemediationPrOpened(ctx: GuardrailContext): void;
|
|
399
410
|
/**
|
|
@@ -406,3 +417,30 @@ export declare function recordRemediationPrOpened(ctx: GuardrailContext): void;
|
|
|
406
417
|
* disables that check. Throws with an agent-actionable message, before creation.
|
|
407
418
|
*/
|
|
408
419
|
export declare function assertUnderOpenPrCaps(ctx: OpenPrCapContext): Promise<void>;
|
|
420
|
+
/** whether a mode names a fresh branch for every change, so that a remote
|
|
421
|
+
* branch of the same name which this run did not push is another change. */
|
|
422
|
+
export declare function isBranchPerChangeMode(mode: string | undefined): boolean;
|
|
423
|
+
/**
|
|
424
|
+
* Refuse to open a PR for a change to files an open PR of this engine already
|
|
425
|
+
* changes against the same base.
|
|
426
|
+
*
|
|
427
|
+
* Checked when the PR is opened, because that is where a duplicate comes into
|
|
428
|
+
* being and where the GitHub API is reachable — the push tools deliberately
|
|
429
|
+
* shell out without it. The pushed branch is left for the agent to delete.
|
|
430
|
+
*
|
|
431
|
+
* Every run of a branch-per-change mode names its own branch, so nothing else
|
|
432
|
+
* notices that a scheduled run is re-proposing what last week's run proposed
|
|
433
|
+
* and nobody has reviewed yet: each run opened another PR over the same code,
|
|
434
|
+
* duplicating the review and conflicting with the first on merge. Overlapping
|
|
435
|
+
* FILES is the test rather than an identical diff: two open PRs editing one
|
|
436
|
+
* file from one base conflict whatever they say, and a run that finds its
|
|
437
|
+
* files taken can still pick work elsewhere.
|
|
438
|
+
*
|
|
439
|
+
* Fails open on an API error — this guards review noise, not infrastructure,
|
|
440
|
+
* and a GitHub hiccup must not throw away a proven change.
|
|
441
|
+
*/
|
|
442
|
+
export declare function assertNotAlreadyProposed(ctx: OpenPrCapContext, opts: {
|
|
443
|
+
branch: string;
|
|
444
|
+
base: string;
|
|
445
|
+
changed: readonly string[];
|
|
446
|
+
}): Promise<void>;
|
|
@@ -13,7 +13,7 @@ import type { ResolvedPayload } from "../utils/config/payload.js";
|
|
|
13
13
|
* needs more (octokit, push, PR state) belongs on `ToolContext`, not here.
|
|
14
14
|
*/
|
|
15
15
|
export interface LocalToolContext {
|
|
16
|
-
payload: Pick<ResolvedPayload, "cwd" | "scanScope" | "iacLanguages" | "severityThreshold" | "grouping" | "autonomyThreshold" | "repeatedLiteralThreshold" | "costIncreaseBlockUsd" | "moduleCatalogue" | "allowReplace" | "allowMajorUpgrade" | "toolsEnabled" | "allowedPaths" | "toolsNetwork" | "gitleaks" | "terratest" | "docs" | "terraformMcp" | "moduleFetchToken" | "moduleFetchSshKey" | "registryToken" | "registryHost" | "opentofuMcp" | "fixMemory">;
|
|
16
|
+
payload: Pick<ResolvedPayload, "cwd" | "scanScope" | "iacLanguages" | "severityThreshold" | "grouping" | "autonomyThreshold" | "repeatedLiteralThreshold" | "varFiles" | "costIncreaseBlockUsd" | "moduleCatalogue" | "allowReplace" | "allowMajorUpgrade" | "toolsEnabled" | "allowedPaths" | "toolsNetwork" | "gitleaks" | "terratest" | "docs" | "terraformMcp" | "moduleFetchToken" | "moduleFetchSshKey" | "registryToken" | "registryHost" | "opentofuMcp" | "fixMemory">;
|
|
17
17
|
toolState: ToolState;
|
|
18
18
|
tmpdir: string;
|
|
19
19
|
/** resolved upstream model specifier driving this run's agent (e.g.
|
|
@@ -61,7 +61,12 @@ export interface ExtractionCluster {
|
|
|
61
61
|
export declare function clusterResources(file: string, resources: ParsedResource[]): ExtractionCluster[];
|
|
62
62
|
export interface CandidateModule {
|
|
63
63
|
name: string;
|
|
64
|
+
/** the module's identity: repo-relative (`./modules/s3`) for a house module. */
|
|
64
65
|
source: string;
|
|
66
|
+
/** a house module only: the `source` the cluster's root writes to call it —
|
|
67
|
+
* relative to that root, so `../../modules/s3` from `env/dev`. `source` is
|
|
68
|
+
* not a valid module source anywhere but the repo root. */
|
|
69
|
+
call_source?: string;
|
|
65
70
|
version: string | null;
|
|
66
71
|
kind: ModuleSourceKind;
|
|
67
72
|
/** how the match was made: a house module's actual resource types, or a
|