@intentius/chant 0.53.0 → 0.53.1
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/op-progress.d.ts +37 -4
- package/dist/cli/handlers/op-progress.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +61 -1
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +9 -1
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/lifecycle/converge-ledger.d.ts +81 -1
- package/dist/lifecycle/converge-ledger.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +43 -1
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cli/handlers/op-progress.test.ts +67 -1
- package/src/cli/handlers/op-progress.ts +70 -14
- package/src/cli/handlers/operator.test.ts +302 -1
- package/src/cli/handlers/operator.ts +193 -5
- package/src/cli/handlers/run.ts +11 -13
- package/src/cli/main.test.ts +19 -0
- package/src/cli/main.ts +26 -5
- package/src/cli/registry.ts +9 -1
- package/src/lifecycle/converge-ledger.test.ts +110 -0
- package/src/lifecycle/converge-ledger.ts +110 -2
- package/src/lifecycle/gate-ledger.test.ts +81 -1
- package/src/lifecycle/gate-ledger.ts +64 -1
|
@@ -22,20 +22,53 @@
|
|
|
22
22
|
import type { OpConfig } from "../../op/types.js";
|
|
23
23
|
import type { StepRecord } from "../../op/local-executor.js";
|
|
24
24
|
import type { WorkflowHistoryRaw, WorkflowHandleRaw } from "./run-client.js";
|
|
25
|
+
/**
|
|
26
|
+
* A step record paired with the position of the declared step it came from.
|
|
27
|
+
*
|
|
28
|
+
* `index` is an offset into `declaredStepOrder(config)`, which depends only
|
|
29
|
+
* on the Op config — not on the history — so it is the record's identity
|
|
30
|
+
* and is stable across polls. The record *list* is not: a mid-run poll omits
|
|
31
|
+
* every step that hasn't settled yet, and the terminal `final: true` pass
|
|
32
|
+
* inserts a `"skipped"` record at each such step's declared position, which
|
|
33
|
+
* shifts every later record's list offset (chant #2032). Anything tracking
|
|
34
|
+
* what it has already seen must key on `index`, never on how many records
|
|
35
|
+
* the previous call returned.
|
|
36
|
+
*/
|
|
37
|
+
export interface IndexedStepRecord {
|
|
38
|
+
index: number;
|
|
39
|
+
record: StepRecord;
|
|
40
|
+
}
|
|
25
41
|
/**
|
|
26
42
|
* Reconstruct per-step progress records from a workflow's history, in
|
|
27
|
-
* declared order
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
43
|
+
* declared order, each tagged with its declared-step `index`. Only settled
|
|
44
|
+
* steps (matched to a scheduled event that has since completed or failed)
|
|
45
|
+
* produce a record — a step that hasn't been scheduled yet simply isn't in
|
|
46
|
+
* the result. Pass `final: true` once the workflow has reached a terminal
|
|
31
47
|
* status to also emit a `"skipped"` record for every declared step that
|
|
32
48
|
* never got a matching scheduled event (an untaken effect branch, or an
|
|
33
49
|
* `onFailure` phase on a run that succeeded) — never before, or a
|
|
34
50
|
* still-running Op would show its not-yet-reached steps as skipped.
|
|
35
51
|
*/
|
|
52
|
+
export declare function extractIndexedStepRecords(config: OpConfig, history: WorkflowHistoryRaw, opts?: {
|
|
53
|
+
final?: boolean;
|
|
54
|
+
}): IndexedStepRecord[];
|
|
55
|
+
/** `extractIndexedStepRecords` without the indices — the plain declared-order record list. */
|
|
36
56
|
export declare function extractStepRecords(config: OpConfig, history: WorkflowHistoryRaw, opts?: {
|
|
37
57
|
final?: boolean;
|
|
38
58
|
}): StepRecord[];
|
|
59
|
+
/**
|
|
60
|
+
* A sink that forwards each declared step's record exactly once, however many
|
|
61
|
+
* times the poll loop re-derives the list.
|
|
62
|
+
*
|
|
63
|
+
* `--progress-json` calls this per poll with the whole list so far, plus once
|
|
64
|
+
* more with the terminal `final: true` list. Deduplicating by declared-step
|
|
65
|
+
* `index` is what makes that safe: an emitted-count watermark assumed the list
|
|
66
|
+
* was append-only, and the `final` pass's inserted `"skipped"` records break
|
|
67
|
+
* that assumption — with an unreached phase declared before an `onFailure`
|
|
68
|
+
* phase that ran, the insertion shifted the tail, so the last record shipped
|
|
69
|
+
* twice and the skipped one never shipped at all (chant #2032).
|
|
70
|
+
*/
|
|
71
|
+
export declare function stepRecordEmitter(emit: (record: StepRecord) => void): (records: IndexedStepRecord[]) => void;
|
|
39
72
|
/** `{completed, scheduled}` activity counts for the summary line `chant run status`/`op-status` show alongside the full record list. */
|
|
40
73
|
export declare function countActivities(history: WorkflowHistoryRaw): {
|
|
41
74
|
completed: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"op-progress.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/op-progress.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAmB,MAAM,gBAAgB,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAgB,iBAAiB,EAAE,MAAM,cAAc,CAAC;AA2FxF;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,QAAQ,EAChB,OAAO,EAAE,kBAAkB,EAC3B,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7B,UAAU,EAAE,
|
|
1
|
+
{"version":3,"file":"op-progress.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/op-progress.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAmB,MAAM,gBAAgB,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAgB,iBAAiB,EAAE,MAAM,cAAc,CAAC;AA2FxF;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;CACpB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,QAAQ,EAChB,OAAO,EAAE,kBAAkB,EAC3B,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7B,iBAAiB,EAAE,CAsCrB;AAED,8FAA8F;AAC9F,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,QAAQ,EAChB,OAAO,EAAE,kBAAkB,EAC3B,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7B,UAAU,EAAE,CAEd;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,GAAG,CAAC,OAAO,EAAE,iBAAiB,EAAE,KAAK,IAAI,CAS5G;AAED,wIAAwI;AACxI,wBAAgB,eAAe,CAAC,OAAO,EAAE,kBAAkB,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAMrG;AAED,0JAA0J;AAC1J,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,GAAG,SAAS,CAAC,CAM3G"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { type ConvergeTickRecord } from "../../lifecycle/converge-ledger.js";
|
|
2
|
+
import type { GateResolutionRecord } from "../../lifecycle/gate-ledger.js";
|
|
1
3
|
import type { CommandContext } from "../registry.js";
|
|
2
4
|
/**
|
|
3
5
|
* `chant operator [--env <env>] [--interval <duration>] [--lease-ttl
|
|
@@ -17,8 +19,66 @@ export declare function runOperator(ctx: CommandContext): Promise<number>;
|
|
|
17
19
|
* operator) ran the last tick.
|
|
18
20
|
*/
|
|
19
21
|
export declare function runOperatorStatus(ctx: CommandContext): Promise<number>;
|
|
22
|
+
/** One entry of the merged tick/gate timeline `chant operator log` prints. */
|
|
23
|
+
export type OperatorLogEntry = {
|
|
24
|
+
kind: "tick";
|
|
25
|
+
timestamp: string;
|
|
26
|
+
record: ConvergeTickRecord;
|
|
27
|
+
} | {
|
|
28
|
+
kind: "gate-resolution";
|
|
29
|
+
timestamp: string;
|
|
30
|
+
record: GateResolutionRecord;
|
|
31
|
+
};
|
|
32
|
+
export interface OperatorLogResult {
|
|
33
|
+
entries: OperatorLogEntry[];
|
|
34
|
+
/**
|
|
35
|
+
* Unreadable lines behind this answer, per ledger. `readConvergeLedger` and
|
|
36
|
+
* `readGateResolutions` both skip a malformed line and count it rather than
|
|
37
|
+
* throwing, so a corrupted ledger renders a shorter timeline; without this a
|
|
38
|
+
* consumer could not tell that from a genuinely quiet environment.
|
|
39
|
+
*/
|
|
40
|
+
malformed: {
|
|
41
|
+
converge: number;
|
|
42
|
+
gates: number;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
20
45
|
/**
|
|
21
|
-
*
|
|
46
|
+
* Gather the converge/gate history a set of ConvergeOps has recorded, merged
|
|
47
|
+
* into one timestamp-ordered timeline, oldest first.
|
|
48
|
+
*
|
|
49
|
+
* `since` (inclusive) and `limit` are applied after the merge — `limit` keeps
|
|
50
|
+
* the *newest* n entries, since that is what a log is asked for, and then
|
|
51
|
+
* prints them oldest-first like every other log.
|
|
52
|
+
*/
|
|
53
|
+
export declare function collectOperatorLog(ops: {
|
|
54
|
+
name: string;
|
|
55
|
+
env: string;
|
|
56
|
+
}[], opts?: {
|
|
57
|
+
op?: string;
|
|
58
|
+
since?: string;
|
|
59
|
+
limit?: number;
|
|
60
|
+
cwd?: string;
|
|
61
|
+
}): Promise<OperatorLogResult>;
|
|
62
|
+
/**
|
|
63
|
+
* `chant operator log [--env <env>] [--op <name>] [--since <iso>] [--limit
|
|
64
|
+
* <n>] [--json]` (#2029) — the converge tick history and the gate resolutions
|
|
65
|
+
* against it, read from the `chant/lifecycle` orphan branch alone.
|
|
66
|
+
*
|
|
67
|
+
* `readConvergeLedger` has always returned every tick for an environment;
|
|
68
|
+
* until this its only caller was `operator status`, which threw the history
|
|
69
|
+
* away and kept `.at(-1)`. A consumer wanting more than the newest row had to
|
|
70
|
+
* `git show chant/lifecycle:<env>/converge.jsonl` and parse it — which pins it
|
|
71
|
+
* to the orphan branch's name, the `<env>/converge.jsonl` path convention, the
|
|
72
|
+
* `_gates/<op>.jsonl` convention and the on-disk encoding, none of which are
|
|
73
|
+
* promised contracts. This is the read surface that makes those private again.
|
|
74
|
+
*
|
|
75
|
+
* Read-only: no lease, no dispatch, no daemon. Same `--json` discipline as
|
|
76
|
+
* `operator status` — one JSON document on stdout, diagnostics on stderr.
|
|
77
|
+
*/
|
|
78
|
+
export declare function runOperatorLog(ctx: CommandContext): Promise<number>;
|
|
79
|
+
/**
|
|
80
|
+
* `chant approve <op> <gate> [--actor <name>] [--note <text>] [--url <url>]`
|
|
81
|
+
* — record the
|
|
22
82
|
* out-of-band resolution fact for a gate a converge tick recorded as gated
|
|
23
83
|
* (issue: "resolution is an out-of-band act that writes the counterpart
|
|
24
84
|
* fact"). Per the issue's own leaning on open question 3 ("local trust in
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operator.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/operator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"operator.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/operator.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAC9F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAIxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAelD;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA0DtE;AA+CD;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAsC5E;AAID,8EAA8E;AAC9E,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,kBAAkB,CAAA;CAAE,GAC/D;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,oBAAoB,CAAA;CAAE,CAAC;AAEjF,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B;;;;;OAKG;IACH,SAAS,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAChD;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,EACpC,IAAI,GAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GACvE,OAAO,CAAC,iBAAiB,CAAC,CA4C5B;AAwBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAgDzE;AAID;;;;;;;;;;;GAWG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAgDrE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/run.ts"],"names":[],"mappings":"AAIA,OAAO,EAA+C,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAQ7F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AA8ClD,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,WAAW,EAAE,MAAM;;;;GAO5F;AA8BD,wBAAsB,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAgEpE;AAoFD,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA6CtE;AAwCD,wBAAsB,QAAQ,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA6CnE;AA2DD;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAkCtE;AAID,2LAA2L;AAC3L,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA8BtE;AAgFD,wBAAsB,KAAK,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAqBhE;AA6ED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAuF1E;AA+OD;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAoFrE;
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/run.ts"],"names":[],"mappings":"AAIA,OAAO,EAA+C,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAQ7F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AA8ClD,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,WAAW,EAAE,MAAM;;;;GAO5F;AA8BD,wBAAsB,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAgEpE;AAoFD,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA6CtE;AAwCD,wBAAsB,QAAQ,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA6CnE;AA2DD;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAkCtE;AAID,2LAA2L;AAC3L,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA8BtE;AAgFD,wBAAsB,KAAK,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAqBhE;AA6ED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAuF1E;AA+OD;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAoFrE;AA6MD,wBAAgB,YAAY,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAMjE"}
|
package/dist/cli/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":";AAMA,OAAO,EAAmC,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAsF9E;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":";AAMA,OAAO,EAAmC,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAsF9E;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CA6UpD;AAmoBD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB5E"}
|
package/dist/cli/registry.d.ts
CHANGED
|
@@ -283,8 +283,16 @@ export interface ParsedArgs {
|
|
|
283
283
|
leaseTtl?: string;
|
|
284
284
|
/** `chant operator --once` (#1485) — run a single round and exit, instead of looping until Ctrl-C. Also the offline test/cron-invoker story. */
|
|
285
285
|
once?: boolean;
|
|
286
|
-
/** `chant approve <op> <gate> --note <text>` (#1485) — optional free-text
|
|
286
|
+
/** `chant approve <op> <gate> --note <text>` (#1485) — optional free-text prose recorded on the gate-resolution fact. The PR link belongs in `--url` since #2028; this is for everything that isn't the link. */
|
|
287
287
|
note?: string;
|
|
288
|
+
/** `chant operator log --op <name>` (#2029) — restrict the tick history to one ConvergeOp by name. Omitted, every discovered ConvergeOp's ticks are merged into one timeline. */
|
|
289
|
+
op?: string;
|
|
290
|
+
/** `chant operator log --since <iso>` (#2029) — only entries at or after this ISO-8601 instant. */
|
|
291
|
+
since?: string;
|
|
292
|
+
/** `chant operator log --limit <n>` (#2029) — keep only the newest n entries (still printed oldest-first). */
|
|
293
|
+
limit?: number;
|
|
294
|
+
/** `chant approve <op> <gate> --url <url>` (#2028) — the address this resolution happened at (the PR/MR that carried the change), recorded typed on the gate-resolution fact so a reader is not sniffing `--note` for something link-shaped. Defaults to the PR/MR the surrounding CI job is for, when there is one. Must be an absolute http/https URL. */
|
|
295
|
+
url?: string;
|
|
288
296
|
}
|
|
289
297
|
/**
|
|
290
298
|
* Declarative command definition for the CLI registry.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/cli/registry.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/cli/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { ComponentStatusRow } from "./status.js";
|
|
2
|
+
export declare function sanitizeLedgerText(raw: string, maxLen?: number): string;
|
|
1
3
|
/** One rule's outcome within a tick. */
|
|
2
4
|
export interface ConvergeRuleOutcome {
|
|
3
5
|
ruleId: string;
|
|
@@ -15,13 +17,70 @@ export interface ConvergeRuleOutcome {
|
|
|
15
17
|
op?: string;
|
|
16
18
|
/** The gate's signal name, for `action: "gated"`. */
|
|
17
19
|
gateName?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Where this gate's approval happens (#2028), for `action: "gated"` — the
|
|
22
|
+
* PR carrying the change in a gate-as-PR flow, or whatever review surface
|
|
23
|
+
* the dispatching environment knows about.
|
|
24
|
+
*
|
|
25
|
+
* The pending fact is the one a human has to act on, and it used to carry
|
|
26
|
+
* no link at all: `chant operator status`'s pending row was
|
|
27
|
+
* `{rule, op, gate}` and the only affordance it could print was a shell
|
|
28
|
+
* command. Absent when there genuinely is no address — a local tick with no
|
|
29
|
+
* PR behind it — never a synthesized one. See
|
|
30
|
+
* `./gate-ledger.ts`'s `resolveApprovalUrl` for where it comes from, and
|
|
31
|
+
* `GateResolutionRecord.url` for the resolved counterpart.
|
|
32
|
+
*/
|
|
33
|
+
url?: string;
|
|
18
34
|
/** The report reason, for `action: "reported"` (including a flap-damped rule's forced report) — and the human-readable explanation for `action: "gated"`. */
|
|
19
35
|
reason?: string;
|
|
20
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* One component's verdict as the tick observed it (#2027) — the per-entity
|
|
39
|
+
* layer behind the tick's single aggregate `status`.
|
|
40
|
+
*
|
|
41
|
+
* A deliberate subset of `ComponentStatusRow`: the five verdict-bearing
|
|
42
|
+
* fields, joined on the same `component` key `chant components status --live
|
|
43
|
+
* --json` emits, and none of the heavy ones. `recorded`, `build` and
|
|
44
|
+
* `componentBom` are dropped on purpose — they are release/build-ledger
|
|
45
|
+
* content, already readable by that same key, and a tick record is one line
|
|
46
|
+
* of JSON appended every tick forever. `detail` goes through
|
|
47
|
+
* {@link sanitizeLedgerText} for the same reason.
|
|
48
|
+
*/
|
|
49
|
+
export interface ConvergeComponentVerdict {
|
|
50
|
+
component: string;
|
|
51
|
+
reconciliation: ComponentStatusRow["reconciliation"];
|
|
52
|
+
/** Human-readable detail backing the verdict, capped to one line. */
|
|
53
|
+
detail: string;
|
|
54
|
+
/** "Observed live", when live evidence was gathered. Absent means "did not look" or "could not look" — see `unobserved`. */
|
|
55
|
+
live?: boolean;
|
|
56
|
+
/** Why live state could not be read for this component (#1089). Mutually exclusive with `live`; this is the row that tripped an aggregate `status: "unknown"`. */
|
|
57
|
+
unobserved?: {
|
|
58
|
+
reason: string;
|
|
59
|
+
detail?: string;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Project the tick's `ComponentStatusRow[]` down to what the ledger keeps.
|
|
64
|
+
* Pure; row order is preserved, so a consumer sees components in the same
|
|
65
|
+
* order the status join produced them.
|
|
66
|
+
*/
|
|
67
|
+
export declare function componentVerdicts(rows: readonly ComponentStatusRow[]): ConvergeComponentVerdict[];
|
|
21
68
|
/** One immutable converge-tick record. */
|
|
22
69
|
export interface ConvergeTickRecord {
|
|
23
70
|
/** Schema version, so an incompatible future shape is detected before being misread. */
|
|
24
71
|
version: 1;
|
|
72
|
+
/**
|
|
73
|
+
* Stable tick id (#2027) — the one thing an outcome, a gate fact or a
|
|
74
|
+
* remediation can point at. Before this, a tick's identity was the
|
|
75
|
+
* `(op, env, timestamp)` triple, so two ticks landing in the same ISO
|
|
76
|
+
* second were indistinguishable and nothing could reference one.
|
|
77
|
+
*
|
|
78
|
+
* Minted by {@link appendConvergeRecord} with `randomUUID()` (the same
|
|
79
|
+
* mint `./lease.ts` uses for a lease token) when the caller doesn't supply
|
|
80
|
+
* one. Optional on the type because `version: 1` records written before
|
|
81
|
+
* #2027 have none — a reader must handle its absence, not assume it.
|
|
82
|
+
*/
|
|
83
|
+
id?: string;
|
|
25
84
|
/** The ConvergeOp's name (`OpConfig.name`). */
|
|
26
85
|
op: string;
|
|
27
86
|
env: string;
|
|
@@ -31,6 +90,18 @@ export interface ConvergeTickRecord {
|
|
|
31
90
|
firedRuleIds: string[];
|
|
32
91
|
/** Per-rule outcome, for every fired rule. */
|
|
33
92
|
outcomes: ConvergeRuleOutcome[];
|
|
93
|
+
/**
|
|
94
|
+
* The per-component verdicts this tick observed (#2027), behind the
|
|
95
|
+
* aggregate counts in `summary`.
|
|
96
|
+
*
|
|
97
|
+
* The tick derives these to compute `summary.drifted` and the whole-tick
|
|
98
|
+
* `status`, and used to throw them away — so a reader could say "this
|
|
99
|
+
* environment drifted twice this hour" but could not colour the node that
|
|
100
|
+
* drifted, or name the single unobserved component whose `unknown` verdict
|
|
101
|
+
* refused remediation for everything else. Optional because `version: 1`
|
|
102
|
+
* records written before #2027 have none; absent is not "no components".
|
|
103
|
+
*/
|
|
104
|
+
components?: ConvergeComponentVerdict[];
|
|
34
105
|
/** Aggregate counts backing the tick's one log line. */
|
|
35
106
|
summary: {
|
|
36
107
|
drifted: number;
|
|
@@ -52,6 +123,13 @@ export type ConvergeTickRecordInput = Omit<ConvergeTickRecord, "version">;
|
|
|
52
123
|
* `pushLifecycle` (./git.ts) afterward, same two-step shape every other
|
|
53
124
|
* ledger write here uses.
|
|
54
125
|
*
|
|
126
|
+
* Mints `record.id` (#2027) when the input doesn't carry one, so every tick
|
|
127
|
+
* written from here on is referenceable. Unlike `timestamp`, which stays
|
|
128
|
+
* caller-supplied because library code never calls `Date.now()` internally,
|
|
129
|
+
* an id has nothing for a caller to decide — the mint lives here so no
|
|
130
|
+
* writer can forget it. An explicit `input.id` still wins, which is what a
|
|
131
|
+
* test asserting an exact record uses.
|
|
132
|
+
*
|
|
55
133
|
* Retries the whole read-modify-write cycle (#1485) on `RefCASConflictError`
|
|
56
134
|
* — `writeBlobToPath`'s ref write is CAS-guarded, so a concurrent writer to
|
|
57
135
|
* a *different* env's file on the same orphan branch (two operators ticking
|
|
@@ -71,7 +149,9 @@ export declare function appendConvergeRecord(input: ConvergeTickRecordInput, opt
|
|
|
71
149
|
cwd?: string;
|
|
72
150
|
}): Promise<{
|
|
73
151
|
commit: string;
|
|
74
|
-
record: ConvergeTickRecord
|
|
152
|
+
record: ConvergeTickRecord & {
|
|
153
|
+
id: string;
|
|
154
|
+
};
|
|
75
155
|
}>;
|
|
76
156
|
/** Read every tick record for `environment`, oldest first. Malformed lines are skipped, not thrown on — a corrupted or hand-edited ledger degrades gracefully, the same stance `readReleaseLedger` takes. */
|
|
77
157
|
export declare function readConvergeLedger(environment: string, opts?: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"converge-ledger.d.ts","sourceRoot":"","sources":["../../src/lifecycle/converge-ledger.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"converge-ledger.d.ts","sourceRoot":"","sources":["../../src/lifecycle/converge-ledger.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The address of the approval surface for a gate, resolved from the CI
|
|
3
|
+
* environment (#2028).
|
|
4
|
+
*
|
|
5
|
+
* #1485's argument for gate-as-fact was that approval gets an address:
|
|
6
|
+
* "gate-as-PR gives approval a URL, a review surface, and CODEOWNERS as the
|
|
7
|
+
* authorization model." What shipped recorded the gate and not the address,
|
|
8
|
+
* so a pending-approval card had nothing to link to and gate-as-PR stayed a
|
|
9
|
+
* convention. This is the narrow, honest half of that: when a tick (or a
|
|
10
|
+
* `chant approve`) runs inside the PR/MR job that carries the change, the
|
|
11
|
+
* loop genuinely knows where approval happens, and says so. Anywhere else it
|
|
12
|
+
* returns `undefined` and the field is simply absent — never a guess, never a
|
|
13
|
+
* synthesized link.
|
|
14
|
+
*
|
|
15
|
+
* The env-var fallback chain is the same one `--actor` and `--run-id` already
|
|
16
|
+
* use (`../cli/handlers/components.ts`): GitHub Actions first, then GitLab CI.
|
|
17
|
+
*
|
|
18
|
+
* - GitHub Actions on a `pull_request` event: `GITHUB_SERVER_URL` +
|
|
19
|
+
* `GITHUB_REPOSITORY` + the PR number, which `GITHUB_REF_NAME` carries as
|
|
20
|
+
* `<n>/merge`. A push-event run has no PR, so it resolves to nothing.
|
|
21
|
+
* - GitLab CI on a merge-request pipeline: `CI_MERGE_REQUEST_PROJECT_URL` +
|
|
22
|
+
* `CI_MERGE_REQUEST_IID`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolveApprovalUrl(env?: NodeJS.ProcessEnv): string | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Whether `raw` is an address worth recording as one: an absolute `http`/
|
|
27
|
+
* `https` URL. A gate's address is a link a reader is expected to follow, so
|
|
28
|
+
* a relative path or a `file:`/`javascript:` scheme is refused at the CLI
|
|
29
|
+
* boundary rather than written into an immutable record.
|
|
30
|
+
*/
|
|
31
|
+
export declare function isApprovalUrl(raw: string): boolean;
|
|
1
32
|
/** One immutable gate-resolution record. */
|
|
2
33
|
export interface GateResolutionRecord {
|
|
3
34
|
/** Schema version, so an incompatible future shape is detected before being misread. */
|
|
@@ -10,8 +41,19 @@ export interface GateResolutionRecord {
|
|
|
10
41
|
resolvedBy: string;
|
|
11
42
|
/** ISO-8601 timestamp, caller-supplied (library code never calls `Date.now()` internally). */
|
|
12
43
|
timestamp: string;
|
|
13
|
-
/** Optional free-text context
|
|
44
|
+
/** Optional free-text context. Before #2028 this was also where a PR link went by convention; put the link in {@link GateResolutionRecord.url} instead and leave this for prose. */
|
|
14
45
|
note?: string;
|
|
46
|
+
/**
|
|
47
|
+
* The address this resolution happened at (#2028) — the PR that carried the
|
|
48
|
+
* change, the review thread, whatever the approval surface was. Typed, so
|
|
49
|
+
* "resolved by this PR" is machine-readable instead of a reader sniffing
|
|
50
|
+
* `note` for something that looks like a link.
|
|
51
|
+
*
|
|
52
|
+
* `chant approve --url` sets it; absent when the resolver genuinely had no
|
|
53
|
+
* address (a human at a terminal, no PR). Always an absolute `http`/`https`
|
|
54
|
+
* URL — see {@link isApprovalUrl}.
|
|
55
|
+
*/
|
|
56
|
+
url?: string;
|
|
15
57
|
}
|
|
16
58
|
export type GateResolutionInput = Omit<GateResolutionRecord, "version">;
|
|
17
59
|
/** Append one immutable gate-resolution record. Does not push to the remote — call `pushLifecycle` (./git.ts) afterward, same two-step shape every other ledger write here uses. Retries on `RefCASConflictError` the same way `appendConvergeRecord` does (./converge-ledger.ts) — a concurrent writer to a different op's/env's file on the same orphan branch is the ordinary case, not an edge case. The baseline read must be `readPathSha` + `readBlobBySha` rather than `readBlobFromPath`, so the exact sha `existing` came from can be passed as `expectPriorPathSha` — see `writeBlobToPath` (./git.ts) for the race that closes. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gate-ledger.d.ts","sourceRoot":"","sources":["../../src/lifecycle/gate-ledger.ts"],"names":[],"mappings":"AA4CA,4CAA4C;AAC5C,MAAM,WAAW,oBAAoB;IACnC,wFAAwF;IACxF,OAAO,EAAE,CAAC,CAAC;IACX,6CAA6C;IAC7C,EAAE,EAAE,MAAM,CAAC;IACX,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,yHAAyH;IACzH,UAAU,EAAE,MAAM,CAAC;IACnB,8FAA8F;IAC9F,SAAS,EAAE,MAAM,CAAC;IAClB,
|
|
1
|
+
{"version":3,"file":"gate-ledger.d.ts","sourceRoot":"","sources":["../../src/lifecycle/gate-ledger.ts"],"names":[],"mappings":"AA4CA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,GAAG,SAAS,CAY3F;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOlD;AAED,4CAA4C;AAC5C,MAAM,WAAW,oBAAoB;IACnC,wFAAwF;IACxF,OAAO,EAAE,CAAC,CAAC;IACX,6CAA6C;IAC7C,EAAE,EAAE,MAAM,CAAC;IACX,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,yHAAyH;IACzH,UAAU,EAAE,MAAM,CAAC;IACnB,8FAA8F;IAC9F,SAAS,EAAE,MAAM,CAAC;IAClB,oLAAoL;IACpL,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;;;OASG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,CAAC;AAMxE,+mBAA+mB;AAC/mB,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,mBAAmB,EAC1B,IAAI,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACtB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,oBAAoB,CAAA;CAAE,CAAC,CAqB3D;AAED,oPAAoP;AACpP,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,MAAM,EACV,IAAI,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACtB,OAAO,CAAC;IAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CA0BjE;AAED,uOAAuO;AACvO,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,oBAAoB,EAAE,EAC/B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,oBAAoB,GAAG,SAAS,CASlC"}
|
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, test, expect } from "vitest";
|
|
2
|
-
import { extractStepRecords, countActivities, queryGateState } from "./op-progress";
|
|
2
|
+
import { extractStepRecords, extractIndexedStepRecords, stepRecordEmitter, countActivities, queryGateState } from "./op-progress";
|
|
3
|
+
import type { StepRecord } from "../../op/local-executor";
|
|
3
4
|
import type { OpConfig } from "../../op/types";
|
|
4
5
|
import type { EffectReceiptRef } from "../../op/receipt-store";
|
|
5
6
|
import type { WorkflowHistoryRaw, HistoryEvent } from "./run-client";
|
|
@@ -171,6 +172,71 @@ describe("extractStepRecords", () => {
|
|
|
171
172
|
});
|
|
172
173
|
});
|
|
173
174
|
|
|
175
|
+
describe("stepRecordEmitter", () => {
|
|
176
|
+
// chant #2032. Declared order is main phases then `onFailure` reversed, so an
|
|
177
|
+
// unreached main phase sits *before* an `onFailure` phase that ran. The live
|
|
178
|
+
// poll therefore returns [Main(fail), Comp(ok)] and the terminal `final: true`
|
|
179
|
+
// pass returns [Main(fail), Never(skipped), Comp(ok)] — an insertion, not an
|
|
180
|
+
// append. The old emitted-count watermark sliced at 2 and re-shipped Comp
|
|
181
|
+
// while dropping Never entirely. The #1676 coverage above misses this because
|
|
182
|
+
// in those configs the skipped record lands last, where insert and append are
|
|
183
|
+
// the same operation.
|
|
184
|
+
const config: OpConfig = {
|
|
185
|
+
name: "op", overview: "o",
|
|
186
|
+
phases: [
|
|
187
|
+
{ name: "Main", steps: [{ kind: "activity", fn: "boom" }] },
|
|
188
|
+
{ name: "Never", steps: [{ kind: "activity", fn: "never" }] },
|
|
189
|
+
],
|
|
190
|
+
onFailure: [{ name: "Comp", steps: [{ kind: "activity", fn: "comp" }] }],
|
|
191
|
+
};
|
|
192
|
+
// Main fails, Never is never reached, compensation runs and succeeds.
|
|
193
|
+
const history: WorkflowHistoryRaw = {
|
|
194
|
+
events: [
|
|
195
|
+
scheduled("1", "boom", sec(0)),
|
|
196
|
+
failed("1", sec(1), "boom"),
|
|
197
|
+
scheduled("2", "comp", sec(2)),
|
|
198
|
+
completed("2", sec(3)),
|
|
199
|
+
],
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const MAIN: StepRecord = { phase: "Main", fn: "boom", status: "fail", durationMs: 1000, error: "boom" };
|
|
203
|
+
const NEVER: StepRecord = { phase: "Never", fn: "never", status: "skipped", durationMs: 0 };
|
|
204
|
+
const COMP: StepRecord = { phase: "Comp", fn: "comp", status: "ok", durationMs: 1000 };
|
|
205
|
+
|
|
206
|
+
test("the final pass inserts rather than appends, so list position is not an identity", () => {
|
|
207
|
+
expect(extractIndexedStepRecords(config, history)).toEqual([
|
|
208
|
+
{ index: 0, record: MAIN },
|
|
209
|
+
{ index: 2, record: COMP },
|
|
210
|
+
]);
|
|
211
|
+
expect(extractIndexedStepRecords(config, history, { final: true })).toEqual([
|
|
212
|
+
{ index: 0, record: MAIN },
|
|
213
|
+
{ index: 1, record: NEVER },
|
|
214
|
+
{ index: 2, record: COMP },
|
|
215
|
+
]);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test("each record ships exactly once across the poll passes and the final pass", () => {
|
|
219
|
+
const emitted: StepRecord[] = [];
|
|
220
|
+
const emit = stepRecordEmitter((r) => emitted.push(r));
|
|
221
|
+
|
|
222
|
+
emit(extractIndexedStepRecords(config, history)); // a poll while the workflow closes
|
|
223
|
+
emit(extractIndexedStepRecords(config, history)); // an identical re-poll adds nothing
|
|
224
|
+
emit(extractIndexedStepRecords(config, history, { final: true }));
|
|
225
|
+
|
|
226
|
+
expect(emitted).toEqual([MAIN, COMP, NEVER]);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test("an empty first poll does not shift what a later pass ships", () => {
|
|
230
|
+
const emitted: StepRecord[] = [];
|
|
231
|
+
const emit = stepRecordEmitter((r) => emitted.push(r));
|
|
232
|
+
|
|
233
|
+
emit(extractIndexedStepRecords(config, { events: [] }));
|
|
234
|
+
expect(emitted).toEqual([]);
|
|
235
|
+
emit(extractIndexedStepRecords(config, history, { final: true }));
|
|
236
|
+
expect(emitted).toEqual([MAIN, NEVER, COMP]);
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
174
240
|
describe("countActivities", () => {
|
|
175
241
|
test("counts scheduled and completed events", () => {
|
|
176
242
|
const history: WorkflowHistoryRaw = {
|
|
@@ -113,34 +113,55 @@ function terminalOutcomes(events: HistoryEvent[]): Map<string, TerminalOutcome>
|
|
|
113
113
|
return out;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
/**
|
|
117
|
+
* A step record paired with the position of the declared step it came from.
|
|
118
|
+
*
|
|
119
|
+
* `index` is an offset into `declaredStepOrder(config)`, which depends only
|
|
120
|
+
* on the Op config — not on the history — so it is the record's identity
|
|
121
|
+
* and is stable across polls. The record *list* is not: a mid-run poll omits
|
|
122
|
+
* every step that hasn't settled yet, and the terminal `final: true` pass
|
|
123
|
+
* inserts a `"skipped"` record at each such step's declared position, which
|
|
124
|
+
* shifts every later record's list offset (chant #2032). Anything tracking
|
|
125
|
+
* what it has already seen must key on `index`, never on how many records
|
|
126
|
+
* the previous call returned.
|
|
127
|
+
*/
|
|
128
|
+
export interface IndexedStepRecord {
|
|
129
|
+
index: number;
|
|
130
|
+
record: StepRecord;
|
|
131
|
+
}
|
|
132
|
+
|
|
116
133
|
/**
|
|
117
134
|
* Reconstruct per-step progress records from a workflow's history, in
|
|
118
|
-
* declared order
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
135
|
+
* declared order, each tagged with its declared-step `index`. Only settled
|
|
136
|
+
* steps (matched to a scheduled event that has since completed or failed)
|
|
137
|
+
* produce a record — a step that hasn't been scheduled yet simply isn't in
|
|
138
|
+
* the result. Pass `final: true` once the workflow has reached a terminal
|
|
122
139
|
* status to also emit a `"skipped"` record for every declared step that
|
|
123
140
|
* never got a matching scheduled event (an untaken effect branch, or an
|
|
124
141
|
* `onFailure` phase on a run that succeeded) — never before, or a
|
|
125
142
|
* still-running Op would show its not-yet-reached steps as skipped.
|
|
126
143
|
*/
|
|
127
|
-
export function
|
|
144
|
+
export function extractIndexedStepRecords(
|
|
128
145
|
config: OpConfig,
|
|
129
146
|
history: WorkflowHistoryRaw,
|
|
130
147
|
opts: { final?: boolean } = {},
|
|
131
|
-
):
|
|
148
|
+
): IndexedStepRecord[] {
|
|
132
149
|
const events = history.events ?? [];
|
|
133
150
|
const queues = scheduledQueues(events);
|
|
134
151
|
const terminals = terminalOutcomes(events);
|
|
135
152
|
const nextIndex = new Map<string, number>();
|
|
136
153
|
|
|
137
|
-
const records:
|
|
138
|
-
|
|
154
|
+
const records: IndexedStepRecord[] = [];
|
|
155
|
+
const declared = declaredStepOrder(config);
|
|
156
|
+
for (let index = 0; index < declared.length; index++) {
|
|
157
|
+
const step = declared[index];
|
|
139
158
|
const queue = queues.get(step.fn);
|
|
140
159
|
const idx = nextIndex.get(step.fn) ?? 0;
|
|
141
160
|
const entry = queue?.[idx];
|
|
142
161
|
if (!entry) {
|
|
143
|
-
if (opts.final)
|
|
162
|
+
if (opts.final) {
|
|
163
|
+
records.push({ index, record: { phase: step.phase, fn: step.fn, status: "skipped", durationMs: 0 } });
|
|
164
|
+
}
|
|
144
165
|
continue;
|
|
145
166
|
}
|
|
146
167
|
nextIndex.set(step.fn, idx + 1);
|
|
@@ -151,16 +172,51 @@ export function extractStepRecords(
|
|
|
151
172
|
const durationMs =
|
|
152
173
|
entry.scheduledAt && terminal.at ? terminal.at.getTime() - entry.scheduledAt.getTime() : 0;
|
|
153
174
|
records.push({
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
175
|
+
index,
|
|
176
|
+
record: {
|
|
177
|
+
phase: step.phase,
|
|
178
|
+
fn: step.fn,
|
|
179
|
+
status: terminal.status,
|
|
180
|
+
durationMs,
|
|
181
|
+
...(terminal.error ? { error: terminal.error } : {}),
|
|
182
|
+
},
|
|
159
183
|
});
|
|
160
184
|
}
|
|
161
185
|
return records;
|
|
162
186
|
}
|
|
163
187
|
|
|
188
|
+
/** `extractIndexedStepRecords` without the indices — the plain declared-order record list. */
|
|
189
|
+
export function extractStepRecords(
|
|
190
|
+
config: OpConfig,
|
|
191
|
+
history: WorkflowHistoryRaw,
|
|
192
|
+
opts: { final?: boolean } = {},
|
|
193
|
+
): StepRecord[] {
|
|
194
|
+
return extractIndexedStepRecords(config, history, opts).map((r) => r.record);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* A sink that forwards each declared step's record exactly once, however many
|
|
199
|
+
* times the poll loop re-derives the list.
|
|
200
|
+
*
|
|
201
|
+
* `--progress-json` calls this per poll with the whole list so far, plus once
|
|
202
|
+
* more with the terminal `final: true` list. Deduplicating by declared-step
|
|
203
|
+
* `index` is what makes that safe: an emitted-count watermark assumed the list
|
|
204
|
+
* was append-only, and the `final` pass's inserted `"skipped"` records break
|
|
205
|
+
* that assumption — with an unreached phase declared before an `onFailure`
|
|
206
|
+
* phase that ran, the insertion shifted the tail, so the last record shipped
|
|
207
|
+
* twice and the skipped one never shipped at all (chant #2032).
|
|
208
|
+
*/
|
|
209
|
+
export function stepRecordEmitter(emit: (record: StepRecord) => void): (records: IndexedStepRecord[]) => void {
|
|
210
|
+
const seen = new Set<number>();
|
|
211
|
+
return (records) => {
|
|
212
|
+
for (const { index, record } of records) {
|
|
213
|
+
if (seen.has(index)) continue;
|
|
214
|
+
seen.add(index);
|
|
215
|
+
emit(record);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
164
220
|
/** `{completed, scheduled}` activity counts for the summary line `chant run status`/`op-status` show alongside the full record list. */
|
|
165
221
|
export function countActivities(history: WorkflowHistoryRaw): { completed: number; scheduled: number } {
|
|
166
222
|
const events = history.events ?? [];
|