@intentius/chant 0.42.1 → 0.44.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/build.d.ts +15 -1
  2. package/dist/build.d.ts.map +1 -1
  3. package/dist/cli/commands/build.d.ts.map +1 -1
  4. package/dist/cli/commands/lint.d.ts +10 -0
  5. package/dist/cli/commands/lint.d.ts.map +1 -1
  6. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  7. package/dist/cli/plugins.d.ts +8 -0
  8. package/dist/cli/plugins.d.ts.map +1 -1
  9. package/dist/components/cli-support.d.ts +1 -1
  10. package/dist/components/cli-support.d.ts.map +1 -1
  11. package/dist/components/deploy-units.d.ts.map +1 -1
  12. package/dist/governance.d.ts +1 -1
  13. package/dist/graph-detail.d.ts +31 -13
  14. package/dist/graph-detail.d.ts.map +1 -1
  15. package/dist/lexicon.d.ts +44 -0
  16. package/dist/lexicon.d.ts.map +1 -1
  17. package/dist/lifecycle/change-set.d.ts +7 -0
  18. package/dist/lifecycle/change-set.d.ts.map +1 -1
  19. package/dist/lifecycle/live-diff.d.ts +18 -0
  20. package/dist/lifecycle/live-diff.d.ts.map +1 -1
  21. package/dist/lifecycle/observe.d.ts.map +1 -1
  22. package/dist/lint/component-checks.d.ts +2 -1
  23. package/dist/lint/component-checks.d.ts.map +1 -1
  24. package/dist/observation.d.ts +33 -3
  25. package/dist/observation.d.ts.map +1 -1
  26. package/dist/reconcile.d.ts +5 -5
  27. package/dist/reconcile.d.ts.map +1 -1
  28. package/dist/terraform/aws-resources.d.ts.map +1 -1
  29. package/dist/terraform/graph.d.ts.map +1 -1
  30. package/dist/terraform/tier-map.d.ts +4 -3
  31. package/dist/terraform/tier-map.d.ts.map +1 -1
  32. package/package.json +1 -1
  33. package/src/audit/rules-doc.ts +1 -1
  34. package/src/build.test.ts +95 -0
  35. package/src/build.ts +46 -1
  36. package/src/cli/commands/build.ts +9 -1
  37. package/src/cli/commands/lint.ts +14 -3
  38. package/src/cli/handlers/components.ts +1 -1
  39. package/src/cli/handlers/graph.ts +32 -7
  40. package/src/cli/handlers/lifecycle.ts +15 -4
  41. package/src/cli/plugins.ts +17 -0
  42. package/src/codegen/publish-order.test.ts +1 -1
  43. package/src/codegen/release-wiring.test.ts +3 -4
  44. package/src/components/cli-support.test.ts +52 -0
  45. package/src/components/cli-support.ts +13 -2
  46. package/src/components/deploy-units.test.ts +13 -0
  47. package/src/components/deploy-units.ts +5 -0
  48. package/src/governance.test.ts +1 -1
  49. package/src/governance.ts +1 -1
  50. package/src/graph-detail.test.ts +117 -6
  51. package/src/graph-detail.ts +76 -18
  52. package/src/lexicon.ts +47 -0
  53. package/src/lifecycle/change-set.test.ts +26 -0
  54. package/src/lifecycle/change-set.ts +13 -0
  55. package/src/lifecycle/live-diff.test.ts +35 -0
  56. package/src/lifecycle/live-diff.ts +21 -0
  57. package/src/lifecycle/observe.ts +2 -1
  58. package/src/lint/component-checks.ts +8 -1
  59. package/src/observation.test.ts +54 -7
  60. package/src/observation.ts +53 -13
  61. package/src/reconcile.ts +7 -7
  62. package/src/terraform/aws-resources.test.ts +52 -2
  63. package/src/terraform/aws-resources.ts +95 -4
  64. package/src/terraform/graph.test.ts +17 -0
  65. package/src/terraform/graph.ts +12 -2
  66. package/src/terraform/tier-map.ts +60 -8
@@ -7,7 +7,7 @@
7
7
  * the plan renderer, and the guardrail framework (rename resolution + a removal
8
8
  * cap + a pluggable check runner).
9
9
  *
10
- * A "warden" (e.g. github-warden) builds its provider-specific resource diffing,
10
+ * A consumer application (e.g. github-warden) builds its provider-specific resource diffing,
11
11
  * live-state types, and domain guardrails on top of this, and drives them with
12
12
  * the generic `runReconcile` loop + `Cycle` interface (below). It complements
13
13
  * chant's `ownership.ts` marker contract: ownership markers make a `delete`
@@ -33,7 +33,7 @@ export interface ChangeSetEntry {
33
33
  /** High-level resource category (e.g. "team", "member", "branch-protection"). */
34
34
  resourceType: string;
35
35
  /**
36
- * Cross-provider governance category (#790). `resourceType` stays the
36
+ * Cross-provider governance category. `resourceType` stays the
37
37
  * provider-specific display string; the verb is the shared grammar SCM and
38
38
  * cloud plans group by. Stamped by `runReconcile` from the cycle's `verb`.
39
39
  */
@@ -180,8 +180,8 @@ export interface Cycle<TClient, TConfig, TLive, TScope = unknown> {
180
180
  /** Human-readable name, e.g. "branch-protection". */
181
181
  name: string;
182
182
  /**
183
- * Cross-provider governance category this cycle reconciles (#790). Every
184
- * SCM warden cycle stamps one; cloud cycles (epic #787 C2) must. Optional
183
+ * Cross-provider governance category this cycle reconciles. Every
184
+ * SCM reconciler cycles stamp one; cloud cycles must. Optional
185
185
  * only so provider-external Cycle implementations don't break.
186
186
  */
187
187
  verb?: GovernanceVerb;
@@ -192,7 +192,7 @@ export interface Cycle<TClient, TConfig, TLive, TScope = unknown> {
192
192
  /** Per-cycle outcome recorded in the run result. */
193
193
  export interface CycleResult {
194
194
  name: string;
195
- /** The cycle's governance verb (#790), when it stamps one. */
195
+ /** The cycle's governance verb, when it stamps one. */
196
196
  verb?: GovernanceVerb;
197
197
  /** Scope id this result is for (e.g. an org login). */
198
198
  org: string;
@@ -1 +1 @@
1
- {"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../src/reconcile.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../src/reconcile.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"aws-resources.d.ts","sourceRoot":"","sources":["../../src/terraform/aws-resources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,+DAA+D;AAC/D,KAAK,SAAS,GAAG,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,CAAA;CAAE,CAAC;AAE/E,MAAM,WAAW,YAAY;IAC3B,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChB,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,yFAAyF;IACzF,IAAI,EAAE,MAAM,CAAC;IACb,gGAAgG;IAChG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAClC,kEAAkE;IAClE,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,eAAO,MAAM,kBAAkB,iCAAiC,CAAC;AAajE,eAAO,MAAM,eAAe,EAAE,YAAY,EA0HzC,CAAC;AAIF,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAErE;AASD;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B;IAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAoB1D"}
1
+ {"version":3,"file":"aws-resources.d.ts","sourceRoot":"","sources":["../../src/terraform/aws-resources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,+DAA+D;AAC/D,KAAK,SAAS,GAAG,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,CAAA;CAAE,CAAC;AAE/E,MAAM,WAAW,YAAY;IAC3B,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChB,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,yFAAyF;IACzF,IAAI,EAAE,MAAM,CAAC;IACb,gGAAgG;IAChG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAClC,kEAAkE;IAClE,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,eAAO,MAAM,kBAAkB,iCAAiC,CAAC;AAajE,eAAO,MAAM,eAAe,EAAE,YAAY,EAqNzC,CAAC;AAIF,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAErE;AASD;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B;IAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAoB1D"}
@@ -1 +1 @@
1
- {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/terraform/graph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAU,MAAM,SAAS,CAAC;AAErE;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;AAKpE,UAAU,MAAM;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AA4BD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAe/D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,EAAE,CAc/D;AAkDD;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAwEhF;AAED,6FAA6F;AAC7F,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAEtE;AAED,0FAA0F;AAC1F,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAEvE"}
1
+ {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/terraform/graph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAU,MAAM,SAAS,CAAC;AAErE;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;AAKpE,UAAU,MAAM;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AA4BD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAe/D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,EAAE,CAc/D;AA4DD;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAwEhF;AAED,6FAA6F;AAC7F,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAEtE;AAED,0FAA0F;AAC1F,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAEvE"}
@@ -29,9 +29,10 @@ export declare const TIER_MAP: Record<string, TierInfo>;
29
29
  */
30
30
  export declare const FOLDS_INTO: Record<string, string>;
31
31
  /**
32
- * The HCL attribute carrying a resource's physical name, per type — derived
33
- * from the AWS carve-out table. Used for the graph's identity and the
34
- * live-import hint. Absent fall back to the TF logical name.
32
+ * The HCL attribute carrying a resource's physical name, per type — AWS entries
33
+ * derived from the carve-out table, non-AWS listed directly. A dotted entry is
34
+ * a path into nested blocks (`manifest.metadata.name`). Used for the graph's
35
+ * identity and the live-import hint. Absent → fall back to the TF logical name.
35
36
  */
36
37
  export declare const IDENTITY_ATTR: Record<string, string>;
37
38
  export declare function resolveTier(tfType: string): TierInfo | null;
@@ -1 +1 @@
1
- {"version":3,"file":"tier-map.d.ts","sourceRoot":"","sources":["../../src/terraform/tier-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChB,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,sEAAsE;AACtE,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAK7C,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAO7C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAEhD,CAAC;AAEF,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAE3D"}
1
+ {"version":3,"file":"tier-map.d.ts","sourceRoot":"","sources":["../../src/terraform/tier-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChB,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;CAChB;AA0CD,sEAAsE;AACtE,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAK7C,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAiB7C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAGhD,CAAC;AAEF,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAE3D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant",
3
- "version": "0.42.1",
3
+ "version": "0.44.2",
4
4
  "description": "Declarative infrastructure-as-code toolkit — TypeScript on Node.js",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -14,7 +14,7 @@ const GROUPS: Array<{ heading: string; prefixes: string[]; blurb: string }> = [
14
14
  { heading: "GitHub Actions (GHA)", prefixes: ["GHA"], blurb: "Also applied to Forgejo workflows, which are GitHub-dialect." },
15
15
  { heading: "GitLab CI (WGL)", prefixes: ["WGL"], blurb: "" },
16
16
  { heading: "Forgejo (WFJ)", prefixes: ["WFJ"], blurb: "" },
17
- { heading: "Kubernetes (WK8 / ARGO)", prefixes: ["WK8", "ARGO"], blurb: "Run against Kubernetes manifests." },
17
+ { heading: "Kubernetes (WK8 / ARGO / FLUX)", prefixes: ["WK8", "ARGO", "FLUX"], blurb: "Run against Kubernetes manifests." },
18
18
  { heading: "Docker (DKRD)", prefixes: ["DKRD"], blurb: "Run against Dockerfiles and Compose files." },
19
19
  { heading: "AWS CloudFormation (WAW / COR / EXT)", prefixes: ["WAW", "COR", "EXT"], blurb: "Run against CloudFormation templates (JSON or YAML)." },
20
20
  { heading: "Azure ARM (AZR)", prefixes: ["AZR"], blurb: "Run against ARM deployment templates (JSON)." },
package/src/build.test.ts CHANGED
@@ -257,6 +257,101 @@ export const testEntity = {
257
257
  });
258
258
  });
259
259
 
260
+ describe("build-root contributors (#1548 piece 3)", () => {
261
+ let testDir: string;
262
+
263
+ beforeEach(async () => {
264
+ testDir = join(tmpdir(), `chant-buildroots-test-${Date.now()}-${Math.random()}`);
265
+ await mkdir(testDir, { recursive: true });
266
+ });
267
+
268
+ afterEach(async () => {
269
+ await rm(testDir, { recursive: true, force: true });
270
+ });
271
+
272
+ const entity = (entityType: string): Declarable =>
273
+ ({
274
+ lexicon: "test",
275
+ entityType,
276
+ kind: "resource",
277
+ props: {},
278
+ [DECLARABLE_MARKER]: true,
279
+ }) as unknown as Declarable;
280
+
281
+ const recordingSerializer = (seen: string[][]): Serializer => ({
282
+ name: "test",
283
+ rulePrefix: "TEST",
284
+ serialize: (entities) => {
285
+ seen.push([...entities.keys()]);
286
+ return "out";
287
+ },
288
+ });
289
+
290
+ test("contributed entities join the entity set and reach the serializer", async () => {
291
+ const seen: string[][] = [];
292
+ const result = await build(testDir, [recordingSerializer(seen)], undefined, {
293
+ buildRoots: [
294
+ async () => ({
295
+ entities: new Map([["overlays/prod/deploymentApp", entity("TestEntity")]]),
296
+ warnings: ["rendered a stray doc"],
297
+ }),
298
+ ],
299
+ });
300
+
301
+ expect(result.errors).toEqual([]);
302
+ expect(result.entities.has("overlays/prod/deploymentApp")).toBe(true);
303
+ expect(seen).toEqual([["overlays/prod/deploymentApp"]]);
304
+ expect(result.warnings).toContain("rendered a stray doc");
305
+ });
306
+
307
+ test("a contributor that throws becomes a build error carrying its message, not a thrown stack", async () => {
308
+ const result = await build(testDir, [], undefined, {
309
+ buildRoots: [
310
+ async () => {
311
+ throw new Error("neither the `kustomize` binary nor `kubectl` was found on PATH");
312
+ },
313
+ ],
314
+ });
315
+
316
+ expect(result.errors).toHaveLength(1);
317
+ expect(result.errors[0].message).toContain("neither the `kustomize` binary nor `kubectl`");
318
+ });
319
+
320
+ test("a contributed name colliding with a discovered entity is a build error, never an overwrite", async () => {
321
+ await writeFile(
322
+ join(testDir, "app.infra.ts"),
323
+ `
324
+ export const discovered = {
325
+ lexicon: "test",
326
+ entityType: "Discovered",
327
+ props: {},
328
+ [Symbol.for("chant.declarable")]: true,
329
+ };
330
+ `,
331
+ );
332
+
333
+ const result = await build(testDir, [recordingSerializer([])], undefined, {
334
+ buildRoots: [async () => ({ entities: new Map([["discovered", entity("Contributed")]]) })],
335
+ });
336
+
337
+ expect(result.errors.some((e) => e.message.includes('"discovered" collides'))).toBe(true);
338
+ expect(result.entities.get("discovered")?.entityType).toBe("Discovered");
339
+ });
340
+
341
+ test("contributors run exactly once per build", async () => {
342
+ let calls = 0;
343
+ await build(testDir, [], undefined, {
344
+ buildRoots: [
345
+ async () => {
346
+ calls++;
347
+ return { entities: new Map() };
348
+ },
349
+ ],
350
+ });
351
+ expect(calls).toBe(1);
352
+ });
353
+ });
354
+
260
355
  describe("partitionByLexicon", () => {
261
356
  test("partitions entities by lexicon", () => {
262
357
  const entities = new Map<string, Declarable>([
package/src/build.ts CHANGED
@@ -2,7 +2,7 @@ import type { Declarable } from "./declarable";
2
2
  import type { Serializer, SerializerResult } from "./serializer";
3
3
  import type { OwnershipMarker } from "./ownership";
4
4
  import type { BuildError, DiscoveryErrorType } from "./errors";
5
- import type { IntrinsicDef } from "./lexicon";
5
+ import type { IntrinsicDef, BuildRootContribution } from "./lexicon";
6
6
  import type { BuildParamProvenance } from "./provenance";
7
7
  import { DiscoveryError, BuildError as BuildErrorClass } from "./errors";
8
8
  import { LexiconOutput, isLexiconOutput } from "./lexicon-output";
@@ -215,6 +215,21 @@ export interface BuildOptions {
215
215
  * carries the already-resolved records for provenance.
216
216
  */
217
217
  buildParams?: BuildParamProvenance[];
218
+
219
+ /**
220
+ * chant #1548 piece 3 — lexicon-contributed build roots: closures the CLI
221
+ * binds from each configured plugin's `buildRoots(ctx)` hook (see
222
+ * `collectBuildRootContributors` in ./cli/plugins.ts), each rendering a
223
+ * non-chant-source root (a kustomize overlay dir) into entities. Run once,
224
+ * at the TOP-LEVEL build only (never repeated for nested child projects),
225
+ * after discovery and before partitioning — so contributed entities are
226
+ * serialized, ownership-stamped, post-synth-checked and observed exactly
227
+ * like discovered ones. A contributor that throws becomes a build error
228
+ * carrying its message (the k8s hook's missing-binary refusal names the
229
+ * binaries); a contributed name colliding with a discovered entity is a
230
+ * build error, never a silent overwrite.
231
+ */
232
+ buildRoots?: Array<() => Promise<BuildRootContribution>>;
218
233
  }
219
234
 
220
235
  export interface BuildResult {
@@ -657,6 +672,36 @@ async function buildFromDiscoveryResult(
657
672
  }
658
673
  }
659
674
 
675
+ // Step 4b (#1548 piece 3): lexicon-contributed build roots — rendered
676
+ // non-source roots (kustomize dirs) joining the entity set before
677
+ // partitioning, so everything downstream (serialization with ownership
678
+ // stamping, post-synth checks, observation) treats them as declared.
679
+ // Top-level build only: nested child builds receive the same options
680
+ // object, and re-running the contributors there would duplicate every
681
+ // contributed entity once per child.
682
+ if (!parentBuildStack && options?.buildRoots) {
683
+ for (const contribute of options.buildRoots) {
684
+ try {
685
+ const contribution = await contribute();
686
+ for (const w of contribution.warnings ?? []) warnings.push(w);
687
+ for (const [name, entity] of contribution.entities) {
688
+ if (discoveryResult.entities.has(name)) {
689
+ errors.push(
690
+ new BuildErrorClass(
691
+ "",
692
+ `Build-root entity "${name}" collides with a discovered entity of the same name`,
693
+ ),
694
+ );
695
+ continue;
696
+ }
697
+ discoveryResult.entities.set(name, entity);
698
+ }
699
+ } catch (error) {
700
+ errors.push(new BuildErrorClass("", error instanceof Error ? error.message : String(error)));
701
+ }
702
+ }
703
+ }
704
+
660
705
  // Step 5: Partition entities by lexicon
661
706
  const partitions = partitionByLexicon(discoveryResult.entities);
662
707
 
@@ -3,7 +3,7 @@ import { loadChantConfigUpward, resolveOwnershipMarker, resolveFoldEnabled, reso
3
3
  import { resolveCliBuildParams } from "../build-params-cli";
4
4
  import type { Serializer, SerializerResult } from "../../serializer";
5
5
  import type { LexiconPlugin } from "../../lexicon";
6
- import { resolveLexiconVersions } from "../plugins";
6
+ import { resolveLexiconVersions, collectBuildRootContributors } from "../plugins";
7
7
  import { runPostSynthChecks } from "../../lint/post-synth";
8
8
  import { applyConfiguredSeverity } from "../../lint/config";
9
9
  import { loadPolicyChecks } from "../../lint/policy";
@@ -261,6 +261,14 @@ export async function buildCommand(options: BuildOptions): Promise<BuildResult>
261
261
  lexicons,
262
262
  lexiconVersions,
263
263
  buildParams: paramsResolution.provenance,
264
+ // #1548 piece 3 — config-declared build roots (kustomize dirs), rendered
265
+ // into entities by the owning lexicon's hook. Rooted at the config dir,
266
+ // not the (possibly sourceDir-scoped) infra path.
267
+ buildRoots: collectBuildRootContributors(
268
+ options.plugins,
269
+ config as unknown as Record<string, unknown>,
270
+ configDir,
271
+ ),
264
272
  });
265
273
 
266
274
  // #1022 — report per-file fold vs run so it's visible what still runs.
@@ -1,4 +1,5 @@
1
1
  import { resolve, join, relative } from "path";
2
+ import type { BuildParamProvenance } from "../../provenance";
2
3
  import { readFileSync, writeFileSync, readdirSync, statSync } from "fs";
3
4
  import { execFileSync } from "child_process";
4
5
  import { runLint, parseDisableComments } from "../../lint/engine";
@@ -152,6 +153,15 @@ async function loadAllPluginRules(
152
153
  export interface LintOptions {
153
154
  /** Path to lint */
154
155
  path: string;
156
+ /**
157
+ * This invocation's resolved build parameters (#1490).
158
+ *
159
+ * The COMP* checks import `*.component.ts`, and an ES module evaluates once
160
+ * per path — so the values in effect during the lint gate are the values
161
+ * every later reader observes. A caller that lints before it graphs must
162
+ * pass the same parameters to both or the later resolution has no effect.
163
+ */
164
+ buildParams?: BuildParamProvenance[];
155
165
  /** Apply auto-fixes */
156
166
  fix?: boolean;
157
167
  /** Output format */
@@ -334,6 +344,7 @@ async function resolveRegistryContext(
334
344
  async function runComponentCheckDiagnostics(
335
345
  infraPath: string,
336
346
  sandbox?: boolean,
347
+ buildParams?: BuildParamProvenance[],
337
348
  ): Promise<{ diagnostics: LintDiagnostic[]; suppressed: Array<LintDiagnostic & { reason?: string }> }> {
338
349
  // Discovery stays scoped to the lint arg; COMP* severity overrides come from
339
350
  // the project-root config, same as the AST-rule pass.
@@ -342,7 +353,7 @@ async function runComponentCheckDiagnostics(
342
353
  const allCheckIds = new Set(checks.map((c) => c.id));
343
354
 
344
355
  const registryContext = await resolveRegistryContext(infraPath);
345
- const raw = await runComponentChecks(infraPath, checks, registryContext, sandbox);
356
+ const raw = await runComponentChecks(infraPath, checks, registryContext, sandbox, buildParams);
346
357
 
347
358
  const fileDirectivesCache = new Map<string, ReturnType<typeof parseDisableComments>>();
348
359
  const fileLevelDisable = (
@@ -483,7 +494,7 @@ export async function lintCommand(options: LintOptions): Promise<LintResult> {
483
494
  // structurally distinct check family (whole-project, post-discovery,
484
495
  // see ../../lint/component-checks.ts) but the same `chant lint` output and
485
496
  // the same error-severity gating as every COR/EVL diagnostic.
486
- const componentResult = await runComponentCheckDiagnostics(infraPath, options.sandbox);
497
+ const componentResult = await runComponentCheckDiagnostics(infraPath, options.sandbox, options.buildParams);
487
498
  diagnostics.push(...componentResult.diagnostics);
488
499
  suppressed.push(...componentResult.suppressed);
489
500
 
@@ -531,7 +542,7 @@ export async function lintCommand(options: LintOptions): Promise<LintResult> {
531
542
  // `*.component.ts` file on their behalf), but a fix applied to another
532
543
  // rule could still be in the same file a component was discovered from —
533
544
  // re-run for consistency with the AST re-lint above.
534
- const postComponentResult = await runComponentCheckDiagnostics(infraPath, options.sandbox);
545
+ const postComponentResult = await runComponentCheckDiagnostics(infraPath, options.sandbox, options.buildParams);
535
546
  diagnostics.push(...postComponentResult.diagnostics);
536
547
  suppressed.push(...postComponentResult.suppressed);
537
548
  }
@@ -430,7 +430,7 @@ export async function runComponentsStatus(ctx: CommandContext): Promise<number>
430
430
  // rather than "stale" (recorded, and nothing live).
431
431
  const message = err instanceof Error ? err.message : String(err);
432
432
  console.error(formatWarning({ message: `${plugin.name}: describeResources failed — ${message} (components in this lexicon report unknown, not stale)` }));
433
- observed = { resources: {}, unobserved: unobservedAll(declared, "read-failed", message, entities) };
433
+ observed = { resources: {}, unobserved: unobservedAll(declared, "read-failed", message, entities), queried: {} };
434
434
  }
435
435
  const cs = buildChangeSet(environment, {
436
436
  declared,
@@ -378,7 +378,11 @@ async function runGraphLive(
378
378
  */
379
379
  async function runComponentGraph(ctx: CommandContext): Promise<number> {
380
380
  const projectPath = resolve(ctx.args.path === "." ? "." : ctx.args.path);
381
- const graph = await computeComponentGraph(projectPath, ctx.args.sandbox);
381
+ // #1490 as in runComponentGraphView: the text/--json mode reads the same
382
+ // source and must not disagree with the --format modes about it.
383
+ const componentParams = await graphBuildParams(ctx, projectPath);
384
+ if (!componentParams) return 1;
385
+ const graph = await computeComponentGraph(projectPath, ctx.args.sandbox, componentParams);
382
386
 
383
387
  if (!graph.success) {
384
388
  console.error(formatError({ message: graph.error ?? "Failed to compute component graph" }));
@@ -434,7 +438,20 @@ async function runComponentGraphView(
434
438
  ): Promise<number> {
435
439
  const projectPath = resolve(ctx.args.path === "." ? "." : ctx.args.path);
436
440
 
437
- const lint = await lintCommand({ path: ctx.args.path, format: "stylish", sandbox: ctx.args.sandbox });
441
+ // #1490 resolved BEFORE the lint gate, not after. The COMP* checks import
442
+ // every `*.component.ts`, an ES module evaluates once per path, and the
443
+ // values in effect at that first import are the ones the graph below will
444
+ // read no matter what it resolves for itself. Ordering is the fix; passing
445
+ // them to `computeComponentGraph` alone did nothing.
446
+ const componentParams = await graphBuildParams(ctx, projectPath);
447
+ if (!componentParams) return 1;
448
+
449
+ const lint = await lintCommand({
450
+ path: ctx.args.path,
451
+ format: "stylish",
452
+ sandbox: ctx.args.sandbox,
453
+ buildParams: componentParams,
454
+ });
438
455
  if (!lint.success) {
439
456
  console.error(
440
457
  formatError({
@@ -444,7 +461,7 @@ async function runComponentGraphView(
444
461
  return 1;
445
462
  }
446
463
 
447
- const graph = await computeComponentGraph(projectPath, ctx.args.sandbox);
464
+ const graph = await computeComponentGraph(projectPath, ctx.args.sandbox, componentParams);
448
465
  if (!graph.success) {
449
466
  console.error(formatError({ message: graph.error ?? "Failed to compute component graph" }));
450
467
  return 1;
@@ -538,8 +555,19 @@ async function runGraphView(
538
555
  return 1;
539
556
  }
540
557
 
558
+ // Resolved before the gate for the reason in runComponentGraphView: the
559
+ // lint pass imports project source, and the first import of a module is the
560
+ // one that binds its parameter-derived values (#1490).
561
+ const buildParams = await graphBuildParams(ctx, projectPath);
562
+ if (!buildParams) return 1;
563
+
541
564
  // Gate: only emit for lint-clean source.
542
- const lint = await lintCommand({ path: ctx.args.path, format: "stylish", sandbox: ctx.args.sandbox });
565
+ const lint = await lintCommand({
566
+ path: ctx.args.path,
567
+ format: "stylish",
568
+ sandbox: ctx.args.sandbox,
569
+ buildParams,
570
+ });
543
571
  if (!lint.success) {
544
572
  console.error(
545
573
  formatError({
@@ -550,9 +578,6 @@ async function runGraphView(
550
578
  return 1;
551
579
  }
552
580
 
553
- const buildParams = await graphBuildParams(ctx, projectPath);
554
- if (!buildParams) return 1;
555
-
556
581
  const result = await discover(projectPath, { buildParams });
557
582
  if (result.errors.length > 0) {
558
583
  for (const e of result.errors) console.error(formatError({ message: e.message }));
@@ -31,6 +31,7 @@ import { cfnDeployStacks } from "./components";
31
31
  import { affectedStacks } from "../../lifecycle/affected";
32
32
  import { rollbackToRevision } from "../../lifecycle/rollback";
33
33
  import { loadChantConfig, environmentNames } from "../../config";
34
+ import { collectBuildRootContributors } from "../plugins";
34
35
  import { applyLiveEndpoint } from "../../live-endpoint";
35
36
  import { isResourceDeclarable } from "../../declarable";
36
37
  import { formatError, formatWarning, formatSuccess, formatBold } from "../format";
@@ -150,9 +151,10 @@ export async function runLifecycleSnapshot(ctx: CommandContext): Promise<number>
150
151
  // estate; a region whose stack declares no security group still has a default
151
152
  // one, and scoping the bound per stack silently drops it.
152
153
  const built: Array<{ target: (typeof targets)[number]; buildResult: Awaited<ReturnType<typeof build>> }> = [];
154
+ const buildRoots = collectBuildRootContributors(targetPlugins, config as unknown as Record<string, unknown>, projectPath);
153
155
  for (const target of targets) {
154
156
  const label = target.stack ? `stack "${target.stack}"` : "project";
155
- const buildResult = await build(target.root, targetSerializers, undefined, { buildParams: declaredParams });
157
+ const buildResult = await build(target.root, targetSerializers, undefined, { buildParams: declaredParams, buildRoots });
156
158
  if (buildResult.errors.length > 0) {
157
159
  console.error(formatError({ message: `Build failed for ${label} — fix errors before taking a snapshot` }));
158
160
  anyHardError = true;
@@ -363,9 +365,11 @@ export async function runLifecycleDiff(ctx: CommandContext): Promise<number> {
363
365
  const endpointResult = applyLiveEndpoint(config.environments, environment, liveLexicons);
364
366
  if (endpointResult.notice) console.error(formatWarning({ message: endpointResult.notice }));
365
367
 
368
+ const diffBuildRoots = collectBuildRootContributors(plugins, config as unknown as Record<string, unknown>, resolve("."));
369
+
366
370
  try {
367
371
  for (const target of targets) {
368
- const buildResult = await build(target.root, targetSerializers, undefined, { buildParams: declaredParams });
372
+ const buildResult = await build(target.root, targetSerializers, undefined, { buildParams: declaredParams, buildRoots: diffBuildRoots });
369
373
  if (buildResult.errors.length > 0) {
370
374
  const label = target.stack ? `stack "${target.stack}"` : "project";
371
375
  console.error(formatError({ message: `Build failed for ${label} — fix errors before diffing` }));
@@ -733,6 +737,7 @@ async function observeLexicon(
733
737
  return {
734
738
  resources: {},
735
739
  unobserved: unobservedAll(entityNames, "read-failed", message, opts.entities),
740
+ queried: {},
736
741
  };
737
742
  }
738
743
  }
@@ -802,7 +807,7 @@ async function runLifecycleDiffLive(args: LiveDiffArgs): Promise<LiveDiffOutcome
802
807
  });
803
808
  const observedNow = observed.resources;
804
809
  const observedThen = prevSnapshot?.resources;
805
- const diff = diffLive({ declared, observedNow, observedThen, unobserved: observed.unobserved });
810
+ const diff = diffLive({ declared, observedNow, observedThen, unobserved: observed.unobserved, queried: observed.queried });
806
811
  // Unobserved entities are deliberately NOT drift: a hole in the read is
807
812
  // not a change in the cloud. They are reported separately (#1089) so a
808
813
  // "no drift detected" line can never be built on top of a failed read.
@@ -958,7 +963,12 @@ function renderLiveDiff(lexiconName: string, environment: string, diff: LiveDiff
958
963
  }
959
964
  if (diff.missing.length > 0) {
960
965
  console.log(formatBold("\nMISSING (declared, provider reports not in cloud):"));
961
- for (const name of diff.missing) console.log(` - ${name}`);
966
+ for (const name of diff.missing) {
967
+ // The address the read actually went to (#1620) — the line between "not
968
+ // there" and "looked in the wrong place" (a defaulted namespace, say).
969
+ const queried = diff.queried?.[name];
970
+ console.log(` - ${name}${queried ? ` [queried ${queried}]` : ""}`);
971
+ }
962
972
  }
963
973
  if (diff.orphan.length > 0) {
964
974
  console.log(formatBold("\nORPHAN (in cloud, not declared):"));
@@ -1063,6 +1073,7 @@ export async function runLifecyclePlan(ctx: CommandContext): Promise<number> {
1063
1073
  if (!declaredParams) return 1;
1064
1074
  const buildResult = await build(resolveBuildRoot(args, config), targetSerializers, undefined, {
1065
1075
  buildParams: declaredParams,
1076
+ buildRoots: collectBuildRootContributors(plugins, config as unknown as Record<string, unknown>, resolve(".")),
1066
1077
  });
1067
1078
  if (buildResult.errors.length > 0) {
1068
1079
  console.error(formatError({ message: "Build failed — fix errors before planning" }));
@@ -95,6 +95,23 @@ export function resolveLexiconVersions(lexiconNames: readonly string[]): Record<
95
95
  return versions;
96
96
  }
97
97
 
98
+ /**
99
+ * Bind each loaded plugin's `buildRoots` hook (#1548 piece 3) to this
100
+ * invocation's config and project root, producing the closures
101
+ * `BuildOptions.buildRoots` takes — the same extract-then-thread shape the
102
+ * CLI uses for `intrinsics`. Plugins without the hook contribute nothing;
103
+ * an empty array is the common case and `build()` treats it as absent.
104
+ */
105
+ export function collectBuildRootContributors(
106
+ plugins: readonly LexiconPlugin[] | undefined,
107
+ config: Record<string, unknown>,
108
+ projectRoot: string,
109
+ ): Array<() => Promise<import("../lexicon").BuildRootContribution>> {
110
+ return (plugins ?? [])
111
+ .filter((plugin) => typeof plugin.buildRoots === "function")
112
+ .map((plugin) => () => plugin.buildRoots!({ projectRoot, config }));
113
+ }
114
+
98
115
  /**
99
116
  * Load plugins for all detected lexicon names.
100
117
  * Calls `init()` on each plugin if present.
@@ -48,7 +48,7 @@ describe("publish order", () => {
48
48
  // same stranding by a different route.
49
49
  const all = execFileSync(
50
50
  "bash",
51
- ["-c", 'for d in packages/*/ lexicons/*/ wardens/*/; do [ -f "$d/package.json" ] && echo "${d%/}"; done'],
51
+ ["-c", 'for d in packages/*/ lexicons/*/; do [ -f "$d/package.json" ] && echo "${d%/}"; done'],
52
52
  { cwd: REPO, encoding: "utf8" },
53
53
  )
54
54
  .split("\n")
@@ -118,10 +118,9 @@ function recipeBody(name: string): string {
118
118
  */
119
119
  describe("release wiring: peer ranges stay in lockstep (#1255)", () => {
120
120
  it("the whole-repo release rewrites every @intentius peer range", () => {
121
- // Since #788 the rewrite is generic (`with_entries` over every
122
- // `@intentius/*` peer) rather than two named keys: the wardens added
123
- // `@intentius/warden-core` as a third in-repo peer, and a hand-listed
124
- // rewrite goes stale the same way the hand-listed publish steps did.
121
+ // The rewrite is generic (`with_entries` over every `@intentius/*`
122
+ // peer) rather than named keys a hand-listed rewrite goes stale the
123
+ // same way the hand-listed publish steps did.
125
124
  const body = recipeBody("release");
126
125
  expect(body).toMatch(/\.peerDependencies\s*\|=\s*with_entries/);
127
126
  expect(body).toMatch(/startswith\("@intentius\/"\)/);
@@ -229,6 +229,58 @@ describe("computeComponentGraph", () => {
229
229
  expect(result.edges).toEqual([]);
230
230
  });
231
231
 
232
+ // #1490 — `discoverComponents` has honoured buildParams since #1108, but
233
+ // every caller stopped short of passing them, so the component graph was
234
+ // always the default-parameter graph. A component conditioned on a parameter
235
+ // survived `--param` that removed the resources it describes, and `chant
236
+ // build` and `chant graph --components` disagreed about the same source.
237
+ //
238
+ // The fixture imports the params module by absolute path rather than by the
239
+ // `@intentius/chant/params` specifier: it is written to a temp dir with no
240
+ // node_modules, so the bare specifier would resolve elsewhere (or nowhere)
241
+ // and the fixture would read a DIFFERENT params object than the one
242
+ // discovery mutates — passing the test for the wrong reason.
243
+ const paramsModule = new URL("../params.ts", import.meta.url).pathname;
244
+ const conditionalComponent = `import { params } from ${JSON.stringify(paramsModule)};
245
+ export const backup =
246
+ params.backups === "omit"
247
+ ? undefined
248
+ : { name: "backup", dependsOn: [], deploy: [{ phase: "Apply", steps: [{ kind: "shell", reason: "t" }] }] };`;
249
+
250
+ // A fresh directory per call: the ESM loader caches a module by path, so
251
+ // re-importing the same fixture would replay the FIRST call's parameter
252
+ // binding and the assertion would pass or fail for the wrong reason.
253
+ async function graphWith(
254
+ dir: string,
255
+ provenance?: Array<{ name: string; value: string; source: "cli" }>,
256
+ ) {
257
+ await mkdir(dir, { recursive: true });
258
+ await writeFile(join(dir, "backup.component.ts"), conditionalComponent);
259
+ return computeComponentGraph(dir, undefined, provenance);
260
+ }
261
+
262
+ test("a component conditioned on a build parameter follows the parameter", async () => {
263
+ const omitted = await graphWith(join(testDir, "omit"), [
264
+ { name: "backups", value: "omit", source: "cli" },
265
+ ]);
266
+ expect(omitted.success).toBe(true);
267
+ expect(omitted.order).toEqual([]);
268
+
269
+ const kept = await graphWith(join(testDir, "keep"), [
270
+ { name: "backups", value: "pg-dump", source: "cli" },
271
+ ]);
272
+ expect(kept.success).toBe(true);
273
+ expect(kept.order).toEqual(["backup"]);
274
+ });
275
+
276
+ // Supplying none must not inherit whatever a previous call set — the same
277
+ // leak `discoverComponents`'s unconditional setBuildParams guards against.
278
+ test("passing no parameters does not inherit the previous call's", async () => {
279
+ await graphWith(join(testDir, "first"), [{ name: "backups", value: "omit", source: "cli" }]);
280
+ const fresh = await graphWith(join(testDir, "second"));
281
+ expect(fresh.order).toEqual(["backup"]);
282
+ });
283
+
232
284
  test("orders a consumer after its producer, with a consumer → producer edge", async () => {
233
285
  await writeFile(
234
286
  join(testDir, "alb.component.ts"),