@groundnuty/macf-core 0.2.57 → 0.2.59

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.
@@ -107,12 +107,18 @@ export interface FleetDriver {
107
107
  * `.claude/.macf/env.*` + `host-prelude.sh`, `CLAUDE.md`, `env.local.*` —
108
108
  * see `ROLL_TOUCHED_CONFIG_PATTERNS` in `fleet-upgrade.ts`, narrowed off a
109
109
  * `.claude/**` wildcard by DR-040 Decision 6 / macf#698). VM: `git status`
110
- * in the agent's workspace, filtered to that path set. A DEAD/absent agent
111
- * reads `false` (nothing to check; reconcile will launch it). Kept as a
112
- * boolean convenience alongside `listDirtyConfig` below — `rollFleet`'s
113
- * pre-flight gate itself calls `listDirtyConfig` (it needs the file list to
114
- * OBJECT with, macf#725), so this predicate form is for OTHER callers that
115
- * only need the yes/no answer.
110
+ * in the agent's workspace, filtered to that path set. Kept as a boolean
111
+ * convenience alongside `listDirtyConfig` below — `rollFleet`'s pre-flight
112
+ * gate itself calls `classifyDirtyConfig` (it needs the tiered file list
113
+ * to OBJECT with, macf#725/macf#1101), so this predicate form is for OTHER
114
+ * callers that only need the yes/no answer.
115
+ *
116
+ * `true` when the workspace itself couldn't be resolved OR inspected at
117
+ * all (macf#1101) — DISTINCT from `isBusy`'s "DEAD/absent agent is not
118
+ * busy": that predicate concerns SESSION liveness on an agent whose
119
+ * workspace resolved fine; this one concerns whether the workspace could
120
+ * be found/read in the first place. "Couldn't verify" must never read as
121
+ * "verified clean" — the false-pass hazard this issue codifies.
116
122
  */
117
123
  readonly isConfigDirty: (agent: string) => Promise<boolean>;
118
124
  /**
@@ -122,7 +128,9 @@ export interface FleetDriver {
122
128
  * uncommitted paths rather than a boolean. Empty array == clean
123
129
  * (equivalent to `isConfigDirty` reading `false`). VM: `git status
124
130
  * --porcelain` in the agent's workspace, filtered to that path set, one
125
- * path per line.
131
+ * path per line. An unresolvable/unreadable workspace (macf#1101) returns
132
+ * a single diagnostic SENTINEL entry (never a real path), not an empty
133
+ * array — see `isConfigDirty`'s doc for why.
126
134
  *
127
135
  * **Superseded as `rollFleet`'s pre-flight call site by `classifyDirtyConfig`
128
136
  * below (DR-040 Decision 3 / macf#698 R1)** — the roll no longer treats
@@ -152,7 +160,12 @@ export interface FleetDriver {
152
160
  * Both arrays are empty when the agent has no dirty config surface at all
153
161
  * (mirrors `listDirtyConfig`'s empty-array-== -clean contract). VM: reuses
154
162
  * `listDirtyConfig` for the raw list, then classifies each path via
155
- * `classifyDirtyFile`.
163
+ * `classifyDirtyFile`. An unresolvable agent (macf#1101 — no workspace
164
+ * matches this routing label under the driver's bound project) yields a
165
+ * NON-EMPTY `genuineDelta` (a diagnostic sentinel) — this is the gate
166
+ * `rollFleet` actually calls, so its fail-safe direction is load-bearing:
167
+ * an unresolvable agent must OBJECT, never silently read as "nothing
168
+ * dirty."
156
169
  */
157
170
  readonly classifyDirtyConfig: (agent: string) => Promise<{
158
171
  readonly alreadyCanonical: readonly string[];
@@ -190,6 +203,24 @@ export interface FleetDriver {
190
203
  * throws.
191
204
  */
192
205
  readonly canonicalBranch: (agent: string) => Promise<string>;
206
+ /**
207
+ * Diagnostic-only: a driver-specific description of WHERE `agent` was
208
+ * inspected — VM: the resolved absolute workspace directory; a future K8s
209
+ * driver: pod/namespace. `null` when `agent` could not be resolved at all
210
+ * (unknown agent, or — VM specifically — no workspace matches BOTH the
211
+ * agent's routing label AND this driver's own bound project; see
212
+ * `vm-driver.ts`'s `resolveTarget`). NEVER used for decision-branching
213
+ * (that stays on `currentBranch`/`classifyDirtyConfig`/etc. — the DR-037
214
+ * hard rule that nothing runtime-specific leaks above the driver line is
215
+ * about DECISIONS, not diagnostics) — its ONLY consumer is `rollFleet`'s
216
+ * pre-flight OBJECT messages (macf#1101), so an operator reading a
217
+ * branch/config-dirty objection can see EXACTLY which on-disk location
218
+ * produced it and catch a misattribution instead of trusting the agent
219
+ * name alone. Optional: a driver that has no meaningful "where" (or
220
+ * predates this field) simply omits it — callers treat the omission the
221
+ * same as `null`.
222
+ */
223
+ readonly resolveWorkspace?: (agent: string) => Promise<string | null>;
193
224
  /**
194
225
  * Read the agent's current pane content for stall-signature matching
195
226
  * (`macf fleet resume`, DR-037 / macf#686). VM: a single `tmux capture-pane`
@@ -1 +1 @@
1
- {"version":3,"file":"fleet-driver.d.ts","sourceRoot":"","sources":["../src/fleet-driver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fleet-driver.d.ts","sourceRoot":"","sources":["../src/fleet-driver.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"fleet-driver.js","sourceRoot":"","sources":["../src/fleet-driver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAuSxC;;;;;GAKG;AACH,MAAM,OAAO,gBAAiB,SAAQ,SAAS;IAC7C,YAAY,OAAe;QACzB,KAAK,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC;QACrC,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF"}
1
+ {"version":3,"file":"fleet-driver.js","sourceRoot":"","sources":["../src/fleet-driver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAuUxC;;;;;GAKG;AACH,MAAM,OAAO,gBAAiB,SAAQ,SAAS;IAC7C,YAAY,OAAe;QACzB,KAAK,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC;QACrC,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF"}
@@ -23,6 +23,16 @@ export interface AgentUpgradePlan {
23
23
  readonly fleet: string;
24
24
  /** The agent's live self-reported `/health.version`, or `null` when offline/version-less. */
25
25
  readonly runningVersion: string | null;
26
+ /**
27
+ * The agent's live self-reported `/health.instance_id` (DR-030 phase-1 mesh
28
+ * self-report; DR-037 Decision 5 mints a NEW one on every relaunch), or
29
+ * `null` when offline or reporting a body that predates the field. Captured
30
+ * at PLAN time so `classifyHalt` can later tell "the SAME process is still
31
+ * answering /health after the restart" (an honest not-yet-serving unknown)
32
+ * apart from "a genuinely NEW process is serving the wrong version" (a
33
+ * confirmed bad release) — see `classifyHalt`'s doc.
34
+ */
35
+ readonly runningInstanceId: string | null;
26
36
  /** The workspace's on-disk version pin (legible even when the agent is dead). */
27
37
  readonly pinnedVersion: string | null;
28
38
  /** The version-only disposition (see `UpgradeDisposition`). */
@@ -56,6 +66,18 @@ export interface AgentUpgradePlan {
56
66
  * ran) but its stale-old-pin state endangers no
57
67
  * OTHER agent, so `rollFleet` skips it and moves
58
68
  * on to the next agent in the SAME fleet.
69
+ * - `not-yet-serving-skipped` — (NEW, follow-up to macf#899) verify-green
70
+ * confirmed the agent REACHABLE at its OLD
71
+ * version, but the responder is PROVEN to be the
72
+ * SAME pre-restart process instance (not a fresh
73
+ * one) — `reason: 'not-yet-serving'` (see
74
+ * `HaltReason`). The agent's own relaunch simply
75
+ * hasn't taken over serving yet (a common cause
76
+ * is an unanswered interactive launch prompt);
77
+ * this is an honest UNKNOWN, not a confirmed bad
78
+ * release, and — like `stale-pin-skipped` —
79
+ * endangers no OTHER agent, so `rollFleet` skips
80
+ * it and continues.
59
81
  * - `halted` — verify-green did NOT confirm the target version,
60
82
  * and the cause is either a confirmed/unverifiable
61
83
  * bad release or a genuinely unconfirmed relaunch;
@@ -64,21 +86,25 @@ export interface AgentUpgradePlan {
64
86
  *
65
87
  * The three pre-flight skip outcomes (`busy-skipped` / `config-dirty-skipped`
66
88
  * / `branch-skipped`) are safe to continue past because the agent was NEVER
67
- * mutated. `stale-pin-skipped` (macf#899) IS safe to continue past DESPITE the
68
- * agent having been mutated, because the specific cause (a stale LAUNCH pin)
69
- * is a property of this one workspace's launch config, not of the release —
70
- * see `HaltReason`'s `stale-pin` doc for the full reasoning. `halted` is
71
- * always terminal because the agent WAS rolled and its post-restart state is
72
- * either confirmed-bad, unverifiable, or unconfirmed — none of those three are
73
- * safe to leave behind while moving on to the next agent (macf#722 Fix A,
74
- * extended by macf#899).
89
+ * mutated. `stale-pin-skipped` (macf#899) and `not-yet-serving-skipped` ARE
90
+ * safe to continue past DESPITE the agent having been mutated, because each
91
+ * specific cause (a stale LAUNCH pin; a not-yet-superseded pre-restart
92
+ * process) is a property of THIS one workspace, not of the release — see
93
+ * `HaltReason`'s `stale-pin` / `not-yet-serving` docs for the full reasoning.
94
+ * `halted` is always terminal because the agent WAS rolled and its
95
+ * post-restart state is either confirmed-bad, unverifiable, or unconfirmed —
96
+ * none of those three are safe to leave behind while moving on to the next
97
+ * agent (macf#722 Fix A, extended by macf#899).
75
98
  */
76
- export type RollOutcome = 'upgraded' | 'busy-skipped' | 'config-dirty-skipped' | 'branch-skipped' | 'stale-pin-skipped' | 'halted';
99
+ export type RollOutcome = 'upgraded' | 'busy-skipped' | 'config-dirty-skipped' | 'branch-skipped' | 'stale-pin-skipped' | 'not-yet-serving-skipped' | 'halted';
77
100
  /**
78
101
  * WHY a roll's verify-green did not confirm the target version:
79
102
  * - `bad-release` — verify-green confirmed the agent came back up
80
- * REACHABLE at its OLD (pre-upgrade) version, AND
81
- * (macf#899) its own launch pin either already
103
+ * REACHABLE at its OLD (pre-upgrade) version, the
104
+ * responder is NOT provably the lingering
105
+ * pre-restart process (see `not-yet-serving`
106
+ * below — ruled out FIRST, `classifyHalt`'s doc),
107
+ * AND (macf#899) its own launch pin either already
82
108
  * matches the target (a genuine crash-loop /
83
109
  * stuck-old-process release) or could not be
84
110
  * read at all (honest-unknown, treated
@@ -108,15 +134,44 @@ export type RollOutcome = 'upgraded' | 'busy-skipped' | 'config-dirty-skipped' |
108
134
  * because a stale launch pin on one workspace says
109
135
  * nothing about the release quality and endangers
110
136
  * no other agent.
137
+ * - `not-yet-serving` — (NEW, follow-up to macf#899) verify-green
138
+ * confirmed the agent REACHABLE at its OLD
139
+ * version, AND the exact same `instance_id` is
140
+ * still answering `/health` as answered BEFORE
141
+ * this roll's own `restart()` call (DR-037
142
+ * Decision 5 mints a fresh `instance_id` per
143
+ * relaunch, so an unchanged one PROVES the
144
+ * pre-restart process was never superseded) — the
145
+ * agent's relaunch simply hasn't taken over
146
+ * serving yet. A common cause is an unanswered
147
+ * interactive launch prompt (Claude Code's trust
148
+ * dialog or the dev-channels confirmation), but
149
+ * this decision layer never touches tmux/
150
+ * capture-pane (DR-037), so it reports the STATE
151
+ * observed (same instance still live), not a
152
+ * confirmed CAUSE. Honest UNKNOWN, never
153
+ * `CONFIRMED` bad-release wording — same floor as
154
+ * `bad-release`'s pin-unreadable case. NOT
155
+ * terminal; `outcome: 'not-yet-serving-skipped'`
156
+ * — `rollFleet` skips this ONE agent and
157
+ * CONTINUES: a slow-to-take-over relaunch on one
158
+ * workspace endangers no other agent, and later
159
+ * fleets are never halted for it. Only reachable
160
+ * when BOTH the pre-restart and post-restart
161
+ * `/health` bodies carry an `instance_id` — an
162
+ * older/mixed-version fleet without that field
163
+ * falls straight through to the `bad-release` /
164
+ * `stale-pin` pin-check below, UNCHANGED.
111
165
  */
112
- export type HaltReason = 'bad-release' | 'relaunch-unconfirmed' | 'stale-pin';
166
+ export type HaltReason = 'bad-release' | 'relaunch-unconfirmed' | 'stale-pin' | 'not-yet-serving';
113
167
  /** The per-agent EXECUTE result. `detail` carries a human-readable summary. */
114
168
  export interface AgentRollResult {
115
169
  readonly agent: string;
116
170
  readonly outcome: RollOutcome;
117
171
  /**
118
172
  * Set when `outcome === 'halted'` OR `outcome === 'stale-pin-skipped'`
119
- * (macf#899 added the latter) — see `HaltReason`.
173
+ * (macf#899 added the latter) OR `outcome === 'not-yet-serving-skipped'`
174
+ * (NEW, follow-up to macf#899) — see `HaltReason`.
120
175
  */
121
176
  readonly reason?: HaltReason;
122
177
  readonly detail?: string;
@@ -267,6 +322,15 @@ export interface FleetRollResult {
267
322
  * skip counters above, but still safe to continue past — see `HaltReason`.
268
323
  */
269
324
  readonly stalePinSkipped: number;
325
+ /**
326
+ * Count of agents skipped because verify-green confirmed them REACHABLE
327
+ * at their OLD version but the responder was PROVEN to be the SAME
328
+ * pre-restart process instance (`reason: 'not-yet-serving'`, NEW —
329
+ * follow-up to macf#899) — POST-flight, like `stalePinSkipped`, but the
330
+ * agent's relaunch simply hasn't taken over serving yet rather than
331
+ * anything about its launch pin.
332
+ */
333
+ readonly notYetServingSkipped: number;
270
334
  }
271
335
  /** Progress events for CLI rendering (the RESULT objects are what tests assert on). */
272
336
  export type UpgradeEvent = {
@@ -289,13 +353,18 @@ export type UpgradeEvent = {
289
353
  * BEFORE the config-dirty tiering / auto-resolve / busy-gate even run for
290
354
  * this agent. `current` is the agent's actual branch (`null` on detached
291
355
  * HEAD / an unresolvable branch); `canonical` is the resolved expected
292
- * branch. Fired INSTEAD of any mutation — nothing was touched.
356
+ * branch. `workspace` (macf#1101) is the on-disk location the branch check
357
+ * actually inspected (`driver.resolveWorkspace(agent)`, `null` when the
358
+ * driver can't report one or the agent is unresolvable) — surfaced so a
359
+ * misattributed resolution is VISIBLE in the objection rather than silent.
360
+ * Fired INSTEAD of any mutation — nothing was touched.
293
361
  */
294
362
  | {
295
363
  readonly kind: 'branch-skip';
296
364
  readonly agent: string;
297
365
  readonly current: string | null;
298
366
  readonly canonical: string;
367
+ readonly workspace: string | null;
299
368
  }
300
369
  /**
301
370
  * The pre-flight AUTO-RESOLVE (DR-040 Decision 3 / macf#698 R1) — `files`
@@ -317,15 +386,20 @@ export type UpgradeEvent = {
317
386
  * auto-resolved above and are never part of this list). `files` is the
318
387
  * exact genuine-delta uncommitted-path list; `message` is the
319
388
  * ready-to-forward, agent-directed text (inspect + commit / delete /
320
- * gitignore, then re-run). Fired INSTEAD of any mutation — nothing on this
321
- * agent's genuine-delta files was touched (though the already-canonical
322
- * subset, if any, WAS committed by the preceding `config-auto-resolved`).
389
+ * gitignore, then re-run) and, per macf#1101, NAMES the workspace the
390
+ * check actually inspected so a misattributed resolution is visible
391
+ * rather than silent. `workspace` carries the same location as a
392
+ * separate structured field for programmatic consumers. Fired INSTEAD of
393
+ * any mutation — nothing on this agent's genuine-delta files was touched
394
+ * (though the already-canonical subset, if any, WAS committed by the
395
+ * preceding `config-auto-resolved`).
323
396
  */
324
397
  | {
325
398
  readonly kind: 'config-dirty-skip';
326
399
  readonly agent: string;
327
400
  readonly files: readonly string[];
328
401
  readonly message: string;
402
+ readonly workspace: string | null;
329
403
  } | {
330
404
  readonly kind: 'busy-skip';
331
405
  readonly agent: string;
@@ -365,6 +439,20 @@ export type UpgradeEvent = {
365
439
  readonly pin: string;
366
440
  readonly target: string;
367
441
  }
442
+ /**
443
+ * The POST-restart NOT-YET-SERVING skip (NEW, follow-up to macf#899) —
444
+ * verify-green confirmed the agent reachable at its OLD version, but the
445
+ * SAME `instanceId` answered before AND after this roll's own `restart()`
446
+ * call — the relaunch hasn't taken over serving yet, so `rollFleet` skips
447
+ * this agent (NOT a halt) and continues to the next one. Distinct from
448
+ * `stale-pin-skip`: the cause here is a not-yet-superseded process, never
449
+ * the launch pin (which is never even consulted on this path).
450
+ */
451
+ | {
452
+ readonly kind: 'not-yet-serving-skip';
453
+ readonly agent: string;
454
+ readonly instanceId: string;
455
+ }
368
456
  /**
369
457
  * DR-043 §D6 write-back (macf#907) — `deps.recordDeployedVersion` (the
370
458
  * confirmed-green `fleet.lock` write) rejected. NON-FATAL: `outcome` for
@@ -448,11 +536,25 @@ export interface RollFleetDeps {
448
536
  export declare const DEFAULT_WAIT_TIMEOUT_MS = 300000;
449
537
  /** Default `--wait` re-poll interval (ms). */
450
538
  export declare const DEFAULT_WAIT_POLL_MS = 5000;
539
+ /**
540
+ * Render the inspected-workspace clause shared by both pre-flight OBJECT
541
+ * messages (macf#1101) — `null` reads as an explicit UNRESOLVED diagnostic,
542
+ * never a blank/omitted clause, so a caller can't mistake "the driver didn't
543
+ * report a workspace" for "this gate has nothing to say about location."
544
+ * Pure — exported for CLI-rendering reuse / tests.
545
+ */
546
+ export declare function describeInspectedWorkspace(workspace: string | null): string;
451
547
  /**
452
548
  * Build the agent-directed OBJECT message for a config-dirty pre-flight skip
453
- * (macf#725). Pure — exported for CLI-rendering reuse / tests.
549
+ * (macf#725). `workspace` (macf#1101) names the on-disk location the check
550
+ * actually inspected — the fix for the class of bug where a routing-label
551
+ * collision (or a caller-cwd-dependent resolution) silently attributed a
552
+ * DIFFERENT agent's — or the orchestrator's OWN — dirty files to `agent`;
553
+ * naming the inspected path makes that misattribution visible in the
554
+ * objection itself rather than requiring separate investigation. Pure —
555
+ * exported for CLI-rendering reuse / tests.
454
556
  */
455
- export declare function buildConfigDirtyMessage(agent: string, files: readonly string[]): string;
557
+ export declare function buildConfigDirtyMessage(agent: string, files: readonly string[], workspace?: string | null): string;
456
558
  /**
457
559
  * Build the agent-directed post-upgrade modified-files message (macf#725).
458
560
  * Pure — exported for CLI-rendering reuse / tests.
@@ -467,9 +569,13 @@ export declare function buildConfigDirtyMessage(agent: string, files: readonly s
467
569
  export declare function buildModifiedFilesMessage(agent: string, files: readonly string[]): string;
468
570
  /**
469
571
  * Build the agent-directed OBJECT message for the branch-gate pre-flight skip
470
- * (macf#755). Pure — exported for CLI-rendering reuse / tests.
572
+ * (macf#755). `workspace` (macf#1101) names the on-disk location the branch
573
+ * check actually inspected — see `buildConfigDirtyMessage`'s doc for why this
574
+ * matters (the same routing-label-collision / caller-cwd-dependent
575
+ * misattribution class applies to the branch check too). Pure — exported for
576
+ * CLI-rendering reuse / tests.
471
577
  */
472
- export declare function buildBranchSkipMessage(agent: string, current: string | null, canonical: string): string;
578
+ export declare function buildBranchSkipMessage(agent: string, current: string | null, canonical: string, workspace?: string | null): string;
473
579
  /**
474
580
  * Roll ONE fleet: for every `behind` plan (in order) run the PRE-FLIGHT gates
475
581
  * (branch-gate FIRST, then config-dirty tier-first, then busy) → upgrade →
@@ -1 +1 @@
1
- {"version":3,"file":"fleet-upgrade.d.ts","sourceRoot":"","sources":["../src/fleet-upgrade.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fleet-upgrade.d.ts","sourceRoot":"","sources":["../src/fleet-upgrade.ts"],"names":[],"mappings":"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"}
@@ -221,6 +221,12 @@ export function planFleetUpgrade(members, state, targetVersion) {
221
221
  return members.map((m) => {
222
222
  const live = byName.get(m.agent);
223
223
  const runningVersion = live?.online ? (live.version ?? null) : null;
224
+ // NEW (follow-up to macf#899) — captured alongside `runningVersion` so
225
+ // `classifyHalt` can later tell "the SAME process is still answering
226
+ // /health post-restart" (not-yet-serving) apart from "a genuinely NEW
227
+ // process is serving the wrong version" (bad-release). `null` when
228
+ // offline or the `/health` body predates the field.
229
+ const runningInstanceId = live?.online ? (live.health?.instance_id ?? null) : null;
224
230
  let disposition;
225
231
  if (runningVersion === null) {
226
232
  disposition = 'offline';
@@ -235,6 +241,7 @@ export function planFleetUpgrade(members, state, targetVersion) {
235
241
  agent: m.agent,
236
242
  fleet: m.project,
237
243
  runningVersion,
244
+ runningInstanceId,
238
245
  pinnedVersion: m.versionPin,
239
246
  disposition,
240
247
  };
@@ -309,8 +316,34 @@ async function waitForIdle(agent, opts, deps) {
309
316
  * be read, so the reason string alone is never mistaken for a positively
310
317
  * confirmed diagnosis, and the operator's remedy is "check the launch
311
318
  * pin/mount," not "assume the release is broken."
319
+ *
320
+ * ## The not-yet-serving discriminator (NEW, follow-up to macf#899)
321
+ *
322
+ * The 0.2.58 roll incident this follow-up is grounded in showed the
323
+ * `pin === targetVersion` branch ABOVE still overstates: it fires unconditional
324
+ * `CONFIRMED` wording even when the agent simply hasn't finished relaunching
325
+ * (e.g. sitting at an unanswered interactive launch prompt) — the SAME
326
+ * pre-restart process was still the one answering `/health` for the whole
327
+ * verify-green budget, which looks IDENTICAL to a genuine crash-loop from the
328
+ * version comparison alone. `restart-self` mints a NEW `instance_id` on every
329
+ * relaunch (DR-037 Decision 5) precisely so this is checkable: if the
330
+ * `instance_id` verify-green saw on its MOST RECENT probe — `lastInstanceId`
331
+ * is cleared on any unreachable probe, never latched like `lastVersion`, see
332
+ * `verify-green.ts` — is the EXACT SAME one recorded before this roll's own
333
+ * `restart()` call, the responder is PROVEN to be STILL CONTINUOUSLY the
334
+ * lingering pre-restart process — not a fresh one running broken code. That
335
+ * is `not-yet-serving`: an honest UNKNOWN (this decision layer never touches
336
+ * tmux/capture-pane, so it cannot confirm WHY the relaunch stalled, only
337
+ * THAT it has), reported and skipped, never a `CONFIRMED` bad-release. Ruled
338
+ * out FIRST, before the pin-check, because a not-yet-superseded process makes
339
+ * the pin question moot — the pin can only describe what the process WILL
340
+ * request once it actually restarts. Only fires when a same-non-null
341
+ * `instance_id` is available on BOTH sides; an older/mixed-version fleet
342
+ * without the field falls straight through to the pin-check UNCHANGED — this
343
+ * is strictly additive, never a behavior change for a fleet that lacks the
344
+ * data.
312
345
  */
313
- async function classifyHalt(green, oldVersion, targetVersion, agent, driver) {
346
+ async function classifyHalt(green, oldVersion, oldInstanceId, targetVersion, agent, driver) {
314
347
  const base = `verify-green ${green.reason} (last=${green.lastVersion ?? 'down'})`;
315
348
  const confirmedOldVersion = green.reason === 'wrong-version' &&
316
349
  green.lastVersion !== null &&
@@ -319,8 +352,29 @@ async function classifyHalt(green, oldVersion, targetVersion, agent, driver) {
319
352
  if (!confirmedOldVersion) {
320
353
  return { reason: 'relaunch-unconfirmed', detail: `relaunch-unconfirmed: ${base}` };
321
354
  }
322
- // macf#899 — POSITIVELY confirmed reachable-at-OLD-version. Read the
323
- // agent's own launch pin before naming this a bad release.
355
+ // NEW (follow-up to macf#899) — before implicating the release at all,
356
+ // rule out the simplest honest explanation: the SAME pre-restart process
357
+ // instance is still the one answering /health, i.e. this agent's relaunch
358
+ // has not taken over serving yet. Only possible when BOTH sides carry an
359
+ // `instance_id` — see this function's doc.
360
+ const lastInstanceId = green.lastInstanceId ?? null;
361
+ if (oldInstanceId !== null && lastInstanceId !== null && lastInstanceId === oldInstanceId) {
362
+ return {
363
+ reason: 'not-yet-serving',
364
+ detail: `not-yet-serving: ${base} — the SAME pre-restart instance (${oldInstanceId}) is still ` +
365
+ `the one answering ${agent}'s /health; its relaunch has not taken over serving yet. ` +
366
+ `This is an honest UNKNOWN, not a confirmed bad release — a common cause is an ` +
367
+ `unanswered interactive launch prompt (Claude Code's trust dialog or the dev-channels ` +
368
+ `confirmation); attach to ${agent}'s session, clear any pending prompt, then re-run the ` +
369
+ `upgrade to pick it up. NOT halting the roll — a slow-to-take-over relaunch on one ` +
370
+ `workspace endangers no other agent.`,
371
+ instanceId: oldInstanceId,
372
+ };
373
+ }
374
+ // macf#899 — POSITIVELY confirmed reachable-at-OLD-version (and, whenever
375
+ // instance-id data was available above, confirmed NOT to be the lingering
376
+ // pre-restart process). Read the agent's own launch pin before naming this
377
+ // a bad release.
324
378
  const pin = await driver.readVersionPin(agent);
325
379
  if (pin === null) {
326
380
  return {
@@ -352,13 +406,30 @@ async function classifyHalt(green, oldVersion, targetVersion, agent, driver) {
352
406
  pin,
353
407
  };
354
408
  }
409
+ /**
410
+ * Render the inspected-workspace clause shared by both pre-flight OBJECT
411
+ * messages (macf#1101) — `null` reads as an explicit UNRESOLVED diagnostic,
412
+ * never a blank/omitted clause, so a caller can't mistake "the driver didn't
413
+ * report a workspace" for "this gate has nothing to say about location."
414
+ * Pure — exported for CLI-rendering reuse / tests.
415
+ */
416
+ export function describeInspectedWorkspace(workspace) {
417
+ return workspace ?? 'UNRESOLVED — no workspace could be matched to this agent for this fleet';
418
+ }
355
419
  /**
356
420
  * Build the agent-directed OBJECT message for a config-dirty pre-flight skip
357
- * (macf#725). Pure — exported for CLI-rendering reuse / tests.
421
+ * (macf#725). `workspace` (macf#1101) names the on-disk location the check
422
+ * actually inspected — the fix for the class of bug where a routing-label
423
+ * collision (or a caller-cwd-dependent resolution) silently attributed a
424
+ * DIFFERENT agent's — or the orchestrator's OWN — dirty files to `agent`;
425
+ * naming the inspected path makes that misattribution visible in the
426
+ * objection itself rather than requiring separate investigation. Pure —
427
+ * exported for CLI-rendering reuse / tests.
358
428
  */
359
- export function buildConfigDirtyMessage(agent, files) {
429
+ export function buildConfigDirtyMessage(agent, files, workspace = null) {
360
430
  return (`Fleet upgrade skipped ${agent} — these files have uncommitted changes ` +
361
- `\`macf update\` would overwrite: ${files.join(', ')}. Inspect each and ` +
431
+ `\`macf update\` would overwrite: ${files.join(', ')} ` +
432
+ `(inspected workspace: ${describeInspectedWorkspace(workspace)}). Inspect each and ` +
362
433
  `commit, delete, or .gitignore it, then re-run the upgrade.`);
363
434
  }
364
435
  /**
@@ -379,11 +450,16 @@ export function buildModifiedFilesMessage(agent, files) {
379
450
  }
380
451
  /**
381
452
  * Build the agent-directed OBJECT message for the branch-gate pre-flight skip
382
- * (macf#755). Pure — exported for CLI-rendering reuse / tests.
453
+ * (macf#755). `workspace` (macf#1101) names the on-disk location the branch
454
+ * check actually inspected — see `buildConfigDirtyMessage`'s doc for why this
455
+ * matters (the same routing-label-collision / caller-cwd-dependent
456
+ * misattribution class applies to the branch check too). Pure — exported for
457
+ * CLI-rendering reuse / tests.
383
458
  */
384
- export function buildBranchSkipMessage(agent, current, canonical) {
459
+ export function buildBranchSkipMessage(agent, current, canonical, workspace = null) {
385
460
  const branchDesc = current === null ? 'a detached HEAD (or an unresolvable branch)' : `branch \`${current}\``;
386
- return (`Fleet upgrade skipped ${agent} — on ${branchDesc}, expected \`${canonical}\`. ` +
461
+ return (`Fleet upgrade skipped ${agent} — on ${branchDesc}, expected \`${canonical}\` ` +
462
+ `(inspected workspace: ${describeInspectedWorkspace(workspace)}). ` +
387
463
  `A mutation (macf update / an auto-resolve commit) or a relaunch would land on the ` +
388
464
  `wrong branch. Switch the workspace to \`${canonical}\` (or set canonicalBranch / ` +
389
465
  `MACF_CANONICAL_BRANCH if this is a legitimate fork), then re-run the upgrade, or ` +
@@ -451,10 +527,20 @@ export async function rollFleet(plans, opts, deps) {
451
527
  let configAutoResolved = 0;
452
528
  let branchSkipped = 0;
453
529
  let stalePinSkipped = 0;
530
+ let notYetServingSkipped = 0;
454
531
  for (const plan of plans) {
455
532
  if (plan.disposition !== 'behind')
456
533
  continue;
457
534
  const agent = plan.agent;
535
+ // macf#1101 — resolve the on-disk location the pre-flight gates below
536
+ // are ABOUT to inspect, ONCE per agent, so both OBJECT messages can name
537
+ // it (a misattribution — e.g. a routing-label collision resolving to a
538
+ // DIFFERENT project's, or the orchestrator's OWN, workspace — becomes
539
+ // visible in the objection instead of silent). `null` when the driver
540
+ // doesn't implement `resolveWorkspace` (optional) or can't resolve
541
+ // `agent` at all. Skipped entirely under `--force`, which bypasses both
542
+ // gates below and never inspects anything.
543
+ const workspace = opts.force ? null : ((await deps.driver.resolveWorkspace?.(agent)) ?? null);
458
544
  // PRE-FLIGHT branch-gate (macf#755) — the FIRST gate, before config-dirty
459
545
  // or busy. Detached HEAD / unresolvable (`current === null`) is ALWAYS
460
546
  // non-canonical.
@@ -463,9 +549,9 @@ export async function rollFleet(plans, opts, deps) {
463
549
  const current = await deps.driver.currentBranch(agent);
464
550
  if (current === null || current !== canonical) {
465
551
  branchSkipped += 1;
466
- const message = buildBranchSkipMessage(agent, current, canonical);
552
+ const message = buildBranchSkipMessage(agent, current, canonical, workspace);
467
553
  results.push({ agent, outcome: 'branch-skipped', detail: message });
468
- deps.onEvent?.({ kind: 'branch-skip', agent, current, canonical });
554
+ deps.onEvent?.({ kind: 'branch-skip', agent, current, canonical, workspace });
469
555
  continue;
470
556
  }
471
557
  }
@@ -489,14 +575,14 @@ export async function rollFleet(plans, opts, deps) {
489
575
  }
490
576
  if (genuineDelta.length > 0) {
491
577
  configDirtySkipped += 1;
492
- const message = buildConfigDirtyMessage(agent, genuineDelta);
578
+ const message = buildConfigDirtyMessage(agent, genuineDelta, workspace);
493
579
  results.push({
494
580
  agent,
495
581
  outcome: 'config-dirty-skipped',
496
582
  detail: message,
497
583
  ...(autoResolvedFiles ? { autoResolvedFiles } : {}),
498
584
  });
499
- deps.onEvent?.({ kind: 'config-dirty-skip', agent, files: genuineDelta, message });
585
+ deps.onEvent?.({ kind: 'config-dirty-skip', agent, files: genuineDelta, message, workspace });
500
586
  continue;
501
587
  }
502
588
  // genuineDelta is empty (nothing dirty at all, or everything dirty WAS
@@ -580,8 +666,30 @@ export async function rollFleet(plans, opts, deps) {
580
666
  }
581
667
  continue;
582
668
  }
583
- const classification = await classifyHalt(green, plan.runningVersion, opts.targetVersion, agent, deps.driver);
669
+ const classification = await classifyHalt(green, plan.runningVersion, plan.runningInstanceId, opts.targetVersion, agent, deps.driver);
584
670
  const { reason, detail } = classification;
671
+ if (reason === 'not-yet-serving') {
672
+ // NEW (follow-up to macf#899) — SAFE TO CONTINUE despite the agent
673
+ // having been mutated: the post-restart old-version state is explained
674
+ // by the SAME pre-restart process instance still answering /health
675
+ // (its relaunch hasn't taken over yet), not a bad release, and a
676
+ // slow-to-take-over relaunch on ONE workspace endangers no OTHER
677
+ // agent. Leave the maintenance lock in place (DR-040 Decision 3 —
678
+ // "release ONLY on confirmed clean success"; this agent is NOT
679
+ // confirmed clean): same posture as `stale-pin`. Only the heartbeat
680
+ // stops here; the lock self-clears via MAINT_LOCK_TTL.
681
+ notYetServingSkipped += 1;
682
+ results.push({
683
+ agent,
684
+ outcome: 'not-yet-serving-skipped',
685
+ reason,
686
+ detail,
687
+ ...(autoResolvedFiles ? { autoResolvedFiles } : {}),
688
+ });
689
+ deps.onEvent?.({ kind: 'not-yet-serving-skip', agent, instanceId: classification.instanceId });
690
+ stopHeartbeat();
691
+ continue;
692
+ }
585
693
  if (reason === 'stale-pin') {
586
694
  // macf#899 — SAFE TO CONTINUE despite the agent having been mutated
587
695
  // (upgrade + restart already ran): the post-restart old-version state
@@ -631,6 +739,7 @@ export async function rollFleet(plans, opts, deps) {
631
739
  configAutoResolved,
632
740
  branchSkipped,
633
741
  stalePinSkipped,
742
+ notYetServingSkipped,
634
743
  };
635
744
  }
636
745
  return {
@@ -642,6 +751,7 @@ export async function rollFleet(plans, opts, deps) {
642
751
  configAutoResolved,
643
752
  branchSkipped,
644
753
  stalePinSkipped,
754
+ notYetServingSkipped,
645
755
  };
646
756
  }
647
757
  /**