@intentius/chant 0.27.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/cli/commands/build.d.ts.map +1 -1
  2. package/dist/cli/commands/lint.d.ts.map +1 -1
  3. package/dist/discovery/sandbox/config-run.d.ts.map +1 -1
  4. package/dist/discovery/sandbox/fork.d.ts +25 -0
  5. package/dist/discovery/sandbox/fork.d.ts.map +1 -1
  6. package/dist/discovery/sandbox/policy-run.d.ts.map +1 -1
  7. package/dist/discovery/sandbox/run.d.ts.map +1 -1
  8. package/dist/kubectl-context.d.ts +73 -0
  9. package/dist/kubectl-context.d.ts.map +1 -0
  10. package/dist/lint/config.d.ts +80 -0
  11. package/dist/lint/config.d.ts.map +1 -1
  12. package/dist/lint/policy.d.ts +8 -2
  13. package/dist/lint/policy.d.ts.map +1 -1
  14. package/dist/lint/post-synth.d.ts +18 -1
  15. package/dist/lint/post-synth.d.ts.map +1 -1
  16. package/dist/stack-output.d.ts +9 -4
  17. package/dist/stack-output.d.ts.map +1 -1
  18. package/package.json +1 -1
  19. package/src/cli/commands/build.test.ts +190 -0
  20. package/src/cli/commands/build.ts +34 -2
  21. package/src/cli/commands/lint.ts +17 -25
  22. package/src/discovery/sandbox/config-boundary.test.ts +55 -1
  23. package/src/discovery/sandbox/config-run.ts +3 -0
  24. package/src/discovery/sandbox/fork.ts +75 -1
  25. package/src/discovery/sandbox/policy-boundary.test.ts +56 -1
  26. package/src/discovery/sandbox/policy-run.ts +15 -1
  27. package/src/discovery/sandbox/run.test.ts +85 -1
  28. package/src/discovery/sandbox/run.ts +3 -0
  29. package/src/kubectl-context.test.ts +94 -0
  30. package/src/kubectl-context.ts +126 -0
  31. package/src/lint/config.test.ts +93 -1
  32. package/src/lint/config.ts +108 -0
  33. package/src/lint/policy.test.ts +90 -0
  34. package/src/lint/policy.ts +17 -5
  35. package/src/lint/post-synth.test.ts +4 -0
  36. package/src/lint/post-synth.ts +30 -1
  37. package/src/stack-output.test.ts +21 -3
  38. package/src/stack-output.ts +21 -9
@@ -1 +1 @@
1
- {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/build.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAoB,MAAM,kBAAkB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAUnD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oBAAoB;IACpB,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,2CAA2C;IAC3C,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,8CAA8C;IAC9C,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAC1B,8BAA8B;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEhC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,EAAE,MAAM,GAAG,SAAS,GACzB;IAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAoB/C;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,kCAAkC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,gCAAgC;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,uBAAuB;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,2JAA2J;IAC3J,WAAW,CAAC,EAAE,OAAO,kBAAkB,EAAE,oBAAoB,EAAE,CAAC;CACjE;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA+Y9E;AAiDD;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAIlD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAItD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,YAAY,EACrB,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,GACxC,MAAM,IAAI,CAmCZ"}
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/build.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAoB,MAAM,kBAAkB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAWnD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oBAAoB;IACpB,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,2CAA2C;IAC3C,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,8CAA8C;IAC9C,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAC1B,8BAA8B;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEhC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,EAAE,MAAM,GAAG,SAAS,GACzB;IAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAoB/C;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,kCAAkC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,gCAAgC;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,uBAAuB;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,2JAA2J;IAC3J,WAAW,CAAC,EAAE,OAAO,kBAAkB,EAAE,oBAAoB,EAAE,CAAC;CACjE;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA8a9E;AAiDD;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAIlD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAItD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,YAAY,EACrB,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,GACxC,MAAM,IAAI,CAmCZ"}
@@ -1 +1 @@
1
- {"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/lint.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAW,MAAM,iBAAiB,CAAC;AAmBzE;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAW5D;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EAAE,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAmBhC;AAgED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,oBAAoB;IACpB,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;IACrC,qCAAqC;IACrC,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IACnB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,sCAAsC;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,uBAAuB;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,yBAAyB;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,sBAAsB;IACtB,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AA0PD;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAoJ3E;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAIxD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,GACtC,MAAM,IAAI,CAiCZ"}
1
+ {"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/lint.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAW,MAAM,iBAAiB,CAAC;AAkBzE;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAW5D;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EAAE,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAmBhC;AAgED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,oBAAoB;IACpB,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;IACrC,qCAAqC;IACrC,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IACnB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,sCAAsC;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,uBAAuB;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,yBAAyB;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,sBAAsB;IACtB,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAmPD;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAoJ3E;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAIxD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,GACtC,MAAM,IAAI,CAiCZ"}
@@ -1 +1 @@
1
- {"version":3,"file":"config-run.d.ts","sourceRoot":"","sources":["../../../src/discovery/sandbox/config-run.ts"],"names":[],"mappings":"AA0DA,MAAM,WAAW,mBAAmB;IAClC,kOAAkO;IAClO,MAAM,EAAE,OAAO,CAAC;IAChB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,mBAAmB,CAAC,CA6C9B"}
1
+ {"version":3,"file":"config-run.d.ts","sourceRoot":"","sources":["../../../src/discovery/sandbox/config-run.ts"],"names":[],"mappings":"AA0DA,MAAM,WAAW,mBAAmB;IAClC,kOAAkO;IAClO,MAAM,EAAE,OAAO,CAAC;IAChB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,mBAAmB,CAAC,CAgD9B"}
@@ -9,6 +9,11 @@
9
9
  * one function is the cheapest way to make "same profile" a fact rather than
10
10
  * a claim.
11
11
  *
12
+ * chant #1148 — this is also the one place chant forwards a sandboxed
13
+ * child's own stdout/stderr, so `./run.ts`, `./config-run.ts` and
14
+ * `./policy-run.ts` cannot drift on whether project output vanishes. See
15
+ * {@link SandboxForkOptions.outputPrefix}.
16
+ *
12
17
  * Isolation mechanics (verified on Node v24.13.1 — see the chant#1045 PR
13
18
  * description for the full write-up):
14
19
  * - `--permission --allow-fs-read=<bundle dir>,<project dir>[,<trusted
@@ -42,6 +47,26 @@ export interface SandboxForkOptions {
42
47
  timeoutMs: number;
43
48
  /** What timed out / exited early, for the error message (e.g. `"sandboxed run"`). */
44
49
  label: string;
50
+ /**
51
+ * chant #1148 — prepended to every line the child writes on EITHER stdout
52
+ * or stderr before it is relayed, line-buffered, to this process's own
53
+ * stderr (e.g. `"[sandbox:run]"`, `"[sandbox:config]"`,
54
+ * `"[policy:org.ts]"`).
55
+ *
56
+ * A sandboxed child's `console.log`/`console.error` used to go nowhere: its
57
+ * stdout was piped but never read, and its stderr was captured only into
58
+ * {@link stderrBuf}'s error-message use, never surfaced on a successful
59
+ * run. Diagnostics crossing as data (the whole point of the boundary) is
60
+ * not the same thing as incidental output being silently dropped — chant's
61
+ * stance is that nothing the project prints vanishes, sandboxed or not.
62
+ *
63
+ * This is forwarding, not a second capture: {@link stderrBuf} still
64
+ * accumulates the child's raw stderr for `classifyChildError`/the
65
+ * exited-before-reporting message exactly as before. Both read the same
66
+ * `data` events; one buffers for classification, this one relays for a
67
+ * human to see.
68
+ */
69
+ outputPrefix: string;
45
70
  /**
46
71
  * chant #1131 — an optional payload sent to the child over the SAME IPC
47
72
  * channel its response comes back on, immediately after the fork.
@@ -1 +1 @@
1
- {"version":3,"file":"fork.d.ts","sourceRoot":"","sources":["../../../src/discovery/sandbox/fork.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;IACnB,6FAA6F;IAC7F,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,eAAe,EAAE,MAAM,CAAC;IACxB,6IAA6I;IAC7I,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC;;;;;OAKG;IACH,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,qFAAqF;IACrF,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;;;;;;;;;;OAgBG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAC7B,OAAO,EAAE,kBAAkB,EAC3B,UAAU,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,IAAI,CAAC,GACzC,OAAO,CAAC,CAAC,CAAC,CAwEZ"}
1
+ {"version":3,"file":"fork.d.ts","sourceRoot":"","sources":["../../../src/discovery/sandbox/fork.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;IACnB,6FAA6F;IAC7F,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,eAAe,EAAE,MAAM,CAAC;IACxB,6IAA6I;IAC7I,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC;;;;;OAKG;IACH,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,qFAAqF;IACrF,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;;;;;;;;;;;;OAkBG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;;;;;;;;;;;OAgBG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAgCD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAC7B,OAAO,EAAE,kBAAkB,EAC3B,UAAU,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,IAAI,CAAC,GACzC,OAAO,CAAC,CAAC,CAAC,CA2FZ"}
@@ -1 +1 @@
1
- {"version":3,"file":"policy-run.d.ts","sourceRoot":"","sources":["../../../src/discovery/sandbox/policy-run.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAIjE,OAAO,EAGL,KAAK,0BAA0B,EAEhC,MAAM,eAAe,CAAC;AA+EvB,MAAM,WAAW,mBAAmB;IAClC,mJAAmJ;IACnJ,WAAW,EAAE,mBAAmB,EAAE,CAAC;IACnC,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,qFAAqF;IACrF,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,+DAA+D;IAC/D,WAAW,EAAE,0BAA0B,CAAC;IACxC,8GAA8G;IAC9G,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iKAAiK;IACjK,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA0DtG"}
1
+ {"version":3,"file":"policy-run.d.ts","sourceRoot":"","sources":["../../../src/discovery/sandbox/policy-run.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAIjE,OAAO,EAGL,KAAK,0BAA0B,EAEhC,MAAM,eAAe,CAAC;AA0FvB,MAAM,WAAW,mBAAmB;IAClC,mJAAmJ;IACnJ,WAAW,EAAE,mBAAmB,EAAE,CAAC;IACnC,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,qFAAqF;IACrF,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,+DAA+D;IAC/D,WAAW,EAAE,0BAA0B,CAAC;IACxC,8GAA8G;IAC9G,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iKAAiK;IACjK,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA6DtG"}
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/discovery/sandbox/run.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,cAAc,EAA2B,MAAM,cAAc,CAAC;AAOvE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,2TAA2T;IAC3T,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAClC,8LAA8L;IAC9L,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,kGAAkG;IAClG,QAAQ,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;;;OAQG;IACH,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C;AAgBD;;;;;;;;;;;GAWG;AACH,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,gBAAgB,CAAC,CAsD3B"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/discovery/sandbox/run.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,cAAc,EAA2B,MAAM,cAAc,CAAC;AAOvE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,2TAA2T;IAC3T,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAClC,8LAA8L;IAC9L,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,kGAAkG;IAClG,QAAQ,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;;;OAQG;IACH,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C;AAgBD;;;;;;;;;;;GAWG;AACH,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,gBAAgB,CAAC,CAyD3B"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * K8s environment→cluster binding — chant #1100.
3
+ *
4
+ * Every cloud lexicon binds an environment to a scope: AWS resolves `<env>`
5
+ * to a CloudFormation stack, Azure treats `<env>` as the resource group,
6
+ * Temporal looks up `temporal.profiles.<env>` in `chant.config.ts`. K8s (and
7
+ * GCP-via-Config-Connector, which observes through the same kubectl path)
8
+ * bound nothing — `describeResources` shelled out to `kubectl get` with no
9
+ * `--context`, so it read whatever cluster `kubectl config current-context`
10
+ * happened to point at. Point `prod` at a dev cluster and every declared
11
+ * resource reads as missing — a wrong-cluster diff that looks like a
12
+ * confident list of deletions.
13
+ *
14
+ * This module is the shared resolver both the k8s and gcp lexicons'
15
+ * `describeResources` call, so they resolve a cluster identity the same way
16
+ * (see `lexicons/k8s/src/config.ts`'s `K8sChantConfig` for the declared
17
+ * shape). It is intentionally provider-agnostic and lives in core (like
18
+ * `./ownership.ts`) rather than in the k8s lexicon package, since gcp's
19
+ * Config Connector observation needs it too without taking a dependency on
20
+ * the k8s lexicon.
21
+ */
22
+ /** A single environment's cluster binding — see `K8sChantConfig` in the k8s lexicon. */
23
+ export interface K8sClusterProfile {
24
+ /** kubectl context name this environment is bound to. */
25
+ context: string;
26
+ }
27
+ /** Shape of the `k8s` passthrough key in `chant.config.ts` that this resolver reads. */
28
+ export interface K8sConfigShape {
29
+ profiles?: Record<string, K8sClusterProfile>;
30
+ }
31
+ /**
32
+ * Thrown when an environment declares a cluster binding but the ambient
33
+ * kubectl context disagrees with it. Refusing here — instead of silently
34
+ * observing whichever cluster is ambient — is the fix for #1100: a
35
+ * wrong-cluster read reports every declared resource as missing, which #1089
36
+ * then classifies as a confident (and wrong) list of `create` actions.
37
+ */
38
+ export declare class ClusterBindingMismatchError extends Error {
39
+ readonly environment: string;
40
+ readonly expectedContext: string;
41
+ readonly ambientContext: string;
42
+ constructor(environment: string, expectedContext: string, ambientContext: string);
43
+ }
44
+ export interface ResolvedClusterTarget {
45
+ /**
46
+ * Explicit `--context` value to pass to every kubectl invocation. Present
47
+ * only when the environment has a declared binding — undefined means
48
+ * "no binding, keep today's ambient-context behavior".
49
+ */
50
+ context?: string;
51
+ /** Where the target came from. */
52
+ source: "bound" | "ambient";
53
+ }
54
+ /**
55
+ * Resolve the kubectl context an environment should be observed/applied
56
+ * against, reading `k8s.profiles.<environment>.context` from `chant.config.ts`
57
+ * (the `config` passed in is the passthrough `ChantConfig`, cast loosely since
58
+ * the `k8s` key isn't declared on the core schema — same pattern as
59
+ * `temporal.profiles`).
60
+ *
61
+ * - No binding declared: returns `{ source: "ambient" }` — unchanged
62
+ * behavior — but logs a visible warning identifying the caller and
63
+ * environment, so the fallback is never silent (#1100 acceptance).
64
+ * - Binding declared and the ambient context agrees (or ambient can't be
65
+ * determined): returns `{ context: bound, source: "bound" }`. Callers
66
+ * should pass this context explicitly on every kubectl invocation rather
67
+ * than relying on it also being ambient.
68
+ * - Binding declared and the ambient context disagrees: throws
69
+ * {@link ClusterBindingMismatchError} — a loud refusal instead of quietly
70
+ * reading the wrong cluster.
71
+ */
72
+ export declare function resolveClusterTarget(config: Record<string, unknown>, environment: string, lexiconName: string): Promise<ResolvedClusterTarget>;
73
+ //# sourceMappingURL=kubectl-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kubectl-context.d.ts","sourceRoot":"","sources":["../src/kubectl-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAOH,wFAAwF;AACxF,MAAM,WAAW,iBAAiB;IAChC,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wFAAwF;AACxF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;CAC9C;AAED;;;;;;GAMG;AACH,qBAAa,2BAA4B,SAAQ,KAAK;aAElC,WAAW,EAAE,MAAM;aACnB,eAAe,EAAE,MAAM;aACvB,cAAc,EAAE,MAAM;gBAFtB,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,MAAM,EACvB,cAAc,EAAE,MAAM;CAWzC;AAED,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,MAAM,EAAE,OAAO,GAAG,SAAS,CAAC;CAC7B;AAaD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,qBAAqB,CAAC,CAmBhC"}
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import type { Severity, RuleConfig } from "./rule.js";
3
+ import type { PostSynthDiagnostic } from "./post-synth.js";
3
4
  export { findProjectRoot } from "../project-root.js";
4
5
  export declare const LintConfigSchema: z.ZodObject<{
5
6
  rules: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodEnum<{
@@ -72,6 +73,31 @@ export interface ParsedRuleConfig {
72
73
  * Parse a rule config value into severity and options
73
74
  */
74
75
  export declare function parseRuleConfig(value: RuleConfig): ParsedRuleConfig;
76
+ /**
77
+ * Resolve one check/rule id's effective severity (and options) against an
78
+ * already-resolved `lint.rules` map — the ONE place `"off"`/severity-override
79
+ * resolution happens, so a rule id behaves identically regardless of which
80
+ * phase produced it.
81
+ *
82
+ * chant #1138 — before this, the same `lint.rules: { ID: "off" }` config was
83
+ * resolved by two independent call sites that had grown their own copy of
84
+ * this logic (`../cli/commands/lint.ts`'s `getDefaultRules` for AST COR/EVL
85
+ * rules, and its `runComponentCheckDiagnostics` for whole-component COMP*
86
+ * checks) — identical in effect, but a rule id's suppression having two
87
+ * places to (potentially, eventually) diverge is itself the bug class #1138
88
+ * is about. Both were converted to call this instead, and post-synth checks/
89
+ * policies (`./post-synth.ts`'s `applyConfiguredSeverity`) now go through it
90
+ * too, closing the gap the issue reports: a post-synth check id honors
91
+ * `lint.rules` exactly like an AST rule id does.
92
+ *
93
+ * `rules` takes the already-resolved map (`config.rules`, or
94
+ * `resolveRulesForFile`'s per-file merge) rather than a whole `LintConfig` —
95
+ * callers that need per-file `overrides` resolve that first; post-synth
96
+ * checks have no per-file scope to begin with (see {@link
97
+ * ./post-synth.ts!PostSynthDiagnostic}'s doc for why), so they always pass
98
+ * `config.rules` directly.
99
+ */
100
+ export declare function resolveConfiguredSeverity(rules: Record<string, RuleConfig> | undefined, id: string, defaultSeverity: Severity): ParsedRuleConfig;
75
101
  /**
76
102
  * Default configuration with all rules enabled at strict preset severities
77
103
  */
@@ -104,4 +130,58 @@ export declare function loadConfig(dir: string): LintConfig;
104
130
  * @returns Merged rule configuration with overrides applied
105
131
  */
106
132
  export declare function resolveRulesForFile(config: LintConfig, filePath: string): Record<string, RuleConfig>;
133
+ /** Result of applying `lint.rules` to a set of post-synth diagnostics. */
134
+ export interface PostSynthSeverityResult {
135
+ /** Diagnostics after config resolution — `"off"`-suppressed ones removed, everything else at its resolved severity. */
136
+ diagnostics: PostSynthDiagnostic[];
137
+ /**
138
+ * Diagnostics `lint.rules` turned `"off"`, unaltered — present so a caller
139
+ * can report a count (chant #1138) rather than the finding simply
140
+ * vanishing, mirroring `../lint/engine.ts`'s `LintRunResult.suppressed` for
141
+ * AST rules.
142
+ */
143
+ suppressed: PostSynthDiagnostic[];
144
+ }
145
+ /**
146
+ * Apply `lint.rules` severity overrides to already-produced post-synth
147
+ * diagnostics — one lexicon-shipped check's findings, or one project's
148
+ * `lint.policies` findings, it doesn't matter which: both are plain
149
+ * `PostSynthDiagnostic[]` by the time they reach this function, so both go
150
+ * through the identical resolution an AST rule id or a COMP* check id gets
151
+ * (`resolveConfiguredSeverity`, above), keyed by `diag.checkId` instead of
152
+ * `LintRule.id`/`ComponentCheck.id`. `"off"` suppresses (moved to
153
+ * `suppressed`); any other configured severity replaces `diag.severity`,
154
+ * exactly as a config override changes an AST diagnostic's reported level.
155
+ *
156
+ * Lives here rather than in `./post-synth.ts` (where `PostSynthDiagnostic` is
157
+ * declared) on purpose: `post-synth.ts` is a leaf every lexicon's checks
158
+ * import as a real runtime module, and this file resolves built-in preset
159
+ * paths via the runtime adapter at module scope — pulling that into every
160
+ * lexicon's check barrel merely to share one filter function would be the
161
+ * wrong trade. Only this file's TYPE (`PostSynthDiagnostic`) crosses back,
162
+ * which costs nothing at runtime.
163
+ *
164
+ * chant #1138 — deliberately does NOT also honor `chant-disable` source
165
+ * comments. `PostSynthDiagnostic` has no source anchor to disable AT — see
166
+ * its doc comment (`./post-synth.ts`) for why `entity` doesn't supply one —
167
+ * so there is no coherent site to check for a directive. Even in the one case
168
+ * a real anchor sometimes exists (a live, in-process `ctx.entities` value
169
+ * stamped with build provenance, `../provenance.ts`'s `getProvenance`), it
170
+ * would not generalize: not every check sets `entity`, `entity` isn't
171
+ * guaranteed to be an entities-map key (it's a name in the synthesized
172
+ * OUTPUT — a CFN logical id, a k8s `metadata.name` — which a serializer is
173
+ * free to have derived, prefixed, or renamed from the source-level entity
174
+ * name), and that provenance never crosses the `--sandbox` policy child's
175
+ * JSON wire (`../discovery/entity-wire-codec.ts` doesn't carry it, and
176
+ * re-deriving it on the far side would mean sending source file paths into a
177
+ * channel that's supposed to carry only the resolved build). Building
178
+ * directive suppression on a sometimes-present, sometimes-not anchor would
179
+ * make `chant build` and `chant build --sandbox` disagree about the
180
+ * identical finding depending on which path happened to still have the
181
+ * entity object around — precisely the kind of inconsistency #1138 exists to
182
+ * remove. Config severity (`"off"`) is the one suppression surface this can
183
+ * offer uniformly; a check that wants a per-instance escape hatch can read
184
+ * `ctx.env`/its own options to decide not to emit a diagnostic at all.
185
+ */
186
+ export declare function applyConfiguredSeverity(diagnostics: readonly PostSynthDiagnostic[], rules: Record<string, RuleConfig> | undefined): PostSynthSeverityResult;
107
187
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lint/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AASnD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAiBlD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAS3B,CAAC;AAkGH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,wCAAwC;IACxC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,mFAAmF;IACnF,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,gDAAgD;IAChD,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;IAC3B,0FAA0F;IAC1F,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,KAAK,GAAG,QAAQ,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,gBAAgB,CAsBnE;AAED;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,UAG5B,CAAC;AA0HF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAkClD;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAkBpG"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lint/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACnD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AASxD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAiBlD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAS3B,CAAC;AAkGH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,wCAAwC;IACxC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,mFAAmF;IACnF,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,gDAAgD;IAChD,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;IAC3B,0FAA0F;IAC1F,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,KAAK,GAAG,QAAQ,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,gBAAgB,CAsBnE;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,SAAS,EAC7C,EAAE,EAAE,MAAM,EACV,eAAe,EAAE,QAAQ,GACxB,gBAAgB,CAIlB;AAED;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,UAG5B,CAAC;AA0HF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAkClD;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAkBpG;AAED,0EAA0E;AAC1E,MAAM,WAAW,uBAAuB;IACtC,uHAAuH;IACvH,WAAW,EAAE,mBAAmB,EAAE,CAAC;IACnC;;;;;OAKG;IACH,UAAU,EAAE,mBAAmB,EAAE,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,SAAS,mBAAmB,EAAE,EAC3C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,SAAS,GAC5C,uBAAuB,CAczB"}
@@ -13,12 +13,18 @@ import type { PostSynthCheck, PostSynthDiagnostic } from "./post-synth.js";
13
13
  */
14
14
  export declare function loadPolicyChecks(paths: string[], configDir: string): Promise<PostSynthCheck[]>;
15
15
  export interface PolicyEvaluation {
16
- /** All policy diagnostics (errors + warnings). */
16
+ /** All policy diagnostics (errors + warnings), after `lint.rules` severity overrides. */
17
17
  diagnostics: PostSynthDiagnostic[];
18
- /** The error-severity subset — these are policy *violations* that gate. */
18
+ /** The error-severity subset (post-override) — these are policy *violations* that gate. */
19
19
  violations: PostSynthDiagnostic[];
20
20
  /** The environment policies were evaluated against (if any). */
21
21
  env?: string;
22
+ /**
23
+ * chant #1138 — diagnostics `lint.rules` turned `"off"`, present here rather
24
+ * than dropped so a caller (`policyGate`, below) can report a count instead
25
+ * of the finding just vanishing.
26
+ */
27
+ suppressed: PostSynthDiagnostic[];
22
28
  }
23
29
  /**
24
30
  * Build a project and run its `lint.policies` over the resolved resources,
@@ -1 +1 @@
1
- {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/lint/policy.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAGxE;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAuBpG;AAED,MAAM,WAAW,gBAAgB;IAC/B,kDAAkD;IAClD,WAAW,EAAE,mBAAmB,EAAE,CAAC;IACnC,2EAA2E;IAC3E,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,gEAAgE;IAChE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAAC,IAAI,EAAE;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA8B5B"}
1
+ {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/lint/policy.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAIxE;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAuBpG;AAED,MAAM,WAAW,gBAAgB;IAC/B,yFAAyF;IACzF,WAAW,EAAE,mBAAmB,EAAE,CAAC;IACnC,2FAA2F;IAC3F,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,gEAAgE;IAChE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,UAAU,EAAE,mBAAmB,EAAE,CAAC;CACnC;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAAC,IAAI,EAAE;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAmC5B"}
@@ -33,6 +33,19 @@ export interface PostSynthContext {
33
33
  export declare function getPrimaryOutput(output: string | SerializerResult): string;
34
34
  /**
35
35
  * A diagnostic from a post-synthesis check.
36
+ *
37
+ * chant #1138 — deliberately carries no `file`/`line` the way `LintDiagnostic`
38
+ * (`./rule.ts`) does. A post-synth check runs over `ctx.outputs` — the
39
+ * SYNTHESIZED output text (a CloudFormation template, a Kubernetes manifest) —
40
+ * not a `ts.SourceFile`, so there is no AST position to report in the first
41
+ * place. `entity` (below) is the closest thing to a locator and is NOT a
42
+ * substitute: it names a resource in that synthesized output (a CFN logical
43
+ * id, a k8s `metadata.name`), which several checks in this repo never even
44
+ * set (a cross-cutting check with no single implicated resource), and which
45
+ * is not guaranteed to match a `ctx.entities` map key. This is why source-
46
+ * comment (`chant-disable`) suppression is out of scope for post-synth
47
+ * findings — see `./config.ts`'s `applyConfiguredSeverity` doc for the full
48
+ * reasoning and what suppression surface post-synth findings get instead.
36
49
  */
37
50
  export interface PostSynthDiagnostic {
38
51
  /** ID of the check that produced this diagnostic */
@@ -41,7 +54,11 @@ export interface PostSynthDiagnostic {
41
54
  severity: Severity;
42
55
  /** Human-readable message */
43
56
  message: string;
44
- /** Optional entity name related to this diagnostic */
57
+ /**
58
+ * Optional resource name related to this diagnostic — a name from the
59
+ * SYNTHESIZED OUTPUT (a CFN logical id, a k8s `metadata.name`), not a
60
+ * source file/line. See this interface's doc comment.
61
+ */
45
62
  entity?: string;
46
63
  /** Optional lexicon related to this diagnostic */
47
64
  lexicon?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"post-synth.d.ts","sourceRoot":"","sources":["../../src/lint/post-synth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAEvC;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,kEAAkE;IAClE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAAC,CAAC;IAChD,8CAA8C;IAC9C,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAClC;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,WAAW,EAAE;QACX,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAAC,CAAC;QAChD,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAClC,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,MAAM,EAAE,KAAK,CAAC;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACjD,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;CACH;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,GAAG,MAAM,CAE1E;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,QAAQ,EAAE,QAAQ,CAAC;IACnB,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,uCAAuC;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,+CAA+C;IAC/C,KAAK,CAAC,GAAG,EAAE,gBAAgB,GAAG,mBAAmB,EAAE,CAAC;CACrD;AAED,8EAA8E;AAC9E,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAQxE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,cAAc,EAAE,EACxB,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,EAC5C,GAAG,CAAC,EAAE,MAAM,GACX,mBAAmB,EAAE,CAavB"}
1
+ {"version":3,"file":"post-synth.d.ts","sourceRoot":"","sources":["../../src/lint/post-synth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAEvC;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,kEAAkE;IAClE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAAC,CAAC;IAChD,8CAA8C;IAC9C,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAClC;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,WAAW,EAAE;QACX,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAAC,CAAC;QAChD,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAClC,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,MAAM,EAAE,KAAK,CAAC;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACjD,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;CACH;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,GAAG,MAAM,CAE1E;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,mBAAmB;IAClC,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,QAAQ,EAAE,QAAQ,CAAC;IACnB,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,uCAAuC;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,+CAA+C;IAC/C,KAAK,CAAC,GAAG,EAAE,gBAAgB,GAAG,mBAAmB,EAAE,CAAC;CACrD;AAED,8EAA8E;AAC9E,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAQxE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,cAAc,EAAE,EACxB,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,EAC5C,GAAG,CAAC,EAAE,MAAM,GACX,mBAAmB,EAAE,CAavB"}
@@ -22,10 +22,13 @@ export interface StackOutput extends Declarable {
22
22
  readonly lexicon: string;
23
23
  readonly entityType: string;
24
24
  readonly kind: "output";
25
- /** The exported value: a bare attribute reference, or an intrinsic wrapping
26
- * one (e.g. `Join(",", zone.NameServers)`). */
27
- readonly sourceRef: AttrRef | Intrinsic;
25
+ /** The exported value: a bare attribute reference, an intrinsic wrapping
26
+ * one (e.g. `Join(",", zone.NameServers)`), or a literal string. */
27
+ readonly sourceRef: AttrRef | Intrinsic | string;
28
28
  readonly description?: string;
29
+ /** When set, the serializer emits a cross-stack export under this name
30
+ * (CloudFormation `Output.Export.Name`) in addition to the plain output. */
31
+ readonly exportName?: string;
29
32
  }
30
33
  /**
31
34
  * Type guard for StackOutput.
@@ -49,7 +52,9 @@ export declare function isStackOutput(value: unknown): value is StackOutput;
49
52
  * });
50
53
  * ```
51
54
  */
52
- export declare function stackOutput(ref: AttrRef | Intrinsic, options?: {
55
+ export declare function stackOutput(ref: AttrRef | Intrinsic | string, options?: {
53
56
  description?: string;
57
+ exportName?: string;
58
+ lexicon?: string;
54
59
  }): StackOutput;
55
60
  //# sourceMappingURL=stack-output.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stack-output.d.ts","sourceRoot":"","sources":["../src/stack-output.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,iBAAiB,EAAE,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAG1D;;GAEG;AACH,eAAO,MAAM,mBAAmB,eAAkC,CAAC;AAEnE;;;GAGG;AACH,MAAM,WAAW,WAAY,SAAQ,UAAU;IAC7C,QAAQ,CAAC,CAAC,mBAAmB,CAAC,EAAE,IAAI,CAAC;IACrC,QAAQ,CAAC,CAAC,iBAAiB,CAAC,EAAE,IAAI,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB;mDAC+C;IAC/C,QAAQ,CAAC,SAAS,EAAE,OAAO,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAqBD;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAOlE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,OAAO,GAAG,SAAS,EACxB,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GACjC,WAAW,CAiCb"}
1
+ {"version":3,"file":"stack-output.d.ts","sourceRoot":"","sources":["../src/stack-output.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,iBAAiB,EAAE,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAG1D;;GAEG;AACH,eAAO,MAAM,mBAAmB,eAAkC,CAAC;AAEnE;;;GAGG;AACH,MAAM,WAAW,WAAY,SAAQ,UAAU;IAC7C,QAAQ,CAAC,CAAC,mBAAmB,CAAC,EAAE,IAAI,CAAC;IACrC,QAAQ,CAAC,CAAC,iBAAiB,CAAC,EAAE,IAAI,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB;wEACoE;IACpE,QAAQ,CAAC,SAAS,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACjD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;gFAC4E;IAC5E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAqBD;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAOlE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,EACjC,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GACxE,WAAW,CA0Cb"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
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",
@@ -1,12 +1,14 @@
1
1
  import { describe, test, expect, beforeEach, afterEach, vi } from "vitest";
2
2
  import { buildCommand, resolveBuildFormat, type BuildOptions } from "./build";
3
3
  import type { Serializer } from "../../serializer";
4
+ import type { LexiconPlugin } from "../../lexicon";
4
5
  import { parseYAML } from "../../yaml";
5
6
  import { mkdir, rm, writeFile } from "node:fs/promises";
6
7
  import { existsSync, readFileSync } from "node:fs";
7
8
  import { join, dirname, resolve as resolvePath } from "node:path";
8
9
  import { tmpdir } from "node:os";
9
10
  import { fileURLToPath } from "node:url";
11
+ import { resetSandboxPolicyExecutionForTests } from "../../lint/policy-sandbox";
10
12
 
11
13
  describe("buildCommand", () => {
12
14
  let testDir: string;
@@ -514,6 +516,194 @@ export const testEntity = {
514
516
  { Key: "env", Value: "prod" },
515
517
  ]);
516
518
  });
519
+
520
+ /**
521
+ * chant #1138 — post-synth findings (a lexicon-shipped check's, and a
522
+ * project's `lint.policies`') honor `lint.rules` severity overrides the
523
+ * same way a pre-synth COR/EVL/COMP diagnostic does. `runComponentChecks`
524
+ * proved the shape (`component-lint.test.ts`); this proves the post-synth
525
+ * side of the same fix.
526
+ */
527
+ describe("post-synth findings honor lint.rules (chant #1138)", () => {
528
+ // `armSandboxPolicyExecution` is a one-way, process-global latch — once a
529
+ // `--sandbox` build in this file arms it, EVERY later `buildCommand` call
530
+ // in this worker (sandboxed or not) would otherwise see it armed and
531
+ // refuse to load policies in-process. Reset it around each test here so
532
+ // the plain/sandboxed pairs below are actually independent.
533
+ beforeEach(() => resetSandboxPolicyExecutionForTests());
534
+ afterEach(() => resetSandboxPolicyExecutionForTests());
535
+
536
+ /**
537
+ * One trivial declarable using the `mockSerializer`'s "test" lexicon, so
538
+ * `result.outputs` is non-empty and the build's own "discovered source
539
+ * but produced no output" guard (`../commands/build.ts`) doesn't fire —
540
+ * these tests are about post-synth suppression, not that guard.
541
+ */
542
+ async function writeTrivialEntity(): Promise<void> {
543
+ await writeFile(
544
+ join(testDir, "main.ts"),
545
+ `export const e = { lexicon: "test", entityType: "TestEntity", [Symbol.for("chant.declarable")]: true };\n`,
546
+ );
547
+ }
548
+
549
+ /** A minimal `LexiconPlugin` whose one post-synth check always emits one fixed diagnostic — the fixed-input half of a severity-override test. */
550
+ function fakePostSynthPlugin(checkId: string, severity: "error" | "warning" | "info"): LexiconPlugin {
551
+ return {
552
+ name: "fake",
553
+ serializer: { name: "fake", rulePrefix: "FAKE", serialize: () => "{}" },
554
+ generate: async () => {},
555
+ validate: async () => {},
556
+ coverage: async () => {},
557
+ package: async () => {},
558
+ postSynthChecks: () => [
559
+ {
560
+ id: checkId,
561
+ description: "test check",
562
+ check: () => [{ checkId, severity, message: `${checkId} triggered` }],
563
+ },
564
+ ],
565
+ };
566
+ }
567
+
568
+ test("lint.rules off suppresses a lexicon-shipped post-synth finding and reports a suppressed count", async () => {
569
+ await writeTrivialEntity();
570
+ await writeFile(
571
+ join(testDir, "chant.config.ts"),
572
+ `export default { lint: { rules: { "FAKE001": "off" } } };\n`,
573
+ );
574
+
575
+ const result = await buildCommand({
576
+ path: testDir,
577
+ format: "json",
578
+ serializers: [mockSerializer],
579
+ plugins: [fakePostSynthPlugin("FAKE001", "error")],
580
+ });
581
+
582
+ expect(result.success).toBe(true);
583
+ expect(result.errors).toEqual([]);
584
+ expect(result.errors.join("\n")).not.toContain("FAKE001 triggered");
585
+ expect(result.warnings.some((w) => w.includes("1 post-synth finding(s) suppressed"))).toBe(true);
586
+ });
587
+
588
+ test("lint.rules downgrades an error-severity post-synth check to warning, so it no longer fails the build", async () => {
589
+ await writeTrivialEntity();
590
+ await writeFile(
591
+ join(testDir, "chant.config.ts"),
592
+ `export default { lint: { rules: { "FAKE002": "warning" } } };\n`,
593
+ );
594
+
595
+ const result = await buildCommand({
596
+ path: testDir,
597
+ format: "json",
598
+ serializers: [mockSerializer],
599
+ plugins: [fakePostSynthPlugin("FAKE002", "error")],
600
+ });
601
+
602
+ expect(result.success).toBe(true);
603
+ expect(result.errors).toEqual([]);
604
+ expect(result.warnings.some((w) => w.includes("FAKE002 triggered"))).toBe(true);
605
+ });
606
+
607
+ test("lint.rules upgrades a warning-severity post-synth check to error, so it now fails the build", async () => {
608
+ await writeTrivialEntity();
609
+ await writeFile(
610
+ join(testDir, "chant.config.ts"),
611
+ `export default { lint: { rules: { "FAKE003": "error" } } };\n`,
612
+ );
613
+
614
+ const result = await buildCommand({
615
+ path: testDir,
616
+ format: "json",
617
+ serializers: [mockSerializer],
618
+ plugins: [fakePostSynthPlugin("FAKE003", "warning")],
619
+ });
620
+
621
+ expect(result.success).toBe(false);
622
+ expect(result.errors.some((e) => e.includes("FAKE003 triggered"))).toBe(true);
623
+ });
624
+
625
+ test("an unconfigured post-synth check id is unaffected — no drift from this fix for the common case", async () => {
626
+ await writeTrivialEntity();
627
+ await writeFile(join(testDir, "chant.config.ts"), `export default {};\n`);
628
+
629
+ const result = await buildCommand({
630
+ path: testDir,
631
+ format: "json",
632
+ serializers: [mockSerializer],
633
+ plugins: [fakePostSynthPlugin("FAKE004", "error")],
634
+ });
635
+
636
+ expect(result.success).toBe(false);
637
+ expect(result.errors.some((e) => e.includes("FAKE004 triggered"))).toBe(true);
638
+ expect(result.warnings.some((w) => w.includes("suppressed"))).toBe(false);
639
+ });
640
+
641
+ /** Write a `lint.policies` project: one policy file whose check always emits one fixed diagnostic. */
642
+ async function writePolicyProject(checkId: string, severity: "error" | "warning", rules: Record<string, string>): Promise<void> {
643
+ await mkdir(join(testDir, "policies"), { recursive: true });
644
+ await writeTrivialEntity();
645
+ await writeFile(
646
+ join(testDir, "policies", "org.ts"),
647
+ `export const check = {\n` +
648
+ ` id: ${JSON.stringify(checkId)},\n` +
649
+ ` description: "test policy",\n` +
650
+ ` check: () => [{ checkId: ${JSON.stringify(checkId)}, severity: ${JSON.stringify(severity)}, message: ${JSON.stringify(`${checkId} triggered`)} }],\n` +
651
+ `};\n`,
652
+ );
653
+ await writeFile(
654
+ join(testDir, "chant.config.ts"),
655
+ `export default { lint: { policies: ["policies/org.ts"], rules: ${JSON.stringify(rules)} } };\n`,
656
+ );
657
+ }
658
+
659
+ test(
660
+ "lint.rules off suppresses a lint.policies finding identically under plain and --sandbox builds",
661
+ async () => {
662
+ await writePolicyProject("ORG-OFF", "error", { "ORG-OFF": "off" });
663
+
664
+ const plain = await buildCommand({ path: testDir, format: "json", serializers: [mockSerializer], plugins: [] });
665
+ const sandboxed = await buildCommand({
666
+ path: testDir,
667
+ format: "json",
668
+ serializers: [mockSerializer],
669
+ plugins: [],
670
+ fold: true,
671
+ sandbox: true,
672
+ });
673
+
674
+ for (const result of [plain, sandboxed]) {
675
+ expect(result.success).toBe(true);
676
+ expect(result.errors).toEqual([]);
677
+ expect(result.warnings.some((w) => w.includes("1 post-synth finding(s) suppressed"))).toBe(true);
678
+ }
679
+ },
680
+ 30_000,
681
+ );
682
+
683
+ test(
684
+ "lint.rules upgrades a lint.policies warning to error identically under plain and --sandbox builds",
685
+ async () => {
686
+ await writePolicyProject("ORG-UP", "warning", { "ORG-UP": "error" });
687
+
688
+ const plain = await buildCommand({ path: testDir, format: "json", serializers: [mockSerializer], plugins: [] });
689
+ const sandboxed = await buildCommand({
690
+ path: testDir,
691
+ format: "json",
692
+ serializers: [mockSerializer],
693
+ plugins: [],
694
+ fold: true,
695
+ sandbox: true,
696
+ });
697
+
698
+ for (const result of [plain, sandboxed]) {
699
+ expect(result.success).toBe(false);
700
+ expect(result.errors.some((e) => e.includes("[policy:ORG-UP]") && e.includes("ORG-UP triggered"))).toBe(true);
701
+ expect(result.warnings.some((w) => w.includes("suppressed"))).toBe(false);
702
+ }
703
+ },
704
+ 30_000,
705
+ );
706
+ });
517
707
  });
518
708
 
519
709
  // ── #284 bug 1: -o extension drives format when --format is absent ────────