@groundnuty/macf-core 0.2.44 → 0.2.45

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.
@@ -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.45",
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",