@mutmutco/codex-plugin 4.5.135 → 4.5.136

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,6 +1,6 @@
1
1
  {
2
2
  "name": "mmi",
3
- "version": "4.5.135",
3
+ "version": "4.5.136",
4
4
  "mmiCompat": "4.x",
5
5
  "description": "MMI workflow skills and org gates delivery.",
6
6
  "author": {
package/cli/dist/main.cjs CHANGED
@@ -17509,6 +17509,7 @@ function assertGitBashOnWindowsForJervHubRelease(repo, platform2 = process.platf
17509
17509
 
17510
17510
  // src/train-apply-correlation.ts
17511
17511
  var import_node_fs19 = require("node:fs");
17512
+ init_github_quota();
17512
17513
  var import_node_path17 = require("node:path");
17513
17514
 
17514
17515
  // src/fleet-lockstep-enforce.ts
@@ -18274,10 +18275,10 @@ var rollout_plan_default = {
18274
18275
  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
18276
  },
18276
18277
  baseline: {
18277
- version: "4.5.135",
18278
- tag: "v4.5.135",
18279
- commit: "2ae28f97dd36",
18280
- npm: "@mutmutco/cli@4.5.135"
18278
+ version: "4.5.136",
18279
+ tag: "v4.5.136",
18280
+ commit: "642526387569",
18281
+ npm: "@mutmutco/cli@4.5.136"
18281
18282
  },
18282
18283
  exitCriterion: "fleet-n-of-n",
18283
18284
  hubOnlyShortcut: "forbidden",
@@ -18294,14 +18295,14 @@ var rollout_plan_default = {
18294
18295
  repo: "mutmutco/mmi-hub",
18295
18296
  role: "canary",
18296
18297
  schedule: "train",
18297
- v3Target: "v4.5.135"
18298
+ v3Target: "v4.5.136"
18298
18299
  }
18299
18300
  ],
18300
18301
  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
18302
  rollback: {
18302
18303
  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)"
18304
+ mechanism: "npm dist-tag latest -> 4.5.136 and redeploy the Hub Lambda from tag v4.5.136 (642526387569); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
18305
+ v3Target: "v4.5.136 (@mutmutco/cli@4.5.136, tag commit 642526387569 \u2014 last known-good release carrying the repo-index v4-only contract)"
18305
18306
  }
18306
18307
  },
18307
18308
  {
@@ -20419,11 +20420,22 @@ async function loadReleaseTrackBranchHints(deps) {
20419
20420
  ]);
20420
20421
  return { hasDevelopmentBranch, hasMainBranch, hasRcBranch };
20421
20422
  }
20423
+ async function readTrainLogin(deps, waitMs = 18e4, attempts = 5) {
20424
+ for (let i = 1; ; i++) {
20425
+ try {
20426
+ return requireValue(clean2(await deps.run("gh", ["api", "user", "--jq", ".login"])), "GitHub login");
20427
+ } catch (e) {
20428
+ if (i >= attempts || !isRateLimitText(e instanceof Error ? e.message : String(e))) throw e;
20429
+ deps.warn?.(`GitHub login read rate-limited; retrying in ${waitMs / 6e4} min (${i}/${attempts - 1})`);
20430
+ await resolveSleep(deps)(waitMs);
20431
+ }
20432
+ }
20433
+ }
20422
20434
  async function buildTrainApplyContext(deps) {
20423
20435
  const repo = requireValue(clean2(await deps.run("gh", ["repo", "view", "--json", "nameWithOwner", "--jq", ".nameWithOwner"])), "repo");
20424
20436
  const [owner, name] = repo.split("/");
20425
20437
  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");
20438
+ const login = await readTrainLogin(deps);
20427
20439
  const verdict = await deps.trainAuthority(repo);
20428
20440
  if (!verdict.ok) throw new Error(`${commandAuthorityLabel(owner)}: train authority could not be verified (${verdict.error})`);
20429
20441
  if (!verdict.train) {
@@ -22443,7 +22455,7 @@ async function runTenantRedeploy(deps, options) {
22443
22455
  const repo = options.repo;
22444
22456
  const [owner, name] = repo.split("/");
22445
22457
  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");
22458
+ const login = await readTrainLogin(deps);
22447
22459
  const verdict = await deps.trainAuthority(repo);
22448
22460
  if (!verdict.ok) throw new Error(`${commandAuthorityLabel(owner)}: train authority could not be verified (${verdict.error})`);
22449
22461
  if (!verdict.train) throw new Error(`${commandAuthorityLabel(owner)}: @${login} is ${verdict.role} \u2014 no train authority on ${repo}`);
@@ -39845,6 +39857,7 @@ function boardAdvanceFailureMessage(result) {
39845
39857
  }
39846
39858
 
39847
39859
  // src/pr-land.ts
39860
+ var PR_LAND_QUEUE_TIMEOUT_MS = 45 * 6e4;
39848
39861
  function prHeadBehindBase(input) {
39849
39862
  if ((input.mergeStateStatus ?? "").toUpperCase() === "BEHIND") return true;
39850
39863
  return /out[- ]of[- ]date|not up to date|up to date with the base|update (?:the|your) branch/i.test(input.message ?? "");
@@ -39968,6 +39981,17 @@ async function runPrLand(prNumber, options, deps) {
39968
39981
  }
39969
39982
  const ciPolicy = await deps.resolveCiPolicy(repo);
39970
39983
  base.ciPolicy = ciPolicy;
39984
+ const hasQueue = await deps.hasMergeQueue?.(repo, resolved.base) ?? false;
39985
+ let queueSnapshot;
39986
+ if (hasQueue) {
39987
+ base.mergeQueue = { base: resolved.base, state: "queued" };
39988
+ if (!deps.readMergeQueue) throw new Error("merge queue reader unavailable");
39989
+ queueSnapshot = await deps.readMergeQueue(prNumber, repo);
39990
+ if (queueSnapshot.isInMergeQueue || queueSnapshot.state === "MERGED") {
39991
+ base.mergeStatus = "auto-merge-enqueued";
39992
+ return pollPrMergeQueue(prNumber, options, deps, base, queueSnapshot);
39993
+ }
39994
+ }
39971
39995
  const checksWaitError = (checksWait) => {
39972
39996
  if (checksWait.status === "conflicting") {
39973
39997
  return `checks-wait conflicting: ${checksWait.reason ?? "PR is conflicting with the base branch"}`;
@@ -40003,6 +40027,14 @@ async function runPrLand(prNumber, options, deps) {
40003
40027
  if (merge.mergeStatus === "failed") {
40004
40028
  return { ...base, error: merge.error ?? "merge failed" };
40005
40029
  }
40030
+ if (hasQueue) {
40031
+ if (merge.mergeStatus === "merged") {
40032
+ return queueMerged(base, deps);
40033
+ }
40034
+ const deadline = (deps.now ?? (() => Date.now()))() + PR_LAND_QUEUE_TIMEOUT_MS;
40035
+ await (deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms))))(PR_LAND_POLL_MS);
40036
+ return pollPrMergeQueue(prNumber, options, deps, base, void 0, deadline);
40037
+ }
40006
40038
  if (merge.mergeStatus === "merged") {
40007
40039
  return {
40008
40040
  ...base,
@@ -40025,6 +40057,53 @@ async function runPrLand(prNumber, options, deps) {
40025
40057
  error: "auto-merge enqueued but PR did not reach MERGED within timeout \u2014 poll manually with gh pr view"
40026
40058
  };
40027
40059
  }
40060
+ function queueMerged(base, deps) {
40061
+ const result = {
40062
+ ...base,
40063
+ status: "merged-via-queue",
40064
+ mergeStatus: "merged",
40065
+ mergeQueue: { ...base.mergeQueue, state: "merged" },
40066
+ branchFinality: prLandedBranchFinality(base.pr, base.base)
40067
+ };
40068
+ deps.queueProgress?.(result);
40069
+ return result;
40070
+ }
40071
+ async function pollPrMergeQueue(pr, options, deps, base, snapshot, deadlineMs) {
40072
+ const now = deps.now ?? (() => Date.now());
40073
+ const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
40074
+ const deadline = deadlineMs ?? now() + PR_LAND_QUEUE_TIMEOUT_MS;
40075
+ while (now() < deadline) {
40076
+ const read = snapshot ?? await deps.readMergeQueue(pr, base.repo);
40077
+ snapshot = void 0;
40078
+ if (read.state === "MERGED") return queueMerged(base, deps);
40079
+ if (read.isInMergeQueue) {
40080
+ base.mergeQueue = {
40081
+ base: base.base,
40082
+ state: "queued",
40083
+ ...read.mergeQueueEntry ? { position: read.mergeQueueEntry.position, entryState: read.mergeQueueEntry.state } : {}
40084
+ };
40085
+ deps.queueProgress?.(base);
40086
+ if (options.enqueueOnly) return { ...base, status: "queued" };
40087
+ } else if (read.state === "OPEN" && !read.autoMergeRequest) {
40088
+ const run = await deps.findMergeGroupRun(pr, base.repo, base.base);
40089
+ const result2 = {
40090
+ ...base,
40091
+ mergeQueue: { ...base.mergeQueue, state: "ejected", ...run ? { ejectedRun: run } : {} },
40092
+ error: `merge queue ejected PR #${pr} \u2014 ${run ? `merge_group run ${run.id} ${run.conclusion} ${run.url}` : "merge_group run not found"}`
40093
+ };
40094
+ deps.queueProgress?.(result2);
40095
+ return result2;
40096
+ }
40097
+ await sleep2(PR_LAND_POLL_MS);
40098
+ }
40099
+ const result = {
40100
+ ...base,
40101
+ mergeQueue: { ...base.mergeQueue, state: "timeout" },
40102
+ error: "merge queue did not reach MERGED within 45 minutes \u2014 rerun pr land to resume polling"
40103
+ };
40104
+ deps.queueProgress?.(result);
40105
+ return result;
40106
+ }
40028
40107
 
40029
40108
  // src/test-policy-core.ts
40030
40109
  var import_node_child_process13 = require("node:child_process");
@@ -41085,18 +41164,19 @@ async function ghMergeAutoEnqueue(prNumber, repo, method, extra) {
41085
41164
  bodyText: method === "--squash" ? extra?.bodyText : void 0
41086
41165
  });
41087
41166
  try {
41088
- return await mergeAutoEnqueueWithBody(prNumber, args, method, io, overrideBody?.path);
41167
+ return await mergeAutoEnqueueWithBody(prNumber, args, method, io, overrideBody?.path, extra?.mergeQueue);
41089
41168
  } finally {
41090
41169
  overrideBody?.cleanup();
41091
41170
  }
41092
41171
  }
41093
- async function mergeAutoEnqueueWithBody(prNumber, args, method, io, bodyFile) {
41172
+ async function mergeAutoEnqueueWithBody(prNumber, args, method, io, bodyFile, mergeQueue = false) {
41094
41173
  const headRef = (await io.gh(["pr", "view", prNumber, ...args, "--json", "headRefName", "--jq", ".headRefName"], GC_GH_TIMEOUT_MS).catch(() => "")).trim();
41095
41174
  const deleteBranch = !isProtectedBranch(headRef);
41096
41175
  const readMergeState = () => readGhPrStateWithRetry(
41097
41176
  () => io.gh(["pr", "view", prNumber, ...args, "--json", "state", "--jq", ".state"], GC_GH_TIMEOUT_MS)
41098
41177
  );
41099
41178
  const confirmEnqueueOutcome = async () => {
41179
+ if (mergeQueue) return { mergeStatus: "auto-merge-enqueued" };
41100
41180
  const stateRead = await readMergeState();
41101
41181
  if (!stateRead.ok) {
41102
41182
  return { mergeStatus: "failed", error: `could not read PR state after merge: ${stateRead.error}` };
@@ -41872,6 +41952,57 @@ function applyChecklistCheck(body, query, checked) {
41872
41952
  return { ok: true, edit: setChecklistMarker(body, sel.item, checked), item: sel.item };
41873
41953
  }
41874
41954
 
41955
+ // src/pr-merge-queue.ts
41956
+ init_cli_shared();
41957
+ var readApi = async (args) => {
41958
+ const env = await ghReadEnv();
41959
+ return (await execFileP("gh", ["api", ...args], {
41960
+ timeout: 2e4,
41961
+ ...env ? { env } : {}
41962
+ })).stdout;
41963
+ };
41964
+ async function queueGraphql(repo, fields, gh2) {
41965
+ const [owner, name] = repo.split("/");
41966
+ const response = JSON.parse(await gh2(["graphql", "-f", `query=query { repository(owner:${JSON.stringify(owner)},name:${JSON.stringify(name)}) { ${fields} } }`]));
41967
+ if (response.errors?.length) throw new Error(response.errors.map((e) => e.message).join("; "));
41968
+ if (!response.data?.repository) throw new Error("merge queue repository read failed");
41969
+ return response.data.repository;
41970
+ }
41971
+ async function hasPrMergeQueue(repo, base, gh2 = readApi) {
41972
+ const result = await queueGraphql(repo, `mergeQueue(branch:${JSON.stringify(base)}) { id }`, gh2);
41973
+ return result.mergeQueue !== null;
41974
+ }
41975
+ async function readPrMergeQueue(pr, repo, gh2 = readApi) {
41976
+ if (!/^\d+$/.test(pr)) throw new Error("merge queue read requires a PR number");
41977
+ const result = await queueGraphql(
41978
+ repo,
41979
+ `pullRequest(number:${pr}) { state isInMergeQueue mergeQueueEntry { state position } autoMergeRequest { enabledAt } }`,
41980
+ gh2
41981
+ );
41982
+ if (!result.pullRequest) throw new Error(`merge queue PR #${pr} not found`);
41983
+ return result.pullRequest;
41984
+ }
41985
+ async function findPrMergeGroupRun(pr, repo, base, gh2 = readApi) {
41986
+ const pages = JSON.parse(await gh2([`repos/${repo}/actions/runs?event=merge_group&per_page=100`, "--paginate", "--slurp"]));
41987
+ const prefix = `gh-readonly-queue/${base}/pr-${pr}-`;
41988
+ 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];
41989
+ return run ? { id: run.id, url: run.html_url, conclusion: run.conclusion } : void 0;
41990
+ }
41991
+ function prMergeQueueLine(result) {
41992
+ const queue = result.mergeQueue;
41993
+ if (!queue) return void 0;
41994
+ switch (queue.state) {
41995
+ case "queued":
41996
+ return `pr land: PR #${result.pr} queued on ${queue.base}${queue.position === void 0 ? "" : ` (position ${queue.position})`}`;
41997
+ case "merged":
41998
+ return `pr land: PR #${result.pr} merged via merge queue`;
41999
+ case "ejected":
42000
+ 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"}`;
42001
+ case "timeout":
42002
+ return `pr land: PR #${result.pr} merge queue timeout on ${queue.base}`;
42003
+ }
42004
+ }
42005
+
41875
42006
  // src/command-register-collaboration.ts
41876
42007
  init_github_client();
41877
42008
  init_github_quota();
@@ -46819,7 +46950,7 @@ ${list}`);
46819
46950
  }
46820
46951
  class PrHeadBehindBaseError extends Error {
46821
46952
  }
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) => {
46953
+ 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
46954
  if (/^(?:[^/]+\/[^/]+)?#\d+$/.test(number.trim())) {
46824
46955
  try {
46825
46956
  const parsed = parseIssueRef(number, o.repo);
@@ -46854,7 +46985,14 @@ ${list}`);
46854
46985
  return;
46855
46986
  }
46856
46987
  if (landClosingGuardVerdict.message) console.warn(landClosingGuardVerdict.message);
46857
- const result = await runPrLand(number, { repo: o.repo, requireTrain: o.requireTrain !== false }, {
46988
+ let mergeQueueEnabled = false;
46989
+ const result = await runPrLand(number, { repo: o.repo, requireTrain: o.requireTrain !== false, enqueueOnly: o.enqueueOnly }, {
46990
+ hasMergeQueue: async (repo, base) => mergeQueueEnabled = await hasPrMergeQueue(repo, base),
46991
+ readMergeQueue: readPrMergeQueue,
46992
+ findMergeGroupRun: findPrMergeGroupRun,
46993
+ queueProgress: (receipt) => {
46994
+ if (!o.json) printLine(prMergeQueueLine(receipt));
46995
+ },
46858
46996
  resolveRepo: async (prNumber, repoOpt) => {
46859
46997
  const knownRepo = repoOpt ?? await resolveRepo(o.repo).catch(() => void 0) ?? o.repo;
46860
46998
  let repoFromGh = "";
@@ -46944,7 +47082,8 @@ ${list}`);
46944
47082
  },
46945
47083
  mergeAuto: (prNumber, repo) => ghMergeAutoEnqueue(prNumber, repo, "--squash", {
46946
47084
  allowedClosing: landClosingGuardInput?.closing,
46947
- bodyText: landSquashBody
47085
+ bodyText: landSquashBody,
47086
+ mergeQueue: mergeQueueEnabled
46948
47087
  }),
46949
47088
  updateBranch: (prNumber, repo) => prLandUpdateBranch(prNumber, repo, Boolean(o.repo)),
46950
47089
  pollMerged: async (prNumber, repo, deadlineMs) => {
@@ -46968,7 +47107,7 @@ ${list}`);
46968
47107
  return false;
46969
47108
  }
46970
47109
  });
46971
- if (result.status !== "failed") {
47110
+ if (result.status !== "failed" && result.status !== "queued") {
46972
47111
  const boardAdvance = await advanceClosedIssuesToDone2(number, result.repo);
46973
47112
  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
47113
  const recovery = result.repo ? buildPostMergeReconRecovery({
@@ -47620,7 +47759,13 @@ function trainApplyDeps() {
47620
47759
  // Slack release announcement (#883/#6521/#6523): the org alerts channel for the Hub, the project's own
47621
47760
  // registry releaseChannel META for a product repo — best-effort inside announceRelease itself.
47622
47761
  announce: (args) => announceRelease({
47623
- run: async (file, cmdArgs) => (await execFileP(file, cmdArgs, { timeout: GH_TRAIN_TIMEOUT_MS })).stdout,
47762
+ // #8236: the announcement reads an already-PUBLISHED Release, so its `release view` rides the App
47763
+ // read pool like the train's other reads (#8030), never the owner's personal REST quota. Scoped
47764
+ // here, not in isAppRoutableGhReadArgs: other train paths may view a draft the App cannot see.
47765
+ run: async (file, cmdArgs) => {
47766
+ const env = file === "gh" && (cmdArgs[0] === "release" && cmdArgs[1] === "view" || isAppRoutableGhReadArgs(cmdArgs)) ? await ghReadEnv() : void 0;
47767
+ return (await execFileP(file, cmdArgs, { timeout: GH_TRAIN_TIMEOUT_MS, ...env ? { env } : {} })).stdout;
47768
+ },
47624
47769
  postAnnouncement: async (payload) => {
47625
47770
  const res = await releaseAnnouncement(payload, registryClientDeps(await loadConfig()));
47626
47771
  const body = res.body;
@@ -50919,7 +51064,8 @@ var surfaces_default = {
50919
51064
  ],
50920
51065
  outputs: [
50921
51066
  "cli/dist/index.cjs",
50922
- "cli/dist/main.cjs"
51067
+ "cli/dist/main.cjs",
51068
+ "cli/dist/runbook-sweep.cjs"
50923
51069
  ]
50924
51070
  }
50925
51071
  ],
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.136",
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/codex-plugin",
3
- "version": "4.5.135",
3
+ "version": "4.5.136",
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*']