@mutmutco/cli 4.5.124 → 4.5.125

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 (2) hide show
  1. package/dist/main.cjs +293 -202
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -76,19 +76,19 @@ function hardExit(code) {
76
76
  process.exit(code);
77
77
  }
78
78
  function flushStream(stream) {
79
- return new Promise((resolve7) => {
79
+ return new Promise((resolve8) => {
80
80
  try {
81
- stream.write("", () => resolve7());
81
+ stream.write("", () => resolve8());
82
82
  } catch {
83
- resolve7();
83
+ resolve8();
84
84
  }
85
85
  });
86
86
  }
87
87
  async function flushStdio(timeoutMs = STDIO_FLUSH_TIMEOUT_MS) {
88
88
  await Promise.race([
89
89
  Promise.all([flushStream(process.stdout), flushStream(process.stderr)]),
90
- new Promise((resolve7) => {
91
- setTimeout(resolve7, timeoutMs).unref?.();
90
+ new Promise((resolve8) => {
91
+ setTimeout(resolve8, timeoutMs).unref?.();
92
92
  })
93
93
  ]);
94
94
  }
@@ -96,7 +96,7 @@ async function cleanExit(code) {
96
96
  process.exitCode = code;
97
97
  await closeHttpPool();
98
98
  await flushStdio();
99
- await new Promise((resolve7) => setImmediate(resolve7));
99
+ await new Promise((resolve8) => setImmediate(resolve8));
100
100
  return void 0;
101
101
  }
102
102
  async function finishCliRun(watchdogMs = CLI_EXIT_WATCHDOG_MS) {
@@ -644,7 +644,7 @@ function rateLimitedReceipt(opts) {
644
644
  };
645
645
  }
646
646
  async function runWithRateLimitBackoff(operation, opts) {
647
- const sleep2 = opts.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
647
+ const sleep2 = opts.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
648
648
  const now = opts.now ?? Date.now;
649
649
  const log = opts.log ?? ((message2) => console.warn(message2));
650
650
  const capMs = opts.capMs ?? RATE_LIMIT_WAIT_CAP_MS;
@@ -734,7 +734,7 @@ async function fetchWithRetry(fetchImpl, url, init, opts = {}) {
734
734
  const attempts = opts.attempts ?? 3;
735
735
  const baseDelayMs = opts.baseDelayMs ?? 250;
736
736
  const retryOn = opts.retryOn ?? ((res) => res.status >= 500);
737
- const sleep2 = opts.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
737
+ const sleep2 = opts.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
738
738
  let lastErr;
739
739
  for (let i = 0; i < attempts; i++) {
740
740
  const isLast = i === attempts - 1;
@@ -1057,8 +1057,8 @@ async function readStdin(opts = {}) {
1057
1057
  })().catch(() => {
1058
1058
  });
1059
1059
  let timer;
1060
- const timeout = new Promise((resolve7) => {
1061
- timer = setTimeout(resolve7, timeoutMs);
1060
+ const timeout = new Promise((resolve8) => {
1061
+ timer = setTimeout(resolve8, timeoutMs);
1062
1062
  });
1063
1063
  try {
1064
1064
  await Promise.race([drain, timeout]);
@@ -1125,12 +1125,12 @@ function killProcessTree(pid) {
1125
1125
  function execFileHard(file, args, options) {
1126
1126
  const { timeout, step, ...rest } = options;
1127
1127
  const started = Date.now();
1128
- return new Promise((resolve7, reject) => {
1128
+ return new Promise((resolve8, reject) => {
1129
1129
  const child2 = (0, import_node_child_process2.execFile)(file, args, { encoding: "utf8", windowsHide: true, ...rest, timeout: 0 }, (error, stdout, stderr) => {
1130
1130
  clearTimeout(timer);
1131
1131
  if (expired) return;
1132
1132
  if (error) reject(error);
1133
- else resolve7({ stdout: String(stdout), stderr: String(stderr) });
1133
+ else resolve8({ stdout: String(stdout), stderr: String(stderr) });
1134
1134
  });
1135
1135
  let expired = false;
1136
1136
  const timer = setTimeout(() => {
@@ -5656,7 +5656,7 @@ function resolveVerbAliasShim(argv) {
5656
5656
 
5657
5657
  // src/command-composition.ts
5658
5658
  var import_node_os25 = require("node:os");
5659
- var import_node_path55 = require("node:path");
5659
+ var import_node_path56 = require("node:path");
5660
5660
 
5661
5661
  // src/board-read.ts
5662
5662
  var import_node_fs14 = require("node:fs");
@@ -6735,7 +6735,7 @@ async function readRestPrSnapshotWithRetry(prNumber, repo, gh2 = defaultGhApi, o
6735
6735
  const retries = options?.retries ?? PR_SNAPSHOT_READ_RETRIES;
6736
6736
  const untilMs = options?.retryTransientUntilMs;
6737
6737
  const delayMs = options?.delayMs ?? (untilMs !== void 0 ? PR_CHECKS_POLL_MS : PR_SNAPSHOT_READ_DELAY_MS);
6738
- const sleep2 = options?.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
6738
+ const sleep2 = options?.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
6739
6739
  const now = options?.now ?? (() => Date.now());
6740
6740
  let lastError = "no attempt completed";
6741
6741
  let attempt = 0;
@@ -10588,7 +10588,7 @@ async function createPrViaRestFallback(args, swappedArgs, deps, knownPools) {
10588
10588
  }
10589
10589
  async function ghCreate(args, deps = {}) {
10590
10590
  const exec3 = deps.exec ?? execFileP;
10591
- const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
10591
+ const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
10592
10592
  const now = deps.now ?? Date.now;
10593
10593
  const read = deps.readFile ?? import_promises.readFile;
10594
10594
  const restCreatePr = deps.restCreatePr ?? ((input) => defaultRestCreatePr(exec3, input));
@@ -11416,8 +11416,8 @@ var PRE_SPAWN_DRAIN_TIMEOUT_MS = 2e3;
11416
11416
  async function drainHttpPoolBeforeSpawn() {
11417
11417
  const drained = await Promise.race([
11418
11418
  closeHttpPool().then(() => true),
11419
- new Promise((resolve7) => {
11420
- setTimeout(() => resolve7(false), PRE_SPAWN_DRAIN_TIMEOUT_MS).unref?.();
11419
+ new Promise((resolve8) => {
11420
+ setTimeout(() => resolve8(false), PRE_SPAWN_DRAIN_TIMEOUT_MS).unref?.();
11421
11421
  })
11422
11422
  ]);
11423
11423
  if (!drained) destroyHttpPool();
@@ -15004,7 +15004,7 @@ async function setBoardItemPriority(client, cfg, itemId, priority) {
15004
15004
  await updateItemSingleSelect(client, cfg.projectId, itemId, cfg.priorityFieldId, optionId);
15005
15005
  return cliPriorityToFieldName(priority);
15006
15006
  }
15007
- var defaultRetrySleep = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
15007
+ var defaultRetrySleep = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
15008
15008
  async function resolveProjectItemIdWithRetry(client, cfg, selector, opts = {}) {
15009
15009
  const attempts = Math.max(1, opts.attempts ?? 5);
15010
15010
  const delayMs = opts.delayMs ?? 300;
@@ -18243,10 +18243,10 @@ var rollout_plan_default = {
18243
18243
  note: "The v4.0.0 stamp happens at cut time (D6e #4463); until then the candidate is the origin/development head artifacts (built cli/dist + npm pack), identity proven by dist content hash (D6a)."
18244
18244
  },
18245
18245
  baseline: {
18246
- version: "4.5.124",
18247
- tag: "v4.5.124",
18248
- commit: "255e8e974e8e",
18249
- npm: "@mutmutco/cli@4.5.124"
18246
+ version: "4.5.125",
18247
+ tag: "v4.5.125",
18248
+ commit: "00accba6d8e0",
18249
+ npm: "@mutmutco/cli@4.5.125"
18250
18250
  },
18251
18251
  exitCriterion: "fleet-n-of-n",
18252
18252
  hubOnlyShortcut: "forbidden",
@@ -18263,14 +18263,14 @@ var rollout_plan_default = {
18263
18263
  repo: "mutmutco/mmi-hub",
18264
18264
  role: "canary",
18265
18265
  schedule: "train",
18266
- v3Target: "v4.5.124"
18266
+ v3Target: "v4.5.125"
18267
18267
  }
18268
18268
  ],
18269
18269
  rollbackTrigger: "Any red inside the post-contract soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a pre-v4 client admitted instead of receiving actionable HTTP 426, or npm consumer install/doctor failure on the v4-only dist.",
18270
18270
  rollback: {
18271
18271
  independent: true,
18272
- mechanism: "npm dist-tag latest -> 4.5.124 and redeploy the Hub Lambda from tag v4.5.124 (255e8e974e8e); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
18273
- v3Target: "v4.5.124 (@mutmutco/cli@4.5.124, tag commit 255e8e974e8e \u2014 last known-good release carrying the repo-index v4-only contract)"
18272
+ mechanism: "npm dist-tag latest -> 4.5.125 and redeploy the Hub Lambda from tag v4.5.125 (00accba6d8e0); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
18273
+ v3Target: "v4.5.125 (@mutmutco/cli@4.5.125, tag commit 00accba6d8e0 \u2014 last known-good release carrying the repo-index v4-only contract)"
18274
18274
  }
18275
18275
  },
18276
18276
  {
@@ -20353,7 +20353,7 @@ async function runMergeTreePreflight(deps, ours, theirs) {
20353
20353
  async function predictMergeConflicts(deps, ours, theirs) {
20354
20354
  return runMergeTreePreflight(deps, ours, theirs);
20355
20355
  }
20356
- async function mergeWithToleratedResolution(deps, sourceRef, label, resolve7, extraTolerated = []) {
20356
+ async function mergeWithToleratedResolution(deps, sourceRef, label, resolve8, extraTolerated = []) {
20357
20357
  try {
20358
20358
  await deps.run("git", ["merge", sourceRef, "--no-edit"]);
20359
20359
  return;
@@ -20367,7 +20367,7 @@ async function mergeWithToleratedResolution(deps, sourceRef, label, resolve7, ex
20367
20367
  unmerged.length === 0 ? `${label} merge failed without conflicted paths \u2014 merge aborted; inspect the repo state and rerun` : `${label} merge conflicts on untolerated path(s): ${blocking.join(", ")} \u2014 merge aborted (the train is misaligned; reconcile the branches via an approved alignment PR, then rerun)`
20368
20368
  );
20369
20369
  }
20370
- await deps.run("git", ["checkout", `--${resolve7}`, "--", ...unmerged]);
20370
+ await deps.run("git", ["checkout", `--${resolve8}`, "--", ...unmerged]);
20371
20371
  await deps.run("git", ["add", "--", ...unmerged]);
20372
20372
  await deps.run("git", ["commit", "--no-edit"]);
20373
20373
  }
@@ -20547,7 +20547,7 @@ var CORRELATE_SKEW_SLACK_MS = 1e4;
20547
20547
  var CORRELATE_PAGE_LIMIT = 50;
20548
20548
  var RUN_CONFIRM_ATTEMPTS = 3;
20549
20549
  var RUN_CONFIRM_DELAY_MS = 1e3;
20550
- var defaultSleep = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
20550
+ var defaultSleep = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
20551
20551
  function resolveSleep(deps) {
20552
20552
  return deps.sleep ?? defaultSleep;
20553
20553
  }
@@ -23726,7 +23726,7 @@ function lockTokenPid(token) {
23726
23726
  const m = /^(\d+)-/.exec(token);
23727
23727
  return m ? Number(m[1]) : void 0;
23728
23728
  }
23729
- var sleep = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
23729
+ var sleep = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
23730
23730
  var IMMEDIATE_RETRY_BUDGET = 3;
23731
23731
  var FileLockBusyError = class extends Error {
23732
23732
  lockPath;
@@ -24602,7 +24602,7 @@ async function defaultWatchRun(deps, runId, repo) {
24602
24602
  }
24603
24603
  let confirmError;
24604
24604
  for (let attempt = 0; attempt < RUN_CONFIRM_ATTEMPTS2; attempt++) {
24605
- if (attempt > 0) await new Promise((resolve7) => setTimeout(resolve7, RUN_CONFIRM_DELAY_MS2));
24605
+ if (attempt > 0) await new Promise((resolve8) => setTimeout(resolve8, RUN_CONFIRM_DELAY_MS2));
24606
24606
  try {
24607
24607
  const parsed = JSON.parse(await deps.run("gh", ["run", "view", String(runId), "--repo", repo, "--json", "status,conclusion"]));
24608
24608
  if (parsed.status === "completed") return parsed.conclusion === "success" ? "success" : "failure";
@@ -25644,11 +25644,11 @@ var defaultDeps = {
25644
25644
  // #5813: the caller supplies that command line. `process.argv` is NOT it — the house shim splices
25645
25645
  // the house root out in place, so re-reading it here re-ran the removed flat alias (`release …`
25646
25646
  // instead of `devops release …`) and the converged child refused with exit 2.
25647
- reexec: (argv) => new Promise((resolve7) => {
25647
+ reexec: (argv) => new Promise((resolve8) => {
25648
25648
  const args = [...argv];
25649
25649
  const child2 = isWin2 ? (0, import_node_child_process8.spawn)("cmd.exe", ["/c", "mmi-cli", ...args], { stdio: "inherit", windowsHide: true }) : (0, import_node_child_process8.spawn)("mmi-cli", args, { stdio: "inherit", windowsHide: true });
25650
- child2.on("exit", (code) => resolve7(code ?? 1));
25651
- child2.on("error", () => resolve7(1));
25650
+ child2.on("exit", (code) => resolve8(code ?? 1));
25651
+ child2.on("error", () => resolve8(1));
25652
25652
  }),
25653
25653
  // stderr only — stdout may be carrying --json output.
25654
25654
  warn: (line) => console.error(line)
@@ -25754,7 +25754,7 @@ var defaultDeps2 = {
25754
25754
  }
25755
25755
  },
25756
25756
  registryWhoami: probeRegistryWhoami,
25757
- sleep: (ms) => new Promise((resolve7) => setTimeout(resolve7, ms)),
25757
+ sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms)),
25758
25758
  env: process.env
25759
25759
  };
25760
25760
  function message(e) {
@@ -37162,7 +37162,7 @@ function readSuiteCut(path2) {
37162
37162
  } catch {
37163
37163
  throw new Error(`suite cut record unreadable at ${path2}; repair it, never recut`);
37164
37164
  }
37165
- if (record.schema !== SCHEMA || record.lead !== SUITE_REPO || !SHA2.test(record.leadDevelopment) || !SHA2.test(record.leadPreviousRc) || !Array.isArray(record.members) || record.members.length === 0 || record.pinPr !== void 0 && (typeof record.pinPr !== "string" || !record.pinPr.startsWith("https://github.com/mutmutco/MM-Work/pull/")) || record.pinsUnchanged !== void 0 && (record.pinsUnchanged !== true || record.pinPr !== void 0) || record.pinMerged !== void 0 && (record.pinMerged !== true && record.pinMerged !== false || !record.pinPr && !record.pinsUnchanged) || record.leadRcTag !== void 0 && (!RC_TAG.test(record.leadRcTag) || !SHA2.test(record.leadRcSha ?? "") || !record.pinMerged) || record.leadReleased !== void 0 && (record.leadReleased !== true || !record.leadRcTag) || record.announcement !== void 0 && (!["pending", "sent", "skipped"].includes(record.announcement) || !record.leadReleased) || record.heldMembers !== void 0 && (!Array.isArray(record.heldMembers) || record.heldMembers.some((h) => !h.id || !h.repo || typeof h.reason !== "string" || !h.reason.trim() || h.since !== void 0 && typeof h.since !== "string" || record.members.some((m) => m.id === h.id))) || record.members.some((m) => !m.id || !m.repo || !m.backend || !m.host || !SHA2.test(m.development) || !SHA2.test(m.previousRc) || m.rcWasAbsent !== void 0 && m.rcWasAbsent !== true || m.tag !== void 0 && (!RC_TAG.test(m.tag) || !SHA2.test(m.sha ?? "")) || m.published !== void 0 && (m.published !== true || !m.tag) || m.released !== void 0 && (m.released !== true || !m.published) || m.mainPublished !== void 0 && (m.mainPublished !== true || !m.released || !!m.carried) || m.backendAfterRelease !== void 0 && (!m.released || typeof m.backendAfterRelease !== "string") || m.carried !== void 0 && (m.carried !== true || m.tag !== void 0 || m.published !== void 0 || !isPin(m.rcPin) || !isPin(m.mainPin)) || m.carried === void 0 && (m.rcPin !== void 0 || m.mainPin !== void 0) || m.ruleset !== void 0 && (!["rc", "main"].includes(m.ruleset.after) || typeof m.ruleset.ok !== "boolean" || !Array.isArray(m.ruleset.lines))))
37165
+ if (record.schema !== SCHEMA || record.lead !== SUITE_REPO || !SHA2.test(record.leadDevelopment) || !SHA2.test(record.leadPreviousRc) || !Array.isArray(record.members) || record.members.length === 0 || record.pinPr !== void 0 && (typeof record.pinPr !== "string" || !record.pinPr.startsWith("https://github.com/mutmutco/MM-Work/pull/")) || record.pinsUnchanged !== void 0 && (record.pinsUnchanged !== true || record.pinPr !== void 0) || record.pinMerged !== void 0 && (record.pinMerged !== true && record.pinMerged !== false || !record.pinPr && !record.pinsUnchanged) || record.leadRcTag !== void 0 && (!RC_TAG.test(record.leadRcTag) || !SHA2.test(record.leadRcSha ?? "") || !record.pinMerged) || record.leadReleased !== void 0 && (record.leadReleased !== true || !record.leadRcTag) || record.announcement !== void 0 && (!["pending", "sent", "skipped"].includes(record.announcement) || !record.leadReleased) || record.heldMembers !== void 0 && (!Array.isArray(record.heldMembers) || record.heldMembers.some((h) => !h.id || !h.repo || typeof h.reason !== "string" || !h.reason.trim() || h.since !== void 0 && typeof h.since !== "string" || record.members.some((m) => m.id === h.id))) || record.members.some((m) => !m.id || !m.repo || !m.backend || !m.host || !SHA2.test(m.development) || !SHA2.test(m.previousRc) || m.rcWasAbsent !== void 0 && m.rcWasAbsent !== true || m.tag !== void 0 && (!RC_TAG.test(m.tag) || !SHA2.test(m.sha ?? "")) || m.carriedPublished !== void 0 && (m.carriedPublished !== true || !m.carried) || m.published !== void 0 && (m.published !== true || !m.tag) || m.released !== void 0 && (m.released !== true || !m.published) || m.mainPublished !== void 0 && (m.mainPublished !== true || !m.released || !!m.carried) || m.backendAfterRelease !== void 0 && (!m.released || typeof m.backendAfterRelease !== "string") || m.carried !== void 0 && (m.carried !== true || m.tag !== void 0 || m.published !== void 0 || !isPin(m.rcPin) || !isPin(m.mainPin)) || m.carried === void 0 && (m.rcPin !== void 0 || m.mainPin !== void 0) || m.ruleset !== void 0 && (!["rc", "main"].includes(m.ruleset.after) || typeof m.ruleset.ok !== "boolean" || !Array.isArray(m.ruleset.lines))))
37166
37166
  throw new Error(`suite cut record has unknown or invalid state at ${path2}; repair it, never recut`);
37167
37167
  return record;
37168
37168
  }
@@ -37274,7 +37274,22 @@ async function pendingBackend(deps, m, dev) {
37274
37274
  const files = (await deps.changedPaths(m.repo, live, dev)).filter((f) => !NOT_BACKEND.test(f));
37275
37275
  return files.length ? `${files.length} backend file(s) changed since live ${version}, e.g. ${files[0]}` : "";
37276
37276
  }
37277
+ function existingCutDescription(record) {
37278
+ const tags = [...record.members.flatMap((m) => m.tag ? [m.tag] : []), ...record.leadRcTag ? [record.leadRcTag] : []];
37279
+ return `from ${record.leadDevelopment.slice(0, 12)}; tags: ${tags.join(", ") || "none"}; pin PR: ${record.pinPr ?? "none"}`;
37280
+ }
37277
37281
  async function suiteRcand(deps, opts) {
37282
+ const existing = readSuiteCut(deps.recordPath);
37283
+ try {
37284
+ return await suiteRcandRun(deps, opts);
37285
+ } catch (e) {
37286
+ if (existing && !opts.dryRun && opts.ownerGo && readSuiteCut(deps.recordPath))
37287
+ throw new Error(`${e.message}
37288
+ An existing cut record ${existingCutDescription(existing)} was resumed. mmi-cli devops suite abandon --owner-go retires it (subject to abandon safety checks).`);
37289
+ throw e;
37290
+ }
37291
+ }
37292
+ async function suiteRcandRun(deps, opts) {
37278
37293
  if (!opts.ownerGo && !opts.dryRun) throw new Error("suite rcand requires --owner-go (rc uses live data); nothing was written");
37279
37294
  const members = suiteMembers(await deps.registry());
37280
37295
  const modules = trainModules(members);
@@ -37288,6 +37303,8 @@ async function suiteRcand(deps, opts) {
37288
37303
  const lead = await deps.head(SUITE_REPO, "development");
37289
37304
  plan.push(await kitCheck(deps, lead, active.map((m) => ({ id: m.id, url: m.bundles.dev })), "dev bundles"));
37290
37305
  if (opts.dryRun) {
37306
+ const existing = readSuiteCut(deps.recordPath);
37307
+ if (existing) plan.push(`existing cut record: ${deps.recordPath} (${existingCutDescription(existing)})`);
37291
37308
  plan.push(...await Promise.all(modules.map(async (m) => `${m.repo}: ${heldById.has(m.id) ? "HELD" : await dryRunStatus(deps, m)}`)));
37292
37309
  return { plan };
37293
37310
  }
@@ -37372,7 +37389,7 @@ async function suiteRcand(deps, opts) {
37372
37389
  }
37373
37390
  const refusals = [];
37374
37391
  for (const member of record.members) {
37375
- if (member.published) continue;
37392
+ if (member.published || member.carriedPublished) continue;
37376
37393
  try {
37377
37394
  await deps.preflight(member.repo);
37378
37395
  } catch (e) {
@@ -37386,28 +37403,60 @@ async function suiteRcand(deps, opts) {
37386
37403
  ${refusals.join("\n")}
37387
37404
  Each module must ship a live release that bind-mounts <deployPath>/data read-only and serves <data>/rc/module.js at /rc/module.js (docs/Guides/suite-trains.md).`);
37388
37405
  }
37389
- for (const member of record.members) {
37390
- if ((await deps.head(member.repo, "development"))?.toLowerCase() !== member.development.toLowerCase())
37391
- throw new Error(`${member.repo}: development moved after the cut snapshot; refuse to add untested code`);
37392
- if (member.carried) {
37393
- await deps.publish(member.repo, pinVersion(member.rcPin));
37394
- continue;
37395
- }
37396
- if (!member.tag) {
37397
- const cut = await deps.rcand(member.repo, member.previousRc, member.rcWasAbsent);
37398
- if (!RC_TAG.test(cut.tag) || !SHA2.test(cut.sha) || cut.sha.toLowerCase() === member.previousRc.toLowerCase()) throw new Error(`${member.repo}: rcand did not return a new immutable rc tag/SHA`);
37399
- if (!await deps.contains(member.repo, member.development, cut.sha)) throw new Error(`${member.repo}: rc tag does not carry the snapshotted development SHA; refuse`);
37400
- member.tag = cut.tag;
37401
- member.sha = cut.sha;
37402
- writeSuiteCut(deps.recordPath, record);
37403
- }
37404
- if ((await deps.tagSha(member.repo, member.tag))?.toLowerCase() !== member.sha.toLowerCase()) throw new Error(`${member.repo}: rc tag ${member.tag} moved or is unreadable; stop`);
37405
- if (!member.published) {
37406
- await deps.publish(member.repo, member.tag);
37407
- member.published = true;
37408
- writeSuiteCut(deps.recordPath, record);
37406
+ const cutRecord = record;
37407
+ let writes = Promise.resolve();
37408
+ const persist = (mutate) => {
37409
+ const next = writes.then(() => {
37410
+ mutate();
37411
+ writeSuiteCut(deps.recordPath, cutRecord);
37412
+ });
37413
+ writes = next.catch(() => {
37414
+ });
37415
+ return next;
37416
+ };
37417
+ let running = 0;
37418
+ const waiting = [];
37419
+ const results = await Promise.allSettled(record.members.map(async (member) => {
37420
+ if (running >= 4) await new Promise((resolve8) => waiting.push(resolve8));
37421
+ else running++;
37422
+ try {
37423
+ if ((await deps.head(member.repo, "development"))?.toLowerCase() !== member.development.toLowerCase())
37424
+ throw new Error(`${member.repo}: development moved after the cut snapshot; refuse to add untested code`);
37425
+ if (member.carried) {
37426
+ if (!member.carriedPublished) {
37427
+ await deps.publish(member.repo, pinVersion(member.rcPin));
37428
+ await persist(() => {
37429
+ member.carriedPublished = true;
37430
+ });
37431
+ }
37432
+ return;
37433
+ }
37434
+ if (!member.tag) {
37435
+ const cut = await deps.rcand(member.repo, member.previousRc, member.rcWasAbsent);
37436
+ if (!RC_TAG.test(cut.tag) || !SHA2.test(cut.sha) || cut.sha.toLowerCase() === member.previousRc.toLowerCase()) throw new Error(`${member.repo}: rcand did not return a new immutable rc tag/SHA`);
37437
+ if (!await deps.contains(member.repo, member.development, cut.sha)) throw new Error(`${member.repo}: rc tag does not carry the snapshotted development SHA; refuse`);
37438
+ await persist(() => {
37439
+ member.tag = cut.tag;
37440
+ member.sha = cut.sha;
37441
+ });
37442
+ }
37443
+ const tag = member.tag;
37444
+ if ((await deps.tagSha(member.repo, tag))?.toLowerCase() !== member.sha.toLowerCase()) throw new Error(`${member.repo}: rc tag ${member.tag} moved or is unreadable; stop`);
37445
+ if (!member.published) {
37446
+ await deps.publish(member.repo, tag);
37447
+ await persist(() => {
37448
+ member.published = true;
37449
+ });
37450
+ }
37451
+ } finally {
37452
+ const next = waiting.shift();
37453
+ if (next) next();
37454
+ else running--;
37409
37455
  }
37410
- }
37456
+ }));
37457
+ const failures = results.flatMap((result, i) => result.status === "rejected" ? [`${record.members[i].repo}: ${result.reason instanceof Error ? result.reason.message : String(result.reason)}`] : []);
37458
+ if (failures.length) throw new Error(`suite member cuts failed:
37459
+ ${failures.join("\n")}`);
37411
37460
  for (const member of record.members) {
37412
37461
  const [live, devHead] = await Promise.all([
37413
37462
  deps.backend({ repo: member.repo, id: member.id, bundles: {} }),
@@ -37852,8 +37901,10 @@ async function runRcDataSwitch(deps, opts) {
37852
37901
  }
37853
37902
 
37854
37903
  // src/suite-train-io.ts
37855
- var import_node_fs45 = require("node:fs");
37904
+ var import_node_child_process18 = require("node:child_process");
37856
37905
  var import_node_path44 = require("node:path");
37906
+ var import_node_fs45 = require("node:fs");
37907
+ var import_node_path45 = require("node:path");
37857
37908
  init_cli_shared();
37858
37909
 
37859
37910
  // src/command-register-train-operations.ts
@@ -38035,7 +38086,7 @@ function clean3(out) {
38035
38086
  return out.trim();
38036
38087
  }
38037
38088
  function sleeper(deps) {
38038
- return deps.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
38089
+ return deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
38039
38090
  }
38040
38091
  function normalizeHotfixVersion(input) {
38041
38092
  const m = /^v?(\d+\.\d+\.\d+)$/.exec(input.trim());
@@ -39707,7 +39758,7 @@ async function updatePrHeadFromBase(input) {
39707
39758
  } catch (e) {
39708
39759
  throw new Error(`GitHub could not update ${input.head} from ${input.base} (${e.message.trim()}) \u2014 ${remedy}`);
39709
39760
  }
39710
- const sleep2 = input.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
39761
+ const sleep2 = input.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
39711
39762
  for (let attempt = 0; attempt < PR_HEAD_UPDATE_API_READ_RETRIES; attempt++) {
39712
39763
  const to = await readHead2();
39713
39764
  if (to && to !== from) return { from, to, base: input.base, via: "update-branch-api" };
@@ -39725,7 +39776,7 @@ async function mergeAutoWithTransientRetry(prNumber, repo, deps) {
39725
39776
  if (first.mergeStatus !== "failed") return first;
39726
39777
  const ready = await deps.probeMergeReady(prNumber, repo).catch(() => ({ open: false, mergeable: false, checksPassing: false }));
39727
39778
  if (!ready.open || !ready.mergeable || !ready.checksPassing) return first;
39728
- const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
39779
+ const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
39729
39780
  await sleep2(PR_LAND_MERGE_RETRY_DELAY_MS);
39730
39781
  const retried = await deps.mergeAuto(prNumber, repo);
39731
39782
  if (retried.mergeStatus !== "failed") return retried;
@@ -39736,7 +39787,7 @@ var AUTO_MERGE_CONFIRM_DELAY_MS = 15e3;
39736
39787
  async function confirmAutoMergeEnqueued(deps, options) {
39737
39788
  const retries = options?.retries ?? AUTO_MERGE_CONFIRM_RETRIES;
39738
39789
  const delayMs = options?.delayMs ?? AUTO_MERGE_CONFIRM_DELAY_MS;
39739
- const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
39790
+ const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
39740
39791
  for (let attempt = 0; attempt < retries; attempt++) {
39741
39792
  if (await deps.readMerged().catch(() => false)) return "merged";
39742
39793
  const stuck = await deps.readAutoMergeRequest().then((s) => s.trim()).catch(() => "");
@@ -39753,7 +39804,7 @@ async function confirmAutoMergeEnqueued(deps, options) {
39753
39804
  async function readGhPrStateWithRetry(fetchState, options) {
39754
39805
  const retries = options?.retries ?? PR_LAND_STATE_READ_RETRIES;
39755
39806
  const delayMs = options?.delayMs ?? PR_LAND_STATE_READ_DELAY_MS;
39756
- const sleep2 = options?.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
39807
+ const sleep2 = options?.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
39757
39808
  let lastError = "empty state";
39758
39809
  for (let attempt = 0; attempt < retries; attempt++) {
39759
39810
  try {
@@ -43381,7 +43432,7 @@ async function checkPrReview(number, repo, head, deps = {
43381
43432
  }
43382
43433
  }
43383
43434
  function computePrPatchId(number, repo) {
43384
- return new Promise((resolve7, reject) => {
43435
+ return new Promise((resolve8, reject) => {
43385
43436
  const gh2 = (0, import_node_child_process14.spawn)("gh", ["pr", "diff", number, "--repo", repo], { windowsHide: true, stdio: ["ignore", "pipe", "pipe"] });
43386
43437
  const git3 = (0, import_node_child_process14.spawn)("git", ["patch-id", "--stable"], { windowsHide: true, stdio: ["pipe", "pipe", "pipe"] });
43387
43438
  let out = "";
@@ -43419,7 +43470,7 @@ function computePrPatchId(number, repo) {
43419
43470
  if (gitCode !== 0) return reject(new Error(`git patch-id --stable exited ${gitCode}: ${gitErr.trim()}`));
43420
43471
  const id = out.trim().split(/\s+/)[0];
43421
43472
  if (!/^[0-9a-f]{40}$/.test(id ?? "")) return reject(new Error(`patch-id: PR #${number} diff is empty or unreadable (gh: ${ghErr.trim() || "no stderr"})`));
43422
- resolve7(id);
43473
+ resolve8(id);
43423
43474
  };
43424
43475
  gh2.on("close", (code) => {
43425
43476
  ghCode = code;
@@ -43470,7 +43521,7 @@ init_cli_shared();
43470
43521
  var GIT_TIMEOUT_MS2 = 15e3;
43471
43522
  function catFileBatch(root, ref, paths) {
43472
43523
  if (paths.length === 0) return Promise.resolve([]);
43473
- return new Promise((resolve7) => {
43524
+ return new Promise((resolve8) => {
43474
43525
  const chunks = [];
43475
43526
  let settled = false;
43476
43527
  const child2 = (0, import_node_child_process15.spawn)("git", ["-C", root, "cat-file", "--batch", "--buffer"], { windowsHide: true });
@@ -43478,7 +43529,7 @@ function catFileBatch(root, ref, paths) {
43478
43529
  if (settled) return;
43479
43530
  settled = true;
43480
43531
  clearTimeout(timer);
43481
- resolve7(parseCatFileBatch(Buffer.concat(chunks), paths.length));
43532
+ resolve8(parseCatFileBatch(Buffer.concat(chunks), paths.length));
43482
43533
  };
43483
43534
  const timer = setTimeout(() => {
43484
43535
  child2.kill("SIGKILL");
@@ -43903,7 +43954,7 @@ function healthPollIntervalMs() {
43903
43954
  return HEALTH_POLL_INTERVAL_MS;
43904
43955
  }
43905
43956
  function waitForProcessStability(child2, graceMs = earlyExitGraceMs()) {
43906
- return new Promise((resolve7, reject) => {
43957
+ return new Promise((resolve8, reject) => {
43907
43958
  let settled = false;
43908
43959
  const finish = (fn) => {
43909
43960
  if (settled) return;
@@ -43913,7 +43964,7 @@ function waitForProcessStability(child2, graceMs = earlyExitGraceMs()) {
43913
43964
  child2.removeAllListeners("exit");
43914
43965
  fn();
43915
43966
  };
43916
- const timer = setTimeout(() => finish(resolve7), graceMs);
43967
+ const timer = setTimeout(() => finish(resolve8), graceMs);
43917
43968
  child2.on("error", (err) => finish(() => reject(new Error(`stage process failed to start: ${err.message}`))));
43918
43969
  child2.on("exit", (code, signal) => {
43919
43970
  const detail = code != null ? `code ${code}` : signal ? `signal ${signal}` : "unknown reason";
@@ -44106,10 +44157,10 @@ function pickStagePort(range, isFree) {
44106
44157
  throw new Error(`no free stage port in range ${start}-${end} \u2014 every port is in use`);
44107
44158
  }
44108
44159
  function isPortFree(port) {
44109
- return new Promise((resolve7) => {
44160
+ return new Promise((resolve8) => {
44110
44161
  const srv = (0, import_node_net.createServer)();
44111
- srv.once("error", () => resolve7(false));
44112
- srv.once("listening", () => srv.close(() => resolve7(true)));
44162
+ srv.once("error", () => resolve8(false));
44163
+ srv.once("listening", () => srv.close(() => resolve8(true)));
44113
44164
  srv.listen(port, "127.0.0.1");
44114
44165
  });
44115
44166
  }
@@ -44387,7 +44438,7 @@ async function killTree(pid) {
44387
44438
  } catch {
44388
44439
  }
44389
44440
  }
44390
- await new Promise((resolve7) => setTimeout(resolve7, 500));
44441
+ await new Promise((resolve8) => setTimeout(resolve8, 500));
44391
44442
  try {
44392
44443
  process.kill(-pid, "SIGKILL");
44393
44444
  } catch {
@@ -44460,7 +44511,7 @@ async function waitForHealth(url, timeoutMs, anyStatus = false) {
44460
44511
  } catch (e) {
44461
44512
  last = e.message;
44462
44513
  }
44463
- await new Promise((resolve7) => setTimeout(resolve7, healthPollIntervalMs()));
44514
+ await new Promise((resolve8) => setTimeout(resolve8, healthPollIntervalMs()));
44464
44515
  }
44465
44516
  throw new Error(`stage health check timed out for ${url}${last ? ` (${last})` : ""}`);
44466
44517
  }
@@ -44820,7 +44871,7 @@ function unlinkNodeModulesJunction(wtPath) {
44820
44871
  }
44821
44872
  }
44822
44873
  function defaultSleep2(ms) {
44823
- return new Promise((resolve7) => setTimeout(resolve7, ms));
44874
+ return new Promise((resolve8) => setTimeout(resolve8, ms));
44824
44875
  }
44825
44876
  async function removeResidueDirectory(wtPath, options = {}) {
44826
44877
  const { sleep: sleep2 = defaultSleep2, removeEmptyDir = import_node_fs42.rmdirSync, exists = import_node_fs42.existsSync } = options;
@@ -46570,7 +46621,7 @@ ${list}`);
46570
46621
  // #5400: after grace, name "GitHub delivered zero runs" instead of burning the full budget as pending.
46571
46622
  pollHeadWorkflowRunCount: () => waitLoopHeadWorkflowRunCount("pr checks-wait", number, repo),
46572
46623
  baseBranch,
46573
- sleep: (ms) => new Promise((resolve7) => setTimeout(resolve7, ms)),
46624
+ sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms)),
46574
46625
  log: (message2) => console.warn(message2),
46575
46626
  timeoutMs: Math.max(1, budgetMs - (Date.now() - waitStarted)),
46576
46627
  // Liveness on stderr, one line per poll. A silent bounded wait is indistinguishable from a hang, and
@@ -46725,7 +46776,7 @@ ${list}`);
46725
46776
  // #5400: same zero-runs delivery probe as checks-wait — do not burn the land budget on silence.
46726
46777
  pollHeadWorkflowRunCount: () => waitLoopHeadWorkflowRunCount("pr land", prNumber, repo),
46727
46778
  baseBranch: "development",
46728
- sleep: (ms) => new Promise((resolve7) => setTimeout(resolve7, ms)),
46779
+ sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms)),
46729
46780
  log: (message2) => console.warn(message2),
46730
46781
  // `pr land` inherits the same (raised) checks budget, so it needs the same liveness — otherwise the
46731
46782
  // 30m wait is SILENT and reads exactly like the hang #2940 was filed about, only three times longer.
@@ -46776,7 +46827,7 @@ ${list}`);
46776
46827
  } else {
46777
46828
  lastFailure = void 0;
46778
46829
  }
46779
- await new Promise((resolve7) => setTimeout(resolve7, PR_LAND_POLL_MS));
46830
+ await new Promise((resolve8) => setTimeout(resolve8, PR_LAND_POLL_MS));
46780
46831
  }
46781
46832
  if (lastFailure) {
46782
46833
  throw new Error(
@@ -46934,7 +46985,7 @@ ${list}`);
46934
46985
  checksDiagnosticCommand: `gh pr checks ${number} --repo ${repo}`,
46935
46986
  pollHeadWorkflowRunCount: () => waitLoopHeadWorkflowRunCount("pr merge --wait", number, repo),
46936
46987
  baseBranch,
46937
- sleep: (ms) => new Promise((resolve7) => setTimeout(resolve7, ms)),
46988
+ sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms)),
46938
46989
  log: (message2) => console.warn(message2),
46939
46990
  timeoutMs: Math.max(1, budgetMs - (Date.now() - waitStarted)),
46940
46991
  progress: ({ state, elapsedMs, remainingMs }) => console.warn(`pr merge: --wait checks \u2014 ${state}, ${Math.round(elapsedMs / 1e3)}s elapsed, ${Math.round(remainingMs / 6e4)}m left`)
@@ -47003,7 +47054,7 @@ ${list}`);
47003
47054
  }
47004
47055
  if (!o.auto && mergeRejectedBaseBranchModified(message2)) {
47005
47056
  console.warn(`pr merge: the base branch was modified by a concurrent merge \u2014 waiting ${PR_MERGE_BASE_RACE_RETRY_DELAY_MS / 1e3}s and retrying PR #${number}'s merge once (#6052).`);
47006
- await new Promise((resolve7) => setTimeout(resolve7, PR_MERGE_BASE_RACE_RETRY_DELAY_MS));
47057
+ await new Promise((resolve8) => setTimeout(resolve8, PR_MERGE_BASE_RACE_RETRY_DELAY_MS));
47007
47058
  await execFileP("gh", buildPrMergeArgs({ number, repoArgs, method, auto: false, deleteBranch: false, bodyFile }), { timeout: GH_MUTATION_TIMEOUT_MS }).catch((e2) => {
47008
47059
  const m2 = String(e2.message || "");
47009
47060
  if (/already been merged/i.test(m2)) {
@@ -47264,7 +47315,7 @@ ${list}`);
47264
47315
  console.warn(`ci checks-wait: zero-runs probe FAILED (${read.error}) \u2014 not treating as delivery failure this cycle`);
47265
47316
  return null;
47266
47317
  },
47267
- sleep: (ms) => new Promise((resolve7) => setTimeout(resolve7, ms)),
47318
+ sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms)),
47268
47319
  log: (message2) => console.warn(message2),
47269
47320
  timeoutMs: timeoutMs ?? PR_CHECKS_TIMEOUT_MS,
47270
47321
  progress: ({ state, elapsedMs, remainingMs }) => console.warn(`ci checks-wait: ${state} \u2014 ${Math.round(elapsedMs / 1e3)}s elapsed, ${Math.round(remainingMs / 6e4)}m left`)
@@ -47629,7 +47680,7 @@ function hotfixFollowUpLegs(runs, foldPort, alignment, foldNote, deployNote) {
47629
47680
  }
47630
47681
  return legs;
47631
47682
  }
47632
- async function runProjectInfoSyncLeg(cb, repo, sleep2 = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms))) {
47683
+ async function runProjectInfoSyncLeg(cb, repo, sleep2 = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms))) {
47633
47684
  for (let attempt = 0; ; attempt++) {
47634
47685
  try {
47635
47686
  return await cb(repo, true);
@@ -48358,18 +48409,63 @@ function rcRunCauseFromLog(log) {
48358
48409
  function memberAnnounceSummaryFile(repo, summaryFile, memberSummaryFiles) {
48359
48410
  return repo === SUITE_REPO ? summaryFile : memberSummaryFiles[repo];
48360
48411
  }
48361
- var suiteStateDir = (root) => (0, import_node_path44.join)(resolvePrimaryGitDir(root), "mmi-runtime", "suite");
48412
+ var suiteStateDir = (root) => (0, import_node_path45.join)(resolvePrimaryGitDir(root), "mmi-runtime", "suite");
48413
+ async function runSuiteMemberRcand(repo, previousRc, rcWasAbsent) {
48414
+ const deps = trainApplyDeps();
48415
+ deps.suiteCut = suiteCutContext(repo, SUITE_REPO);
48416
+ deps.announce = async () => ({ status: "skipped", note: "suite announcement belongs to MM-Work" });
48417
+ return rcandInCheckout(repo, deps, previousRc, rcWasAbsent);
48418
+ }
48419
+ async function rcandInCheckout(repo, deps, previousRc, rcWasAbsent) {
48420
+ if (repo !== SUITE_REPO) await prepareDevelopmentCheckout((args) => deps.run("git", args), repo);
48421
+ await deps.run("git", ["fetch", "origin", "--tags"]);
48422
+ let rcNow;
48423
+ try {
48424
+ rcNow = (await deps.run("git", ["rev-parse", "--verify", "origin/rc"])).trim();
48425
+ } catch (e) {
48426
+ if (!rcWasAbsent || !previousRc || repo === SUITE_REPO) throw e;
48427
+ const slug = repo.split("/")[1].toLowerCase();
48428
+ const meta = await fetchProjectBySlugChecked(slug, registryClientDeps(await loadConfig()));
48429
+ if (!meta.ok || meta.project?.releaseTrack !== "full" || meta.project.suite !== "mm-work")
48430
+ throw new Error(`${repo}: first rc branch requires releaseTrack full and suite=mm-work in META; no branch created`);
48431
+ const main = (await deps.run("git", ["rev-parse", "origin/main"])).trim();
48432
+ if (main.toLowerCase() !== previousRc.toLowerCase()) throw new Error(`${repo}: main moved since the cut snapshot; no rc branch created`);
48433
+ await deps.run("git", ["push", "origin", `${previousRc}:refs/heads/rc`]);
48434
+ await deps.run("git", ["fetch", "origin", "rc"]);
48435
+ rcNow = (await deps.run("git", ["rev-parse", "origin/rc"])).trim();
48436
+ }
48437
+ if (!(await deps.run("git", ["branch", "--list", "rc"])).trim()) {
48438
+ await deps.run("git", ["switch", "-c", "rc", "--track", "origin/rc"]);
48439
+ await deps.run("git", ["switch", "development"]);
48440
+ }
48441
+ if (previousRc && rcNow.toLowerCase() !== previousRc.toLowerCase()) {
48442
+ const tags = (await deps.run("git", ["tag", "--points-at", rcNow, "--list", "v*-rc.*"])).trim().split("\n").filter(Boolean);
48443
+ if (tags.length !== 1) throw new Error(`${repo}: rc moved since cut start without exactly one candidate tag; stop`);
48444
+ return { tag: tags[0], sha: rcNow };
48445
+ }
48446
+ try {
48447
+ const result = await runTrainApply("rcand", deps, { watch: true });
48448
+ if (result.deployStatus !== "success") throw new Error(`${repo}: rcand deploy not green (${result.deployStatus})`);
48449
+ return { tag: result.tag, sha: (await deps.run("git", ["rev-parse", result.tag])).trim() };
48450
+ } catch (e) {
48451
+ const existing = (await deps.run("git", ["tag", "--list", "v*-rc.*"])).trim();
48452
+ if (!existing || !/resume|partial|ledger|tag.*pushed/i.test(e.message)) throw e;
48453
+ const result = await runRcandResume(deps, { watch: true });
48454
+ if (result.deployStatus !== "success") throw new Error(`${repo}: resumed rcand deploy not green (${result.deployStatus})`);
48455
+ return { tag: result.tag, sha: result.tagSha };
48456
+ }
48457
+ }
48362
48458
  function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr, memberSummaryFilesTr = {}) {
48363
- const recordPath = (0, import_node_path44.join)(suiteStateDir(root), "cut.json");
48364
- const base = (0, import_node_path44.join)(suiteStateDir(root), "checkouts");
48459
+ const recordPath = (0, import_node_path45.join)(suiteStateDir(root), "cut.json");
48460
+ const base = (0, import_node_path45.join)(suiteStateDir(root), "checkouts");
48365
48461
  const checkout = async (repo) => {
48366
48462
  if (repo === SUITE_REPO) return root;
48367
- const target = (0, import_node_path44.join)(base, repo.split("/")[1]);
48463
+ const target = (0, import_node_path45.join)(base, repo.split("/")[1]);
48368
48464
  if (!(0, import_node_fs45.existsSync)(target)) {
48369
48465
  (0, import_node_fs45.mkdirSync)(base, { recursive: true });
48370
48466
  await exec("git", ["clone", "--quiet", "--branch", "development", `https://github.com/${repo}.git`, target], root, WATCH_TIMEOUT);
48371
48467
  }
48372
- if (!(0, import_node_fs45.existsSync)((0, import_node_path44.join)(target, ".git"))) throw new Error(`${repo}: suite checkout has no .git at ${target}; stop`);
48468
+ if (!(0, import_node_fs45.existsSync)((0, import_node_path45.join)(target, ".git"))) throw new Error(`${repo}: suite checkout has no .git at ${target}; stop`);
48373
48469
  const origin = await exec("git", ["remote", "get-url", "origin"], target);
48374
48470
  if (!origin.toLowerCase().endsWith(`/${repo.toLowerCase()}.git`) && !origin.toLowerCase().endsWith(`/${repo.toLowerCase()}`))
48375
48471
  throw new Error(`${repo}: suite checkout origin mismatch at ${target}; stop`);
@@ -48429,45 +48525,28 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
48429
48525
  throw e;
48430
48526
  }
48431
48527
  };
48432
- const runRcand = async (repo, previousRc, rcWasAbsent) => inTrain(repo, async (deps) => {
48433
- if (repo !== SUITE_REPO) await prepareDevelopmentCheckout((args) => deps.run("git", args), repo);
48434
- await deps.run("git", ["fetch", "origin", "--tags"]);
48435
- let rcNow;
48436
- try {
48437
- rcNow = (await deps.run("git", ["rev-parse", "--verify", "origin/rc"])).trim();
48438
- } catch (e) {
48439
- if (!rcWasAbsent || !previousRc || repo === SUITE_REPO) throw e;
48440
- const slug = repo.split("/")[1].toLowerCase();
48441
- const meta = await fetchProjectBySlugChecked(slug, registryClientDeps(await loadConfig()));
48442
- if (!meta.ok || meta.project?.releaseTrack !== "full" || meta.project.suite !== "mm-work")
48443
- throw new Error(`${repo}: first rc branch requires releaseTrack full and suite=mm-work in META; no branch created`);
48444
- const main = (await deps.run("git", ["rev-parse", "origin/main"])).trim();
48445
- if (main.toLowerCase() !== previousRc.toLowerCase()) throw new Error(`${repo}: main moved since the cut snapshot; no rc branch created`);
48446
- await deps.run("git", ["push", "origin", `${previousRc}:refs/heads/rc`]);
48447
- await deps.run("git", ["fetch", "origin", "rc"]);
48448
- rcNow = (await deps.run("git", ["rev-parse", "origin/rc"])).trim();
48449
- }
48450
- if (!(await deps.run("git", ["branch", "--list", "rc"])).trim()) {
48451
- await deps.run("git", ["switch", "-c", "rc", "--track", "origin/rc"]);
48452
- await deps.run("git", ["switch", "development"]);
48453
- }
48454
- if (previousRc && rcNow.toLowerCase() !== previousRc.toLowerCase()) {
48455
- const tags = (await deps.run("git", ["tag", "--points-at", rcNow, "--list", "v*-rc.*"])).trim().split("\n").filter(Boolean);
48456
- if (tags.length !== 1) throw new Error(`${repo}: rc moved since cut start without exactly one candidate tag; stop`);
48457
- return { tag: tags[0], sha: rcNow };
48458
- }
48459
- try {
48460
- const result = await runTrainApply("rcand", deps, { watch: true });
48461
- if (result.deployStatus !== "success") throw new Error(`${repo}: rcand deploy not green (${result.deployStatus})`);
48462
- return { tag: result.tag, sha: (await deps.run("git", ["rev-parse", result.tag])).trim() };
48463
- } catch (e) {
48464
- const existing = (await deps.run("git", ["tag", "--list", "v*-rc.*"])).trim();
48465
- if (!existing || !/resume|partial|ledger|tag.*pushed/i.test(e.message)) throw e;
48466
- const result = await runRcandResume(deps, { watch: true });
48467
- if (result.deployStatus !== "success") throw new Error(`${repo}: resumed rcand deploy not green (${result.deployStatus})`);
48468
- return { tag: result.tag, sha: result.tagSha };
48469
- }
48470
- });
48528
+ const runRcand = async (repo, previousRc, rcWasAbsent) => {
48529
+ if (repo === SUITE_REPO) return inTrain(repo, (deps) => rcandInCheckout(repo, deps, previousRc, rcWasAbsent));
48530
+ const target = await checkout(repo);
48531
+ return new Promise((resolveResult, reject) => {
48532
+ const child2 = (0, import_node_child_process18.fork)((0, import_node_path44.resolve)(process.argv[1]), ["devops", "suite", "member-rcand-internal", repo, previousRc ?? "", ...rcWasAbsent ? ["--rc-was-absent"] : []], {
48533
+ cwd: target,
48534
+ stdio: ["ignore", "inherit", "inherit", "ipc"],
48535
+ execArgv: []
48536
+ });
48537
+ let result;
48538
+ let failure;
48539
+ child2.on("message", (message2) => {
48540
+ result = message2.result;
48541
+ failure = message2.error;
48542
+ });
48543
+ child2.on("error", reject);
48544
+ child2.on("exit", (code) => {
48545
+ if (code === 0 && result) resolveResult(result);
48546
+ else reject(new Error(failure ?? `${repo}: member rcand process exited ${code} without a result`));
48547
+ });
48548
+ });
48549
+ };
48471
48550
  const release = async (repo, expected, rcSha, opts) => inTrain(repo, async (deps) => {
48472
48551
  await prepareRcCheckout((args) => deps.run("git", args), repo);
48473
48552
  const plainWords = {
@@ -48522,7 +48601,7 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
48522
48601
  if (!reuse) await gh(["workflow", "run", "module-rc-publish.yml", "--repo", "mutmutco/MMI-Hub", ...fields.flatMap((f) => ["--field", f])]);
48523
48602
  let candidate = reuse;
48524
48603
  for (let i = 0; !candidate && i < 25; i++) {
48525
- await new Promise((resolve7) => setTimeout(resolve7, 1e3));
48604
+ await new Promise((resolve8) => setTimeout(resolve8, 1e3));
48526
48605
  candidate = (await rcRuns()).find((r) => r.displayTitle === title && Date.parse(r.createdAt) >= since);
48527
48606
  }
48528
48607
  if (!candidate) throw new Error(`workflow dispatch outcome ambiguous for "${title}"; inspect Actions before retrying`);
@@ -48677,7 +48756,7 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
48677
48756
  for (let i = 0; ; i++) {
48678
48757
  const pin = await openSuitePinPr({ root, run: (file, args, cwd) => exec(file, args, cwd, GIT_TIMEOUT_MS) }, { repo, tag, stage: "main" });
48679
48758
  if (pin.status !== "pending" || i >= 40) return pin;
48680
- await new Promise((resolve7) => setTimeout(resolve7, 3e4));
48759
+ await new Promise((resolve8) => setTimeout(resolve8, 3e4));
48681
48760
  }
48682
48761
  },
48683
48762
  suiteMapping: async (leadTag) => {
@@ -48693,7 +48772,7 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
48693
48772
  if (parseSuiteMapping(body)) return;
48694
48773
  const dir = suiteStateDir(root);
48695
48774
  (0, import_node_fs45.mkdirSync)(dir, { recursive: true });
48696
- const file = (0, import_node_path44.join)(dir, `notes-${leadTag}.md`);
48775
+ const file = (0, import_node_path45.join)(dir, `notes-${leadTag}.md`);
48697
48776
  (0, import_node_fs45.writeFileSync)(file, `${body}
48698
48777
 
48699
48778
  ${suiteMappingBlock(leadTag, mapping)}`);
@@ -48759,11 +48838,23 @@ function suiteRcDataProdDeps() {
48759
48838
  const rc = facts.stages.rc;
48760
48839
  return rc?.present ? rc.substrate ?? "an unnamed substrate" : null;
48761
48840
  },
48762
- sleep: (ms) => new Promise((resolve7) => setTimeout(resolve7, ms))
48841
+ sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms))
48763
48842
  };
48764
48843
  }
48765
48844
  function registerSuiteCommands(program3) {
48766
48845
  const suite = program3.command("suite").description("MM-Work suite status, candidate cut and release (#7262/#7263)");
48846
+ suite.command("member-rcand-internal", { hidden: true }).argument("<repo>").argument("<previous-rc>").option("--rc-was-absent").action(async (repo, previousRc, o) => {
48847
+ if (!process.send) return fail("member rcand is an internal suite child operation");
48848
+ try {
48849
+ if (repo === SUITE_REPO || (await resolveRepo())?.toLowerCase() !== repo.toLowerCase())
48850
+ throw new Error(`${repo}: member rcand checkout mismatch; stop`);
48851
+ const result = await runSuiteMemberRcand(repo, previousRc || void 0, o.rcWasAbsent);
48852
+ process.send({ result }, () => process.disconnect());
48853
+ } catch (e) {
48854
+ process.exitCode = 1;
48855
+ process.send({ error: e.message }, () => process.disconnect());
48856
+ }
48857
+ });
48767
48858
  const leadRoot = async () => {
48768
48859
  const repo = await resolveRepo();
48769
48860
  if (repo?.toLowerCase() !== SUITE_REPO.toLowerCase()) throw new Error(`suite commands run only from ${SUITE_REPO} (got ${repo ?? "unknown"})`);
@@ -49005,10 +49096,10 @@ function suiteStatusDeps(github, registry2) {
49005
49096
  }
49006
49097
 
49007
49098
  // src/packages-adopt-export.ts
49008
- var import_node_child_process18 = require("node:child_process");
49099
+ var import_node_child_process19 = require("node:child_process");
49009
49100
  var import_node_fs47 = require("node:fs");
49010
49101
  var import_node_os22 = require("node:os");
49011
- var import_node_path45 = require("node:path");
49102
+ var import_node_path46 = require("node:path");
49012
49103
  var import_node_util6 = require("node:util");
49013
49104
  init_cli_shared();
49014
49105
 
@@ -49628,25 +49719,25 @@ function expectedFromRun(run, opts) {
49628
49719
  }
49629
49720
  async function adoptExport(opts, deps) {
49630
49721
  if (!/^sha256:[0-9a-f]{64}$/.test(opts.digest)) throw new Error("--digest must be sha256:<64 hex>, from the export run summary");
49631
- const packagePath = (0, import_node_path45.join)(opts.dir, "package.json");
49632
- const lockPath = (0, import_node_path45.join)(opts.dir, "package-lock.json");
49722
+ const packagePath = (0, import_node_path46.join)(opts.dir, "package.json");
49723
+ const lockPath = (0, import_node_path46.join)(opts.dir, "package-lock.json");
49633
49724
  const packageText = (0, import_node_fs47.readFileSync)(packagePath, "utf8");
49634
49725
  const lockText = (0, import_node_fs47.readFileSync)(lockPath, "utf8");
49635
- const work = (0, import_node_fs47.mkdtempSync)((0, import_node_path45.join)((0, import_node_os22.tmpdir)(), "mmi-adopt-export-"));
49726
+ const work = (0, import_node_fs47.mkdtempSync)((0, import_node_path46.join)((0, import_node_os22.tmpdir)(), "mmi-adopt-export-"));
49636
49727
  try {
49637
49728
  const run = JSON.parse((await deps.ghApi(`repos/${EXPORT_REPO}/actions/runs/${opts.run}`)).toString("utf8"));
49638
- (0, import_node_fs47.writeFileSync)((0, import_node_path45.join)(work, "expected.json"), JSON.stringify(expectedFromRun(run, opts)));
49639
- (0, import_node_fs47.writeFileSync)((0, import_node_path45.join)(work, "provider.json"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}`));
49640
- (0, import_node_fs47.writeFileSync)((0, import_node_path45.join)(work, "artifact.zip"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}/zip`));
49641
- const script = (0, import_node_path45.join)(work, "package_export.py");
49729
+ (0, import_node_fs47.writeFileSync)((0, import_node_path46.join)(work, "expected.json"), JSON.stringify(expectedFromRun(run, opts)));
49730
+ (0, import_node_fs47.writeFileSync)((0, import_node_path46.join)(work, "provider.json"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}`));
49731
+ (0, import_node_fs47.writeFileSync)((0, import_node_path46.join)(work, "artifact.zip"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}/zip`));
49732
+ const script = (0, import_node_path46.join)(work, "package_export.py");
49642
49733
  (0, import_node_fs47.writeFileSync)(script, package_export_default);
49643
49734
  await deps.python(script, ["verify", work, opts.ds, opts.screens]);
49644
- const verified = (0, import_node_path45.join)(work, "verified");
49645
- const metadata = Object.fromEntries(PACKAGES.map(({ name, short: short3 }) => [name, JSON.parse((0, import_node_fs47.readFileSync)((0, import_node_path45.join)(verified, `${short3}.metadata.json`), "utf8"))]));
49735
+ const verified = (0, import_node_path46.join)(work, "verified");
49736
+ const metadata = Object.fromEntries(PACKAGES.map(({ name, short: short3 }) => [name, JSON.parse((0, import_node_fs47.readFileSync)((0, import_node_path46.join)(verified, `${short3}.metadata.json`), "utf8"))]));
49646
49737
  const plan = planAdoption(packageText, lockText, metadata);
49647
49738
  for (const { name } of plan.result.adopted) {
49648
49739
  const short3 = PACKAGES.find((p) => p.name === name).short;
49649
- await deps.npm(["cache", "add", (0, import_node_path45.join)(verified, `${short3}.tgz`)], opts.dir);
49740
+ await deps.npm(["cache", "add", (0, import_node_path46.join)(verified, `${short3}.tgz`)], opts.dir);
49650
49741
  }
49651
49742
  (0, import_node_fs47.writeFileSync)(packagePath, plan.packageText);
49652
49743
  (0, import_node_fs47.writeFileSync)(lockPath, plan.lockText);
@@ -49662,7 +49753,7 @@ async function adoptExport(opts, deps) {
49662
49753
  (0, import_node_fs47.rmSync)(work, { recursive: true, force: true });
49663
49754
  }
49664
49755
  }
49665
- var execFileBuffer = (0, import_node_util6.promisify)(import_node_child_process18.execFile);
49756
+ var execFileBuffer = (0, import_node_util6.promisify)(import_node_child_process19.execFile);
49666
49757
  var MAX_OUTPUT = 200 * 1024 * 1024;
49667
49758
  async function findPython() {
49668
49759
  for (const bin of ["python3", "python"]) {
@@ -49788,7 +49879,7 @@ init_github_client();
49788
49879
  init_cli_shared();
49789
49880
  var import_node_fs49 = require("node:fs");
49790
49881
  var import_node_os23 = require("node:os");
49791
- var import_node_path46 = require("node:path");
49882
+ var import_node_path47 = require("node:path");
49792
49883
  var GC_GH_TIMEOUT_MS3 = 2e4;
49793
49884
  async function collectStatus() {
49794
49885
  const repo = await resolveRepo();
@@ -49979,8 +50070,8 @@ async function collectOnboardStatus(opts = {}) {
49979
50070
  }
49980
50071
  const home = (0, import_node_os23.homedir)();
49981
50072
  const plugin = onboardPluginGate({
49982
- readKnown: () => readFileSyncSafe((0, import_node_path46.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs49.readFileSync),
49983
- readSettings: () => readFileSyncSafe((0, import_node_path46.join)(home, ".claude", "settings.json"), import_node_fs49.readFileSync)
50073
+ readKnown: () => readFileSyncSafe((0, import_node_path47.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs49.readFileSync),
50074
+ readSettings: () => readFileSyncSafe((0, import_node_path47.join)(home, ".claude", "settings.json"), import_node_fs49.readFileSync)
49984
50075
  });
49985
50076
  return {
49986
50077
  guide: "Read the MMI org-guide skill before project work; load only the relevant topic. Use mmi-help for a short introduction.",
@@ -53537,7 +53628,7 @@ function registerSessionReport(program3) {
53537
53628
 
53538
53629
  // src/stage-commands.ts
53539
53630
  var import_node_fs51 = require("node:fs");
53540
- var import_node_path47 = require("node:path");
53631
+ var import_node_path48 = require("node:path");
53541
53632
  init_cli_shared();
53542
53633
  init_clean_exit();
53543
53634
 
@@ -53785,8 +53876,8 @@ function registerStageCommands(program3) {
53785
53876
  const repo = Array.isArray(project2?.repos) && typeof project2.repos[0] === "string" ? project2.repos[0] : slug;
53786
53877
  const resolution = decideStage({
53787
53878
  registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
53788
- hasCompose: (0, import_node_fs51.existsSync)((0, import_node_path47.join)(process.cwd(), "docker-compose.yml")),
53789
- hasEnvExample: (0, import_node_fs51.existsSync)((0, import_node_path47.join)(process.cwd(), ".env.example")),
53879
+ hasCompose: (0, import_node_fs51.existsSync)((0, import_node_path48.join)(process.cwd(), "docker-compose.yml")),
53880
+ hasEnvExample: (0, import_node_fs51.existsSync)((0, import_node_path48.join)(process.cwd(), ".env.example")),
53790
53881
  repo
53791
53882
  });
53792
53883
  if (resolution.source === "derived" && resolution.derived) {
@@ -54081,7 +54172,7 @@ vercel preview (${preview.branch}): ${d.state}${d.url ? ` \u2014 ${d.url}` : ""}
54081
54172
  var import_node_crypto17 = require("node:crypto");
54082
54173
  var import_node_fs52 = require("node:fs");
54083
54174
  var import_promises11 = require("node:fs/promises");
54084
- var import_node_path48 = require("node:path");
54175
+ var import_node_path49 = require("node:path");
54085
54176
  var ARTIFACT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
54086
54177
  var MAX_BYTES2 = 5 * 1024 * 1024 * 1024;
54087
54178
  async function sha256File(path2) {
@@ -54091,7 +54182,7 @@ async function sha256File(path2) {
54091
54182
  }
54092
54183
  async function putTenantArtifact(repo, stage, inputPath, deps) {
54093
54184
  if (!["dev", "rc", "main"].includes(stage)) throw new Error("tenant artifact put: <stage> must be dev, rc, or main");
54094
- const path2 = (0, import_node_path48.resolve)(inputPath);
54185
+ const path2 = (0, import_node_path49.resolve)(inputPath);
54095
54186
  const info = await (0, import_promises11.stat)(path2);
54096
54187
  if (!info.isFile()) throw new Error("tenant artifact put: input path must be a file");
54097
54188
  if (!Number.isSafeInteger(info.size) || info.size < 1 || info.size > MAX_BYTES2) throw new Error(`tenant artifact put: file must be 1..${MAX_BYTES2} bytes`);
@@ -54281,7 +54372,7 @@ async function resolveWhoami(deps) {
54281
54372
  }
54282
54373
 
54283
54374
  // src/command-register-developer.ts
54284
- var import_node_path54 = require("node:path");
54375
+ var import_node_path55 = require("node:path");
54285
54376
  init_house_map();
54286
54377
  init_hub_url();
54287
54378
 
@@ -54314,21 +54405,21 @@ async function executeWaveLand(plan, deps) {
54314
54405
  }
54315
54406
 
54316
54407
  // src/dist-drift.ts
54317
- var import_node_child_process19 = require("node:child_process");
54408
+ var import_node_child_process20 = require("node:child_process");
54318
54409
  var import_node_crypto19 = require("node:crypto");
54319
54410
  var import_node_fs54 = require("node:fs");
54320
54411
  var import_node_os24 = require("node:os");
54321
- var import_node_path50 = require("node:path");
54412
+ var import_node_path51 = require("node:path");
54322
54413
 
54323
54414
  // ../scripts/distribution-digest.mjs
54324
54415
  var import_node_crypto18 = require("node:crypto");
54325
54416
  var import_node_fs53 = require("node:fs");
54326
- var import_node_path49 = require("node:path");
54417
+ var import_node_path50 = require("node:path");
54327
54418
  var slash = (value) => value.replaceAll("\\", "/");
54328
54419
  function repoPath(root, declaredPath, label) {
54329
- const absoluteRoot = (0, import_node_path49.resolve)(root);
54330
- const target = (0, import_node_path49.resolve)(root, declaredPath);
54331
- if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path49.sep}`)) {
54420
+ const absoluteRoot = (0, import_node_path50.resolve)(root);
54421
+ const target = (0, import_node_path50.resolve)(root, declaredPath);
54422
+ if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path50.sep}`)) {
54332
54423
  throw new Error(`${label} ${declaredPath} escapes the repository root`);
54333
54424
  }
54334
54425
  return target;
@@ -54443,7 +54534,7 @@ function walkFiles(root) {
54443
54534
  const files = [];
54444
54535
  const walk2 = (directory) => {
54445
54536
  for (const entry of (0, import_node_fs54.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
54446
- const child2 = (0, import_node_path50.join)(directory, entry.name);
54537
+ const child2 = (0, import_node_path51.join)(directory, entry.name);
54447
54538
  if (entry.isDirectory()) walk2(child2);
54448
54539
  else files.push(child2);
54449
54540
  }
@@ -54453,14 +54544,14 @@ function walkFiles(root) {
54453
54544
  }
54454
54545
  function bomPathFor(root) {
54455
54546
  try {
54456
- const registry2 = JSON.parse((0, import_node_fs54.readFileSync)((0, import_node_path50.join)(root, "surfaces.json"), "utf8"));
54457
- return (0, import_node_path50.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
54547
+ const registry2 = JSON.parse((0, import_node_fs54.readFileSync)((0, import_node_path51.join)(root, "surfaces.json"), "utf8"));
54548
+ return (0, import_node_path51.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
54458
54549
  } catch {
54459
- return (0, import_node_path50.join)(root, "distribution-bom.json");
54550
+ return (0, import_node_path51.join)(root, "distribution-bom.json");
54460
54551
  }
54461
54552
  }
54462
54553
  function rebuildTo(packageRoot, outDir) {
54463
- (0, import_node_child_process19.execFileSync)(process.execPath, ["build.mjs"], {
54554
+ (0, import_node_child_process20.execFileSync)(process.execPath, ["build.mjs"], {
54464
54555
  cwd: packageRoot,
54465
54556
  env: { ...process.env, MMI_DIST_OUTDIR: outDir },
54466
54557
  windowsHide: true,
@@ -54469,28 +54560,28 @@ function rebuildTo(packageRoot, outDir) {
54469
54560
  });
54470
54561
  }
54471
54562
  function runDistStatus(root) {
54472
- const stage = (0, import_node_fs54.mkdtempSync)((0, import_node_path50.join)((0, import_node_os24.tmpdir)(), "mmi-dist-drift-"));
54563
+ const stage = (0, import_node_fs54.mkdtempSync)((0, import_node_path51.join)((0, import_node_os24.tmpdir)(), "mmi-dist-drift-"));
54473
54564
  let overlayCount = 0;
54474
54565
  try {
54475
- const cliOut = (0, import_node_path50.join)(stage, "cli-dist");
54476
- const hubOut = (0, import_node_path50.join)(stage, "hub-dist");
54477
- rebuildTo((0, import_node_path50.join)(root, "cli"), cliOut);
54478
- rebuildTo((0, import_node_path50.join)(root, "updater"), hubOut);
54566
+ const cliOut = (0, import_node_path51.join)(stage, "cli-dist");
54567
+ const hubOut = (0, import_node_path51.join)(stage, "hub-dist");
54568
+ rebuildTo((0, import_node_path51.join)(root, "cli"), cliOut);
54569
+ rebuildTo((0, import_node_path51.join)(root, "updater"), hubOut);
54479
54570
  const outDirFor = (packageDir) => packageDir === "cli" ? cliOut : hubOut;
54480
54571
  const rebuilt = (path2) => {
54481
54572
  const spec = DIST_ARTIFACTS.find((entry) => entry.path === path2);
54482
- return spec ? readOrNull((0, import_node_path50.join)(outDirFor(spec.packageDir), spec.output)) : null;
54573
+ return spec ? readOrNull((0, import_node_path51.join)(outDirFor(spec.packageDir), spec.output)) : null;
54483
54574
  };
54484
- const committed = (path2) => readOrNull((0, import_node_path50.join)(root, path2));
54485
- const tree = (path2) => readOrNull((0, import_node_path50.join)(root, path2));
54486
- const distRoot = (0, import_node_path50.join)(root, "cli", "dist");
54487
- const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path50.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
54575
+ const committed = (path2) => readOrNull((0, import_node_path51.join)(root, path2));
54576
+ const tree = (path2) => readOrNull((0, import_node_path51.join)(root, path2));
54577
+ const distRoot = (0, import_node_path51.join)(root, "cli", "dist");
54578
+ const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path51.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
54488
54579
  const bom = JSON.parse((0, import_node_fs54.readFileSync)(bomPathFor(root), "utf8"));
54489
54580
  const digest = (entries) => {
54490
- const overlay = (0, import_node_path50.join)(stage, `overlay-${overlayCount++}`);
54581
+ const overlay = (0, import_node_path51.join)(stage, `overlay-${overlayCount++}`);
54491
54582
  for (const entry of entries) {
54492
- const target = (0, import_node_path50.join)(overlay, entry.path);
54493
- (0, import_node_fs54.mkdirSync)((0, import_node_path50.dirname)(target), { recursive: true });
54583
+ const target = (0, import_node_path51.join)(overlay, entry.path);
54584
+ (0, import_node_fs54.mkdirSync)((0, import_node_path51.dirname)(target), { recursive: true });
54494
54585
  (0, import_node_fs54.writeFileSync)(target, entry.bytes);
54495
54586
  }
54496
54587
  return digestPackedFiles(overlay, entries.map((entry) => entry.path));
@@ -54502,7 +54593,7 @@ function runDistStatus(root) {
54502
54593
  }
54503
54594
 
54504
54595
  // src/doc-refs-live.ts
54505
- var import_node_path51 = require("node:path");
54596
+ var import_node_path52 = require("node:path");
54506
54597
  var concrete = (value) => !!value && !/[<>{}$]/.test(value);
54507
54598
  var liveOption = (tokens2, flag) => {
54508
54599
  const index = tokens2.indexOf(flag);
@@ -54575,7 +54666,7 @@ async function runLiveDocRefs(root, deps, live) {
54575
54666
  const offline = runDocRefs(root, { ...deps, runbooksOnly: true });
54576
54667
  const read = deps.readFile ?? readFileOrNull;
54577
54668
  const docs = Object.fromEntries((deps.listDocs ?? defaultListDocs)(root).flatMap((doc) => {
54578
- const body = read((0, import_node_path51.join)(root, doc));
54669
+ const body = read((0, import_node_path52.join)(root, doc));
54579
54670
  return body !== null && isRunbook(doc, body) && !deps.isIgnored?.([doc]).has(doc) ? [[doc, body]] : [];
54580
54671
  }));
54581
54672
  const remote = await checkRunbookLive(docs, live);
@@ -54584,9 +54675,9 @@ async function runLiveDocRefs(root, deps, live) {
54584
54675
  }
54585
54676
 
54586
54677
  // src/doc-refs-live-io.ts
54587
- var import_node_child_process20 = require("node:child_process");
54678
+ var import_node_child_process21 = require("node:child_process");
54588
54679
  var import_node_util7 = require("node:util");
54589
- var exec2 = (0, import_node_util7.promisify)(import_node_child_process20.execFile);
54680
+ var exec2 = (0, import_node_util7.promisify)(import_node_child_process21.execFile);
54590
54681
  function createRunbookLiveDeps(cliBin) {
54591
54682
  const json = async (args) => {
54592
54683
  const { stdout } = await exec2(process.execPath, [cliBin, ...args, "--json"], { windowsHide: true, timeout: 3e4, maxBuffer: 4 * 1024 * 1024 });
@@ -54612,7 +54703,7 @@ function createRunbookLiveDeps(cliBin) {
54612
54703
  };
54613
54704
  const first = await probe().catch(() => 599);
54614
54705
  if (first < 500) return first;
54615
- await new Promise((resolve7) => setTimeout(resolve7, 2e4));
54706
+ await new Promise((resolve8) => setTimeout(resolve8, 2e4));
54616
54707
  return probe();
54617
54708
  }
54618
54709
  };
@@ -54715,7 +54806,7 @@ init_hub_auth();
54715
54806
 
54716
54807
  // src/schedules-lift-command.ts
54717
54808
  var import_promises12 = require("node:fs/promises");
54718
- var import_node_path52 = require("node:path");
54809
+ var import_node_path53 = require("node:path");
54719
54810
  init_clean_exit();
54720
54811
  init_cli_shared();
54721
54812
  var DEFAULT_WORKFLOWS_DIR = ".github/workflows";
@@ -54744,7 +54835,7 @@ async function readWorkflowFiles(dir) {
54744
54835
  const files = [];
54745
54836
  for (const name of names.sort()) {
54746
54837
  if (!/\.ya?ml$/.test(name)) continue;
54747
- files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises12.readFile)((0, import_node_path52.join)(dir, name), "utf8") });
54838
+ files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises12.readFile)((0, import_node_path53.join)(dir, name), "utf8") });
54748
54839
  }
54749
54840
  return files;
54750
54841
  }
@@ -54826,9 +54917,9 @@ function registerSchedulesLiftCommand(program3, deps = {}) {
54826
54917
  }
54827
54918
 
54828
54919
  // src/spawn-policy-core.ts
54829
- var import_node_child_process21 = require("node:child_process");
54920
+ var import_node_child_process22 = require("node:child_process");
54830
54921
  var import_node_fs55 = require("node:fs");
54831
- var import_node_path53 = require("node:path");
54922
+ var import_node_path54 = require("node:path");
54832
54923
  var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
54833
54924
  var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
54834
54925
  var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
@@ -54897,7 +54988,7 @@ function findViolationsInSource(raw) {
54897
54988
  return found;
54898
54989
  }
54899
54990
  function policedFiles(root) {
54900
- const r = (0, import_node_child_process21.spawnSync)("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
54991
+ const r = (0, import_node_child_process22.spawnSync)("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
54901
54992
  cwd: root,
54902
54993
  encoding: "utf8",
54903
54994
  windowsHide: true,
@@ -54914,7 +55005,7 @@ function runSpawnPolicy(root) {
54914
55005
  for (const file of files) {
54915
55006
  let raw;
54916
55007
  try {
54917
- raw = (0, import_node_fs55.readFileSync)((0, import_node_path53.join)(root, file), "utf8");
55008
+ raw = (0, import_node_fs55.readFileSync)((0, import_node_path54.join)(root, file), "utf8");
54918
55009
  } catch {
54919
55010
  continue;
54920
55011
  }
@@ -54934,7 +55025,7 @@ function runSpawnPolicy(root) {
54934
55025
  function registerDeveloperCommands(program3) {
54935
55026
  const rules = program3.command("rules").description("org-managed .gitignore delivery");
54936
55027
  rules.command("gitignore").option("--write", "upsert the managed block into .gitignore (default: check only, non-zero exit on drift)").option("--json", "machine-readable output").description("verify (or --write) this repo's org-managed .gitignore block matches the SSOT").action((opts) => {
54937
- const path2 = (0, import_node_path54.join)(process.cwd(), ".gitignore");
55028
+ const path2 = (0, import_node_path55.join)(process.cwd(), ".gitignore");
54938
55029
  const current = (0, import_node_fs56.existsSync)(path2) ? (0, import_node_fs56.readFileSync)(path2, "utf8") : null;
54939
55030
  const plan = planManagedGitignore(current);
54940
55031
  const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
@@ -55215,7 +55306,7 @@ ${r.stderr ?? ""}`).catch(() => "");
55215
55306
  var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
55216
55307
  var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
55217
55308
  function envHealLockPath(home) {
55218
- return (0, import_node_path55.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
55309
+ return (0, import_node_path56.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
55219
55310
  }
55220
55311
  async function withEnvHealLock(what, run) {
55221
55312
  try {
@@ -55387,7 +55478,7 @@ function mmiDoctorDeps(opts = {}) {
55387
55478
  // has no generated routing index would
55388
55479
  // get a permanent — demanding an artifact it never asked for.
55389
55480
  docsIndexState: (root) => {
55390
- if (!(0, import_node_fs57.existsSync)((0, import_node_path55.join)(root, DOCS_INDEX_PATH))) return void 0;
55481
+ if (!(0, import_node_fs57.existsSync)((0, import_node_path56.join)(root, DOCS_INDEX_PATH))) return void 0;
55391
55482
  const real = createDocsIndexDeps(root);
55392
55483
  let docs;
55393
55484
  const listDocs = () => docs ??= real.listDocs();
@@ -55396,7 +55487,7 @@ function mmiDoctorDeps(opts = {}) {
55396
55487
  },
55397
55488
  // #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
55398
55489
  healDocsIndex: (root) => {
55399
- if (!(0, import_node_fs57.existsSync)((0, import_node_path55.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
55490
+ if (!(0, import_node_fs57.existsSync)((0, import_node_path56.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
55400
55491
  const real = createDocsIndexDeps(root);
55401
55492
  let docs;
55402
55493
  const listDocs = () => docs ??= real.listDocs();
@@ -55417,8 +55508,8 @@ function mmiDoctorDeps(opts = {}) {
55417
55508
  });
55418
55509
  const raced = await Promise.race([
55419
55510
  work.then((r) => ({ ...r, timedOut: false })),
55420
- new Promise((resolve7) => {
55421
- ceiling = setTimeout(() => resolve7({ timedOut: true, scanned: 0, findings: 0, fixed: 0, failed: 0 }), BOARD_DOCTOR_TIMEOUT_MS);
55511
+ new Promise((resolve8) => {
55512
+ ceiling = setTimeout(() => resolve8({ timedOut: true, scanned: 0, findings: 0, fixed: 0, failed: 0 }), BOARD_DOCTOR_TIMEOUT_MS);
55422
55513
  })
55423
55514
  ]);
55424
55515
  if (raced.timedOut) return { scanned: 0, findings: 0, fixed: 0, failed: 0, timedOut: true };
@@ -55451,8 +55542,8 @@ function mmiDoctorDeps(opts = {}) {
55451
55542
  incomplete: nb.incomplete,
55452
55543
  timedOut: false
55453
55544
  })),
55454
- new Promise((resolve7) => {
55455
- ceiling = setTimeout(() => resolve7({ driftLines: [], incomplete: [], timedOut: true }), SCHEDULES_DRIFT_TIMEOUT_MS);
55545
+ new Promise((resolve8) => {
55546
+ ceiling = setTimeout(() => resolve8({ driftLines: [], incomplete: [], timedOut: true }), SCHEDULES_DRIFT_TIMEOUT_MS);
55456
55547
  })
55457
55548
  ]);
55458
55549
  if (!raced.timedOut && raced.incomplete.length === 0) writeSchedulesDriftCache(cachePath, raced.driftLines);
@@ -56504,16 +56595,16 @@ function ciAuditDeps2() {
56504
56595
  // gate re-seed step is skipped gracefully rather than failing mid-run.
56505
56596
  readSeedFile: (path2) => {
56506
56597
  if (!root) return null;
56507
- const fullPath = (0, import_node_path55.join)(root, path2);
56598
+ const fullPath = (0, import_node_path56.join)(root, path2);
56508
56599
  return (0, import_node_fs57.existsSync)(fullPath) ? (0, import_node_fs57.readFileSync)(fullPath, "utf8") : null;
56509
56600
  }
56510
56601
  };
56511
56602
  }
56512
56603
  function hubRoot2() {
56513
- const fromPkg = (0, import_node_path55.join)(__dirname, "..", "..");
56604
+ const fromPkg = (0, import_node_path56.join)(__dirname, "..", "..");
56514
56605
  const marker = "skills/bootstrap/seeds/manifest.json";
56515
- if ((0, import_node_fs57.existsSync)((0, import_node_path55.join)(fromPkg, marker))) return fromPkg;
56516
- if ((0, import_node_fs57.existsSync)((0, import_node_path55.join)(process.cwd(), marker))) return process.cwd();
56606
+ if ((0, import_node_fs57.existsSync)((0, import_node_path56.join)(fromPkg, marker))) return fromPkg;
56607
+ if ((0, import_node_fs57.existsSync)((0, import_node_path56.join)(process.cwd(), marker))) return process.cwd();
56517
56608
  return null;
56518
56609
  }
56519
56610
  registerQueryCommands(program2);
@@ -56682,7 +56773,7 @@ function directoryBytes(path2) {
56682
56773
  return 0;
56683
56774
  }
56684
56775
  for (const entry of entries) {
56685
- const child2 = (0, import_node_path55.join)(path2, entry.name);
56776
+ const child2 = (0, import_node_path56.join)(path2, entry.name);
56686
56777
  if (entry.isDirectory()) total += directoryBytes(child2);
56687
56778
  else {
56688
56779
  try {
@@ -56712,7 +56803,7 @@ function pluginCacheFsDeps(configRoot, dirBytes) {
56712
56803
  dirBytes,
56713
56804
  listStagingDirs: (root) => (0, import_node_fs57.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
56714
56805
  try {
56715
- return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path55.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs57.statSync)(p).mtimeMs) };
56806
+ return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path56.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs57.statSync)(p).mtimeMs) };
56716
56807
  } catch {
56717
56808
  return { name: d.name, mtimeMs: Date.now() };
56718
56809
  }
@@ -56726,7 +56817,7 @@ function stagingApplyFsGuard(configRoot) {
56726
56817
  return {
56727
56818
  referencedPaths: () => readInstalledPluginRefs(configRoot),
56728
56819
  mtimeMs: (name) => {
56729
- const p = (0, import_node_path55.join)(stagingRoot, name);
56820
+ const p = (0, import_node_path56.join)(stagingRoot, name);
56730
56821
  if (!(0, import_node_fs57.existsSync)(p)) return null;
56731
56822
  try {
56732
56823
  return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs57.statSync)(q).mtimeMs);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.5.124",
3
+ "version": "4.5.125",
4
4
  "description": "MMI Future CLI — the org dev toolbox and shared cross-IDE engine for every registry-declared MMI coding surface.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",