@mutmutco/cli 4.5.33 → 4.5.35

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 +73 -20
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -5485,7 +5485,7 @@ function isSnapshotShape(body) {
5485
5485
  const staleValid = b?.stale === void 0 || typeof b.stale === "object" && b.stale !== null && Number.isFinite(b.stale.ageMs) && b.stale.ageMs >= 0;
5486
5486
  const githubValid = b?.github === void 0 || b.github.credential === "app_installation" && (rate === void 0 || Number.isFinite(rate?.limit) && Number.isFinite(rate?.remaining) && Number.isFinite(rate?.cost) && typeof rate?.resetAt === "string");
5487
5487
  return Boolean(
5488
- b && typeof b === "object" && typeof b.project?.id === "string" && typeof b.project?.title === "string" && typeof b.viewer === "string" && b.viewer.length > 0 && Array.isArray(b.nodes) && Array.isArray(b.writableRepos) && Array.isArray(b.unreadableRepos) && Array.isArray(b.pullRequests) && Array.isArray(b.warnings) && typeof b.partial === "boolean" && githubValid && staleValid
5488
+ b && typeof b === "object" && typeof b.project?.id === "string" && typeof b.project?.title === "string" && typeof b.viewer === "string" && b.viewer.length > 0 && Array.isArray(b.nodes) && Array.isArray(b.writableRepos) && Array.isArray(b.unreadableRepos) && Array.isArray(b.pullRequests) && Array.isArray(b.warnings) && typeof b.partial === "boolean" && (b.eventCursor === void 0 || Number.isInteger(b.eventCursor) && b.eventCursor >= 0) && githubValid && staleValid
5489
5489
  );
5490
5490
  }
5491
5491
  async function fetchHubBoardSnapshot(request, deps) {
@@ -12541,6 +12541,7 @@ async function readBoard(options, deps = {}) {
12541
12541
  const { collected, snapshot } = await collectBoardPreferringSnapshot(cfg, options, deps);
12542
12542
  let writable;
12543
12543
  let pullRequests;
12544
+ let eventCursor;
12544
12545
  let github;
12545
12546
  if (snapshot) {
12546
12547
  for (const unreadable of snapshot.unreadableRepos) {
@@ -12552,6 +12553,7 @@ async function readBoard(options, deps = {}) {
12552
12553
  unknown: new Set(snapshot.unreadableRepos.map((entry) => entry.repo.toLowerCase()))
12553
12554
  };
12554
12555
  pullRequests = snapshot.pullRequests;
12556
+ eventCursor = snapshot.eventCursor;
12555
12557
  github = snapshot.github && { ...snapshot.github, ...snapshot.quotaResetAt ? { quotaResetAt: snapshot.quotaResetAt } : {} };
12556
12558
  } else {
12557
12559
  const probed = await resolveWritableReposForClaimables(collected.items, client);
@@ -12569,6 +12571,7 @@ async function readBoard(options, deps = {}) {
12569
12571
  partial: collected.partial,
12570
12572
  source: "live",
12571
12573
  ...pullRequests ? { pullRequests } : {},
12574
+ ...eventCursor !== void 0 ? { eventCursor } : {},
12572
12575
  ...github ? { github } : {}
12573
12576
  };
12574
12577
  if (options.includeBundleDetails || options.includeAllBodies) {
@@ -16837,10 +16840,10 @@ var rollout_plan_default = {
16837
16840
  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)."
16838
16841
  },
16839
16842
  baseline: {
16840
- version: "4.5.33",
16841
- tag: "v4.5.33",
16842
- commit: "43f256b77935",
16843
- npm: "@mutmutco/cli@4.5.33"
16843
+ version: "4.5.35",
16844
+ tag: "v4.5.35",
16845
+ commit: "f10f5c65382f",
16846
+ npm: "@mutmutco/cli@4.5.35"
16844
16847
  },
16845
16848
  exitCriterion: "fleet-n-of-n",
16846
16849
  hubOnlyShortcut: "forbidden",
@@ -16857,14 +16860,14 @@ var rollout_plan_default = {
16857
16860
  repo: "mutmutco/mmi-hub",
16858
16861
  role: "canary",
16859
16862
  schedule: "train",
16860
- v3Target: "v4.5.33"
16863
+ v3Target: "v4.5.35"
16861
16864
  }
16862
16865
  ],
16863
16866
  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.",
16864
16867
  rollback: {
16865
16868
  independent: true,
16866
- mechanism: "npm dist-tag latest -> 4.5.33 and redeploy the Hub Lambda from tag v4.5.33 (43f256b77935); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
16867
- v3Target: "v4.5.33 (@mutmutco/cli@4.5.33, tag commit 43f256b77935 \u2014 last known-good release carrying the repo-index v4-only contract)"
16869
+ mechanism: "npm dist-tag latest -> 4.5.35 and redeploy the Hub Lambda from tag v4.5.35 (f10f5c65382f); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
16870
+ v3Target: "v4.5.35 (@mutmutco/cli@4.5.35, tag commit f10f5c65382f \u2014 last known-good release carrying the repo-index v4-only contract)"
16868
16871
  }
16869
16872
  },
16870
16873
  {
@@ -23544,7 +23547,8 @@ async function supersedeContentCausedDeployLeg(deps, cwd) {
23544
23547
  `release --supersede-deploy: origin reports ${prior.tag} at ${liveSha.slice(0, 12) || "no tag"} but the ledger anchors ${prior.tagSha.slice(0, 12)} \u2014 an absent or divergent release is NOT supersedeable (release --abort --apply is the unpublished candidate's own exit); nothing was written (#6553)`
23545
23548
  );
23546
23549
  }
23547
- const note = `superseded: content-caused deploy failure on the immutable promoted ${prior.tag} \u2014 the cause fix rides the next patch train (master-approved closure, #6553)`;
23550
+ const failed = prior.phases.deploy.state === "failed";
23551
+ const note = failed ? `superseded: content-caused deploy failure on the immutable promoted ${prior.tag} \u2014 the cause fix rides the next patch train (master-approved closure, #6553)` : `superseded: ${prior.tag} deploy outcome uncorrelatable from the release SHA (no failure recorded) \u2014 closed on the owner's evidence (master-approved closure, #6553, #7327)`;
23548
23552
  await fence(() => atomicWriteJson(path2, {
23549
23553
  ...prior,
23550
23554
  phases: {
@@ -23558,7 +23562,7 @@ async function supersedeContentCausedDeployLeg(deps, cwd) {
23558
23562
  repo: prior.repo,
23559
23563
  tag: prior.tag,
23560
23564
  tagSha: prior.tagSha,
23561
- note: `closed the ${prior.tag} deploy leg as superseded (skipped) \u2014 the ledger is phases-green; rerun the train doctor to confirm, then run the next patch train (hotfix or release) to carry the cause fix`
23565
+ note: failed ? `closed the ${prior.tag} deploy leg as superseded (skipped) \u2014 the ledger is phases-green; rerun the train doctor to confirm, then run the next patch train (hotfix or release) to carry the cause fix` : `closed the ${prior.tag} uncorrelatable deploy leg as superseded (skipped) \u2014 the ledger is phases-green; rerun the train doctor to confirm`
23562
23566
  };
23563
23567
  });
23564
23568
  }
@@ -25929,6 +25933,14 @@ Nothing was written. Inspect ${ledger.path} (or clear it only after proving the
25929
25933
  }
25930
25934
  if (persisted?.phases.deploy.superseded === true) phaseInputs.deploy = persisted.phases.deploy;
25931
25935
  if (persisted?.phases.publish.superseded === true) phaseInputs.publish = persisted.phases.publish;
25936
+ for (const leg of ["deploy", "publish"]) {
25937
+ const kept = persisted?.phases[leg];
25938
+ if (kept?.state === "complete" && kept.runId != null && phaseInputs[leg].state === "pending") phaseInputs[leg] = kept;
25939
+ }
25940
+ if (!companion && phaseInputs.deploy.state === "complete" && (phaseInputs.publish.state === "complete" || phaseInputs.publish.state === "skipped")) {
25941
+ dispatch2.deployStatus = "success";
25942
+ dispatch2.note = `historical ${deployModel} deploy/publish status: success (re-verified by the recorded run)`;
25943
+ }
25932
25944
  if (persisted) {
25933
25945
  if (persisted.phases.deploy.superseded !== true) {
25934
25946
  await recordPhase(ledger, deps, anchors, "deploy", phaseEntry(phaseInputs.deploy), { strict: true, landed: "the verified GitHub Release and origin/main at the immutable tag SHA" });
@@ -34301,12 +34313,13 @@ var import_node_crypto11 = require("node:crypto");
34301
34313
  var SCHEMA = "mmi-suite-cut/v1";
34302
34314
  var SHA2 = /^[a-f0-9]{40}$/i;
34303
34315
  var RC_TAG = /^v\d+\.\d+\.\d+-rc\.\d+$/;
34316
+ var RELEASE_TAG = /^v\d+\.\d+\.\d+$/;
34304
34317
  function suitePins(record) {
34305
- return record.members.map((m) => ({
34318
+ return record.members.map((m) => m.carried ? { module: m.id, rc: m.rcPin, main: m.mainPin } : {
34306
34319
  module: m.id,
34307
34320
  rc: `https://${m.id === "katip" ? m.rcHost ?? m.host : m.host}/${m.id === "katip" ? "" : "rc/"}module.js?v=${m.tag}`,
34308
34321
  main: `https://${m.host}/module.js?v=${suiteReleaseTag(m.tag)}`
34309
- }));
34322
+ });
34310
34323
  }
34311
34324
  function suiteReleaseTag(rc) {
34312
34325
  if (!RC_TAG.test(rc)) throw new Error(`invalid rc tag ${rc}`);
@@ -34326,7 +34339,7 @@ function readSuiteCut(path2) {
34326
34339
  } catch {
34327
34340
  throw new Error(`suite cut record unreadable at ${path2}; repair it, never recut`);
34328
34341
  }
34329
- 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.pinMerged !== void 0 && (record.pinMerged !== true && record.pinMerged !== false || !record.pinPr) || 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"].includes(record.announcement) || !record.leadReleased) || 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.backendAfterRelease !== void 0 && (!m.released || typeof m.backendAfterRelease !== "string")))
34342
+ 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.pinMerged !== void 0 && (record.pinMerged !== true && record.pinMerged !== false || !record.pinPr) || 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"].includes(record.announcement) || !record.leadReleased) || 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.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)))
34330
34343
  throw new Error(`suite cut record has unknown or invalid state at ${path2}; repair it, never recut`);
34331
34344
  return record;
34332
34345
  }
@@ -34340,6 +34353,27 @@ function writeSuiteCut(path2, record) {
34340
34353
  throw new Error(`suite cut record write failed at ${path2}: ${e.message}; stop, verify the file before retrying`);
34341
34354
  }
34342
34355
  }
34356
+ function isPin(url) {
34357
+ return typeof url === "string" && url.startsWith("https://");
34358
+ }
34359
+ function pinVersion(url) {
34360
+ try {
34361
+ return url ? new URL(url).searchParams.get("v") ?? "" : "";
34362
+ } catch {
34363
+ return "";
34364
+ }
34365
+ }
34366
+ async function carriedPins(deps, member, bundles) {
34367
+ const main = bundles.main;
34368
+ if (!main || !RELEASE_TAG.test(pinVersion(main))) throw new Error(`${member.repo}: nothing new since rc, but its MM-Work main pin ${main ?? "(none)"} has no ?v=vX.Y.Z release; pin it before the suite cut`);
34369
+ for (const url of [bundles.rc, main]) {
34370
+ const tag = pinVersion(url);
34371
+ if (!RC_TAG.test(tag) && !RELEASE_TAG.test(tag)) continue;
34372
+ const sha = await deps.tagSha(member.repo, tag);
34373
+ if (sha && await deps.contains(member.repo, member.development, sha)) return { rcPin: url, mainPin: main };
34374
+ }
34375
+ throw new Error(`${member.repo}: nothing new since rc, but no pinned MM-Work bundle carries development ${member.development.slice(0, 12)}; pin it before the suite cut`);
34376
+ }
34343
34377
  async function suiteRcand(deps, opts) {
34344
34378
  if (!opts.ownerGo) throw new Error("suite rcand requires --owner-go (rc uses live data); nothing was written");
34345
34379
  const members = suiteMembers(await deps.registry());
@@ -34352,8 +34386,6 @@ async function suiteRcand(deps, opts) {
34352
34386
  if (opts.dryRun) return { plan };
34353
34387
  let record = readSuiteCut(deps.recordPath);
34354
34388
  if (record?.announcement === "sent" && record.leadReleased) {
34355
- const heads = await Promise.all(modules.map(async (m) => [await deps.head(m.repo, "development"), await deps.head(m.repo, "rc")]));
34356
- if (heads.some(([dev, rc]) => dev === rc)) throw new Error("a member has no new development commits since rc; no new suite cut started");
34357
34389
  const archive = `${deps.recordPath}.${record.leadRcTag}.json`;
34358
34390
  if ((0, import_node_fs29.existsSync)(archive)) throw new Error(`suite cut archive already exists at ${archive}; inspect before a new cut`);
34359
34391
  (0, import_node_fs29.renameSync)(deps.recordPath, archive);
@@ -34378,9 +34410,22 @@ async function suiteRcand(deps, opts) {
34378
34410
  if (!record) throw new Error("suite cut record unavailable after initialization");
34379
34411
  if (record.members.length !== modules.length || modules.some((m) => !record.members.some((r) => r.repo === m.repo && r.id === m.id)))
34380
34412
  throw new Error("suite roster changed since cut began; refuse to omit or add a member");
34413
+ const quiet = [];
34414
+ for (const member of record.members) {
34415
+ if (!member.tag && !member.carried && await deps.contains(member.repo, member.development, member.previousRc)) quiet.push(member);
34416
+ }
34417
+ if (record.members.every((m) => m.carried || quiet.includes(m))) {
34418
+ (0, import_node_fs29.rmSync)(deps.recordPath, { force: true });
34419
+ throw new Error("nothing to promote: no MM-Work module has new development commits since its rc; no suite cut started");
34420
+ }
34421
+ for (const member of quiet) {
34422
+ Object.assign(member, { carried: true }, await carriedPins(deps, member, modules.find((m) => m.repo === member.repo).bundles));
34423
+ writeSuiteCut(deps.recordPath, record);
34424
+ }
34381
34425
  for (const member of record.members) {
34382
34426
  if ((await deps.head(member.repo, "development"))?.toLowerCase() !== member.development.toLowerCase())
34383
34427
  throw new Error(`${member.repo}: development moved after the cut snapshot; refuse to add untested code`);
34428
+ if (member.carried) continue;
34384
34429
  if (!member.tag) {
34385
34430
  const cut = await deps.rcand(member.repo, member.previousRc, member.rcWasAbsent);
34386
34431
  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`);
@@ -34431,6 +34476,11 @@ async function suiteRelease(deps, ownerGo) {
34431
34476
  if (members.length !== record.members.length || members.some((m) => !record.members.some((r) => r.repo === m.repo && r.id === m.id))) throw new Error("suite roster changed after the cut; release refused");
34432
34477
  await deps.verifyPins(suitePins(record));
34433
34478
  for (const member of record.members) {
34479
+ if (member.carried) {
34480
+ const live2 = await deps.backend({ repo: member.repo, id: member.id, bundles: {} });
34481
+ if (live2.version !== member.backend) throw new Error(`${member.repo}: live backend changed after the cut (${member.backend} \u2192 ${live2.version}); release refused`);
34482
+ continue;
34483
+ }
34434
34484
  const [sha, live, rcHead] = await Promise.all([deps.tagSha(member.repo, member.tag), deps.backend({ repo: member.repo, id: member.id, bundles: {} }), deps.head(member.repo, "rc")]);
34435
34485
  if (!sha || sha.toLowerCase() !== member.sha?.toLowerCase() || !member.released && rcHead?.toLowerCase() !== member.sha?.toLowerCase()) throw new Error(`${member.repo}: rc tag/branch ${member.tag} changed after the cut; release refused`);
34436
34486
  const katipReleaseSha = member.id === "katip" ? await deps.tagSha(member.repo, suiteReleaseTag(member.tag)) : void 0;
@@ -34440,7 +34490,7 @@ async function suiteRelease(deps, ownerGo) {
34440
34490
  }
34441
34491
  if ((await deps.tagSha(SUITE_REPO, record.leadRcTag))?.toLowerCase() !== record.leadRcSha?.toLowerCase()) throw new Error("MM-Work rc tag changed after the cut; release refused");
34442
34492
  for (const member of record.members) {
34443
- if (member.released) continue;
34493
+ if (member.released || member.carried) continue;
34444
34494
  await deps.release(member.repo, suiteReleaseTag(member.tag), member.sha);
34445
34495
  if (member.id === "katip") {
34446
34496
  const expected = await deps.tagSha(member.repo, suiteReleaseTag(member.tag));
@@ -37585,8 +37635,11 @@ function readRepoVersion() {
37585
37635
  }
37586
37636
  function registerTrainCommands(program3, trainDeps) {
37587
37637
  const train = program3.command("train").description("release-train observability \u2014 track position, version lag, and the shared preflight-and-heal doctor (#2688, #6067)");
37588
- train.command("doctor").description("one shared preflight-and-heal verdict for the release, rcand and hotfix lanes; read-only unless --heal (#6067)").addOption(new Option("--lane <lane>", "train lane to check (defaults from the release track: direct \u2192 release)").choices([...TRAIN_LANES])).option("--dev", "judge the `release --dev` lane (development -> main, skipping rc): the lane starts from development and is read on development's workflows (#6318)").option("--heal", "repair safe local reconstructible state (scratch gitignore, churn, stray local tags, ff-only branch lag, finished scratch branches, stale alignment ledger leg); it never clears a ledger-pending, ledger-failed or branch-mismatch blocker (#6404, #6401, #6399) \u2014 those need `mmi-cli devops release --resume` (or, with fresh approval, `--abort --apply`) and `git checkout <start branch>`, so exit 1 after --heal is the gate working, not a failed heal").option("--repo <owner/repo>", "target repo (defaults to the current checkout)").option("--json", "machine-readable output").option("--out <path>", "write the verdict to this file as UTF-8 (no BOM) instead of stdout \u2014 the shell-free receipt path (#5802/#6563); pair with --json for a machine-readable file (e.g. `--json --out .jerv/tmp/train-doctor.json`); the file is written even when the verdict is NOT READY").addHelpText("after", "\nExit codes:\n 0 ready \u2014 no blocker and origin verified\n 1 a blocker remains, or origin could not be verified (never green on an unread origin)\n\nEvery finding carries code, severity (blocker|warning|healed|info), source (local|origin), remedy and a\ndocs/Guides/train-troubleshooting.md#<code> anchor. `release --apply` and `rcand --apply` run this at step 0 with --heal.\n").action(async (o) => {
37638
+ train.command("doctor").description("one shared preflight-and-heal verdict for the release, rcand and hotfix lanes; read-only unless --heal (#6067)").addOption(new Option("--lane <lane>", "train lane to check (defaults from the release track: direct \u2192 release)").choices([...TRAIN_LANES])).option("--dev", "judge the `release --dev` lane (development -> main, skipping rc): the lane starts from development and is read on development's workflows (#6318)").option("--heal", "repair safe local reconstructible state (scratch gitignore, churn, stray local tags, ff-only branch lag, finished scratch branches, stale alignment ledger leg); it never clears a ledger-pending, ledger-failed or branch-mismatch blocker (#6404, #6401, #6399) \u2014 those need `mmi-cli devops release --resume` (or, with fresh approval, `--abort --apply`) and `git checkout <start branch>`, so exit 1 after --heal is the gate working, not a failed heal").option("--repo <owner/repo>", "target repo (defaults to the current checkout)").option("--suite-solo", "judge an owner-approved solo release of one suite module, as `release --suite-solo --owner-go` does (requires --owner-go; never on rcand, #7327)").option("--owner-go", "explicit owner approval for the suite-solo lane (requires --suite-solo)").option("--json", "machine-readable output").option("--out <path>", "write the verdict to this file as UTF-8 (no BOM) instead of stdout \u2014 the shell-free receipt path (#5802/#6563); pair with --json for a machine-readable file (e.g. `--json --out .jerv/tmp/train-doctor.json`); the file is written even when the verdict is NOT READY").addHelpText("after", "\nExit codes:\n 0 ready \u2014 no blocker and origin verified\n 1 a blocker remains, or origin could not be verified (never green on an unread origin)\n\nEvery finding carries code, severity (blocker|warning|healed|info), source (local|origin), remedy and a\ndocs/Guides/train-troubleshooting.md#<code> anchor. `release --apply` and `rcand --apply` run this at step 0 with --heal.\n").action(async (o) => {
37589
37639
  try {
37640
+ if (o.suiteSolo !== o.ownerGo || o.lane === "rcand" && o.suiteSolo) {
37641
+ return failGraceful("train doctor: a suite-solo verdict needs BOTH --suite-solo --owner-go, and never on the rcand lane");
37642
+ }
37590
37643
  if (o.dev && o.lane && o.lane !== "release") {
37591
37644
  return failGraceful(`train doctor: --dev applies only to the release lane \u2014 it names the development -> main variant that skips rc, which the ${o.lane} lane does not have`);
37592
37645
  }
@@ -37607,7 +37660,7 @@ function registerTrainCommands(program3, trainDeps) {
37607
37660
  }
37608
37661
  }
37609
37662
  };
37610
- const verdict = await runTrainDoctor({ lane: o.lane, dev: o.dev === true, heal: o.heal === true, train: deps, repo: o.repo, cwd: root, argv: INVOKED_ARGV });
37663
+ const verdict = await runTrainDoctor({ lane: o.lane, dev: o.dev === true, heal: o.heal === true, train: deps, repo: o.repo, cwd: root, argv: INVOKED_ARGV, suiteSolo: o.suiteSolo === true });
37611
37664
  if (verdict.reexecCode !== void 0) return process.exit(verdict.reexecCode);
37612
37665
  const output = o.json ? JSON.stringify(verdict, null, 2) : formatTrainDoctor(verdict);
37613
37666
  if (!o.out) console.log(output);
@@ -44456,7 +44509,7 @@ function registerSuiteCommands(program3) {
44456
44509
  console.log(o.json ? JSON.stringify(result, null, 2) : [
44457
44510
  `suite rcand ${o.dryRun ? "dry-run" : "receipt"}:`,
44458
44511
  ...result.plan.map((line) => ` - ${line}`),
44459
- ...result.record?.members.map((m) => ` - ${m.repo}: ${m.tag} (${m.sha?.slice(0, 12)}) ${m.published ? "served" : "pending"}`) ?? [],
44512
+ ...result.record?.members.map((m) => m.carried ? ` - ${m.repo}: carried (nothing new) at ${m.mainPin}` : ` - ${m.repo}: ${m.tag} (${m.sha?.slice(0, 12)}) ${m.published ? "served" : "pending"}`) ?? [],
44460
44513
  ...result.record ? [` - pin PR ${result.record.pinPr}: merged; MM-Work ${result.record.leadRcTag}`] : []
44461
44514
  ].join("\n"));
44462
44515
  } catch (e) {
@@ -44479,7 +44532,7 @@ function registerSuiteCommands(program3) {
44479
44532
  }
44480
44533
  console.log(o.json ? JSON.stringify(result, null, 2) : [
44481
44534
  `suite release: ${result.leadRcTag} \u2192 main`,
44482
- ...result.members.map((m) => ` - ${m.repo}: ${m.tag} \u2192 main ${m.released ? "released" : "pending"}`),
44535
+ ...result.members.map((m) => m.carried ? ` - ${m.repo}: carried, untouched` : ` - ${m.repo}: ${m.tag} \u2192 main ${m.released ? "released" : "pending"}`),
44483
44536
  ` - MM-Work: ${result.leadReleased ? "released" : "pending"}; announcement: ${result.announcement}`
44484
44537
  ].join("\n"));
44485
44538
  } catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.5.33",
3
+ "version": "4.5.35",
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",