@mutmutco/cli 4.3.55 → 4.3.56

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 +71 -23
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -15877,10 +15877,10 @@ var rollout_plan_default = {
15877
15877
  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)."
15878
15878
  },
15879
15879
  baseline: {
15880
- version: "4.3.55",
15881
- tag: "v4.3.55",
15882
- commit: "7f7f602ac9bc",
15883
- npm: "@mutmutco/cli@4.3.55"
15880
+ version: "4.3.56",
15881
+ tag: "v4.3.56",
15882
+ commit: "497a375fabfc",
15883
+ npm: "@mutmutco/cli@4.3.56"
15884
15884
  },
15885
15885
  exitCriterion: "fleet-n-of-n",
15886
15886
  hubOnlyShortcut: "forbidden",
@@ -15897,14 +15897,14 @@ var rollout_plan_default = {
15897
15897
  repo: "mutmutco/mmi-hub",
15898
15898
  role: "canary",
15899
15899
  schedule: "train",
15900
- v3Target: "v4.3.55"
15900
+ v3Target: "v4.3.56"
15901
15901
  }
15902
15902
  ],
15903
15903
  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.",
15904
15904
  rollback: {
15905
15905
  independent: true,
15906
- mechanism: "npm dist-tag latest -> 4.3.55 and redeploy the Hub Lambda from tag v4.3.55 (7f7f602ac9bc); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15907
- v3Target: "v4.3.55 (@mutmutco/cli@4.3.55, tag commit 7f7f602ac9bc \u2014 last known-good release carrying the repo-index v4-only contract)"
15906
+ mechanism: "npm dist-tag latest -> 4.3.56 and redeploy the Hub Lambda from tag v4.3.56 (497a375fabfc); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15907
+ v3Target: "v4.3.56 (@mutmutco/cli@4.3.56, tag commit 497a375fabfc \u2014 last known-good release carrying the repo-index v4-only contract)"
15908
15908
  }
15909
15909
  },
15910
15910
  {
@@ -18916,6 +18916,21 @@ function prodReleaseTargets(model) {
18916
18916
  if (model === "registry-publish") return [{ workflow: "publish.yml", event: "release" }];
18917
18917
  return [];
18918
18918
  }
18919
+ function trainObservesShipment(model) {
18920
+ return isCentralDispatchModel(model) || prodReleaseTargets(model).length > 0;
18921
+ }
18922
+ function shipLegsGreen(deploy, publish) {
18923
+ const green = (s) => s === "complete" || s === "skipped";
18924
+ return green(deploy?.state) && green(publish?.state);
18925
+ }
18926
+ async function announceShippedRelease(deps, args, model, legs, deferred) {
18927
+ if (!deps.announce) return void 0;
18928
+ if (trainObservesShipment(model) && !shipLegsGreen(legs.deploy, legs.publish)) {
18929
+ const failed = legs.deploy.state === "failed" || legs.publish.state === "failed";
18930
+ return `not announced: deploy/publish ${failed ? "FAILED" : "not proven green"} \u2014 ${deferred}`;
18931
+ }
18932
+ return (await deps.announce(args)).note;
18933
+ }
18919
18934
  async function listTargetRun(deps, repo, target, headSha) {
18920
18935
  let rows;
18921
18936
  try {
@@ -22836,6 +22851,9 @@ async function recordRcandDeployLegs(ledger, deps, anchors, deployModel, d, opts
22836
22851
  await recordPhase(ledger, deps, anchors, "alignment", phaseEntry({ state: "skipped", sha, note: "rcand aligns no branch \u2014 alignment rides the release" }), { ...opts, landed });
22837
22852
  return dispatch;
22838
22853
  }
22854
+ function releaseAnnounceDeferral(model, summaryFile) {
22855
+ return prodReleaseTargets(model).length > 0 ? `\`mmi-cli devops release --resume --watch${summaryFile ? ` --announce-summary-file ${summaryFile}` : ""}\` announces it once they are green` : "a central deploy is proven only under --watch, so this release stays unannounced";
22856
+ }
22839
22857
  async function completeMainRelease(deps, ctx, meta, deployModel, watch, options, tag, releaseSha, startBranch, preFold, tagProbe, branchHints, ledger, alignEarly) {
22840
22858
  const resumeCommand = options.dev ? "mmi-cli devops release --dev --apply" : "mmi-cli devops release --apply";
22841
22859
  const ledgerAnchors = { repo: ctx.repo, tag, tagSha: releaseSha };
@@ -22940,13 +22958,12 @@ ${recovery.note}`), recoveryInput);
22940
22958
  throw partialTrainRecoveryError(e, recoveryInput);
22941
22959
  }
22942
22960
  await recordPhase(ledger, deps, ledgerAnchors, "promotion", phaseEntry({ state: "complete", sha: releaseSha, note: `immutable ${tag} pushed at ${releaseSha.slice(0, 12)}; origin/main fast-forwarded to it` }), { landed: "the immutable tag and the green required-check wall" });
22943
- deps.warn?.("Release: Creating the release announcement");
22961
+ deps.warn?.("Release: Creating the GitHub Release");
22962
+ const autoRunSince = (deps.now ?? Date.now)();
22944
22963
  const releaseUrl = clean2(await deps.run("gh", ["release", "create", tag, "--target", "main", "--generate-notes", "--latest", "--repo", ctx.repo])) || void 0;
22945
22964
  await verifyPublishedRelease(deps, ctx.repo, tag, "main", releaseSha);
22946
22965
  await recordPhase(ledger, deps, ledgerAnchors, "githubRelease", phaseEntry({ state: "complete", sha: releaseSha, runUrl: releaseUrl, note: `verified GitHub Release ${tag} against ${releaseSha.slice(0, 12)}` }), { landed: "the immutable tag, the green required-check wall, and the origin/main fast-forward" });
22947
- const announceNote = deps.announce ? (await deps.announce({ repo: ctx.repo, tag, summaryFile: options.announceSummaryFile })).note : void 0;
22948
22966
  let alignment = alignEarly ? await alignEarly() : void 0;
22949
- const autoRunSince = (deps.now ?? Date.now)();
22950
22967
  const deployDispatch0 = await dispatchDeploy(deps, ctx, "main", "main", deployModel, watch, autoRunSince, releaseSha, "report", meta.publishDir);
22951
22968
  const deployRunRepo = releaseRunRepoFor(deployModel, ctx.repo);
22952
22969
  const failedReleaseRun = deployDispatch0.workflowRuns?.find((run) => run.conclusion === "failure");
@@ -22965,7 +22982,8 @@ ${recovery.note}`), recoveryInput);
22965
22982
  }
22966
22983
  );
22967
22984
  const deployDispatch = await appendJervGatewayReleaseDeploy(deps, ctx.repo, tag, releaseSha, recoveredDeployDispatch);
22968
- await recordPhase(ledger, deps, ledgerAnchors, "deploy", phaseEntry(deployPhaseInput(deployModel, deployDispatch, { releaseSha })), {
22985
+ const deployLeg = phaseEntry(deployPhaseInput(deployModel, deployDispatch, { releaseSha }));
22986
+ await recordPhase(ledger, deps, ledgerAnchors, "deploy", deployLeg, {
22969
22987
  landed: "the immutable tag, the green required-check wall, the origin/main fast-forward, and the verified GitHub Release"
22970
22988
  });
22971
22989
  const publishDispatch0 = deployDispatch.deployStatus === "success" ? await dispatchPublishIfRequired(deps, ctx, meta, deployModel, "main", tag, watch, "report") : null;
@@ -22981,21 +22999,30 @@ ${recovery.note}`), recoveryInput);
22981
22999
  if (!publishDispatch && deployDispatch.deployStatus !== "success" && meta.publishRequired && (deployModel === "tenant-container" || deployModel === "solo-container")) {
22982
23000
  publishSkipNote = deployDispatch.deployStatus === "failure" ? "box deploy failed - redeploy the box before publishing" : "box deploy not confirmed (run with --watch) - publish after the box deploy lands";
22983
23001
  }
22984
- await recordPhase(ledger, deps, ledgerAnchors, "publish", phaseEntry(publishPhaseInput(deployModel, deployDispatch, publishDispatch, meta.publishRequired === true, {
23002
+ const publishLeg = phaseEntry(publishPhaseInput(deployModel, deployDispatch, publishDispatch, meta.publishRequired === true, {
22985
23003
  releaseSha,
22986
23004
  repo: ctx.repo,
22987
23005
  ...publishSkipNote !== void 0 ? { skipPublishNote: publishSkipNote } : {}
22988
- })), {
23006
+ }));
23007
+ await recordPhase(ledger, deps, ledgerAnchors, "publish", publishLeg, {
22989
23008
  landed: "the immutable tag, the green required-check wall, origin/main, the verified GitHub Release, and the dispatched deploy path"
22990
23009
  });
22991
23010
  let dispatch = appendPublishDispatch(deployDispatch, publishDispatch);
22992
23011
  if (publishSkipNote) dispatch = { ...dispatch, note: `${dispatch.note}; tenant-publish.yml skipped (${publishSkipNote})` };
23012
+ const announcing = announceShippedRelease(
23013
+ deps,
23014
+ { repo: ctx.repo, tag, summaryFile: options.announceSummaryFile },
23015
+ deployModel,
23016
+ { deploy: deployLeg, publish: publishLeg },
23017
+ releaseAnnounceDeferral(deployModel, options.announceSummaryFile)
23018
+ );
22993
23019
  if (watch && alignment) {
22994
23020
  alignment = {
22995
23021
  dev: await awaitAlignmentLanded(deps, ctx, alignment.dev, "development"),
22996
23022
  ...alignment.rc ? { rc: await awaitAlignmentLanded(deps, ctx, alignment.rc, "rc") } : {}
22997
23023
  };
22998
23024
  }
23025
+ const announceNote = await announcing;
22999
23026
  return { checks, releaseUrl, announceNote, dispatch, ledgerAnchors, ...alignment ? { alignment } : {} };
23000
23027
  }
23001
23028
  function resumeStateOf(status) {
@@ -23243,6 +23270,7 @@ async function runReleaseResumePipeline(deps, options, prepared) {
23243
23270
  Live origin/tag/Release proof stays authoritative; nothing was written. Inspect ${ledger.path} before retrying.`
23244
23271
  );
23245
23272
  }
23273
+ const shippedBefore = shipLegsGreen(persisted?.phases.deploy, persisted?.phases.publish);
23246
23274
  if (persisted?.lane && persisted.lane !== "release") {
23247
23275
  throw new Error(`release --resume refused: the ${tag} ledger belongs to the ${persisted.lane} lane \u2014 finish it with ${ledgerLaneResumeCommand(persisted)} (#6068). Nothing was written.`);
23248
23276
  }
@@ -23299,8 +23327,8 @@ Nothing was written. Inspect ${ledger.path} (or clear it only after proving the
23299
23327
  deployStatus: historicalDeploy,
23300
23328
  workflowRuns: historicalRows
23301
23329
  });
23330
+ const phaseInputs = phaseInputsFromRunRows(deployModel, dispatch2.workflowRuns ?? historicalRows, tagSha, { repo: ctx.repo });
23302
23331
  if (persisted) {
23303
- const phaseInputs = phaseInputsFromRunRows(deployModel, dispatch2.workflowRuns ?? historicalRows, tagSha, { repo: ctx.repo });
23304
23332
  if (persisted.phases.deploy.superseded !== true) {
23305
23333
  await recordPhase(ledger, deps, anchors, "deploy", phaseEntry(phaseInputs.deploy), { strict: true, landed: "the verified GitHub Release and origin/main at the immutable tag SHA" });
23306
23334
  }
@@ -23309,6 +23337,13 @@ Nothing was written. Inspect ${ledger.path} (or clear it only after proving the
23309
23337
  await recordPhase(ledger, deps, anchors, "promotion", phaseEntry({ state: "complete", sha: tagSha, note: "re-verified live: origin/main contains the immutable tag SHA" }), { strict: true, landed: "nothing beyond the already-public release" });
23310
23338
  }
23311
23339
  if (isJervHubRepo(ctx.repo)) steps2.push(dispatch2.note);
23340
+ const announceNote2 = persisted && !shippedBefore && persisted.phases.deploy.superseded !== true && prodReleaseTargets(deployModel).length > 0 ? await announceShippedRelease(
23341
+ deps,
23342
+ { repo: ctx.repo, tag, summaryFile: options.announceSummaryFile },
23343
+ deployModel,
23344
+ phaseInputs,
23345
+ releaseAnnounceDeferral(deployModel, options.announceSummaryFile)
23346
+ ) : void 0;
23312
23347
  if (persisted) {
23313
23348
  await recordPhase(ledger, deps, anchors, "alignment", phaseEntry(alignmentPhaseEntry(devRollForward2, rcAlignment2)), { strict: true, landed: "the released, verified, deploy-resolved release (alignment PRs reused/landed)" });
23314
23349
  }
@@ -23326,6 +23361,7 @@ Nothing was written. Inspect ${ledger.path} (or clear it only after proving the
23326
23361
  state: state2,
23327
23362
  resumed: true,
23328
23363
  steps: steps2,
23364
+ ...announceNote2 ? { announceNote: announceNote2 } : {},
23329
23365
  devRollForward: devRollForward2,
23330
23366
  rcAlignment: rcAlignment2,
23331
23367
  dispatch: dispatch2,
@@ -23382,16 +23418,15 @@ ${recovery.note}`), recoveryInput);
23382
23418
  await runGitPush(deps, ["push", "origin", `${tagSha}:refs/heads/main`]);
23383
23419
  steps.push(`pushed ${tagSha.slice(0, 12)} to origin/main`);
23384
23420
  await recordPhase(ledger, deps, anchors, "promotion", phaseEntry({ state: "complete", sha: tagSha, note: `resumed: ${tagSha.slice(0, 12)} pushed to origin/main (tag preserved, never re-cut)` }), { strict: ledgerMode === "strict", landed: "the immutable tag and the re-proven required-check wall" });
23385
- deps.warn?.("Release: Creating the release announcement");
23421
+ deps.warn?.("Release: Creating the GitHub Release");
23422
+ const autoRunSince = (deps.now ?? Date.now)();
23386
23423
  const releaseUrl = clean2(await deps.run("gh", ["release", "create", tag, "--target", "main", "--generate-notes", "--latest", "--repo", ctx.repo])) || void 0;
23387
23424
  steps.push(`created the GitHub Release for ${tag}`);
23388
23425
  await verifyPublishedRelease(deps, ctx.repo, tag, "main", tagSha);
23389
23426
  steps.push("verified the Release published against the tagged commit");
23390
23427
  await recordPhase(ledger, deps, anchors, "githubRelease", phaseEntry({ state: "complete", sha: tagSha, runUrl: releaseUrl, note: "verified the Release published against the tagged commit" }), { strict: ledgerMode === "strict", landed: "the immutable tag, the re-proven wall, and the origin/main push" });
23391
- const announceNote = deps.announce ? (await deps.announce({ repo: ctx.repo, tag, summaryFile: options.announceSummaryFile })).note : void 0;
23392
23428
  let devRollForward = await rollDevelopmentForward(deps, ctx, tag);
23393
23429
  let rcAlignment = !directTrack && branchHints.hasRcBranch ? await alignRcForward(deps, ctx, tag) : void 0;
23394
- const autoRunSince = (deps.now ?? Date.now)();
23395
23430
  const deployDispatch0 = await dispatchDeploy(deps, ctx, "main", "main", deployModel, watch, autoRunSince, tagSha, "report", meta.publishDir);
23396
23431
  const recoveredDeployDispatch = await recoverFailedDispatchPhase(
23397
23432
  deps,
@@ -23408,7 +23443,8 @@ ${recovery.note}`), recoveryInput);
23408
23443
  );
23409
23444
  const deployDispatch = await appendJervGatewayReleaseDeploy(deps, ctx.repo, tag, tagSha, recoveredDeployDispatch);
23410
23445
  steps.push(`dispatched the ${deployModel} deploy path`);
23411
- await recordPhase(ledger, deps, anchors, "deploy", phaseEntry(deployPhaseInput(deployModel, deployDispatch, { releaseSha: tagSha })), {
23446
+ const deployLeg = phaseEntry(deployPhaseInput(deployModel, deployDispatch, { releaseSha: tagSha }));
23447
+ await recordPhase(ledger, deps, anchors, "deploy", deployLeg, {
23412
23448
  strict: ledgerMode === "strict",
23413
23449
  landed: "the immutable tag, the re-proven wall, the origin/main push, and the verified GitHub Release"
23414
23450
  });
@@ -23421,15 +23457,24 @@ ${recovery.note}`), recoveryInput);
23421
23457
  publishDispatch0,
23422
23458
  { repo: releaseRunRepoFor(deployModel, ctx.repo), sha: tagSha, watched: isCentralDispatchModel(deployModel), workflow: isCentralDispatchModel(deployModel) ? "tenant-publish.yml" : "publish.yml" }
23423
23459
  ) : null;
23424
- await recordPhase(ledger, deps, anchors, "publish", phaseEntry(publishPhaseInput(deployModel, deployDispatch, publishDispatch, meta.publishRequired === true, { releaseSha: tagSha, repo: ctx.repo })), {
23460
+ const publishLeg = phaseEntry(publishPhaseInput(deployModel, deployDispatch, publishDispatch, meta.publishRequired === true, { releaseSha: tagSha, repo: ctx.repo }));
23461
+ await recordPhase(ledger, deps, anchors, "publish", publishLeg, {
23425
23462
  strict: ledgerMode === "strict",
23426
23463
  landed: "the immutable tag, the re-proven wall, origin/main, the verified GitHub Release, and the dispatched deploy path"
23427
23464
  });
23428
23465
  const dispatch = appendPublishDispatch(deployDispatch, publishDispatch);
23466
+ const announcing = announceShippedRelease(
23467
+ deps,
23468
+ { repo: ctx.repo, tag, summaryFile: options.announceSummaryFile },
23469
+ deployModel,
23470
+ { deploy: deployLeg, publish: publishLeg },
23471
+ releaseAnnounceDeferral(deployModel, options.announceSummaryFile)
23472
+ );
23429
23473
  if (watch) {
23430
23474
  devRollForward = await awaitAlignmentLanded(deps, ctx, devRollForward, "development");
23431
23475
  if (rcAlignment) rcAlignment = await awaitAlignmentLanded(deps, ctx, rcAlignment, "rc");
23432
23476
  }
23477
+ const announceNote = await announcing;
23433
23478
  steps.push(`development roll-forward: ${devRollForward.status}`);
23434
23479
  if (rcAlignment) steps.push(`rc alignment: ${rcAlignment.status}`);
23435
23480
  const followUp = deriveTrainFollowUpStatus({
@@ -43473,15 +43518,11 @@ ${decision.note}`);
43473
43518
  );
43474
43519
  await recordHotfixPhase(ledger, anchors, "promotion", { state: "complete", sha: mergedSha, note: `immutable ${tag} pushed at ${mergedSha.slice(0, 12)}; origin/main already carries it (merged hotfix PR #${pr.number})` }, "the immutable tag and the green required-check wall");
43475
43520
  let releaseNote;
43476
- let announceNote;
43477
43521
  if (releaseExists) {
43478
43522
  releaseNote = `Release ${tag} already exists \u2014 resumed without recreating`;
43479
43523
  } else {
43480
43524
  await deps.run("gh", ["release", "create", tag, "--repo", ctx.repo, "--target", "main", "--generate-notes", "--latest"]);
43481
43525
  releaseNote = `Release ${tag} created (target main)`;
43482
- if (deps.announce) {
43483
- announceNote = (await deps.announce({ repo: ctx.repo, tag, summaryFile: options.announceSummaryFile })).note;
43484
- }
43485
43526
  }
43486
43527
  await recordHotfixPhase(ledger, anchors, "githubRelease", { state: "complete", sha: mergedSha, note: releaseNote }, "the immutable tag, the green wall, and origin/main");
43487
43528
  const runs = [];
@@ -43554,6 +43595,13 @@ ${decision.note}`);
43554
43595
  );
43555
43596
  await recordHotfixPhase(ledger, anchors, "deploy", ledgerInputs.deploy, "the immutable tag, the green wall, origin/main, and the GitHub Release");
43556
43597
  await recordHotfixPhase(ledger, anchors, "publish", ledgerInputs.publish, "the immutable tag, the green wall, origin/main, the GitHub Release, and the deploy path");
43598
+ const announceNote = releaseExists ? void 0 : await announceShippedRelease(
43599
+ deps,
43600
+ { repo: ctx.repo, tag, summaryFile: options.announceSummaryFile },
43601
+ deployModel,
43602
+ ledgerInputs,
43603
+ "a hotfix is announced only by the run that proves it shipped, so this one stays unannounced"
43604
+ );
43557
43605
  let verifyNote;
43558
43606
  if (deployModel === "hub-serverless") {
43559
43607
  const previousRef = clean3(await deps.run("git", ["rev-parse", "--abbrev-ref", "HEAD"]));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.3.55",
3
+ "version": "4.3.56",
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",