@groundnuty/macf-core 0.2.44 → 0.2.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/discovery.d.ts +59 -0
  2. package/dist/discovery.d.ts.map +1 -0
  3. package/dist/discovery.js +103 -0
  4. package/dist/discovery.js.map +1 -0
  5. package/dist/fleet-driver.d.ts +135 -0
  6. package/dist/fleet-driver.d.ts.map +1 -0
  7. package/dist/fleet-driver.js +44 -0
  8. package/dist/fleet-driver.js.map +1 -0
  9. package/dist/fleet-reconcile.d.ts +180 -0
  10. package/dist/fleet-reconcile.d.ts.map +1 -0
  11. package/dist/fleet-reconcile.js +249 -0
  12. package/dist/fleet-reconcile.js.map +1 -0
  13. package/dist/fleet-upgrade.d.ts +171 -0
  14. package/dist/fleet-upgrade.d.ts.map +1 -0
  15. package/dist/fleet-upgrade.js +177 -0
  16. package/dist/fleet-upgrade.js.map +1 -0
  17. package/dist/guest.d.ts +100 -0
  18. package/dist/guest.d.ts.map +1 -0
  19. package/dist/guest.js +120 -0
  20. package/dist/guest.js.map +1 -0
  21. package/dist/index.d.ts +8 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +8 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/norm.d.ts +2 -2
  26. package/dist/prompt-responses.d.ts +208 -0
  27. package/dist/prompt-responses.d.ts.map +1 -0
  28. package/dist/prompt-responses.js +274 -0
  29. package/dist/prompt-responses.js.map +1 -0
  30. package/dist/registry/local-client.d.ts.map +1 -1
  31. package/dist/registry/local-client.js +15 -9
  32. package/dist/registry/local-client.js.map +1 -1
  33. package/dist/registry/registry.d.ts.map +1 -1
  34. package/dist/registry/registry.js +91 -27
  35. package/dist/registry/registry.js.map +1 -1
  36. package/dist/registry/types.d.ts +57 -12
  37. package/dist/registry/types.d.ts.map +1 -1
  38. package/dist/registry/types.js.map +1 -1
  39. package/dist/stall-signatures.d.ts +139 -0
  40. package/dist/stall-signatures.d.ts.map +1 -0
  41. package/dist/stall-signatures.js +219 -0
  42. package/dist/stall-signatures.js.map +1 -0
  43. package/dist/verify-green.d.ts +62 -0
  44. package/dist/verify-green.d.ts.map +1 -0
  45. package/dist/verify-green.js +53 -0
  46. package/dist/verify-green.js.map +1 -0
  47. package/package.json +1 -1
@@ -25,7 +25,8 @@ export type AgentInfo = z.infer<typeof AgentInfoSchema>;
25
25
  */
26
26
  export declare function agentInfoEquals(a: AgentInfo | null, b: AgentInfo | null): boolean;
27
27
  /**
28
- * Outcome of a conditional (compare-and-set) register (groundnuty/macf#439).
28
+ * Outcome of a conditional (compare-and-set) register (groundnuty/macf#439,
29
+ * reshaped by groundnuty/macf#702 — over-register).
29
30
  *
30
31
  * `ok: true` — this instance now owns the slot; `current` is the value just
31
32
  * written (=== the `info` passed in). Ownership strength is
@@ -36,11 +37,38 @@ export declare function agentInfoEquals(a: AgentInfo | null, b: AgentInfo | null
36
37
  * API; see registry.ts `registerConditional`).
37
38
  * `ok: false` — a concurrent writer changed the slot between the collision
38
39
  * check and this write; `current` is the conflicting value
39
- * observed (null if the slot was emptied). The caller must NOT
40
- * treat itself as the registered owner — abort or re-check.
40
+ * observed (null if the slot was emptied). The caller decides
41
+ * claim-vs-yield from `reason` (see below) — `ok:false` is NO
42
+ * LONGER an unconditional abort signal (groundnuty/macf#702 fixed
43
+ * the devops 8h-outage bug where it was).
44
+ *
45
+ * `reason` discriminates WHY the CAS resolved the way it did — this is the
46
+ * groundnuty/macf#702 fix. Before #702, `ok:false` was always treated as "lost
47
+ * a race, abort" (`RegisterRaceError`), which mis-fired when the write
48
+ * actually succeeded but the GitHub Variables API's read-after-write lag
49
+ * served a stale GET on the post-write read-back (the exact devops bug:
50
+ * `current == expected` — nothing changed — yet the read-back momentarily
51
+ * still showed the pre-write value, and the old code aborted anyway):
52
+ *
53
+ * - `'claimed'` — `ok:true`. The write landed and (when a read-back
54
+ * exists) is confirmed, OR the pre-write compare
55
+ * found the slot unchanged from `expected` and the
56
+ * write was issued — the CAS ALWAYS claims in this
57
+ * case, never aborts, even if a same-backend
58
+ * read-back lag means confirmation isn't
59
+ * immediately visible (groundnuty/macf#702 §1).
60
+ * - `'lost-to-newer'` — `ok:false`. The write-time re-read (or read-back)
61
+ * observed a DIFFERENT registration than `expected`
62
+ * — a genuine concurrent writer. The caller
63
+ * (server.ts) is responsible for classifying that
64
+ * `current` as newer-live (yield) vs stale/older
65
+ * (retry the takeover) — the registry layer only
66
+ * reports the conflicting value; it doesn't probe
67
+ * liveness (that's `collision.ts`'s job).
41
68
  */
42
69
  export interface RegisterResult {
43
70
  readonly ok: boolean;
71
+ readonly reason: 'claimed' | 'lost-to-newer';
44
72
  readonly current: AgentInfo | null;
45
73
  }
46
74
  /**
@@ -98,15 +126,32 @@ export interface HeartbeatResult {
98
126
  export interface Registry {
99
127
  readonly register: (name: string, info: AgentInfo) => Promise<void>;
100
128
  /**
101
- * Compare-and-set register (groundnuty/macf#439): write `info` only if the
102
- * slot still matches `expected` (the value observed during the collision
103
- * check; `null` = expected-absent). Closes the TOCTOU window between the
104
- * collision check's read and the registration write — a racing second
105
- * writer can't silently clobber the first. Returns whether this instance
106
- * won the slot. Backends differ in atomicity: the local registry is fully
107
- * atomic under its file lock; the GitHub-Variables backend narrows the
108
- * window with a pre-write compare + post-write read-back verify (no native
109
- * CAS primitive exists on that API — see registry.ts).
129
+ * Compare-and-set register (groundnuty/macf#439; over-register semantics
130
+ * per groundnuty/macf#702): write `info` if the slot still matches
131
+ * `expected` (the value observed during the collision check; `null` =
132
+ * expected-absent). Closes the TOCTOU window between the collision check's
133
+ * read and the registration write — a racing second writer can't silently
134
+ * clobber the first. Backends differ in atomicity: the local registry is
135
+ * fully atomic under its file lock (no read-back needed); the
136
+ * GitHub-Variables backend narrows the window with a pre-write compare and
137
+ * best-effort-confirms via a post-write read-back (no native CAS primitive
138
+ * exists on that API — see registry.ts).
139
+ *
140
+ * **groundnuty/macf#702 (over-register):** `current == expected` — the slot
141
+ * is UNCHANGED, including the case where `expected` is itself a stale/dead
142
+ * entry the caller decided to take over — is ALWAYS a claim (`ok:true,
143
+ * reason:'claimed'`), never a failure. This holds even when a same-backend
144
+ * read-back can't yet observe the write (GitHub Variables API
145
+ * read-after-write lag) — a lagging read-back is a confirmation gap, not a
146
+ * lost race, and must not be reported as one (this was the devops 8h-outage
147
+ * bug: `current==expected==<stale>` incorrectly returned `ok:false`).
148
+ * `ok:false, reason:'lost-to-newer'` fires ONLY when the write-time re-read
149
+ * (or the read-back) observes a value that is a genuinely different
150
+ * registration than the caller's own `info` AND different from `expected`
151
+ * — a real concurrent writer. The registry layer reports the conflicting
152
+ * `current` but does not classify it as live/dead/newer/older — that
153
+ * liveness judgment is the caller's (server.ts + collision.ts), which
154
+ * decides claim-over-stale (retry) vs yield-to-newer-live.
110
155
  */
111
156
  readonly registerConditional: (name: string, info: AgentInfo, expected: AgentInfo | null) => Promise<RegisterResult>;
112
157
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/registry/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/registry/types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/registry/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,4DAA4D;AAE5D,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACjC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IACrC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB;;;;;;;;;OASG;IACH,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACtC,CAAC,CAAC;AAIH;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAAC,CAAmB,EAAE,CAAmB;IACtE,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC7C,OAAO,CACL,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI;QACjB,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI;QACjB,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI;QACjB,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,WAAW;QAC/B,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,CACxB,CAAC;AACJ,CAAC;AAoJD,iCAAiC;AAEjC,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;IACtB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACvB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,MAAM,CAAC;IAClD,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;IAC1B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACxB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/C,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;IACvB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACxB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACxB,CAAC,CAAC;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChD,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;IACxB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACxB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC;IAC1C,uBAAuB;IACvB,2BAA2B;IAC3B,wBAAwB;IACxB,yBAAyB;CAC1B,CAAC,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/registry/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,4DAA4D;AAE5D,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACjC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IACrC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB;;;;;;;;;OASG;IACH,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACtC,CAAC,CAAC;AAIH;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAAC,CAAmB,EAAE,CAAmB;IACtE,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC7C,OAAO,CACL,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI;QACjB,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI;QACjB,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI;QACjB,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,WAAW;QAC/B,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,CACxB,CAAC;AACJ,CAAC;AAiMD,iCAAiC;AAEjC,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;IACtB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACvB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,MAAM,CAAC;IAClD,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;IAC1B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACxB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/C,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;IACvB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACxB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACxB,CAAC,CAAC;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChD,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;IACxB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACxB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC;IAC1C,uBAAuB;IACvB,2BAA2B;IAC3B,wBAAwB;IACxB,yBAAyB;CAC1B,CAAC,CAAC"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Stall-signature allowlist — config schema, the pure pane-matcher, and the
3
+ * per-action fire-cap logic behind `macf fleet resume` (DR-037 / groundnuty/macf#686,
4
+ * porting `groundnuty/macf-devops-toolkit:fleet/resume.sh` + `stall-signatures.json`).
5
+ *
6
+ * SAFETY-CRITICAL. `macf fleet resume` acts on an IDLE agent — an idle agent is one
7
+ * of three things and only the pane tells them apart (operator, 2026-06-28):
8
+ *
9
+ * - idle-CLEAN (no signature matched) → legitimately idle/done → DO NOTHING.
10
+ * - idle-STALLED (rate-limit / turn-abort) → `action: nudge` — resume the SAME
11
+ * session (a nudge preserves in-progress work; a restart loses it + re-hits a
12
+ * rate-limit). The gentlest recovery.
13
+ * - idle-BLOCKED (permission / trust / → `action: report` — a DURABLE operator
14
+ * skill / memory prompt) alert, NEVER auto-answered (an
15
+ * authorization decision needs a human;
16
+ * DR-033 ceremony-not-authorization).
17
+ *
18
+ * The safety contract is the sibling of DR-033's: **allowlist-only, never a blind
19
+ * nudge.** We act ONLY on an idle agent whose recent pane matches a KNOWN, vetted
20
+ * signature. An unmatched idle pane is legitimately idle/done → never touched (no
21
+ * spam). An unknown-but-blocking prompt falls through to no-match → skip; the
22
+ * operator adds a `report` signature as the TUI evolves (the allowlist is DATA).
23
+ *
24
+ * This module holds the PURE logic — the config the operator curates in
25
+ * `.claude/.macf/stall-signatures.json`, the schema/validator the CLI runs at
26
+ * `macf init/update/rules refresh`, and the frame-matcher the `fleet resume`
27
+ * decision layer drives off. The tested reference is the devops `resume.sh` bash
28
+ * (which reimplements the same match-then-dispatch in bash + jq); the two MUST
29
+ * stay in lockstep.
30
+ */
31
+ import { z } from 'zod';
32
+ import { MacfError } from './errors.js';
33
+ /** A matched stall's dispatched action: nudge a stalled agent, or report a blocked one. */
34
+ export declare const STALL_ACTIONS: readonly ["nudge", "report"];
35
+ export type StallAction = (typeof STALL_ACTIONS)[number];
36
+ /**
37
+ * Per-action default fire cap when an entry omits `max_fires` — a nudge is a
38
+ * gentle repeatable resume (retry a few times, then escalate), a report is a
39
+ * one-shot durable alert per episode (re-alerting spams the operator). Mirrors
40
+ * the devops `resume.sh` defaults (`nudge`=3, `report`=1).
41
+ */
42
+ export declare const DEFAULT_NUDGE_MAX_FIRES = 3;
43
+ export declare const DEFAULT_REPORT_MAX_FIRES = 1;
44
+ /** Thrown when the stall-signatures config fails schema / regex validation. */
45
+ export declare class StallSignaturesError extends MacfError {
46
+ constructor(message: string);
47
+ }
48
+ /**
49
+ * A single stall-signature allowlist entry.
50
+ *
51
+ * - `name` — the signature's stable identifier (rendered in the plan / the
52
+ * alert title, so a fire-cap counter can key off it).
53
+ * - `signature` — a regular-expression source matched CASE-INSENSITIVELY against
54
+ * the captured idle pane (the ERE the devops bash greps with `grep -qiE`; the
55
+ * seeds' alternation/`\?`/`\.` forms are JS-RegExp-compatible). Validated for
56
+ * compilability at load — a malformed pattern is a LOUD config error, never a
57
+ * silent non-match.
58
+ * - `action` — `nudge` (resume a stalled agent) or `report` (durable alert for a
59
+ * blocked agent; NEVER auto-answered). Defaults to `nudge`.
60
+ * - `nudge` — the text sent into the TUI for `action: nudge` (default: a generic
61
+ * "please continue").
62
+ * - `report` — the operator-facing summary for `action: report` (default: a
63
+ * generic "blocked on an operator-input prompt").
64
+ * - `max_fires` — per-episode fire cap; omitted → the per-action default.
65
+ * - `comment` — operator documentation (why this signature; caveats). Ignored by
66
+ * the matcher; kept in the schema so the seed's `comment` fields validate.
67
+ *
68
+ * `.strict()` so an operator typo in a field name (e.g. `signatures`) is a loud
69
+ * validation error, not a silently-ignored field.
70
+ */
71
+ export declare const StallSignatureEntrySchema: z.ZodObject<{
72
+ name: z.ZodString;
73
+ signature: z.ZodString;
74
+ action: z.ZodDefault<z.ZodEnum<{
75
+ nudge: "nudge";
76
+ report: "report";
77
+ }>>;
78
+ nudge: z.ZodOptional<z.ZodString>;
79
+ report: z.ZodOptional<z.ZodString>;
80
+ max_fires: z.ZodOptional<z.ZodNumber>;
81
+ comment: z.ZodOptional<z.ZodString>;
82
+ }, z.core.$strict>;
83
+ export type StallSignatureEntry = z.infer<typeof StallSignatureEntrySchema>;
84
+ /**
85
+ * The whole config: a bare array of entries — the exact shape of the devops
86
+ * `fleet/stall-signatures.json` reference (so the two files are interchangeable).
87
+ */
88
+ export declare const StallSignaturesConfigSchema: z.ZodArray<z.ZodObject<{
89
+ name: z.ZodString;
90
+ signature: z.ZodString;
91
+ action: z.ZodDefault<z.ZodEnum<{
92
+ nudge: "nudge";
93
+ report: "report";
94
+ }>>;
95
+ nudge: z.ZodOptional<z.ZodString>;
96
+ report: z.ZodOptional<z.ZodString>;
97
+ max_fires: z.ZodOptional<z.ZodNumber>;
98
+ comment: z.ZodOptional<z.ZodString>;
99
+ }, z.core.$strict>>;
100
+ export type StallSignaturesConfig = z.infer<typeof StallSignaturesConfigSchema>;
101
+ /**
102
+ * Validate + regex-check a parsed config. Throws `StallSignaturesError` on a
103
+ * schema-invalid shape OR an uncompilable `signature` (loud — a malformed
104
+ * allowlist must never silently degrade to "matches nothing"). Returns the
105
+ * accepted entries.
106
+ */
107
+ export declare function loadStallSignatures(raw: unknown): readonly StallSignatureEntry[];
108
+ /**
109
+ * The per-episode fire cap for an entry — its explicit `max_fires`, else the
110
+ * per-action default (nudge 3 / report 1).
111
+ */
112
+ export declare function resolveMaxFires(entry: StallSignatureEntry): number;
113
+ /** Fire-cap guard: may this stall-signature fire again given its prior fire count? */
114
+ export declare function canFireStall(entry: StallSignatureEntry, firedCount: number): boolean;
115
+ /**
116
+ * First allowlist entry whose `signature` regex matches the captured pane
117
+ * (case-insensitive), or `null` when the pane matches nothing — the idle-CLEAN
118
+ * case the caller treats as "legitimately idle/done, never touched". Pure.
119
+ *
120
+ * A signature that fails to compile is skipped (defensively) rather than
121
+ * throwing here — `loadStallSignatures` is the fail-loud gate; the matcher stays
122
+ * total so a single bad entry never breaks a whole sweep.
123
+ */
124
+ export declare function matchStallSignature(pane: string, entries: readonly StallSignatureEntry[]): StallSignatureEntry | null;
125
+ /**
126
+ * The canonical seed `macf init` writes when no `stall-signatures.json` exists.
127
+ * Ported verbatim from `groundnuty/macf-devops-toolkit:fleet/stall-signatures.json`
128
+ * (the tested reference). Two `nudge` signatures (transient rate-limit / turn-abort
129
+ * — resume the same session) and three `report` signatures (permission / trust /
130
+ * skill-or-memory prompts — authorization decisions that must NEVER be
131
+ * auto-answered, only surfaced as a durable operator alert).
132
+ *
133
+ * Signature strings are best-effort across Claude Code versions — the allowlist is
134
+ * DATA; an operator tunes it against the real pane as the TUI evolves. An imperfect
135
+ * signature fails SAFE (falls through to no-match → the agent is left untouched,
136
+ * never a wrong action).
137
+ */
138
+ export declare const STALL_SIGNATURES_SEED: StallSignaturesConfig;
139
+ //# sourceMappingURL=stall-signatures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stall-signatures.d.ts","sourceRoot":"","sources":["../src/stall-signatures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,2FAA2F;AAC3F,eAAO,MAAM,aAAa,8BAA+B,CAAC;AAC1D,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,IAAI,CAAC;AACzC,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,+EAA+E;AAC/E,qBAAa,oBAAqB,SAAQ,SAAS;gBACrC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;kBAU3B,CAAC;AACZ,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E;;;GAGG;AACH,eAAO,MAAM,2BAA2B;;;;;;;;;;;mBAAqC,CAAC;AAC9E,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAEhF;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,mBAAmB,EAAE,CAsBhF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,mBAAmB,GAAG,MAAM,CAGlE;AAED,sFAAsF;AACtF,wBAAgB,YAAY,CAAC,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAEpF;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,SAAS,mBAAmB,EAAE,GACtC,mBAAmB,GAAG,IAAI,CAW5B;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,EAAE,qBA8DnC,CAAC"}
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Stall-signature allowlist — config schema, the pure pane-matcher, and the
3
+ * per-action fire-cap logic behind `macf fleet resume` (DR-037 / groundnuty/macf#686,
4
+ * porting `groundnuty/macf-devops-toolkit:fleet/resume.sh` + `stall-signatures.json`).
5
+ *
6
+ * SAFETY-CRITICAL. `macf fleet resume` acts on an IDLE agent — an idle agent is one
7
+ * of three things and only the pane tells them apart (operator, 2026-06-28):
8
+ *
9
+ * - idle-CLEAN (no signature matched) → legitimately idle/done → DO NOTHING.
10
+ * - idle-STALLED (rate-limit / turn-abort) → `action: nudge` — resume the SAME
11
+ * session (a nudge preserves in-progress work; a restart loses it + re-hits a
12
+ * rate-limit). The gentlest recovery.
13
+ * - idle-BLOCKED (permission / trust / → `action: report` — a DURABLE operator
14
+ * skill / memory prompt) alert, NEVER auto-answered (an
15
+ * authorization decision needs a human;
16
+ * DR-033 ceremony-not-authorization).
17
+ *
18
+ * The safety contract is the sibling of DR-033's: **allowlist-only, never a blind
19
+ * nudge.** We act ONLY on an idle agent whose recent pane matches a KNOWN, vetted
20
+ * signature. An unmatched idle pane is legitimately idle/done → never touched (no
21
+ * spam). An unknown-but-blocking prompt falls through to no-match → skip; the
22
+ * operator adds a `report` signature as the TUI evolves (the allowlist is DATA).
23
+ *
24
+ * This module holds the PURE logic — the config the operator curates in
25
+ * `.claude/.macf/stall-signatures.json`, the schema/validator the CLI runs at
26
+ * `macf init/update/rules refresh`, and the frame-matcher the `fleet resume`
27
+ * decision layer drives off. The tested reference is the devops `resume.sh` bash
28
+ * (which reimplements the same match-then-dispatch in bash + jq); the two MUST
29
+ * stay in lockstep.
30
+ */
31
+ import { z } from 'zod';
32
+ import { MacfError } from './errors.js';
33
+ /** A matched stall's dispatched action: nudge a stalled agent, or report a blocked one. */
34
+ export const STALL_ACTIONS = ['nudge', 'report'];
35
+ /**
36
+ * Per-action default fire cap when an entry omits `max_fires` — a nudge is a
37
+ * gentle repeatable resume (retry a few times, then escalate), a report is a
38
+ * one-shot durable alert per episode (re-alerting spams the operator). Mirrors
39
+ * the devops `resume.sh` defaults (`nudge`=3, `report`=1).
40
+ */
41
+ export const DEFAULT_NUDGE_MAX_FIRES = 3;
42
+ export const DEFAULT_REPORT_MAX_FIRES = 1;
43
+ /** Thrown when the stall-signatures config fails schema / regex validation. */
44
+ export class StallSignaturesError extends MacfError {
45
+ constructor(message) {
46
+ super('STALL_SIGNATURES_ERROR', message);
47
+ this.name = 'StallSignaturesError';
48
+ }
49
+ }
50
+ /**
51
+ * A single stall-signature allowlist entry.
52
+ *
53
+ * - `name` — the signature's stable identifier (rendered in the plan / the
54
+ * alert title, so a fire-cap counter can key off it).
55
+ * - `signature` — a regular-expression source matched CASE-INSENSITIVELY against
56
+ * the captured idle pane (the ERE the devops bash greps with `grep -qiE`; the
57
+ * seeds' alternation/`\?`/`\.` forms are JS-RegExp-compatible). Validated for
58
+ * compilability at load — a malformed pattern is a LOUD config error, never a
59
+ * silent non-match.
60
+ * - `action` — `nudge` (resume a stalled agent) or `report` (durable alert for a
61
+ * blocked agent; NEVER auto-answered). Defaults to `nudge`.
62
+ * - `nudge` — the text sent into the TUI for `action: nudge` (default: a generic
63
+ * "please continue").
64
+ * - `report` — the operator-facing summary for `action: report` (default: a
65
+ * generic "blocked on an operator-input prompt").
66
+ * - `max_fires` — per-episode fire cap; omitted → the per-action default.
67
+ * - `comment` — operator documentation (why this signature; caveats). Ignored by
68
+ * the matcher; kept in the schema so the seed's `comment` fields validate.
69
+ *
70
+ * `.strict()` so an operator typo in a field name (e.g. `signatures`) is a loud
71
+ * validation error, not a silently-ignored field.
72
+ */
73
+ export const StallSignatureEntrySchema = z
74
+ .object({
75
+ name: z.string().min(1),
76
+ signature: z.string().min(1),
77
+ action: z.enum(STALL_ACTIONS).default('nudge'),
78
+ nudge: z.string().min(1).optional(),
79
+ report: z.string().min(1).optional(),
80
+ max_fires: z.number().int().positive().optional(),
81
+ comment: z.string().optional(),
82
+ })
83
+ .strict();
84
+ /**
85
+ * The whole config: a bare array of entries — the exact shape of the devops
86
+ * `fleet/stall-signatures.json` reference (so the two files are interchangeable).
87
+ */
88
+ export const StallSignaturesConfigSchema = z.array(StallSignatureEntrySchema);
89
+ /**
90
+ * Validate + regex-check a parsed config. Throws `StallSignaturesError` on a
91
+ * schema-invalid shape OR an uncompilable `signature` (loud — a malformed
92
+ * allowlist must never silently degrade to "matches nothing"). Returns the
93
+ * accepted entries.
94
+ */
95
+ export function loadStallSignatures(raw) {
96
+ const parsed = StallSignaturesConfigSchema.safeParse(raw);
97
+ if (!parsed.success) {
98
+ throw new StallSignaturesError(`stall-signatures config is invalid: ${parsed.error.issues
99
+ .map((i) => `${i.path.join('.') || '<root>'}: ${i.message}`)
100
+ .join('; ')}`);
101
+ }
102
+ for (const entry of parsed.data) {
103
+ try {
104
+ // Validate compilability up front (fail-loud). Case-insensitive to mirror
105
+ // the devops `grep -iE`.
106
+ new RegExp(entry.signature, 'i');
107
+ }
108
+ catch (err) {
109
+ const msg = err instanceof Error ? err.message : String(err);
110
+ throw new StallSignaturesError(`stall-signatures entry "${entry.name}" has an invalid regex signature: ${msg}`);
111
+ }
112
+ }
113
+ return parsed.data;
114
+ }
115
+ /**
116
+ * The per-episode fire cap for an entry — its explicit `max_fires`, else the
117
+ * per-action default (nudge 3 / report 1).
118
+ */
119
+ export function resolveMaxFires(entry) {
120
+ if (entry.max_fires !== undefined)
121
+ return entry.max_fires;
122
+ return entry.action === 'report' ? DEFAULT_REPORT_MAX_FIRES : DEFAULT_NUDGE_MAX_FIRES;
123
+ }
124
+ /** Fire-cap guard: may this stall-signature fire again given its prior fire count? */
125
+ export function canFireStall(entry, firedCount) {
126
+ return firedCount < resolveMaxFires(entry);
127
+ }
128
+ /**
129
+ * First allowlist entry whose `signature` regex matches the captured pane
130
+ * (case-insensitive), or `null` when the pane matches nothing — the idle-CLEAN
131
+ * case the caller treats as "legitimately idle/done, never touched". Pure.
132
+ *
133
+ * A signature that fails to compile is skipped (defensively) rather than
134
+ * throwing here — `loadStallSignatures` is the fail-loud gate; the matcher stays
135
+ * total so a single bad entry never breaks a whole sweep.
136
+ */
137
+ export function matchStallSignature(pane, entries) {
138
+ for (const entry of entries) {
139
+ let re;
140
+ try {
141
+ re = new RegExp(entry.signature, 'i');
142
+ }
143
+ catch {
144
+ continue;
145
+ }
146
+ if (re.test(pane))
147
+ return entry;
148
+ }
149
+ return null;
150
+ }
151
+ /**
152
+ * The canonical seed `macf init` writes when no `stall-signatures.json` exists.
153
+ * Ported verbatim from `groundnuty/macf-devops-toolkit:fleet/stall-signatures.json`
154
+ * (the tested reference). Two `nudge` signatures (transient rate-limit / turn-abort
155
+ * — resume the same session) and three `report` signatures (permission / trust /
156
+ * skill-or-memory prompts — authorization decisions that must NEVER be
157
+ * auto-answered, only surfaced as a durable operator alert).
158
+ *
159
+ * Signature strings are best-effort across Claude Code versions — the allowlist is
160
+ * DATA; an operator tunes it against the real pane as the TUI evolves. An imperfect
161
+ * signature fails SAFE (falls through to no-match → the agent is left untouched,
162
+ * never a wrong action).
163
+ */
164
+ export const STALL_SIGNATURES_SEED = [
165
+ {
166
+ name: 'rate-limit',
167
+ signature: '(temporarily limiting requests|Rate limited|API Error.*limiting)',
168
+ action: 'nudge',
169
+ nudge: 'You appear to have stalled on a transient API rate-limit (server-side, not ' +
170
+ 'your usage quota). The limit should have cleared — please continue your work ' +
171
+ 'where you left off.',
172
+ max_fires: 4,
173
+ comment: 'Anthropic server-side capacity throttle (NOT the account quota). Leaves the ' +
174
+ 'agent idle mid-flow. Nudge resumes the SAME session (preserves work); restart ' +
175
+ 'would lose it + re-hit the limit. verify-resumed self-corrects a nudge into a ' +
176
+ 'still-active throttle (back off + retry).',
177
+ },
178
+ {
179
+ name: 'turn-aborted',
180
+ signature: '(Request was aborted|aborted by user|API Error: Connection error)',
181
+ action: 'nudge',
182
+ nudge: 'Your last turn aborted before completing. Please continue your work where you left off.',
183
+ max_fires: 3,
184
+ comment: 'A turn that died on a transient connection/abort error, leaving the agent ' +
185
+ 'idle. Same resume-not-restart logic.',
186
+ },
187
+ {
188
+ name: 'permission-prompt',
189
+ signature: '(Do you want to proceed\\?|Do you want to make this edit\\?|Do you want to create|Yes, and don.t ask again)',
190
+ action: 'report',
191
+ report: 'blocked on a tool-permission prompt ("Do you want to proceed?") — needs the ' +
192
+ 'operator to approve/deny. Cannot be auto-answered (an authorization decision, ' +
193
+ 'per DR-033 ceremony-not-authorization).',
194
+ max_fires: 1,
195
+ comment: 'Claude Code tool-permission dialog. action=report (NOT nudge/answer): ' +
196
+ 'authorization prompts require human judgment, so the correct automation is to ' +
197
+ 'make the silent block LOUD, never to answer it. macf-devops-toolkit#132.',
198
+ },
199
+ {
200
+ name: 'trust-folder-prompt',
201
+ signature: '(Do you trust the files in this folder\\?|trust the files in this)',
202
+ action: 'report',
203
+ report: 'blocked on a folder-trust prompt — needs the operator to confirm trust. ' +
204
+ 'Authorization decision; cannot be auto-answered.',
205
+ max_fires: 1,
206
+ comment: "Claude Code 'trust this folder' dialog on a new/changed workspace. action=report. #132.",
207
+ },
208
+ {
209
+ name: 'skill-or-memory-prompt',
210
+ signature: '(Allow .* to run\\?|Save this to memory\\?|Add this to memory\\?|Allow this skill)',
211
+ action: 'report',
212
+ report: 'blocked on a skill-approval or memory-accept prompt — needs the operator\'s ' +
213
+ 'decision. Cannot be auto-answered.',
214
+ max_fires: 1,
215
+ comment: 'Skill-approval / memory-accept dialogs. action=report. Signature strings are ' +
216
+ 'best-effort across CC versions — the allowlist is DATA, tune as the TUI evolves. #132.',
217
+ },
218
+ ];
219
+ //# sourceMappingURL=stall-signatures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stall-signatures.js","sourceRoot":"","sources":["../src/stall-signatures.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import type { HealthResponse } from './types.js';
2
+ /** Default poll interval between `probe` attempts (ms). */
3
+ export declare const DEFAULT_VERIFY_GREEN_INTERVAL_MS = 2000;
4
+ /** Inputs to `verifyGreen`. */
5
+ export interface VerifyGreenOptions {
6
+ /** The agent (routing label) to verify — passed straight to `probe`. */
7
+ readonly agent: string;
8
+ /** The version the agent must self-report to be considered green. */
9
+ readonly targetVersion: string;
10
+ /** Total wall-time budget before giving up (ms). */
11
+ readonly timeoutMs: number;
12
+ /** Poll interval between attempts (ms; default `DEFAULT_VERIFY_GREEN_INTERVAL_MS`). */
13
+ readonly intervalMs?: number;
14
+ }
15
+ /**
16
+ * Injectable seams for `verifyGreen` — keeps it a pure function of its inputs
17
+ * (no clock, no network, no timers baked in), so it's unit-testable with a fake
18
+ * probe sequence + a virtual clock.
19
+ */
20
+ export interface VerifyGreenDeps {
21
+ /**
22
+ * Re-resolve the agent's CURRENT registry endpoint and probe its `/health`.
23
+ * Returns the parsed body, or `null` when unreachable (offline / mid-restart /
24
+ * endpoint not yet re-advertised). MUST re-read the registry each call so a
25
+ * restart-self port churn is picked up (DR-037 Decision 5).
26
+ */
27
+ readonly probe: (agent: string) => Promise<HealthResponse | null>;
28
+ /** Sleep `ms` between poll attempts (injected so tests don't wait). */
29
+ readonly sleep: (ms: number) => Promise<void>;
30
+ /** Monotonic-ish clock in ms (injected so tests drive the timeout). */
31
+ readonly now: () => number;
32
+ }
33
+ /**
34
+ * The terminal outcome of a `verifyGreen` poll.
35
+ *
36
+ * - `ok: true` — the agent self-reported the target version; `version` echoes it.
37
+ * - `ok: false` + `reason: 'wrong-version'` — the agent was REACHABLE within the
38
+ * budget but never at the target (`lastVersion` is the last version seen, or
39
+ * `null` if a reachable body carried no version). The common mid-restart case
40
+ * that never converges (bad pin / stuck old process).
41
+ * - `ok: false` + `reason: 'unreachable'` — the agent was NEVER reachable within
42
+ * the budget (every probe returned `null`). The dead / never-relaunched case.
43
+ */
44
+ export type VerifyGreenResult = {
45
+ readonly ok: true;
46
+ readonly version: string;
47
+ } | {
48
+ readonly ok: false;
49
+ readonly reason: 'wrong-version' | 'unreachable';
50
+ readonly lastVersion: string | null;
51
+ };
52
+ /**
53
+ * Poll `deps.probe(agent)` until the agent self-reports `targetVersion`
54
+ * (via `compareSemver === 0`) or the `timeoutMs` budget is exhausted.
55
+ *
56
+ * Guarantees at least one probe (even with `timeoutMs === 0`), then checks the
57
+ * elapsed budget BEFORE each `sleep`, so the loop always terminates. The
58
+ * version comparison uses `compareSemver` (macf-core, macf#424) so a leading
59
+ * `v` / normalization is handled consistently with the rest of the fleet.
60
+ */
61
+ export declare function verifyGreen(opts: VerifyGreenOptions, deps: VerifyGreenDeps): Promise<VerifyGreenResult>;
62
+ //# sourceMappingURL=verify-green.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-green.d.ts","sourceRoot":"","sources":["../src/verify-green.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,2DAA2D;AAC3D,eAAO,MAAM,gCAAgC,OAAO,CAAC;AAErD,+BAA+B;AAC/B,MAAM,WAAW,kBAAkB;IACjC,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,oDAAoD;IACpD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,uFAAuF;IACvF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAClE,uEAAuE;IACvE,QAAQ,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,uEAAuE;IACvE,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,iBAAiB,GACzB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC/C;IACE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,MAAM,EAAE,eAAe,GAAG,aAAa,CAAC;IACjD,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC,CAAC;AAEN;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,kBAAkB,EACxB,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,iBAAiB,CAAC,CAyB5B"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * `verifyGreen` — the shared post-restart health-and-version gate (DR-037
3
+ * Decision 5). Both the rolling-upgrade sequencer and the reconcile loop need it:
4
+ * after a `driver.restart(agent)`, confirm the agent came back REACHABLE at the
5
+ * TARGET version before proceeding to the next agent (upgrade) or clearing the
6
+ * heal (reconcile).
7
+ *
8
+ * Pure + runtime-agnostic (macf-core, DR-037 OQ2). It takes an injectable
9
+ * "re-resolve + probe" callback and polls-with-timeout. The **re-resolve is the
10
+ * caller's job, inside `probe`** — `restart-self` relaunches with a NEW port +
11
+ * instance_id (DR-037 Decision 5), so a correct `probe(agent)` MUST re-read the
12
+ * registry for the FRESH endpoint on each call and never reuse the dead
13
+ * pre-restart `host:port` (a silent-fallback trap: a stale endpoint returns
14
+ * `null` forever → a false "not-green"). `verifyGreen` itself only polls that
15
+ * callback + applies the version check via macf-core's `compareSemver`.
16
+ */
17
+ import { compareSemver } from './semver.js';
18
+ /** Default poll interval between `probe` attempts (ms). */
19
+ export const DEFAULT_VERIFY_GREEN_INTERVAL_MS = 2000;
20
+ /**
21
+ * Poll `deps.probe(agent)` until the agent self-reports `targetVersion`
22
+ * (via `compareSemver === 0`) or the `timeoutMs` budget is exhausted.
23
+ *
24
+ * Guarantees at least one probe (even with `timeoutMs === 0`), then checks the
25
+ * elapsed budget BEFORE each `sleep`, so the loop always terminates. The
26
+ * version comparison uses `compareSemver` (macf-core, macf#424) so a leading
27
+ * `v` / normalization is handled consistently with the rest of the fleet.
28
+ */
29
+ export async function verifyGreen(opts, deps) {
30
+ const intervalMs = opts.intervalMs ?? DEFAULT_VERIFY_GREEN_INTERVAL_MS;
31
+ const start = deps.now();
32
+ let reachedOnce = false;
33
+ let lastVersion = null;
34
+ for (;;) {
35
+ const health = await deps.probe(opts.agent);
36
+ if (health) {
37
+ reachedOnce = true;
38
+ lastVersion = health.version ?? null;
39
+ if (lastVersion !== null && compareSemver(lastVersion, opts.targetVersion) === 0) {
40
+ return { ok: true, version: lastVersion };
41
+ }
42
+ }
43
+ if (deps.now() - start >= opts.timeoutMs) {
44
+ return {
45
+ ok: false,
46
+ reason: reachedOnce ? 'wrong-version' : 'unreachable',
47
+ lastVersion,
48
+ };
49
+ }
50
+ await deps.sleep(intervalMs);
51
+ }
52
+ }
53
+ //# sourceMappingURL=verify-green.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-green.js","sourceRoot":"","sources":["../src/verify-green.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,2DAA2D;AAC3D,MAAM,CAAC,MAAM,gCAAgC,GAAG,IAAI,CAAC;AAoDrD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,IAAwB,EACxB,IAAqB;IAErB,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,gCAAgC,CAAC;IACvE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,IAAI,WAAW,GAAG,KAAK,CAAC;IACxB,IAAI,WAAW,GAAkB,IAAI,CAAC;IAEtC,SAAS,CAAC;QACR,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5C,IAAI,MAAM,EAAE,CAAC;YACX,WAAW,GAAG,IAAI,CAAC;YACnB,WAAW,GAAG,MAAM,CAAC,OAAO,IAAI,IAAI,CAAC;YACrC,IAAI,WAAW,KAAK,IAAI,IAAI,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;gBACjF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;YAC5C,CAAC;QACH,CAAC;QAED,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACzC,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,aAAa;gBACrD,WAAW;aACZ,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groundnuty/macf-core",
3
- "version": "0.2.44",
3
+ "version": "0.2.46",
4
4
  "description": "Shared internals consumed by the MACF CLI and channel-server packages. Not intended for direct external use — will be marked deprecated-internal on first npm publish per DR-022 Amendment A.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",