@intentius/chant 0.124.14 → 0.124.16

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 (178) hide show
  1. package/dist/build.d.ts +6 -0
  2. package/dist/build.d.ts.map +1 -1
  3. package/dist/build.js +1 -0
  4. package/dist/change-set.d.ts +24 -2
  5. package/dist/change-set.d.ts.map +1 -1
  6. package/dist/change-set.js +50 -1
  7. package/dist/ci-jobs.d.ts +227 -0
  8. package/dist/ci-jobs.d.ts.map +1 -0
  9. package/dist/ci-jobs.js +448 -0
  10. package/dist/cli/flush.d.ts +16 -0
  11. package/dist/cli/flush.d.ts.map +1 -0
  12. package/dist/cli/flush.js +42 -0
  13. package/dist/cli/handlers/approve.d.ts +98 -0
  14. package/dist/cli/handlers/approve.d.ts.map +1 -0
  15. package/dist/cli/handlers/approve.js +495 -0
  16. package/dist/cli/handlers/operator.d.ts +1 -95
  17. package/dist/cli/handlers/operator.d.ts.map +1 -1
  18. package/dist/cli/handlers/operator.js +4 -488
  19. package/dist/cli/handlers/override-approve.js +1 -1
  20. package/dist/cli/main.d.ts +4 -15
  21. package/dist/cli/main.d.ts.map +1 -1
  22. package/dist/cli/main.js +4 -1062
  23. package/dist/cli/parse-args.d.ts +6 -0
  24. package/dist/cli/parse-args.d.ts.map +1 -0
  25. package/dist/cli/parse-args.js +1028 -0
  26. package/dist/components/fan-out-record.d.ts +1 -1
  27. package/dist/components/fan-out-record.d.ts.map +1 -1
  28. package/dist/components/fan-out-record.js +3 -7
  29. package/dist/components/fan-out-run.d.ts +5 -3
  30. package/dist/components/fan-out-run.d.ts.map +1 -1
  31. package/dist/components/fan-out-run.js +7 -5
  32. package/dist/components/pr-pipeline.d.ts.map +1 -1
  33. package/dist/components/pr-pipeline.js +3 -2
  34. package/dist/composite.d.ts +10 -2
  35. package/dist/composite.d.ts.map +1 -1
  36. package/dist/composite.js +8 -0
  37. package/dist/discovery/collect.d.ts +2 -1
  38. package/dist/discovery/collect.d.ts.map +1 -1
  39. package/dist/discovery/collect.js +16 -1
  40. package/dist/discovery/fold-import.d.ts.map +1 -1
  41. package/dist/discovery/fold-import.js +2 -107
  42. package/dist/discovery/host-composite.d.ts +35 -0
  43. package/dist/discovery/host-composite.d.ts.map +1 -0
  44. package/dist/discovery/host-composite.js +93 -0
  45. package/dist/discovery/index.d.ts +9 -0
  46. package/dist/discovery/index.d.ts.map +1 -1
  47. package/dist/discovery/index.js +1 -1
  48. package/dist/gate-decision.d.ts +20 -9
  49. package/dist/gate-decision.d.ts.map +1 -1
  50. package/dist/gate-decision.js +72 -19
  51. package/dist/gated-waves.d.ts +25 -6
  52. package/dist/gated-waves.d.ts.map +1 -1
  53. package/dist/gated-waves.js +21 -6
  54. package/dist/lifecycle/gate-audit.d.ts +69 -2
  55. package/dist/lifecycle/gate-audit.d.ts.map +1 -1
  56. package/dist/lifecycle/gate-audit.js +76 -2
  57. package/dist/lifecycle/gate-ledger.d.ts +24 -0
  58. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  59. package/dist/lifecycle/gate-ledger.js +9 -1
  60. package/dist/op/activities/activity-contracts.d.ts +5 -5
  61. package/dist/op/activities/activity-contracts.js +1 -1
  62. package/dist/op/activities/change-set.d.ts +8 -3
  63. package/dist/op/activities/change-set.d.ts.map +1 -1
  64. package/dist/op/activities/change-set.js +5 -2
  65. package/dist/op/activities/reconcile.d.ts +28 -16
  66. package/dist/op/activities/reconcile.d.ts.map +1 -1
  67. package/dist/op/activities/reconcile.js +34 -20
  68. package/dist/op/builders.d.ts +1 -1
  69. package/dist/op/builders.d.ts.map +1 -1
  70. package/dist/op/builders.js +1 -1
  71. package/dist/op/op-waves-run.d.ts +2 -2
  72. package/dist/op/op-waves-run.d.ts.map +1 -1
  73. package/dist/op/op-waves-run.js +8 -9
  74. package/dist/op/op-waves.d.ts.map +1 -1
  75. package/dist/op/op-waves.js +4 -8
  76. package/dist/package-version.d.ts +2 -0
  77. package/dist/package-version.d.ts.map +1 -0
  78. package/dist/package-version.js +10 -0
  79. package/dist/wave-engine.d.ts +62 -0
  80. package/dist/wave-engine.d.ts.map +1 -0
  81. package/dist/wave-engine.js +59 -0
  82. package/dist/wave-layout.d.ts +29 -0
  83. package/dist/wave-layout.d.ts.map +1 -0
  84. package/dist/wave-layout.js +41 -0
  85. package/dist/wave-runtime.d.ts +29 -0
  86. package/dist/wave-runtime.d.ts.map +1 -0
  87. package/dist/wave-runtime.js +103 -0
  88. package/dist/wave-runtime.mjs +331 -0
  89. package/dist/workspace/__fixtures__/contract-repo.d.ts +7 -0
  90. package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -1
  91. package/dist/workspace/__fixtures__/contract-repo.js +17 -1
  92. package/dist/workspace/__fixtures__/sessions.d.ts +1 -1
  93. package/dist/workspace/__fixtures__/sessions.d.ts.map +1 -1
  94. package/dist/workspace/__fixtures__/sessions.js +3 -3
  95. package/dist/workspace/ajv2020.d.ts +5 -0
  96. package/dist/workspace/ajv2020.d.ts.map +1 -0
  97. package/dist/workspace/ajv2020.js +14 -0
  98. package/dist/workspace/change-set.schema.json +5 -3
  99. package/dist/workspace/ci-green-workflow.d.ts +3 -1
  100. package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
  101. package/dist/workspace/ci-green-workflow.js +5 -2
  102. package/dist/workspace/class-data.d.ts +84 -0
  103. package/dist/workspace/class-data.d.ts.map +1 -0
  104. package/dist/workspace/class-data.js +238 -0
  105. package/dist/workspace/declaration.d.ts.map +1 -1
  106. package/dist/workspace/declaration.js +4 -8
  107. package/dist/workspace/kinds.d.ts +3 -7
  108. package/dist/workspace/kinds.d.ts.map +1 -1
  109. package/dist/workspace/kinds.js +8 -45
  110. package/dist/workspace/points.d.ts.map +1 -1
  111. package/dist/workspace/points.js +2 -5
  112. package/dist/workspace/principal-classes.d.ts +4 -11
  113. package/dist/workspace/principal-classes.d.ts.map +1 -1
  114. package/dist/workspace/principal-classes.js +13 -99
  115. package/package.json +5 -3
  116. package/src/build.ts +8 -0
  117. package/src/change-set.test.ts +48 -0
  118. package/src/change-set.ts +52 -2
  119. package/src/ci-jobs.test.ts +49 -0
  120. package/src/ci-jobs.ts +515 -0
  121. package/src/cli/flush.ts +44 -0
  122. package/src/cli/handlers/approve.ts +618 -0
  123. package/src/cli/handlers/operator.ts +10 -608
  124. package/src/cli/handlers/override-approve.test.ts +1 -1
  125. package/src/cli/handlers/override-approve.ts +1 -1
  126. package/src/cli/main.ts +5 -816
  127. package/src/cli/parse-args.ts +781 -0
  128. package/src/components/fan-out-record.ts +3 -7
  129. package/src/components/fan-out-run.ts +7 -5
  130. package/src/components/pr-pipeline.ts +3 -2
  131. package/src/composite.ts +15 -3
  132. package/src/discovery/collect.ts +15 -1
  133. package/src/discovery/fold-build-parity.test.ts +71 -0
  134. package/src/discovery/fold-import.ts +2 -113
  135. package/src/discovery/fold-project-provenance.test.ts +21 -4
  136. package/src/discovery/host-composite.ts +101 -0
  137. package/src/discovery/index.ts +11 -1
  138. package/src/gate-decision-bundle.test.ts +6 -4
  139. package/src/gate-decision-identity.test.ts +59 -6
  140. package/src/gate-decision.ts +81 -21
  141. package/src/gated-waves.ts +28 -6
  142. package/src/lifecycle/gate-audit.test.ts +55 -1
  143. package/src/lifecycle/gate-audit.ts +116 -3
  144. package/src/lifecycle/gate-ledger.test.ts +20 -1
  145. package/src/lifecycle/gate-ledger.ts +33 -1
  146. package/src/op/activities/activity-contracts.ts +1 -1
  147. package/src/op/activities/change-set.ts +11 -3
  148. package/src/op/activities/lexicon-upgrade.test.ts +2 -0
  149. package/src/op/activities/reconcile.test.ts +62 -2
  150. package/src/op/activities/reconcile.ts +46 -22
  151. package/src/op/builders.ts +1 -1
  152. package/src/op/op-waves-run.ts +9 -9
  153. package/src/op/op-waves.test.ts +2 -0
  154. package/src/op/op-waves.ts +4 -8
  155. package/src/op/work-lease-run.test.ts +12 -4
  156. package/src/package-version.ts +11 -0
  157. package/src/wave-engine.test.ts +107 -0
  158. package/src/wave-engine.ts +98 -0
  159. package/src/wave-layout.ts +44 -0
  160. package/src/wave-runtime-bundle.test.ts +70 -0
  161. package/src/wave-runtime.e2e.test.ts +177 -0
  162. package/src/wave-runtime.ts +104 -0
  163. package/src/workspace/__fixtures__/contract-repo.ts +17 -1
  164. package/src/workspace/__fixtures__/sessions.ts +3 -3
  165. package/src/workspace/ajv2020.ts +17 -0
  166. package/src/workspace/change-set.schema.json +5 -3
  167. package/src/workspace/ci-green-workflow.test.ts +4 -4
  168. package/src/workspace/ci-green-workflow.ts +5 -2
  169. package/src/workspace/class-data.test.ts +64 -0
  170. package/src/workspace/class-data.ts +236 -0
  171. package/src/workspace/declaration.ts +4 -10
  172. package/src/workspace/kinds.ts +8 -40
  173. package/src/workspace/points.ts +2 -5
  174. package/src/workspace/principal-classes.ts +13 -99
  175. package/src/workspace/records-formats.test.ts +7 -2
  176. package/src/workspace/records-sessions-write.test.ts +3 -3
  177. package/src/workspace/status-contract.e2e.test.ts +37 -0
  178. package/src/workspace/status-contract.test.ts +23 -21
@@ -18,8 +18,9 @@
18
18
  *
19
19
  * A gate the declaration at base names under `identity.gates` counts only a
20
20
  * sealed approval from its class: {@link identityGateAdmissionAt} reads that
21
- * rule from a bare clone at base and judges by `admitApprover`, the function
22
- * chant's `gateAdmission` judges by (#3862); pass its `refuses` to
21
+ * rule from a bare clone at base, with the principal classes the pinned
22
+ * packages supply read as data (#3888), and judges by `admitApprover`, the
23
+ * function chant's `gateAdmission` judges by (#3862); pass its `refuses` to
23
24
  * {@link decideGateApproval}.
24
25
  *
25
26
  * With `spend` (#3784), an approval a wave already applied under is spent and
@@ -27,8 +28,9 @@
27
28
  * the lines of `_gates/<op>/applied.jsonl` ({@link parseAppliedGateLines}).
28
29
  *
29
30
  * The module reads the ledger lines and the review it is handed; reading
30
- * them is the caller's. It imports no TypeScript, esbuild, zod, lint, fold or
31
- * codegen module, and none of chant's git ledger code, which
31
+ * them is the caller's. It imports no TypeScript, esbuild, zod, ajv, lint,
32
+ * fold or codegen module, no package's code, and none of chant's git ledger
33
+ * code, which
32
34
  * `gate-decision-bundle.test.ts` holds it to. Its rules are chant's own:
33
35
  * `./lifecycle/gate-ledger-select.ts` is what `evaluateGate` decides by, and
34
36
  * `gate-decision.test.ts` decides the same ledgers both ways.
@@ -90,15 +92,24 @@ export interface IdentityGateAdmission {
90
92
  * class, the role grants in `.chant/trust.json` at base put the approver in
91
93
  * it. Both judge with the same function ({@link admitApprover}).
92
94
  *
93
- * The classes are the four core ones, then `classes`, the domain classes the
94
- * caller read from the pinned packages (`./workspace-principals`), in pin
95
- * order. This reader loads no package. A gate whose class is in neither
96
- * refuses every approval, and when the declaration at base pins anything the
97
- * reason says the pins were not read, since one of them may supply it.
95
+ * The classes are the four core ones, then the domain classes the
96
+ * declaration's pins supply, in pin order (#3888), read as chant reads them:
97
+ * a path pin's `./workspace-principals` file at `base`, and a package pin's
98
+ * from the package installed under `packages` (default `repo`) at the
99
+ * pinned version. A bare clone has no `node_modules`, so a caller that reads
100
+ * one passes the directory its packages are installed under. Only data is
101
+ * read; no package is imported. `classes`, when given, stands for every
102
+ * domain class instead and no pin is read.
103
+ *
104
+ * When a pin can't be read (its package is not installed, or not at the
105
+ * pinned version), a gate whose class it could change (a domain class, or
106
+ * `human`) refuses every approval and says which pins were not read; a gate
107
+ * naming `agent`, `runner` or `service`, or no class, is judged as usual.
98
108
  * Throws when the declaration at base cannot be parsed.
99
109
  */
100
110
  export declare function identityGateAdmissionAt(repo: string, base: string, gate: string, opts?: {
101
111
  classes?: readonly ClassGrant[];
112
+ packages?: string;
102
113
  }): IdentityGateAdmission | null;
103
114
  /** What a pull request's review says about a wave, under `pr-review`. */
104
115
  export interface ReviewApproval {
@@ -1 +1 @@
1
- {"version":3,"file":"gate-decision.d.ts","sourceRoot":"","sources":["../src/gate-decision.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gate-decision.d.ts","sourceRoot":"","sources":["../src/gate-decision.ts"],"names":[],"mappings":"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"}
@@ -18,8 +18,9 @@
18
18
  *
19
19
  * A gate the declaration at base names under `identity.gates` counts only a
20
20
  * sealed approval from its class: {@link identityGateAdmissionAt} reads that
21
- * rule from a bare clone at base and judges by `admitApprover`, the function
22
- * chant's `gateAdmission` judges by (#3862); pass its `refuses` to
21
+ * rule from a bare clone at base, with the principal classes the pinned
22
+ * packages supply read as data (#3888), and judges by `admitApprover`, the
23
+ * function chant's `gateAdmission` judges by (#3862); pass its `refuses` to
23
24
  * {@link decideGateApproval}.
24
25
  *
25
26
  * With `spend` (#3784), an approval a wave already applied under is spent and
@@ -27,13 +28,16 @@
27
28
  * the lines of `_gates/<op>/applied.jsonl` ({@link parseAppliedGateLines}).
28
29
  *
29
30
  * The module reads the ledger lines and the review it is handed; reading
30
- * them is the caller's. It imports no TypeScript, esbuild, zod, lint, fold or
31
- * codegen module, and none of chant's git ledger code, which
31
+ * them is the caller's. It imports no TypeScript, esbuild, zod, ajv, lint,
32
+ * fold or codegen module, no package's code, and none of chant's git ledger
33
+ * code, which
32
34
  * `gate-decision-bundle.test.ts` holds it to. Its rules are chant's own:
33
35
  * `./lifecycle/gate-ledger-select.ts` is what `evaluateGate` decides by, and
34
36
  * `gate-decision.test.ts` decides the same ledgers both ways.
35
37
  */
36
38
  import { spawnSync } from "node:child_process";
39
+ import { readFileSync } from "node:fs";
40
+ import { join, posix } from "node:path";
37
41
  import { isPendingGateExpired, latestPendingGate, latestResolutionForPlan, } from "./lifecycle/gate-ledger-select.js";
38
42
  import { samePlanDigest } from "./lifecycle/plan-digest.js";
39
43
  import { isApprovalSpent } from "./lifecycle/gate-applied.js";
@@ -41,6 +45,7 @@ import { gateSealRefusal } from "./workspace/trust/gate-seal.js";
41
45
  import { DEFAULT_SIGNERS_PATH, TRUST_CONFIG_PATH, parseAllowedSigners, trustConfigProblems, } from "./workspace/trust/trust-files.js";
42
46
  import { parseJsonText } from "./workspace/jsonc.js";
43
47
  import { CORE_CLASSES, admitApprover } from "./workspace/identity-gate-rule.js";
48
+ import { diskFiles, findInstalledPackage, mergePinClasses, parseClassDataByHand, readClassFiles } from "./workspace/class-data.js";
44
49
  export { GATE_APPROVAL_SOURCES } from "./op/op-waves.js";
45
50
  export { isPendingGateExpired, latestPendingGate, latestResolutionForPlan, latestResolutionSince, } from "./lifecycle/gate-ledger-select.js";
46
51
  export { GATE_SEAL_NAMESPACE, gateSealPayload, gateSealRefusal, sshKeyFingerprint, verifySshSignatureWith, } from "./workspace/trust/gate-seal.js";
@@ -141,6 +146,49 @@ export function identityGatesAt(repo, base) {
141
146
  const declaration = declarationAt(repo, base);
142
147
  return declaration === null ? [] : identityGatesOf(declaration.value);
143
148
  }
149
+ /**
150
+ * Read the classes each pin supplies, as chant's `gateAdmission(cwd, gate,
151
+ * base)` reads them: a path pin's `./workspace-principals` file from `base`
152
+ * itself, so a change can't remap a class by editing its own copy of the
153
+ * plugin; a package pin's from the package installed under `packages` (its
154
+ * `node_modules` or a parent's), which must be at the pinned version. Reads
155
+ * data only and imports no package.
156
+ */
157
+ function pinnedClassesAt(repo, base, pins, packages) {
158
+ const reads = [];
159
+ const unread = [];
160
+ (Array.isArray(pins) ? pins : []).forEach((raw, i) => {
161
+ const pin = (raw !== null && typeof raw === "object" ? raw : {});
162
+ if (typeof pin.path === "string" && pin.path) {
163
+ const dir = posix.normalize(pin.path);
164
+ const files = (path) => showAt(repo, base, dir === "." ? path : posix.join(dir, path));
165
+ reads.push({ pin: i, source: pin.path, read: readClassFiles(files, pin.path, parseClassDataByHand) });
166
+ return;
167
+ }
168
+ if (typeof pin.package !== "string" || !pin.package)
169
+ return;
170
+ const dir = findInstalledPackage(pin.package, packages);
171
+ if (!dir) {
172
+ unread.push(`${pin.package} is not installed under ${packages}`);
173
+ return;
174
+ }
175
+ let installed;
176
+ try {
177
+ installed = JSON.parse(readFileSync(join(dir, "package.json"), "utf-8")).version;
178
+ }
179
+ catch {
180
+ installed = undefined;
181
+ }
182
+ if (typeof pin.version === "string" && pin.version && installed !== pin.version) {
183
+ unread.push(`${pin.package} is pinned at ${pin.version}, and ${String(installed ?? "an unknown version")} is installed under ${packages}`);
184
+ return;
185
+ }
186
+ reads.push({ pin: i, source: pin.package, read: readClassFiles(diskFiles(dir), pin.package, parseClassDataByHand) });
187
+ });
188
+ return { classes: mergePinClasses(reads).classes, unread };
189
+ }
190
+ /** The core classes whose members no domain class can change: every one but `human`, which is the principals in no other class. */
191
+ const PIN_FREE = new Set(CORE_CLASSES.filter((c) => c.role !== null).map((c) => c.name));
144
192
  /**
145
193
  * The `identity.gates` rule for `gate` at `base`, or null when the
146
194
  * declaration there names none: what chant's `gateAdmission(cwd, gate, base)`
@@ -149,11 +197,19 @@ export function identityGatesAt(repo, base) {
149
197
  * class, the role grants in `.chant/trust.json` at base put the approver in
150
198
  * it. Both judge with the same function ({@link admitApprover}).
151
199
  *
152
- * The classes are the four core ones, then `classes`, the domain classes the
153
- * caller read from the pinned packages (`./workspace-principals`), in pin
154
- * order. This reader loads no package. A gate whose class is in neither
155
- * refuses every approval, and when the declaration at base pins anything the
156
- * reason says the pins were not read, since one of them may supply it.
200
+ * The classes are the four core ones, then the domain classes the
201
+ * declaration's pins supply, in pin order (#3888), read as chant reads them:
202
+ * a path pin's `./workspace-principals` file at `base`, and a package pin's
203
+ * from the package installed under `packages` (default `repo`) at the
204
+ * pinned version. A bare clone has no `node_modules`, so a caller that reads
205
+ * one passes the directory its packages are installed under. Only data is
206
+ * read; no package is imported. `classes`, when given, stands for every
207
+ * domain class instead and no pin is read.
208
+ *
209
+ * When a pin can't be read (its package is not installed, or not at the
210
+ * pinned version), a gate whose class it could change (a domain class, or
211
+ * `human`) refuses every approval and says which pins were not read; a gate
212
+ * naming `agent`, `runner` or `service`, or no class, is judged as usual.
157
213
  * Throws when the declaration at base cannot be parsed.
158
214
  */
159
215
  export function identityGateAdmissionAt(repo, base, gate, opts = {}) {
@@ -163,16 +219,13 @@ export function identityGateAdmissionAt(repo, base, gate, opts = {}) {
163
219
  return null;
164
220
  const { signers, roles } = trustAt(repo, base);
165
221
  const pins = declaration.value.pins;
166
- const rule = {
167
- gate,
168
- class: requirement.class,
169
- signers,
170
- roles,
171
- classes: [...CORE_CLASSES, ...(opts.classes ?? [])],
172
- ...(Array.isArray(pins) && pins.length > 0
173
- ? { unknownClass: `is not a core class, and this reader does not load the classes the pinned packages in ${declaration.file} at base supply` }
174
- : {}),
175
- };
222
+ const pinned = opts.classes ? { classes: opts.classes, unread: [] } : pinnedClassesAt(repo, base, pins, opts.packages ?? repo);
223
+ const want = requirement.class;
224
+ if (want !== null && !PIN_FREE.has(want) && pinned.unread.length > 0) {
225
+ const reason = `identity.gates.${gate} names the class ${want}, and the pins in ${declaration.file} at base could not all be read, so no approval can show it is in that class: ${pinned.unread.join("; ")}`;
226
+ return { requirement, refuses: () => reason };
227
+ }
228
+ const rule = { gate, class: want, signers, roles, classes: [...CORE_CLASSES, ...pinned.classes] };
176
229
  return { requirement, refuses: (a) => admitApprover(rule, a) };
177
230
  }
178
231
  /**
@@ -2,12 +2,17 @@
2
2
  * Gated waves (#3049): one change rolled out across many roots, a wave at a
3
3
  * time, each wave behind its own gate bound to that wave's set digest.
4
4
  *
5
- * `chant components fan-out --wave-gate` is the runner. This module holds the
6
- * parts that need no runner: how a set of roots is cut into waves, what a
7
- * wave's digest is, what its gate is called, and what the attempt record says
8
- * about each wave. They are kept apart from the runner because terragucci
9
- * calls them from its CI stages as one bundled file with no TypeScript
10
- * toolchain (#3421). Everything here imports `./change-set` and nothing else,
5
+ * Two runners use it (#3766): `chant components fan-out --wave-gate` and the
6
+ * Op waves spec's `chant run wave` (`./op/op-waves-run.ts`). This module
7
+ * holds the parts that need no runner: how a set of roots is cut into waves,
8
+ * what a wave's digest is and which members it covers, what its gate is
9
+ * called, and what the attempt record says about each wave. A gate name
10
+ * pattern and the share split of a wide wave are in `./wave-layout.ts`;
11
+ * `./wave-engine.ts` puts them together with the gate decision call and the
12
+ * record file writer, which need the gate ledger and the filesystem.
13
+ *
14
+ * They are kept apart from the runners because terragucci calls them from
15
+ * its CI stages as one bundled file with no TypeScript toolchain (#3421). Everything here imports `./change-set` and nothing else,
11
16
  * and `gated-waves-bundle.test.ts` holds that.
12
17
  *
13
18
  * ## The set digest
@@ -96,6 +101,20 @@ export declare function waveSetDigest(members: readonly WaveMember[]): string;
96
101
  * on a resumed attempt and its approval still applies.
97
102
  */
98
103
  export declare function waveGateName(gate: string, wave: number): string;
104
+ /** Which members a wave's set digest covers (#3821). */
105
+ export type WaveDigestOver = "all" | "changing";
106
+ /**
107
+ * The members a wave's set digest is taken over (#3821, #3766). `all` (the
108
+ * default): every member. `changing`: the members whose plan is not
109
+ * `empty`, or every member when all of them are empty. `extras`, members
110
+ * that are no run (such as a wave's cost), are always in it.
111
+ */
112
+ export declare function waveDigestMembers<M extends WaveMember & {
113
+ empty?: boolean;
114
+ }>(members: readonly M[], options?: {
115
+ over?: WaveDigestOver;
116
+ extras?: readonly WaveMember[];
117
+ }): WaveMember[];
99
118
  /** Where a wave got to. */
100
119
  export type WaveStatus =
101
120
  /** Planned, and its gate is not approved for this digest. Nothing in it ran. */
@@ -1 +1 @@
1
- {"version":3,"file":"gated-waves.d.ts","sourceRoot":"","sources":["../src/gated-waves.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gated-waves.d.ts","sourceRoot":"","sources":["../src/gated-waves.ts"],"names":[],"mappings":"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"}
@@ -2,12 +2,17 @@
2
2
  * Gated waves (#3049): one change rolled out across many roots, a wave at a
3
3
  * time, each wave behind its own gate bound to that wave's set digest.
4
4
  *
5
- * `chant components fan-out --wave-gate` is the runner. This module holds the
6
- * parts that need no runner: how a set of roots is cut into waves, what a
7
- * wave's digest is, what its gate is called, and what the attempt record says
8
- * about each wave. They are kept apart from the runner because terragucci
9
- * calls them from its CI stages as one bundled file with no TypeScript
10
- * toolchain (#3421). Everything here imports `./change-set` and nothing else,
5
+ * Two runners use it (#3766): `chant components fan-out --wave-gate` and the
6
+ * Op waves spec's `chant run wave` (`./op/op-waves-run.ts`). This module
7
+ * holds the parts that need no runner: how a set of roots is cut into waves,
8
+ * what a wave's digest is and which members it covers, what its gate is
9
+ * called, and what the attempt record says about each wave. A gate name
10
+ * pattern and the share split of a wide wave are in `./wave-layout.ts`;
11
+ * `./wave-engine.ts` puts them together with the gate decision call and the
12
+ * record file writer, which need the gate ledger and the filesystem.
13
+ *
14
+ * They are kept apart from the runners because terragucci calls them from
15
+ * its CI stages as one bundled file with no TypeScript toolchain (#3421). Everything here imports `./change-set` and nothing else,
11
16
  * and `gated-waves-bundle.test.ts` holds that.
12
17
  *
13
18
  * ## The set digest
@@ -106,6 +111,16 @@ export function waveSetDigest(members) {
106
111
  export function waveGateName(gate, wave) {
107
112
  return `${gate}-wave-${wave}`;
108
113
  }
114
+ /**
115
+ * The members a wave's set digest is taken over (#3821, #3766). `all` (the
116
+ * default): every member. `changing`: the members whose plan is not
117
+ * `empty`, or every member when all of them are empty. `extras`, members
118
+ * that are no run (such as a wave's cost), are always in it.
119
+ */
120
+ export function waveDigestMembers(members, options = {}) {
121
+ const changing = options.over === "changing" ? members.filter((m) => m.empty !== true) : [];
122
+ return [...(changing.length > 0 ? changing : members), ...(options.extras ?? [])];
123
+ }
109
124
  const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
110
125
  const strings = (value) => Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
111
126
  /**
@@ -1,5 +1,7 @@
1
1
  import type { GateLedgerPort } from "../op/gate.js";
2
- export declare const GATE_AUDIT_KINDS: readonly ["approval-requested", "approved", "override-requested", "override-recorded", "revoked", "applied", "override-spent"];
2
+ import type { LedgerReadPort } from "./ledger-port.js";
3
+ import type { GateApprovalSource } from "../op/op-waves.js";
4
+ export declare const GATE_AUDIT_KINDS: readonly ["approval-requested", "approved", "override-requested", "override-recorded", "revoked", "applied", "override-spent", "hook"];
3
5
  export type GateAuditKind = (typeof GATE_AUDIT_KINDS)[number];
4
6
  /** How an approval was authorised: a forge review, an ssh seal, or neither. A review wins; a review that is also sealed has `sealed: true` beside it. */
5
7
  export type GateAuditSeal = "sealed" | "review" | "unsigned";
@@ -63,7 +65,7 @@ export interface GateAuditSource {
63
65
  commit?: string;
64
66
  /** The commit that added the line, or for a revocation removed it, when the line came from history. */
65
67
  author?: GateLedgerCommit;
66
- change: "added" | "removed" | "apply";
68
+ change: "added" | "removed" | "apply" | "decision";
67
69
  }
68
70
  export interface GateAuditEntry {
69
71
  /** `sha256:` over where the entry came from: the same sources give the same id. */
@@ -111,8 +113,27 @@ export interface GateAuditEntry {
111
113
  name: string;
112
114
  op?: string;
113
115
  };
116
+ /** Pending facts of a hold (#3886): the hook that holds the wave. */
117
+ heldBy?: {
118
+ hook: string;
119
+ moment: string;
120
+ target?: string;
121
+ };
122
+ /** Pending facts of an op wave (#3861): the approval mode the wave asked under. */
123
+ approvalMode?: GateApprovalSource;
114
124
  /** Overrides: the rules the denial named (from the denial line when there is one). */
115
125
  rules?: string[];
126
+ /** A hook entry: the hook's moment, name, run, how it came out, and why a moment was skipped (#3810). */
127
+ hook?: {
128
+ moment: string;
129
+ name: string;
130
+ target?: string;
131
+ outcome: "passed" | "failed" | "held" | "skipped";
132
+ exit: number;
133
+ reason?: string;
134
+ };
135
+ /** A hook entry: the applied entry of the same wave and digest, if the caller passed that apply in. */
136
+ applied?: string;
116
137
  /** A revoked entry: what was revoked. */
117
138
  subject?: "approval" | "override";
118
139
  /** An applied entry: the approval entry it ran under, if one is held. An override-spent entry: the override entry. */
@@ -128,6 +149,40 @@ export interface GateAuditGap {
128
149
  digest?: string;
129
150
  detail: string;
130
151
  }
152
+ /**
153
+ * An op-wave decision record (`.chant/op-waves/<name>/wave-<k>.json`, written
154
+ * by `chant run wave`), as far as the audit reads it: the wave's gate and
155
+ * digest, and the `outcomes` of its hooks (#3804, #3811, #3847). Typed
156
+ * structurally so this file imports nothing from `../op/`.
157
+ */
158
+ export interface WaveDecisionRecord {
159
+ wave: number;
160
+ name: string;
161
+ op: string;
162
+ gate: string;
163
+ digest?: string;
164
+ heldBy?: string[];
165
+ heldOn?: unknown;
166
+ outcomes?: ReadonlyArray<{
167
+ moment: string;
168
+ name: string;
169
+ target?: string;
170
+ exit: number;
171
+ outcome: string;
172
+ reason?: string;
173
+ }>;
174
+ }
175
+ /**
176
+ * A decision record the caller passes in. The record carries no time and no
177
+ * place, so `ref` (a file path, an artifact, a row) and `at` (when it was
178
+ * written) come from the caller. Give the parsed `record`, or its `text`.
179
+ */
180
+ export interface GateAuditDecision {
181
+ ref: string;
182
+ at: string;
183
+ record?: unknown;
184
+ text?: string;
185
+ }
131
186
  export interface GateAuditOptions {
132
187
  /** Whether a gate is an override gate. Default: op `policy-override`, or a gate named `override-...`. */
133
188
  isOverride?: (op: string, gate: string) => boolean;
@@ -143,6 +198,8 @@ export interface GateAuditOptions {
143
198
  export interface GateAuditInput extends GateAuditOptions {
144
199
  ledgers: readonly GateAuditLedger[];
145
200
  applies?: readonly GateAuditApply[];
201
+ /** Op-wave decision records: each hook outcome in them becomes a `hook` entry linked to the wave's approval and apply (#3810). */
202
+ decisions?: readonly GateAuditDecision[];
146
203
  }
147
204
  export declare const POLICY_OVERRIDE_OP = "policy-override";
148
205
  /** The default {@link GateAuditOptions.isOverride}. */
@@ -172,4 +229,14 @@ export declare function gateLedgerLinesFromLog(log: string): GateLedgerLine[];
172
229
  * history through {@link gateLedgerLinesFromLog} for that.
173
230
  */
174
231
  export declare function readGateAuditLedgers(port: Pick<GateLedgerPort, "read">, ops: readonly string[]): Promise<GateAuditLedger[]>;
232
+ /**
233
+ * The thin reader for decision records kept on the ledger branch: every
234
+ * `.json` file directly under `dir` (such as a product's `_wave-decisions/<name>`),
235
+ * as input for {@link GateAuditInput.decisions}. A decision record holds no
236
+ * time, so `at` stamps them all: when the product collected them. Where the
237
+ * records live is the product's business; chant writes them to
238
+ * `.chant/op-waves/<name>/wave-<k>.json` in the job, and a product that
239
+ * collects them elsewhere passes them in directly.
240
+ */
241
+ export declare function readGateAuditDecisions(port: Pick<LedgerReadPort, "read" | "list">, dir: string, at: string): Promise<GateAuditDecision[]>;
175
242
  //# sourceMappingURL=gate-audit.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"gate-audit.d.ts","sourceRoot":"","sources":["../../src/lifecycle/gate-audit.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gate-audit.d.ts","sourceRoot":"","sources":["../../src/lifecycle/gate-audit.ts"],"names":[],"mappings":"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"}
@@ -21,6 +21,7 @@
21
21
  * | `revoked` | a resolution a commit removed from the file (`subject` says which) |
22
22
  * | `applied` | an apply the caller passes in, with the approval it ran under |
23
23
  * | `override-spent` | an override an apply the caller passes in cites |
24
+ * | `hook` | a hook outcome in an op-wave decision record the caller passes in |
24
25
  *
25
26
  * An override gate is, by default, any gate of the op `policy-override`
26
27
  * (terragucci writes one gate per root) or named `override-<rule>` (SQL
@@ -86,6 +87,7 @@ export const GATE_AUDIT_KINDS = [
86
87
  "revoked",
87
88
  "applied",
88
89
  "override-spent",
90
+ "hook",
89
91
  ];
90
92
  export const POLICY_OVERRIDE_OP = "policy-override";
91
93
  /** The default {@link GateAuditOptions.isOverride}. */
@@ -94,12 +96,17 @@ export function isOverrideGate(op, gate) {
94
96
  }
95
97
  const KNOWN = new Set([
96
98
  "version", "kind", "op", "gate", "resolvedBy", "relayedBy", "timestamp", "note", "url", "planDigest", "environment",
97
- "approver", "seal", "review", "runId", "expiresAt", "description", "wave", "rules", "pr", "head", "reviewers",
99
+ "approver", "seal", "review", "runId", "expiresAt", "description", "wave", "heldBy", "approvalMode", "rules", "pr", "head", "reviewers",
98
100
  ]);
99
101
  const waveOf = (v) => {
100
102
  const w = v;
101
103
  return w && typeof w === "object" && typeof w.number === "number" && typeof w.name === "string" ? { number: w.number, name: w.name, ...(typeof w.op === "string" ? { op: w.op } : {}) } : undefined;
102
104
  };
105
+ const heldByOf = (v) => {
106
+ const h = v;
107
+ return h && typeof h === "object" && typeof h.hook === "string" && typeof h.moment === "string" ? { hook: h.hook, moment: h.moment, ...(typeof h.target === "string" ? { target: h.target } : {}) } : undefined;
108
+ };
109
+ const modeOf = (v) => (v === "ledger" || v === "pr-review" || v === "sealed" ? v : undefined);
103
110
  const str = (v) => (typeof v === "string" && v !== "" ? v : undefined);
104
111
  const strings = (v) => (Array.isArray(v) ? v.filter((x) => typeof x === "string") : undefined);
105
112
  const time = (iso) => Date.parse(iso) || 0;
@@ -142,6 +149,24 @@ const drop = (o) => Object.fromEntries(Object.entries(o).filter(([, v]) => v !==
142
149
  const idOf = (...parts) => contentDigest(canonicalJson(parts));
143
150
  /** Whether two digests, either absent, can be the same plan: absent matches anything, as an old line records none. */
144
151
  const compatible = (a, b) => a === undefined || b === undefined || samePlanDigest(a, b);
152
+ function decisionOf(d) {
153
+ let v = d.record;
154
+ if (v === undefined && d.text !== undefined) {
155
+ try {
156
+ v = JSON.parse(d.text);
157
+ }
158
+ catch {
159
+ return undefined;
160
+ }
161
+ }
162
+ const r = v;
163
+ if (!r || typeof r !== "object" || typeof r.wave !== "number" || typeof r.name !== "string" || typeof r.op !== "string" || typeof r.gate !== "string")
164
+ return undefined;
165
+ const outcomes = Array.isArray(r.outcomes)
166
+ ? r.outcomes.filter((o) => !!o && typeof o === "object" && typeof o.moment === "string" && typeof o.name === "string" && typeof o.exit === "number" && typeof o.outcome === "string")
167
+ : undefined;
168
+ return { wave: r.wave, name: r.name, op: r.op, gate: r.gate, ...(str(r.digest) ? { digest: str(r.digest) } : {}), ...(strings(r.heldBy) ? { heldBy: strings(r.heldBy) } : {}), ...(r.heldOn !== undefined ? { heldOn: r.heldOn } : {}), ...(outcomes ? { outcomes } : {}) };
169
+ }
145
170
  /** The entries `input` adds up to, oldest first, and the gaps they show. */
146
171
  export function deriveGateAudit(input) {
147
172
  const isOverride = input.isOverride ?? isOverrideGate;
@@ -197,7 +222,7 @@ export function deriveGateAudit(input) {
197
222
  ...base,
198
223
  kind: override ? "override-requested" : "approval-requested",
199
224
  at: String(r.timestamp),
200
- ...drop({ runId: str(r.runId), expiresAt: str(r.expiresAt), description: str(r.description), url: str(r.url), rules: strings(r.rules), wave: waveOf(r.wave) }),
225
+ ...drop({ runId: str(r.runId), expiresAt: str(r.expiresAt), description: str(r.description), url: str(r.url), rules: strings(r.rules), wave: waveOf(r.wave), heldBy: heldByOf(r.heldBy), approvalMode: modeOf(r.approvalMode) }),
201
226
  detail: extra,
202
227
  }));
203
228
  continue;
@@ -280,6 +305,35 @@ export function deriveGateAudit(input) {
280
305
  }));
281
306
  }
282
307
  }
308
+ for (const d of input.decisions ?? []) {
309
+ const rec = decisionOf(d);
310
+ if (!rec) {
311
+ gaps.push({ kind: "malformed-line", detail: `${d.ref} is not an op-wave decision record` });
312
+ continue;
313
+ }
314
+ const approval = rec.digest === undefined ? undefined : entries
315
+ .filter((e) => e.kind === "approved" && e.gate === rec.gate && e.op === rec.op && e.digest !== undefined && samePlanDigest(e.digest, rec.digest))
316
+ .sort((x, y) => time(y.at) - time(x.at))[0];
317
+ const applied = rec.digest === undefined ? undefined : entries.find((e) => e.kind === "applied" && e.gate === rec.gate && e.digest !== undefined && samePlanDigest(e.digest, rec.digest));
318
+ for (const [i, o] of (rec.outcomes ?? []).entries()) {
319
+ if (o.outcome !== "passed" && o.outcome !== "failed" && o.outcome !== "held" && o.outcome !== "skipped")
320
+ continue;
321
+ entries.push(keyed({
322
+ id: idOf("hook", d.ref, d.at, i, o),
323
+ kind: "hook",
324
+ at: d.at,
325
+ gate: rec.gate,
326
+ op: rec.op,
327
+ ...(rec.digest ? { digest: rec.digest } : {}),
328
+ ...(approval ? { under: approval.id } : {}),
329
+ ...(applied ? { applied: applied.id } : {}),
330
+ hook: { moment: o.moment, name: o.name, ...(o.target ? { target: o.target } : {}), outcome: o.outcome, exit: o.exit, ...(o.reason ? { reason: o.reason } : {}) },
331
+ wave: { number: rec.wave, name: rec.name, op: rec.op },
332
+ detail: drop({ heldBy: rec.heldBy?.length ? rec.heldBy : undefined, heldOn: o.outcome === "held" ? rec.heldOn : undefined }),
333
+ source: { path: d.ref, op: rec.op, change: "decision" },
334
+ }));
335
+ }
336
+ }
283
337
  entries.sort((x, y) => time(x.at) - time(y.at));
284
338
  return { entries, gaps };
285
339
  }
@@ -339,3 +393,23 @@ export async function readGateAuditLedgers(port, ops) {
339
393
  }
340
394
  return out;
341
395
  }
396
+ /**
397
+ * The thin reader for decision records kept on the ledger branch: every
398
+ * `.json` file directly under `dir` (such as a product's `_wave-decisions/<name>`),
399
+ * as input for {@link GateAuditInput.decisions}. A decision record holds no
400
+ * time, so `at` stamps them all: when the product collected them. Where the
401
+ * records live is the product's business; chant writes them to
402
+ * `.chant/op-waves/<name>/wave-<k>.json` in the job, and a product that
403
+ * collects them elsewhere passes them in directly.
404
+ */
405
+ export async function readGateAuditDecisions(port, dir, at) {
406
+ const out = [];
407
+ for (const file of (await port.list(dir)).sort()) {
408
+ if (!file.endsWith(".json"))
409
+ continue;
410
+ const text = await port.read(dir, file);
411
+ if (text !== null)
412
+ out.push({ ref: `${dir}/${file}`, at, text });
413
+ }
414
+ return out;
415
+ }
@@ -166,6 +166,19 @@ export type GateResolutionInput = Omit<GateResolutionRecord, "version" | "kind">
166
166
  * ledger by a line a minute. Once `expiresAt` passes, the fact is stale and
167
167
  * the next run records a fresh one.
168
168
  */
169
+ export interface PendingGateWave {
170
+ number: number;
171
+ name: string;
172
+ /** The Op waves spec's name. */
173
+ op?: string;
174
+ }
175
+ /** The hook that holds a wave on a gate (#3886): which hook asked, at which moment, and for which run. */
176
+ export interface PendingGateHeldBy {
177
+ hook: string;
178
+ moment: string;
179
+ /** The run, for a `perRun` hook. */
180
+ target?: string;
181
+ }
169
182
  export interface PendingGateRecord {
170
183
  /** Schema version, so an incompatible future shape is detected before being misread. */
171
184
  version: 1;
@@ -234,6 +247,17 @@ export interface PendingGateRecord {
234
247
  * than inferring it from config. Not the quorum block in `approval`.
235
248
  */
236
249
  approvalMode?: GateApprovalSource;
250
+ /**
251
+ * The Op wave this gate belongs to (#3839): a hold on another gate or a
252
+ * policy denial. Absent on a gate that is not a wave's.
253
+ */
254
+ wave?: PendingGateWave;
255
+ /**
256
+ * The hook that holds the wave on this gate (#3886), on a hold only: its
257
+ * name, the moment it ran at and its run. A reader names the holder from it
258
+ * rather than from `description`, which the hook writes.
259
+ */
260
+ heldBy?: PendingGateHeldBy;
237
261
  /**
238
262
  * The open question this gate waits on, on a gate that asks a decision
239
263
  * point (#3170). It is answered with `chant workspace points answer <id>`,