@mutmutco/claude-plugin 4.5.135 → 4.5.137

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mmi",
3
3
  "displayName": "MMI",
4
- "version": "4.5.135",
4
+ "version": "4.5.137",
5
5
  "mmiCompat": "4.x",
6
6
  "description": "MMI workflow skills and org gates delivery.",
7
7
  "author": {
package/cli/dist/main.cjs CHANGED
@@ -14682,7 +14682,9 @@ async function claimOneBoardItem(ctx, selector, options) {
14682
14682
  const wouldWrite = flatItem.assignees.length === 0 && (flatItem.status === "Todo" || flatItem.status === "In Progress" || flatItem.status === "In Review");
14683
14683
  if (wouldWrite && !ctx.writable.has(flatItem.repository.toLowerCase())) {
14684
14684
  throw new NotClaimableError(
14685
- `${flatItem.ref} is not claimable: the token from ${describeTokenSource()} reports no write access to ${flatItem.repository}`,
14685
+ // #8245: name the real source. With a Hub snapshot the probe is the Hub's server-side check for the
14686
+ // session login, not the local gh token, so `gh repo view --json viewerPermission` can disagree.
14687
+ `${flatItem.ref} is not claimable: no write access to ${flatItem.repository} for @${report.viewer} (read from the Hub board snapshot's access probe when one was used, else the token from ${describeTokenSource()})`,
14686
14688
  { ref: flatItem.ref }
14687
14689
  );
14688
14690
  }
@@ -17509,6 +17511,7 @@ function assertGitBashOnWindowsForJervHubRelease(repo, platform2 = process.platf
17509
17511
 
17510
17512
  // src/train-apply-correlation.ts
17511
17513
  var import_node_fs19 = require("node:fs");
17514
+ init_github_quota();
17512
17515
  var import_node_path17 = require("node:path");
17513
17516
 
17514
17517
  // src/fleet-lockstep-enforce.ts
@@ -18274,10 +18277,10 @@ var rollout_plan_default = {
18274
18277
  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)."
18275
18278
  },
18276
18279
  baseline: {
18277
- version: "4.5.135",
18278
- tag: "v4.5.135",
18279
- commit: "2ae28f97dd36",
18280
- npm: "@mutmutco/cli@4.5.135"
18280
+ version: "4.5.137",
18281
+ tag: "v4.5.137",
18282
+ commit: "5d0403299d67",
18283
+ npm: "@mutmutco/cli@4.5.137"
18281
18284
  },
18282
18285
  exitCriterion: "fleet-n-of-n",
18283
18286
  hubOnlyShortcut: "forbidden",
@@ -18294,14 +18297,14 @@ var rollout_plan_default = {
18294
18297
  repo: "mutmutco/mmi-hub",
18295
18298
  role: "canary",
18296
18299
  schedule: "train",
18297
- v3Target: "v4.5.135"
18300
+ v3Target: "v4.5.137"
18298
18301
  }
18299
18302
  ],
18300
18303
  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.",
18301
18304
  rollback: {
18302
18305
  independent: true,
18303
- mechanism: "npm dist-tag latest -> 4.5.135 and redeploy the Hub Lambda from tag v4.5.135 (2ae28f97dd36); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
18304
- v3Target: "v4.5.135 (@mutmutco/cli@4.5.135, tag commit 2ae28f97dd36 \u2014 last known-good release carrying the repo-index v4-only contract)"
18306
+ mechanism: "npm dist-tag latest -> 4.5.137 and redeploy the Hub Lambda from tag v4.5.137 (5d0403299d67); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
18307
+ v3Target: "v4.5.137 (@mutmutco/cli@4.5.137, tag commit 5d0403299d67 \u2014 last known-good release carrying the repo-index v4-only contract)"
18305
18308
  }
18306
18309
  },
18307
18310
  {
@@ -20419,11 +20422,22 @@ async function loadReleaseTrackBranchHints(deps) {
20419
20422
  ]);
20420
20423
  return { hasDevelopmentBranch, hasMainBranch, hasRcBranch };
20421
20424
  }
20425
+ async function readTrainLogin(deps, waitMs = 18e4, attempts = 5) {
20426
+ for (let i = 1; ; i++) {
20427
+ try {
20428
+ return requireValue(clean2(await deps.run("gh", ["api", "user", "--jq", ".login"])), "GitHub login");
20429
+ } catch (e) {
20430
+ if (i >= attempts || !isRateLimitText(e instanceof Error ? e.message : String(e))) throw e;
20431
+ deps.warn?.(`GitHub login read rate-limited; retrying in ${waitMs / 6e4} min (${i}/${attempts - 1})`);
20432
+ await resolveSleep(deps)(waitMs);
20433
+ }
20434
+ }
20435
+ }
20422
20436
  async function buildTrainApplyContext(deps) {
20423
20437
  const repo = requireValue(clean2(await deps.run("gh", ["repo", "view", "--json", "nameWithOwner", "--jq", ".nameWithOwner"])), "repo");
20424
20438
  const [owner, name] = repo.split("/");
20425
20439
  if (!owner || !name) throw new Error(`repo must be owner/name, got ${repo}`);
20426
- const login = requireValue(clean2(await deps.run("gh", ["api", "user", "--jq", ".login"])), "GitHub login");
20440
+ const login = await readTrainLogin(deps);
20427
20441
  const verdict = await deps.trainAuthority(repo);
20428
20442
  if (!verdict.ok) throw new Error(`${commandAuthorityLabel(owner)}: train authority could not be verified (${verdict.error})`);
20429
20443
  if (!verdict.train) {
@@ -22443,7 +22457,7 @@ async function runTenantRedeploy(deps, options) {
22443
22457
  const repo = options.repo;
22444
22458
  const [owner, name] = repo.split("/");
22445
22459
  if (!owner || !name) throw new Error(`repo must be owner/name, got ${repo}`);
22446
- const login = requireValue(clean2(await deps.run("gh", ["api", "user", "--jq", ".login"])), "GitHub login");
22460
+ const login = await readTrainLogin(deps);
22447
22461
  const verdict = await deps.trainAuthority(repo);
22448
22462
  if (!verdict.ok) throw new Error(`${commandAuthorityLabel(owner)}: train authority could not be verified (${verdict.error})`);
22449
22463
  if (!verdict.train) throw new Error(`${commandAuthorityLabel(owner)}: @${login} is ${verdict.role} \u2014 no train authority on ${repo}`);
@@ -39845,6 +39859,7 @@ function boardAdvanceFailureMessage(result) {
39845
39859
  }
39846
39860
 
39847
39861
  // src/pr-land.ts
39862
+ var PR_LAND_QUEUE_TIMEOUT_MS = 45 * 6e4;
39848
39863
  function prHeadBehindBase(input) {
39849
39864
  if ((input.mergeStateStatus ?? "").toUpperCase() === "BEHIND") return true;
39850
39865
  return /out[- ]of[- ]date|not up to date|up to date with the base|update (?:the|your) branch/i.test(input.message ?? "");
@@ -39968,6 +39983,17 @@ async function runPrLand(prNumber, options, deps) {
39968
39983
  }
39969
39984
  const ciPolicy = await deps.resolveCiPolicy(repo);
39970
39985
  base.ciPolicy = ciPolicy;
39986
+ const hasQueue = await deps.hasMergeQueue?.(repo, resolved.base) ?? false;
39987
+ let queueSnapshot;
39988
+ if (hasQueue) {
39989
+ base.mergeQueue = { base: resolved.base, state: "queued" };
39990
+ if (!deps.readMergeQueue) throw new Error("merge queue reader unavailable");
39991
+ queueSnapshot = await deps.readMergeQueue(prNumber, repo);
39992
+ if (queueSnapshot.isInMergeQueue || queueSnapshot.state === "MERGED") {
39993
+ base.mergeStatus = "auto-merge-enqueued";
39994
+ return pollPrMergeQueue(prNumber, options, deps, base, queueSnapshot);
39995
+ }
39996
+ }
39971
39997
  const checksWaitError = (checksWait) => {
39972
39998
  if (checksWait.status === "conflicting") {
39973
39999
  return `checks-wait conflicting: ${checksWait.reason ?? "PR is conflicting with the base branch"}`;
@@ -40003,6 +40029,14 @@ async function runPrLand(prNumber, options, deps) {
40003
40029
  if (merge.mergeStatus === "failed") {
40004
40030
  return { ...base, error: merge.error ?? "merge failed" };
40005
40031
  }
40032
+ if (hasQueue) {
40033
+ if (merge.mergeStatus === "merged") {
40034
+ return queueMerged(base, deps);
40035
+ }
40036
+ const deadline = (deps.now ?? (() => Date.now()))() + PR_LAND_QUEUE_TIMEOUT_MS;
40037
+ await (deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms))))(PR_LAND_POLL_MS);
40038
+ return pollPrMergeQueue(prNumber, options, deps, base, void 0, deadline);
40039
+ }
40006
40040
  if (merge.mergeStatus === "merged") {
40007
40041
  return {
40008
40042
  ...base,
@@ -40025,6 +40059,53 @@ async function runPrLand(prNumber, options, deps) {
40025
40059
  error: "auto-merge enqueued but PR did not reach MERGED within timeout \u2014 poll manually with gh pr view"
40026
40060
  };
40027
40061
  }
40062
+ function queueMerged(base, deps) {
40063
+ const result = {
40064
+ ...base,
40065
+ status: "merged-via-queue",
40066
+ mergeStatus: "merged",
40067
+ mergeQueue: { ...base.mergeQueue, state: "merged" },
40068
+ branchFinality: prLandedBranchFinality(base.pr, base.base)
40069
+ };
40070
+ deps.queueProgress?.(result);
40071
+ return result;
40072
+ }
40073
+ async function pollPrMergeQueue(pr, options, deps, base, snapshot, deadlineMs) {
40074
+ const now = deps.now ?? (() => Date.now());
40075
+ const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
40076
+ const deadline = deadlineMs ?? now() + PR_LAND_QUEUE_TIMEOUT_MS;
40077
+ while (now() < deadline) {
40078
+ const read = snapshot ?? await deps.readMergeQueue(pr, base.repo);
40079
+ snapshot = void 0;
40080
+ if (read.state === "MERGED") return queueMerged(base, deps);
40081
+ if (read.isInMergeQueue) {
40082
+ base.mergeQueue = {
40083
+ base: base.base,
40084
+ state: "queued",
40085
+ ...read.mergeQueueEntry ? { position: read.mergeQueueEntry.position, entryState: read.mergeQueueEntry.state } : {}
40086
+ };
40087
+ deps.queueProgress?.(base);
40088
+ if (options.enqueueOnly) return { ...base, status: "queued" };
40089
+ } else if (read.state === "OPEN" && !read.autoMergeRequest) {
40090
+ const run = await deps.findMergeGroupRun(pr, base.repo, base.base);
40091
+ const result2 = {
40092
+ ...base,
40093
+ mergeQueue: { ...base.mergeQueue, state: "ejected", ...run ? { ejectedRun: run } : {} },
40094
+ error: `merge queue ejected PR #${pr} \u2014 ${run ? `merge_group run ${run.id} ${run.conclusion} ${run.url}` : "merge_group run not found"}`
40095
+ };
40096
+ deps.queueProgress?.(result2);
40097
+ return result2;
40098
+ }
40099
+ await sleep2(PR_LAND_POLL_MS);
40100
+ }
40101
+ const result = {
40102
+ ...base,
40103
+ mergeQueue: { ...base.mergeQueue, state: "timeout" },
40104
+ error: "merge queue did not reach MERGED within 45 minutes \u2014 rerun pr land to resume polling"
40105
+ };
40106
+ deps.queueProgress?.(result);
40107
+ return result;
40108
+ }
40028
40109
 
40029
40110
  // src/test-policy-core.ts
40030
40111
  var import_node_child_process13 = require("node:child_process");
@@ -41085,18 +41166,19 @@ async function ghMergeAutoEnqueue(prNumber, repo, method, extra) {
41085
41166
  bodyText: method === "--squash" ? extra?.bodyText : void 0
41086
41167
  });
41087
41168
  try {
41088
- return await mergeAutoEnqueueWithBody(prNumber, args, method, io, overrideBody?.path);
41169
+ return await mergeAutoEnqueueWithBody(prNumber, args, method, io, overrideBody?.path, extra?.mergeQueue);
41089
41170
  } finally {
41090
41171
  overrideBody?.cleanup();
41091
41172
  }
41092
41173
  }
41093
- async function mergeAutoEnqueueWithBody(prNumber, args, method, io, bodyFile) {
41174
+ async function mergeAutoEnqueueWithBody(prNumber, args, method, io, bodyFile, mergeQueue = false) {
41094
41175
  const headRef = (await io.gh(["pr", "view", prNumber, ...args, "--json", "headRefName", "--jq", ".headRefName"], GC_GH_TIMEOUT_MS).catch(() => "")).trim();
41095
41176
  const deleteBranch = !isProtectedBranch(headRef);
41096
41177
  const readMergeState = () => readGhPrStateWithRetry(
41097
41178
  () => io.gh(["pr", "view", prNumber, ...args, "--json", "state", "--jq", ".state"], GC_GH_TIMEOUT_MS)
41098
41179
  );
41099
41180
  const confirmEnqueueOutcome = async () => {
41181
+ if (mergeQueue) return { mergeStatus: "auto-merge-enqueued" };
41100
41182
  const stateRead = await readMergeState();
41101
41183
  if (!stateRead.ok) {
41102
41184
  return { mergeStatus: "failed", error: `could not read PR state after merge: ${stateRead.error}` };
@@ -41872,6 +41954,57 @@ function applyChecklistCheck(body, query, checked) {
41872
41954
  return { ok: true, edit: setChecklistMarker(body, sel.item, checked), item: sel.item };
41873
41955
  }
41874
41956
 
41957
+ // src/pr-merge-queue.ts
41958
+ init_cli_shared();
41959
+ var readApi = async (args) => {
41960
+ const env = await ghReadEnv();
41961
+ return (await execFileP("gh", ["api", ...args], {
41962
+ timeout: 2e4,
41963
+ ...env ? { env } : {}
41964
+ })).stdout;
41965
+ };
41966
+ async function queueGraphql(repo, fields, gh2) {
41967
+ const [owner, name] = repo.split("/");
41968
+ const response = JSON.parse(await gh2(["graphql", "-f", `query=query { repository(owner:${JSON.stringify(owner)},name:${JSON.stringify(name)}) { ${fields} } }`]));
41969
+ if (response.errors?.length) throw new Error(response.errors.map((e) => e.message).join("; "));
41970
+ if (!response.data?.repository) throw new Error("merge queue repository read failed");
41971
+ return response.data.repository;
41972
+ }
41973
+ async function hasPrMergeQueue(repo, base, gh2 = readApi) {
41974
+ const result = await queueGraphql(repo, `mergeQueue(branch:${JSON.stringify(base)}) { id }`, gh2);
41975
+ return result.mergeQueue !== null;
41976
+ }
41977
+ async function readPrMergeQueue(pr, repo, gh2 = readApi) {
41978
+ if (!/^\d+$/.test(pr)) throw new Error("merge queue read requires a PR number");
41979
+ const result = await queueGraphql(
41980
+ repo,
41981
+ `pullRequest(number:${pr}) { state isInMergeQueue mergeQueueEntry { state position } autoMergeRequest { enabledAt } }`,
41982
+ gh2
41983
+ );
41984
+ if (!result.pullRequest) throw new Error(`merge queue PR #${pr} not found`);
41985
+ return result.pullRequest;
41986
+ }
41987
+ async function findPrMergeGroupRun(pr, repo, base, gh2 = readApi) {
41988
+ const pages = JSON.parse(await gh2([`repos/${repo}/actions/runs?event=merge_group&per_page=100`, "--paginate", "--slurp"]));
41989
+ const prefix = `gh-readonly-queue/${base}/pr-${pr}-`;
41990
+ const run = pages.flatMap((page) => page.workflow_runs).filter((run2) => run2.head_branch?.startsWith(prefix)).sort((a, b) => b.created_at.localeCompare(a.created_at) || b.id - a.id)[0];
41991
+ return run ? { id: run.id, url: run.html_url, conclusion: run.conclusion } : void 0;
41992
+ }
41993
+ function prMergeQueueLine(result) {
41994
+ const queue = result.mergeQueue;
41995
+ if (!queue) return void 0;
41996
+ switch (queue.state) {
41997
+ case "queued":
41998
+ return `pr land: PR #${result.pr} queued on ${queue.base}${queue.position === void 0 ? "" : ` (position ${queue.position})`}`;
41999
+ case "merged":
42000
+ return `pr land: PR #${result.pr} merged via merge queue`;
42001
+ case "ejected":
42002
+ return `pr land: PR #${result.pr} ejected from merge queue \u2014 ${queue.ejectedRun ? `merge_group run ${queue.ejectedRun.id} ${queue.ejectedRun.conclusion} ${queue.ejectedRun.url}` : "merge_group run not found"}`;
42003
+ case "timeout":
42004
+ return `pr land: PR #${result.pr} merge queue timeout on ${queue.base}`;
42005
+ }
42006
+ }
42007
+
41875
42008
  // src/command-register-collaboration.ts
41876
42009
  init_github_client();
41877
42010
  init_github_quota();
@@ -46819,7 +46952,7 @@ ${list}`);
46819
46952
  }
46820
46953
  class PrHeadBehindBaseError extends Error {
46821
46954
  }
46822
- pr.command("land <number>").description("agent merge path (#1440): train probe \u2014 checks-wait \u2014 merge --auto \u2014 poll enqueued \u2014 development PRs only").option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the PR repo)").option("--no-require-train", "skip train-authority preflight (not recommended for autonomous agents)").option("--force", "acknowledge and land past a remaining prunable/severe scratch housekeeping block (advisory kept plans/ never blocks, #3012) or a negated-closing-keyword (#3718) or ambiguous-cross-repo-closing (#4279) refusal").option("--without-review <reason>", "compatibility option; shared merge helpers do not require a review verdict").action(async (number, o) => {
46955
+ pr.command("land <number>").description("agent merge path (#1440): train probe \u2014 checks-wait \u2014 merge --auto \u2014 poll enqueued \u2014 development PRs only").option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the PR repo)").option("--enqueue-only", "return once queue membership is confirmed; without a merge queue, land normally").option("--no-require-train", "skip train-authority preflight (not recommended for autonomous agents)").option("--force", "acknowledge and land past a remaining prunable/severe scratch housekeeping block (advisory kept plans/ never blocks, #3012) or a negated-closing-keyword (#3718) or ambiguous-cross-repo-closing (#4279) refusal").option("--without-review <reason>", "compatibility option; shared merge helpers do not require a review verdict").action(async (number, o) => {
46823
46956
  if (/^(?:[^/]+\/[^/]+)?#\d+$/.test(number.trim())) {
46824
46957
  try {
46825
46958
  const parsed = parseIssueRef(number, o.repo);
@@ -46854,7 +46987,14 @@ ${list}`);
46854
46987
  return;
46855
46988
  }
46856
46989
  if (landClosingGuardVerdict.message) console.warn(landClosingGuardVerdict.message);
46857
- const result = await runPrLand(number, { repo: o.repo, requireTrain: o.requireTrain !== false }, {
46990
+ let mergeQueueEnabled = false;
46991
+ const result = await runPrLand(number, { repo: o.repo, requireTrain: o.requireTrain !== false, enqueueOnly: o.enqueueOnly }, {
46992
+ hasMergeQueue: async (repo, base) => mergeQueueEnabled = await hasPrMergeQueue(repo, base),
46993
+ readMergeQueue: readPrMergeQueue,
46994
+ findMergeGroupRun: findPrMergeGroupRun,
46995
+ queueProgress: (receipt) => {
46996
+ if (!o.json) printLine(prMergeQueueLine(receipt));
46997
+ },
46858
46998
  resolveRepo: async (prNumber, repoOpt) => {
46859
46999
  const knownRepo = repoOpt ?? await resolveRepo(o.repo).catch(() => void 0) ?? o.repo;
46860
47000
  let repoFromGh = "";
@@ -46944,7 +47084,8 @@ ${list}`);
46944
47084
  },
46945
47085
  mergeAuto: (prNumber, repo) => ghMergeAutoEnqueue(prNumber, repo, "--squash", {
46946
47086
  allowedClosing: landClosingGuardInput?.closing,
46947
- bodyText: landSquashBody
47087
+ bodyText: landSquashBody,
47088
+ mergeQueue: mergeQueueEnabled
46948
47089
  }),
46949
47090
  updateBranch: (prNumber, repo) => prLandUpdateBranch(prNumber, repo, Boolean(o.repo)),
46950
47091
  pollMerged: async (prNumber, repo, deadlineMs) => {
@@ -46968,7 +47109,7 @@ ${list}`);
46968
47109
  return false;
46969
47110
  }
46970
47111
  });
46971
- if (result.status !== "failed") {
47112
+ if (result.status !== "failed" && result.status !== "queued") {
46972
47113
  const boardAdvance = await advanceClosedIssuesToDone2(number, result.repo);
46973
47114
  const crossRepoFilingIssue = result.repo ? await reconcileCrossRepoFilingIssue(defaultGitHubClient(), result.repo, Number(number)) : { status: "failed", error: "could not resolve the PR repo for cross-repo filing-issue reconciliation" };
46974
47115
  const recovery = result.repo ? buildPostMergeReconRecovery({
@@ -47620,7 +47761,13 @@ function trainApplyDeps() {
47620
47761
  // Slack release announcement (#883/#6521/#6523): the org alerts channel for the Hub, the project's own
47621
47762
  // registry releaseChannel META for a product repo — best-effort inside announceRelease itself.
47622
47763
  announce: (args) => announceRelease({
47623
- run: async (file, cmdArgs) => (await execFileP(file, cmdArgs, { timeout: GH_TRAIN_TIMEOUT_MS })).stdout,
47764
+ // #8236: the announcement reads an already-PUBLISHED Release, so its `release view` rides the App
47765
+ // read pool like the train's other reads (#8030), never the owner's personal REST quota. Scoped
47766
+ // here, not in isAppRoutableGhReadArgs: other train paths may view a draft the App cannot see.
47767
+ run: async (file, cmdArgs) => {
47768
+ const env = file === "gh" && (cmdArgs[0] === "release" && cmdArgs[1] === "view" || isAppRoutableGhReadArgs(cmdArgs)) ? await ghReadEnv() : void 0;
47769
+ return (await execFileP(file, cmdArgs, { timeout: GH_TRAIN_TIMEOUT_MS, ...env ? { env } : {} })).stdout;
47770
+ },
47624
47771
  postAnnouncement: async (payload) => {
47625
47772
  const res = await releaseAnnouncement(payload, registryClientDeps(await loadConfig()));
47626
47773
  const body = res.body;
@@ -50919,7 +51066,8 @@ var surfaces_default = {
50919
51066
  ],
50920
51067
  outputs: [
50921
51068
  "cli/dist/index.cjs",
50922
- "cli/dist/main.cjs"
51069
+ "cli/dist/main.cjs",
51070
+ "cli/dist/runbook-sweep.cjs"
50923
51071
  ]
50924
51072
  }
50925
51073
  ],
package/cli/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.5.135",
3
+ "version": "4.5.137",
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",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/claude-plugin",
3
- "version": "4.5.135",
3
+ "version": "4.5.137",
4
4
  "description": "MMI workflow skills and org gates delivery.",
5
5
  "author": {
6
6
  "name": "MMI Future",
@@ -34,6 +34,8 @@ name: gate
34
34
  # (seed touch: keep BOM digests in lockstep after classifier land.)
35
35
  on:
36
36
  pull_request:
37
+ merge_group:
38
+ types: [checks_requested]
37
39
  push:
38
40
  branches: {{GATE_PUSH_BRANCHES_YAML}}
39
41
  tags: ['v*']
@@ -99,10 +101,10 @@ jobs:
99
101
  lockfile-path: {{GATE_CACHE_DEP_PATH}}
100
102
  fetch-depth: 0 # #8129: runbook freshness needs the PR merge-base history.
101
103
  - name: Runbook guard
102
- if: ${{ steps.development-gate.outputs.reuse != 'true' && (github.event_name == 'pull_request') }}
104
+ if: ${{ steps.development-gate.outputs.reuse != 'true' && (github.event_name == 'pull_request' || github.event_name == 'merge_group') }}
103
105
  uses: mutmutco/MMI-Hub/.github/actions/runbook-guard@{{GATE_RUNBOOK_SHA}}
104
106
  with:
105
- base-ref: origin/${{ github.base_ref }}
107
+ base-ref: ${{ github.event_name == 'merge_group' && github.event.merge_group.base_sha || format('origin/{0}', github.base_ref) }}
106
108
  # #5427: per-job npm cache under $RUNNER_TEMP — never the shared ~/.npm. Twelve mmi-live lanes
107
109
  # share the gha-runner home; npm hardlinks from that cache into node_modules, so one job's
108
110
  # `npm ci` can make another job's vitest/dist/workers/forks.js vanish mid-suite (Cannot find
@@ -1,6 +1,8 @@
1
1
  name: gate
2
2
  on:
3
3
  pull_request:
4
+ merge_group:
5
+ types: [checks_requested]
4
6
  push:
5
7
  branches: {{GATE_PUSH_BRANCHES_YAML}}
6
8
  tags: ['v*']
@@ -49,10 +51,10 @@ jobs:
49
51
  lockfile-path: package-lock.json
50
52
  fetch-depth: 0 # #8129: runbook freshness needs the PR merge-base history.
51
53
  - name: Runbook guard
52
- if: ${{ steps.development-gate.outputs.reuse != 'true' && (github.event_name == 'pull_request') }}
54
+ if: ${{ steps.development-gate.outputs.reuse != 'true' && (github.event_name == 'pull_request' || github.event_name == 'merge_group') }}
53
55
  uses: mutmutco/MMI-Hub/.github/actions/runbook-guard@{{GATE_RUNBOOK_SHA}}
54
56
  with:
55
- base-ref: origin/${{ github.base_ref }}
57
+ base-ref: ${{ github.event_name == 'merge_group' && github.event.merge_group.base_sha || format('origin/{0}', github.base_ref) }}
56
58
  - name: Isolate npm cache
57
59
  if: ${{ steps.development-gate.outputs.reuse != 'true' }}
58
60
  run: echo "NPM_CONFIG_CACHE=$RUNNER_TEMP/npm-cache" >> "$GITHUB_ENV"
@@ -3,6 +3,8 @@ name: screens
3
3
  # waits on a context that never reports.
4
4
  on:
5
5
  pull_request:
6
+ merge_group:
7
+ types: [checks_requested]
6
8
  push:
7
9
  branches: {{GATE_PUSH_BRANCHES_YAML}}
8
10
  tags: ['v*']