@mutmutco/cli 4.4.9 → 4.4.11

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 +122 -163
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -15938,10 +15938,10 @@ var rollout_plan_default = {
15938
15938
  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)."
15939
15939
  },
15940
15940
  baseline: {
15941
- version: "4.4.9",
15942
- tag: "v4.4.9",
15943
- commit: "da79ce082e9b",
15944
- npm: "@mutmutco/cli@4.4.9"
15941
+ version: "4.4.11",
15942
+ tag: "v4.4.11",
15943
+ commit: "47113a5df32d",
15944
+ npm: "@mutmutco/cli@4.4.11"
15945
15945
  },
15946
15946
  exitCriterion: "fleet-n-of-n",
15947
15947
  hubOnlyShortcut: "forbidden",
@@ -15958,14 +15958,14 @@ var rollout_plan_default = {
15958
15958
  repo: "mutmutco/mmi-hub",
15959
15959
  role: "canary",
15960
15960
  schedule: "train",
15961
- v3Target: "v4.4.9"
15961
+ v3Target: "v4.4.11"
15962
15962
  }
15963
15963
  ],
15964
15964
  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.",
15965
15965
  rollback: {
15966
15966
  independent: true,
15967
- mechanism: "npm dist-tag latest -> 4.4.9 and redeploy the Hub Lambda from tag v4.4.9 (da79ce082e9b); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15968
- v3Target: "v4.4.9 (@mutmutco/cli@4.4.9, tag commit da79ce082e9b \u2014 last known-good release carrying the repo-index v4-only contract)"
15967
+ mechanism: "npm dist-tag latest -> 4.4.11 and redeploy the Hub Lambda from tag v4.4.11 (47113a5df32d); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15968
+ v3Target: "v4.4.11 (@mutmutco/cli@4.4.11, tag commit 47113a5df32d \u2014 last known-good release carrying the repo-index v4-only contract)"
15969
15969
  }
15970
15970
  },
15971
15971
  {
@@ -18235,7 +18235,7 @@ async function correlateRun(deps, args) {
18235
18235
  });
18236
18236
  const tokens2 = args.mode === "dispatch" ? args.titleIncludes : void 0;
18237
18237
  const titleMatch = tokens2 && tokens2.length ? candidates.find((c) => tokens2.every((t) => (c.row.displayTitle ?? "").includes(t))) : void 0;
18238
- const match = titleMatch ?? (windowProvenWhole ? candidates[0] : void 0);
18238
+ const match = titleMatch ?? (args.mode === "workflow" || windowProvenWhole ? candidates[0] : void 0);
18239
18239
  if (match) return { runId: match.row.databaseId, runUrl: match.row.url, read: "found" };
18240
18240
  if (!windowProvenWhole) truncatedWindow = true;
18241
18241
  }
@@ -18256,9 +18256,6 @@ async function correlateRun(deps, args) {
18256
18256
  function correlateTenantRun(deps, since, titleIncludes) {
18257
18257
  return correlateRun(deps, { workflow: "tenant-deploy.yml", since, mode: "dispatch", titleIncludes });
18258
18258
  }
18259
- function correlatePublishRun(deps, since, titleIncludes) {
18260
- return correlateRun(deps, { workflow: "tenant-publish.yml", since, mode: "dispatch", titleIncludes });
18261
- }
18262
18259
  function correlateGetPublishRun(deps, since, product) {
18263
18260
  return correlateRun(deps, { workflow: "get-publish.yml", since, mode: "dispatch", titleIncludes: [product] });
18264
18261
  }
@@ -18877,134 +18874,19 @@ async function dispatchTenantDeployWithRetry(deps, input) {
18877
18874
  }
18878
18875
  }
18879
18876
  }
18880
- function tenantPublishRecoveryCommand(slug, repo, ref, stage, publishDir) {
18881
- const parts = [
18882
- `gh workflow run tenant-publish.yml --repo ${HUB_REPO2}`,
18883
- `-f slug=${slug}`,
18884
- `-f repo=${repo}`,
18885
- `-f ref=${ref}`,
18886
- `-f stage=${stage}`
18887
- ];
18888
- if (publishDir && publishDir !== ".") parts.push(`-f publishDir=${publishDir}`);
18889
- return parts.join(" ");
18890
- }
18891
- var PUBLISH_CONFLICT_SIGNAL = /E409|cannot publish over|previously published|\b(?:HTTP|status|code)\s*[:=]?\s*409\b|\b409\s+Conflict\b/i;
18892
- var PUBLISH_LOG_RETRY_ATTEMPTS = 4;
18893
- var PUBLISH_LOG_RETRY_DELAY_MS = 6e3;
18894
- function publishRegistryTarget(meta, repo, publishRef) {
18895
- const version = publishRef.replace(/^v/, "");
18896
- if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version)) return void 0;
18897
- try {
18898
- return { name: resolveReleaseAbortNpmPackage(meta, repo), version };
18899
- } catch {
18900
- return void 0;
18901
- }
18902
- }
18903
- async function provePublishedVersion(deps, target) {
18904
- try {
18905
- return clean2(await deps.run("npm", ["view", `${target.name}@${target.version}`, "version"])) === target.version;
18906
- } catch {
18907
- return false;
18908
- }
18909
- }
18910
- function publishReconcileFailureNote(input) {
18911
- if (input.signal && input.target) {
18912
- return `run FAILED and its log shows a publish conflict (${input.signal}) but the registry does not report ${input.target.name}@${input.target.version} live \u2014 the failure stands (verify with an authenticated registry read, never a bare npm view \u2014 see docs/Guides/train-troubleshooting.md#publish-private-package)`;
18913
- }
18914
- if (input.signal) {
18915
- return `run FAILED and its log shows a publish conflict (${input.signal}) but no npm package name could be resolved to prove the exact version live \u2014 the failure stands`;
18916
- }
18917
- if (input.readError) {
18918
- return `run FAILED and its log could not be read (${input.readError}) \u2014 the failure stands`;
18919
- }
18920
- return 'run FAILED and its log shows no publish-conflict signal (E409, "cannot publish over", "previously published", or a 409 status) \u2014 the failure stands';
18921
- }
18922
- async function reconcilePublishFailure(deps, runId, target) {
18923
- if (runId == null) {
18924
- return { status: "failure", note: "the failed publish run had no run id to re-read \u2014 the failure stands" };
18925
- }
18926
- const sleep2 = resolveSleep(deps);
18927
- let signal;
18928
- let readError;
18929
- for (let attempt = 0; attempt < PUBLISH_LOG_RETRY_ATTEMPTS; attempt++) {
18930
- if (attempt > 0) await sleep2(PUBLISH_LOG_RETRY_DELAY_MS);
18931
- const log = await fetchControlRunLog(deps, runId);
18932
- if (!log.ok) {
18933
- readError = log.error;
18934
- continue;
18935
- }
18936
- const match = log.log.match(PUBLISH_CONFLICT_SIGNAL);
18937
- if (!match) continue;
18938
- signal = match[0];
18939
- if (target && await provePublishedVersion(deps, target)) {
18940
- return {
18941
- status: "success",
18942
- note: `registry reports ${target.name}@${target.version} live; run failed with a publish conflict (${signal}) \u2014 #2428/#6777`
18943
- };
18944
- }
18945
- }
18946
- return { status: "failure", note: publishReconcileFailureNote({ signal, readError, target }) };
18947
- }
18948
- async function dispatchTenantPublish(deps, ctx, stage, ref, watch, dispatchFailure = "throw", publishDir, publishTarget) {
18949
- deps.warn?.("Release: Starting package publishing");
18950
- const since = (deps.now ?? Date.now)();
18951
- const dispatchArgs = [
18952
- "workflow",
18953
- "run",
18954
- "tenant-publish.yml",
18955
- "--repo",
18956
- HUB_REPO2,
18957
- "-f",
18958
- `slug=${ctx.slug}`,
18959
- "-f",
18960
- `repo=${ctx.repo}`,
18961
- "-f",
18962
- `ref=${ref}`,
18963
- "-f",
18964
- `stage=${stage}`
18965
- ];
18966
- if (publishDir && publishDir !== ".") dispatchArgs.push("-f", `publishDir=${publishDir}`);
18967
- try {
18968
- await deps.run("gh", dispatchArgs);
18969
- } catch (e) {
18970
- if (dispatchFailure === "throw") throw e;
18971
- const msg = e instanceof Error ? e.message : String(e);
18972
- const recovery = tenantPublishRecoveryCommand(ctx.slug, ctx.repo, ref, stage, publishDir);
18973
- return {
18974
- note: `tenant-publish dispatch FAILED: ${msg}. The promotion itself landed \u2014 recover with \`${recovery}\``,
18975
- deployStatus: "failure"
18976
- };
18977
- }
18978
- const correlated = await correlatePublishRun(deps, since, [ctx.slug, stage]);
18979
- if (correlated.read === "failed") {
18980
- return {
18981
- note: `tenant-publish dispatched (slug=${ctx.slug}, ref=${ref}, stage=${stage}) but its run could not be READ: ${correlated.error}. The promotion itself landed \u2014 check the Actions tab, and if the publish never ran, recover with \`${tenantPublishRecoveryCommand(ctx.slug, ctx.repo, ref, stage, publishDir)}\``,
18982
- deployStatus: "failure"
18983
- };
18984
- }
18985
- const { runId, runUrl } = correlated;
18986
- let deployStatus = watch ? await watchTenantRun(deps, runId) : "pending";
18987
- let note = `dispatched tenant-publish.yml (slug=${ctx.slug}, ref=${ref}, stage=${stage})`;
18988
- if (deployStatus === "failure") {
18989
- const reconciled = await reconcilePublishFailure(deps, runId, publishTarget);
18990
- deployStatus = reconciled.status;
18991
- note = `${note}; ${reconciled.note}`;
18992
- }
18993
- return { note, runId, runUrl, deployStatus };
18994
- }
18995
- async function dispatchPublishIfRequired(deps, ctx, meta, model, stage, publishRef, watch, dispatchFailure) {
18877
+ async function observePublishIfRequired(deps, ctx, meta, model, stage, releaseSha, watch) {
18996
18878
  if (!meta.publishRequired || stage !== "main") return null;
18997
18879
  if (model !== "tenant-container" && model !== "solo-container") return null;
18998
- return dispatchTenantPublish(
18999
- deps,
19000
- ctx,
19001
- stage,
19002
- publishRef,
19003
- watch,
19004
- dispatchFailure,
19005
- meta.publishDir,
19006
- publishRegistryTarget(meta, ctx.repo, publishRef)
19007
- );
18880
+ const target = { workflow: "publish.yml", event: "release" };
18881
+ const workflowRuns = watch ? await watchOwnWorkflowRuns(deps, ctx.repo, [target], 0, releaseSha) : [await listTargetRun(deps, ctx.repo, target, releaseSha)];
18882
+ const primary = workflowRuns[0];
18883
+ return {
18884
+ note: `observed ${ctx.repo} release-triggered publish.yml for ${releaseSha}`,
18885
+ runId: primary?.runId,
18886
+ runUrl: primary?.runUrl,
18887
+ workflowRuns,
18888
+ deployStatus: aggregateWorkflowRuns(workflowRuns)
18889
+ };
19008
18890
  }
19009
18891
  function appendPublishDispatch(deploy, publish) {
19010
18892
  if (!publish) return deploy;
@@ -19012,7 +18894,7 @@ function appendPublishDispatch(deploy, publish) {
19012
18894
  note: `${deploy.note}; ${publish.note}`,
19013
18895
  runId: deploy.runId,
19014
18896
  runUrl: deploy.runUrl,
19015
- workflowRuns: [...deploy.workflowRuns ?? [], ...publish.workflowRuns ?? [{ workflow: "tenant-publish.yml", runId: publish.runId, runUrl: publish.runUrl, conclusion: publish.deployStatus }]],
18897
+ workflowRuns: [...deploy.workflowRuns ?? [], ...publish.workflowRuns ?? [{ workflow: "publish.yml", runId: publish.runId, runUrl: publish.runUrl, conclusion: publish.deployStatus }]],
19016
18898
  deployStatus: deploy.deployStatus === "failure" || publish.deployStatus === "failure" ? "failure" : deploy.deployStatus === "pending" || publish.deployStatus === "pending" ? "pending" : "success"
19017
18899
  };
19018
18900
  }
@@ -19020,6 +18902,11 @@ var HUB_SERVED_PRODUCT = "mmi";
19020
18902
  function getPublishDispatchCommand(repo, product = HUB_SERVED_PRODUCT) {
19021
18903
  return `gh workflow run get-publish.yml --repo ${repo} --ref main -f product=${product}`;
19022
18904
  }
18905
+ var HUB_GET_SLUG = "mmi-get";
18906
+ var HUB_GET_STAGE = "main";
18907
+ function mmiGetRedeployCommand(repo) {
18908
+ return `gh workflow run tenant-deploy.yml --repo ${repo} --ref main -f slug=${HUB_GET_SLUG} -f repo=${repo} -f ref=${HUB_GET_STAGE} -f stage=${HUB_GET_STAGE}`;
18909
+ }
19023
18910
  function readLocalLauncherVersion(root = process.cwd()) {
19024
18911
  try {
19025
18912
  const raw = (0, import_node_fs19.readFileSync)((0, import_node_path17.join)(root, "installer", "launcher", "package.json"), "utf8");
@@ -19051,6 +18938,69 @@ async function fetchServedChannelVersion(url, fetchImpl = fetch) {
19051
18938
  return { ok: false, error: e instanceof Error ? e.message : String(e) };
19052
18939
  }
19053
18940
  }
18941
+ async function appendMmiGetRedeploy(deps, input, dispatch) {
18942
+ if (!isHubControlRepo(input.repo)) return dispatch;
18943
+ const remedy = mmiGetRedeployCommand(input.repo);
18944
+ const withNote = (note, deployStatus) => ({
18945
+ ...dispatch,
18946
+ note: `${dispatch.note}; ${note}`,
18947
+ ...deployStatus ? { deployStatus } : {}
18948
+ });
18949
+ if (input.publishStatus !== "success") {
18950
+ return withNote(`mmi-get box NOT redeployed (publish did not succeed) \u2014 after the publish is green, run \`${remedy}\``);
18951
+ }
18952
+ const since = (deps.now ?? Date.now)();
18953
+ try {
18954
+ await deps.run("gh", [
18955
+ "workflow",
18956
+ "run",
18957
+ "tenant-deploy.yml",
18958
+ "--repo",
18959
+ input.repo,
18960
+ "--ref",
18961
+ "main",
18962
+ "-f",
18963
+ `slug=${HUB_GET_SLUG}`,
18964
+ "-f",
18965
+ `repo=${input.repo}`,
18966
+ "-f",
18967
+ `ref=${HUB_GET_STAGE}`,
18968
+ "-f",
18969
+ `stage=${HUB_GET_STAGE}`
18970
+ ]);
18971
+ } catch (e) {
18972
+ const msg = e instanceof Error ? e.message : String(e);
18973
+ return withNote(`tenant-deploy dispatch FAILED: ${msg}. The release itself landed, but the MMI-GET BOX WAS NOT REDEPLOYED \u2014 recover with \`${remedy}\``, "failure");
18974
+ }
18975
+ const correlated = await correlateTenantRun(deps, since, [HUB_GET_SLUG, HUB_GET_STAGE]);
18976
+ if (correlated.read === "failed" || correlated.read === "absent") {
18977
+ const why = correlated.read === "failed" ? `its run could not be READ: ${correlated.error}` : "no matching run appeared";
18978
+ return withNote(`tenant-deploy dispatched (slug=${HUB_GET_SLUG}, stage=${HUB_GET_STAGE}) but ${why}. Check the Actions tab; if it never ran, recover with \`${remedy}\``, "failure");
18979
+ }
18980
+ const { runId, runUrl } = correlated;
18981
+ const runRow = { workflow: "tenant-deploy.yml", ...runId ? { runId } : {}, ...runUrl ? { runUrl } : {} };
18982
+ if (!input.watch) {
18983
+ return {
18984
+ ...dispatch,
18985
+ note: `${dispatch.note}; dispatched tenant-deploy.yml (slug=${HUB_GET_SLUG}, ref=${HUB_GET_STAGE}, stage=${HUB_GET_STAGE}) \u2014 NOT watched, so the mmi-get box is not yet proven redeployed`,
18986
+ workflowRuns: [...dispatch.workflowRuns ?? [], { ...runRow, conclusion: "pending" }]
18987
+ };
18988
+ }
18989
+ const runStatus = await watchTenantRun(deps, runId, input.repo);
18990
+ if (runStatus !== "success") {
18991
+ return {
18992
+ ...dispatch,
18993
+ note: `${dispatch.note}; tenant-deploy.yml ${runStatus} \u2014 the MMI-GET BOX WAS NOT REDEPLOYED; recover with \`${remedy}\``,
18994
+ workflowRuns: [...dispatch.workflowRuns ?? [], { ...runRow, conclusion: runStatus }],
18995
+ deployStatus: "failure"
18996
+ };
18997
+ }
18998
+ return {
18999
+ ...dispatch,
19000
+ note: `${dispatch.note}; mmi-get box redeployed (tenant-deploy.yml slug=${HUB_GET_SLUG} stage=${HUB_GET_STAGE})`,
19001
+ workflowRuns: [...dispatch.workflowRuns ?? [], { ...runRow, conclusion: "success" }]
19002
+ };
19003
+ }
19054
19004
  async function appendServedChannelPublish(deps, input, dispatch) {
19055
19005
  if (!isHubControlRepo(input.repo)) return dispatch;
19056
19006
  const channelUrl = input.channelUrl ?? readServedChannelUrl();
@@ -19118,9 +19068,16 @@ async function appendServedChannelPublish(deps, input, dispatch) {
19118
19068
  deployStatus: "failure"
19119
19069
  };
19120
19070
  }
19071
+ if (!expected) {
19072
+ return {
19073
+ ...dispatch,
19074
+ note: `${dispatch.note}; get-publish.yml succeeded and ${channelUrl} reports ${probe.version}, but the local launcher version could not be read \u2014 nothing was compared, so the move is unverified`,
19075
+ workflowRuns: runs
19076
+ };
19077
+ }
19121
19078
  return {
19122
19079
  ...dispatch,
19123
- note: `${dispatch.note}; served channel moved \u2014 ${channelUrl} now reports ${probe.version}${expected ? "" : " (no local launcher version to compare against)"}`,
19080
+ note: `${dispatch.note}; served channel moved \u2014 ${channelUrl} now reports ${probe.version}`,
19124
19081
  workflowRuns: runs
19125
19082
  };
19126
19083
  }
@@ -21505,7 +21462,8 @@ function publishPhaseInput(model, dispatch, publishDispatch, publishRequired, op
21505
21462
  const sha = opts.releaseSha;
21506
21463
  const publishRow = publishDispatch ? {
21507
21464
  state: publishDispatch.deployStatus === "success" ? "complete" : publishDispatch.deployStatus === "failure" ? "failed" : "pending",
21508
- ...runMetadata({ runId: publishDispatch.runId, runUrl: publishDispatch.runUrl, workflow: "tenant-publish.yml", event: "workflow_dispatch" }, HUB_REPO2, "dispatch"),
21465
+ ...sha ? { sha } : {},
21466
+ ...runMetadata({ runId: publishDispatch.runId, runUrl: publishDispatch.runUrl, workflow: "publish.yml", event: "release" }, opts.repo ?? "", "sha"),
21509
21467
  note: publishDispatch.note
21510
21468
  } : void 0;
21511
21469
  switch (model) {
@@ -23645,29 +23603,28 @@ ${recovery.note}`), recoveryInput);
23645
23603
  await recordPhase(ledger, deps, ledgerAnchors, "deploy", deployLeg, {
23646
23604
  landed: "the immutable tag, the green required-check wall, the origin/main fast-forward, and the verified GitHub Release"
23647
23605
  });
23648
- const publishDispatch0 = deployDispatch.deployStatus === "success" ? await dispatchPublishIfRequired(deps, ctx, meta, deployModel, "main", tag, watch, "report") : null;
23606
+ const publishDispatch0 = await observePublishIfRequired(deps, ctx, meta, deployModel, "main", releaseSha, watch);
23649
23607
  const publishDispatch = publishDispatch0 ? await recoverFailedDispatchPhase(
23650
23608
  deps,
23651
23609
  ledger,
23652
23610
  ledgerAnchors,
23653
23611
  "publish",
23654
23612
  publishDispatch0,
23655
- { repo: releaseRunRepoFor(deployModel, ctx.repo), sha: releaseSha, watched: isCentralDispatchModel(deployModel), workflow: isCentralDispatchModel(deployModel) ? "tenant-publish.yml" : "publish.yml" }
23613
+ { repo: ctx.repo, sha: releaseSha, watched: false, workflow: "publish.yml" }
23656
23614
  ) : null;
23657
- let publishSkipNote;
23658
- if (!publishDispatch && deployDispatch.deployStatus !== "success" && meta.publishRequired && (deployModel === "tenant-container" || deployModel === "solo-container")) {
23659
- 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";
23660
- }
23661
23615
  const publishLeg = phaseEntry(publishPhaseInput(deployModel, deployDispatch, publishDispatch, meta.publishRequired === true, {
23662
23616
  releaseSha,
23663
- repo: ctx.repo,
23664
- ...publishSkipNote !== void 0 ? { skipPublishNote: publishSkipNote } : {}
23617
+ repo: ctx.repo
23665
23618
  }));
23666
23619
  await recordPhase(ledger, deps, ledgerAnchors, "publish", publishLeg, {
23667
23620
  landed: "the immutable tag, the green required-check wall, origin/main, the verified GitHub Release, and the dispatched deploy path"
23668
23621
  });
23669
23622
  let dispatch = appendPublishDispatch(deployDispatch, publishDispatch);
23670
- if (publishSkipNote) dispatch = { ...dispatch, note: `${dispatch.note}; tenant-publish.yml skipped (${publishSkipNote})` };
23623
+ dispatch = await appendMmiGetRedeploy(
23624
+ deps,
23625
+ { repo: ctx.repo, publishStatus: publishLeg.state === "complete" ? "success" : "failure", watch },
23626
+ dispatch
23627
+ );
23671
23628
  dispatch = await appendServedChannelPublish(
23672
23629
  deps,
23673
23630
  { repo: ctx.repo, publishStatus: publishLeg.state === "complete" ? "success" : "failure", watch },
@@ -23992,6 +23949,15 @@ Nothing was written. Inspect ${ledger.path} (or clear it only after proving the
23992
23949
  workflowRuns: historicalRows
23993
23950
  });
23994
23951
  const phaseInputs = phaseInputsFromRunRows(deployModel, dispatch2.workflowRuns ?? historicalRows, tagSha, { repo: ctx.repo });
23952
+ const companion = await observePublishIfRequired(deps, ctx, meta, deployModel, "main", tagSha, watch);
23953
+ if (companion) {
23954
+ if (persisted) phaseInputs.deploy = persisted.phases.deploy;
23955
+ phaseInputs.publish = publishPhaseInput(deployModel, dispatch2, companion, true, { releaseSha: tagSha, repo: ctx.repo });
23956
+ historicalRows.push(...companion.workflowRuns ?? []);
23957
+ historicalDeploy = phaseInputs.deploy.state === "failed" || companion.deployStatus === "failure" ? "failure" : phaseInputs.deploy.state === "complete" && companion.deployStatus === "success" ? "success" : "pending";
23958
+ dispatch2.deployStatus = historicalDeploy;
23959
+ dispatch2.note = `historical ${deployModel} deploy/publish status: ${historicalDeploy}; ${companion.note}`;
23960
+ }
23995
23961
  if (persisted) {
23996
23962
  if (persisted.phases.deploy.superseded !== true) {
23997
23963
  await recordPhase(ledger, deps, anchors, "deploy", phaseEntry(phaseInputs.deploy), { strict: true, landed: "the verified GitHub Release and origin/main at the immutable tag SHA" });
@@ -24003,7 +23969,7 @@ Nothing was written. Inspect ${ledger.path} (or clear it only after proving the
24003
23969
  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" });
24004
23970
  }
24005
23971
  if (isJervHubRepo(ctx.repo)) steps2.push(dispatch2.note);
24006
- const announceNote2 = persisted && !shippedBefore && persisted.phases.deploy.superseded !== true && prodReleaseTargets(deployModel).length > 0 ? await announceShippedRelease(
23972
+ const announceNote2 = persisted && !shippedBefore && persisted.phases.deploy.superseded !== true && (prodReleaseTargets(deployModel).length > 0 || companion !== null) ? await announceShippedRelease(
24007
23973
  deps,
24008
23974
  { repo: ctx.repo, tag, summaryFile: options.announceSummaryFile },
24009
23975
  deployModel,
@@ -24114,14 +24080,14 @@ ${recovery.note}`), recoveryInput);
24114
24080
  strict: ledgerMode === "strict",
24115
24081
  landed: "the immutable tag, the re-proven wall, the origin/main push, and the verified GitHub Release"
24116
24082
  });
24117
- const publishDispatch0 = deployDispatch.deployStatus === "success" ? await dispatchPublishIfRequired(deps, ctx, meta, deployModel, "main", tag, watch, "report") : null;
24083
+ const publishDispatch0 = await observePublishIfRequired(deps, ctx, meta, deployModel, "main", tagSha, watch);
24118
24084
  const publishDispatch = publishDispatch0 ? await recoverFailedDispatchPhase(
24119
24085
  deps,
24120
24086
  ledger,
24121
24087
  anchors,
24122
24088
  "publish",
24123
24089
  publishDispatch0,
24124
- { repo: releaseRunRepoFor(deployModel, ctx.repo), sha: tagSha, watched: isCentralDispatchModel(deployModel), workflow: isCentralDispatchModel(deployModel) ? "tenant-publish.yml" : "publish.yml" }
24090
+ { repo: ctx.repo, sha: tagSha, watched: false, workflow: "publish.yml" }
24125
24091
  ) : null;
24126
24092
  const publishLeg = phaseEntry(publishPhaseInput(deployModel, deployDispatch, publishDispatch, meta.publishRequired === true, { releaseSha: tagSha, repo: ctx.repo }));
24127
24093
  await recordPhase(ledger, deps, anchors, "publish", publishLeg, {
@@ -44481,27 +44447,20 @@ ${decision.note}`);
44481
44447
  deploy0,
44482
44448
  { repo: releaseRunRepoFor(deployModel, ctx.repo), sha: mergedSha, watched: true, workflow: "tenant-deploy.yml" }
44483
44449
  );
44484
- const publish0 = deploy.deployStatus === "success" ? await dispatchPublishIfRequired(deps, ctx, meta, deployModel, "main", tag, true, "report") : null;
44450
+ const publish0 = await observePublishIfRequired(deps, ctx, meta, deployModel, "main", mergedSha, true);
44485
44451
  const publish = publish0 ? await recoverFailedDispatchPhase(
44486
44452
  deps,
44487
44453
  ledger,
44488
44454
  anchors,
44489
44455
  "publish",
44490
44456
  publish0,
44491
- { repo: releaseRunRepoFor(deployModel, ctx.repo), sha: mergedSha, watched: true, workflow: "tenant-publish.yml" }
44457
+ { repo: ctx.repo, sha: mergedSha, watched: false, workflow: "publish.yml" }
44492
44458
  ) : null;
44493
44459
  tenantPublishDispatch = publish;
44494
- let dispatch = appendPublishDispatch(deploy, publish);
44495
- if (!publish && deploy.deployStatus !== "success" && meta.publishRequired) {
44496
- const reason = deploy.deployStatus === "failure" ? "box deploy failed \u2014 redeploy the box before publishing" : "box deploy not confirmed (run with --watch) \u2014 publish after the box deploy lands";
44497
- dispatch = {
44498
- ...dispatch,
44499
- note: `${dispatch.note}; tenant-publish.yml skipped (${reason})`
44500
- };
44501
- }
44460
+ const dispatch = appendPublishDispatch(deploy, publish);
44502
44461
  deployNote = dispatch.note;
44503
44462
  runs.push(hotfixWorkflowRunFromDispatch("tenant-deploy.yml", deploy));
44504
- if (publish) runs.push(hotfixWorkflowRunFromDispatch("tenant-publish.yml", publish));
44463
+ if (publish) runs.push(hotfixWorkflowRunFromDispatch("publish.yml", publish));
44505
44464
  } else {
44506
44465
  deployNote = `no hotfix deploy dispatch for deployModel=${deployModel} \u2014 prod deploy is repo-specific`;
44507
44466
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.4.9",
3
+ "version": "4.4.11",
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",