@groundnuty/macf-core 0.2.46 → 0.2.48
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/delivery/index.d.ts +3 -0
- package/dist/delivery/index.d.ts.map +1 -0
- package/dist/delivery/index.js +2 -0
- package/dist/delivery/index.js.map +1 -0
- package/dist/delivery/store.d.ts +135 -0
- package/dist/delivery/store.d.ts.map +1 -0
- package/dist/delivery/store.js +81 -0
- package/dist/delivery/store.js.map +1 -0
- package/dist/discovery.d.ts +19 -8
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +42 -12
- package/dist/discovery.js.map +1 -1
- package/dist/fleet-driver.d.ts +57 -1
- package/dist/fleet-driver.d.ts.map +1 -1
- package/dist/fleet-driver.js.map +1 -1
- package/dist/fleet-upgrade.d.ts +175 -22
- package/dist/fleet-upgrade.d.ts.map +1 -1
- package/dist/fleet-upgrade.js +207 -29
- package/dist/fleet-upgrade.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/prompt-responses.d.ts +9 -0
- package/dist/prompt-responses.d.ts.map +1 -1
- package/dist/prompt-responses.js +10 -1
- package/dist/prompt-responses.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +13 -0
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/dist/fleet-upgrade.d.ts
CHANGED
|
@@ -16,7 +16,10 @@ export type UpgradeDisposition = 'behind' | 'at-target' | 'offline';
|
|
|
16
16
|
export interface AgentUpgradePlan {
|
|
17
17
|
/** The agent's routing label (registry key). */
|
|
18
18
|
readonly agent: string;
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* The fleet this member belongs to — the workspace's PROJECT (macf#710), NOT
|
|
21
|
+
* its (possibly-shared) registry scope. See `WorkspaceRecord.project`.
|
|
22
|
+
*/
|
|
20
23
|
readonly fleet: string;
|
|
21
24
|
/** The agent's live self-reported `/health.version`, or `null` when offline/version-less. */
|
|
22
25
|
readonly runningVersion: string | null;
|
|
@@ -27,18 +30,92 @@ export interface AgentUpgradePlan {
|
|
|
27
30
|
}
|
|
28
31
|
/**
|
|
29
32
|
* The EXECUTE-path outcome of a single agent in the roll:
|
|
30
|
-
* - `upgraded`
|
|
31
|
-
* - `busy-skipped`
|
|
32
|
-
*
|
|
33
|
-
* - `
|
|
33
|
+
* - `upgraded` — rolled + confirmed green on the target. CONTINUES.
|
|
34
|
+
* - `busy-skipped` — busy-gated (or `--wait` timed out still busy),
|
|
35
|
+
* BEFORE any mutation; NOT a failure, CONTINUES.
|
|
36
|
+
* - `config-dirty-skipped` — config-dirty-gated (uncommitted changes on the
|
|
37
|
+
* roll's touched-config surface), BEFORE any
|
|
38
|
+
* mutation; NOT a failure, CONTINUES. The roll
|
|
39
|
+
* OBJECTS with the specific file list + an
|
|
40
|
+
* agent-directed message (macf#725 — replaces
|
|
41
|
+
* macf#722 Fix B's silent skip).
|
|
42
|
+
* - `halted` — verify-green did NOT confirm the target version;
|
|
43
|
+
* the roll STOPS here. `reason` distinguishes WHY
|
|
44
|
+
* (see `HaltReason`).
|
|
45
|
+
*
|
|
46
|
+
* The two pre-flight skip outcomes (`busy-skipped` / `config-dirty-skipped`)
|
|
47
|
+
* are safe to continue past because the agent was NEVER mutated. `halted` is
|
|
48
|
+
* always terminal because the agent WAS rolled and its post-restart state is
|
|
49
|
+
* either confirmed-bad or unconfirmed — neither is safe to leave behind while
|
|
50
|
+
* moving on to the next agent (macf#722 Fix A).
|
|
51
|
+
*/
|
|
52
|
+
export type RollOutcome = 'upgraded' | 'busy-skipped' | 'config-dirty-skipped' | 'halted';
|
|
53
|
+
/**
|
|
54
|
+
* WHY a roll halted (only meaningful when `outcome === 'halted'`):
|
|
55
|
+
* - `bad-release` — verify-green confirmed the agent came back up
|
|
56
|
+
* REACHABLE at its OLD (pre-upgrade) version — a
|
|
57
|
+
* crash-loop / stuck-old-process release. Terminal;
|
|
58
|
+
* stops this fleet's roll + later fleets.
|
|
59
|
+
* - `relaunch-unconfirmed` — past the full verify-green grace budget, the
|
|
60
|
+
* agent was never confirmed at the target version:
|
|
61
|
+
* down the whole time, unreachable, or reachable at
|
|
62
|
+
* some OTHER (neither old-pin nor target) version.
|
|
63
|
+
* "Not yet green" cannot distinguish a slow-but-fine
|
|
64
|
+
* relaunch from a release that crashes on startup
|
|
65
|
+
* (which never shows the old version either), so
|
|
66
|
+
* this is deliberately NOT a continue.
|
|
34
67
|
*/
|
|
35
|
-
export type
|
|
36
|
-
/** The per-agent EXECUTE result. `detail` carries
|
|
68
|
+
export type HaltReason = 'bad-release' | 'relaunch-unconfirmed';
|
|
69
|
+
/** The per-agent EXECUTE result. `detail` carries a human-readable summary. */
|
|
37
70
|
export interface AgentRollResult {
|
|
38
71
|
readonly agent: string;
|
|
39
72
|
readonly outcome: RollOutcome;
|
|
73
|
+
/** Set only when `outcome === 'halted'` — see `HaltReason`. */
|
|
74
|
+
readonly reason?: HaltReason;
|
|
40
75
|
readonly detail?: string;
|
|
41
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* The roll's TOUCHED-CONFIG-SURFACE UNION (macf#722 Fix B, corrected naming
|
|
79
|
+
* macf#725) — the path globs `rollFleet`'s pre-flight config-dirty gate checks
|
|
80
|
+
* for uncommitted tracked changes before touching an agent.
|
|
81
|
+
*
|
|
82
|
+
* **This is a UNION, not an "operator-preserved" set** (the DR-037 Amendment B
|
|
83
|
+
* naming was a misnomer, corrected here macf#725): it spans BOTH (a) the files
|
|
84
|
+
* `macf update` OVERWRITES (the managed surface — canonical rules/scripts/hooks
|
|
85
|
+
* under `.claude/**`, `.macf/*`) and (b) the files `restart-self` would STASH
|
|
86
|
+
* (the operator surface — `settings.local.json`, `rules/project/**`,
|
|
87
|
+
* `env.local.*`, `CLAUDE.md`, a hand-authored `claude.sh`). Both halves answer
|
|
88
|
+
* the SAME pre-flight question the gate asks — "would this roll's `upgrade` +
|
|
89
|
+
* `restart` silently clobber or stash SOMETHING uncommitted on this surface?"
|
|
90
|
+
* — so both belong in the one union the gate checks, regardless of which half
|
|
91
|
+
* of DR-029's managed-vs-operator taxonomy a given path falls in.
|
|
92
|
+
*
|
|
93
|
+
* Sourced from DR-029's Amendment (2026-06-27, macf#598) managed-vs-operator
|
|
94
|
+
* taxonomy: `.claude/**` (rules + scripts + settings — managed AND
|
|
95
|
+
* project-tier), `CLAUDE.md` (operator workbench doc), `claude.sh` (the
|
|
96
|
+
* launcher — operator-preserved when hand-authored/header-less, and harmless
|
|
97
|
+
* to include even when macf-managed since a macf-managed copy is never
|
|
98
|
+
* legitimately "dirty" outside an in-flight `macf update`), and `env.local.*`
|
|
99
|
+
* (the operator-custom env extension slot — see
|
|
100
|
+
* `design/decisions/DR-029-substrate-config-via-init-and-reintegrate.md`
|
|
101
|
+
* Amendment table). Deliberately excludes macf-managed/regenerated files
|
|
102
|
+
* (`env.*` canonical seven, `host-prelude.sh`) that are ONLY ever touched by
|
|
103
|
+
* `macf update` itself and never by an operator — dirtiness there pre-flight
|
|
104
|
+
* would still be a genuine conflict (someone hand-edited a regenerated file),
|
|
105
|
+
* so those ARE covered by `.claude/**`/`.macf/*` where they live; the excluded
|
|
106
|
+
* class is narrower than "everything macf update touches."
|
|
107
|
+
*
|
|
108
|
+
* NOTE (macf#724 review, still applicable post-rename): this gate's set is
|
|
109
|
+
* intentionally a slight SUPERSET of DR-029's *regenerate*-boundary for
|
|
110
|
+
* `claude.sh`. The two answer DIFFERENT questions — this gate: "does the roll
|
|
111
|
+
* need to OBJECT before touching this agent?"; DR-029's header-conditional
|
|
112
|
+
* rule: "should `macf update` REGENERATE this file?". So do NOT "align"
|
|
113
|
+
* `claude.sh` here to DR-029's header-conditional form: that would reintroduce
|
|
114
|
+
* the roll-past-a-dirty-managed-`claude.sh` hole. Unconditional here is
|
|
115
|
+
* harmless when clean (nothing flagged) and protective when dirty, regardless
|
|
116
|
+
* of header.
|
|
117
|
+
*/
|
|
118
|
+
export declare const ROLL_TOUCHED_CONFIG_PATTERNS: readonly [".claude/**", "CLAUDE.md", "claude.sh", "env.local.*"];
|
|
42
119
|
/** The result of rolling ONE fleet (the EXECUTE path). */
|
|
43
120
|
export interface FleetRollResult {
|
|
44
121
|
readonly results: readonly AgentRollResult[];
|
|
@@ -48,6 +125,8 @@ export interface FleetRollResult {
|
|
|
48
125
|
readonly upgraded: number;
|
|
49
126
|
/** Count of agents skipped because they were busy. */
|
|
50
127
|
readonly busySkipped: number;
|
|
128
|
+
/** Count of agents skipped because their config surface was dirty (macf#722 Fix B). */
|
|
129
|
+
readonly configDirtySkipped: number;
|
|
51
130
|
}
|
|
52
131
|
/** Progress events for CLI rendering (the RESULT objects are what tests assert on). */
|
|
53
132
|
export type UpgradeEvent = {
|
|
@@ -64,18 +143,42 @@ export type UpgradeEvent = {
|
|
|
64
143
|
readonly agent: string;
|
|
65
144
|
readonly from: string | null;
|
|
66
145
|
readonly to: string;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* The pre-flight OBJECT (macf#725) — the agent's touched-config surface has
|
|
149
|
+
* UNCOMMITTED changes. `files` is the exact uncommitted-path list;
|
|
150
|
+
* `message` is the ready-to-forward, agent-directed text (inspect + commit /
|
|
151
|
+
* delete / gitignore, then re-run). Fired INSTEAD of any mutation — nothing
|
|
152
|
+
* on this agent was touched.
|
|
153
|
+
*/
|
|
154
|
+
| {
|
|
155
|
+
readonly kind: 'config-dirty-skip';
|
|
156
|
+
readonly agent: string;
|
|
157
|
+
readonly files: readonly string[];
|
|
158
|
+
readonly message: string;
|
|
67
159
|
} | {
|
|
68
160
|
readonly kind: 'busy-skip';
|
|
69
161
|
readonly agent: string;
|
|
70
162
|
readonly waited: boolean;
|
|
71
|
-
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* The post-upgrade modified-files report (macf#725) — fired once an agent is
|
|
166
|
+
* confirmed green, before advancing to the next agent. `files` lists what
|
|
167
|
+
* `macf update` regenerated (and `restart-self` deliberately left
|
|
168
|
+
* uncommitted) in the relaunched agent's workspace; `message` is the
|
|
169
|
+
* ready-to-forward, agent-directed text (review with `git diff` + commit /
|
|
170
|
+
* act as needed).
|
|
171
|
+
*/
|
|
172
|
+
| {
|
|
72
173
|
readonly kind: 'upgraded';
|
|
73
174
|
readonly agent: string;
|
|
74
175
|
readonly version: string;
|
|
176
|
+
readonly modifiedFiles: readonly string[];
|
|
177
|
+
readonly message: string;
|
|
75
178
|
} | {
|
|
76
179
|
readonly kind: 'halt';
|
|
77
180
|
readonly agent: string;
|
|
78
|
-
readonly reason:
|
|
181
|
+
readonly reason: HaltReason;
|
|
79
182
|
readonly lastVersion: string | null;
|
|
80
183
|
};
|
|
81
184
|
/**
|
|
@@ -96,6 +199,16 @@ export interface RollFleetOptions {
|
|
|
96
199
|
readonly waitTimeoutMs?: number;
|
|
97
200
|
/** Re-poll interval while waiting for idle (ms). */
|
|
98
201
|
readonly waitPollMs?: number;
|
|
202
|
+
/**
|
|
203
|
+
* `--force` (macf#722 Fix B / macf#725): roll an agent EVEN IF its config
|
|
204
|
+
* surface is dirty PRE-flight — bypasses the pre-flight config-dirty OBJECT
|
|
205
|
+
* gate. Once bypassed for an agent, that agent's `restart` is ALSO told
|
|
206
|
+
* `leaveConfigUncommitted: true` (same as the normal clean-path transaction)
|
|
207
|
+
* — `--force` means "roll anyway," not "stash the pre-existing dirt," so the
|
|
208
|
+
* pre-existing uncommitted files are left in place exactly like `macf
|
|
209
|
+
* update`'s own regeneration would be.
|
|
210
|
+
*/
|
|
211
|
+
readonly force?: boolean;
|
|
99
212
|
}
|
|
100
213
|
/** Injected side-effect seams for the roll (all fakeable in tests). */
|
|
101
214
|
export interface RollFleetDeps {
|
|
@@ -119,11 +232,46 @@ export declare const DEFAULT_WAIT_TIMEOUT_MS = 300000;
|
|
|
119
232
|
/** Default `--wait` re-poll interval (ms). */
|
|
120
233
|
export declare const DEFAULT_WAIT_POLL_MS = 5000;
|
|
121
234
|
/**
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
235
|
+
* Build the agent-directed OBJECT message for a config-dirty pre-flight skip
|
|
236
|
+
* (macf#725). Pure — exported for CLI-rendering reuse / tests.
|
|
237
|
+
*/
|
|
238
|
+
export declare function buildConfigDirtyMessage(agent: string, files: readonly string[]): string;
|
|
239
|
+
/**
|
|
240
|
+
* Build the agent-directed post-upgrade modified-files message (macf#725).
|
|
241
|
+
* Pure — exported for CLI-rendering reuse / tests.
|
|
242
|
+
*
|
|
243
|
+
* The message names the intended STEADY-STATE explicitly: a clean roll LEAVES
|
|
244
|
+
* this regeneration uncommitted (so the agent can review it), which means the
|
|
245
|
+
* NEXT roll's pre-flight WILL re-flag these same files as uncommitted unless
|
|
246
|
+
* they're committed. That re-flag is intended hygiene ("commit last update's
|
|
247
|
+
* regen before upgrading again"), not a surprise — so the message tells the
|
|
248
|
+
* agent to commit them, not merely review them.
|
|
249
|
+
*/
|
|
250
|
+
export declare function buildModifiedFilesMessage(agent: string, files: readonly string[]): string;
|
|
251
|
+
/**
|
|
252
|
+
* Roll ONE fleet: for every `behind` plan (in order) run the PRE-FLIGHT gates
|
|
253
|
+
* (config-dirty, then busy) → upgrade → restart → verify-green cycle, HALTING
|
|
254
|
+
* the roll on the first agent whose post-restart state isn't confirmed-green.
|
|
255
|
+
* Non-`behind` plans are ignored (planned skips). Pre-flight-gated agents are
|
|
256
|
+
* skipped + reported and NEVER a halt (they were never mutated, so continuing
|
|
257
|
+
* is always safe) — config-dirty is checked first because a busy-but-clean
|
|
258
|
+
* agent may still become idle later (`--wait`), whereas a dirty agent is
|
|
259
|
+
* skip-only regardless. With `wait`, a busy agent is re-polled for idle first.
|
|
260
|
+
* The verb order + HALT semantics mirror `fleet/upgrade.sh`'s `roll_one`.
|
|
261
|
+
*
|
|
262
|
+
* **Transactional + OBJECT-with-message revision (macf#725).** The pre-flight
|
|
263
|
+
* config-dirty gate is the ONLY decision point that inspects PRE-upgrade
|
|
264
|
+
* state: clean (or explicitly `--force`-bypassed) ⇒ the roll enters the
|
|
265
|
+
* transaction and MUST complete it — `upgrade` → `restart(agent,
|
|
266
|
+
* { leaveConfigUncommitted: true })` always runs together, never mutating
|
|
267
|
+
* `upgrade` without following through to `restart`. Dirty (and not forced) ⇒
|
|
268
|
+
* the roll does NOTHING to this agent (no `upgrade`, no `restart`) and OBJECTS
|
|
269
|
+
* via `onEvent` with the exact file list + an agent-directed message —
|
|
270
|
+
* reconcile is MANUAL: the agent (or operator) resolves the flagged files and
|
|
271
|
+
* a clean re-run completes it. This replaces the earlier (macf#722 Fix B)
|
|
272
|
+
* silent-skip + separately-force-stashed-restart shape, which could dirty the
|
|
273
|
+
* workspace via `upgrade` and then have `restart-self`'s OWN guard refuse —
|
|
274
|
+
* aborting mid-transaction with the workspace already mutated.
|
|
127
275
|
*/
|
|
128
276
|
export declare function rollFleet(plans: readonly AgentUpgradePlan[], opts: RollFleetOptions, deps: RollFleetDeps): Promise<FleetRollResult>;
|
|
129
277
|
/** Options for the multi-fleet orchestrator. */
|
|
@@ -134,9 +282,11 @@ export interface UpgradeFleetsOptions extends RollFleetOptions {
|
|
|
134
282
|
/** Injected seams for the multi-fleet orchestrator. */
|
|
135
283
|
export interface UpgradeFleetsDeps {
|
|
136
284
|
/**
|
|
137
|
-
* Resolve the driver for a fleet (
|
|
138
|
-
* `createVmDriverFromConfig` at a representative workspace
|
|
139
|
-
*
|
|
285
|
+
* Resolve the driver for a fleet (a PROJECT identifier — macf#710). Production
|
|
286
|
+
* binds `createVmDriverFromConfig` at a representative workspace OF THAT
|
|
287
|
+
* PROJECT (so the driver's mTLS CA + registry namespace are the project's own,
|
|
288
|
+
* never a sibling project's sharing the same registry scope); tests return a
|
|
289
|
+
* fake. `null` ⇒ the fleet cannot be reached (skip + report, NOT a halt).
|
|
140
290
|
*/
|
|
141
291
|
readonly resolveDriver: (fleet: string) => Promise<FleetDriver | null>;
|
|
142
292
|
readonly verifyGreen: (opts: VerifyGreenOptions) => Promise<VerifyGreenResult>;
|
|
@@ -161,11 +311,14 @@ export interface FleetUpgradeReport {
|
|
|
161
311
|
readonly halted: boolean;
|
|
162
312
|
}
|
|
163
313
|
/**
|
|
164
|
-
* Orchestrate a rolling upgrade across the ORDERED `fleets` (
|
|
165
|
-
* identifiers
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
314
|
+
* Orchestrate a rolling upgrade across the ORDERED `fleets` (PROJECT
|
|
315
|
+
* identifiers — macf#710: a "fleet" == a project, which owns its own CA +
|
|
316
|
+
* registry namespace + version cadence; a profile/org registry scope hosting N
|
|
317
|
+
* projects yields N fleets here, never one), fleet-by-fleet. Each fleet is
|
|
318
|
+
* probed + planned; in EXECUTE mode it is then rolled, and a HALT in one fleet
|
|
319
|
+
* STOPS the whole run — later fleets are never started (a bad release cannot
|
|
320
|
+
* cascade across fleets). An unresolvable fleet is skipped + reported and does
|
|
321
|
+
* NOT halt the run.
|
|
169
322
|
*/
|
|
170
323
|
export declare function upgradeFleets(fleets: readonly string[], opts: UpgradeFleetsOptions, deps: UpgradeFleetsDeps): Promise<FleetUpgradeReport>;
|
|
171
324
|
//# sourceMappingURL=fleet-upgrade.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fleet-upgrade.d.ts","sourceRoot":"","sources":["../src/fleet-upgrade.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fleet-upgrade.d.ts","sourceRoot":"","sources":["../src/fleet-upgrade.ts"],"names":[],"mappings":"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"}
|
package/dist/fleet-upgrade.js
CHANGED
|
@@ -7,21 +7,46 @@
|
|
|
7
7
|
* logic lives in macf-core; the per-runtime driver bodies live in `packages/macf`
|
|
8
8
|
* or a future K8s package). It NEVER touches `/proc`, tmux, `capture-pane`, or
|
|
9
9
|
* kubectl — it drives entirely off the injected `FleetDriver` verbs
|
|
10
|
-
* (`probe`/`discoverWorkspaces`/`isBusy`/`upgrade`/`restart
|
|
11
|
-
* `verifyGreen` runner, so the SAME
|
|
12
|
-
* host, and a far-future K8s deployment
|
|
10
|
+
* (`probe`/`discoverWorkspaces`/`isBusy`/`listDirtyConfig`/`upgrade`/`restart`/
|
|
11
|
+
* `listModifiedFiles`) plus an injected `verifyGreen` runner, so the SAME
|
|
12
|
+
* sequencer rolls a VM tmux fleet, a macOS host, and a far-future K8s deployment
|
|
13
|
+
* by swapping only the driver.
|
|
13
14
|
*
|
|
14
15
|
* The state machine (per fleet, one agent at a time):
|
|
15
16
|
*
|
|
16
17
|
* probe() + discoverWorkspaces()
|
|
17
18
|
* → for each member whose RUNNING version < target (compareSemver):
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
19
|
+
* PRE-FLIGHT gate (BEFORE any mutation — never touches the agent):
|
|
20
|
+
* config-dirty-gate (driver.listDirtyConfig → non-empty ⇒ OBJECT:
|
|
21
|
+
* SKIP+REPORT the list of uncommitted files + an agent-directed
|
|
22
|
+
* message to inspect-and-commit-or-delete-or-gitignore-then-retry,
|
|
23
|
+
* unless `--force`; macf#725 — TRANSACTIONAL revision of macf#722
|
|
24
|
+
* Fix B: rather than silently stashing operator dirt, the roll
|
|
25
|
+
* OBJECTS and does NOTHING until the agent resolves it)
|
|
26
|
+
* busy-gate (driver.isBusy → busy ⇒ SKIP+REPORT, or `--wait` for idle)
|
|
27
|
+
* → driver.upgrade(agent) // dirties the managed config surface —
|
|
28
|
+
* // this is EXPECTED, not a fault
|
|
29
|
+
* → driver.restart(agent, { leaveConfigUncommitted: true })
|
|
30
|
+
* // relaunches WITHOUT stashing macf update's own regeneration —
|
|
31
|
+
* // the pre-flight gate already proved the ONLY dirt is the
|
|
32
|
+
* // upgrade's, so leaving it uncommitted (visible via `git status`
|
|
33
|
+
* // in the relaunched agent) is safe + intended (macf#725)
|
|
34
|
+
* → verifyGreen({agent, target}) // re-resolves the fresh endpoint
|
|
35
|
+
* each poll, over a relaunch-aware grace budget (`--verify-timeout`)
|
|
36
|
+
* → green on TARGET ⇒ driver.listModifiedFiles(agent) → emit an
|
|
37
|
+
* agent-directed "review + commit what macf update regenerated"
|
|
38
|
+
* message + list, THEN next agent (`upgraded`)
|
|
39
|
+
* → confirmed back on the OLD (pre-upgrade) version ⇒ HALT the
|
|
40
|
+
* roll, reason `bad-release` (a crash-loop / stuck-old-process
|
|
41
|
+
* release stalls at agent 1 and CANNOT brick the fleet)
|
|
42
|
+
* → past-grace still NOT confirmed green (down / unreachable /
|
|
43
|
+
* some other unknown version) ⇒ HALT the roll, reason
|
|
44
|
+
* `relaunch-unconfirmed` — this is NOT a continue: "not yet
|
|
45
|
+
* green" cannot distinguish a slow-but-fine relaunch from a
|
|
46
|
+
* release that crashes on startup (which never shows the old
|
|
47
|
+
* version either), so continuing past an unconfirmed agent
|
|
48
|
+
* would risk cascading a crash-on-start release across the
|
|
49
|
+
* fleet (macf#722 Fix A)
|
|
25
50
|
*
|
|
26
51
|
* Multi-fleet (`--fleet a,b,c`) rolls fleet-by-fleet: each fleet's roll must
|
|
27
52
|
* finish without a HALT before the next fleet starts (a bad release in fleet-1
|
|
@@ -29,8 +54,61 @@
|
|
|
29
54
|
*
|
|
30
55
|
* DRY-RUN is the default at the command boundary: `execute: false` computes +
|
|
31
56
|
* reports the PLAN (probe + classify) and performs NO driver mutations.
|
|
57
|
+
*
|
|
58
|
+
* **Reconcile is MANUAL this iteration (macf#725).** The config-dirty OBJECT
|
|
59
|
+
* path surfaces the dirt via `onEvent` + a non-mutating skip; it does NOT
|
|
60
|
+
* auto-resolve (no auto-stash, no auto-commit, no auto-delete). A clean
|
|
61
|
+
* re-run, after the agent (or operator) resolves the flagged files, completes
|
|
62
|
+
* the roll for that agent.
|
|
32
63
|
*/
|
|
33
64
|
import { compareSemver } from './semver.js';
|
|
65
|
+
/**
|
|
66
|
+
* The roll's TOUCHED-CONFIG-SURFACE UNION (macf#722 Fix B, corrected naming
|
|
67
|
+
* macf#725) — the path globs `rollFleet`'s pre-flight config-dirty gate checks
|
|
68
|
+
* for uncommitted tracked changes before touching an agent.
|
|
69
|
+
*
|
|
70
|
+
* **This is a UNION, not an "operator-preserved" set** (the DR-037 Amendment B
|
|
71
|
+
* naming was a misnomer, corrected here macf#725): it spans BOTH (a) the files
|
|
72
|
+
* `macf update` OVERWRITES (the managed surface — canonical rules/scripts/hooks
|
|
73
|
+
* under `.claude/**`, `.macf/*`) and (b) the files `restart-self` would STASH
|
|
74
|
+
* (the operator surface — `settings.local.json`, `rules/project/**`,
|
|
75
|
+
* `env.local.*`, `CLAUDE.md`, a hand-authored `claude.sh`). Both halves answer
|
|
76
|
+
* the SAME pre-flight question the gate asks — "would this roll's `upgrade` +
|
|
77
|
+
* `restart` silently clobber or stash SOMETHING uncommitted on this surface?"
|
|
78
|
+
* — so both belong in the one union the gate checks, regardless of which half
|
|
79
|
+
* of DR-029's managed-vs-operator taxonomy a given path falls in.
|
|
80
|
+
*
|
|
81
|
+
* Sourced from DR-029's Amendment (2026-06-27, macf#598) managed-vs-operator
|
|
82
|
+
* taxonomy: `.claude/**` (rules + scripts + settings — managed AND
|
|
83
|
+
* project-tier), `CLAUDE.md` (operator workbench doc), `claude.sh` (the
|
|
84
|
+
* launcher — operator-preserved when hand-authored/header-less, and harmless
|
|
85
|
+
* to include even when macf-managed since a macf-managed copy is never
|
|
86
|
+
* legitimately "dirty" outside an in-flight `macf update`), and `env.local.*`
|
|
87
|
+
* (the operator-custom env extension slot — see
|
|
88
|
+
* `design/decisions/DR-029-substrate-config-via-init-and-reintegrate.md`
|
|
89
|
+
* Amendment table). Deliberately excludes macf-managed/regenerated files
|
|
90
|
+
* (`env.*` canonical seven, `host-prelude.sh`) that are ONLY ever touched by
|
|
91
|
+
* `macf update` itself and never by an operator — dirtiness there pre-flight
|
|
92
|
+
* would still be a genuine conflict (someone hand-edited a regenerated file),
|
|
93
|
+
* so those ARE covered by `.claude/**`/`.macf/*` where they live; the excluded
|
|
94
|
+
* class is narrower than "everything macf update touches."
|
|
95
|
+
*
|
|
96
|
+
* NOTE (macf#724 review, still applicable post-rename): this gate's set is
|
|
97
|
+
* intentionally a slight SUPERSET of DR-029's *regenerate*-boundary for
|
|
98
|
+
* `claude.sh`. The two answer DIFFERENT questions — this gate: "does the roll
|
|
99
|
+
* need to OBJECT before touching this agent?"; DR-029's header-conditional
|
|
100
|
+
* rule: "should `macf update` REGENERATE this file?". So do NOT "align"
|
|
101
|
+
* `claude.sh` here to DR-029's header-conditional form: that would reintroduce
|
|
102
|
+
* the roll-past-a-dirty-managed-`claude.sh` hole. Unconditional here is
|
|
103
|
+
* harmless when clean (nothing flagged) and protective when dirty, regardless
|
|
104
|
+
* of header.
|
|
105
|
+
*/
|
|
106
|
+
export const ROLL_TOUCHED_CONFIG_PATTERNS = [
|
|
107
|
+
'.claude/**',
|
|
108
|
+
'CLAUDE.md',
|
|
109
|
+
'claude.sh',
|
|
110
|
+
'env.local.*',
|
|
111
|
+
];
|
|
34
112
|
/**
|
|
35
113
|
* Classify each discovered fleet member against the target version. PURE — no
|
|
36
114
|
* driver, no clock, no network. The single source of the behind/at-target/offline
|
|
@@ -53,7 +131,7 @@ export function planFleetUpgrade(members, state, targetVersion) {
|
|
|
53
131
|
}
|
|
54
132
|
return {
|
|
55
133
|
agent: m.agent,
|
|
56
|
-
fleet: m.
|
|
134
|
+
fleet: m.project,
|
|
57
135
|
runningVersion,
|
|
58
136
|
pinnedVersion: m.versionPin,
|
|
59
137
|
disposition,
|
|
@@ -82,20 +160,107 @@ async function waitForIdle(agent, opts, deps) {
|
|
|
82
160
|
return false;
|
|
83
161
|
}
|
|
84
162
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
163
|
+
* Classify a failed verify-green against the agent's PRE-upgrade running
|
|
164
|
+
* version (macf#722 Fix A). `bad-release` requires POSITIVE confirmation that
|
|
165
|
+
* the agent came back reachable at the OLD pin — everything else (never
|
|
166
|
+
* reachable, or reachable at some other unrecognized version) is
|
|
167
|
+
* `relaunch-unconfirmed`. Never infer `bad-release` from absence of success;
|
|
168
|
+
* only from the last-seen version actually matching the old pin.
|
|
169
|
+
*/
|
|
170
|
+
function classifyHalt(green, oldVersion) {
|
|
171
|
+
if (green.reason === 'wrong-version' &&
|
|
172
|
+
green.lastVersion !== null &&
|
|
173
|
+
oldVersion !== null &&
|
|
174
|
+
compareSemver(green.lastVersion, oldVersion) === 0) {
|
|
175
|
+
return 'bad-release';
|
|
176
|
+
}
|
|
177
|
+
return 'relaunch-unconfirmed';
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Build the agent-directed OBJECT message for a config-dirty pre-flight skip
|
|
181
|
+
* (macf#725). Pure — exported for CLI-rendering reuse / tests.
|
|
182
|
+
*/
|
|
183
|
+
export function buildConfigDirtyMessage(agent, files) {
|
|
184
|
+
return (`Fleet upgrade skipped ${agent} — these files have uncommitted changes ` +
|
|
185
|
+
`\`macf update\` would overwrite: ${files.join(', ')}. Inspect each and ` +
|
|
186
|
+
`commit, delete, or .gitignore it, then re-run the upgrade.`);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Build the agent-directed post-upgrade modified-files message (macf#725).
|
|
190
|
+
* Pure — exported for CLI-rendering reuse / tests.
|
|
191
|
+
*
|
|
192
|
+
* The message names the intended STEADY-STATE explicitly: a clean roll LEAVES
|
|
193
|
+
* this regeneration uncommitted (so the agent can review it), which means the
|
|
194
|
+
* NEXT roll's pre-flight WILL re-flag these same files as uncommitted unless
|
|
195
|
+
* they're committed. That re-flag is intended hygiene ("commit last update's
|
|
196
|
+
* regen before upgrading again"), not a surprise — so the message tells the
|
|
197
|
+
* agent to commit them, not merely review them.
|
|
198
|
+
*/
|
|
199
|
+
export function buildModifiedFilesMessage(agent, files) {
|
|
200
|
+
return (`Fleet upgrade regenerated these files in ${agent}'s workspace: ` +
|
|
201
|
+
`${files.join(', ')}. Review with \`git diff\` and commit them (so the next ` +
|
|
202
|
+
`upgrade's pre-flight doesn't re-flag them as uncommitted) or act as needed.`);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Roll ONE fleet: for every `behind` plan (in order) run the PRE-FLIGHT gates
|
|
206
|
+
* (config-dirty, then busy) → upgrade → restart → verify-green cycle, HALTING
|
|
207
|
+
* the roll on the first agent whose post-restart state isn't confirmed-green.
|
|
208
|
+
* Non-`behind` plans are ignored (planned skips). Pre-flight-gated agents are
|
|
209
|
+
* skipped + reported and NEVER a halt (they were never mutated, so continuing
|
|
210
|
+
* is always safe) — config-dirty is checked first because a busy-but-clean
|
|
211
|
+
* agent may still become idle later (`--wait`), whereas a dirty agent is
|
|
212
|
+
* skip-only regardless. With `wait`, a busy agent is re-polled for idle first.
|
|
213
|
+
* The verb order + HALT semantics mirror `fleet/upgrade.sh`'s `roll_one`.
|
|
214
|
+
*
|
|
215
|
+
* **Transactional + OBJECT-with-message revision (macf#725).** The pre-flight
|
|
216
|
+
* config-dirty gate is the ONLY decision point that inspects PRE-upgrade
|
|
217
|
+
* state: clean (or explicitly `--force`-bypassed) ⇒ the roll enters the
|
|
218
|
+
* transaction and MUST complete it — `upgrade` → `restart(agent,
|
|
219
|
+
* { leaveConfigUncommitted: true })` always runs together, never mutating
|
|
220
|
+
* `upgrade` without following through to `restart`. Dirty (and not forced) ⇒
|
|
221
|
+
* the roll does NOTHING to this agent (no `upgrade`, no `restart`) and OBJECTS
|
|
222
|
+
* via `onEvent` with the exact file list + an agent-directed message —
|
|
223
|
+
* reconcile is MANUAL: the agent (or operator) resolves the flagged files and
|
|
224
|
+
* a clean re-run completes it. This replaces the earlier (macf#722 Fix B)
|
|
225
|
+
* silent-skip + separately-force-stashed-restart shape, which could dirty the
|
|
226
|
+
* workspace via `upgrade` and then have `restart-self`'s OWN guard refuse —
|
|
227
|
+
* aborting mid-transaction with the workspace already mutated.
|
|
90
228
|
*/
|
|
91
229
|
export async function rollFleet(plans, opts, deps) {
|
|
92
230
|
const results = [];
|
|
93
231
|
let upgraded = 0;
|
|
94
232
|
let busySkipped = 0;
|
|
233
|
+
let configDirtySkipped = 0;
|
|
95
234
|
for (const plan of plans) {
|
|
96
235
|
if (plan.disposition !== 'behind')
|
|
97
236
|
continue;
|
|
98
237
|
const agent = plan.agent;
|
|
238
|
+
// PRE-FLIGHT config-dirty gate. By-design inter-roll STEADY-STATE
|
|
239
|
+
// (macf#725): a clean roll LEAVES `macf update`'s regeneration uncommitted
|
|
240
|
+
// (so the relaunched agent reviews it — see `restart`'s
|
|
241
|
+
// `leaveConfigUncommitted` below + `buildModifiedFilesMessage`).
|
|
242
|
+
// Consequently, the NEXT roll's pre-flight WILL object on that
|
|
243
|
+
// still-uncommitted regen. This is INTENDED HYGIENE — "commit last update's
|
|
244
|
+
// regen before upgrading again" — NOT the mid-run abort bug this revision
|
|
245
|
+
// fixed: it is self-resolving via the object-with-message (the agent
|
|
246
|
+
// commits the flagged regen, then a clean re-run rolls). The alternative —
|
|
247
|
+
// auto-committing the deterministic canonical regen at end-of-transaction
|
|
248
|
+
// (zero inter-roll friction, but trades away the agent's review-visibility
|
|
249
|
+
// of what changed) — is a DEFERRED future option, the operator's call.
|
|
250
|
+
if (!opts.force) {
|
|
251
|
+
const dirtyFiles = await deps.driver.listDirtyConfig(agent);
|
|
252
|
+
if (dirtyFiles.length > 0) {
|
|
253
|
+
configDirtySkipped += 1;
|
|
254
|
+
const message = buildConfigDirtyMessage(agent, dirtyFiles);
|
|
255
|
+
results.push({
|
|
256
|
+
agent,
|
|
257
|
+
outcome: 'config-dirty-skipped',
|
|
258
|
+
detail: message,
|
|
259
|
+
});
|
|
260
|
+
deps.onEvent?.({ kind: 'config-dirty-skip', agent, files: dirtyFiles, message });
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
99
264
|
let busy = await deps.driver.isBusy(agent);
|
|
100
265
|
let waited = false;
|
|
101
266
|
if (busy && opts.wait) {
|
|
@@ -108,9 +273,16 @@ export async function rollFleet(plans, opts, deps) {
|
|
|
108
273
|
deps.onEvent?.({ kind: 'busy-skip', agent, waited });
|
|
109
274
|
continue;
|
|
110
275
|
}
|
|
276
|
+
// --- ENTERING THE TRANSACTION: upgrade + restart run together, always. ---
|
|
111
277
|
deps.onEvent?.({ kind: 'roll-start', agent, from: plan.runningVersion, to: opts.targetVersion });
|
|
112
278
|
await deps.driver.upgrade(agent);
|
|
113
|
-
|
|
279
|
+
// The pre-flight gate above (clean, or explicitly `--force`-bypassed) is
|
|
280
|
+
// the only thing that decided whether to enter this transaction. Once
|
|
281
|
+
// entered, `restart` must NOT stash or refuse on the config surface —
|
|
282
|
+
// `upgrade` (macf update) is EXPECTED to have just dirtied it, and that
|
|
283
|
+
// dirt (plus any pre-existing `--force`-bypassed dirt) is deliberately
|
|
284
|
+
// LEFT UNCOMMITTED for the relaunched agent to see via `git status`.
|
|
285
|
+
await deps.driver.restart(agent, { leaveConfigUncommitted: true });
|
|
114
286
|
const green = await deps.verifyGreen({
|
|
115
287
|
agent,
|
|
116
288
|
targetVersion: opts.targetVersion,
|
|
@@ -118,27 +290,33 @@ export async function rollFleet(plans, opts, deps) {
|
|
|
118
290
|
});
|
|
119
291
|
if (green.ok) {
|
|
120
292
|
upgraded += 1;
|
|
121
|
-
|
|
122
|
-
|
|
293
|
+
const modifiedFiles = await deps.driver.listModifiedFiles(agent);
|
|
294
|
+
const message = buildModifiedFilesMessage(agent, modifiedFiles);
|
|
295
|
+
results.push({ agent, outcome: 'upgraded', detail: message });
|
|
296
|
+
deps.onEvent?.({ kind: 'upgraded', agent, version: green.version, modifiedFiles, message });
|
|
123
297
|
continue;
|
|
124
298
|
}
|
|
125
|
-
const reason = green.
|
|
299
|
+
const reason = classifyHalt(green, plan.runningVersion);
|
|
126
300
|
results.push({
|
|
127
301
|
agent,
|
|
128
302
|
outcome: 'halted',
|
|
129
|
-
|
|
303
|
+
reason,
|
|
304
|
+
detail: `${reason}: verify-green ${green.reason} (last=${green.lastVersion ?? 'down'})`,
|
|
130
305
|
});
|
|
131
306
|
deps.onEvent?.({ kind: 'halt', agent, reason, lastVersion: green.lastVersion });
|
|
132
|
-
return { results, halted: true, upgraded, busySkipped };
|
|
307
|
+
return { results, halted: true, upgraded, busySkipped, configDirtySkipped };
|
|
133
308
|
}
|
|
134
|
-
return { results, halted: false, upgraded, busySkipped };
|
|
309
|
+
return { results, halted: false, upgraded, busySkipped, configDirtySkipped };
|
|
135
310
|
}
|
|
136
311
|
/**
|
|
137
|
-
* Orchestrate a rolling upgrade across the ORDERED `fleets` (
|
|
138
|
-
* identifiers
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
312
|
+
* Orchestrate a rolling upgrade across the ORDERED `fleets` (PROJECT
|
|
313
|
+
* identifiers — macf#710: a "fleet" == a project, which owns its own CA +
|
|
314
|
+
* registry namespace + version cadence; a profile/org registry scope hosting N
|
|
315
|
+
* projects yields N fleets here, never one), fleet-by-fleet. Each fleet is
|
|
316
|
+
* probed + planned; in EXECUTE mode it is then rolled, and a HALT in one fleet
|
|
317
|
+
* STOPS the whole run — later fleets are never started (a bad release cannot
|
|
318
|
+
* cascade across fleets). An unresolvable fleet is skipped + reported and does
|
|
319
|
+
* NOT halt the run.
|
|
142
320
|
*/
|
|
143
321
|
export async function upgradeFleets(fleets, opts, deps) {
|
|
144
322
|
const reports = [];
|
|
@@ -150,7 +328,7 @@ export async function upgradeFleets(fleets, opts, deps) {
|
|
|
150
328
|
deps.onEvent?.({ kind: 'fleet-skipped', fleet, reason: 'driver-unresolved' });
|
|
151
329
|
continue;
|
|
152
330
|
}
|
|
153
|
-
const members = driver.discoverWorkspaces().filter((r) => r.
|
|
331
|
+
const members = driver.discoverWorkspaces().filter((r) => r.project === fleet);
|
|
154
332
|
const state = await driver.probe();
|
|
155
333
|
const plans = planFleetUpgrade(members, state, opts.targetVersion);
|
|
156
334
|
const behind = plans.filter((p) => p.disposition === 'behind').length;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fleet-upgrade.js","sourceRoot":"","sources":["../src/fleet-upgrade.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"fleet-upgrade.js","sourceRoot":"","sources":["../src/fleet-upgrade.ts"],"names":[],"mappings":"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"}
|