@intentius/chant 0.124.40 → 0.124.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__fixtures__/oidc-emulator.d.ts +49 -0
- package/dist/__fixtures__/oidc-emulator.d.ts.map +1 -0
- package/dist/__fixtures__/oidc-emulator.js +89 -0
- package/dist/ci-handover.d.ts +117 -0
- package/dist/ci-handover.d.ts.map +1 -0
- package/dist/ci-handover.js +217 -0
- package/dist/ci-identity.d.ts +190 -0
- package/dist/ci-identity.d.ts.map +1 -0
- package/dist/ci-identity.js +219 -0
- package/dist/cli/handlers/approve.d.ts +2 -1
- package/dist/cli/handlers/approve.d.ts.map +1 -1
- package/dist/cli/handlers/approve.js +20 -6
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +15 -1
- package/dist/cli/handlers/fan-out.js +16 -3
- package/dist/cli/handlers/gate-resume.d.ts +3 -0
- package/dist/cli/handlers/gate-resume.d.ts.map +1 -1
- package/dist/cli/handlers/gate-resume.js +15 -2
- package/dist/cli/handlers/lock.d.ts +8 -0
- package/dist/cli/handlers/lock.d.ts.map +1 -0
- package/dist/cli/handlers/lock.js +197 -0
- package/dist/cli/handlers/pr-env.d.ts +34 -0
- package/dist/cli/handlers/pr-env.d.ts.map +1 -0
- package/dist/cli/handlers/pr-env.js +436 -0
- package/dist/cli/handlers/pr.d.ts +74 -0
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +503 -53
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +9 -0
- package/dist/cli/handlers/query.d.ts +6 -0
- package/dist/cli/handlers/query.d.ts.map +1 -0
- package/dist/cli/handlers/query.js +80 -0
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +41 -2
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +31 -2
- package/dist/cli/registry.d.ts +12 -2
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/components/fan-out-run.d.ts +28 -0
- package/dist/components/fan-out-run.d.ts.map +1 -1
- package/dist/components/fan-out-run.js +27 -3
- package/dist/components/pr-environment.d.ts +298 -0
- package/dist/components/pr-environment.d.ts.map +1 -0
- package/dist/components/pr-environment.js +406 -0
- package/dist/components/pr-hooks.d.ts +66 -0
- package/dist/components/pr-hooks.d.ts.map +1 -0
- package/dist/components/pr-hooks.js +119 -0
- package/dist/components/pr-pipeline.d.ts +72 -2
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +96 -5
- package/dist/components/pr-policy.d.ts +109 -0
- package/dist/components/pr-policy.d.ts.map +1 -0
- package/dist/components/pr-policy.js +150 -0
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/pr-run.js +5 -3
- package/dist/gate-decision.d.ts +15 -2
- package/dist/gate-decision.d.ts.map +1 -1
- package/dist/gate-decision.js +17 -2
- package/dist/gate-policy.d.ts +140 -0
- package/dist/gate-policy.d.ts.map +1 -0
- package/dist/gate-policy.js +242 -0
- package/dist/lifecycle/gate-checkpoint.d.ts +1 -1
- package/dist/lifecycle/gate-checkpoint.js +1 -1
- package/dist/{workspace/trust → lifecycle}/gate-seal.d.ts +1 -1
- package/dist/lifecycle/gate-seal.d.ts.map +1 -0
- package/dist/lifecycle/query.d.ts +68 -0
- package/dist/lifecycle/query.d.ts.map +1 -0
- package/dist/lifecycle/query.js +246 -0
- package/dist/lifecycle/run-ledger.d.ts +13 -0
- package/dist/lifecycle/run-ledger.d.ts.map +1 -1
- package/dist/lifecycle/run-ledger.js +16 -0
- package/dist/lifecycle/target-lock.d.ts +97 -0
- package/dist/lifecycle/target-lock.d.ts.map +1 -0
- package/dist/lifecycle/target-lock.js +286 -0
- package/dist/op/activities/activity-contracts.d.ts +30 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.js +26 -0
- package/dist/op/activities/index.d.ts +2 -0
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/index.js +4 -0
- package/dist/op/activities/review-agent.d.ts +137 -0
- package/dist/op/activities/review-agent.d.ts.map +1 -0
- package/dist/op/activities/review-agent.js +266 -0
- package/dist/op/builders.d.ts +9 -0
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/builders.js +11 -0
- package/dist/op/cron.d.ts +8 -0
- package/dist/op/cron.d.ts.map +1 -1
- package/dist/op/cron.js +51 -0
- package/dist/op/gate-approval.d.ts +20 -13
- package/dist/op/gate-approval.d.ts.map +1 -1
- package/dist/op/gate-approval.js +13 -18
- package/dist/op/gate-quorum.d.ts +131 -0
- package/dist/op/gate-quorum.d.ts.map +1 -0
- package/dist/op/gate-quorum.js +183 -0
- package/dist/op/gate-resume.d.ts +4 -2
- package/dist/op/gate-resume.d.ts.map +1 -1
- package/dist/op/gate-resume.js +5 -4
- package/dist/op/gate-review.js +1 -1
- package/dist/op/gate.d.ts +32 -4
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/gate.js +40 -24
- package/dist/op/index.d.ts +6 -4
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/index.js +4 -3
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-executor.js +9 -3
- package/dist/op/op-waves-run.d.ts +62 -1
- package/dist/op/op-waves-run.d.ts.map +1 -1
- package/dist/op/op-waves-run.js +190 -11
- package/dist/op/op-waves.d.ts +55 -3
- package/dist/op/op-waves.d.ts.map +1 -1
- package/dist/op/op-waves.js +63 -8
- package/dist/op/schedule-overdue.d.ts +57 -0
- package/dist/op/schedule-overdue.d.ts.map +1 -0
- package/dist/op/schedule-overdue.js +138 -0
- package/dist/override-gate.d.ts +2 -2
- package/dist/override-gate.d.ts.map +1 -1
- package/dist/override-gate.js +1 -1
- package/dist/pr-forge.d.ts +36 -0
- package/dist/pr-forge.d.ts.map +1 -1
- package/dist/pr-forge.js +82 -0
- package/dist/pr-loop.d.ts +90 -1
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +77 -3
- package/dist/pr-post.d.ts +72 -0
- package/dist/pr-post.d.ts.map +1 -0
- package/dist/pr-post.js +134 -0
- package/dist/wave-blocked.d.ts +79 -0
- package/dist/wave-blocked.d.ts.map +1 -0
- package/dist/wave-blocked.js +245 -0
- package/dist/wave-engine.d.ts +6 -0
- package/dist/wave-engine.d.ts.map +1 -1
- package/dist/wave-engine.js +2 -0
- package/dist/wave-runtime.mjs +242 -242
- package/dist/workspace/__fixtures__/two-member-workspace.d.ts +1 -0
- package/dist/workspace/__fixtures__/two-member-workspace.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/two-member-workspace.js +1 -1
- package/dist/workspace/ci-branches.d.ts +51 -0
- package/dist/workspace/ci-branches.d.ts.map +1 -0
- package/dist/workspace/ci-branches.js +69 -0
- package/dist/workspace/ci-cloud-identity.d.ts +11 -0
- package/dist/workspace/ci-cloud-identity.d.ts.map +1 -0
- package/dist/workspace/ci-cloud-identity.js +24 -0
- package/dist/workspace/ci-locks.d.ts +12 -0
- package/dist/workspace/ci-locks.d.ts.map +1 -0
- package/dist/workspace/ci-locks.js +6 -0
- package/dist/workspace/ci-run.d.ts +17 -1
- package/dist/workspace/ci-run.d.ts.map +1 -1
- package/dist/workspace/ci-run.js +64 -3
- package/dist/workspace/declaration.d.ts +63 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +72 -1
- package/dist/workspace/declaration.schema.json +147 -0
- package/dist/workspace/identity-gate-rule.d.ts +1 -1
- package/dist/workspace/identity-gate-rule.d.ts.map +1 -1
- package/dist/workspace/identity-gate-rule.js +1 -1
- package/dist/workspace/identity.d.ts +1 -1
- package/dist/workspace/identity.d.ts.map +1 -1
- package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
- package/dist/workspace/lineage-upgrade.js +8 -5
- package/dist/workspace/member-pipeline.d.ts +6 -0
- package/dist/workspace/member-pipeline.d.ts.map +1 -1
- package/dist/workspace/member-pipeline.js +23 -0
- package/dist/workspace/pr-report.schema.json +73 -2
- package/dist/workspace/security.d.ts.map +1 -1
- package/dist/workspace/security.js +4 -0
- package/dist/workspace/status-gates.d.ts +3 -2
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/status-gates.js +8 -7
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/status.js +4 -1
- package/dist/workspace/trust/seal.d.ts +2 -2
- package/dist/workspace/trust/seal.d.ts.map +1 -1
- package/dist/workspace/trust/seal.js +2 -2
- package/package.json +1 -1
- package/src/__fixtures__/oidc-emulator.ts +118 -0
- package/src/ci-handover.test.ts +209 -0
- package/src/ci-handover.ts +237 -0
- package/src/ci-identity.test.ts +201 -0
- package/src/ci-identity.ts +310 -0
- package/src/cli/handlers/approve.ts +18 -5
- package/src/cli/handlers/build.ts +15 -1
- package/src/cli/handlers/fan-out.ts +17 -4
- package/src/cli/handlers/gate-resume.ts +16 -2
- package/src/cli/handlers/lock.test.ts +98 -0
- package/src/cli/handlers/lock.ts +202 -0
- package/src/cli/handlers/operator.test.ts +3 -1
- package/src/cli/handlers/pr-env.ts +462 -0
- package/src/cli/handlers/pr-waves-blocked.test.ts +247 -0
- package/src/cli/handlers/pr.test.ts +184 -0
- package/src/cli/handlers/pr.ts +496 -59
- package/src/cli/handlers/promote.ts +9 -0
- package/src/cli/handlers/query.ts +78 -0
- package/src/cli/main.ts +41 -2
- package/src/cli/parse-args.ts +23 -2
- package/src/cli/registry.ts +12 -2
- package/src/components/fan-out-run.ts +39 -2
- package/src/components/fan-out-waves.test.ts +45 -1
- package/src/components/pr-environment.test.ts +249 -0
- package/src/components/pr-environment.ts +538 -0
- package/src/components/pr-hooks.test.ts +118 -0
- package/src/components/pr-hooks.ts +157 -0
- package/src/components/pr-pipeline.ts +130 -5
- package/src/components/pr-policy.test.ts +145 -0
- package/src/components/pr-policy.ts +211 -0
- package/src/components/pr-run.ts +5 -2
- package/src/gate-decision-bundle.test.ts +4 -3
- package/src/gate-decision.ts +39 -2
- package/src/gate-policy-bundle.test.ts +28 -0
- package/src/gate-policy.test.ts +162 -0
- package/src/gate-policy.ts +298 -0
- package/src/lifecycle/gate-checkpoint.ts +1 -1
- package/src/{workspace/trust → lifecycle}/gate-seal.ts +1 -1
- package/src/lifecycle/query.test.ts +91 -0
- package/src/lifecycle/query.ts +315 -0
- package/src/lifecycle/run-ledger.ts +18 -0
- package/src/lifecycle/target-lock.test.ts +195 -0
- package/src/lifecycle/target-lock.ts +347 -0
- package/src/op/activities/activity-contracts.ts +31 -0
- package/src/op/activities/index.ts +6 -0
- package/src/op/activities/review-agent.test.ts +240 -0
- package/src/op/activities/review-agent.ts +355 -0
- package/src/op/builders.ts +13 -0
- package/src/op/cron.ts +44 -0
- package/src/op/gate-alias.test.ts +2 -2
- package/src/op/gate-approval.test.ts +30 -9
- package/src/op/gate-approval.ts +23 -26
- package/src/op/gate-quorum.test.ts +139 -0
- package/src/op/gate-quorum.ts +248 -0
- package/src/op/gate-resume.ts +6 -3
- package/src/op/gate-review.ts +1 -1
- package/src/op/gate.ts +64 -23
- package/src/op/index.ts +13 -1
- package/src/op/local-executor.ts +9 -2
- package/src/op/op-waves-hook-targets.test.ts +126 -0
- package/src/op/op-waves-policy-hold.test.ts +66 -0
- package/src/op/op-waves-quorum.test.ts +101 -0
- package/src/op/op-waves-run.ts +233 -11
- package/src/op/op-waves.ts +83 -10
- package/src/op/schedule-overdue.test.ts +220 -0
- package/src/op/schedule-overdue.ts +161 -0
- package/src/override-gate-bundle.test.ts +1 -1
- package/src/override-gate.ts +2 -2
- package/src/pr-forge-bundle.test.ts +2 -2
- package/src/pr-forge.test.ts +33 -0
- package/src/pr-forge.ts +114 -0
- package/src/pr-loop-bundle.test.ts +2 -2
- package/src/pr-loop.test.ts +58 -0
- package/src/pr-loop.ts +146 -4
- package/src/pr-post.test.ts +141 -0
- package/src/pr-post.ts +159 -0
- package/src/wave-blocked-bundle.test.ts +25 -0
- package/src/wave-blocked.test.ts +146 -0
- package/src/wave-blocked.ts +271 -0
- package/src/wave-engine.ts +8 -0
- package/src/workspace/__fixtures__/two-member-workspace.ts +2 -2
- package/src/workspace/ci-branches.test.ts +62 -0
- package/src/workspace/ci-branches.ts +79 -0
- package/src/workspace/ci-cloud-identity.test.ts +53 -0
- package/src/workspace/ci-cloud-identity.ts +23 -0
- package/src/workspace/ci-locks.ts +16 -0
- package/src/workspace/ci-run-index.test.ts +2 -1
- package/src/workspace/ci-run.test.ts +107 -8
- package/src/workspace/ci-run.ts +72 -4
- package/src/workspace/declaration.schema.json +147 -0
- package/src/workspace/declaration.test.ts +76 -0
- package/src/workspace/declaration.ts +134 -2
- package/src/workspace/identity-gate-rule.ts +1 -1
- package/src/workspace/identity.ts +1 -1
- package/src/workspace/lineage-upgrade.ts +11 -8
- package/src/workspace/member-pipeline.test.ts +29 -0
- package/src/workspace/member-pipeline.ts +30 -0
- package/src/workspace/pr-report.schema.json +73 -2
- package/src/workspace/security.ts +4 -0
- package/src/workspace/status-gates.ts +9 -6
- package/src/workspace/status.ts +4 -1
- package/src/workspace/trust/gate-seal.test.ts +1 -1
- package/src/workspace/trust/seal.ts +2 -2
- package/dist/workspace/trust/gate-seal.d.ts.map +0 -1
- /package/dist/{workspace/trust → lifecycle}/gate-seal.js +0 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A failed member blocks only the members that depend on it (#4291).
|
|
3
|
+
*
|
|
4
|
+
* A run applied a wave at a time stops a member when something it reads did
|
|
5
|
+
* not apply, and lets every other member go on. This module holds the rule
|
|
6
|
+
* and the records it reads, with no runner in it, so a product that runs its
|
|
7
|
+
* waves in its own CI stages (terragucci's roots, lifted from its
|
|
8
|
+
* `blocked.ts`) and chant's own wave jobs (`pr-apply --wave-gate`,
|
|
9
|
+
* `chant components fan-out --wave-gate`) block the same members for the
|
|
10
|
+
* same reasons. It imports `node:fs` and `node:path` and nothing else, so it
|
|
11
|
+
* bundles small (`wave-blocked-bundle.test.ts`).
|
|
12
|
+
*
|
|
13
|
+
* ## Outcomes
|
|
14
|
+
*
|
|
15
|
+
* Each member of an earlier wave has an outcome. `applied` and `unchanged`
|
|
16
|
+
* let a dependent through. Every other status holds it:
|
|
17
|
+
*
|
|
18
|
+
* - `failed`: its plan or its apply failed.
|
|
19
|
+
* - `waiting`: its wave waits for an approval.
|
|
20
|
+
* - `refused`: its wave was refused, held or stood down.
|
|
21
|
+
* - `decided`: its wave's deciding job decided it, and a share job applies it.
|
|
22
|
+
* - `not-applied`: its wave applied nothing because another member of it failed to plan.
|
|
23
|
+
* - `blocked`: something it reads did not apply. `by` names the member whose
|
|
24
|
+
* outcome held it, past any member blocked in between.
|
|
25
|
+
*
|
|
26
|
+
* ## Outcome files
|
|
27
|
+
*
|
|
28
|
+
* A wave job that hands the next job its outcomes as files writes one per
|
|
29
|
+
* job, `outcome-wave-<k>[-group-<g>][-share-<i>].json`:
|
|
30
|
+
*
|
|
31
|
+
* {
|
|
32
|
+
* "version": 1, "wave": 2, "at": "2026-10-18T10:00:09.000Z",
|
|
33
|
+
* "members": {
|
|
34
|
+
* "cluster-b": { "status": "applied" },
|
|
35
|
+
* "cluster-a": { "status": "failed" },
|
|
36
|
+
* "app-01": { "status": "blocked", "by": "cluster-a" }
|
|
37
|
+
* }
|
|
38
|
+
* }
|
|
39
|
+
*
|
|
40
|
+
* A wave job runs again on a re-run, so the newest outcome of a member wins.
|
|
41
|
+
* A file written with `roots` in place of `members` (terragucci's, before
|
|
42
|
+
* this module) reads the same. `holds_all` marks a wave that holds every
|
|
43
|
+
* later member, such as a wave of state migrations that did not apply: each
|
|
44
|
+
* later member is blocked by `wave-<k>`.
|
|
45
|
+
*/
|
|
46
|
+
import { existsSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
47
|
+
import { join } from "node:path";
|
|
48
|
+
export const WAVE_OUTCOME_VERSION = 1;
|
|
49
|
+
const THROUGH = new Set(["applied", "unchanged"]);
|
|
50
|
+
/** Whether `outcome` lets a dependent through: it applied, or had nothing to apply. */
|
|
51
|
+
export function passesThrough(outcome) {
|
|
52
|
+
return outcome !== undefined && THROUGH.has(outcome.status);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The members of `members` an upstream that did not apply holds, each with
|
|
56
|
+
* the member whose outcome holds it. `upstreams` is each member's upstreams;
|
|
57
|
+
* only those in `earlier` count (a member of this wave or a later one is no
|
|
58
|
+
* upstream it waits for here). An upstream with no outcome did not apply as
|
|
59
|
+
* far as this run knows, so it holds its dependents and names itself. A
|
|
60
|
+
* blocked upstream passes on the member that blocked it. With `holdsAll`,
|
|
61
|
+
* every member is blocked by `wave-<holdsAll>`.
|
|
62
|
+
*/
|
|
63
|
+
export function blockedMembers(members, upstreams, earlier, outcomes, holdsAll) {
|
|
64
|
+
if (holdsAll !== undefined)
|
|
65
|
+
return new Map(members.map((m) => [m, `wave-${holdsAll}`]));
|
|
66
|
+
const out = new Map();
|
|
67
|
+
for (const member of members) {
|
|
68
|
+
const by = [...(upstreams.get(member) ?? [])]
|
|
69
|
+
.filter((u) => earlier.has(u))
|
|
70
|
+
.sort()
|
|
71
|
+
.map((u) => {
|
|
72
|
+
const o = outcomes.get(u);
|
|
73
|
+
if (passesThrough(o))
|
|
74
|
+
return undefined;
|
|
75
|
+
return o?.status === "blocked" && o.by ? o.by : u;
|
|
76
|
+
})
|
|
77
|
+
.find((b) => b !== undefined);
|
|
78
|
+
if (by !== undefined)
|
|
79
|
+
out.set(member, by);
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Walk waves 1 to `before - 1` and give each member that has no outcome the
|
|
85
|
+
* one it ends with: `blocked` behind an upstream that did not apply. A member
|
|
86
|
+
* with no outcome that nothing blocks has not run yet: it is in `unsettled`,
|
|
87
|
+
* and wave `before` cannot run until it has. `outcomes` is what the records
|
|
88
|
+
* say: `applied` for what applied, and `failed`, `waiting` or `not-applied`
|
|
89
|
+
* for what did not. Returns those outcomes with the blocked members added.
|
|
90
|
+
*/
|
|
91
|
+
export function settleEarlierWaves(waves, before, upstreams, outcomes) {
|
|
92
|
+
const out = new Map(outcomes);
|
|
93
|
+
const unsettled = [];
|
|
94
|
+
const earlier = new Set();
|
|
95
|
+
for (let k = 1; k < before && k <= waves.length; k++) {
|
|
96
|
+
const wave = waves[k - 1];
|
|
97
|
+
const open = wave.filter((m) => !out.has(m));
|
|
98
|
+
const blocked = blockedMembers(open, upstreams, earlier, out);
|
|
99
|
+
for (const m of open) {
|
|
100
|
+
const by = blocked.get(m);
|
|
101
|
+
if (by !== undefined)
|
|
102
|
+
out.set(m, { status: "blocked", by });
|
|
103
|
+
else
|
|
104
|
+
unsettled.push(m);
|
|
105
|
+
}
|
|
106
|
+
for (const m of wave)
|
|
107
|
+
earlier.add(m);
|
|
108
|
+
}
|
|
109
|
+
return { outcomes: out, unsettled };
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Whether every member that did not apply waits for an approval, or is
|
|
113
|
+
* blocked behind one that does: the run waits rather than failed, so the
|
|
114
|
+
* job ends as waiting (exit 3), and the approval's re-run applies them.
|
|
115
|
+
*/
|
|
116
|
+
export function onlyWaiting(outcomes) {
|
|
117
|
+
let held = false;
|
|
118
|
+
for (const o of outcomes.values()) {
|
|
119
|
+
if (THROUGH.has(o.status))
|
|
120
|
+
continue;
|
|
121
|
+
held = true;
|
|
122
|
+
if (o.status === "waiting")
|
|
123
|
+
continue;
|
|
124
|
+
if (o.status === "blocked" && o.by !== undefined && outcomes.get(o.by)?.status === "waiting")
|
|
125
|
+
continue;
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
return held;
|
|
129
|
+
}
|
|
130
|
+
const byName = ([a], [b]) => (a < b ? -1 : a > b ? 1 : 0);
|
|
131
|
+
/** The blocked members grouped by what blocked them, as a log and a note list them: `app-01, app-02 (blocked by cluster-a)`. */
|
|
132
|
+
export function blockedWords(blocked) {
|
|
133
|
+
const byRoot = new Map();
|
|
134
|
+
for (const [member, by] of [...blocked].sort(byName))
|
|
135
|
+
byRoot.set(by, [...(byRoot.get(by) ?? []), member]);
|
|
136
|
+
return [...byRoot].map(([by, ms]) => `${ms.join(", ")} (blocked by ${by})`).join("; ");
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* How the run came out, from every member's newest outcome: "8 applied, 1
|
|
140
|
+
* failed, 6 blocked", with waiting, refused and the rest named when there
|
|
141
|
+
* are any, and undefined when every member applied or had nothing to apply.
|
|
142
|
+
*/
|
|
143
|
+
export function runSummary(outcomes) {
|
|
144
|
+
const counts = new Map();
|
|
145
|
+
for (const o of outcomes)
|
|
146
|
+
counts.set(o.status, (counts.get(o.status) ?? 0) + 1);
|
|
147
|
+
if (![...counts.keys()].some((s) => !THROUGH.has(s)))
|
|
148
|
+
return undefined;
|
|
149
|
+
const parts = [`${counts.get("applied") ?? 0} applied`, `${counts.get("failed") ?? 0} failed`, `${counts.get("blocked") ?? 0} blocked`];
|
|
150
|
+
for (const s of ["waiting", "refused", "decided", "not-applied"]) {
|
|
151
|
+
if (counts.get(s))
|
|
152
|
+
parts.push(`${counts.get(s)} ${s === "decided" ? "not reported by their share" : s}`);
|
|
153
|
+
}
|
|
154
|
+
if (counts.get("unchanged"))
|
|
155
|
+
parts.push(`${counts.get("unchanged")} unchanged`);
|
|
156
|
+
return parts.join(", ");
|
|
157
|
+
}
|
|
158
|
+
/** The outcome file of a wave's job, or of one group or share of it. */
|
|
159
|
+
export function waveOutcomeFileName(wave, part = {}) {
|
|
160
|
+
const group = part.group !== undefined ? `-group-${part.group.replace(/[^A-Za-z0-9._-]/g, "_")}` : "";
|
|
161
|
+
const share = part.share !== undefined ? `-share-${part.share}` : "";
|
|
162
|
+
return `outcome-wave-${wave}${group}${share}.json`;
|
|
163
|
+
}
|
|
164
|
+
/** Write a wave job's outcome file in `dir`, members sorted by name. Returns its path. */
|
|
165
|
+
export function writeWaveOutcomeFile(dir, wave, part, members, at = new Date().toISOString(), holdsAll = false) {
|
|
166
|
+
const file = join(dir, waveOutcomeFileName(wave, part));
|
|
167
|
+
const body = {
|
|
168
|
+
version: WAVE_OUTCOME_VERSION,
|
|
169
|
+
wave,
|
|
170
|
+
at,
|
|
171
|
+
members: Object.fromEntries(Object.entries(members).sort(byName)),
|
|
172
|
+
...(holdsAll ? { holds_all: true } : {}),
|
|
173
|
+
};
|
|
174
|
+
writeFileSync(file, JSON.stringify(body, null, 2) + "\n");
|
|
175
|
+
return file;
|
|
176
|
+
}
|
|
177
|
+
/** Every outcome file under `dirs`, four directories deep at most: downloaded artifacts keep each job's in a directory of its own. */
|
|
178
|
+
function outcomeFiles(dirs) {
|
|
179
|
+
const out = [];
|
|
180
|
+
const walk = (dir, depth) => {
|
|
181
|
+
let names;
|
|
182
|
+
try {
|
|
183
|
+
names = readdirSync(dir);
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
for (const name of names) {
|
|
189
|
+
const abs = join(dir, name);
|
|
190
|
+
let isDir = false;
|
|
191
|
+
try {
|
|
192
|
+
isDir = statSync(abs).isDirectory();
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (isDir) {
|
|
198
|
+
if (depth < 4)
|
|
199
|
+
walk(abs, depth + 1);
|
|
200
|
+
}
|
|
201
|
+
else if (/^outcome-wave-\d+.*\.json$/.test(name))
|
|
202
|
+
out.push(abs);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
for (const d of dirs)
|
|
206
|
+
if (existsSync(d))
|
|
207
|
+
walk(d, 0);
|
|
208
|
+
return out.sort();
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* The outcome of each member of the waves before `wave`, as their jobs wrote
|
|
212
|
+
* them under `dirs`, and the earliest such wave that holds every later
|
|
213
|
+
* member, if any. For a member more than one job wrote (a re-run, or a split
|
|
214
|
+
* wave's deciding job and the share that applied it), the newest. A file
|
|
215
|
+
* that does not parse, or is of another version, is skipped.
|
|
216
|
+
*/
|
|
217
|
+
export function earlierWaveOutcomes(dirs, wave) {
|
|
218
|
+
const out = new Map();
|
|
219
|
+
const newest = new Map();
|
|
220
|
+
for (const file of outcomeFiles(dirs)) {
|
|
221
|
+
let f;
|
|
222
|
+
try {
|
|
223
|
+
f = JSON.parse(readFileSync(file, "utf-8"));
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
const members = (f?.members ?? f?.roots);
|
|
229
|
+
if (f?.version !== WAVE_OUTCOME_VERSION || !Number.isInteger(f.wave) || f.wave >= wave || typeof f.at !== "string")
|
|
230
|
+
continue;
|
|
231
|
+
if (typeof members !== "object" || members === null)
|
|
232
|
+
continue;
|
|
233
|
+
for (const [member, o] of Object.entries(members)) {
|
|
234
|
+
const had = out.get(member);
|
|
235
|
+
if (had && had.at > f.at)
|
|
236
|
+
continue;
|
|
237
|
+
out.set(member, { ...o, wave: f.wave, at: f.at });
|
|
238
|
+
}
|
|
239
|
+
const n = newest.get(f.wave);
|
|
240
|
+
if (!n || n.at <= f.at)
|
|
241
|
+
newest.set(f.wave, { at: f.at, holdsAll: f.holds_all === true });
|
|
242
|
+
}
|
|
243
|
+
const holding = [...newest].filter(([, n]) => n.holdsAll).map(([k]) => k).sort((a, b) => a - b);
|
|
244
|
+
return { members: out, ...(holding.length ? { holdsAll: holding[0] } : {}) };
|
|
245
|
+
}
|
package/dist/wave-engine.d.ts
CHANGED
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
*/
|
|
28
28
|
import { type GateCheck, type GateLedgerPort } from "./op/gate.js";
|
|
29
29
|
import type { AppliedGateRecord } from "./lifecycle/gate-applied.js";
|
|
30
|
+
import type { ResolvedGateApproval } from "./op/gate-approval.js";
|
|
31
|
+
import type { GateQuorumJudge } from "./op/gate-quorum.js";
|
|
30
32
|
export { WAVE_GATE_NAME_PATTERN, waveGateNameFrom, waveShare } from "./wave-layout.js";
|
|
31
33
|
export { layerWaves, waveGateName, waveDigestMembers, waveSetDigest, readWaveRecords, withWaveRecord, describeChangedWave, type WaveDigestOver, type WaveMember, type WaveNode, type WaveRecord, type WaveStatus, } from "./gated-waves.js";
|
|
32
34
|
/** What a wave asks its gate. */
|
|
@@ -48,6 +50,10 @@ export interface WaveGateRequest {
|
|
|
48
50
|
fields?: Record<string, unknown>;
|
|
49
51
|
/** The pending fact carries `neverOverMcp` (#3821). */
|
|
50
52
|
neverOverMcp?: boolean;
|
|
53
|
+
/** The approval rules the wave takes (#4290), as an `approval` block with a quorum. */
|
|
54
|
+
approval?: ResolvedGateApproval;
|
|
55
|
+
/** Judges approvals for those rules (#4290). */
|
|
56
|
+
judge?: GateQuorumJudge;
|
|
51
57
|
}
|
|
52
58
|
/**
|
|
53
59
|
* Decide a wave's gate: an approval of `digest` under `gate` on `op`'s
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wave-engine.d.ts","sourceRoot":"","sources":["../src/wave-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,OAAO,EAAgB,KAAK,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAC9E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"wave-engine.d.ts","sourceRoot":"","sources":["../src/wave-engine.ts"],"names":[],"mappings":"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"}
|
package/dist/wave-engine.js
CHANGED
|
@@ -47,6 +47,8 @@ export function decideWaveGate(port, request) {
|
|
|
47
47
|
...(request.spent ? { spent: request.spent } : {}),
|
|
48
48
|
...(request.fields ? { fields: request.fields } : {}),
|
|
49
49
|
...(request.neverOverMcp ? { neverOverMcp: true } : {}),
|
|
50
|
+
...(request.approval ? { approval: request.approval } : {}),
|
|
51
|
+
...(request.judge ? { judge: request.judge } : {}),
|
|
50
52
|
});
|
|
51
53
|
}
|
|
52
54
|
/**
|