@intentius/chant 0.104.2 → 0.104.3
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/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/mcp/op-tools.d.ts.map +1 -1
- package/dist/components/capability.d.ts +15 -0
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/fan-out-record.d.ts +10 -0
- package/dist/components/fan-out-record.d.ts.map +1 -1
- package/dist/components/fan-out-run.d.ts +11 -0
- package/dist/components/fan-out-run.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +6 -0
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cli/handlers/fan-out.ts +15 -0
- package/src/cli/handlers/operator.ts +10 -0
- package/src/cli/mcp/never-over-mcp.test.ts +68 -0
- package/src/cli/mcp/op-tools.ts +15 -3
- package/src/components/capability.ts +15 -0
- package/src/components/fan-out-record.test.ts +27 -0
- package/src/components/fan-out-record.ts +11 -0
- package/src/components/fan-out-run.ts +22 -2
- package/src/components/fan-out-waves.test.ts +50 -0
- package/src/lifecycle/gate-ledger.ts +10 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fan-out.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/fan-out.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAIH,OAAO,EAA+C,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAa7F,OAAO,EAAmB,KAAK,YAAY,EAAuB,MAAM,0BAA0B,CAAC;AAMnG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,WAAW,EACnB,IAAI,GAAE;IACJ,yFAAyF;IACzF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wFAAwF;IACxF,UAAU,CAAC,EAAE,OAAO,CAAC;CACjB,GACL,OAAO,CAAC;IAAE,KAAK,EAAE,YAAY,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA+ErE;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"fan-out.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/fan-out.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAIH,OAAO,EAA+C,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAa7F,OAAO,EAAmB,KAAK,YAAY,EAAuB,MAAM,0BAA0B,CAAC;AAMnG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,WAAW,EACnB,IAAI,GAAE;IACJ,yFAAyF;IACzF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wFAAwF;IACxF,UAAU,CAAC,EAAE,OAAO,CAAC;CACjB,GACL,OAAO,CAAC;IAAE,KAAK,EAAE,YAAY,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA+ErE;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAgR9E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operator.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/operator.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"operator.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/operator.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"op-tools.d.ts","sourceRoot":"","sources":["../../../src/cli/mcp/op-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;
|
|
1
|
+
{"version":3,"file":"op-tools.d.ts","sourceRoot":"","sources":["../../../src/cli/mcp/op-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAWH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AA8D1D,wBAAgB,gBAAgB,IAAI,gBAAgB,CAsCnD;AAED,wBAAgB,eAAe,IAAI,gBAAgB,CA0ClD;AAED,wBAAgB,kBAAkB,IAAI,gBAAgB,CAoCrD;AAED,wBAAgB,mBAAmB,IAAI,gBAAgB,CA6DtD;AAED,wBAAgB,kBAAkB,IAAI,gBAAgB,CAkDrD"}
|
|
@@ -44,6 +44,21 @@ export interface DeployContext {
|
|
|
44
44
|
* approved, instead of planning again.
|
|
45
45
|
*/
|
|
46
46
|
plans?: Record<string, unknown>;
|
|
47
|
+
/**
|
|
48
|
+
* What a step kept for a member in an earlier attempt at this same fan-out
|
|
49
|
+
* (#3459), keyed by member: the value it last passed to {@link carry}. A
|
|
50
|
+
* choudoufu root keeps choudoufu's wave resume file here, so the next
|
|
51
|
+
* attempt hands choudoufu the file it wrote. Set by `chant components
|
|
52
|
+
* fan-out --resume`; absent otherwise.
|
|
53
|
+
*/
|
|
54
|
+
carried?: Record<string, unknown>;
|
|
55
|
+
/**
|
|
56
|
+
* Keep `value` for `member` in the fan-out's attempt record, replacing what
|
|
57
|
+
* was kept before. The next attempt finds it in {@link carried}. Call it
|
|
58
|
+
* before throwing, too: a failed root's state is what a retry needs most.
|
|
59
|
+
* Absent outside a fan-out that writes a record.
|
|
60
|
+
*/
|
|
61
|
+
carry?: (member: string, value: unknown) => void;
|
|
47
62
|
}
|
|
48
63
|
/**
|
|
49
64
|
* What a capability's `plan` produced (#3049): the member it planned, that
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capability.d.ts","sourceRoot":"","sources":["../../src/components/capability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,+DAA+D;IAC/D,GAAG,EAAE,MAAM,CAAC;IACZ,mEAAmE;IACnE,SAAS,EAAE,MAAM,CAAC;IAClB,qGAAqG;IACrG,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"capability.d.ts","sourceRoot":"","sources":["../../src/components/capability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,+DAA+D;IAC/D,GAAG,EAAE,MAAM,CAAC;IACZ,mEAAmE;IACnE,SAAS,EAAE,MAAM,CAAC;IAClB,qGAAqG;IACrG,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAClD;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,uFAAuF;IACvF,MAAM,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,aAAa,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,UAAU,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,GAAG,OAAO;IACrD,uGAAuG;IACvG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,6BAA6B;IAC7B,GAAG,CAAC,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACjD;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,CAAC,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE;;;;;;;OAOG;IACH,IAAI,CAAC,CAAC,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAC9D;;;;;OAKG;IACH,OAAO,CAAC,CAAC,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1E;;;;;;;;OAQG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC;CAC1C;AAED,iFAAiF;AACjF,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,gBAAgB,GAAG,eAAe,CAAC;AAE3E,wCAAwC;AACxC,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI,CAAC,SAAS,UAAU,CAAC,MAAM,EAAE,EAAE,OAAO,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;AACtF,yCAAyC;AACzC,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,CAAC,SAAS,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,CAAC,GAAG,GAAG,GAAG,KAAK,CAAC;AAEzF;;;;GAIG;AACH,qBAAa,6BAA8B,SAAQ,KAAK;aAC1B,IAAI,EAAE,MAAM;IAAxC,YAA4B,IAAI,EAAE,MAAM,EAGvC;CACF;AAED;;;;GAIG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAiD;IAE9E,+GAA+G;IAC/G,QAAQ,CAAC,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,UAAU,CAAC,EAAE,EAAE,GAAG,CAAC,GAAG,IAAI,CAMvD;IAED,6FAA6F;IAC7F,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,CAOhD;IAED,qDAAqD;IACrD,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzB;IAED,oCAAoC;IACpC,KAAK,IAAI,MAAM,EAAE,CAEhB;CACF"}
|
|
@@ -21,6 +21,10 @@
|
|
|
21
21
|
* a wave afresh rather than trusting this, since the point of the gate is the
|
|
22
22
|
* plan now. The entries are for whoever reads the record, the plan report
|
|
23
23
|
* (#3349) among them.
|
|
24
|
+
*
|
|
25
|
+
* `carried` holds what a step asked to keep for its member (#3459). It rides
|
|
26
|
+
* in the record rather than beside it because the record is the one file a
|
|
27
|
+
* per-wave CI job hands to the next.
|
|
24
28
|
*/
|
|
25
29
|
import { type WaveRecord } from "../gated-waves.js";
|
|
26
30
|
export interface FanOutAttempt {
|
|
@@ -30,6 +34,12 @@ export interface FanOutAttempt {
|
|
|
30
34
|
outputs: Record<string, Record<string, unknown>>;
|
|
31
35
|
/** Gated-wave fan-outs only (#3049). */
|
|
32
36
|
waves?: WaveRecord[];
|
|
37
|
+
/**
|
|
38
|
+
* What steps kept per member through `DeployContext.carry` (#3459), handed
|
|
39
|
+
* back as `DeployContext.carried` on the next attempt. For a choudoufu
|
|
40
|
+
* root, choudoufu's wave resume file.
|
|
41
|
+
*/
|
|
42
|
+
carried?: Record<string, unknown>;
|
|
33
43
|
}
|
|
34
44
|
/** Read an attempt record, or `undefined` when there is none yet. Throws on a file that is not JSON. */
|
|
35
45
|
export declare function readFanOutAttempt(path: string): FanOutAttempt | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fan-out-record.d.ts","sourceRoot":"","sources":["../../src/components/fan-out-record.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"fan-out-record.d.ts","sourceRoot":"","sources":["../../src/components/fan-out-record.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAIH,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAElE,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACjD,wCAAwC;IACxC,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IACrB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAKD,wGAAwG;AACxG,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAgBzE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI,CAK7E"}
|
|
@@ -108,6 +108,17 @@ export interface FanOutRunOptions {
|
|
|
108
108
|
onWaveSettled?: (record: WaveRecord) => void;
|
|
109
109
|
/** What an earlier attempt at this same plan already did. */
|
|
110
110
|
progress?: FanOutProgress;
|
|
111
|
+
/**
|
|
112
|
+
* What steps kept per member in an earlier attempt (#3459), handed to every
|
|
113
|
+
* step as `DeployContext.carried`. Read from the attempt record.
|
|
114
|
+
*/
|
|
115
|
+
carried?: Record<string, unknown>;
|
|
116
|
+
/**
|
|
117
|
+
* Called when a step keeps a value for its member (`DeployContext.carry`),
|
|
118
|
+
* so the caller can write it into the attempt record at once. Without it,
|
|
119
|
+
* steps get no `carry`.
|
|
120
|
+
*/
|
|
121
|
+
onCarry?: (member: string, value: unknown) => void;
|
|
111
122
|
/** The release each component deploys (#3061), as `InterpretRunOptions.releaseIdentity`. */
|
|
112
123
|
releaseIdentity?: (component: string) => ReleaseIdentity | undefined;
|
|
113
124
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fan-out-run.d.ts","sourceRoot":"","sources":["../../src/components/fan-out-run.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"fan-out-run.d.ts","sourceRoot":"","sources":["../../src/components/fan-out-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EAGL,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAE3B,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAqC,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AACrH,OAAO,EAAmC,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAgD,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE/F;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,mEAAmE;IACnE,EAAE,EAAE,MAAM,CAAC;IACX,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,aAAa,CAAC,EAAE,CACd,SAAS,EAAE,eAAe,EAC1B,GAAG,EAAE,aAAa,EAClB,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,KACtD,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAChC;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,8FAA8F;AAC9F,qBAAa,0BAA2B,SAAQ,KAAK;IAEjD,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE;IAH/B,YACW,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAAE,EAO9B;CACF;AAED,wEAAwE;AACxE,MAAM,WAAW,UAAU;IACzB,0FAA0F;IAC1F,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,yFAAyF;IACzF,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3D,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC/C,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAChD,qFAAqF;IACrF,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;IAC7C,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnD,4FAA4F;IAC5F,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,eAAe,GAAG,SAAS,CAAC;IACrE;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,KAAK,IAAI,CAAC;CAC5G;AAED,MAAM,WAAW,eAAe;IAC9B,4EAA4E;IAC5E,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,IAAI,GAAG,MAAM,GAAG,OAAO,CAAC;IAChC,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACjC,8EAA8E;IAC9E,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,2DAA2D;IAC3D,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,mFAAmF;IACnF,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1D,sEAAsE;IACtE,IAAI,CAAC,EAAE,iBAAiB,CAAC;IACzB,iGAAiG;IACjG,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;CACtB;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,UAAU,EAChB,UAAU,EAAE,eAAe,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,eAAe,CAAC,CA2I1B"}
|
|
@@ -195,6 +195,12 @@ export interface PendingGateRecord {
|
|
|
195
195
|
* and resolved over MCP has one author, not two.
|
|
196
196
|
*/
|
|
197
197
|
origin?: GateOrigin;
|
|
198
|
+
/**
|
|
199
|
+
* The gate is never resolved over MCP or ACP (chant#3485), whichever channel
|
|
200
|
+
* reached it. The declaration travels on the record because an Op that MCP
|
|
201
|
+
* cannot discover, such as `fan-out`, has no config to read it from.
|
|
202
|
+
*/
|
|
203
|
+
neverOverMcp?: true;
|
|
198
204
|
/**
|
|
199
205
|
* The gate's quorum and policy (#2508), with its context resolved against
|
|
200
206
|
* this run. `chant approve` reads it to evaluate the policy against the plan
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gate-ledger.d.ts","sourceRoot":"","sources":["../../src/lifecycle/gate-ledger.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"gate-ledger.d.ts","sourceRoot":"","sources":["../../src/lifecycle/gate-ledger.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -254,6 +254,7 @@ export async function runComponentsFanOut(ctx: CommandContext): Promise<number>
|
|
|
254
254
|
let priorCompleted: string[] = [];
|
|
255
255
|
let priorOutputs: Record<string, Record<string, unknown>> = {};
|
|
256
256
|
let priorWaves: WaveRecord[] = [];
|
|
257
|
+
let priorCarried: Record<string, unknown> = {};
|
|
257
258
|
let progress: FanOutProgress | undefined;
|
|
258
259
|
if (resumePath) {
|
|
259
260
|
let attempt: FanOutAttempt | undefined;
|
|
@@ -280,6 +281,9 @@ export async function runComponentsFanOut(ctx: CommandContext): Promise<number>
|
|
|
280
281
|
);
|
|
281
282
|
progress = { completed: attempt.completed };
|
|
282
283
|
priorWaves = attempt.waves ?? [];
|
|
284
|
+
// What steps kept per member (#3459), such as a choudoufu root's wave
|
|
285
|
+
// resume file. Each step decides whether what it kept still applies.
|
|
286
|
+
priorCarried = attempt.carried ?? {};
|
|
283
287
|
}
|
|
284
288
|
}
|
|
285
289
|
|
|
@@ -316,6 +320,7 @@ export async function runComponentsFanOut(ctx: CommandContext): Promise<number>
|
|
|
316
320
|
const failedSoFar: string[] = [];
|
|
317
321
|
const outputsSoFar = { ...priorOutputs };
|
|
318
322
|
let wavesSoFar = priorWaves;
|
|
323
|
+
const carriedSoFar: Record<string, unknown> = { ...priorCarried };
|
|
319
324
|
const recordAttempt = (): void => {
|
|
320
325
|
if (!resumePath) return;
|
|
321
326
|
const attempt: FanOutAttempt = {
|
|
@@ -324,6 +329,7 @@ export async function runComponentsFanOut(ctx: CommandContext): Promise<number>
|
|
|
324
329
|
failed: [...failedSoFar].sort(),
|
|
325
330
|
outputs: outputsSoFar,
|
|
326
331
|
...(wavesSoFar.length > 0 ? { waves: wavesSoFar } : {}),
|
|
332
|
+
...(Object.keys(carriedSoFar).length > 0 ? { carried: carriedSoFar } : {}),
|
|
327
333
|
};
|
|
328
334
|
writeFanOutAttempt(resumePath, attempt);
|
|
329
335
|
};
|
|
@@ -348,6 +354,15 @@ export async function runComponentsFanOut(ctx: CommandContext): Promise<number>
|
|
|
348
354
|
}
|
|
349
355
|
: {}),
|
|
350
356
|
...(progress ? { progress } : {}),
|
|
357
|
+
...(Object.keys(priorCarried).length > 0 ? { carried: priorCarried } : {}),
|
|
358
|
+
...(resumePath
|
|
359
|
+
? {
|
|
360
|
+
onCarry: (member: string, value: unknown) => {
|
|
361
|
+
carriedSoFar[member] = value;
|
|
362
|
+
recordAttempt();
|
|
363
|
+
},
|
|
364
|
+
}
|
|
365
|
+
: {}),
|
|
351
366
|
...(args.progressJson ? { onProgress: ndjsonProgressSink() } : {}),
|
|
352
367
|
...(resumePath
|
|
353
368
|
? {
|
|
@@ -1104,6 +1104,16 @@ export async function recordGateApproval(
|
|
|
1104
1104
|
const origin = opts.origin ?? currentGateOrigin();
|
|
1105
1105
|
const ledger = await readGateLedger(opName);
|
|
1106
1106
|
const standingForOrigin = latestPendingGate(pendingFor(ledger.pending), gate);
|
|
1107
|
+
|
|
1108
|
+
// chant#3485: a gate that is never resolved over MCP or ACP says so on its
|
|
1109
|
+
// own record, or on its Op's config. No flag overrides this one.
|
|
1110
|
+
if (isModelAuthored(origin) && (standingForOrigin?.neverOverMcp || ops.get(opName)?.config.neverOverMcp)) {
|
|
1111
|
+
console.error(formatError({
|
|
1112
|
+
message: `Gate "${gate}" on "${opName}" is never resolved over ${origin === "acp" ? "ACP" : "MCP"}`,
|
|
1113
|
+
hint: `Approvals belong to people: \`chant approve ${opName} ${gate}\` at a shell.`,
|
|
1114
|
+
}));
|
|
1115
|
+
return { ok: false };
|
|
1116
|
+
}
|
|
1107
1117
|
const refusal = sameOriginRefusal(standingForOrigin?.origin, origin);
|
|
1108
1118
|
if (refusal && !opts.allowSameOrigin) {
|
|
1109
1119
|
console.error(formatError({
|
|
@@ -32,6 +32,7 @@ vi.mock("../../op/runtimes/local", () => ({
|
|
|
32
32
|
}),
|
|
33
33
|
}));
|
|
34
34
|
|
|
35
|
+
import { recordGateApproval } from "../handlers/operator";
|
|
35
36
|
import { createOpApproveTool, createOpRunTool, createOpStatusTool, createOpReportTool } from "./op-tools";
|
|
36
37
|
|
|
37
38
|
function git(args: string[], cwd: string): void {
|
|
@@ -98,4 +99,71 @@ describe("neverOverMcp (chant#3447)", () => {
|
|
|
98
99
|
await expect(createOpStatusTool().handler({ name: "tf-apply" })).resolves.toMatchObject({ op: "tf-apply" });
|
|
99
100
|
await expect(createOpReportTool().handler({ name: "tf-apply" })).resolves.toContain("# tf-apply");
|
|
100
101
|
});
|
|
102
|
+
|
|
103
|
+
async function repoWithFanOutGate(dir: string): Promise<void> {
|
|
104
|
+
git(["init", "-q", "-b", "main"], dir);
|
|
105
|
+
git(["config", "user.email", "t@chant.dev"], dir);
|
|
106
|
+
git(["config", "user.name", "T"], dir);
|
|
107
|
+
writeFileSync(join(dir, "README.md"), "x\n");
|
|
108
|
+
git(["add", "."], dir);
|
|
109
|
+
git(["commit", "-q", "-m", "init"], dir);
|
|
110
|
+
// fan-out is not discovered, and its gate was reached at a shell.
|
|
111
|
+
await appendPendingGate(
|
|
112
|
+
{
|
|
113
|
+
op: "fan-out",
|
|
114
|
+
gate: "wave-2",
|
|
115
|
+
timestamp: "2026-01-01T00:00:00.000Z",
|
|
116
|
+
origin: "cli",
|
|
117
|
+
expiresAt: "2999-01-01T00:00:00.000Z",
|
|
118
|
+
planDigest: `sha256:${"b".repeat(64)}`,
|
|
119
|
+
},
|
|
120
|
+
{ cwd: dir },
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
test("an undiscovered Op's gate carries the declaration (chant#3485)", async () => {
|
|
125
|
+
await withTestDir(async (dir) => {
|
|
126
|
+
await repoWithFanOutGate(dir);
|
|
127
|
+
const { pending } = await readGateLedger("fan-out", { cwd: dir });
|
|
128
|
+
expect(pending[0].neverOverMcp).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("op-approve refuses an undiscovered Op's gate and the ledger is unchanged (chant#3485)", async () => {
|
|
133
|
+
await withTestDir(async (dir) => {
|
|
134
|
+
await repoWithFanOutGate(dir);
|
|
135
|
+
const before = await readGateLedger("fan-out", { cwd: dir });
|
|
136
|
+
process.chdir(dir);
|
|
137
|
+
await expect(createOpApproveTool().handler({ name: "fan-out", gate: "wave-2" })).rejects.toThrow(
|
|
138
|
+
/neverOverMcp.*whichever channel/s,
|
|
139
|
+
);
|
|
140
|
+
expect(await readGateLedger("fan-out", { cwd: dir })).toEqual(before);
|
|
141
|
+
expect(resolveGate).not.toHaveBeenCalled();
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("ACP refuses the same gate, even with the same-origin override (chant#3485)", async () => {
|
|
146
|
+
await withTestDir(async (dir) => {
|
|
147
|
+
await repoWithFanOutGate(dir);
|
|
148
|
+
const before = await readGateLedger("fan-out", { cwd: dir });
|
|
149
|
+
process.chdir(dir);
|
|
150
|
+
const outcome = await recordGateApproval("fan-out", "wave-2", {
|
|
151
|
+
origin: "acp",
|
|
152
|
+
actor: "agent",
|
|
153
|
+
allowSameOrigin: true,
|
|
154
|
+
cwd: dir,
|
|
155
|
+
});
|
|
156
|
+
expect(outcome.ok).toBe(false);
|
|
157
|
+
expect(await readGateLedger("fan-out", { cwd: dir })).toEqual(before);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("a person at a shell can still approve the gate (chant#3485)", async () => {
|
|
162
|
+
await withTestDir(async (dir) => {
|
|
163
|
+
await repoWithFanOutGate(dir);
|
|
164
|
+
process.chdir(dir);
|
|
165
|
+
const outcome = await recordGateApproval("fan-out", "wave-2", { origin: "cli", actor: "alex", cwd: dir });
|
|
166
|
+
expect(outcome.ok).toBe(true);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
101
169
|
});
|
package/src/cli/mcp/op-tools.ts
CHANGED
|
@@ -21,6 +21,7 @@ import { createLocalOpRuntime } from "../../op/runtimes/local";
|
|
|
21
21
|
import type { OpRuntimeProvider } from "../../op/runtime";
|
|
22
22
|
import { loadChantConfig } from "../../config";
|
|
23
23
|
import { loadPlugins } from "../plugins";
|
|
24
|
+
import { readGateLedger, latestPendingGate } from "../../lifecycle/gate-ledger";
|
|
24
25
|
import { recordGateApproval } from "../handlers/operator";
|
|
25
26
|
import type { ToolRegistration } from "./lifecycle-tools";
|
|
26
27
|
|
|
@@ -59,9 +60,20 @@ async function runtimeFor(name: unknown): Promise<OpRuntimeProvider> {
|
|
|
59
60
|
* shell. An Op that is not discovered is left to the caller's own not-found
|
|
60
61
|
* handling.
|
|
61
62
|
*/
|
|
62
|
-
async function neverOverMcpRefusal(
|
|
63
|
+
async function neverOverMcpRefusal(
|
|
64
|
+
name: string,
|
|
65
|
+
action: "run" | "approve",
|
|
66
|
+
gate?: string,
|
|
67
|
+
): Promise<string | undefined> {
|
|
63
68
|
const { ops } = await discoverOps();
|
|
64
|
-
|
|
69
|
+
let declared = Boolean(ops.get(name)?.config.neverOverMcp);
|
|
70
|
+
// An Op that is not discovered (fan-out) declares nothing, so its gate
|
|
71
|
+
// records the rule itself (chant#3485).
|
|
72
|
+
if (!declared && action === "approve" && gate !== undefined) {
|
|
73
|
+
const { pending } = await readGateLedger(name).catch(() => ({ pending: [] }));
|
|
74
|
+
declared = Boolean(latestPendingGate(pending, gate)?.neverOverMcp);
|
|
75
|
+
}
|
|
76
|
+
if (!declared) return undefined;
|
|
65
77
|
return action === "run"
|
|
66
78
|
? `Op "${name}" declares neverOverMcp, so it is not run over MCP. Run it at a shell with \`chant run ${name}\`; op-status and op-report still answer here.`
|
|
67
79
|
: `Op "${name}" declares neverOverMcp, so its gates are not resolved over MCP, whichever channel reached them. ` +
|
|
@@ -225,7 +237,7 @@ export function createOpApproveTool(): ToolRegistration {
|
|
|
225
237
|
const gate = params.gate as string;
|
|
226
238
|
if (!name || !gate) throw new Error("op-approve needs both an Op name and a gate name");
|
|
227
239
|
|
|
228
|
-
const refusal = await neverOverMcpRefusal(name, "approve");
|
|
240
|
+
const refusal = await neverOverMcpRefusal(name, "approve", gate);
|
|
229
241
|
if (refusal) throw new Error(refusal);
|
|
230
242
|
|
|
231
243
|
const runtime = await runtimeFor(params.runtime);
|
|
@@ -46,6 +46,21 @@ export interface DeployContext {
|
|
|
46
46
|
* approved, instead of planning again.
|
|
47
47
|
*/
|
|
48
48
|
plans?: Record<string, unknown>;
|
|
49
|
+
/**
|
|
50
|
+
* What a step kept for a member in an earlier attempt at this same fan-out
|
|
51
|
+
* (#3459), keyed by member: the value it last passed to {@link carry}. A
|
|
52
|
+
* choudoufu root keeps choudoufu's wave resume file here, so the next
|
|
53
|
+
* attempt hands choudoufu the file it wrote. Set by `chant components
|
|
54
|
+
* fan-out --resume`; absent otherwise.
|
|
55
|
+
*/
|
|
56
|
+
carried?: Record<string, unknown>;
|
|
57
|
+
/**
|
|
58
|
+
* Keep `value` for `member` in the fan-out's attempt record, replacing what
|
|
59
|
+
* was kept before. The next attempt finds it in {@link carried}. Call it
|
|
60
|
+
* before throwing, too: a failed root's state is what a retry needs most.
|
|
61
|
+
* Absent outside a fan-out that writes a record.
|
|
62
|
+
*/
|
|
63
|
+
carry?: (member: string, value: unknown) => void;
|
|
49
64
|
}
|
|
50
65
|
|
|
51
66
|
/**
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The attempt record keeps what steps carried per member (#3459), so the
|
|
3
|
+
* next attempt, in this process or in the next CI job, hands it back.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, expect, test } from "vitest";
|
|
7
|
+
import { mkdtempSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { readFanOutAttempt, writeFanOutAttempt } from "./fan-out-record";
|
|
11
|
+
|
|
12
|
+
describe("carried", () => {
|
|
13
|
+
test("round-trips through the record", () => {
|
|
14
|
+
const path = join(mkdtempSync(join(tmpdir(), "fan-out-record-")), "fan-out.json");
|
|
15
|
+
const carried = { e01: { kind: "choudoufu-wave-resume", setDigest: "sha256:set", resume: { set_digest: "sha256:set", roots: [] } } };
|
|
16
|
+
writeFanOutAttempt(path, { digest: "d", completed: [], failed: ["e01"], outputs: {}, carried });
|
|
17
|
+
expect(readFanOutAttempt(path)?.carried).toEqual(carried);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("a record without it, or with something that is not an object, reads as none", () => {
|
|
21
|
+
const dir = mkdtempSync(join(tmpdir(), "fan-out-record-"));
|
|
22
|
+
writeFileSync(join(dir, "a.json"), JSON.stringify({ digest: "d", completed: [], failed: [], outputs: {} }));
|
|
23
|
+
writeFileSync(join(dir, "b.json"), JSON.stringify({ digest: "d", completed: [], failed: [], outputs: {}, carried: ["x"] }));
|
|
24
|
+
expect(readFanOutAttempt(join(dir, "a.json"))?.carried).toBeUndefined();
|
|
25
|
+
expect(readFanOutAttempt(join(dir, "b.json"))?.carried).toBeUndefined();
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -21,6 +21,10 @@
|
|
|
21
21
|
* a wave afresh rather than trusting this, since the point of the gate is the
|
|
22
22
|
* plan now. The entries are for whoever reads the record, the plan report
|
|
23
23
|
* (#3349) among them.
|
|
24
|
+
*
|
|
25
|
+
* `carried` holds what a step asked to keep for its member (#3459). It rides
|
|
26
|
+
* in the record rather than beside it because the record is the one file a
|
|
27
|
+
* per-wave CI job hands to the next.
|
|
24
28
|
*/
|
|
25
29
|
|
|
26
30
|
import { dirname } from "node:path";
|
|
@@ -34,6 +38,12 @@ export interface FanOutAttempt {
|
|
|
34
38
|
outputs: Record<string, Record<string, unknown>>;
|
|
35
39
|
/** Gated-wave fan-outs only (#3049). */
|
|
36
40
|
waves?: WaveRecord[];
|
|
41
|
+
/**
|
|
42
|
+
* What steps kept per member through `DeployContext.carry` (#3459), handed
|
|
43
|
+
* back as `DeployContext.carried` on the next attempt. For a choudoufu
|
|
44
|
+
* root, choudoufu's wave resume file.
|
|
45
|
+
*/
|
|
46
|
+
carried?: Record<string, unknown>;
|
|
37
47
|
}
|
|
38
48
|
|
|
39
49
|
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
@@ -54,6 +64,7 @@ export function readFanOutAttempt(path: string): FanOutAttempt | undefined {
|
|
|
54
64
|
failed: Array.isArray(parsed.failed) ? parsed.failed.filter((n): n is string => typeof n === "string") : [],
|
|
55
65
|
outputs,
|
|
56
66
|
...(waves.length > 0 ? { waves } : {}),
|
|
67
|
+
...(isRecord(parsed.carried) && Object.keys(parsed.carried).length > 0 ? { carried: parsed.carried } : {}),
|
|
57
68
|
};
|
|
58
69
|
}
|
|
59
70
|
|
|
@@ -130,6 +130,17 @@ export interface FanOutRunOptions {
|
|
|
130
130
|
onWaveSettled?: (record: WaveRecord) => void;
|
|
131
131
|
/** What an earlier attempt at this same plan already did. */
|
|
132
132
|
progress?: FanOutProgress;
|
|
133
|
+
/**
|
|
134
|
+
* What steps kept per member in an earlier attempt (#3459), handed to every
|
|
135
|
+
* step as `DeployContext.carried`. Read from the attempt record.
|
|
136
|
+
*/
|
|
137
|
+
carried?: Record<string, unknown>;
|
|
138
|
+
/**
|
|
139
|
+
* Called when a step keeps a value for its member (`DeployContext.carry`),
|
|
140
|
+
* so the caller can write it into the attempt record at once. Without it,
|
|
141
|
+
* steps get no `carry`.
|
|
142
|
+
*/
|
|
143
|
+
onCarry?: (member: string, value: unknown) => void;
|
|
133
144
|
/** The release each component deploys (#3061), as `InterpretRunOptions.releaseIdentity`. */
|
|
134
145
|
releaseIdentity?: (component: string) => ReleaseIdentity | undefined;
|
|
135
146
|
/**
|
|
@@ -247,7 +258,7 @@ export async function runFanOut(
|
|
|
247
258
|
const planned = options.plans?.[name];
|
|
248
259
|
const result = await runComponentDeploy(
|
|
249
260
|
byName.get(name)!,
|
|
250
|
-
{ ...
|
|
261
|
+
{ ...fanOutContext(options, name), ...(planned && Object.keys(planned).length > 0 ? { plans: planned } : {}) },
|
|
251
262
|
registry,
|
|
252
263
|
componentOutputs,
|
|
253
264
|
options.onProgress,
|
|
@@ -311,6 +322,15 @@ export async function runFanOut(
|
|
|
311
322
|
};
|
|
312
323
|
}
|
|
313
324
|
|
|
325
|
+
/** The run's `deployContext`, with the carried state and the carry callback (#3459) when the caller gave them. */
|
|
326
|
+
function fanOutContext(options: FanOutRunOptions, component: string): DeployContext {
|
|
327
|
+
return {
|
|
328
|
+
...deployContext(options, component),
|
|
329
|
+
...(options.carried && Object.keys(options.carried).length > 0 ? { carried: options.carried } : {}),
|
|
330
|
+
...(options.onCarry ? { carry: options.onCarry } : {}),
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
314
334
|
/** A plan failure as a component result, so it reads like any other failed component. */
|
|
315
335
|
function planFailure(component: string, error: string): DriverComponentResult {
|
|
316
336
|
return {
|
|
@@ -381,7 +401,7 @@ async function runGatedWaves(
|
|
|
381
401
|
if (runnable.length === 0) continue;
|
|
382
402
|
|
|
383
403
|
const gateName = waveGateName(waveGate.gate, waveNum);
|
|
384
|
-
const contexts = new Map(runnable.map((name) => [name,
|
|
404
|
+
const contexts = new Map(runnable.map((name) => [name, fanOutContext(options, name)]));
|
|
385
405
|
const planned = await Promise.all(
|
|
386
406
|
runnable.map((name) => planComponent(byName.get(name)!, contexts.get(name)!, componentOutputs)),
|
|
387
407
|
);
|
|
@@ -373,3 +373,53 @@ describe("both modes", () => {
|
|
|
373
373
|
expect(result.waves?.[0]?.members[0]?.planDigest).toMatch(/^jcs1-sha256:/);
|
|
374
374
|
});
|
|
375
375
|
});
|
|
376
|
+
|
|
377
|
+
describe("carried state (#3459): what a step keeps for its member reaches the next attempt", () => {
|
|
378
|
+
function carryingRegistry(seen: Array<unknown>, failing: Set<string>) {
|
|
379
|
+
const registry = new CapabilityRegistry();
|
|
380
|
+
registry.register({
|
|
381
|
+
kind: "fake-root",
|
|
382
|
+
async plan(_ctx: DeployContext, input: RootInput) {
|
|
383
|
+
return { member: input.root, planDigest: `d-${input.root}`, artifact: {} };
|
|
384
|
+
},
|
|
385
|
+
async run(ctx: DeployContext, input: RootInput) {
|
|
386
|
+
seen.push(ctx.carried?.[input.root]);
|
|
387
|
+
ctx.carry?.(input.root, { attempt: (ctx.carried?.[input.root] as { attempt?: number } | undefined)?.attempt ?? 0 });
|
|
388
|
+
if (failing.has(input.root)) throw new Error(`${input.root} failed`);
|
|
389
|
+
return { outputs: {} };
|
|
390
|
+
},
|
|
391
|
+
} as never);
|
|
392
|
+
return registry;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/** Wave 1 over `net` alone, approved for the digest it plans to. */
|
|
396
|
+
async function approvedNet() {
|
|
397
|
+
const components = [root("net")];
|
|
398
|
+
const plan = planFanOut({ components, changed: ["net"] });
|
|
399
|
+
const first = await runFanOut(plan, components, carryingRegistry([], new Set()), waveOptions(ledger()));
|
|
400
|
+
expect(first.status).toBe("gated");
|
|
401
|
+
const gates = () => ledger([first.gate!], [{ planDigest: first.gate!.planDigest! }]);
|
|
402
|
+
return { components, plan, gates };
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
test("a failed root's carried value is kept, and handed back on the next attempt", async () => {
|
|
406
|
+
const { components, plan, gates } = await approvedNet();
|
|
407
|
+
const seen: unknown[] = [];
|
|
408
|
+
const kept: Record<string, unknown> = {};
|
|
409
|
+
|
|
410
|
+
const failed = await runFanOut(plan, components, carryingRegistry(seen, new Set(["net"])), waveOptions(gates(), { onCarry: (m, v) => (kept[m] = v) }));
|
|
411
|
+
expect(failed.status).toBe("fail");
|
|
412
|
+
expect(seen).toEqual([undefined]);
|
|
413
|
+
expect(kept).toEqual({ net: { attempt: 0 } });
|
|
414
|
+
|
|
415
|
+
const retried = await runFanOut(plan, components, carryingRegistry(seen, new Set()), waveOptions(gates(), { carried: { net: { attempt: 1 } } }));
|
|
416
|
+
expect(retried.status).toBe("ok");
|
|
417
|
+
expect(seen).toEqual([undefined, { attempt: 1 }]);
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
test("without onCarry a step gets no carry, and runs as before", async () => {
|
|
421
|
+
const { components, plan, gates } = await approvedNet();
|
|
422
|
+
const result = await runFanOut(plan, components, carryingRegistry([], new Set()), waveOptions(gates()));
|
|
423
|
+
expect(result.status).toBe("ok");
|
|
424
|
+
});
|
|
425
|
+
});
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
* (absent on the resolution lines written before #2119, which is why
|
|
42
42
|
* `"resolution"` is the default reading).
|
|
43
43
|
*/
|
|
44
|
+
import { FAN_OUT_GATE_OP } from "../op/gate-name";
|
|
44
45
|
import { samePlanDigest } from "./plan-digest";
|
|
45
46
|
import { sortedJsonReplacer } from "../utils";
|
|
46
47
|
import { currentGateOrigin, type GateOrigin } from "./gate-origin";
|
|
@@ -270,6 +271,12 @@ export interface PendingGateRecord {
|
|
|
270
271
|
* and resolved over MCP has one author, not two.
|
|
271
272
|
*/
|
|
272
273
|
origin?: GateOrigin;
|
|
274
|
+
/**
|
|
275
|
+
* The gate is never resolved over MCP or ACP (chant#3485), whichever channel
|
|
276
|
+
* reached it. The declaration travels on the record because an Op that MCP
|
|
277
|
+
* cannot discover, such as `fan-out`, has no config to read it from.
|
|
278
|
+
*/
|
|
279
|
+
neverOverMcp?: true;
|
|
273
280
|
/**
|
|
274
281
|
* The gate's quorum and policy (#2508), with its context resolved against
|
|
275
282
|
* this run. `chant approve` reads it to evaluate the policy against the plan
|
|
@@ -334,6 +341,9 @@ export async function appendPendingGate(
|
|
|
334
341
|
version: 1,
|
|
335
342
|
kind: "pending",
|
|
336
343
|
origin: currentGateOrigin(),
|
|
344
|
+
// chant#3485: an Op MCP cannot discover declares nothing, so the gate
|
|
345
|
+
// records the rule for it. Every fan-out gate is one.
|
|
346
|
+
...(input.op === FAN_OUT_GATE_OP ? { neverOverMcp: true as const } : {}),
|
|
337
347
|
...input,
|
|
338
348
|
};
|
|
339
349
|
const commit = await appendGateLine(record, "Pending gate record", opts);
|