@intentius/chant-lexicon-terraform 0.61.0 → 0.62.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.
@@ -13,8 +13,18 @@
13
13
  * the two values that would claim it. Cloud-to-code here is "claim what the
14
14
  * configuration already describes" — a tag write, not a regeneration.
15
15
  *
16
- * Phases: Check, Ledger, Gate, Adopt.
16
+ * Phases: Init, Check, Ledger, Gate, Adopt.
17
17
  *
18
+ * - **Init** runs `terraform init`, the same step `TerraformApplyOp` and
19
+ * `TerraformWatchOp` open with. The Ledger step's `live-plan` needs the
20
+ * provider schema to discover markers on the live resources it reads, and
21
+ * a checkout that has never run `init` has no `.terraform` directory to
22
+ * read one from — which is every fresh CI checkout, not a GitLab
23
+ * peculiarity (#2302). Without this phase `live-plan` fails outright:
24
+ * `Error: Provider unavailable for marker discovery`. This composite had
25
+ * no Init phase because nothing had ever run `live-adopt` before
26
+ * INTENTIUS/choudoufu#1026 did, so the gap survived every guard that only
27
+ * read the generated YAML.
18
28
  * - **Check** runs `live-check`, which makes no cloud calls and says whether
19
29
  * this configuration can move under markers at all. It runs first because
20
30
  * a root that `live-check` refuses will produce an adoption ledger that
@@ -103,6 +113,12 @@ export interface TerraformAdoptOpConfig {
103
113
  gateTimeout?: string;
104
114
  /** Override the gate description shown to the approver. */
105
115
  gateDescription?: string;
116
+ /**
117
+ * `-upgrade` on the Init step: re-resolve provider and module versions.
118
+ * Same field, same meaning, as `TerraformApplyOpConfig.upgrade` and
119
+ * `TerraformWatchOpConfig.upgrade`.
120
+ */
121
+ upgrade?: boolean;
106
122
  /**
107
123
  * Directory each step starts the `chant.config.*` search from, which is what
108
124
  * `terraform.roots` and the root's relative `dir` resolve against. Default:
@@ -124,7 +140,7 @@ export interface TerraformAdoptOpConfig {
124
140
  };
125
141
  }
126
142
  export interface TerraformAdoptOpResources {
127
- /** Op resource. Generates the Check/Ledger/Gate/Adopt workflow. */
143
+ /** Op resource. Generates the Init/Check/Ledger/Gate/Adopt workflow. */
128
144
  op: InstanceType<typeof OpResource>;
129
145
  }
130
146
  export declare function TerraformAdoptOp(config: TerraformAdoptOpConfig): TerraformAdoptOpResources;
@@ -1 +1 @@
1
- {"version":3,"file":"terraform-adopt-op.d.ts","sourceRoot":"","sources":["../../src/composites/terraform-adopt-op.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiFG;AAEH,OAAO,EAA6B,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAO5E,MAAM,WAAW,sBAAsB;IACrC,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,wGAAwG;IACxG,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qGAAqG;IACrG,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,OAAO,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C;AAED,MAAM,WAAW,yBAAyB;IACxC,mEAAmE;IACnE,EAAE,EAAE,YAAY,CAAC,OAAO,UAAU,CAAC,CAAC;CACrC;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,yBAAyB,CAiE1F"}
1
+ {"version":3,"file":"terraform-adopt-op.d.ts","sourceRoot":"","sources":["../../src/composites/terraform-adopt-op.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2FG;AAEH,OAAO,EAA6B,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAQ5E,MAAM,WAAW,sBAAsB;IACrC,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,wGAAwG;IACxG,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qGAAqG;IACrG,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,OAAO,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C;AAED,MAAM,WAAW,yBAAyB;IACxC,wEAAwE;IACxE,EAAE,EAAE,YAAY,CAAC,OAAO,UAAU,CAAC,CAAC;CACrC;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,yBAAyB,CAkE1F"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "da8f1f7391554299a9dac6411b3d346aac233088d22d080a5c31feb56754ca19",
4
+ "manifest.json": "68a63fa7e36966979057c179df4393ea7af5ebaa3d4c4126b972fdb260a8c3d4",
5
5
  "meta.json": "ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356",
6
6
  "types/index.d.ts": "022927e6a7b351cc4cf52d5f973f60ccd140e5810e17b32fb8868965aa1db161",
7
7
  "rules/plan-before-apply.ts": "31b1b3176799fd365b2583fe3947edd187c2a863e3dc0094ed9e91ece2464c99",
@@ -37,5 +37,5 @@
37
37
  "rules/tf028.ts": "81ef95659c40ad1774c83e663473ed769143d894a4ba0517c3defa31f00ba723",
38
38
  "skills/chant-terraform.md": "d9ca7b7c28aed0038ce775a3491150298492eed2858fd3651ed9bfca0e577ae8"
39
39
  },
40
- "composite": "14b513f2e660229ea7f075738074c2cb08fe27e2e42a6ae1fd7e7cd708233ca1"
40
+ "composite": "bce1d6bc7353146056d897096ba518203b0b588cb4b4546b420e6d93169b77c4"
41
41
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terraform",
3
- "version": "0.61.0",
3
+ "version": "0.62.0",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "Terraform"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-terraform",
3
- "version": "0.61.0",
3
+ "version": "0.62.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src/",
@@ -56,7 +56,7 @@
56
56
  },
57
57
  "peerDependencies": {
58
58
  "zod": "^4.3.6",
59
- "@intentius/chant": "^0.61.0",
59
+ "@intentius/chant": "^0.62.0",
60
60
  "typescript": "^5.9.3"
61
61
  },
62
62
  "description": "terraform lexicon for chant, existing Terraform root modules as declarable entities",
@@ -1,6 +1,6 @@
1
1
  # What the gated acceptance suites have actually run against
2
2
 
3
- Four `describe` blocks across three files run this lexicon against a real
3
+ Five `describe` blocks across three files run this lexicon against a real
4
4
  binary rather than a fixture, and every one of them skips rather than fails
5
5
  when its dependency is absent. A skip is silent by design, so without this
6
6
  file nothing in the tree says which of them has ever passed, against what, or
@@ -170,6 +170,47 @@ The fixture was deliberately left as an `aws_vpc`. A log group would make the
170
170
  block pass off `unowned[]` alone and would stop it proving the content-matcher
171
171
  path, which is the only thing it exists to prove.
172
172
 
173
+ ### 5. `TerraformAdoptOp reaches its gate on a checkout that has never run init`
174
+
175
+ Same file, third block (#2302). One test: build `TerraformAdoptOp` and drive
176
+ it through `runOpLocally`, exactly as the apply Op's own acceptance suite
177
+ does, over a project this test creates fresh and never initializes by hand —
178
+ no `terraformInit` call anywhere in the block. The Op's own Init phase is what
179
+ has to download the provider, or the Ledger step fails the way it failed in
180
+ INTENTIUS/choudoufu#1026. `TerraformAdoptOp` gates unconditionally, so a
181
+ passing run ends `status: "gated"` right after the Ledger phase — reaching the
182
+ gate is the pass, not a completed adoption.
183
+
184
+ Gated on a `choudoufu` on PATH and `CHOUDOUFU_EMULATOR_ENDPOINT`. No `aws` CLI:
185
+ this block creates no live resource of its own.
186
+
187
+ ```
188
+ npx vitest run lexicons/terraform/src/composites/terraform-adopt-op.acceptance.test.ts
189
+ ```
190
+
191
+ Last passed: 2026-09-09, choudoufu v0.15.0 with the pinned floci emulator up.
192
+ Removing the Init phase this block exists to guard reproduces
193
+ INTENTIUS/choudoufu#1026 verbatim:
194
+
195
+ ```
196
+ choudoufu live-plan failed in <dir> (exit 1)
197
+ Error: Provider unavailable for marker discovery
198
+
199
+ Finding the live resources of this estate needs provider
200
+ provider["registry.opentofu.org/hashicorp/aws"], which could not be used:
201
+ cannot read the schema of provider registry.opentofu.org/hashicorp/aws:
202
+ failed to instantiate provider "registry.opentofu.org/hashicorp/aws" to
203
+ obtain schema: unavailable provider "registry.opentofu.org/hashicorp/aws".
204
+ ```
205
+
206
+ With the Init phase back, the same run reaches its gate:
207
+
208
+ ```
209
+ ✓ TerraformAdoptOp reaches its gate on a checkout that has never run init >
210
+ Init downloads the provider the Ledger step needs, and the run reaches
211
+ its gate
212
+ ```
213
+
173
214
  ## Running everything that can run on a machine with no choudoufu
174
215
 
175
216
  ```
@@ -179,15 +220,15 @@ npx vitest run \
179
220
  lexicons/terraform/src/op/activities/choudoufu.acceptance.test.ts
180
221
  ```
181
222
 
182
- With `terraform` on PATH and the registry reachable, that is 1 passed and 5
223
+ With `terraform` on PATH and the registry reachable, that is 1 passed and 6
183
224
  skipped, and each skip names its own reason in the block title. With
184
- choudoufu v0.15.0, the `aws` CLI and the emulator all present, it is 6 passed
185
- and 0 skipped: no block in this file skips any more. On 2026-09-08 that is
186
- exactly what it printed.
225
+ choudoufu v0.15.0 and the emulator present (the `aws` CLI besides, for block
226
+ 4), it is 7 passed and 0 skipped: no block in this file skips any more. On
227
+ 2026-09-09 that is exactly what it printed.
187
228
 
188
229
  ```
189
230
  Test Files 3 passed (3)
190
- Tests 6 passed (6)
231
+ Tests 7 passed (7)
191
232
  ```
192
233
 
193
234
  Bring the emulator up on a fixed port from choudoufu's own compose file rather
@@ -69,6 +69,8 @@ import { cpSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
69
69
  import { tmpdir } from "node:os";
70
70
  import { join } from "node:path";
71
71
  import { afterAll, describe, expect, it } from "vitest";
72
+ import { loadActivities, loadProfiles, runOpLocally, type OpConfig } from "@intentius/chant/op";
73
+ import { TerraformAdoptOp } from "./terraform-adopt-op";
72
74
  import { choudoufuAdopt, choudoufuLivePlan, terraformInit } from "../op/activities/terraform";
73
75
 
74
76
  function onPath(cmd: string): boolean {
@@ -177,3 +179,66 @@ describe.skipIf(skipReason !== "")(
177
179
  );
178
180
  },
179
181
  );
182
+
183
+ /**
184
+ * #2302: `TerraformAdoptOp` built with no Init phase reached its Ledger step
185
+ * with an empty `.terraform` on every checkout that had never run `init` —
186
+ * every fresh CI checkout, not a GitLab peculiarity. This drives the built Op
187
+ * itself through `runOpLocally`, the way `terraform-apply-op.acceptance.test.ts`
188
+ * does, over a project this test creates fresh and never initializes by hand:
189
+ * no `terraformInit` call anywhere in this block, unlike the suite above. The
190
+ * Op's own Init phase is what has to do that work, or the Ledger step fails
191
+ * the same way it failed in INTENTIUS/choudoufu#1026.
192
+ *
193
+ * `TerraformAdoptOp` gates unconditionally (no `gate: "never"`, unlike
194
+ * `TerraformApplyOp`), so a passing run here ends with `status: "gated"`
195
+ * right after the Ledger phase — "reaching its gate" is the pass, not a
196
+ * completed adoption. That is proof enough: the Ledger step needs the
197
+ * provider schema, and a run that reaches the gate is a run whose Ledger step
198
+ * already read it.
199
+ *
200
+ * Needs only a `choudoufu` on PATH and the emulator, not the `aws` CLI: this
201
+ * block creates no live resource and never reads `ledger.adoptions`, so a
202
+ * narrower gate than the suite above is correct instead of borrowed.
203
+ */
204
+ const gateSkipReason = !onPath("choudoufu")
205
+ ? "no choudoufu binary on PATH"
206
+ : !emulatorEndpoint
207
+ ? "CHOUDOUFU_EMULATOR_ENDPOINT is not set (bring up choudoufu's `just smoke` emulator stack and export it)"
208
+ : "";
209
+
210
+ describe.skipIf(gateSkipReason !== "")(
211
+ `TerraformAdoptOp reaches its gate on a checkout that has never run init${gateSkipReason ? ` (skipped: ${gateSkipReason})` : ""}`,
212
+ () => {
213
+ it(
214
+ "Init downloads the provider the Ledger step needs, and the run reaches its gate",
215
+ { timeout: 300_000 },
216
+ async () => {
217
+ const dir = project();
218
+
219
+ process.env.AWS_ENDPOINT_URL ??= emulatorEndpoint;
220
+ process.env.AWS_ACCESS_KEY_ID ??= "choudoufu-emulator";
221
+ process.env.AWS_SECRET_ACCESS_KEY ??= "choudoufu-emulator";
222
+ process.env.AWS_DEFAULT_REGION ??= "us-east-1";
223
+
224
+ // No `terraformInit` call here. `dir/root` is a fresh checkout: no
225
+ // `.terraform`, only the fixture's own `main.tf` and
226
+ // `estate.chdf.hcl` — exactly what `git clone` leaves behind, since
227
+ // `.terraform` is what every `.gitignore` in this ecosystem excludes.
228
+ const { op } = TerraformAdoptOp({ name: "gate-check-adopt", root: "estate", cwd: dir });
229
+
230
+ const activities = await loadActivities(["terraform"]);
231
+ const result = await runOpLocally((op as unknown as { props: OpConfig }).props, activities, await loadProfiles());
232
+
233
+ expect(result.status).toBe("gated");
234
+ expect(result.records.map((r) => `${r.phase}:${r.status}`)).toEqual([
235
+ "Init:ok",
236
+ "Check:ok",
237
+ "Ledger:ok",
238
+ "Gate:skipped",
239
+ "Adopt:skipped",
240
+ ]);
241
+ },
242
+ );
243
+ },
244
+ );
@@ -26,15 +26,37 @@ const isActivity = (s: StepDefinition): s is ActivityStep => s.kind === "activit
26
26
  const steps = (op: OpConfig): ActivityStep[] => op.phases.flatMap((p) => p.steps.filter(isActivity));
27
27
  const base = { name: "estate-adopt", root: "estate" };
28
28
 
29
- describe("TerraformAdoptOp phases (#2105)", () => {
30
- test("Check, Ledger, Gate, Adopt, in that order", () => {
29
+ describe("TerraformAdoptOp phases (#2105, #2302)", () => {
30
+ test("Init, Check, Ledger, Gate, Adopt, in that order", () => {
31
31
  const op = props(base);
32
- expect(phaseNames(op)).toEqual(["Check", "Ledger", "Gate", "Adopt"]);
33
- expect(steps(op).map((s) => s.fn)).toEqual(["choudoufuLiveCheck", "choudoufuLivePlan", "choudoufuAdopt"]);
32
+ expect(phaseNames(op)).toEqual(["Init", "Check", "Ledger", "Gate", "Adopt"]);
33
+ expect(steps(op).map((s) => s.fn)).toEqual([
34
+ "terraformInit",
35
+ "choudoufuLiveCheck",
36
+ "choudoufuLivePlan",
37
+ "choudoufuAdopt",
38
+ ]);
39
+ });
40
+
41
+ // #2302: the Ledger step's `live-plan` needs the provider schema to
42
+ // discover markers, which a checkout that has never run `init` does not
43
+ // have — every fresh CI checkout, not a GitLab peculiarity. Without this
44
+ // phase `live-plan` fails outright: "Error: Provider unavailable for
45
+ // marker discovery".
46
+ test("Init runs terraform init first, the same shape TerraformApplyOp and TerraformWatchOp use", () => {
47
+ const init = props(base).phases[0].steps[0] as ActivityStep;
48
+ expect(init.fn).toBe("terraformInit");
49
+ expect(init.args?.root).toBe("estate");
50
+ expect(init.args && "upgrade" in init.args).toBe(false);
51
+ });
52
+
53
+ test("upgrade rides the Init step only, the same option name as the other two Ops", () => {
54
+ const init = props({ ...base, upgrade: true }).phases[0].steps[0] as ActivityStep;
55
+ expect(init.args?.upgrade).toBe(true);
34
56
  });
35
57
 
36
- test("Check runs live-check first, and reports whether the root was refused", () => {
37
- const check = props(base).phases[0].steps[0] as ActivityStep;
58
+ test("Check runs live-check second, and reports whether the root was refused", () => {
59
+ const check = props(base).phases[1].steps[0] as ActivityStep;
38
60
  expect(check.fn).toBe("choudoufuLiveCheck");
39
61
  expect(check.outcomeAttribute).toEqual({ name: "Refused", from: "refused" });
40
62
  // No cloud calls, so this is the cheapest place to learn the root cannot
@@ -43,7 +65,7 @@ describe("TerraformAdoptOp phases (#2105)", () => {
43
65
  });
44
66
 
45
67
  test("Ledger is the -adoption-only live plan, and publishes both counts", () => {
46
- const ledger = props(base).phases[1].steps[0] as ActivityStep;
68
+ const ledger = props(base).phases[2].steps[0] as ActivityStep;
47
69
  expect(ledger.fn).toBe("choudoufuLivePlan");
48
70
  expect(ledger.id).toBe("ledger");
49
71
  expect(ledger.args?.adoptionOnly).toBe(true);
@@ -64,14 +86,14 @@ describe("TerraformAdoptOp phases (#2105)", () => {
64
86
 
65
87
  test("an explicit estate rides the Ledger step only", () => {
66
88
  const op = props({ ...base, estate: "prod-networking" });
67
- expect((op.phases[1].steps[0] as ActivityStep).args?.estate).toBe("prod-networking");
68
- expect((op.phases[0].steps[0] as ActivityStep).args?.estate).toBeUndefined();
89
+ expect((op.phases[2].steps[0] as ActivityStep).args?.estate).toBe("prod-networking");
90
+ expect((op.phases[1].steps[0] as ActivityStep).args?.estate).toBeUndefined();
69
91
  });
70
92
  });
71
93
 
72
94
  describe("TerraformAdoptOp gates on the ledger (#2105)", () => {
73
95
  const gateOf = (op: OpConfig): GateStep => {
74
- const step = op.phases[2].steps[0];
96
+ const step = op.phases[3].steps[0];
75
97
  expect(step.kind).toBe("gate");
76
98
  return step as GateStep;
77
99
  };
@@ -107,7 +129,7 @@ describe("TerraformAdoptOp gates on the ledger (#2105)", () => {
107
129
  });
108
130
 
109
131
  describe("TerraformAdoptOp adopts the ledger's matches and nothing else (#2105)", () => {
110
- const adoptStep = (op: OpConfig): ActivityStep => op.phases[3].steps[0] as ActivityStep;
132
+ const adoptStep = (op: OpConfig): ActivityStep => op.phases[4].steps[0] as ActivityStep;
111
133
 
112
134
  test("the Adopt step takes the ledger's adoptable set by reference", () => {
113
135
  const adopt = adoptStep(props(base));
@@ -13,8 +13,18 @@
13
13
  * the two values that would claim it. Cloud-to-code here is "claim what the
14
14
  * configuration already describes" — a tag write, not a regeneration.
15
15
  *
16
- * Phases: Check, Ledger, Gate, Adopt.
16
+ * Phases: Init, Check, Ledger, Gate, Adopt.
17
17
  *
18
+ * - **Init** runs `terraform init`, the same step `TerraformApplyOp` and
19
+ * `TerraformWatchOp` open with. The Ledger step's `live-plan` needs the
20
+ * provider schema to discover markers on the live resources it reads, and
21
+ * a checkout that has never run `init` has no `.terraform` directory to
22
+ * read one from — which is every fresh CI checkout, not a GitLab
23
+ * peculiarity (#2302). Without this phase `live-plan` fails outright:
24
+ * `Error: Provider unavailable for marker discovery`. This composite had
25
+ * no Init phase because nothing had ever run `live-adopt` before
26
+ * INTENTIUS/choudoufu#1026 did, so the gap survived every guard that only
27
+ * read the generated YAML.
18
28
  * - **Check** runs `live-check`, which makes no cloud calls and says whether
19
29
  * this configuration can move under markers at all. It runs first because
20
30
  * a root that `live-check` refuses will produce an adoption ledger that
@@ -83,6 +93,7 @@
83
93
 
84
94
  import { Op, phase, gate, activity, OpResource } from "@intentius/chant/op";
85
95
  import {
96
+ terraformInit as initStep,
86
97
  choudoufuLiveCheck as checkStep,
87
98
  choudoufuLivePlan as ledgerStep,
88
99
  choudoufuAdopt as adoptStep,
@@ -110,6 +121,12 @@ export interface TerraformAdoptOpConfig {
110
121
  gateTimeout?: string;
111
122
  /** Override the gate description shown to the approver. */
112
123
  gateDescription?: string;
124
+ /**
125
+ * `-upgrade` on the Init step: re-resolve provider and module versions.
126
+ * Same field, same meaning, as `TerraformApplyOpConfig.upgrade` and
127
+ * `TerraformWatchOpConfig.upgrade`.
128
+ */
129
+ upgrade?: boolean;
113
130
  /**
114
131
  * Directory each step starts the `chant.config.*` search from, which is what
115
132
  * `terraform.roots` and the root's relative `dir` resolve against. Default:
@@ -130,7 +147,7 @@ export interface TerraformAdoptOpConfig {
130
147
  }
131
148
 
132
149
  export interface TerraformAdoptOpResources {
133
- /** Op resource. Generates the Check/Ledger/Gate/Adopt workflow. */
150
+ /** Op resource. Generates the Init/Check/Ledger/Gate/Adopt workflow. */
134
151
  op: InstanceType<typeof OpResource>;
135
152
  }
136
153
 
@@ -178,6 +195,7 @@ export function TerraformAdoptOp(config: TerraformAdoptOpConfig): TerraformAdopt
178
195
  TerraformMode: "live",
179
196
  },
180
197
  phases: [
198
+ phase("Init", [initStep(config.root, { ...where, ...(config.upgrade ? { upgrade: true } : {}) })]),
181
199
  phase("Check", [check]),
182
200
  phase("Ledger", [ledger]),
183
201
  phase("Gate", [