@mutmutco/claude-plugin 4.6.23 → 4.6.24

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.
package/cli/dist/main.cjs CHANGED
@@ -13527,7 +13527,7 @@ function useColor() {
13527
13527
  var program = new Command();
13528
13528
 
13529
13529
  // src/command-composition.ts
13530
- var import_node_fs65 = require("node:fs");
13530
+ var import_node_fs66 = require("node:fs");
13531
13531
  init_clean_exit();
13532
13532
  init_cli_shared();
13533
13533
 
@@ -15195,7 +15195,7 @@ function resolveVerbAliasShim(argv) {
15195
15195
 
15196
15196
  // src/command-composition.ts
15197
15197
  var import_node_os28 = require("node:os");
15198
- var import_node_path62 = require("node:path");
15198
+ var import_node_path63 = require("node:path");
15199
15199
 
15200
15200
  // src/board-read.ts
15201
15201
  var import_node_fs18 = require("node:fs");
@@ -15362,12 +15362,19 @@ var APP_POLL_PERMISSIONS = {
15362
15362
  var APP_JWT_BACKDATE_S = 60;
15363
15363
  var APP_JWT_TTL_S = 9 * 60;
15364
15364
  var AppActorError = class extends Error {
15365
- constructor(message2, code = "mint-failed") {
15365
+ constructor(message2, code = "mint-failed", mintRefusal) {
15366
15366
  super(message2);
15367
15367
  this.code = code;
15368
+ this.mintRefusal = mintRefusal;
15368
15369
  }
15369
15370
  code;
15371
+ mintRefusal;
15370
15372
  };
15373
+ function sanitizeMintRefusalReason(raw) {
15374
+ if (typeof raw !== "string") return void 0;
15375
+ const cleaned = raw.replace(/-----BEGIN [^-]+-----[\s\S]*?(?:-----END [^-]+-----|$)/g, "[redacted]").replace(/\bBearer\s+\S+/gi, "Bearer [redacted]").replace(/\beyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.?[A-Za-z0-9_-]*/g, "[redacted]").replace(/\b(?:gh[pousr]|github_pat)_[A-Za-z0-9_]+/g, "[redacted]").replace(/\s+/g, " ").trim();
15376
+ return cleaned === "" ? void 0 : cleaned.slice(0, 200);
15377
+ }
15371
15378
  function commandPathOf(cmd) {
15372
15379
  const parts = [];
15373
15380
  let cursor = cmd;
@@ -15425,13 +15432,16 @@ var APP_WRITE_COMMANDS = /* @__PURE__ */ new Map([
15425
15432
  ["run rerun", { metadata: "read", actions: "write" }],
15426
15433
  // #9780: repository Actions variables — get/list mint only Variables:read; create/update/delete
15427
15434
  // Variables:write. Repository scope only; no secret store, no rename, no workflow dispatch.
15428
- ["vars get", { metadata: "read", variables: "read" }],
15429
- ["vars list", { metadata: "read", variables: "read" }],
15430
- ["vars create", { metadata: "read", variables: "write" }],
15431
- ["vars update", { metadata: "read", variables: "write" }],
15432
- ["vars delete", { metadata: "read", variables: "write" }],
15435
+ // #9790: the token key is `actions_variables` (as ci-reconcile.yml mints); `variables` was refused 422.
15436
+ ["vars get", { metadata: "read", actions_variables: "read" }],
15437
+ ["vars list", { metadata: "read", actions_variables: "read" }],
15438
+ ["vars create", { metadata: "read", actions_variables: "write" }],
15439
+ ["vars update", { metadata: "read", actions_variables: "write" }],
15440
+ ["vars delete", { metadata: "read", actions_variables: "write" }],
15433
15441
  // #9779: the archive-status read is a metadata:read-only repository read — nothing broader.
15434
15442
  ["repo archive-status", { metadata: "read" }],
15443
+ // #9788: historical merge-queue lifecycle — one fixed PullRequest.timelineItems GraphQL query; read-only.
15444
+ ["read merge-queue lifecycle", { metadata: "read", pull_requests: "read" }],
15435
15445
  ["issue comment", { metadata: "read", issues: "write", pull_requests: "write" }],
15436
15446
  // #9614: closing an issue is an issue-state write — it rides the org App (mmi-github-app), so the close
15437
15447
  // is attributed to the App instead of the personal account. Nothing broader than issues.
@@ -15574,7 +15584,17 @@ async function mintFromMember(deps, permissions, keys) {
15574
15584
  throw new AppActorError(`app actor: token mint transport failed (${e?.constructor?.name ?? "Error"}) \u2014 automation fails closed, no personal-token fallback`);
15575
15585
  }
15576
15586
  if (!res.ok) {
15577
- throw new AppActorError(`app actor: installation token mint failed (HTTP ${res.status}) \u2014 automation fails closed, no personal-token fallback`);
15587
+ let reason;
15588
+ try {
15589
+ reason = sanitizeMintRefusalReason((await res.json())?.message);
15590
+ } catch {
15591
+ reason = void 0;
15592
+ }
15593
+ throw new AppActorError(
15594
+ `app actor: installation token mint failed (HTTP ${res.status}${reason ? `: ${reason}` : ""}) \u2014 automation fails closed, no personal-token fallback`,
15595
+ "mint-failed",
15596
+ { status: res.status, ...reason ? { reason } : {} }
15597
+ );
15578
15598
  }
15579
15599
  let body;
15580
15600
  try {
@@ -28146,10 +28166,10 @@ var rollout_plan_default = {
28146
28166
  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)."
28147
28167
  },
28148
28168
  baseline: {
28149
- version: "4.6.23",
28150
- tag: "v4.6.23",
28151
- commit: "27b8f434196b",
28152
- npm: "@mutmutco/cli@4.6.23"
28169
+ version: "4.6.24",
28170
+ tag: "v4.6.24",
28171
+ commit: "3632dffdf8a5",
28172
+ npm: "@mutmutco/cli@4.6.24"
28153
28173
  },
28154
28174
  exitCriterion: "fleet-n-of-n",
28155
28175
  hubOnlyShortcut: "forbidden",
@@ -28166,14 +28186,14 @@ var rollout_plan_default = {
28166
28186
  repo: "mutmutco/mmi-hub",
28167
28187
  role: "canary",
28168
28188
  schedule: "train",
28169
- v3Target: "v4.6.23"
28189
+ v3Target: "v4.6.24"
28170
28190
  }
28171
28191
  ],
28172
28192
  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.",
28173
28193
  rollback: {
28174
28194
  independent: true,
28175
- mechanism: "npm dist-tag latest -> 4.6.23 and redeploy the Hub Lambda from tag v4.6.23 (27b8f434196b); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
28176
- v3Target: "v4.6.23 (@mutmutco/cli@4.6.23, tag commit 27b8f434196b \u2014 last known-good release carrying the repo-index v4-only contract)"
28195
+ mechanism: "npm dist-tag latest -> 4.6.24 and redeploy the Hub Lambda from tag v4.6.24 (3632dffdf8a5); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
28196
+ v3Target: "v4.6.24 (@mutmutco/cli@4.6.24, tag commit 3632dffdf8a5 \u2014 last known-good release carrying the repo-index v4-only contract)"
28177
28197
  }
28178
28198
  },
28179
28199
  {
@@ -44554,6 +44574,10 @@ function resolveCanonicalRepo(raw, projects) {
44554
44574
  return { kind: "unregistered" };
44555
44575
  }
44556
44576
  function classifyUpstreamError(e) {
44577
+ if (e instanceof AppActorError) {
44578
+ const status = e.mintRefusal?.status ?? 0;
44579
+ return { status, reason: "mint-refused", mint: { status, reason: e.mintRefusal?.reason ?? e.message } };
44580
+ }
44557
44581
  if (e instanceof GitHubApiError) {
44558
44582
  if (e.status === 404) return { status: 404, reason: "bare-404" };
44559
44583
  if (e.status === 403 || e.status === 401) return { status: e.status, reason: "denied" };
@@ -44571,7 +44595,7 @@ async function call(deps, log, method, path2, body) {
44571
44595
  log.push({ method, path: path2, status: res.status });
44572
44596
  return res;
44573
44597
  } catch (e) {
44574
- log.push({ method, path: path2, status: classifyUpstreamError(e).status });
44598
+ if (!(e instanceof AppActorError)) log.push({ method, path: path2, status: classifyUpstreamError(e).status });
44575
44599
  throw e;
44576
44600
  }
44577
44601
  }
@@ -44589,7 +44613,7 @@ function receiptBase(deps, operation, repo, ctx, now) {
44589
44613
  upstream: ctx.log
44590
44614
  };
44591
44615
  }
44592
- var VARS_PER_PAGE = 100;
44616
+ var VARS_PER_PAGE = 30;
44593
44617
  var VARS_MAX_PAGES = 100;
44594
44618
  function parseVariableEntry(raw) {
44595
44619
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) throw new Error("variable entry is not an object");
@@ -44637,6 +44661,12 @@ async function enumerateRepositoryVariables(deps, repo, log = []) {
44637
44661
  }
44638
44662
  return { complete: true, entries, totalVariables: total, pages: Math.max(1, Math.ceil(total / VARS_PER_PAGE)) };
44639
44663
  }
44664
+ function mintCapability(c, permission) {
44665
+ return c.mint ? { permission, outcome: "mint-refused", status: c.mint.status, reason: c.mint.reason } : void 0;
44666
+ }
44667
+ function mintDetail(c) {
44668
+ return `App token mint refused${c.mint?.status ? ` (HTTP ${c.mint.status})` : ""}: ${c.mint?.reason ?? "unknown"}`;
44669
+ }
44640
44670
  function findEntry(entries, name) {
44641
44671
  const lower = name.toLowerCase();
44642
44672
  return entries.find((e) => e.name.toLowerCase() === lower);
@@ -44646,7 +44676,9 @@ async function readVariable(deps, repo, name, log) {
44646
44676
  try {
44647
44677
  ({ body } = await call(deps, log, "GET", `repos/${repo}/actions/variables/${name}`));
44648
44678
  } catch (e) {
44649
- const { status, reason } = classifyUpstreamError(e);
44679
+ const classified = classifyUpstreamError(e);
44680
+ const { status, reason } = classified;
44681
+ if (reason === "mint-refused") return { presence: "unknown", unknownReason: "mint-refused", detail: mintDetail(classified), capability: mintCapability(classified, deps.mintPermission ?? "variables:read") };
44650
44682
  if (reason === "denied") return { presence: "unknown", unknownReason: "denied", detail: `HTTP ${status}: the App installation cannot read repository variables`, capability: { permission: "variables:read", outcome: "denied" } };
44651
44683
  if (reason !== "bare-404") return { presence: "unknown", unknownReason: reason, detail: String(e.message).slice(0, 200) };
44652
44684
  try {
@@ -44657,6 +44689,7 @@ async function readVariable(deps, repo, name, log) {
44657
44689
  return { presence: "absent", absenceProof: { provenBy: "complete-enumeration", pages: en.pages, perPage: VARS_PER_PAGE, totalVariables: en.totalVariables, nameExcluded: true } };
44658
44690
  } catch (proofError) {
44659
44691
  const proof = classifyUpstreamError(proofError);
44692
+ if (proof.reason === "mint-refused") return { presence: "unknown", unknownReason: "mint-refused", detail: `bare 404 on GET, and the absence proof failed: ${mintDetail(proof)}`, capability: mintCapability(proof, deps.mintPermission ?? "variables:read") };
44660
44693
  return { presence: "unknown", unknownReason: proof.reason === "bare-404" ? "transport-error" : proof.reason, detail: `bare 404 on GET, and the absence proof failed: ${String(proofError.message).slice(0, 160)}` };
44661
44694
  }
44662
44695
  }
@@ -44701,12 +44734,14 @@ async function listVariablesReceipt(deps, repo) {
44701
44734
  unknownReason: en.reason
44702
44735
  };
44703
44736
  } catch (e) {
44704
- const { status, reason } = classifyUpstreamError(e);
44737
+ const classified = classifyUpstreamError(e);
44738
+ const { status, reason } = classified;
44705
44739
  return {
44706
44740
  ...receiptBase(deps, "list", repo, ctx, now),
44707
44741
  pagination: { pages: 0, perPage: VARS_PER_PAGE, complete: false, reason },
44708
44742
  variables: [],
44709
44743
  unknownReason: reason,
44744
+ ...reason === "mint-refused" ? { capability: mintCapability(classified, deps.mintPermission ?? "variables:read"), pagination: { pages: 0, perPage: VARS_PER_PAGE, complete: false, reason, detail: mintDetail(classified) } } : {},
44710
44745
  ...reason === "denied" ? { capability: { permission: "variables:read", outcome: "denied" }, pagination: { pages: 0, perPage: VARS_PER_PAGE, complete: false, reason, detail: `HTTP ${status}: the App installation cannot list repository variables` } } : {}
44711
44746
  };
44712
44747
  }
@@ -44744,7 +44779,8 @@ async function createVariableReceipt(deps, repo, name, value) {
44744
44779
  try {
44745
44780
  en = await enumerateRepositoryVariables(deps, repo, ctx.log);
44746
44781
  } catch (e) {
44747
- const { status, reason } = classifyUpstreamError(e);
44782
+ const classified = classifyUpstreamError(e);
44783
+ const { status, reason } = classified;
44748
44784
  return {
44749
44785
  ...receiptBase(deps, "create", repo, ctx, now),
44750
44786
  operation: "create",
@@ -44752,8 +44788,9 @@ async function createVariableReceipt(deps, repo, name, value) {
44752
44788
  outcome: "not-dispatched",
44753
44789
  casLimitation: "no-atomic-compare-and-swap",
44754
44790
  baseline: { presence: "unknown", source: "complete-enumeration" },
44755
- reason: `no observed absent baseline: ${reason === "denied" ? `HTTP ${status}: the App installation cannot list repository variables` : String(e.message).slice(0, 160)}`,
44756
- ...reason === "denied" ? { capability: { permission: "variables:read", outcome: "denied" } } : {}
44791
+ reason: `no observed absent baseline: ${reason === "denied" ? `HTTP ${status}: the App installation cannot list repository variables` : reason === "mint-refused" ? mintDetail(classified) : String(e.message).slice(0, 160)}`,
44792
+ ...reason === "denied" ? { capability: { permission: "variables:read", outcome: "denied" } } : {},
44793
+ ...reason === "mint-refused" ? { capability: mintCapability(classified, deps.mintPermission ?? "variables:write") } : {}
44757
44794
  };
44758
44795
  }
44759
44796
  const base = { ...receiptBase(deps, "create", repo, ctx, now), operation: "create", name, casLimitation: "no-atomic-compare-and-swap" };
@@ -44767,8 +44804,10 @@ async function createVariableReceipt(deps, repo, name, value) {
44767
44804
  try {
44768
44805
  await call(deps, ctx.log, "POST", `repos/${repo}/actions/variables`, { name, value });
44769
44806
  } catch (e) {
44770
- const { status, reason } = classifyUpstreamError(e);
44807
+ const classified = classifyUpstreamError(e);
44808
+ const { status, reason } = classified;
44771
44809
  const absentBaseline = { presence: "absent", source: "complete-enumeration" };
44810
+ if (reason === "mint-refused") return { ...base, outcome: "not-dispatched", baseline: absentBaseline, reason: mintDetail(classified), capability: mintCapability(classified, deps.mintPermission ?? "variables:write") };
44772
44811
  if (reason === "denied") return { ...base, outcome: "rejected", baseline: absentBaseline, reason: `HTTP ${status}: the App installation lacks repository Variables write`, capability: { permission: "variables:write", outcome: "denied" } };
44773
44812
  if (status === 404) return { ...base, outcome: "rejected", baseline: absentBaseline, reason: "HTTP 404: repository missing or not visible to the installation" };
44774
44813
  if (status === 409 || status === 422) return { ...base, outcome: "rejected", baseline: absentBaseline, reason: `HTTP ${status}: ${String(e.message).slice(0, 160)}` };
@@ -44819,7 +44858,9 @@ async function updateVariableReceipt(deps, repo, name, value) {
44819
44858
  try {
44820
44859
  await call(deps, ctx.log, "PATCH", `repos/${repo}/actions/variables/${name}`, { value });
44821
44860
  } catch (e) {
44822
- const { status, reason } = classifyUpstreamError(e);
44861
+ const classified = classifyUpstreamError(e);
44862
+ const { status, reason } = classified;
44863
+ if (reason === "mint-refused") return { ...base, outcome: "not-dispatched", baseline: { presence: "present", value: baseline.value, source: "get" }, reason: mintDetail(classified), capability: mintCapability(classified, deps.mintPermission ?? "variables:write") };
44823
44864
  if (reason === "denied") return rejectedWrite(base, { presence: "present", value: baseline.value }, `HTTP ${status}: the App installation lacks repository Variables write`, { permission: "variables:write", outcome: "denied" });
44824
44865
  if (status === 404) return rejectedWrite(base, { presence: "present", value: baseline.value }, "HTTP 404 at write: the variable vanished after the baseline check (concurrent deletion suspected) \u2014 reconcile read-only, never create");
44825
44866
  if (status === 409 || status === 422) return rejectedWrite(base, { presence: "present", value: baseline.value }, `HTTP ${status}: ${String(e.message).slice(0, 160)}`);
@@ -44868,7 +44909,9 @@ async function deleteVariableReceipt(deps, repo, name) {
44868
44909
  try {
44869
44910
  await call(deps, ctx.log, "DELETE", `repos/${repo}/actions/variables/${name}`);
44870
44911
  } catch (e) {
44871
- const { status, reason } = classifyUpstreamError(e);
44912
+ const classified = classifyUpstreamError(e);
44913
+ const { status, reason } = classified;
44914
+ if (reason === "mint-refused") return { ...base, outcome: "not-dispatched", baseline: { presence: "present", value: baseline.value, source: "get" }, reason: mintDetail(classified), capability: mintCapability(classified, deps.mintPermission ?? "variables:write") };
44872
44915
  if (reason === "denied") return rejectedWrite(base, { presence: "present", value: baseline.value }, `HTTP ${status}: the App installation lacks repository Variables write`, { permission: "variables:write", outcome: "denied" });
44873
44916
  if (status === 404) return rejectedWrite(base, { presence: "present", value: baseline.value }, "HTTP 404 at write: the variable vanished after the baseline check (concurrent deletion suspected) \u2014 reconcile read-only");
44874
44917
  if (status === 409 || status === 422) return rejectedWrite(base, { presence: "present", value: baseline.value }, `HTTP ${status}: ${String(e.message).slice(0, 160)}`);
@@ -44940,7 +44983,7 @@ function registerDevopsVarsCommands(program3, deps) {
44940
44983
  }
44941
44984
  const projects = await deps.projects();
44942
44985
  const repo = resolveRepoOrFail(repoArg, projects, verb);
44943
- const ops = { transport: await deps.appClient(commandPath4), ...deps.actor ? { actor: deps.actor() } : {} };
44986
+ const ops = { transport: await deps.appClient(commandPath4), mintPermission: "variables:write", ...deps.actor ? { actor: deps.actor() } : {} };
44944
44987
  const receipt = verb === "create" ? await createVariableReceipt(ops, repo, name, exactValue) : verb === "update" ? await updateVariableReceipt(ops, repo, name, exactValue) : await deleteVariableReceipt(ops, repo, name);
44945
44988
  if (o.json) console.log(JSON.stringify(receipt, null, 2));
44946
44989
  else printMutationText(receipt);
@@ -44960,14 +45003,14 @@ function printReadText(receipt) {
44960
45003
  const p = receipt.absenceProof;
44961
45004
  console.log(`vars get: ${receipt.repo} ${receipt.name} absent \u2014 proven by complete enumeration (${p.pages} page(s), ${p.totalVariables} variable(s), name excluded)`);
44962
45005
  } else {
44963
- console.log(`vars get: ${receipt.repo} ${receipt.name} presence UNKNOWN (${receipt.unknownReason}${receipt.capability ? ", capability denied" : ""}) \u2014 absence is never claimed from this read`);
45006
+ console.log(`vars get: ${receipt.repo} ${receipt.name} presence UNKNOWN (${receipt.unknownReason}${receipt.capability ? `, capability ${receipt.capability.outcome}` : ""}) \u2014 absence is never claimed from this read`);
44964
45007
  }
44965
45008
  return;
44966
45009
  }
44967
45010
  if (receipt.pagination.complete) {
44968
45011
  console.log(`vars list: ${receipt.repo} \u2014 ${receipt.pagination.totalVariables} variable(s), complete (${receipt.pagination.pages} page(s)): ${receipt.variables.map((v) => v.name).join(", ") || "(none)"}`);
44969
45012
  } else {
44970
- console.log(`vars list: ${receipt.repo} \u2014 INCOMPLETE enumeration (${receipt.pagination.reason ?? "unknown"}); the names below are NOT the full set: ${receipt.variables.map((v) => v.name).join(", ") || "(none)"}`);
45013
+ console.log(`vars list: ${receipt.repo} \u2014 INCOMPLETE enumeration (${receipt.pagination.reason ?? "unknown"}${receipt.pagination.detail ? `: ${receipt.pagination.detail}` : ""}); the names below are NOT the full set: ${receipt.variables.map((v) => v.name).join(", ") || "(none)"}`);
44971
45014
  }
44972
45015
  }
44973
45016
  function printMutationText(receipt) {
@@ -44988,12 +45031,17 @@ async function call2(deps, log, method, path2) {
44988
45031
  log.push({ method, path: path2, status: res.status });
44989
45032
  return res;
44990
45033
  } catch (e) {
45034
+ if (e instanceof AppActorError) throw e;
44991
45035
  const status = e instanceof GitHubApiError ? e.status : 0;
44992
45036
  log.push({ method, path: path2, status });
44993
45037
  throw e;
44994
45038
  }
44995
45039
  }
44996
45040
  function classify2(e) {
45041
+ if (e instanceof AppActorError) {
45042
+ const status = e.mintRefusal?.status ?? 0;
45043
+ return { status, reason: "mint-refused", mint: { status, reason: e.mintRefusal?.reason ?? e.message } };
45044
+ }
44997
45045
  if (e instanceof GitHubApiError) {
44998
45046
  if (e.status === 404) return { status: 404, reason: "missing-or-invisible" };
44999
45047
  if (e.status === 403 || e.status === 401) return { status: e.status, reason: "denied" };
@@ -45039,12 +45087,13 @@ async function readArchiveStatusReceipt(deps, repo) {
45039
45087
  window: { ...base.window, endedAt: now().toISOString() }
45040
45088
  };
45041
45089
  } catch (e) {
45042
- const { status, reason } = classify2(e);
45090
+ const { reason, mint } = classify2(e);
45043
45091
  return {
45044
45092
  ...base,
45045
45093
  archived: "unknown",
45046
45094
  unknownReason: reason,
45047
45095
  ...reason === "denied" ? { capability: { permission: "metadata:read", outcome: "denied" } } : {},
45096
+ ...mint ? { capability: { permission: "metadata:read", outcome: "mint-refused", status: mint.status, reason: mint.reason } } : {},
45048
45097
  ...reason === "missing-or-invisible" ? { sourceTime: { ...base.sourceTime, observedAt: now().toISOString(), note: `${ARCHIVE_SOURCE_TIME_NOTE} \u2014 HTTP 404: the repository is missing or not visible to the installation, which is NOT archived:false` } } : { sourceTime: { ...base.sourceTime, observedAt: now().toISOString() } },
45049
45098
  window: { ...base.window, endedAt: now().toISOString() }
45050
45099
  };
@@ -45058,12 +45107,618 @@ function registerRepoArchiveStatusCommand(program3, deps) {
45058
45107
  const repo = resolution.kind === "resolved" ? resolution.repo : resolution.kind === "conflict" ? fail(`repo archive-status: '${o.repo ?? ""}' resolves to MULTIPLE registered repositories (${resolution.candidates.join(", ")}) \u2014 pass the exact owner/repo (#9779)`) : resolution.kind === "unavailable" ? fail("repo archive-status: the Hub project registry is unreadable; refusing to address a repo this command cannot prove registered") : fail(`repo archive-status: '${o.repo ?? ""}' is not a registered repository in the Hub registry \u2014 pass the exact owner/repo`);
45059
45108
  const receipt = await readArchiveStatusReceipt({ transport: await deps.appClient("repo archive-status"), ...deps.actor ? { actor: deps.actor() } : {} }, repo);
45060
45109
  if (o.json) console.log(JSON.stringify(receipt, null, 2));
45061
- else if (receipt.archived === "unknown") console.log(`repo archive-status: ${receipt.repo} \u2014 archived state UNKNOWN (${receipt.unknownReason}) \u2014 never reads as not-archived`);
45110
+ else if (receipt.archived === "unknown") console.log(`repo archive-status: ${receipt.repo} \u2014 archived state UNKNOWN (${receipt.unknownReason}${receipt.capability?.outcome === "mint-refused" ? `: App token mint refused${receipt.capability.status ? ` (HTTP ${receipt.capability.status})` : ""}: ${receipt.capability.reason}` : ""}) \u2014 never reads as not-archived`);
45062
45111
  else console.log(`repo archive-status: ${receipt.repo} \u2014 archived: ${receipt.archived} (observed ${receipt.sourceTime.observedAt}${receipt.sourceTime.providerUpdatedAt ? `, provider updated_at ${receipt.sourceTime.providerUpdatedAt}` : ""})`);
45063
45112
  if (receipt.archived === "unknown") process.exitCode = 1;
45064
45113
  });
45065
45114
  }
45066
45115
 
45116
+ // src/merge-queue-lifecycle.ts
45117
+ var import_node_crypto15 = require("node:crypto");
45118
+ var import_node_fs35 = require("node:fs");
45119
+ var import_node_path32 = require("node:path");
45120
+ init_github_client();
45121
+ init_cli_shared();
45122
+ var LIFECYCLE_SCHEMA = "mmi-cli.merge-queue-lifecycle/1";
45123
+ var COHORT_SCHEMA = "mmi.merge-queue-cohort/1";
45124
+ var LIFECYCLE_SOURCE = {
45125
+ provider: "github-graphql",
45126
+ operation: "PullRequest.timelineItems",
45127
+ itemTypes: ["ADDED_TO_MERGE_QUEUE_EVENT", "REMOVED_FROM_MERGE_QUEUE_EVENT", "MERGED_EVENT", "HEAD_REF_FORCE_PUSHED_EVENT", "BASE_REF_CHANGED_EVENT"],
45128
+ schemaRef: "https://docs.github.com/public/fpt/schema.docs.graphql",
45129
+ version: "merge-queue-lifecycle-source/1",
45130
+ permission: "pull_requests:read"
45131
+ };
45132
+ var LIFECYCLE_QUERY = "query($owner:String!,$name:String!,$number:Int!,$after:String){repository(owner:$owner,name:$name){pullRequest(number:$number){number baseRefName mergedAt mergeCommit{oid} timelineItems(first:100,after:$after,itemTypes:[ADDED_TO_MERGE_QUEUE_EVENT,REMOVED_FROM_MERGE_QUEUE_EVENT,MERGED_EVENT,HEAD_REF_FORCE_PUSHED_EVENT,BASE_REF_CHANGED_EVENT]){pageInfo{hasNextPage endCursor} nodes{__typename ... on AddedToMergeQueueEvent{id createdAt} ... on RemovedFromMergeQueueEvent{id createdAt reason beforeCommit{oid}} ... on MergedEvent{id createdAt commit{oid}} ... on HeadRefForcePushedEvent{id createdAt beforeCommit{oid} afterCommit{oid}} ... on BaseRefChangedEvent{id createdAt previousRefName currentRefName}}}}}}";
45133
+ var UTC_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?Z$/;
45134
+ var SHA_RE2 = /^[0-9a-f]{40}$/;
45135
+ function parseUtc(raw, flag) {
45136
+ if (!UTC_RE.test(raw) || Number.isNaN(Date.parse(raw))) throw new Error(`${flag} must be a UTC timestamp like 2026-11-02T00:00:00Z \u2014 received '${raw.slice(0, 40)}'`);
45137
+ return raw;
45138
+ }
45139
+ function parseBound(raw, flag, max) {
45140
+ if (!/^\d+$/.test(raw) || Number(raw) < 1 || Number(raw) > max) throw new Error(`${flag} must be an integer 1-${max} \u2014 received '${raw.slice(0, 20)}'`);
45141
+ return Number(raw);
45142
+ }
45143
+ function optionalUtc(v, where) {
45144
+ if (v === void 0) return void 0;
45145
+ if (typeof v !== "string") throw new Error(`${where} must be a UTC timestamp string`);
45146
+ return parseUtc(v, where);
45147
+ }
45148
+ function parseCohortManifest(text) {
45149
+ let raw;
45150
+ try {
45151
+ raw = JSON.parse(text);
45152
+ } catch {
45153
+ throw new Error("cohort manifest is not valid JSON");
45154
+ }
45155
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) throw new Error("cohort manifest must be a JSON object");
45156
+ const m = raw;
45157
+ if (m.schema !== COHORT_SCHEMA) throw new Error(`cohort manifest schema must be '${COHORT_SCHEMA}'`);
45158
+ if (typeof m.repo !== "string" || !/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(m.repo)) throw new Error("cohort manifest repo must be owner/repo");
45159
+ if (typeof m.base_branch !== "string" || m.base_branch.trim() === "") throw new Error("cohort manifest base_branch is required");
45160
+ const receipt = m.measurement_receipt;
45161
+ if (!receipt || typeof receipt.id !== "string" || receipt.id.trim() === "" || typeof receipt.sha256 !== "string" || !/^[0-9a-f]{64}$/.test(receipt.sha256)) {
45162
+ throw new Error("cohort manifest measurement_receipt needs an id and a 64-hex sha256 binding the existing measurement");
45163
+ }
45164
+ const since = parseUtc(String(m.since ?? ""), "manifest since");
45165
+ const until = parseUtc(String(m.until ?? ""), "manifest until");
45166
+ if (!Array.isArray(m.prs) || m.prs.length === 0) throw new Error("cohort manifest prs must list explicit PR identities");
45167
+ const seen = /* @__PURE__ */ new Set();
45168
+ const prs = m.prs.map((p, i) => {
45169
+ const row = p;
45170
+ if (!row || !Number.isInteger(row.number) || row.number < 1) throw new Error(`cohort manifest prs[${i}].number must be a positive integer`);
45171
+ const number = row.number;
45172
+ if (seen.has(number)) throw new Error(`cohort manifest lists PR #${number} twice`);
45173
+ seen.add(number);
45174
+ if (row.excluded !== void 0 && (typeof row.excluded !== "string" || row.excluded.trim() === "")) throw new Error(`cohort manifest prs[${i}].excluded must be a reason string`);
45175
+ return { number, ...typeof row.excluded === "string" ? { excluded: row.excluded } : {} };
45176
+ });
45177
+ const runs2 = m.runs === void 0 ? [] : m.runs;
45178
+ if (!Array.isArray(runs2)) throw new Error("cohort manifest runs must be an array");
45179
+ const parsedRuns = runs2.map((r, i) => {
45180
+ const row = r;
45181
+ const where = `cohort manifest runs[${i}]`;
45182
+ if (!row || !Number.isInteger(row.run_id) || !Number.isInteger(row.run_attempt) || row.run_attempt < 1) throw new Error(`${where} needs integer run_id and run_attempt >= 1`);
45183
+ if (typeof row.head_sha !== "string" || !SHA_RE2.test(row.head_sha)) throw new Error(`${where}.head_sha must be a full 40-hex SHA`);
45184
+ if (!Array.isArray(row.members)) throw new Error(`${where}.members must list explicit PR memberships`);
45185
+ const members = row.members.map((mem, j) => {
45186
+ const mm = mem;
45187
+ if (!mm || !Number.isInteger(mm.pr)) throw new Error(`${where}.members[${j}].pr must be an integer`);
45188
+ if (mm.admission_event_id !== void 0 && (typeof mm.admission_event_id !== "string" || mm.admission_event_id === "")) throw new Error(`${where}.members[${j}].admission_event_id must be a provider event id`);
45189
+ return { pr: mm.pr, ...typeof mm.admission_event_id === "string" ? { admission_event_id: mm.admission_event_id } : {} };
45190
+ });
45191
+ const started = optionalUtc(row.started_at, `${where}.started_at`);
45192
+ const failureResult = optionalUtc(row.failure_result_at, `${where}.failure_result_at`);
45193
+ const failureObserved = optionalUtc(row.failure_observed_at, `${where}.failure_observed_at`);
45194
+ return {
45195
+ run_id: row.run_id,
45196
+ run_attempt: row.run_attempt,
45197
+ head_sha: row.head_sha,
45198
+ members,
45199
+ ...started ? { started_at: started } : {},
45200
+ ...failureResult ? { failure_result_at: failureResult } : {},
45201
+ ...failureObserved ? { failure_observed_at: failureObserved } : {}
45202
+ };
45203
+ });
45204
+ return { schema: COHORT_SCHEMA, repo: m.repo, base_branch: m.base_branch, measurement_receipt: { id: receipt.id, sha256: receipt.sha256 }, since, until, prs, runs: parsedRuns };
45205
+ }
45206
+ var oidOf = (v) => v && typeof v === "object" && typeof v.oid === "string" ? v.oid : null;
45207
+ function normalizeNodes(nodes, malformed) {
45208
+ const out = [];
45209
+ for (const node of nodes) {
45210
+ const n = node;
45211
+ if (!n || typeof n.id !== "string" || typeof n.createdAt !== "string") {
45212
+ malformed.push("event-missing-id-or-createdAt");
45213
+ continue;
45214
+ }
45215
+ const base = { id: n.id, at: n.createdAt };
45216
+ switch (n.__typename) {
45217
+ case "AddedToMergeQueueEvent":
45218
+ out.push({ kind: "enqueued", ...base });
45219
+ break;
45220
+ case "RemovedFromMergeQueueEvent":
45221
+ out.push({ kind: "removed", ...base, reason: typeof n.reason === "string" && n.reason !== "" ? n.reason : null, before_head: oidOf(n.beforeCommit) });
45222
+ break;
45223
+ case "MergedEvent":
45224
+ out.push({ kind: "merged", ...base, merge_sha: oidOf(n.commit) });
45225
+ break;
45226
+ case "HeadRefForcePushedEvent":
45227
+ out.push({ kind: "head_changed", ...base, before: oidOf(n.beforeCommit), after: oidOf(n.afterCommit) });
45228
+ break;
45229
+ case "BaseRefChangedEvent":
45230
+ if (typeof n.previousRefName !== "string" || typeof n.currentRefName !== "string") {
45231
+ malformed.push("base-change-missing-refs");
45232
+ break;
45233
+ }
45234
+ out.push({ kind: "base_changed", ...base, from: n.previousRefName, to: n.currentRefName });
45235
+ break;
45236
+ default:
45237
+ malformed.push(`unexpected-typename:${String(n.__typename).slice(0, 40)}`);
45238
+ }
45239
+ }
45240
+ return out;
45241
+ }
45242
+ function classifyError(e) {
45243
+ if (e instanceof GitHubApiError) {
45244
+ if (e.rateLimited) return { code: "rate-limited", status: e.status };
45245
+ const types = (e.graphqlErrors ?? []).map((g) => g.type ?? "");
45246
+ if (e.status === 401 || e.status === 403 || types.includes("FORBIDDEN")) return { code: "permission-denied", status: e.status };
45247
+ if (e.status === 404 || types.includes("NOT_FOUND")) return { code: "not-found-or-masked", status: e.status };
45248
+ if (types.some((t) => t !== "")) return { code: "unsupported-source", status: e.status };
45249
+ if (/did not include data/i.test(e.message)) return { code: "malformed-response", status: e.status };
45250
+ return { code: "transport-error", status: e.status };
45251
+ }
45252
+ const name = e?.name ?? "";
45253
+ if (name === "TimeoutError" || name === "AbortError" || /timeout|timed out|abort/i.test(String(e?.message ?? ""))) return { code: "timeout", status: 0 };
45254
+ return { code: "transport-error", status: 0 };
45255
+ }
45256
+ function classifyRemovalReason(reason, headChangedBeforeReentry) {
45257
+ if (headChangedBeforeReentry) return "head-change";
45258
+ if (reason === null) return "unknown";
45259
+ if (/manual|dequeued by|removed by|user/i.test(reason)) return "manual";
45260
+ if (/fail|timed? ?out|timeout|check|status/i.test(reason)) return "failure-linked";
45261
+ if (/conflict|protection|invalid|base|push|commit|head/i.test(reason)) return "invalidation";
45262
+ return "unknown";
45263
+ }
45264
+ var secondsBetween = (from, to) => {
45265
+ const d = (Date.parse(to) - Date.parse(from)) / 1e3;
45266
+ if (!Number.isFinite(d)) return { value: null, reason: "unparseable-timestamp" };
45267
+ if (d < 0) return { value: null, reason: "negative-duration" };
45268
+ return { value: d };
45269
+ };
45270
+ var ZERO_BOUNCES = () => ({ manual: 0, "failure-linked": 0, "head-change": 0, invalidation: 0, unknown: 0 });
45271
+ function buildPrLifecycle(state, manifest, joins) {
45272
+ const entry = manifest.prs.find((p) => p.number === state.number);
45273
+ const anomalies = [...state.malformed ?? []];
45274
+ const events = state.events.map((e, i) => ({ e, i })).filter(({ e }) => {
45275
+ if (UTC_RE.test(e.at) && !Number.isNaN(Date.parse(e.at))) return true;
45276
+ anomalies.push(`malformed-timestamp:${e.id}`);
45277
+ return false;
45278
+ });
45279
+ for (let k = 1; k < events.length; k++) if (Date.parse(events[k].e.at) < Date.parse(events[k - 1].e.at)) anomalies.push(`out-of-order:${events[k].e.id}`);
45280
+ const ordered = events.sort((a, b) => Date.parse(a.e.at) - Date.parse(b.e.at) || a.i - b.i).map(({ e }) => e);
45281
+ const prBase = state.pr?.base_ref ?? null;
45282
+ const baseChanges = ordered.filter((e) => e.kind === "base_changed");
45283
+ const baseAt = (at) => {
45284
+ const next = baseChanges.find((c) => Date.parse(c.at) > Date.parse(at));
45285
+ if (next) return { value: next.from, reason: "derived-from-base-ref-changed-event" };
45286
+ return prBase === null ? { value: null, reason: "base-unavailable" } : { value: prBase, reason: baseChanges.length ? "derived-from-base-ref-changed-event" : "current-base-no-recorded-change" };
45287
+ };
45288
+ const episodes = [];
45289
+ let open2 = null;
45290
+ let leftCensored = false;
45291
+ let sawQueueEvent = false;
45292
+ let mergedEvent = null;
45293
+ for (const e of ordered) {
45294
+ if (e.kind === "enqueued") {
45295
+ if (open2) {
45296
+ anomalies.push(`admission-without-removal:${e.id}`);
45297
+ episodes.push(open2);
45298
+ }
45299
+ const prev = open2 ?? episodes[episodes.length - 1] ?? null;
45300
+ const headChanged = prev ? ordered.some((h) => h.kind === "head_changed" && Date.parse(h.at) >= Date.parse(prev.terminal.at ?? prev.enqueued_at) && Date.parse(h.at) <= Date.parse(e.at)) : null;
45301
+ open2 = {
45302
+ episode_id: e.id,
45303
+ enqueued_at: e.at,
45304
+ head_sha: { value: null, reason: "source-omits-admission-head (AddedToMergeQueueEvent has no commit field)" },
45305
+ base: baseAt(e.at),
45306
+ terminal: { type: null, at: null, event_id: null, reason: null, reason_class: null, head_at_removal: null },
45307
+ reentry_of: prev && prev.terminal.type === "removed" ? prev.episode_id : null,
45308
+ head_changed_since_previous_episode: headChanged,
45309
+ runs: [],
45310
+ queue_wait_to_execution_seconds: { value: null, reason: "no-explicit-run-join" }
45311
+ };
45312
+ sawQueueEvent = true;
45313
+ } else if (e.kind === "removed") {
45314
+ if (!open2) {
45315
+ if (!sawQueueEvent) leftCensored = true;
45316
+ anomalies.push(`removal-without-admission:${e.id}`);
45317
+ sawQueueEvent = true;
45318
+ continue;
45319
+ }
45320
+ open2.terminal = { type: "removed", at: e.at, event_id: e.id, reason: e.reason, reason_class: null, head_at_removal: e.before_head };
45321
+ episodes.push(open2);
45322
+ open2 = null;
45323
+ } else if (e.kind === "merged") {
45324
+ mergedEvent = e;
45325
+ if (open2) {
45326
+ open2.terminal = { type: "merged", at: e.at, event_id: e.id, reason: null, reason_class: null, head_at_removal: null };
45327
+ episodes.push(open2);
45328
+ open2 = null;
45329
+ }
45330
+ }
45331
+ }
45332
+ if (open2) episodes.push(open2);
45333
+ let bounceCount = 0;
45334
+ const byReason = ZERO_BOUNCES();
45335
+ episodes.forEach((ep, idx) => {
45336
+ if (ep.terminal.type !== "removed") return;
45337
+ const next = episodes[idx + 1];
45338
+ const headChange = next ? next.head_changed_since_previous_episode === true : false;
45339
+ ep.terminal.reason_class = classifyRemovalReason(ep.terminal.reason, headChange);
45340
+ if (next && next.reentry_of === ep.episode_id) {
45341
+ bounceCount++;
45342
+ byReason[ep.terminal.reason_class]++;
45343
+ }
45344
+ });
45345
+ for (const run of manifest.runs) {
45346
+ for (const member of run.members) {
45347
+ if (member.pr !== state.number) continue;
45348
+ if (!member.admission_event_id) continue;
45349
+ const ep = episodes.find((x) => x.episode_id === member.admission_event_id);
45350
+ const join54 = joins.find((j) => j.run_id === run.run_id && j.run_attempt === run.run_attempt && j.pr === member.pr && j.admission_event_id === member.admission_event_id);
45351
+ if (!join54) continue;
45352
+ if (!ep) {
45353
+ if (state.status === "complete") {
45354
+ join54.status = "unresolved";
45355
+ join54.reason = "admission-event-not-in-history";
45356
+ } else {
45357
+ join54.status = "unresolved";
45358
+ join54.reason = "history-incomplete";
45359
+ }
45360
+ continue;
45361
+ }
45362
+ join54.status = "joined";
45363
+ delete join54.reason;
45364
+ ep.runs.push({ run_id: run.run_id, run_attempt: run.run_attempt, head_sha: run.head_sha });
45365
+ }
45366
+ }
45367
+ const runOf = (ep) => manifest.runs.filter((r) => ep.runs.some((x) => x.run_id === r.run_id && x.run_attempt === r.run_attempt));
45368
+ const earliest = (ep, field) => {
45369
+ const vals = runOf(ep).map((r) => r[field]).filter((v) => typeof v === "string").sort((a, b) => Date.parse(a) - Date.parse(b));
45370
+ return vals[0] ?? null;
45371
+ };
45372
+ for (const ep of episodes) {
45373
+ if (!ep.runs.length) continue;
45374
+ const started = earliest(ep, "started_at");
45375
+ ep.queue_wait_to_execution_seconds = started ? secondsBetween(ep.enqueued_at, started) : { value: null, reason: "joined-run-has-no-execution-start" };
45376
+ }
45377
+ const mergedAt = state.pr?.merged_at ?? null;
45378
+ let cohort;
45379
+ let cohortReason;
45380
+ if (entry.excluded || state.status === "excluded") {
45381
+ cohort = "excluded";
45382
+ cohortReason = `manifest-exclusion: ${entry.excluded ?? "excluded"}`;
45383
+ } else if (state.status !== "complete") {
45384
+ cohort = "unknown";
45385
+ cohortReason = state.status === "failed" || state.status === "unsupported" ? `lookup-${state.status}:${state.error?.code ?? "unknown"}` : "history-incomplete";
45386
+ } else if (mergedAt === null) {
45387
+ cohort = "right-censored";
45388
+ cohortReason = "not-merged-pending-or-closed";
45389
+ } else if (Date.parse(mergedAt) >= Date.parse(manifest.until)) {
45390
+ cohort = "right-censored";
45391
+ cohortReason = "merged-at-or-after-until";
45392
+ } else if (Date.parse(mergedAt) < Date.parse(manifest.since)) {
45393
+ cohort = "outside-window";
45394
+ cohortReason = "merged-before-since";
45395
+ } else cohort = "eligible";
45396
+ const blocked = () => {
45397
+ if (state.status === "excluded") return "excluded-by-manifest";
45398
+ if (state.status !== "complete") return state.status === "incomplete" || state.status === "pending" ? "history-incomplete" : `lookup-${state.status}`;
45399
+ if (mergedAt === null) return "not-merged";
45400
+ if (anomalies.some((a) => a.startsWith("malformed") || a.startsWith("event-missing") || a.startsWith("unexpected-typename") || a.startsWith("base-change-missing"))) return "malformed-history";
45401
+ if (anomalies.some((a) => a.startsWith("out-of-order") || a.startsWith("admission-without-removal"))) return "contradictory-ordering";
45402
+ if (mergedEvent && mergedEvent.at !== mergedAt && Date.parse(mergedEvent.at) !== Date.parse(mergedAt)) return "conflicting-merge-time";
45403
+ if (mergedEvent && state.pr?.merge_sha && mergedEvent.merge_sha && mergedEvent.merge_sha !== state.pr.merge_sha) return "conflicting-merge-sha";
45404
+ return null;
45405
+ };
45406
+ const block = blocked();
45407
+ const nul = (reason) => ({ value: null, reason });
45408
+ let first;
45409
+ if (block) first = nul(block);
45410
+ else if (leftCensored) first = nul("left-censored: removal precedes any recorded admission");
45411
+ else if (!episodes.length) first = nul("no-verified-admission-in-history (not proof of queue bypass)");
45412
+ else first = secondsBetween(episodes[0].enqueued_at, mergedAt);
45413
+ const successful = episodes.filter((ep) => ep.terminal.type === "merged");
45414
+ let success;
45415
+ let successEp = null;
45416
+ if (block) success = nul(block);
45417
+ else if (successful.length !== 1) success = nul(successful.length ? "ambiguous-successful-episode" : "no-episode-terminated-by-merge");
45418
+ else {
45419
+ successEp = successful[0];
45420
+ const headMoved = ordered.some((h) => h.kind === "head_changed" && Date.parse(h.at) > Date.parse(successEp.enqueued_at) && Date.parse(h.at) <= Date.parse(mergedAt));
45421
+ success = headMoved ? nul("head-changed-within-successful-episode") : secondsBetween(successEp.enqueued_at, mergedAt);
45422
+ }
45423
+ const wait = block ? nul(block) : successEp ? successEp.queue_wait_to_execution_seconds : nul("no-successful-episode");
45424
+ const failureEp = episodes.find((ep) => earliest(ep, "failure_result_at") !== null);
45425
+ const observedEp = episodes.find((ep) => earliest(ep, "failure_observed_at") !== null);
45426
+ const failureResult = failureEp ? secondsBetween(failureEp.enqueued_at, earliest(failureEp, "failure_result_at")) : nul("no-joined-failure-result-evidence");
45427
+ const failureObserved = observedEp ? secondsBetween(observedEp.enqueued_at, earliest(observedEp, "failure_observed_at")) : nul("no-joined-failure-observation-evidence");
45428
+ const historyUsable = state.status === "complete" && !leftCensored && !(block && block !== "not-merged");
45429
+ const failureBlock = block === "not-merged" ? null : block;
45430
+ return {
45431
+ number: state.number,
45432
+ lookup: state.status,
45433
+ ...state.error ? { error: state.error } : {},
45434
+ cohort,
45435
+ ...cohortReason ? { cohort_reason: cohortReason } : {},
45436
+ merged_at: mergedAt,
45437
+ merge_sha: state.pr?.merge_sha ?? null,
45438
+ base_ref: prBase,
45439
+ head_transitions: ordered.filter((e) => e.kind === "head_changed").map((e) => ({ event_id: e.id, at: e.at, before: e.before, after: e.after })),
45440
+ base_transitions: baseChanges.map((e) => ({ event_id: e.id, at: e.at, from: e.from, to: e.to })),
45441
+ episodes,
45442
+ events: ordered,
45443
+ anomalies,
45444
+ metrics: {
45445
+ first_enqueue_to_merge_seconds: first,
45446
+ successful_episode_enqueue_to_merge_seconds: success,
45447
+ queue_wait_to_execution_seconds: wait,
45448
+ enqueue_to_failure_result_seconds: { ...failureBlock ? nul(failureBlock) : failureResult, semantics: "provider-completion-time" },
45449
+ enqueue_to_first_observed_failure_seconds: { ...failureBlock ? nul(failureBlock) : failureObserved, semantics: "measurement-poll-observation" },
45450
+ bounce_count: historyUsable ? bounceCount : null,
45451
+ bounce_by_reason: historyUsable ? byReason : null
45452
+ }
45453
+ };
45454
+ }
45455
+ function median(values) {
45456
+ if (!values.length) return null;
45457
+ const s = [...values].sort((a, b) => a - b);
45458
+ const mid = Math.floor(s.length / 2);
45459
+ return s.length % 2 ? s[mid] : (s[mid - 1] + s[mid]) / 2;
45460
+ }
45461
+ function summarizeMetric(prs, key, definition, complete) {
45462
+ const values = [];
45463
+ const missing = {};
45464
+ for (const pr of prs) {
45465
+ if (pr.cohort !== "eligible") continue;
45466
+ const m = pr.metrics[key];
45467
+ if (m.value === null) missing[m.reason ?? "unknown"] = (missing[m.reason ?? "unknown"] ?? 0) + 1;
45468
+ else values.push(m.value);
45469
+ }
45470
+ const count = (c) => prs.filter((p) => p.cohort === c).length;
45471
+ const missingTotal = Object.values(missing).reduce((a, b) => a + b, 0);
45472
+ return {
45473
+ definition,
45474
+ label: complete && missingTotal === 0 && count("unknown") === 0 ? "completed-cohort-median" : "available-case-median",
45475
+ median_seconds: median(values),
45476
+ cohort_denominator: prs.length,
45477
+ eligible: values.length,
45478
+ missing_by_reason: missing,
45479
+ right_censored: count("right-censored"),
45480
+ excluded: count("excluded"),
45481
+ outside_window: count("outside-window"),
45482
+ unknown: count("unknown")
45483
+ };
45484
+ }
45485
+ async function readMergeQueueLifecycle(deps, req) {
45486
+ const now = deps.now ?? (() => /* @__PURE__ */ new Date());
45487
+ const started = now();
45488
+ const deadlineMs = started.getTime() + req.deadlineSeconds * 1e3;
45489
+ const [owner, name] = req.repo.split("/");
45490
+ const prior = new Map((req.resume?.prs ?? []).map((p) => [p.number, p]));
45491
+ const states = req.manifest.prs.map((p) => {
45492
+ const was = prior.get(p.number);
45493
+ if (was && was.status !== "incomplete" && was.status !== "pending") return was;
45494
+ if (was) return { ...was, status: "pending" };
45495
+ return { number: p.number, status: "pending", pages: 0, cursors: [], next_cursor: null, events: [], duplicates_dropped: 0 };
45496
+ });
45497
+ let pages = 0;
45498
+ let eventCount = 0;
45499
+ const exhausted = /* @__PURE__ */ new Set();
45500
+ for (const st of states) {
45501
+ if (st.status !== "pending") continue;
45502
+ if (req.manifest.prs.find((p) => p.number === st.number)?.excluded) {
45503
+ st.status = "excluded";
45504
+ continue;
45505
+ }
45506
+ const ids = new Set(st.events.map((e) => e.id));
45507
+ for (; ; ) {
45508
+ if (exhausted.size) break;
45509
+ if (pages >= req.maxPages) {
45510
+ exhausted.add("max-pages");
45511
+ break;
45512
+ }
45513
+ if (eventCount >= req.maxEvents) {
45514
+ exhausted.add("max-events");
45515
+ break;
45516
+ }
45517
+ const remainingMs = deadlineMs - now().getTime();
45518
+ if (remainingMs <= 0) {
45519
+ exhausted.add("deadline");
45520
+ break;
45521
+ }
45522
+ const after = st.next_cursor;
45523
+ let page;
45524
+ try {
45525
+ page = await deps.transport.graphql(LIFECYCLE_QUERY, { owner, name, number: st.number, after }, { timeoutMs: Math.min(6e4, remainingMs) });
45526
+ } catch (e) {
45527
+ const err = classifyError(e);
45528
+ if (err.code === "rate-limited") {
45529
+ exhausted.add("rate-limited");
45530
+ st.error = err;
45531
+ break;
45532
+ }
45533
+ st.status = err.code === "unsupported-source" ? "unsupported" : "failed";
45534
+ st.error = err;
45535
+ break;
45536
+ }
45537
+ pages++;
45538
+ st.pages++;
45539
+ st.cursors.push(after);
45540
+ const pr = page?.repository?.pullRequest;
45541
+ const items = pr?.timelineItems;
45542
+ const hasNext = items?.pageInfo?.hasNextPage;
45543
+ const endCursor = items?.pageInfo?.endCursor;
45544
+ if (!pr || typeof pr.baseRefName !== "string" || !items || !Array.isArray(items.nodes) || typeof hasNext !== "boolean" || hasNext && typeof endCursor !== "string") {
45545
+ st.status = "failed";
45546
+ st.error = { code: pr === null ? "not-found-or-masked" : "malformed-response", status: 200 };
45547
+ break;
45548
+ }
45549
+ st.pr = { base_ref: pr.baseRefName, merged_at: typeof pr.mergedAt === "string" ? pr.mergedAt : null, merge_sha: oidOf(pr.mergeCommit) };
45550
+ const malformed = st.malformed ?? [];
45551
+ for (const ev of normalizeNodes(items.nodes, malformed)) {
45552
+ if (ids.has(ev.id)) {
45553
+ st.duplicates_dropped++;
45554
+ continue;
45555
+ }
45556
+ ids.add(ev.id);
45557
+ st.events.push(ev);
45558
+ eventCount++;
45559
+ }
45560
+ if (malformed.length) st.malformed = malformed;
45561
+ if (!hasNext) {
45562
+ st.status = "complete";
45563
+ st.next_cursor = null;
45564
+ break;
45565
+ }
45566
+ if (endCursor === after) {
45567
+ st.status = "failed";
45568
+ st.error = { code: "malformed-response", status: 200 };
45569
+ break;
45570
+ }
45571
+ st.next_cursor = endCursor;
45572
+ }
45573
+ if (st.status === "pending") st.status = "incomplete";
45574
+ }
45575
+ const joins = [];
45576
+ const cohortNumbers = new Set(req.manifest.prs.map((p) => p.number));
45577
+ for (const run of req.manifest.runs) {
45578
+ if (!run.members.length) {
45579
+ joins.push({ run_id: run.run_id, run_attempt: run.run_attempt, pr: null, admission_event_id: null, status: "unresolved", reason: "no-explicit-pr-membership" });
45580
+ continue;
45581
+ }
45582
+ const prsInRun = run.members.map((m) => m.pr);
45583
+ for (const member of run.members) {
45584
+ const base = { run_id: run.run_id, run_attempt: run.run_attempt, pr: member.pr, admission_event_id: member.admission_event_id ?? null };
45585
+ if (!cohortNumbers.has(member.pr)) joins.push({ ...base, status: "unresolved", reason: "pr-not-in-cohort" });
45586
+ else if (prsInRun.filter((n) => n === member.pr).length > 1) joins.push({ ...base, status: "unresolved", reason: "conflicting-membership" });
45587
+ else if (!member.admission_event_id) joins.push({ ...base, status: "unresolved", reason: "no-explicit-episode-identity" });
45588
+ else joins.push({ ...base, status: "unresolved", reason: "pending-history" });
45589
+ }
45590
+ }
45591
+ const joinable = {
45592
+ ...req.manifest,
45593
+ runs: req.manifest.runs.map((r) => ({ ...r, members: r.members.filter((m) => joins.some((j) => j.run_id === r.run_id && j.run_attempt === r.run_attempt && j.pr === m.pr && j.reason === "pending-history")) }))
45594
+ };
45595
+ const prs = states.map((st) => buildPrLifecycle(st, joinable, joins));
45596
+ for (const j of joins) if (j.reason === "pending-history") j.reason = "history-incomplete";
45597
+ const complete = !exhausted.size && states.every((s) => s.status === "complete" || s.status === "excluded");
45598
+ const bounceTotals = ZERO_BOUNCES();
45599
+ let bounceTotal = 0;
45600
+ let bouncePrs = 0;
45601
+ for (const pr of prs) {
45602
+ if (pr.metrics.bounce_count === null || pr.cohort === "excluded") continue;
45603
+ bouncePrs++;
45604
+ bounceTotal += pr.metrics.bounce_count;
45605
+ for (const [k, v] of Object.entries(pr.metrics.bounce_by_reason)) bounceTotals[k] += v;
45606
+ }
45607
+ return {
45608
+ schema: LIFECYCLE_SCHEMA,
45609
+ repo: req.repo,
45610
+ provider: "github.com",
45611
+ base_branch: req.manifest.base_branch,
45612
+ cohort: { manifest_sha256: req.manifestSha256, measurement_receipt: req.manifest.measurement_receipt, pr_count: req.manifest.prs.length, run_count: req.manifest.runs.length },
45613
+ window: { since: req.since, until: req.until, semantics: "half-open [since, until) over merged_at; queue history is read from each PR timeline start, not truncated at since" },
45614
+ observation: { started_at: started.toISOString(), ended_at: now().toISOString() },
45615
+ source: LIFECYCLE_SOURCE,
45616
+ actor: deps.actor ?? { credential: "app_installation" },
45617
+ correlation_id: (deps.correlationId ?? import_node_crypto15.randomUUID)(),
45618
+ bounds: { max_pages: req.maxPages, max_events: req.maxEvents, deadline_seconds: req.deadlineSeconds },
45619
+ counts: { pages, events: eventCount, duplicates_dropped: states.reduce((a, s) => a + s.duplicates_dropped, 0) },
45620
+ complete,
45621
+ exhausted: [...exhausted],
45622
+ continuation: complete ? null : {
45623
+ repo: req.repo,
45624
+ manifest_sha256: req.manifestSha256,
45625
+ since: req.since,
45626
+ until: req.until,
45627
+ source_version: LIFECYCLE_SOURCE.version,
45628
+ remaining: states.filter((s) => s.status === "incomplete" || s.status === "pending").map((s) => ({ pr: s.number, after: s.next_cursor }))
45629
+ },
45630
+ resumed_from: req.resume?.receipt_sha256 ?? null,
45631
+ prs,
45632
+ unresolved_joins: joins.filter((j) => j.status === "unresolved"),
45633
+ joins,
45634
+ medians: {
45635
+ first_enqueue_to_merge_seconds: summarizeMetric(prs, "first_enqueue_to_merge_seconds", "merged_at minus the first verified admission of the complete PR queue journey (re-entries included)", complete),
45636
+ successful_episode_enqueue_to_merge_seconds: summarizeMetric(prs, "successful_episode_enqueue_to_merge_seconds", "merged_at minus the admission of the unique episode terminated by the merge", complete),
45637
+ queue_wait_to_execution_seconds: summarizeMetric(prs, "queue_wait_to_execution_seconds", "successful-episode admission to the earliest execution start of an explicitly joined run", complete)
45638
+ },
45639
+ bounces: { total: bounceTotal, by_reason: bounceTotals, prs_counted: bouncePrs },
45640
+ checkpoint: { prs: states }
45641
+ };
45642
+ }
45643
+ var sha256 = (text) => (0, import_node_crypto15.createHash)("sha256").update(text).digest("hex");
45644
+ function writeLifecycleReceipt(path2, receipt) {
45645
+ const text = `${JSON.stringify(receipt, null, 2)}
45646
+ `;
45647
+ (0, import_node_fs35.mkdirSync)((0, import_node_path32.dirname)(path2), { recursive: true });
45648
+ (0, import_node_fs35.writeFileSync)(path2, text, { encoding: "utf8", flag: "wx" });
45649
+ const back = (0, import_node_fs35.readFileSync)(path2);
45650
+ if (back.toString("utf8") !== text) throw new Error(`receipt read-back mismatch at ${path2}`);
45651
+ return { path: path2, sha256: sha256(back), bytes: back.length };
45652
+ }
45653
+ function loadCheckpoint(text, scope) {
45654
+ let parsed;
45655
+ try {
45656
+ parsed = JSON.parse(text);
45657
+ } catch {
45658
+ throw new Error("checkpoint receipt is not valid JSON");
45659
+ }
45660
+ const r = parsed.result;
45661
+ if (!r || r.schema !== LIFECYCLE_SCHEMA || !r.checkpoint || !Array.isArray(r.checkpoint.prs)) throw new Error("checkpoint is not a merge-queue lifecycle receipt");
45662
+ const mismatch = [];
45663
+ if (r.repo.toLowerCase() !== scope.repo.toLowerCase()) mismatch.push("repo");
45664
+ if (r.cohort?.manifest_sha256 !== scope.manifestSha256) mismatch.push("cohort manifest");
45665
+ if (r.window?.since !== scope.since || r.window?.until !== scope.until) mismatch.push("window");
45666
+ if (r.source?.version !== LIFECYCLE_SOURCE.version) mismatch.push("source version");
45667
+ if (mismatch.length) throw new Error(`checkpoint scope mismatch (${mismatch.join(", ")}) \u2014 a continuation is bound to the same repository, cohort, window and source; it never widens scope`);
45668
+ return { receipt_sha256: sha256(text), prs: r.checkpoint.prs };
45669
+ }
45670
+ var LIFECYCLE_COMMAND_PATH = "read merge-queue lifecycle";
45671
+ function registerMergeQueueLifecycleCommand(readRoot, deps) {
45672
+ readRoot.command("merge-queue").description("historical merge-queue reads (#9788)").command("lifecycle").description("typed per-PR merge-queue history for a retained cohort: one fixed GraphQL timeline query on the App pull_requests:read profile, bounded, receipt-bound; unknown values are null with a reason, never zero (#9788)").requiredOption("--repo <owner/repo>", "registered repository (owner/repo, repo name, or one unambiguous project alias)").requiredOption("--input <manifest>", `retained cohort manifest JSON (${COHORT_SCHEMA})`).requiredOption("--since <utc>", "window start, inclusive (UTC, e.g. 2026-11-02T00:00:00Z)").requiredOption("--until <utc>", "window end, exclusive (UTC)").requiredOption("--max-pages <n>", "page bound across the cohort (1-1000)").requiredOption("--max-events <n>", "event bound across the cohort (1-100000)").requiredOption("--deadline-seconds <n>", "wall-clock bound (1-3600)").requiredOption("--receipt <new-path>", "new append-only receipt file (refused if it exists)").option("--resume <prior-receipt>", "continue an incomplete read from its receipt (same repo, cohort, window and source only)").option("--json", "machine-readable result").action(async (o) => {
45673
+ const label = "read merge-queue lifecycle";
45674
+ let since, until, maxPages, maxEvents, deadlineSeconds, manifest, manifestText;
45675
+ try {
45676
+ since = parseUtc(o.since, "--since");
45677
+ until = parseUtc(o.until, "--until");
45678
+ if (Date.parse(since) >= Date.parse(until)) throw new Error("--since must be earlier than --until (half-open window)");
45679
+ maxPages = parseBound(o.maxPages, "--max-pages", 1e3);
45680
+ maxEvents = parseBound(o.maxEvents, "--max-events", 1e5);
45681
+ deadlineSeconds = parseBound(o.deadlineSeconds, "--deadline-seconds", 3600);
45682
+ manifestText = (0, import_node_fs35.readFileSync)(o.input, "utf8");
45683
+ manifest = parseCohortManifest(manifestText);
45684
+ if (manifest.since !== since || manifest.until !== until) throw new Error("--since/--until must equal the manifest window exactly; the authorized window is never widened or narrowed silently");
45685
+ } catch (e) {
45686
+ return fail(`${label}: ${e.message}`);
45687
+ }
45688
+ const resolution = resolveCanonicalRepo(o.repo, await deps.projects());
45689
+ const repo = resolution.kind === "resolved" ? resolution.repo : resolution.kind === "conflict" ? fail(`${label}: '${o.repo}' resolves to MULTIPLE registered repositories (${resolution.candidates.join(", ")}) \u2014 pass the exact owner/repo`) : resolution.kind === "unavailable" ? fail(`${label}: the Hub project registry is unreadable; refusing to address a repo this command cannot prove registered`) : fail(`${label}: '${o.repo}' is not a registered repository in the Hub registry \u2014 pass the exact owner/repo`);
45690
+ if (manifest.repo.toLowerCase() !== repo.toLowerCase()) return fail(`${label}: manifest repo ${manifest.repo} does not match --repo ${repo}; refusing before dispatch`);
45691
+ const manifestSha256 = sha256(manifestText);
45692
+ let resume;
45693
+ try {
45694
+ if (o.resume) resume = loadCheckpoint((0, import_node_fs35.readFileSync)(o.resume, "utf8"), { repo, manifestSha256, since, until });
45695
+ } catch (e) {
45696
+ return fail(`${label}: ${e.message}`);
45697
+ }
45698
+ const request = { repo, input: o.input, since, until, max_pages: maxPages, max_events: maxEvents, deadline_seconds: deadlineSeconds, resume: o.resume ?? null };
45699
+ const result = await readMergeQueueLifecycle(
45700
+ { transport: await deps.appClient(LIFECYCLE_COMMAND_PATH), ...deps.actor ? { actor: deps.actor() } : {} },
45701
+ { repo, since, until, maxPages, maxEvents, deadlineSeconds, manifestSha256, manifest, ...resume ? { resume } : {} }
45702
+ );
45703
+ let receipt;
45704
+ try {
45705
+ receipt = writeLifecycleReceipt(o.receipt, { request, result });
45706
+ } catch (e) {
45707
+ const code = e.code;
45708
+ return fail(`${label}: receipt not written (${code === "EEXIST" ? "path exists \u2014 receipts are append-only, pass a new path" : code ?? "write failed"})`);
45709
+ }
45710
+ const output = { ...result, receipt };
45711
+ if (o.json) console.log(JSON.stringify(output, null, 2));
45712
+ else {
45713
+ const m = result.medians;
45714
+ console.log(`${label}: ${repo} ${since}..${until} \u2014 ${result.complete ? "complete" : `INCOMPLETE (${result.exhausted.join(", ") || "lookup failures"})`}; pages ${result.counts.pages}, events ${result.counts.events}`);
45715
+ for (const [k, s] of Object.entries(m)) console.log(` ${k}: ${s.median_seconds ?? "UNKNOWN"} (${s.label}; eligible ${s.eligible}/${s.cohort_denominator}, right-censored ${s.right_censored}, unknown ${s.unknown})`);
45716
+ console.log(` bounces: ${result.bounces.total}; unresolved joins: ${result.unresolved_joins.length}; receipt ${receipt.path} sha256 ${receipt.sha256}`);
45717
+ }
45718
+ if (!result.complete) process.exitCode = 1;
45719
+ });
45720
+ }
45721
+
45067
45722
  // src/board-commands.ts
45068
45723
  init_cli_shared();
45069
45724
 
@@ -45691,9 +46346,9 @@ function registerBoardCommands(program3) {
45691
46346
  }
45692
46347
 
45693
46348
  // src/bootstrap-commands.ts
45694
- var import_node_fs38 = require("node:fs");
46349
+ var import_node_fs39 = require("node:fs");
45695
46350
  var import_node_os21 = require("node:os");
45696
- var import_node_path35 = require("node:path");
46351
+ var import_node_path36 = require("node:path");
45697
46352
  init_cli_shared();
45698
46353
  init_clean_exit();
45699
46354
 
@@ -45878,8 +46533,8 @@ init_github_client();
45878
46533
  init_cli_shared();
45879
46534
 
45880
46535
  // src/port-registry.ts
45881
- var import_node_fs35 = require("node:fs");
45882
- var import_node_path32 = require("node:path");
46536
+ var import_node_fs36 = require("node:fs");
46537
+ var import_node_path33 = require("node:path");
45883
46538
 
45884
46539
  // ../infra/port-geometry.mjs
45885
46540
  var PORT_BLOCK = 100;
@@ -45893,8 +46548,8 @@ function nextPortBlock(registry2) {
45893
46548
  return [base, base + PORT_SPAN];
45894
46549
  }
45895
46550
  function loadPortRegistry(path2) {
45896
- if (!(0, import_node_fs35.existsSync)(path2)) return {};
45897
- const raw = JSON.parse((0, import_node_fs35.readFileSync)(path2, "utf8"));
46551
+ if (!(0, import_node_fs36.existsSync)(path2)) return {};
46552
+ const raw = JSON.parse((0, import_node_fs36.readFileSync)(path2, "utf8"));
45898
46553
  const out = {};
45899
46554
  for (const [key, value] of Object.entries(raw)) {
45900
46555
  if (Array.isArray(value) && value.length === 2 && value.every((n) => typeof n === "number")) {
@@ -45922,22 +46577,22 @@ function existingPortRange(repo, registry2) {
45922
46577
  return registry2[repo] ?? null;
45923
46578
  }
45924
46579
  function portRangeInfraAt(root, source) {
45925
- const registryPath = (0, import_node_path32.join)(root, "infra", "port-ranges.json");
45926
- const ddbScriptPath = (0, import_node_path32.join)(root, "infra", "port-ddb.mjs");
45927
- if (!(0, import_node_fs35.existsSync)(registryPath) || !(0, import_node_fs35.existsSync)(ddbScriptPath)) return null;
46580
+ const registryPath = (0, import_node_path33.join)(root, "infra", "port-ranges.json");
46581
+ const ddbScriptPath = (0, import_node_path33.join)(root, "infra", "port-ddb.mjs");
46582
+ if (!(0, import_node_fs36.existsSync)(registryPath) || !(0, import_node_fs36.existsSync)(ddbScriptPath)) return null;
45928
46583
  return { root, source, registryPath, ddbScriptPath };
45929
46584
  }
45930
46585
  function resolvePortRangeInfra(cwd, packageDir) {
45931
46586
  const direct = portRangeInfraAt(cwd, "cwd");
45932
46587
  if (direct) return direct;
45933
- for (let dir = cwd; ; dir = (0, import_node_path32.dirname)(dir)) {
45934
- const sibling = portRangeInfraAt((0, import_node_path32.join)(dir, "MMI-Hub"), "sibling-hub");
46588
+ for (let dir = cwd; ; dir = (0, import_node_path33.dirname)(dir)) {
46589
+ const sibling = portRangeInfraAt((0, import_node_path33.join)(dir, "MMI-Hub"), "sibling-hub");
45935
46590
  if (sibling) return sibling;
45936
- const parent = (0, import_node_path32.dirname)(dir);
46591
+ const parent = (0, import_node_path33.dirname)(dir);
45937
46592
  if (parent === dir) break;
45938
46593
  }
45939
46594
  if (packageDir) {
45940
- const pkgRoot = (0, import_node_path32.join)(packageDir, "..", "..");
46595
+ const pkgRoot = (0, import_node_path33.join)(packageDir, "..", "..");
45941
46596
  const pkgFrom = portRangeInfraAt(pkgRoot, "pkg-root");
45942
46597
  if (pkgFrom) return pkgFrom;
45943
46598
  }
@@ -45954,7 +46609,7 @@ async function ensurePortRangeAtomic(repo, path2, allocate) {
45954
46609
  } catch (e) {
45955
46610
  const why = e.message;
45956
46611
  const missing = /@aws-sdk\/[\w-]+/.exec(why)?.[0];
45957
- throw new Error(`DDB port allocator failed for ${repo} \u2014 refusing to fall back to the frozen ${path2} (it would hand out a block the cursor already gave away). ` + (missing && /cannot find|not found|ERR_MODULE_NOT_FOUND/i.test(why) ? `Missing dependency ${missing}: run \`npm ci\` in ${(0, import_node_path32.dirname)(path2)}, then retry.` : "Check AWS credentials and that the registry table is reachable, then retry.") + ` Cause: ${why}`);
46612
+ throw new Error(`DDB port allocator failed for ${repo} \u2014 refusing to fall back to the frozen ${path2} (it would hand out a block the cursor already gave away). ` + (missing && /cannot find|not found|ERR_MODULE_NOT_FOUND/i.test(why) ? `Missing dependency ${missing}: run \`npm ci\` in ${(0, import_node_path33.dirname)(path2)}, then retry.` : "Check AWS credentials and that the registry table is reachable, then retry.") + ` Cause: ${why}`);
45958
46613
  }
45959
46614
  }
45960
46615
 
@@ -46006,13 +46661,13 @@ async function assignPersistedPortRange(repo, slug, reg, opts) {
46006
46661
  }
46007
46662
 
46008
46663
  // src/box-commands.ts
46009
- var import_node_fs37 = require("node:fs");
46664
+ var import_node_fs38 = require("node:fs");
46010
46665
  var import_node_child_process12 = require("node:child_process");
46011
46666
  var import_node_os20 = require("node:os");
46012
- var import_node_path34 = require("node:path");
46667
+ var import_node_path35 = require("node:path");
46013
46668
 
46014
46669
  // src/box-control.ts
46015
- var import_node_crypto15 = require("node:crypto");
46670
+ var import_node_crypto16 = require("node:crypto");
46016
46671
  var MANIFEST_PATH = "infra/box-control/manifest.json";
46017
46672
  var ALLOWED_POST = /* @__PURE__ */ new Set(["systemctl daemon-reload"]);
46018
46673
  var sshdPost = (dest) => `if sshd -t; then systemctl reload ssh; else rm -f ${dest}; echo 'sshd -t rejected ${dest}: removed, not reloaded' >&2; exit 1; fi`;
@@ -46116,7 +46771,7 @@ function laneCoverageAction(files, box, op) {
46116
46771
  return { notice: `LANE_COVERAGE: ${mismatch}` };
46117
46772
  }
46118
46773
  function md5(content) {
46119
- return (0, import_node_crypto15.createHash)("md5").update(content).digest("hex");
46774
+ return (0, import_node_crypto16.createHash)("md5").update(content).digest("hex");
46120
46775
  }
46121
46776
  function planRemoteScript(dests) {
46122
46777
  const lines2 = ["set -u"];
@@ -46234,8 +46889,8 @@ init_clean_exit();
46234
46889
  init_cli_shared();
46235
46890
 
46236
46891
  // src/secrets-commands.ts
46237
- var import_node_fs36 = require("node:fs");
46238
- var import_node_path33 = require("node:path");
46892
+ var import_node_fs37 = require("node:fs");
46893
+ var import_node_path34 = require("node:path");
46239
46894
  var import_node_os19 = require("node:os");
46240
46895
  init_cli_shared();
46241
46896
  init_hub_auth();
@@ -47432,18 +48087,18 @@ function collectMap(value, previous = []) {
47432
48087
  return [...previous, value];
47433
48088
  }
47434
48089
  async function decryptRailsCredentials(input) {
47435
- const appDir = (0, import_node_path33.resolve)(input.appDir ?? process.cwd());
48090
+ const appDir = (0, import_node_path34.resolve)(input.appDir ?? process.cwd());
47436
48091
  const credentialsFile = input.credentialsFile ?? DEFAULT_RAILS_CREDENTIALS_FILE;
47437
48092
  const masterKeyFile = input.masterKeyFile ?? DEFAULT_RAILS_MASTER_KEY_FILE;
47438
- const credentialsPath = (0, import_node_path33.resolve)(appDir, credentialsFile);
47439
- const masterKeyPath = (0, import_node_path33.resolve)(appDir, masterKeyFile);
48093
+ const credentialsPath = (0, import_node_path34.resolve)(appDir, credentialsFile);
48094
+ const masterKeyPath = (0, import_node_path34.resolve)(appDir, masterKeyFile);
47440
48095
  const env = {
47441
48096
  ...process.env,
47442
48097
  MMI_RAILS_CREDENTIALS_FILE: credentialsPath,
47443
48098
  MMI_RAILS_MASTER_KEY_FILE: masterKeyPath
47444
48099
  };
47445
- if ((0, import_node_fs36.existsSync)(masterKeyPath)) {
47446
- env.RAILS_MASTER_KEY = (0, import_node_fs36.readFileSync)(masterKeyPath, "utf8").trim();
48100
+ if ((0, import_node_fs37.existsSync)(masterKeyPath)) {
48101
+ env.RAILS_MASTER_KEY = (0, import_node_fs37.readFileSync)(masterKeyPath, "utf8").trim();
47447
48102
  }
47448
48103
  const script = [
47449
48104
  'require "json"',
@@ -47453,9 +48108,9 @@ async function decryptRailsCredentials(input) {
47453
48108
  'config = ActiveSupport::EncryptedConfiguration.new(config_path: config_path, key_path: key_path, env_key: "RAILS_MASTER_KEY", raise_if_missing_key: true)',
47454
48109
  "puts JSON.generate(config.config)"
47455
48110
  ].join("\n");
47456
- const scriptDir = (0, import_node_fs36.mkdtempSync)((0, import_node_path33.join)((0, import_node_os19.tmpdir)(), "mmi-rails-decrypt-"));
47457
- const scriptPath = (0, import_node_path33.join)(scriptDir, "decrypt.rb");
47458
- (0, import_node_fs36.writeFileSync)(scriptPath, script, "utf8");
48111
+ const scriptDir = (0, import_node_fs37.mkdtempSync)((0, import_node_path34.join)((0, import_node_os19.tmpdir)(), "mmi-rails-decrypt-"));
48112
+ const scriptPath = (0, import_node_path34.join)(scriptDir, "decrypt.rb");
48113
+ (0, import_node_fs37.writeFileSync)(scriptPath, script, "utf8");
47459
48114
  try {
47460
48115
  const args = ["exec", "ruby", scriptPath];
47461
48116
  const cmd = process.platform === "win32" ? "cmd.exe" : "bundle";
@@ -47467,7 +48122,7 @@ async function decryptRailsCredentials(input) {
47467
48122
  });
47468
48123
  return JSON.parse(stdout);
47469
48124
  } finally {
47470
- (0, import_node_fs36.rmSync)(scriptDir, { recursive: true, force: true });
48125
+ (0, import_node_fs37.rmSync)(scriptDir, { recursive: true, force: true });
47471
48126
  }
47472
48127
  }
47473
48128
  async function readSecretStdin() {
@@ -47557,7 +48212,7 @@ function registerSecretsCommands(program3) {
47557
48212
  let body;
47558
48213
  if (o.file) {
47559
48214
  try {
47560
- body = (0, import_node_fs36.readFileSync)((0, import_node_path33.resolve)(o.file), "utf8");
48215
+ body = (0, import_node_fs37.readFileSync)((0, import_node_path34.resolve)(o.file), "utf8");
47561
48216
  } catch (e) {
47562
48217
  return fail(`secrets org-catalog: cannot read --file ${o.file}: ${e.message}`);
47563
48218
  }
@@ -47693,7 +48348,7 @@ function registerSecretsCommands(program3) {
47693
48348
  {
47694
48349
  ...d,
47695
48350
  decryptRailsCredentials,
47696
- removeFile: (path2) => (0, import_node_fs36.unlinkSync)((0, import_node_path33.resolve)(o.appDir ?? process.cwd(), path2))
48351
+ removeFile: (path2) => (0, import_node_fs37.unlinkSync)((0, import_node_path34.resolve)(o.appDir ?? process.cwd(), path2))
47697
48352
  },
47698
48353
  {
47699
48354
  repo: o.repo,
@@ -48009,7 +48664,7 @@ function registerBoxCommands(program3) {
48009
48664
  const stdinSource = o.body ? `"${o.body}"` : "<body.sh>";
48010
48665
  const wroteScript = o.ssh && o.script ? o.script : null;
48011
48666
  const runLine = wroteScript ? useStdin ? `bash "${wroteScript}" < ${stdinSource}` : `bash "${wroteScript}"` : void 0;
48012
- if (wroteScript) (0, import_node_fs37.writeFileSync)(wroteScript, sshRecipeScript(found, remote), "utf8");
48667
+ if (wroteScript) (0, import_node_fs38.writeFileSync)(wroteScript, sshRecipeScript(found, remote), "utf8");
48013
48668
  if (o.json) {
48014
48669
  console.log(JSON.stringify({
48015
48670
  box: found,
@@ -48136,17 +48791,17 @@ function hubGitShow(ref) {
48136
48791
  return (path2) => git3(["show", `${ref}:${path2}`]);
48137
48792
  }
48138
48793
  function runOnBox(found, script) {
48139
- const dir = (0, import_node_fs37.mkdtempSync)((0, import_node_path34.join)((0, import_node_os20.tmpdir)(), "mmi-box-control-"));
48794
+ const dir = (0, import_node_fs38.mkdtempSync)((0, import_node_path35.join)((0, import_node_os20.tmpdir)(), "mmi-box-control-"));
48140
48795
  try {
48141
- const recipe = (0, import_node_path34.join)(dir, "recipe.sh");
48142
- (0, import_node_fs37.writeFileSync)(recipe, sshRecipeScript(found, "sh -s"), "utf8");
48796
+ const recipe = (0, import_node_path35.join)(dir, "recipe.sh");
48797
+ (0, import_node_fs38.writeFileSync)(recipe, sshRecipeScript(found, "sh -s"), "utf8");
48143
48798
  const r = (0, import_node_child_process12.spawnSync)("bash", [recipe], { input: script, encoding: "utf8", maxBuffer: 16 * 1024 * 1024, timeout: 18e4, windowsHide: true });
48144
48799
  if (r.status !== 0) {
48145
48800
  throw new UnreadableError(`box control: the ssh session to ${found.name} failed (exit ${r.status ?? r.signal}): ${(r.stderr || "").trim().split("\n").slice(-3).join(" | ")}`);
48146
48801
  }
48147
48802
  return r.stdout;
48148
48803
  } finally {
48149
- (0, import_node_fs37.rmSync)(dir, { recursive: true, force: true });
48804
+ (0, import_node_fs38.rmSync)(dir, { recursive: true, force: true });
48150
48805
  }
48151
48806
  }
48152
48807
  function formatControl(mode, box, rows) {
@@ -48274,7 +48929,7 @@ function renderDeployPortDoctor(report) {
48274
48929
  }
48275
48930
 
48276
48931
  // src/bootstrap-drift.ts
48277
- var import_node_crypto16 = require("node:crypto");
48932
+ var import_node_crypto17 = require("node:crypto");
48278
48933
  function byteComparableSeeds(manifest, cls) {
48279
48934
  return manifest.seeds.filter((s) => s.ownership === "org" && s.source === "self" && s.classes.includes(cls));
48280
48935
  }
@@ -48294,7 +48949,7 @@ function compareSeedBytes(hubContent, repoContent) {
48294
48949
  return normalize(hubContent) === normalize(repoContent) ? "match" : "drift";
48295
48950
  }
48296
48951
  function seedContentHash(content) {
48297
- return (0, import_node_crypto16.createHash)("sha256").update(content.replace(/\r\n/g, "\n"), "utf8").digest("hex");
48952
+ return (0, import_node_crypto17.createHash)("sha256").update(content.replace(/\r\n/g, "\n"), "utf8").digest("hex");
48298
48953
  }
48299
48954
  function auditRepoSeedDrift(repo, seeds, hubContents, repoReads) {
48300
48955
  const byTarget = new Map(repoReads.map((r) => [r.target, r.content]));
@@ -49544,7 +50199,7 @@ function renderBootstrapVerifyReport(report) {
49544
50199
  }
49545
50200
 
49546
50201
  // src/bootstrap-seed-delivery.ts
49547
- var import_node_crypto17 = require("node:crypto");
50202
+ var import_node_crypto18 = require("node:crypto");
49548
50203
  init_github_client();
49549
50204
  var SHA = /^[a-f0-9]{40}$/;
49550
50205
  async function readSeedFile(repo, target, ref, client) {
@@ -49577,7 +50232,7 @@ async function targetedSeedPlan(repo, slug, target, baseBranch, sourceSha, clien
49577
50232
  }
49578
50233
  const baseSha = base?.object?.sha;
49579
50234
  if (!baseSha || !SHA.test(baseSha) || !SHA.test(sourceSha)) throw new Error("bootstrap apply: seed identity is unreadable");
49580
- const identity = (0, import_node_crypto17.createHash)("sha256").update(JSON.stringify([repo.toLowerCase(), target, baseSha, sourceSha])).digest("hex").slice(0, 24);
50235
+ const identity = (0, import_node_crypto18.createHash)("sha256").update(JSON.stringify([repo.toLowerCase(), target, baseSha, sourceSha])).digest("hex").slice(0, 24);
49581
50236
  const delivery = planSeedDelivery(rules, slug, baseBranch, `bootstrap-seed-${identity}`);
49582
50237
  const scoped = forcePr && delivery.mode === "direct" ? { mode: "pr", ref: `bootstrap-seed-${identity}-${slug}`, branch: `bootstrap-seed-${identity}-${slug}`, reason: "registry gate render requires review" } : delivery;
49583
50238
  return { ...scoped, baseSha, baseBranch };
@@ -49647,12 +50302,12 @@ var declaredProjectAdmins = (meta) => {
49647
50302
  };
49648
50303
  var execGitForSeedSource = async (args) => (await execFileP("git", args, { timeout: GIT_TIMEOUT_MS })).stdout;
49649
50304
  async function gateRenderDiff(current, rendered) {
49650
- const dir = (0, import_node_fs38.mkdtempSync)((0, import_node_path35.join)((0, import_node_os21.tmpdir)(), "mmi-gate-diff-"));
49651
- const before = (0, import_node_path35.join)(dir, "committed-gate.yml");
49652
- const after = (0, import_node_path35.join)(dir, "registry-render.yml");
50305
+ const dir = (0, import_node_fs39.mkdtempSync)((0, import_node_path36.join)((0, import_node_os21.tmpdir)(), "mmi-gate-diff-"));
50306
+ const before = (0, import_node_path36.join)(dir, "committed-gate.yml");
50307
+ const after = (0, import_node_path36.join)(dir, "registry-render.yml");
49653
50308
  try {
49654
- (0, import_node_fs38.writeFileSync)(before, current);
49655
- (0, import_node_fs38.writeFileSync)(after, rendered);
50309
+ (0, import_node_fs39.writeFileSync)(before, current);
50310
+ (0, import_node_fs39.writeFileSync)(after, rendered);
49656
50311
  try {
49657
50312
  return (await execFileP("git", ["diff", "--no-index", "--", before, after], { timeout: GIT_TIMEOUT_MS })).stdout;
49658
50313
  } catch (e) {
@@ -49661,7 +50316,7 @@ async function gateRenderDiff(current, rendered) {
49661
50316
  throw e;
49662
50317
  }
49663
50318
  } finally {
49664
- (0, import_node_fs38.rmSync)(dir, { recursive: true, force: true });
50319
+ (0, import_node_fs39.rmSync)(dir, { recursive: true, force: true });
49665
50320
  }
49666
50321
  }
49667
50322
  function bootstrapTrainDoctorDeps() {
@@ -49682,9 +50337,9 @@ async function propagationScope(classes, what) {
49682
50337
  }
49683
50338
  const rosterRepos = collectRegistryRepos(projects).filter((r) => r.toLowerCase() !== "mutmutco/mmi-hub");
49684
50339
  let independentCount = rosterRepos.length;
49685
- if ((0, import_node_fs38.existsSync)("projects.json")) {
50340
+ if ((0, import_node_fs39.existsSync)("projects.json")) {
49686
50341
  try {
49687
- const local = JSON.parse((0, import_node_fs38.readFileSync)("projects.json", "utf8"));
50342
+ const local = JSON.parse((0, import_node_fs39.readFileSync)("projects.json", "utf8"));
49688
50343
  const localRepos = /* @__PURE__ */ new Set();
49689
50344
  for (const p of local.projects ?? []) for (const r of p.repos ?? []) {
49690
50345
  const full = (r.includes("/") ? r : `mutmutco/${r}`).toLowerCase();
@@ -49809,13 +50464,13 @@ function registerBootstrapCommands(program3) {
49809
50464
  })),
49810
50465
  pushAllowlistOwners: pushAllowlistAuthority(verifyOwners, declaredProjectAdmins(meta)),
49811
50466
  pushAllowlistOwnersRead: verifyOwnersRead,
49812
- readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs38.existsSync)(path2) ? (0, import_node_fs38.readFileSync)(path2, "utf8") : null,
50467
+ readLocalFile: (path2) => path2 === "projects.json" && apiProjects != null ? apiProjects : (0, import_node_fs39.existsSync)(path2) ? (0, import_node_fs39.readFileSync)(path2, "utf8") : null,
49813
50468
  // requiredGcpApis is stored as an array by a JSON write, but `org project set --var KEY=VALUE` stores a raw
49814
50469
  // comma-string — accept either so the seeded value verifies regardless of how it was written.
49815
50470
  // #3689: the same committed map the org access audit reads (#3664), so a sanctioned admin is not a
49816
50471
  // permanent bootstrap failure on one surface and an intended state on the other. Absent file → no
49817
50472
  // sanction, which is the pre-#3664 behaviour.
49818
- sanctionedAdmins: (0, import_node_fs38.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs38.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
50473
+ sanctionedAdmins: (0, import_node_fs39.existsSync)("access-matrix.json") ? entriesValueByCanonicalRepo(loadSanctionedAdmins((0, import_node_fs39.readFileSync)("access-matrix.json", "utf8")), repo) : void 0,
49819
50474
  requiredGcpApis: (() => {
49820
50475
  const v = meta?.requiredGcpApis;
49821
50476
  if (Array.isArray(v)) return v;
@@ -49893,14 +50548,14 @@ function registerBootstrapCommands(program3) {
49893
50548
  bootstrap.command("drift").description("#3818: compare every org-owned whole-file seed against MMI-Hub's copy across the registry roster; read-only").option("--repo <owner/repo>", "audit one repo instead of the roster (never a fleet verdict)").option("--json", "machine-readable output").action(async () => {
49894
50549
  const o = { repo: rawValue("--repo", ""), json: rawFlag("--json") };
49895
50550
  const manifestPath = "skills/bootstrap/seeds/manifest.json";
49896
- if (!(0, import_node_fs38.existsSync)(manifestPath)) return fail(`bootstrap drift: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the reference this compares against`);
50551
+ if (!(0, import_node_fs39.existsSync)(manifestPath)) return fail(`bootstrap drift: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the reference this compares against`);
49897
50552
  const seedSource = await resolveHubSeedSource(execGitForSeedSource);
49898
50553
  if (!seedSource.ok) return fail(`bootstrap drift: ${seedSource.reason}`);
49899
- const manifest = loadBootstrapSeeds((0, import_node_fs38.readFileSync)(manifestPath, "utf8"));
50554
+ const manifest = loadBootstrapSeeds((0, import_node_fs39.readFileSync)(manifestPath, "utf8"));
49900
50555
  const hubContents = /* @__PURE__ */ new Map();
49901
50556
  for (const s of manifest.seeds) {
49902
50557
  if (s.ownership !== "org" || s.source !== "self") continue;
49903
- hubContents.set(s.target, (0, import_node_fs38.existsSync)(s.target) ? (0, import_node_fs38.readFileSync)(s.target, "utf8") : null);
50558
+ hubContents.set(s.target, (0, import_node_fs39.existsSync)(s.target) ? (0, import_node_fs39.readFileSync)(s.target, "utf8") : null);
49904
50559
  }
49905
50560
  let targets;
49906
50561
  let classOf = (_repo) => "deployable";
@@ -50156,24 +50811,24 @@ function registerBootstrapCommands(program3) {
50156
50811
  return;
50157
50812
  }
50158
50813
  const manifestPath = "skills/bootstrap/seeds/manifest.json";
50159
- if (!(0, import_node_fs38.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; bootstrap runs from the MMI-Hub repo root by design \u2014 it stamps org-level resources (Project, Ruleset, secrets, access) through the GitHub App, which is only authorized from the Hub checkout`);
50814
+ if (!(0, import_node_fs39.existsSync)(manifestPath)) return fail(`bootstrap apply: ${manifestPath} not found; bootstrap runs from the MMI-Hub repo root by design \u2014 it stamps org-level resources (Project, Ruleset, secrets, access) through the GitHub App, which is only authorized from the Hub checkout`);
50160
50815
  const seedSource = await resolveHubSeedSource(execGitForSeedSource);
50161
50816
  if (!seedSource.ok) return fail(`bootstrap apply: ${seedSource.reason}`);
50162
- const manifest = loadBootstrapSeeds((0, import_node_fs38.readFileSync)(manifestPath, "utf8"));
50817
+ const manifest = loadBootstrapSeeds((0, import_node_fs39.readFileSync)(manifestPath, "utf8"));
50163
50818
  const baseBranch = o.class === "content" ? "main" : "development";
50164
50819
  const slug = parsedRepo.slug;
50165
50820
  const onlyTarget = o.fromConfig && o.only.trim() === "gate.yml" ? ".github/workflows/gate.yml" : o.only.trim();
50166
50821
  const gh2 = async (args) => execFileP("gh", args, { timeout: 2e4 });
50167
- const readFile10 = (p) => (0, import_node_fs38.existsSync)(p) ? (0, import_node_fs38.readFileSync)(p, "utf8") : null;
50822
+ const readFile10 = (p) => (0, import_node_fs39.existsSync)(p) ? (0, import_node_fs39.readFileSync)(p, "utf8") : null;
50168
50823
  const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
50169
50824
  const putSeed = async (target, content, ref, sha) => {
50170
- const tmp = (0, import_node_path35.join)((0, import_node_os21.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
50171
- (0, import_node_fs38.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
50825
+ const tmp = (0, import_node_path36.join)((0, import_node_os21.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
50826
+ (0, import_node_fs39.writeFileSync)(tmp, JSON.stringify(contentPutBody(target, content, ref, sha)), "utf8");
50172
50827
  try {
50173
50828
  await gh2(contentPutInputArgs(repo, target, tmp));
50174
50829
  } finally {
50175
50830
  try {
50176
- (0, import_node_fs38.unlinkSync)(tmp);
50831
+ (0, import_node_fs39.unlinkSync)(tmp);
50177
50832
  } catch {
50178
50833
  }
50179
50834
  }
@@ -50811,10 +51466,10 @@ LIVE apply to ${repo}:
50811
51466
  if (rawFlag("--pins")) return propagatePins(rawFlag("--apply"), rawFlag("--json"));
50812
51467
  const o = { target: rawValue("--target", ""), execute: rawFlag("--execute"), json: rawFlag("--json") };
50813
51468
  const manifestPath = "skills/bootstrap/seeds/manifest.json";
50814
- if (!(0, import_node_fs38.existsSync)(manifestPath)) return fail(`bootstrap propagate: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the desired state this tick propagates`);
51469
+ if (!(0, import_node_fs39.existsSync)(manifestPath)) return fail(`bootstrap propagate: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the Hub's copies ARE the desired state this tick propagates`);
50815
51470
  const seedSource = await resolveHubSeedSource(execGitForSeedSource);
50816
51471
  if (!seedSource.ok) return fail(`bootstrap propagate: ${seedSource.reason}`);
50817
- const manifest = loadBootstrapSeeds((0, import_node_fs38.readFileSync)(manifestPath, "utf8"));
51472
+ const manifest = loadBootstrapSeeds((0, import_node_fs39.readFileSync)(manifestPath, "utf8"));
50818
51473
  const propagatable = manifest.seeds.filter(isPropagatableSeed);
50819
51474
  if (!o.target) {
50820
51475
  return fail(`bootstrap propagate: --target <path> is required \u2014 one of:
@@ -50823,9 +51478,9 @@ LIVE apply to ${repo}:
50823
51478
  const seed = propagatable.find((s) => s.target === o.target);
50824
51479
  if (!seed) return fail(`bootstrap propagate: --target '${o.target}' names no centrally propagatable seed in ${manifestPath}. Propagatable targets:
50825
51480
  ${propagatable.map((s) => s.target).join("\n ")}`);
50826
- if (!seed.managedBlock && !(0, import_node_fs38.existsSync)(seed.target)) return fail(`bootstrap propagate: the Hub's own copy of '${seed.target}' is missing \u2014 nothing to propagate`);
50827
- const hubContent = seed.managedBlock ? null : (0, import_node_fs38.readFileSync)(seed.target, "utf8");
50828
- const readSeedFile2 = (path2) => (0, import_node_fs38.existsSync)(path2) ? (0, import_node_fs38.readFileSync)(path2, "utf8") : null;
51481
+ if (!seed.managedBlock && !(0, import_node_fs39.existsSync)(seed.target)) return fail(`bootstrap propagate: the Hub's own copy of '${seed.target}' is missing \u2014 nothing to propagate`);
51482
+ const hubContent = seed.managedBlock ? null : (0, import_node_fs39.readFileSync)(seed.target, "utf8");
51483
+ const readSeedFile2 = (path2) => (0, import_node_fs39.existsSync)(path2) ? (0, import_node_fs39.readFileSync)(path2, "utf8") : null;
50829
51484
  const isWorkflowSeed = seed.target.startsWith(".github/workflows/");
50830
51485
  const { projects, classOf, inClassRepos, canarySlug, sourcePilot } = await propagationScope(seed.classes, `target '${seed.target}'`);
50831
51486
  const repos = inClassRepos.map((repo) => {
@@ -50944,7 +51599,7 @@ LIVE apply to ${repo}:
50944
51599
  if (plan.refusedNoCanary) return fail("bootstrap propagate --execute: no canary declared for this target \u2014 refusing to write (set seedCanary:true on exactly one registry repo)");
50945
51600
  const headSha = seedSource.sha;
50946
51601
  const sourceBlob = (await execGitForSeedSource(["rev-parse", `${headSha}:${seedSourcePath(seed)}`])).trim();
50947
- const runbookUnaffected = (0, import_node_fs38.existsSync)(RUNBOOK_UNAFFECTED_PATH) ? runbookUnaffectedReason(JSON.parse((0, import_node_fs38.readFileSync)(RUNBOOK_UNAFFECTED_PATH, "utf8")), seed.target, sourceBlob) : void 0;
51602
+ const runbookUnaffected = (0, import_node_fs39.existsSync)(RUNBOOK_UNAFFECTED_PATH) ? runbookUnaffectedReason(JSON.parse((0, import_node_fs39.readFileSync)(RUNBOOK_UNAFFECTED_PATH, "utf8")), seed.target, sourceBlob) : void 0;
50948
51603
  for (const rec of plan.records) {
50949
51604
  if (rec.action !== "open-pr") continue;
50950
51605
  const baseBranch = classOf(rec.repo) === "content" ? "main" : "development";
@@ -50966,15 +51621,15 @@ LIVE apply to ${repo}:
50966
51621
  } catch {
50967
51622
  existingSha = void 0;
50968
51623
  }
50969
- const tmp = (0, import_node_path35.join)((0, import_node_os21.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
51624
+ const tmp = (0, import_node_path36.join)((0, import_node_os21.tmpdir)(), `mmi-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
50970
51625
  const desiredContent = desiredByRepo.get(rec.repo);
50971
51626
  if (desiredContent == null) return fail(`bootstrap propagate: no resolved content for ${rec.repo} ${seed.target} \u2014 refusing to write`);
50972
- (0, import_node_fs38.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, desiredContent, branch, existingSha, runbookUnaffected)), "utf8");
51627
+ (0, import_node_fs39.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, desiredContent, branch, existingSha, runbookUnaffected)), "utf8");
50973
51628
  try {
50974
51629
  await gh2(contentPutInputArgs(rec.repo, seed.target, tmp));
50975
51630
  } finally {
50976
51631
  try {
50977
- (0, import_node_fs38.unlinkSync)(tmp);
51632
+ (0, import_node_fs39.unlinkSync)(tmp);
50978
51633
  } catch {
50979
51634
  }
50980
51635
  }
@@ -51043,10 +51698,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
51043
51698
  return fail(`bootstrap rollback: ${e.message}`);
51044
51699
  }
51045
51700
  const manifestPath = "skills/bootstrap/seeds/manifest.json";
51046
- if (!(0, import_node_fs38.existsSync)(manifestPath)) return fail(`bootstrap rollback: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the manifest names which targets are org-owned and therefore propagated (and rollback-able)`);
51701
+ if (!(0, import_node_fs39.existsSync)(manifestPath)) return fail(`bootstrap rollback: ${manifestPath} not found; run from the MMI-Hub repo root \u2014 the manifest names which targets are org-owned and therefore propagated (and rollback-able)`);
51047
51702
  const seedSource = await resolveHubSeedSource(execGitForSeedSource);
51048
51703
  if (!seedSource.ok) return fail(`bootstrap rollback: ${seedSource.reason}`);
51049
- const manifest = loadBootstrapSeeds((0, import_node_fs38.readFileSync)(manifestPath, "utf8"));
51704
+ const manifest = loadBootstrapSeeds((0, import_node_fs39.readFileSync)(manifestPath, "utf8"));
51050
51705
  const propagatable = manifest.seeds.filter(isPropagatableSeed);
51051
51706
  if (!o.target) {
51052
51707
  return fail(`bootstrap rollback: --target <path> is required \u2014 one of:
@@ -51063,10 +51718,10 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
51063
51718
  const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
51064
51719
  let candidates;
51065
51720
  if (o.record) {
51066
- if (!(0, import_node_fs38.existsSync)(o.record)) return fail(`bootstrap rollback: --record '${o.record}' not found`);
51721
+ if (!(0, import_node_fs39.existsSync)(o.record)) return fail(`bootstrap rollback: --record '${o.record}' not found`);
51067
51722
  let parsed;
51068
51723
  try {
51069
- parsed = JSON.parse((0, import_node_fs38.readFileSync)(o.record, "utf8"));
51724
+ parsed = JSON.parse((0, import_node_fs39.readFileSync)(o.record, "utf8"));
51070
51725
  } catch (e) {
51071
51726
  return fail(`bootstrap rollback: --record '${o.record}' is not valid JSON: ${e.message}`);
51072
51727
  }
@@ -51143,13 +51798,13 @@ Rollback: \`mmi-cli devops bootstrap rollback ${rec.repo} --target ${seed.target
51143
51798
  } catch {
51144
51799
  existingSha = void 0;
51145
51800
  }
51146
- const tmp = (0, import_node_path35.join)((0, import_node_os21.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
51147
- (0, import_node_fs38.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, preSeedContent, plan.branch, existingSha)), "utf8");
51801
+ const tmp = (0, import_node_path36.join)((0, import_node_os21.tmpdir)(), `mmi-rollback-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
51802
+ (0, import_node_fs39.writeFileSync)(tmp, JSON.stringify(contentPutBody(seed.target, preSeedContent, plan.branch, existingSha)), "utf8");
51148
51803
  try {
51149
51804
  await gh2(contentPutInputArgs(repo, seed.target, tmp));
51150
51805
  } finally {
51151
51806
  try {
51152
- (0, import_node_fs38.unlinkSync)(tmp);
51807
+ (0, import_node_fs39.unlinkSync)(tmp);
51153
51808
  } catch {
51154
51809
  }
51155
51810
  }
@@ -51462,7 +52117,7 @@ init_github_client();
51462
52117
  init_cli_shared();
51463
52118
 
51464
52119
  // src/suite-status.ts
51465
- var import_node_crypto18 = require("node:crypto");
52120
+ var import_node_crypto19 = require("node:crypto");
51466
52121
  var SUITE_REPO = "mutmutco/MM-Work";
51467
52122
  var REPOS = {
51468
52123
  "mmi-scout": "mutmutco/MMI-Scout",
@@ -51490,7 +52145,7 @@ function parseSuiteMapping(body) {
51490
52145
  return void 0;
51491
52146
  }
51492
52147
  }
51493
- var sha256 = (b) => (0, import_node_crypto18.createHash)("sha256").update(b).digest("hex");
52148
+ var sha2562 = (b) => (0, import_node_crypto19.createHash)("sha256").update(b).digest("hex");
51494
52149
  async function proveBundle(url, tag, fetchUrl) {
51495
52150
  const get = async (u) => {
51496
52151
  try {
@@ -51502,7 +52157,7 @@ async function proveBundle(url, tag, fetchUrl) {
51502
52157
  const bundle = await get(url);
51503
52158
  if (typeof bundle === "string") return { state: "unprovable", detail: `bundle ${bundle}` };
51504
52159
  if (bundle.status !== 200) return { state: "unprovable", detail: `bundle HTTP ${bundle.status}` };
51505
- const served = sha256(bundle.body);
52160
+ const served = sha2562(bundle.body);
51506
52161
  const recordUrl = new URL(url);
51507
52162
  recordUrl.search = "";
51508
52163
  recordUrl.pathname = recordUrl.pathname.replace(/[^/]*$/, "module.json");
@@ -51745,9 +52400,9 @@ async function waitSuiteDev(deps, opts) {
51745
52400
  }
51746
52401
 
51747
52402
  // src/suite-train.ts
51748
- var import_node_fs39 = require("node:fs");
51749
- var import_node_path36 = require("node:path");
51750
- var import_node_crypto19 = require("node:crypto");
52403
+ var import_node_fs40 = require("node:fs");
52404
+ var import_node_path37 = require("node:path");
52405
+ var import_node_crypto20 = require("node:crypto");
51751
52406
 
51752
52407
  // src/kit-version.ts
51753
52408
  var parse = (v) => /^(\d+)\.(\d+)\.(\d+)(-.+)?$/.exec(typeof v === "string" ? v.trim() : "");
@@ -51832,7 +52487,7 @@ function suiteReleaseTag(rc) {
51832
52487
  function readSuiteCut(path2) {
51833
52488
  let raw;
51834
52489
  try {
51835
- raw = (0, import_node_fs39.readFileSync)(path2, "utf8");
52490
+ raw = (0, import_node_fs40.readFileSync)(path2, "utf8");
51836
52491
  } catch (e) {
51837
52492
  if (e.code === "ENOENT") return void 0;
51838
52493
  throw e;
@@ -51848,11 +52503,11 @@ function readSuiteCut(path2) {
51848
52503
  return record2;
51849
52504
  }
51850
52505
  function writeSuiteCut(path2, record2) {
51851
- (0, import_node_fs39.mkdirSync)((0, import_node_path36.dirname)(path2), { recursive: true });
51852
- const temp = `${path2}.${(0, import_node_crypto19.randomBytes)(8).toString("hex")}.tmp`;
52506
+ (0, import_node_fs40.mkdirSync)((0, import_node_path37.dirname)(path2), { recursive: true });
52507
+ const temp = `${path2}.${(0, import_node_crypto20.randomBytes)(8).toString("hex")}.tmp`;
51853
52508
  try {
51854
- (0, import_node_fs39.writeFileSync)(temp, JSON.stringify(record2, null, 2), { flag: "wx" });
51855
- (0, import_node_fs39.renameSync)(temp, path2);
52509
+ (0, import_node_fs40.writeFileSync)(temp, JSON.stringify(record2, null, 2), { flag: "wx" });
52510
+ (0, import_node_fs40.renameSync)(temp, path2);
51856
52511
  } catch (e) {
51857
52512
  throw new Error(`suite cut record write failed at ${path2}: ${e.message}; stop, verify the file before retrying`);
51858
52513
  }
@@ -51901,25 +52556,25 @@ async function suiteMarkAbandoned(deps, ownerGo = false) {
51901
52556
  return tags.map((t) => `${t.repo}@${t.tag}`);
51902
52557
  }
51903
52558
  async function retireFailedLeadRcLedger(deps, ownerGo) {
51904
- if (!(0, import_node_fs39.existsSync)(deps.ledgerPath)) return void 0;
52559
+ if (!(0, import_node_fs40.existsSync)(deps.ledgerPath)) return void 0;
51905
52560
  return withLedgerLock(deps.ledgerPath, async (fence) => {
51906
- const ledger = parseReleaseLedger((0, import_node_fs39.readFileSync)(deps.ledgerPath, "utf8"));
52561
+ const ledger = parseReleaseLedger((0, import_node_fs40.readFileSync)(deps.ledgerPath, "utf8"));
51907
52562
  const states = Object.values(ledger.phases).map((p) => p.state);
51908
52563
  if (ledger.repo !== SUITE_REPO || ledger.lane !== "rcand" || ledger.phases.promotion.state === "complete" || !states.includes("failed")) return void 0;
51909
52564
  if (!ownerGo) throw new Error(`the lead's failed rcand ledger for ${ledger.tag} blocks every train; retire it with suite abandon --owner-go`);
51910
52565
  if ((await deps.rcHead())?.toLowerCase() === ledger.tagSha.toLowerCase()) throw new Error(`origin rc already points at ${ledger.tag}; it was promoted, so its ledger is not retired`);
51911
- const archive = (0, import_node_path36.join)((0, import_node_path36.dirname)(deps.ledgerPath), `phases.archive-${ledger.tag}.json`);
51912
- if ((0, import_node_fs39.existsSync)(archive)) throw new Error(`ledger archive already exists at ${archive}; inspect before retiring again`);
52566
+ const archive = (0, import_node_path37.join)((0, import_node_path37.dirname)(deps.ledgerPath), `phases.archive-${ledger.tag}.json`);
52567
+ if ((0, import_node_fs40.existsSync)(archive)) throw new Error(`ledger archive already exists at ${archive}; inspect before retiring again`);
51913
52568
  await deps.markAbandoned(SUITE_REPO, ledger.tag, ledger.tagSha);
51914
- await fence(() => (0, import_node_fs39.renameSync)(deps.ledgerPath, archive));
52569
+ await fence(() => (0, import_node_fs40.renameSync)(deps.ledgerPath, archive));
51915
52570
  return `${ledger.tag} (ledger archived to ${archive})`;
51916
52571
  });
51917
52572
  }
51918
52573
  function suiteAbandon(recordPath, ownerGo = false, now = /* @__PURE__ */ new Date()) {
51919
52574
  const record2 = abandonableCut(recordPath, ownerGo);
51920
52575
  const archive = `${recordPath}.abandoned-${record2.leadDevelopment.slice(0, 12)}-${now.toISOString().replace(/[:.]/g, "")}.json`;
51921
- if ((0, import_node_fs39.existsSync)(archive)) throw new Error(`abandoned-cut archive already exists at ${archive}; inspect before abandoning again`);
51922
- (0, import_node_fs39.renameSync)(recordPath, archive);
52576
+ if ((0, import_node_fs40.existsSync)(archive)) throw new Error(`abandoned-cut archive already exists at ${archive}; inspect before abandoning again`);
52577
+ (0, import_node_fs40.renameSync)(recordPath, archive);
51923
52578
  return { archive, orphanTags: record2.members.filter((m) => m.tag).map((m) => `${m.repo}@${m.tag}`) };
51924
52579
  }
51925
52580
  async function suiteRestoreMainPins(deps) {
@@ -52006,12 +52661,12 @@ async function suiteRcandRun(deps, opts) {
52006
52661
  }
52007
52662
  if ((record2?.announcement === "sent" || record2?.announcement === "skipped") && record2.leadReleased) {
52008
52663
  const archive = `${deps.recordPath}.${record2.leadRcTag}.json`;
52009
- if ((0, import_node_fs39.existsSync)(archive)) throw new Error(`suite cut archive already exists at ${archive}; inspect before a new cut`);
52010
- (0, import_node_fs39.renameSync)(deps.recordPath, archive);
52664
+ if ((0, import_node_fs40.existsSync)(archive)) throw new Error(`suite cut archive already exists at ${archive}; inspect before a new cut`);
52665
+ (0, import_node_fs40.renameSync)(deps.recordPath, archive);
52011
52666
  record2 = void 0;
52012
52667
  }
52013
52668
  if (record2 && !record2.pinPr && !record2.leadRcTag && record2.members.every((m) => !m.tag)) {
52014
- (0, import_node_fs39.rmSync)(deps.recordPath, { force: true });
52669
+ (0, import_node_fs40.rmSync)(deps.recordPath, { force: true });
52015
52670
  record2 = void 0;
52016
52671
  }
52017
52672
  if (!record2) {
@@ -52065,7 +52720,7 @@ async function suiteRcandRun(deps, opts) {
52065
52720
  quiet.push(member);
52066
52721
  }
52067
52722
  if (record2.members.every((m) => m.carried || quiet.includes(m)) && await deps.contains(SUITE_REPO, record2.leadDevelopment, record2.leadPreviousRc)) {
52068
- (0, import_node_fs39.rmSync)(deps.recordPath, { force: true });
52723
+ (0, import_node_fs40.rmSync)(deps.recordPath, { force: true });
52069
52724
  throw new Error("nothing to promote: neither MM-Work nor any module has new development commits since its rc; no suite cut started");
52070
52725
  }
52071
52726
  for (const member of quiet) {
@@ -52083,7 +52738,7 @@ async function suiteRcandRun(deps, opts) {
52083
52738
  }
52084
52739
  if (refusals.length) {
52085
52740
  const untouched = record2.members.every((m) => !m.tag) && !record2.pinPr;
52086
- if (untouched) (0, import_node_fs39.rmSync)(deps.recordPath, { force: true });
52741
+ if (untouched) (0, import_node_fs40.rmSync)(deps.recordPath, { force: true });
52087
52742
  throw new Error(`module rc serving is not ready; ${untouched ? "no suite cut started" : "no new tag was cut"}:
52088
52743
  ${refusals.join("\n")}
52089
52744
  Each module must ship a live release that bind-mounts <deployPath>/data read-only and serves <data>/rc/module.js at /rc/module.js (docs/Guides/suite-trains.md).`);
@@ -52275,11 +52930,11 @@ Each member's live release must serve <data>/main/module.js at its main pin path
52275
52930
  }
52276
52931
  return record2;
52277
52932
  }
52278
- var hotfixRecordPath = (deps) => (0, import_node_path36.join)((0, import_node_path36.dirname)(deps.recordPath), "hotfix.json");
52933
+ var hotfixRecordPath = (deps) => (0, import_node_path37.join)((0, import_node_path37.dirname)(deps.recordPath), "hotfix.json");
52279
52934
  function readSuiteHotfix(path2) {
52280
52935
  let raw;
52281
52936
  try {
52282
- raw = (0, import_node_fs39.readFileSync)(path2, "utf8");
52937
+ raw = (0, import_node_fs40.readFileSync)(path2, "utf8");
52283
52938
  } catch (e) {
52284
52939
  if (e.code === "ENOENT") return void 0;
52285
52940
  throw e;
@@ -52322,7 +52977,7 @@ async function suiteHotfix(deps, opts) {
52322
52977
  if (completed) record2 = void 0;
52323
52978
  if (record2 && record2.repo !== found.repo) throw new Error(`an unfinished suite hotfix for ${record2.repo} is recorded; finish it (rerun with that member) first`);
52324
52979
  if (!record2?.tag) await deps.validateHotfixSummary();
52325
- if (completed) (0, import_node_fs39.renameSync)(path2, `${path2}.${completed.leadTag}.json`);
52980
+ if (completed) (0, import_node_fs40.renameSync)(path2, `${path2}.${completed.leadTag}.json`);
52326
52981
  const rec = record2 ?? { schema: "mmi-suite-hotfix/v1", member: found.id, repo: found.repo, from: opts.from, ...opts.ack?.length ? { ack: opts.ack } : {} };
52327
52982
  const save = () => writeSuiteHotfix(path2, rec);
52328
52983
  save();
@@ -52590,9 +53245,9 @@ async function runRcDataSwitch(deps, opts) {
52590
53245
 
52591
53246
  // src/suite-train-io.ts
52592
53247
  var import_node_child_process19 = require("node:child_process");
52593
- var import_node_path49 = require("node:path");
52594
- var import_node_fs53 = require("node:fs");
52595
53248
  var import_node_path50 = require("node:path");
53249
+ var import_node_fs54 = require("node:fs");
53250
+ var import_node_path51 = require("node:path");
52596
53251
  init_cli_shared();
52597
53252
 
52598
53253
  // src/command-register-train-operations.ts
@@ -54284,8 +54939,8 @@ function checkHotfixCarries(options) {
54284
54939
  }
54285
54940
 
54286
54941
  // src/merge-cleanup.ts
54287
- var import_node_fs40 = require("node:fs");
54288
- var import_node_path37 = require("node:path");
54942
+ var import_node_fs41 = require("node:fs");
54943
+ var import_node_path38 = require("node:path");
54289
54944
  var import_node_os22 = require("node:os");
54290
54945
  init_cli_shared();
54291
54946
  init_github_client();
@@ -54693,13 +55348,13 @@ function resolveSquashMergeBodyText(commits, allowedClosing, cwd) {
54693
55348
  return rewritten === base ? null : rewritten;
54694
55349
  }
54695
55350
  function writeSquashBodyFile(body) {
54696
- const dir = (0, import_node_fs40.mkdtempSync)((0, import_node_path37.join)((0, import_node_os22.tmpdir)(), "mmi-squash-body-"));
54697
- const path2 = (0, import_node_path37.join)(dir, "body.txt");
54698
- (0, import_node_fs40.writeFileSync)(path2, body.endsWith("\n") ? body : `${body}
55351
+ const dir = (0, import_node_fs41.mkdtempSync)((0, import_node_path38.join)((0, import_node_os22.tmpdir)(), "mmi-squash-body-"));
55352
+ const path2 = (0, import_node_path38.join)(dir, "body.txt");
55353
+ (0, import_node_fs41.writeFileSync)(path2, body.endsWith("\n") ? body : `${body}
54699
55354
  `, "utf8");
54700
55355
  return { path: path2, cleanup: () => {
54701
55356
  try {
54702
- (0, import_node_fs40.rmSync)(dir, { recursive: true, force: true });
55357
+ (0, import_node_fs41.rmSync)(dir, { recursive: true, force: true });
54703
55358
  } catch {
54704
55359
  }
54705
55360
  } };
@@ -54950,9 +55605,9 @@ async function deleteMergedRemoteBranch(options) {
54950
55605
  init_github_client();
54951
55606
 
54952
55607
  // src/release-custody.ts
54953
- var import_node_crypto20 = require("node:crypto");
54954
- var import_node_fs41 = require("node:fs");
54955
- var import_node_path38 = require("node:path");
55608
+ var import_node_crypto21 = require("node:crypto");
55609
+ var import_node_fs42 = require("node:fs");
55610
+ var import_node_path39 = require("node:path");
54956
55611
  init_github_client();
54957
55612
 
54958
55613
  // src/release-custody-bootstrap.ts
@@ -55173,7 +55828,7 @@ var ReleaseCustodyError = class extends Error {
55173
55828
  this.name = "ReleaseCustodyError";
55174
55829
  }
55175
55830
  };
55176
- var custodyDigest = (value) => (0, import_node_crypto20.createHash)("sha256").update(value).digest("hex");
55831
+ var custodyDigest = (value) => (0, import_node_crypto21.createHash)("sha256").update(value).digest("hex");
55177
55832
  function parseCustody(raw, repo) {
55178
55833
  const s = JSON.parse(raw);
55179
55834
  if (s.schema !== "mmi-release-custody/v1" || s.repo !== repo || !s.precutId || !Number.isSafeInteger(s.revision) || s.revision < 1 || !s.controller || !Number.isSafeInteger(s.controller.runId) || s.controller.runId <= 0 || !Number.isSafeInteger(s.controller.runAttempt) || s.controller.runAttempt <= 0 || !s.controller.token || !(s.ledger === null || typeof s.ledger === "string") || !s.archives || !s.effects) {
@@ -55261,7 +55916,7 @@ var ReleaseCustodySession = class _ReleaseCustodySession {
55261
55916
  throw new ReleaseCustodyError(`previous controller run ${found.state.controller.runId} attempt ${found.state.controller.runAttempt} is not proven terminal`);
55262
55917
  }
55263
55918
  let state = {
55264
- ...found.state ?? { schema: "mmi-release-custody/v1", repo, precutId: (0, import_node_crypto20.randomUUID)(), effects: {}, ...await initialize() },
55919
+ ...found.state ?? { schema: "mmi-release-custody/v1", repo, precutId: (0, import_node_crypto21.randomUUID)(), effects: {}, ...await initialize() },
55265
55920
  revision: (found.state?.revision ?? 0) + 1,
55266
55921
  controller
55267
55922
  };
@@ -55322,7 +55977,7 @@ var ReleaseCustodySession = class _ReleaseCustodySession {
55322
55977
  proof: { kind: "ledger-lifecycle", controller: state.controller }
55323
55978
  };
55324
55979
  }
55325
- return { ...state, ledger: raw, archives, precutId: state.ledger && !raw ? (0, import_node_crypto20.randomUUID)() : state.precutId };
55980
+ return { ...state, ledger: raw, archives, precutId: state.ledger && !raw ? (0, import_node_crypto21.randomUUID)() : state.precutId };
55326
55981
  });
55327
55982
  }
55328
55983
  async preserveLocal(raw) {
@@ -55422,7 +56077,7 @@ function reclassifySupersededArchives(state) {
55422
56077
  }
55423
56078
  function localRaw(path2) {
55424
56079
  try {
55425
- return (0, import_node_fs41.readFileSync)(path2, "utf8");
56080
+ return (0, import_node_fs42.readFileSync)(path2, "utf8");
55426
56081
  } catch (e) {
55427
56082
  if (e.code === "ENOENT") return null;
55428
56083
  throw e;
@@ -55431,14 +56086,14 @@ function localRaw(path2) {
55431
56086
  function hydrateLocal(path2, raw) {
55432
56087
  const previous = localRaw(path2);
55433
56088
  if (previous === raw) return;
55434
- (0, import_node_fs41.mkdirSync)((0, import_node_path38.dirname)(path2), { recursive: true });
56089
+ (0, import_node_fs42.mkdirSync)((0, import_node_path39.dirname)(path2), { recursive: true });
55435
56090
  if (previous !== null) {
55436
56091
  const ledger = parseReleaseLedger(previous);
55437
- const archive = (0, import_node_path38.join)((0, import_node_path38.dirname)(path2), `phases.local-${ledger.tagSha}-${custodyDigest(previous)}.json`);
55438
- if (!(0, import_node_fs41.existsSync)(archive)) (0, import_node_fs41.writeFileSync)(archive, previous, { flag: "wx" });
55439
- if (raw === null) (0, import_node_fs41.renameSync)(path2, `${path2}.retired-${custodyDigest(previous)}`);
56092
+ const archive = (0, import_node_path39.join)((0, import_node_path39.dirname)(path2), `phases.local-${ledger.tagSha}-${custodyDigest(previous)}.json`);
56093
+ if (!(0, import_node_fs42.existsSync)(archive)) (0, import_node_fs42.writeFileSync)(archive, previous, { flag: "wx" });
56094
+ if (raw === null) (0, import_node_fs42.renameSync)(path2, `${path2}.retired-${custodyDigest(previous)}`);
55440
56095
  }
55441
- if (raw !== null) (0, import_node_fs41.writeFileSync)(path2, raw, "utf8");
56096
+ if (raw !== null) (0, import_node_fs42.writeFileSync)(path2, raw, "utf8");
55442
56097
  }
55443
56098
  async function commandIdentity(file, args, run, repo) {
55444
56099
  if (file === "git" && args[0] === "push") {
@@ -55548,7 +56203,7 @@ function withHubReleaseCustody(deps) {
55548
56203
  if (!authority.ok || !authority.train || authority.authenticatedDelegation !== true) {
55549
56204
  throw new ReleaseCustodyError("repository custody requires the server-verified owner-triggered train session");
55550
56205
  }
55551
- const controller = { ...runIdentity, token: (0, import_node_crypto20.randomUUID)() };
56206
+ const controller = { ...runIdentity, token: (0, import_node_crypto21.randomUUID)() };
55552
56207
  const raw = localRaw(path2);
55553
56208
  const requiredContexts = () => discoverRequiredCheckContexts(
55554
56209
  deps,
@@ -55628,8 +56283,8 @@ function withHubReleaseCustody(deps) {
55628
56283
  }
55629
56284
 
55630
56285
  // src/project-info-sync.ts
55631
- var import_node_fs42 = require("node:fs");
55632
- var import_node_path39 = require("node:path");
56286
+ var import_node_fs43 = require("node:fs");
56287
+ var import_node_path40 = require("node:path");
55633
56288
  var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
55634
56289
  updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
55635
56290
  projectV2 { id }
@@ -55676,14 +56331,14 @@ function sharedName(entries, fallback) {
55676
56331
  }
55677
56332
  function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
55678
56333
  if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
55679
- const readmePath = (0, import_node_path39.join)(repoRoot2, "README.md");
55680
- if (!(0, import_node_fs42.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
56334
+ const readmePath = (0, import_node_path40.join)(repoRoot2, "README.md");
56335
+ if (!(0, import_node_fs43.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
55681
56336
  const entries = entriesFor(project2, projects);
55682
56337
  const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
55683
56338
  const projectName = sharedName(entries, project2.name?.trim() || targetRepo2.split("/").pop() || targetRepo2);
55684
56339
  if (!memberRepos.length) throw new Error(`org project sync-info: project ${projectName} has no registered member repos`);
55685
56340
  const entryNames = entries.map((entry) => entry.name?.trim()).filter((name) => Boolean(name));
55686
- const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs42.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
56341
+ const shortDescription = memberRepos.length === 1 ? shortDescriptionFromReadme((0, import_node_fs43.readFileSync)(readmePath, "utf8")) : `Shared work across ${new Intl.ListFormat("en", { type: "conjunction" }).format(entryNames)}.`;
55687
56342
  const lines2 = [
55688
56343
  `# ${projectName}`,
55689
56344
  "",
@@ -55702,8 +56357,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
55702
56357
  const targetBase = `https://github.com/${targetRepo2}`;
55703
56358
  const targetBranch = branchFor(targetRepo2, projects);
55704
56359
  const orgDocs = [
55705
- (0, import_node_fs42.existsSync)((0, import_node_path39.join)(repoRoot2, "architecture.md")) ? `- [Hub architecture](${targetBase}/blob/${targetBranch}/architecture.md)` : "",
55706
- (0, import_node_fs42.existsSync)((0, import_node_path39.join)(repoRoot2, "docs", "Architecture", "agentic-dev-environment.md")) ? `- [Agentic development environment](${targetBase}/blob/${targetBranch}/docs/Architecture/agentic-dev-environment.md)` : ""
56360
+ (0, import_node_fs43.existsSync)((0, import_node_path40.join)(repoRoot2, "architecture.md")) ? `- [Hub architecture](${targetBase}/blob/${targetBranch}/architecture.md)` : "",
56361
+ (0, import_node_fs43.existsSync)((0, import_node_path40.join)(repoRoot2, "docs", "Architecture", "agentic-dev-environment.md")) ? `- [Agentic development environment](${targetBase}/blob/${targetBranch}/docs/Architecture/agentic-dev-environment.md)` : ""
55707
56362
  ].filter(Boolean);
55708
56363
  if (orgDocs.length) lines2.push("", "## Organisation docs", "", ...orgDocs);
55709
56364
  return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines2.join("\n")}
@@ -55725,8 +56380,8 @@ async function syncProjectInfo(plan, client, apply) {
55725
56380
  }
55726
56381
 
55727
56382
  // src/train-commands.ts
55728
- var import_node_fs43 = require("node:fs");
55729
- var import_node_path40 = require("node:path");
56383
+ var import_node_fs44 = require("node:fs");
56384
+ var import_node_path41 = require("node:path");
55730
56385
  init_cli_shared();
55731
56386
  init_clean_exit();
55732
56387
  init_client_version();
@@ -55741,7 +56396,7 @@ function resolveReleaseBumpIntent(raw) {
55741
56396
  }
55742
56397
  function readRepoVersion() {
55743
56398
  try {
55744
- return JSON.parse((0, import_node_fs43.readFileSync)((0, import_node_path40.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
56399
+ return JSON.parse((0, import_node_fs44.readFileSync)((0, import_node_path41.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
55745
56400
  } catch {
55746
56401
  return void 0;
55747
56402
  }
@@ -55852,7 +56507,7 @@ function releaseProgressVerdict(status, subject = "Release") {
55852
56507
 
55853
56508
  // src/suite-pin.ts
55854
56509
  var import_promises6 = require("node:fs/promises");
55855
- var import_node_path41 = require("node:path");
56510
+ var import_node_path42 = require("node:path");
55856
56511
  var SuitePinError = class _SuitePinError extends Error {
55857
56512
  diagnostic;
55858
56513
  constructor(repo, tag, error) {
@@ -55920,8 +56575,8 @@ async function openSuiteCutPinPr(deps, input) {
55920
56575
  if (input.carriedOnly) return { status: "unchanged" };
55921
56576
  throw new Error("pins already match without a merged cut PR \u2014 provenance unverified");
55922
56577
  }
55923
- await (0, import_promises6.mkdir)((0, import_node_path41.join)(deps.root, "tmp"), { recursive: true });
55924
- scratch = await (0, import_promises6.mkdtemp)((0, import_node_path41.join)(deps.root, "tmp", "suite-pin-"));
56578
+ await (0, import_promises6.mkdir)((0, import_node_path42.join)(deps.root, "tmp"), { recursive: true });
56579
+ scratch = await (0, import_promises6.mkdtemp)((0, import_node_path42.join)(deps.root, "tmp", "suite-pin-"));
55925
56580
  const run = (file, args) => deps.run(file, args, scratch);
55926
56581
  await run("git", ["clone", "--quiet", "--branch", prs.length ? branch : "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
55927
56582
  if (prs.length) {
@@ -55933,7 +56588,7 @@ async function openSuiteCutPinPr(deps, input) {
55933
56588
  await run("git", ["switch", "-c", branch]);
55934
56589
  }
55935
56590
  for (const stage of ["rc", "main"]) {
55936
- const path2 = (0, import_node_path41.join)(scratch, `${stage}-pins.json`);
56591
+ const path2 = (0, import_node_path42.join)(scratch, `${stage}-pins.json`);
55937
56592
  await (0, import_promises6.writeFile)(path2, JSON.stringify(input.pins.map((p) => ({ module: p.module, url: p[stage] }))));
55938
56593
  await run("node", ["scripts/pin-modules.mjs", "--stage", stage, "--pins", path2]);
55939
56594
  }
@@ -55986,12 +56641,12 @@ async function openSuiteRestorePinPr(deps, input) {
55986
56641
  }
55987
56642
  });
55988
56643
  if (!todo.length) return void 0;
55989
- await (0, import_promises6.mkdir)((0, import_node_path41.join)(deps.root, "tmp"), { recursive: true });
55990
- scratch = await (0, import_promises6.mkdtemp)((0, import_node_path41.join)(deps.root, "tmp", "suite-pin-"));
56644
+ await (0, import_promises6.mkdir)((0, import_node_path42.join)(deps.root, "tmp"), { recursive: true });
56645
+ scratch = await (0, import_promises6.mkdtemp)((0, import_node_path42.join)(deps.root, "tmp", "suite-pin-"));
55991
56646
  const run = (file, args) => deps.run(file, args, scratch);
55992
56647
  await run("git", ["clone", "--quiet", "--branch", "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
55993
56648
  await run("git", ["switch", "-c", branch]);
55994
- const path2 = (0, import_node_path41.join)(scratch, "main-pins.json");
56649
+ const path2 = (0, import_node_path42.join)(scratch, "main-pins.json");
55995
56650
  await (0, import_promises6.writeFile)(path2, JSON.stringify(todo.map((p) => ({ module: p.module, url: p.prior }))));
55996
56651
  await run("node", ["scripts/pin-modules.mjs", "--stage", "main", "--pins", path2]);
55997
56652
  await run("git", ["add", "seed/modules.json"]);
@@ -56065,8 +56720,8 @@ async function openSuitePinPr(deps, input) {
56065
56720
  if (newer.length) return { status: "superseded", pr: newer[0].url, url: pin };
56066
56721
  const open2 = onBranch.find((p) => p.state === "OPEN");
56067
56722
  if (open2 && tagOf(open2.title ?? "") === tag) return { status: "pending", pr: open2.url, url: pin };
56068
- await (0, import_promises6.mkdir)((0, import_node_path41.join)(deps.root, "tmp"), { recursive: true });
56069
- scratch = await (0, import_promises6.mkdtemp)((0, import_node_path41.join)(deps.root, "tmp", "suite-pin-"));
56723
+ await (0, import_promises6.mkdir)((0, import_node_path42.join)(deps.root, "tmp"), { recursive: true });
56724
+ scratch = await (0, import_promises6.mkdtemp)((0, import_node_path42.join)(deps.root, "tmp", "suite-pin-"));
56070
56725
  const run = (file, args) => deps.run(file, args, scratch);
56071
56726
  await run("git", ["clone", "--quiet", "--branch", "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
56072
56727
  await run("git", ["switch", "-c", branch]);
@@ -56118,7 +56773,7 @@ function prTitleRefusal(title) {
56118
56773
 
56119
56774
  // src/command-register-collaboration.ts
56120
56775
  var import_node_child_process18 = require("node:child_process");
56121
- var import_node_fs52 = require("node:fs");
56776
+ var import_node_fs53 = require("node:fs");
56122
56777
  var import_promises8 = require("node:fs/promises");
56123
56778
  init_clean_exit();
56124
56779
  init_cli_shared();
@@ -56142,7 +56797,7 @@ function spawnDetachedSelf(args, deps, opts = {}) {
56142
56797
  }
56143
56798
 
56144
56799
  // src/command-register-collaboration.ts
56145
- var import_node_path48 = require("node:path");
56800
+ var import_node_path49 = require("node:path");
56146
56801
  init_house_map();
56147
56802
 
56148
56803
  // src/issue-body.ts
@@ -56259,8 +56914,8 @@ init_github_client();
56259
56914
  init_github_quota();
56260
56915
 
56261
56916
  // src/issue-commands.ts
56262
- var import_node_fs44 = require("node:fs");
56263
- var import_node_crypto21 = require("node:crypto");
56917
+ var import_node_fs45 = require("node:fs");
56918
+ var import_node_crypto22 = require("node:crypto");
56264
56919
  init_cli_shared();
56265
56920
  init_clean_exit();
56266
56921
  init_error_codes();
@@ -56452,7 +57107,7 @@ async function editIssue(client, options, deps = {}) {
56452
57107
  const url = `https://github.com/${repo}/issues/${parsed.number}`;
56453
57108
  const patch = {};
56454
57109
  let bodyChanged = false;
56455
- const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs44.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
57110
+ const textDeps = () => deps.textDeps ?? { readFile: (p, e) => Promise.resolve((0, import_node_fs45.readFileSync)(p, e)), readStdin: () => Promise.resolve("") };
56456
57111
  if (options.titleFile !== void 0) {
56457
57112
  patch.title = await resolveIssueTitle({ title: options.title, titleFile: options.titleFile }, textDeps());
56458
57113
  } else if (options.title !== void 0) {
@@ -56737,7 +57392,7 @@ function rowIdempotencyKey(batchKey, spec) {
56737
57392
  const identity = `${spec.type}
56738
57393
  ${spec.title.trim()}
56739
57394
  ${spec.body ?? ""}`;
56740
- const hash = (0, import_node_crypto21.createHash)("sha256").update(identity).digest("hex").slice(0, 16);
57395
+ const hash = (0, import_node_crypto22.createHash)("sha256").update(identity).digest("hex").slice(0, 16);
56741
57396
  return `${batchKey}:${hash}`;
56742
57397
  }
56743
57398
  var BATCH_SPEC_KEYS = /* @__PURE__ */ new Set(["type", "title", "body", "priority", "labels", "label", "parent", "repo", "surface"]);
@@ -57119,7 +57774,7 @@ function extendCreateCommand(issue, batchAttach) {
57119
57774
  if (opts.batch) {
57120
57775
  let specs;
57121
57776
  try {
57122
- const raw = (0, import_node_fs44.readFileSync)(opts.batch, "utf8");
57777
+ const raw = (0, import_node_fs45.readFileSync)(opts.batch, "utf8");
57123
57778
  specs = JSON.parse(raw);
57124
57779
  if (!Array.isArray(specs)) {
57125
57780
  const top = specs === null ? "null" : typeof specs === "object" ? `an object with keys: ${Object.keys(specs).join(", ") || "(none)"}` : `a ${typeof specs}`;
@@ -57196,8 +57851,8 @@ ${lines2}`, {
57196
57851
  }
57197
57852
 
57198
57853
  // src/post-merge-recon.ts
57199
- var import_node_fs45 = require("node:fs");
57200
- var import_node_path42 = require("node:path");
57854
+ var import_node_fs46 = require("node:fs");
57855
+ var import_node_path43 = require("node:path");
57201
57856
 
57202
57857
  // src/cross-repo-filing-issue.ts
57203
57858
  init_github_client();
@@ -57367,16 +58022,16 @@ function buildPostMergeReconRecovery(input) {
57367
58022
  }
57368
58023
  function writePostMergeReconRecovery(cwd, recovery) {
57369
58024
  const path2 = postMergeReconStatePath(cwd, recovery.repo, recovery.pr);
57370
- (0, import_node_fs45.mkdirSync)((0, import_node_path42.dirname)(path2), { recursive: true });
57371
- (0, import_node_fs45.writeFileSync)(path2, `${JSON.stringify(recovery, null, 2)}
58025
+ (0, import_node_fs46.mkdirSync)((0, import_node_path43.dirname)(path2), { recursive: true });
58026
+ (0, import_node_fs46.writeFileSync)(path2, `${JSON.stringify(recovery, null, 2)}
57372
58027
  `, "utf8");
57373
58028
  return path2;
57374
58029
  }
57375
58030
  function clearPostMergeReconRecovery(cwd, repo, pr) {
57376
58031
  const path2 = postMergeReconStatePath(cwd, repo, pr);
57377
- if (!(0, import_node_fs45.existsSync)(path2)) return;
58032
+ if (!(0, import_node_fs46.existsSync)(path2)) return;
57378
58033
  try {
57379
- (0, import_node_fs45.unlinkSync)(path2);
58034
+ (0, import_node_fs46.unlinkSync)(path2);
57380
58035
  } catch {
57381
58036
  }
57382
58037
  }
@@ -57411,7 +58066,7 @@ var import_promises7 = require("node:fs/promises");
57411
58066
  init_cli_shared();
57412
58067
 
57413
58068
  // src/api-read.ts
57414
- var import_node_fs46 = require("node:fs");
58069
+ var import_node_fs47 = require("node:fs");
57415
58070
  init_cli_shared();
57416
58071
  var API_ACCEPT_HEADERS = {
57417
58072
  json: "application/vnd.github+json",
@@ -57676,7 +58331,7 @@ function registerApiReadCommands(program3, overrides) {
57676
58331
  if (req.workflowRuns) body = sliceWorkflowRunsPage(body, req.workflowRuns);
57677
58332
  if (o.jq !== void 0) body = await jqRun(o.jq, body, localJqEnv(), ...bindings.length ? [bindings] : []);
57678
58333
  if (zip) {
57679
- (0, import_node_fs46.writeFileSync)(o.out, body, "latin1");
58334
+ (0, import_node_fs47.writeFileSync)(o.out, body, "latin1");
57680
58335
  return;
57681
58336
  }
57682
58337
  } catch (e) {
@@ -58088,7 +58743,7 @@ function parseBrokerPrState(raw) {
58088
58743
  function buildPrListSpec(o) {
58089
58744
  const repo = parseBrokerRepo(o.repo, "--repo");
58090
58745
  const fields = rejectPrViewQueueFields(parseBrokerJsonFields(o.json), "<number>", repo, "list");
58091
- const args = withJson(withRepo(["pr", "list"], repo), fields, o.json);
58746
+ const args = withJson(withRepo(["pr", "list"], repo), fields, o.json, PR_LIST_JSON_FIELDS);
58092
58747
  const state = parseBrokerPrState(o.state);
58093
58748
  if (state) args.push("--state", state);
58094
58749
  for (const flag of ["base", "head"]) {
@@ -58107,9 +58762,11 @@ function buildPrListSpec(o) {
58107
58762
  function withRepo(args, repo) {
58108
58763
  return repo ? [...args, "--repo", repo] : args;
58109
58764
  }
58110
- function withJson(args, jsonFields, jsonFlag) {
58765
+ function withJson(args, jsonFields, jsonFlag, bareDefault) {
58111
58766
  if (jsonFlag === void 0 || jsonFlag === false) return args;
58112
- return typeof jsonFields === "string" ? [...args, "--json", jsonFields] : [...args, "--json"];
58767
+ const fields = typeof jsonFields === "string" ? jsonFields : bareDefault;
58768
+ if (fields === void 0) throw new Error(`read: --json needs a comma-separated field list for gh ${args.slice(0, 2).join(" ")} (e.g. --json number,title,state)`);
58769
+ return [...args, "--json", fields];
58113
58770
  }
58114
58771
  function buildRunViewSpec(id, o) {
58115
58772
  const args = withJson(withRepo(["run", "view", parseBrokerId(id, "run id"), ...o.attempt !== void 0 ? ["--attempt", String(parseBrokerInt(o.attempt, "--attempt", 1, 1e3))] : []], parseBrokerRepo(o.repo, "--repo")), parseBrokerJsonFields(o.json), o.json);
@@ -58127,8 +58784,11 @@ function parseBrokerCommit(v) {
58127
58784
  if (!/^[0-9a-f]{40}$/i.test(v)) throw new Error(`read: --commit must be a full 40-character commit SHA (received '${v}')`);
58128
58785
  return v;
58129
58786
  }
58787
+ var PR_LIST_JSON_FIELDS = "number,title,state,isDraft,headRefName,baseRefName,author,createdAt,updatedAt,url";
58788
+ var ISSUE_LIST_JSON_FIELDS = "number,title,state,labels,assignees,createdAt,updatedAt,url";
58789
+ var RUN_LIST_JSON_FIELDS = "attempt,conclusion,createdAt,databaseId,displayTitle,event,headBranch,headSha,name,number,startedAt,status,updatedAt,url,workflowDatabaseId,workflowName";
58130
58790
  function buildRunListSpec(o) {
58131
- const args = withJson(withRepo(["run", "list"], parseBrokerRepo(o.repo, "--repo")), parseBrokerJsonFields(o.json), o.json);
58791
+ const args = withJson(withRepo(["run", "list"], parseBrokerRepo(o.repo, "--repo")), parseBrokerJsonFields(o.json), o.json, RUN_LIST_JSON_FIELDS);
58132
58792
  const workflow = parseBrokerWorkflow(o.workflow);
58133
58793
  if (workflow) args.push("--workflow", workflow);
58134
58794
  const branch = parseBrokerBranch(o.branch);
@@ -58211,7 +58871,7 @@ function parseBrokerLabel(raw) {
58211
58871
  return raw;
58212
58872
  }
58213
58873
  function buildIssueListSpec(o) {
58214
- const args = withJson(withRepo(["issue", "list"], parseBrokerRepo(o.repo, "--repo")), parseBrokerJsonFields(o.json), o.json);
58874
+ const args = withJson(withRepo(["issue", "list"], parseBrokerRepo(o.repo, "--repo")), parseBrokerJsonFields(o.json), o.json, ISSUE_LIST_JSON_FIELDS);
58215
58875
  const search = parseBrokerSearch(o.search);
58216
58876
  if (search !== void 0) args.push("--search", search);
58217
58877
  const assignee = parseBrokerAssignee(o.assignee);
@@ -59212,9 +59872,9 @@ function registerPrLifecycleCommands(program3) {
59212
59872
 
59213
59873
  // src/review-verdict.ts
59214
59874
  var import_node_child_process15 = require("node:child_process");
59215
- var import_node_fs47 = require("node:fs");
59875
+ var import_node_fs48 = require("node:fs");
59216
59876
  var import_node_os24 = require("node:os");
59217
- var import_node_path43 = require("node:path");
59877
+ var import_node_path44 = require("node:path");
59218
59878
  init_cli_shared();
59219
59879
  var REVIEW_VERDICT_MARKER = "<!-- zeroci-review v1 -->";
59220
59880
  var REVIEW_VERDICTS = ["PROCEED", "CORRECT", "ESCALATE"];
@@ -59379,15 +60039,15 @@ async function readPrIssueComments(number, repo) {
59379
60039
  return comments;
59380
60040
  }
59381
60041
  async function postPrCommentFromFile(number, repo, body) {
59382
- const dir = (0, import_node_fs47.mkdtempSync)((0, import_node_path43.join)((0, import_node_os24.tmpdir)(), "mmi-review-verdict-"));
59383
- const path2 = (0, import_node_path43.join)(dir, "body.md");
60042
+ const dir = (0, import_node_fs48.mkdtempSync)((0, import_node_path44.join)((0, import_node_os24.tmpdir)(), "mmi-review-verdict-"));
60043
+ const path2 = (0, import_node_path44.join)(dir, "body.md");
59384
60044
  try {
59385
- (0, import_node_fs47.writeFileSync)(path2, body, "utf8");
60045
+ (0, import_node_fs48.writeFileSync)(path2, body, "utf8");
59386
60046
  const { stdout } = await execFileP("gh", ["pr", "comment", number, "--repo", repo, "--body-file", path2], { timeout: GH_MUTATION_TIMEOUT_MS });
59387
60047
  return stdout.trim();
59388
60048
  } finally {
59389
60049
  try {
59390
- (0, import_node_fs47.rmSync)(dir, { recursive: true, force: true });
60050
+ (0, import_node_fs48.rmSync)(dir, { recursive: true, force: true });
59391
60051
  } catch {
59392
60052
  }
59393
60053
  }
@@ -59800,18 +60460,18 @@ function createPreparationDeps(input, io = {}) {
59800
60460
  }
59801
60461
 
59802
60462
  // src/worktree-residue-sweep.ts
59803
- var import_node_fs51 = require("node:fs");
59804
- var import_node_path47 = require("node:path");
60463
+ var import_node_fs52 = require("node:fs");
60464
+ var import_node_path48 = require("node:path");
59805
60465
 
59806
60466
  // src/worktree-merge-cleanup.ts
59807
- var import_node_fs50 = require("node:fs");
59808
- var import_node_path46 = require("node:path");
60467
+ var import_node_fs51 = require("node:fs");
60468
+ var import_node_path47 = require("node:path");
59809
60469
  init_cli_shared();
59810
60470
 
59811
60471
  // src/stage-runner.ts
59812
60472
  var import_node_child_process17 = require("node:child_process");
59813
- var import_node_fs49 = require("node:fs");
59814
- var import_node_path45 = require("node:path");
60473
+ var import_node_fs50 = require("node:fs");
60474
+ var import_node_path46 = require("node:path");
59815
60475
  var import_node_net = require("node:net");
59816
60476
  var import_node_util5 = require("node:util");
59817
60477
 
@@ -59821,8 +60481,8 @@ function normalizeEol2(s) {
59821
60481
  }
59822
60482
 
59823
60483
  // src/env-detection.ts
59824
- var import_node_fs48 = require("node:fs");
59825
- var import_node_path44 = require("node:path");
60484
+ var import_node_fs49 = require("node:fs");
60485
+ var import_node_path45 = require("node:path");
59826
60486
  var SKIPPED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", ".jerv", ".pi", ".venv", "venv"]);
59827
60487
  function isEnvBaseName(name) {
59828
60488
  return name.toLowerCase() === ".env";
@@ -59832,7 +60492,7 @@ function findEnvFiles(root) {
59832
60492
  const walk2 = (dir, rel) => {
59833
60493
  let entries;
59834
60494
  try {
59835
- entries = (0, import_node_fs48.readdirSync)(dir, { withFileTypes: true });
60495
+ entries = (0, import_node_fs49.readdirSync)(dir, { withFileTypes: true });
59836
60496
  } catch {
59837
60497
  return;
59838
60498
  }
@@ -59840,13 +60500,13 @@ function findEnvFiles(root) {
59840
60500
  const relPath = rel ? `${rel}/${entry.name}` : entry.name;
59841
60501
  if (entry.isDirectory()) {
59842
60502
  if (SKIPPED_DIRS.has(entry.name)) continue;
59843
- walk2((0, import_node_path44.join)(dir, entry.name), relPath);
60503
+ walk2((0, import_node_path45.join)(dir, entry.name), relPath);
59844
60504
  continue;
59845
60505
  }
59846
60506
  if (isEnvBaseName(entry.name)) {
59847
60507
  let isDir = false;
59848
60508
  try {
59849
- isDir = (0, import_node_fs48.lstatSync)((0, import_node_path44.join)(dir, entry.name)).isDirectory();
60509
+ isDir = (0, import_node_fs49.lstatSync)((0, import_node_path45.join)(dir, entry.name)).isDirectory();
59850
60510
  } catch {
59851
60511
  }
59852
60512
  if (!isDir) found.push(relPath);
@@ -59985,12 +60645,12 @@ var DOCKER_TIMEOUT_MS = 15e3;
59985
60645
  function stageBash() {
59986
60646
  if (process.platform !== "win32") return "bash";
59987
60647
  const configured = process.env.SHELL;
59988
- if (configured && (0, import_node_path45.isAbsolute)(configured) && /[/\\]bash(?:\.exe)?$/i.test(configured) && !/[/\\](?:System32|Sysnative)[/\\]bash(?:\.exe)?$/i.test(configured) && (0, import_node_fs49.existsSync)(configured)) return configured;
60648
+ if (configured && (0, import_node_path46.isAbsolute)(configured) && /[/\\]bash(?:\.exe)?$/i.test(configured) && !/[/\\](?:System32|Sysnative)[/\\]bash(?:\.exe)?$/i.test(configured) && (0, import_node_fs50.existsSync)(configured)) return configured;
59989
60649
  const candidates = [
59990
- (0, import_node_path45.join)(process.env.ProgramFiles || "C:\\Program Files", "Git", "bin", "bash.exe"),
59991
- ...process.env.LOCALAPPDATA ? [(0, import_node_path45.join)(process.env.LOCALAPPDATA, "Programs", "Git", "bin", "bash.exe")] : []
60650
+ (0, import_node_path46.join)(process.env.ProgramFiles || "C:\\Program Files", "Git", "bin", "bash.exe"),
60651
+ ...process.env.LOCALAPPDATA ? [(0, import_node_path46.join)(process.env.LOCALAPPDATA, "Programs", "Git", "bin", "bash.exe")] : []
59992
60652
  ];
59993
- const bash = candidates.find((path2) => (0, import_node_fs49.existsSync)(path2));
60653
+ const bash = candidates.find((path2) => (0, import_node_fs50.existsSync)(path2));
59994
60654
  if (!bash) throw new Error("Local stages require Git Bash on Windows. Install Git for Windows or set SHELL to its absolute bash.exe path.");
59995
60655
  return bash;
59996
60656
  }
@@ -60128,7 +60788,7 @@ function appendForceRecreate(up) {
60128
60788
  return `${up.trimEnd()} --force-recreate`;
60129
60789
  }
60130
60790
  function stageStatePath(cwd = process.cwd()) {
60131
- return (0, import_node_path45.join)(cwd, "tmp", "stage", "state.json");
60791
+ return (0, import_node_path46.join)(cwd, "tmp", "stage", "state.json");
60132
60792
  }
60133
60793
  function normPath(path2) {
60134
60794
  return path2.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
@@ -60345,8 +61005,8 @@ function stageProcessEnv(stagePort, extraEnv) {
60345
61005
  }
60346
61006
  function composeResolvesPort(cwd) {
60347
61007
  if (process.env.PORT) return true;
60348
- const envFile = (0, import_node_path45.join)(cwd, ".env");
60349
- return (0, import_node_fs49.existsSync)(envFile) && envFileKeys((0, import_node_fs49.readFileSync)(envFile, "utf8")).has("PORT");
61008
+ const envFile = (0, import_node_path46.join)(cwd, ".env");
61009
+ return (0, import_node_fs50.existsSync)(envFile) && envFileKeys((0, import_node_fs50.readFileSync)(envFile, "utf8")).has("PORT");
60350
61010
  }
60351
61011
  function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
60352
61012
  const composeProject = config.env?.COMPOSE_PROJECT_NAME ?? vaultEnvMerge?.COMPOSE_PROJECT_NAME ?? stageComposeProjectName(cwd);
@@ -60361,13 +61021,13 @@ function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
60361
61021
  function stageComposeFiles(cwd) {
60362
61022
  const files = ["docker-compose.yml"];
60363
61023
  for (const conventional of ["docker-compose.override.yml", "docker-compose.override.yaml"]) {
60364
- if ((0, import_node_fs49.existsSync)((0, import_node_path45.join)(cwd, conventional))) files.push(conventional);
61024
+ if ((0, import_node_fs50.existsSync)((0, import_node_path46.join)(cwd, conventional))) files.push(conventional);
60365
61025
  }
60366
61026
  return [...files, RUNTIME_ENV_OVERRIDE_RELPATH];
60367
61027
  }
60368
61028
  async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
60369
61029
  const keys = Object.keys(vaultEnvMerge ?? {});
60370
- if (!keys.length || !(0, import_node_fs49.existsSync)((0, import_node_path45.join)(cwd, "docker-compose.yml"))) return {};
61030
+ if (!keys.length || !(0, import_node_fs50.existsSync)((0, import_node_path46.join)(cwd, "docker-compose.yml"))) return {};
60371
61031
  let configJson;
60372
61032
  try {
60373
61033
  const { stdout } = await execFileP3("docker", ["compose", "config", "--format", "json"], {
@@ -60383,21 +61043,21 @@ async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
60383
61043
  }
60384
61044
  const body = renderRuntimeEnvOverride(runtimeEnvMarkedServices(configJson), keys, runtimeEnvExcludes(configJson), runtimeEnvSelects(configJson));
60385
61045
  if (!body) return {};
60386
- const overridePath = (0, import_node_path45.join)(cwd, RUNTIME_ENV_OVERRIDE_RELPATH);
60387
- (0, import_node_fs49.mkdirSync)((0, import_node_path45.join)(cwd, "tmp", "stage"), { recursive: true });
60388
- (0, import_node_fs49.writeFileSync)(overridePath, body, "utf8");
61046
+ const overridePath = (0, import_node_path46.join)(cwd, RUNTIME_ENV_OVERRIDE_RELPATH);
61047
+ (0, import_node_fs50.mkdirSync)((0, import_node_path46.join)(cwd, "tmp", "stage"), { recursive: true });
61048
+ (0, import_node_fs50.writeFileSync)(overridePath, body, "utf8");
60389
61049
  return stageComposeFileEnv(stageComposeFiles(cwd));
60390
61050
  }
60391
61051
  async function ensureStageRuntimeEnv(config, opts, cwd) {
60392
61052
  if (!config.ensureEnv) return;
60393
- const target = (0, import_node_path45.join)(cwd, config.ensureEnv.target);
60394
- const example = (0, import_node_path45.join)(cwd, config.ensureEnv.example);
60395
- if (!(0, import_node_fs49.existsSync)(target) && (0, import_node_fs49.existsSync)(example)) {
60396
- (0, import_node_fs49.copyFileSync)(example, target);
60397
- } else if ((0, import_node_fs49.existsSync)(target) && (0, import_node_fs49.existsSync)(example)) {
60398
- const stale = detectStaleEnvFile((0, import_node_fs49.readFileSync)(example, "utf8"), (0, import_node_fs49.readFileSync)(target, "utf8"), {
60399
- exampleMtimeMs: (0, import_node_fs49.statSync)(example).mtimeMs,
60400
- targetMtimeMs: (0, import_node_fs49.statSync)(target).mtimeMs
61053
+ const target = (0, import_node_path46.join)(cwd, config.ensureEnv.target);
61054
+ const example = (0, import_node_path46.join)(cwd, config.ensureEnv.example);
61055
+ if (!(0, import_node_fs50.existsSync)(target) && (0, import_node_fs50.existsSync)(example)) {
61056
+ (0, import_node_fs50.copyFileSync)(example, target);
61057
+ } else if ((0, import_node_fs50.existsSync)(target) && (0, import_node_fs50.existsSync)(example)) {
61058
+ const stale = detectStaleEnvFile((0, import_node_fs50.readFileSync)(example, "utf8"), (0, import_node_fs50.readFileSync)(target, "utf8"), {
61059
+ exampleMtimeMs: (0, import_node_fs50.statSync)(example).mtimeMs,
61060
+ targetMtimeMs: (0, import_node_fs50.statSync)(target).mtimeMs
60401
61061
  });
60402
61062
  if (stale) {
60403
61063
  const msg = `stale ${config.ensureEnv.target} (${stale}) \u2014 delete it or refresh from ${config.ensureEnv.example} before re-running mmi-cli stage`;
@@ -60405,8 +61065,8 @@ async function ensureStageRuntimeEnv(config, opts, cwd) {
60405
61065
  console.error(`mmi-cli stage: ${msg} (allowed via --allow-stale-env)`);
60406
61066
  }
60407
61067
  }
60408
- if (opts.vaultEnvMerge && Object.keys(opts.vaultEnvMerge).length && (0, import_node_fs49.existsSync)(target)) {
60409
- (0, import_node_fs49.writeFileSync)(target, mergeEnvSecretsIntoFile((0, import_node_fs49.readFileSync)(target, "utf8"), opts.vaultEnvMerge), "utf8");
61068
+ if (opts.vaultEnvMerge && Object.keys(opts.vaultEnvMerge).length && (0, import_node_fs50.existsSync)(target)) {
61069
+ (0, import_node_fs50.writeFileSync)(target, mergeEnvSecretsIntoFile((0, import_node_fs50.readFileSync)(target, "utf8"), opts.vaultEnvMerge), "utf8");
60410
61070
  }
60411
61071
  }
60412
61072
  async function gitText(cwd, args) {
@@ -60436,20 +61096,20 @@ async function resolveGlobalStatePath(cwd, explicit) {
60436
61096
  return void 0;
60437
61097
  }
60438
61098
  function readState(path2) {
60439
- if (!(0, import_node_fs49.existsSync)(path2)) return null;
61099
+ if (!(0, import_node_fs50.existsSync)(path2)) return null;
60440
61100
  try {
60441
- return JSON.parse((0, import_node_fs49.readFileSync)(path2, "utf8"));
61101
+ return JSON.parse((0, import_node_fs50.readFileSync)(path2, "utf8"));
60442
61102
  } catch {
60443
61103
  return null;
60444
61104
  }
60445
61105
  }
60446
61106
  function mkdirFor(path2) {
60447
61107
  const dir = path2.slice(0, Math.max(path2.lastIndexOf("/"), path2.lastIndexOf("\\")));
60448
- (0, import_node_fs49.mkdirSync)(dir, { recursive: true });
61108
+ (0, import_node_fs50.mkdirSync)(dir, { recursive: true });
60449
61109
  }
60450
61110
  function writeState(path2, state) {
60451
61111
  mkdirFor(path2);
60452
- (0, import_node_fs49.writeFileSync)(path2, JSON.stringify(state, null, 2), "utf8");
61112
+ (0, import_node_fs50.writeFileSync)(path2, JSON.stringify(state, null, 2), "utf8");
60453
61113
  }
60454
61114
  function writeStagePortReservation(port, cwd, statePath, globalStatePath, now) {
60455
61115
  const reservation = {
@@ -60478,7 +61138,7 @@ async function cleanupStageState(state, paths, timeoutMs, fallbackCwd, currentEn
60478
61138
  );
60479
61139
  }
60480
61140
  for (const path2 of [...new Set(paths.filter((p) => Boolean(p)))]) {
60481
- (0, import_node_fs49.rmSync)(path2, { force: true });
61141
+ (0, import_node_fs50.rmSync)(path2, { force: true });
60482
61142
  }
60483
61143
  }
60484
61144
  async function killTree(pid) {
@@ -60725,8 +61385,8 @@ async function runStage(config = {}, opts = {}) {
60725
61385
  });
60726
61386
  }
60727
61387
  } catch (e) {
60728
- (0, import_node_fs49.rmSync)(statePath, { force: true });
60729
- if (globalStatePath && globalStatePath !== statePath) (0, import_node_fs49.rmSync)(globalStatePath, { force: true });
61388
+ (0, import_node_fs50.rmSync)(statePath, { force: true });
61389
+ if (globalStatePath && globalStatePath !== statePath) (0, import_node_fs50.rmSync)(globalStatePath, { force: true });
60730
61390
  throw e;
60731
61391
  }
60732
61392
  const started = await startStage(config, {
@@ -60745,9 +61405,9 @@ function normPath2(p) {
60745
61405
  return p.replace(/\\/g, "/").replace(/\/+$/, "");
60746
61406
  }
60747
61407
  function unlinkNodeModulesJunction(wtPath) {
60748
- const nm = (0, import_node_path46.join)(wtPath, "node_modules");
61408
+ const nm = (0, import_node_path47.join)(wtPath, "node_modules");
60749
61409
  try {
60750
- if ((0, import_node_fs50.lstatSync)(nm).isSymbolicLink()) (0, import_node_fs50.rmdirSync)(nm);
61410
+ if ((0, import_node_fs51.lstatSync)(nm).isSymbolicLink()) (0, import_node_fs51.rmdirSync)(nm);
60751
61411
  return { ok: true };
60752
61412
  } catch (e) {
60753
61413
  if (e.code === "ENOENT") return { ok: true };
@@ -60758,8 +61418,8 @@ function defaultSleep2(ms) {
60758
61418
  return new Promise((resolve7) => setTimeout(resolve7, ms));
60759
61419
  }
60760
61420
  async function removeResidueDirectory(wtPath, options = {}) {
60761
- const { sleep: sleep2 = defaultSleep2, removeEmptyDir = import_node_fs50.rmdirSync, exists = import_node_fs50.existsSync } = options;
60762
- const removeRecursive = options.removeRecursive ?? ((path2) => (0, import_node_fs50.rmSync)(path2, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }));
61421
+ const { sleep: sleep2 = defaultSleep2, removeEmptyDir = import_node_fs51.rmdirSync, exists = import_node_fs51.existsSync } = options;
61422
+ const removeRecursive = options.removeRecursive ?? ((path2) => (0, import_node_fs51.rmSync)(path2, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }));
60763
61423
  const junction = unlinkNodeModulesJunction(wtPath);
60764
61424
  if (!junction.ok) return junction;
60765
61425
  try {
@@ -60882,28 +61542,28 @@ async function preCleanWorktreeForRemoval(wtPath, execGit) {
60882
61542
  }
60883
61543
  async function listNestedIgnoredNodeModules(wtPath, execGit) {
60884
61544
  const out = await execGit(["-C", wtPath, "ls-files", "--others", "--ignored", "--exclude-standard", "--directory"]).catch(() => "");
60885
- return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0, import_node_path46.join)(wtPath, line.slice(0, -1)));
61545
+ return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0, import_node_path47.join)(wtPath, line.slice(0, -1)));
60886
61546
  }
60887
61547
  function safeRemoveTree(path2) {
60888
- const stat4 = (0, import_node_fs50.lstatSync)(path2);
61548
+ const stat4 = (0, import_node_fs51.lstatSync)(path2);
60889
61549
  if (stat4.isSymbolicLink()) {
60890
61550
  try {
60891
- (0, import_node_fs50.rmdirSync)(path2);
61551
+ (0, import_node_fs51.rmdirSync)(path2);
60892
61552
  } catch {
60893
- (0, import_node_fs50.unlinkSync)(path2);
61553
+ (0, import_node_fs51.unlinkSync)(path2);
60894
61554
  }
60895
61555
  return;
60896
61556
  }
60897
61557
  if (stat4.isDirectory()) {
60898
- for (const entry of (0, import_node_fs50.readdirSync)(path2)) safeRemoveTree((0, import_node_path46.join)(path2, entry));
60899
- (0, import_node_fs50.rmdirSync)(path2);
61558
+ for (const entry of (0, import_node_fs51.readdirSync)(path2)) safeRemoveTree((0, import_node_path47.join)(path2, entry));
61559
+ (0, import_node_fs51.rmdirSync)(path2);
60900
61560
  return;
60901
61561
  }
60902
- (0, import_node_fs50.unlinkSync)(path2);
61562
+ (0, import_node_fs51.unlinkSync)(path2);
60903
61563
  }
60904
61564
  async function removeWorktreeNodeModules(wtPath) {
60905
61565
  try {
60906
- safeRemoveTree((0, import_node_path46.join)(wtPath, "node_modules"));
61566
+ safeRemoveTree((0, import_node_path47.join)(wtPath, "node_modules"));
60907
61567
  return { ok: true };
60908
61568
  } catch (e) {
60909
61569
  if (e.code === "ENOENT") return { ok: true };
@@ -60915,7 +61575,7 @@ function errorMessage(e) {
60915
61575
  }
60916
61576
  function resolvedOrRaw(path2) {
60917
61577
  try {
60918
- return normPath2((0, import_node_fs50.realpathSync)(path2));
61578
+ return normPath2((0, import_node_fs51.realpathSync)(path2));
60919
61579
  } catch {
60920
61580
  return normPath2(path2);
60921
61581
  }
@@ -60923,10 +61583,10 @@ function resolvedOrRaw(path2) {
60923
61583
  function unlinkEscapingReparsePoints(root, primaryRoot) {
60924
61584
  let realRoot;
60925
61585
  try {
60926
- if ((0, import_node_fs50.lstatSync)(root).isSymbolicLink()) {
61586
+ if ((0, import_node_fs51.lstatSync)(root).isSymbolicLink()) {
60927
61587
  return { ok: false, error: `delete root ${normPath2(root)} is a reparse point resolving to ${resolvedOrRaw(root)}` };
60928
61588
  }
60929
- realRoot = normPath2((0, import_node_fs50.realpathSync)(root));
61589
+ realRoot = normPath2((0, import_node_fs51.realpathSync)(root));
60930
61590
  } catch (e) {
60931
61591
  if (e.code === "ENOENT") return { ok: true, unlinked: [] };
60932
61592
  return { ok: false, error: `cannot resolve delete root ${normPath2(root)}: ${errorMessage(e)}` };
@@ -60942,17 +61602,17 @@ function unlinkEscapingReparsePoints(root, primaryRoot) {
60942
61602
  const dir = stack.pop();
60943
61603
  let entries;
60944
61604
  try {
60945
- entries = (0, import_node_fs50.readdirSync)(dir, { withFileTypes: true });
61605
+ entries = (0, import_node_fs51.readdirSync)(dir, { withFileTypes: true });
60946
61606
  } catch (e) {
60947
61607
  if (e.code === "ENOENT") continue;
60948
61608
  return { ok: false, error: `cannot scan ${normPath2(dir)} for reparse points: ${errorMessage(e)}` };
60949
61609
  }
60950
61610
  for (const entry of entries) {
60951
- const child2 = (0, import_node_path46.join)(dir, entry.name);
61611
+ const child2 = (0, import_node_path47.join)(dir, entry.name);
60952
61612
  if (entry.isSymbolicLink()) {
60953
61613
  let target = "";
60954
61614
  try {
60955
- target = normPath2((0, import_node_fs50.realpathSync)(child2));
61615
+ target = normPath2((0, import_node_fs51.realpathSync)(child2));
60956
61616
  } catch {
60957
61617
  target = "";
60958
61618
  }
@@ -61135,7 +61795,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
61135
61795
  return report;
61136
61796
  }
61137
61797
  const execGit = options.execGit ?? (async (args) => (await execFileP("git", args, { timeout: GIT_TIMEOUT_MS })).stdout);
61138
- const pathExists = options.pathExists ?? import_node_fs50.existsSync;
61798
+ const pathExists = options.pathExists ?? import_node_fs51.existsSync;
61139
61799
  let afterWorktrees = [];
61140
61800
  try {
61141
61801
  afterWorktrees = parseGitWorktreePorcelain(await execGit(["worktree", "list", "--porcelain"]));
@@ -61275,7 +61935,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
61275
61935
  const preHelperGuard = unlinkEscapingReparsePoints(wtPath, options.primaryRoot);
61276
61936
  if (!preHelperGuard.ok) return refuseReparseEscape(preHelperGuard.error);
61277
61937
  unlinkedReparsePoints.push(...preHelperGuard.unlinked);
61278
- if (pathExists((0, import_node_path46.join)(wtPath, "node_modules"))) {
61938
+ if (pathExists((0, import_node_path47.join)(wtPath, "node_modules"))) {
61279
61939
  const nmRemoved = await (options.removeRealNodeModules ?? removeWorktreeNodeModules)(wtPath);
61280
61940
  if (!nmRemoved.ok) {
61281
61941
  report.worktree = {
@@ -61411,18 +62071,18 @@ function renderPrMergeCleanupLines(cleanup) {
61411
62071
  // src/worktree-residue-sweep.ts
61412
62072
  var RESIDUE_LEDGER_FILE = "mmi-worktree-residue.json";
61413
62073
  function canonicalWorktreeRoot(primary) {
61414
- return (0, import_node_path47.join)((0, import_node_path47.dirname)(primary), "mmi-worktrees", (0, import_node_path47.basename)(primary));
62074
+ return (0, import_node_path48.join)((0, import_node_path48.dirname)(primary), "mmi-worktrees", (0, import_node_path48.basename)(primary));
61415
62075
  }
61416
62076
  function norm(p) {
61417
62077
  const s = p.replace(/\\/g, "/").replace(/\/+$/, "");
61418
62078
  return process.platform === "win32" ? s.toLowerCase() : s;
61419
62079
  }
61420
62080
  function residueLedgerPath(gitCommonDir) {
61421
- return (0, import_node_path47.join)(gitCommonDir, RESIDUE_LEDGER_FILE);
62081
+ return (0, import_node_path48.join)(gitCommonDir, RESIDUE_LEDGER_FILE);
61422
62082
  }
61423
62083
  function readPaths(file, deps) {
61424
62084
  try {
61425
- const parsed = JSON.parse((deps.readLedger ?? ((f) => (0, import_node_fs51.readFileSync)(f, "utf8")))(file));
62085
+ const parsed = JSON.parse((deps.readLedger ?? ((f) => (0, import_node_fs52.readFileSync)(f, "utf8")))(file));
61426
62086
  return Array.isArray(parsed) ? parsed.filter((p) => typeof p === "string") : [];
61427
62087
  } catch {
61428
62088
  return [];
@@ -61431,8 +62091,8 @@ function readPaths(file, deps) {
61431
62091
  function writePaths(file, paths, deps) {
61432
62092
  try {
61433
62093
  const write = deps.writeLedger ?? ((f, t) => {
61434
- (0, import_node_fs51.mkdirSync)((0, import_node_path47.dirname)(f), { recursive: true });
61435
- (0, import_node_fs51.writeFileSync)(f, t, "utf8");
62094
+ (0, import_node_fs52.mkdirSync)((0, import_node_path48.dirname)(f), { recursive: true });
62095
+ (0, import_node_fs52.writeFileSync)(f, t, "utf8");
61436
62096
  });
61437
62097
  write(file, `${JSON.stringify([...new Set(paths)], null, 1)}
61438
62098
  `);
@@ -61446,17 +62106,17 @@ function recordResidue(gitCommonDir, path2, deps = {}) {
61446
62106
  writePaths(file, paths, deps);
61447
62107
  }
61448
62108
  async function sweepWorktreeResidue(gitCommonDir, registered, deps = {}) {
61449
- const exists = deps.exists ?? import_node_fs51.existsSync;
61450
- const readDir = deps.readDir ?? ((p) => (0, import_node_fs51.readdirSync)(p));
62109
+ const exists = deps.exists ?? import_node_fs52.existsSync;
62110
+ const readDir = deps.readDir ?? ((p) => (0, import_node_fs52.readdirSync)(p));
61451
62111
  const isDir = deps.isDir ?? ((p) => {
61452
62112
  try {
61453
- return (0, import_node_fs51.statSync)(p).isDirectory();
62113
+ return (0, import_node_fs52.statSync)(p).isDirectory();
61454
62114
  } catch {
61455
62115
  return false;
61456
62116
  }
61457
62117
  });
61458
62118
  const removeRecorded = deps.removeRecorded ?? ((p) => removeResidueDirectory(p));
61459
- const removeEmpty = deps.removeEmpty ?? ((p) => (0, import_node_fs51.rmdirSync)(p));
62119
+ const removeEmpty = deps.removeEmpty ?? ((p) => (0, import_node_fs52.rmdirSync)(p));
61460
62120
  const report = { swept: [], kept: [], dropped: [], emptyRemoved: [], unrecorded: [] };
61461
62121
  const live = new Set(registered.map(norm));
61462
62122
  const primary = registered[0] ? norm(registered[0]) : void 0;
@@ -61491,7 +62151,7 @@ async function sweepWorktreeResidue(gitCommonDir, registered, deps = {}) {
61491
62151
  } catch {
61492
62152
  }
61493
62153
  for (const name of children) {
61494
- const child2 = (0, import_node_path47.join)(root, name);
62154
+ const child2 = (0, import_node_path48.join)(root, name);
61495
62155
  const key = norm(child2);
61496
62156
  if (live.has(key) || key === primary || keptKeys.has(key) || !isDir(child2)) continue;
61497
62157
  let entries;
@@ -61534,10 +62194,10 @@ function argvWantsJson2() {
61534
62194
  return process.argv.some((a) => a === "--json" || a.startsWith("--json="));
61535
62195
  }
61536
62196
  function hubRoot() {
61537
- const fromPkg = (0, import_node_path48.join)(__dirname, "..", "..");
62197
+ const fromPkg = (0, import_node_path49.join)(__dirname, "..", "..");
61538
62198
  const marker = "skills/bootstrap/seeds/manifest.json";
61539
- if ((0, import_node_fs52.existsSync)((0, import_node_path48.join)(fromPkg, marker))) return fromPkg;
61540
- if ((0, import_node_fs52.existsSync)((0, import_node_path48.join)(process.cwd(), marker))) return process.cwd();
62199
+ if ((0, import_node_fs53.existsSync)((0, import_node_path49.join)(fromPkg, marker))) return fromPkg;
62200
+ if ((0, import_node_fs53.existsSync)((0, import_node_path49.join)(process.cwd(), marker))) return process.cwd();
61541
62201
  return null;
61542
62202
  }
61543
62203
  function ciAuditDeps() {
@@ -61549,8 +62209,8 @@ function ciAuditDeps() {
61549
62209
  getProjectMeta: async (slug) => fetchProjectBySlug(slug, registryClientDeps(await cfgPromise)),
61550
62210
  readSeedFile: (path2) => {
61551
62211
  if (!root) return null;
61552
- const fullPath = (0, import_node_path48.join)(root, path2);
61553
- return (0, import_node_fs52.existsSync)(fullPath) ? (0, import_node_fs52.readFileSync)(fullPath, "utf8") : null;
62212
+ const fullPath = (0, import_node_path49.join)(root, path2);
62213
+ return (0, import_node_fs53.existsSync)(fullPath) ? (0, import_node_fs53.readFileSync)(fullPath, "utf8") : null;
61554
62214
  }
61555
62215
  };
61556
62216
  }
@@ -62417,11 +63077,11 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
62417
63077
  }
62418
63078
  });
62419
63079
  async function listCiWorkflowPaths(cwd = process.cwd()) {
62420
- const wfDir = (0, import_node_path48.join)(cwd, ".github", "workflows");
62421
- if (!(0, import_node_fs52.existsSync)(wfDir)) return [];
62422
- return (0, import_node_fs52.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
63080
+ const wfDir = (0, import_node_path49.join)(cwd, ".github", "workflows");
63081
+ if (!(0, import_node_fs53.existsSync)(wfDir)) return [];
63082
+ return (0, import_node_fs53.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
62423
63083
  try {
62424
- return workflowReportsPrChecks((0, import_node_fs52.readFileSync)((0, import_node_path48.join)(wfDir, name), "utf8"));
63084
+ return workflowReportsPrChecks((0, import_node_fs53.readFileSync)((0, import_node_path49.join)(wfDir, name), "utf8"));
62425
63085
  } catch {
62426
63086
  return true;
62427
63087
  }
@@ -62626,7 +63286,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
62626
63286
  jsonParity(pr.command("review-verdict <number>").description("post an explicit review verdict comment: computes the PR diff patch-id and head sha, renders the v1 contract body, and posts it as a PR comment").requiredOption("--repo <owner/repo>", "target repo").requiredOption("--verdict <verdict>", `one of ${REVIEW_VERDICTS.join("|")}`).requiredOption("--scope <text>", "one line: what was reviewed").requiredOption("--risk <text>", "one line: the residual risk").option("--unverified <text>", "a claim the reviewer could not verify (repeatable)", (v, acc) => [...acc, v], []).requiredOption("--reviewer <id>", "reviewer seat or model id").option("--findings-file <path>", "Markdown findings appended after the JSON block")).action(async (number, o) => {
62627
63287
  if (!isReviewVerdict(o.verdict)) return fail(`pr review-verdict: --verdict must be one of ${REVIEW_VERDICTS.join("|")} (got ${o.verdict})`);
62628
63288
  const verdict = o.verdict;
62629
- const findings = o.findingsFile ? (0, import_node_fs52.readFileSync)(o.findingsFile, "utf8") : void 0;
63289
+ const findings = o.findingsFile ? (0, import_node_fs53.readFileSync)(o.findingsFile, "utf8") : void 0;
62630
63290
  let patch;
62631
63291
  let head;
62632
63292
  try {
@@ -62904,7 +63564,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
62904
63564
  const cleanupMerged = async (r) => {
62905
63565
  if (opts.preserveWorktree) return "cleanup skipped (--preserve-worktree)";
62906
63566
  const ran = await execFileP(process.execPath, [process.argv[1], ...canonicalArgvFor(["pr", "merge", r.pr, "--repo", r.repo])], {
62907
- cwd: (0, import_node_fs52.existsSync)(cwd0) ? cwd0 : primaryRoot0,
63567
+ cwd: (0, import_node_fs53.existsSync)(cwd0) ? cwd0 : primaryRoot0,
62908
63568
  timeout: GH_MUTATION_TIMEOUT_MS * 3
62909
63569
  }).then((x) => ({ stdout: x.stdout, stderr: x.stderr }), (e) => ({ stdout: e.stdout ?? "", stderr: e.stderr ?? "" }));
62910
63570
  const json = ran.stdout.split("\n").reverse().find((l) => l.startsWith("{"));
@@ -62975,7 +63635,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
62975
63635
  const mergeSquashBody = squashBodyTextForMerge(
62976
63636
  closingGuardInput,
62977
63637
  method === "--squash",
62978
- o.squashBodyFile ? (0, import_node_fs52.readFileSync)(o.squashBodyFile, "utf8") : void 0
63638
+ o.squashBodyFile ? (0, import_node_fs53.readFileSync)(o.squashBodyFile, "utf8") : void 0
62979
63639
  );
62980
63640
  if (!o.squashBodyFile) warnSquashBodyClosingStrip("pr merge", closingGuardInput, mergeSquashBody);
62981
63641
  const closingGuardVerdict = evaluateClosingGuard(closingGuardInput, {
@@ -63007,7 +63667,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
63007
63667
  const remote = foreignCwd ? `https://github.com/${targetRepo2}.git` : "origin";
63008
63668
  let foreignCheckout;
63009
63669
  if (foreignCwd) {
63010
- const sibling = (0, import_node_path48.join)((0, import_node_path48.dirname)(beforeWorktrees[0]?.path || startingPath || process.cwd()), targetRepo2.split("/")[1]);
63670
+ const sibling = (0, import_node_path49.join)((0, import_node_path49.dirname)(beforeWorktrees[0]?.path || startingPath || process.cwd()), targetRepo2.split("/")[1]);
63011
63671
  const siblingRepo = repoFromRemoteUrl(await gitOut(["-C", sibling, "remote", "get-url", "origin"]).catch(() => ""));
63012
63672
  if (siblingRepo?.toLowerCase() === targetRepo2.toLowerCase()) {
63013
63673
  const siblingWorktrees = await execFileP("git", ["-C", sibling, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).then((r) => parseGitWorktreePorcelain(r.stdout)).catch(() => void 0);
@@ -63126,7 +63786,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
63126
63786
  }
63127
63787
  if (!repoForPostCleanup) throw e;
63128
63788
  console.warn(`pr merge: gh GraphQL rate-limited \u2014 merging PR #${number} via REST PUT instead (#4588).`);
63129
- const commitMessage = bodyFile ? (0, import_node_fs52.readFileSync)(bodyFile, "utf8") : void 0;
63789
+ const commitMessage = bodyFile ? (0, import_node_fs53.readFileSync)(bodyFile, "utf8") : void 0;
63130
63790
  await defaultGitHubClient().rest("PUT", `repos/${repoForPostCleanup}/pulls/${number}/merge`, {
63131
63791
  body: { merge_method: method.slice(2), ...commitMessage ? { commit_message: commitMessage } : {} },
63132
63792
  timeoutMs: GH_MUTATION_TIMEOUT_MS
@@ -63241,7 +63901,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
63241
63901
  let residueSweep;
63242
63902
  if (!(foreignCwd && !foreignCheckout)) {
63243
63903
  try {
63244
- gitCommonDir = (0, import_node_path48.resolve)(primaryRoot, (await execFileP("git", ["-C", primaryRoot, "rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS })).stdout.trim());
63904
+ gitCommonDir = (0, import_node_path49.resolve)(primaryRoot, (await execFileP("git", ["-C", primaryRoot, "rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS })).stdout.trim());
63245
63905
  residueSweep = await sweepWorktreeResidue(gitCommonDir, beforeWorktrees.map((w) => w.path));
63246
63906
  } catch {
63247
63907
  }
@@ -63259,7 +63919,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
63259
63919
  preserveWorktree: o.preserveWorktree,
63260
63920
  gcAcknowledged: o.gc,
63261
63921
  expectedHeadOid: headRefOid,
63262
- pathExists: (p) => (0, import_node_fs52.existsSync)(p),
63922
+ pathExists: (p) => (0, import_node_fs53.existsSync)(p),
63263
63923
  // #5899: pin cleanup git calls to the main checkout — the task worktree this process may be
63264
63924
  // standing in is removed mid-cleanup, so a cwd-relative invocation fails with
63265
63925
  // 'fatal: not a git repository' and leaves a spurious partial-cleanup exit.
@@ -64525,7 +65185,7 @@ function rcRunCauseFromLog(log) {
64525
65185
  function memberAnnounceSummaryFile(repo, summaryFile, memberSummaryFiles) {
64526
65186
  return repo === SUITE_REPO ? summaryFile : memberSummaryFiles[repo];
64527
65187
  }
64528
- var suiteStateDir = (root) => (0, import_node_path50.join)(resolvePrimaryGitDir(root), "mmi-runtime", "suite");
65188
+ var suiteStateDir = (root) => (0, import_node_path51.join)(resolvePrimaryGitDir(root), "mmi-runtime", "suite");
64529
65189
  async function runSuiteMemberRcand(repo, previousRc, rcWasAbsent) {
64530
65190
  const deps = trainApplyDeps();
64531
65191
  deps.suiteCut = suiteCutContext(repo, SUITE_REPO);
@@ -64572,16 +65232,16 @@ async function rcandInCheckout(repo, deps, previousRc, rcWasAbsent) {
64572
65232
  }
64573
65233
  }
64574
65234
  function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr, memberSummaryFilesTr = {}) {
64575
- const recordPath = (0, import_node_path50.join)(suiteStateDir(root), "cut.json");
64576
- const base = (0, import_node_path50.join)(suiteStateDir(root), "checkouts");
65235
+ const recordPath = (0, import_node_path51.join)(suiteStateDir(root), "cut.json");
65236
+ const base = (0, import_node_path51.join)(suiteStateDir(root), "checkouts");
64577
65237
  const checkout = async (repo) => {
64578
65238
  if (repo === SUITE_REPO) return root;
64579
- const target = (0, import_node_path50.join)(base, repo.split("/")[1]);
64580
- if (!(0, import_node_fs53.existsSync)(target)) {
64581
- (0, import_node_fs53.mkdirSync)(base, { recursive: true });
65239
+ const target = (0, import_node_path51.join)(base, repo.split("/")[1]);
65240
+ if (!(0, import_node_fs54.existsSync)(target)) {
65241
+ (0, import_node_fs54.mkdirSync)(base, { recursive: true });
64582
65242
  await exec("git", ["clone", "--quiet", "--branch", "development", `https://github.com/${repo}.git`, target], root, WATCH_TIMEOUT);
64583
65243
  }
64584
- if (!(0, import_node_fs53.existsSync)((0, import_node_path50.join)(target, ".git"))) throw new Error(`${repo}: suite checkout has no .git at ${target}; stop`);
65244
+ if (!(0, import_node_fs54.existsSync)((0, import_node_path51.join)(target, ".git"))) throw new Error(`${repo}: suite checkout has no .git at ${target}; stop`);
64585
65245
  const origin = await exec("git", ["remote", "get-url", "origin"], target);
64586
65246
  if (!origin.toLowerCase().endsWith(`/${repo.toLowerCase()}.git`) && !origin.toLowerCase().endsWith(`/${repo.toLowerCase()}`))
64587
65247
  throw new Error(`${repo}: suite checkout origin mismatch at ${target}; stop`);
@@ -64645,7 +65305,7 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
64645
65305
  if (repo === SUITE_REPO) return inTrain(repo, (deps) => rcandInCheckout(repo, deps, previousRc, rcWasAbsent));
64646
65306
  const target = await checkout(repo);
64647
65307
  return new Promise((resolveResult, reject2) => {
64648
- const child2 = (0, import_node_child_process19.fork)((0, import_node_path49.resolve)(process.argv[1]), ["devops", "suite", "member-rcand-internal", repo, previousRc ?? "", ...rcWasAbsent ? ["--rc-was-absent"] : []], {
65308
+ const child2 = (0, import_node_child_process19.fork)((0, import_node_path50.resolve)(process.argv[1]), ["devops", "suite", "member-rcand-internal", repo, previousRc ?? "", ...rcWasAbsent ? ["--rc-was-absent"] : []], {
64649
65309
  cwd: target,
64650
65310
  stdio: ["ignore", "inherit", "inherit", "ipc"],
64651
65311
  execArgv: []
@@ -64888,9 +65548,9 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
64888
65548
  const body = await gh(["release", "view", leadTag, "--repo", SUITE_REPO, "--json", "body", "--jq", ".body"]);
64889
65549
  if (parseSuiteMapping(body)) return;
64890
65550
  const dir = suiteStateDir(root);
64891
- (0, import_node_fs53.mkdirSync)(dir, { recursive: true });
64892
- const file = (0, import_node_path50.join)(dir, `notes-${leadTag}.md`);
64893
- (0, import_node_fs53.writeFileSync)(file, `${body}
65551
+ (0, import_node_fs54.mkdirSync)(dir, { recursive: true });
65552
+ const file = (0, import_node_path51.join)(dir, `notes-${leadTag}.md`);
65553
+ (0, import_node_fs54.writeFileSync)(file, `${body}
64894
65554
 
64895
65555
  ${suiteMappingBlock(leadTag, mapping)}`);
64896
65556
  await gh(["release", "edit", leadTag, "--repo", SUITE_REPO, "--notes-file", file]);
@@ -64931,7 +65591,7 @@ ${suiteMappingBlock(leadTag, mapping)}`);
64931
65591
 
64932
65592
  // src/suite-commands.ts
64933
65593
  init_cli_shared();
64934
- var import_node_fs54 = require("node:fs");
65594
+ var import_node_fs55 = require("node:fs");
64935
65595
  function parseMemberSummaryFiles(entries) {
64936
65596
  const memberSummaryFiles = {};
64937
65597
  for (const entry of entries) {
@@ -65025,7 +65685,7 @@ function registerSuiteCommands(program3) {
65025
65685
  try {
65026
65686
  const io = suiteTrainIo(root);
65027
65687
  retired = await retireFailedLeadRcLedger({ ledgerPath: releaseLedgerPath(root), rcHead: () => io.head(SUITE_REPO, "rc").then((h) => h ?? void 0), markAbandoned: io.markAbandoned }, o.ownerGo === true);
65028
- if (retired && !(0, import_node_fs54.existsSync)(io.recordPath)) {
65688
+ if (retired && !(0, import_node_fs55.existsSync)(io.recordPath)) {
65029
65689
  console.log(o.json ? JSON.stringify({ retiredLeadRc: retired }, null, 2) : `suite abandon: retired the lead's failed, never-promoted rc candidate ${retired}; marked abandoned on origin (#7854)`);
65030
65690
  return;
65031
65691
  }
@@ -65265,9 +65925,9 @@ function registerJevCommands(program3) {
65265
65925
 
65266
65926
  // src/packages-adopt-export.ts
65267
65927
  var import_node_child_process20 = require("node:child_process");
65268
- var import_node_fs55 = require("node:fs");
65928
+ var import_node_fs56 = require("node:fs");
65269
65929
  var import_node_os25 = require("node:os");
65270
- var import_node_path51 = require("node:path");
65930
+ var import_node_path52 = require("node:path");
65271
65931
  var import_node_util6 = require("node:util");
65272
65932
  init_cli_shared();
65273
65933
 
@@ -65887,38 +66547,38 @@ function expectedFromRun(run, opts) {
65887
66547
  }
65888
66548
  async function adoptExport(opts, deps) {
65889
66549
  if (!/^sha256:[0-9a-f]{64}$/.test(opts.digest)) throw new Error("--digest must be sha256:<64 hex>, from the export run summary");
65890
- const packagePath = (0, import_node_path51.join)(opts.dir, "package.json");
65891
- const lockPath = (0, import_node_path51.join)(opts.dir, "package-lock.json");
65892
- const packageText = (0, import_node_fs55.readFileSync)(packagePath, "utf8");
65893
- const lockText = (0, import_node_fs55.readFileSync)(lockPath, "utf8");
65894
- const work = (0, import_node_fs55.mkdtempSync)((0, import_node_path51.join)((0, import_node_os25.tmpdir)(), "mmi-adopt-export-"));
66550
+ const packagePath = (0, import_node_path52.join)(opts.dir, "package.json");
66551
+ const lockPath = (0, import_node_path52.join)(opts.dir, "package-lock.json");
66552
+ const packageText = (0, import_node_fs56.readFileSync)(packagePath, "utf8");
66553
+ const lockText = (0, import_node_fs56.readFileSync)(lockPath, "utf8");
66554
+ const work = (0, import_node_fs56.mkdtempSync)((0, import_node_path52.join)((0, import_node_os25.tmpdir)(), "mmi-adopt-export-"));
65895
66555
  try {
65896
66556
  const run = JSON.parse((await deps.ghApi(`repos/${EXPORT_REPO}/actions/runs/${opts.run}`)).toString("utf8"));
65897
- (0, import_node_fs55.writeFileSync)((0, import_node_path51.join)(work, "expected.json"), JSON.stringify(expectedFromRun(run, opts)));
65898
- (0, import_node_fs55.writeFileSync)((0, import_node_path51.join)(work, "provider.json"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}`));
65899
- (0, import_node_fs55.writeFileSync)((0, import_node_path51.join)(work, "artifact.zip"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}/zip`));
65900
- const script = (0, import_node_path51.join)(work, "package_export.py");
65901
- (0, import_node_fs55.writeFileSync)(script, package_export_default);
66557
+ (0, import_node_fs56.writeFileSync)((0, import_node_path52.join)(work, "expected.json"), JSON.stringify(expectedFromRun(run, opts)));
66558
+ (0, import_node_fs56.writeFileSync)((0, import_node_path52.join)(work, "provider.json"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}`));
66559
+ (0, import_node_fs56.writeFileSync)((0, import_node_path52.join)(work, "artifact.zip"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}/zip`));
66560
+ const script = (0, import_node_path52.join)(work, "package_export.py");
66561
+ (0, import_node_fs56.writeFileSync)(script, package_export_default);
65902
66562
  await deps.python(script, ["verify", work, opts.ds, opts.screens]);
65903
- const verified = (0, import_node_path51.join)(work, "verified");
65904
- const metadata = Object.fromEntries(PACKAGES.map(({ name, short: short3 }) => [name, JSON.parse((0, import_node_fs55.readFileSync)((0, import_node_path51.join)(verified, `${short3}.metadata.json`), "utf8"))]));
66563
+ const verified = (0, import_node_path52.join)(work, "verified");
66564
+ const metadata = Object.fromEntries(PACKAGES.map(({ name, short: short3 }) => [name, JSON.parse((0, import_node_fs56.readFileSync)((0, import_node_path52.join)(verified, `${short3}.metadata.json`), "utf8"))]));
65905
66565
  const plan = planAdoption(packageText, lockText, metadata);
65906
66566
  for (const { name } of plan.result.adopted) {
65907
66567
  const short3 = PACKAGES.find((p) => p.name === name).short;
65908
- await deps.npm(["cache", "add", (0, import_node_path51.join)(verified, `${short3}.tgz`)], opts.dir);
66568
+ await deps.npm(["cache", "add", (0, import_node_path52.join)(verified, `${short3}.tgz`)], opts.dir);
65909
66569
  }
65910
- (0, import_node_fs55.writeFileSync)(packagePath, plan.packageText);
65911
- (0, import_node_fs55.writeFileSync)(lockPath, plan.lockText);
66570
+ (0, import_node_fs56.writeFileSync)(packagePath, plan.packageText);
66571
+ (0, import_node_fs56.writeFileSync)(lockPath, plan.lockText);
65912
66572
  try {
65913
66573
  await deps.npm(["ci", "--offline", "--ignore-scripts", "--no-audit", "--no-fund"], opts.dir);
65914
66574
  } catch (error) {
65915
- (0, import_node_fs55.writeFileSync)(packagePath, packageText);
65916
- (0, import_node_fs55.writeFileSync)(lockPath, lockText);
66575
+ (0, import_node_fs56.writeFileSync)(packagePath, packageText);
66576
+ (0, import_node_fs56.writeFileSync)(lockPath, lockText);
65917
66577
  throw new Error(`npm ci --offline failed, package.json and package-lock.json restored: ${error.message}`);
65918
66578
  }
65919
66579
  return plan.result;
65920
66580
  } finally {
65921
- (0, import_node_fs55.rmSync)(work, { recursive: true, force: true });
66581
+ (0, import_node_fs56.rmSync)(work, { recursive: true, force: true });
65922
66582
  }
65923
66583
  }
65924
66584
  var execFileBuffer = (0, import_node_util6.promisify)(import_node_child_process20.execFile);
@@ -66022,12 +66682,12 @@ function registerPackagesCommands(program3) {
66022
66682
  init_cli_shared();
66023
66683
 
66024
66684
  // src/wave-status.ts
66025
- var import_node_fs56 = require("node:fs");
66685
+ var import_node_fs57 = require("node:fs");
66026
66686
  function observeStage(worktreePath) {
66027
66687
  const statePath = stageStatePath(worktreePath);
66028
66688
  let raw;
66029
66689
  try {
66030
- raw = (0, import_node_fs56.readFileSync)(statePath, "utf8");
66690
+ raw = (0, import_node_fs57.readFileSync)(statePath, "utf8");
66031
66691
  } catch (error) {
66032
66692
  if (error.code === "ENOENT") return { state: "ok", value: { running: false } };
66033
66693
  return { state: "unavailable", reason: "stage state could not be read" };
@@ -66094,9 +66754,9 @@ async function collectWaveStatus(deps) {
66094
66754
  // src/discovery-commands.ts
66095
66755
  init_github_client();
66096
66756
  init_cli_shared();
66097
- var import_node_fs57 = require("node:fs");
66757
+ var import_node_fs58 = require("node:fs");
66098
66758
  var import_node_os26 = require("node:os");
66099
- var import_node_path52 = require("node:path");
66759
+ var import_node_path53 = require("node:path");
66100
66760
  var GC_GH_TIMEOUT_MS3 = 2e4;
66101
66761
  var defaultStatusDeps = {
66102
66762
  repo: resolveRepo,
@@ -66317,8 +66977,8 @@ async function collectOnboardStatus(opts = {}) {
66317
66977
  }
66318
66978
  const home = (0, import_node_os26.homedir)();
66319
66979
  const plugin = onboardPluginGate({
66320
- readKnown: () => readFileSyncSafe((0, import_node_path52.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs57.readFileSync),
66321
- readSettings: () => readFileSyncSafe((0, import_node_path52.join)(home, ".claude", "settings.json"), import_node_fs57.readFileSync)
66980
+ readKnown: () => readFileSyncSafe((0, import_node_path53.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs58.readFileSync),
66981
+ readSettings: () => readFileSyncSafe((0, import_node_path53.join)(home, ".claude", "settings.json"), import_node_fs58.readFileSync)
66322
66982
  });
66323
66983
  return {
66324
66984
  guide: "Read the MMI org-guide skill before project work; load only the relevant topic. Use mmi-help for a short introduction.",
@@ -69449,8 +70109,8 @@ function registerSessionReport(program3) {
69449
70109
  }
69450
70110
 
69451
70111
  // src/stage-commands.ts
69452
- var import_node_fs58 = require("node:fs");
69453
- var import_node_path53 = require("node:path");
70112
+ var import_node_fs59 = require("node:fs");
70113
+ var import_node_path54 = require("node:path");
69454
70114
  init_cli_shared();
69455
70115
  init_clean_exit();
69456
70116
 
@@ -69698,8 +70358,8 @@ function registerStageCommands(program3) {
69698
70358
  const repo = Array.isArray(project2?.repos) && typeof project2.repos[0] === "string" ? project2.repos[0] : slug;
69699
70359
  const resolution = decideStage({
69700
70360
  registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
69701
- hasCompose: (0, import_node_fs58.existsSync)((0, import_node_path53.join)(process.cwd(), "docker-compose.yml")),
69702
- hasEnvExample: (0, import_node_fs58.existsSync)((0, import_node_path53.join)(process.cwd(), ".env.example")),
70361
+ hasCompose: (0, import_node_fs59.existsSync)((0, import_node_path54.join)(process.cwd(), "docker-compose.yml")),
70362
+ hasEnvExample: (0, import_node_fs59.existsSync)((0, import_node_path54.join)(process.cwd(), ".env.example")),
69703
70363
  repo
69704
70364
  });
69705
70365
  if (resolution.source === "derived" && resolution.derived) {
@@ -69991,25 +70651,25 @@ vercel preview (${preview.branch}): ${d.state}${d.url ? ` \u2014 ${d.url}` : ""}
69991
70651
  }
69992
70652
 
69993
70653
  // src/tenant-artifact.ts
69994
- var import_node_crypto22 = require("node:crypto");
69995
- var import_node_fs59 = require("node:fs");
70654
+ var import_node_crypto23 = require("node:crypto");
70655
+ var import_node_fs60 = require("node:fs");
69996
70656
  var import_promises11 = require("node:fs/promises");
69997
- var import_node_path54 = require("node:path");
70657
+ var import_node_path55 = require("node:path");
69998
70658
  var ARTIFACT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
69999
70659
  var MAX_BYTES2 = 5 * 1024 * 1024 * 1024;
70000
70660
  async function sha256File(path2) {
70001
- const hash = (0, import_node_crypto22.createHash)("sha256");
70002
- for await (const chunk of (0, import_node_fs59.createReadStream)(path2)) hash.update(chunk);
70661
+ const hash = (0, import_node_crypto23.createHash)("sha256");
70662
+ for await (const chunk of (0, import_node_fs60.createReadStream)(path2)) hash.update(chunk);
70003
70663
  return hash.digest("hex");
70004
70664
  }
70005
70665
  async function putTenantArtifact(repo, stage, inputPath, deps) {
70006
70666
  if (!["dev", "rc", "main"].includes(stage)) throw new Error("tenant artifact put: <stage> must be dev, rc, or main");
70007
- const path2 = (0, import_node_path54.resolve)(inputPath);
70667
+ const path2 = (0, import_node_path55.resolve)(inputPath);
70008
70668
  const info = await (0, import_promises11.stat)(path2);
70009
70669
  if (!info.isFile()) throw new Error("tenant artifact put: input path must be a file");
70010
70670
  if (!Number.isSafeInteger(info.size) || info.size < 1 || info.size > MAX_BYTES2) throw new Error(`tenant artifact put: file must be 1..${MAX_BYTES2} bytes`);
70011
- const sha2563 = await sha256File(path2);
70012
- const prepared = await tenantArtifactUpload({ repo, stage, size: info.size, sha256: sha2563 }, deps);
70671
+ const sha2564 = await sha256File(path2);
70672
+ const prepared = await tenantArtifactUpload({ repo, stage, size: info.size, sha256: sha2564 }, deps);
70013
70673
  if (!prepared.ok) {
70014
70674
  const detail = prepared.body?.error ?? prepared.error ?? `HTTP ${prepared.status}`;
70015
70675
  throw new Error(`tenant artifact put: ${detail}`);
@@ -70022,7 +70682,7 @@ async function putTenantArtifact(repo, stage, inputPath, deps) {
70022
70682
  return [key, value];
70023
70683
  }));
70024
70684
  headers["content-length"] = String(info.size);
70025
- const stream = (0, import_node_fs59.createReadStream)(path2);
70685
+ const stream = (0, import_node_fs60.createReadStream)(path2);
70026
70686
  let uploaded;
70027
70687
  try {
70028
70688
  uploaded = await fetch(body.uploadUrl, {
@@ -70037,8 +70697,8 @@ async function putTenantArtifact(repo, stage, inputPath, deps) {
70037
70697
  throw error;
70038
70698
  }
70039
70699
  if (!uploaded.ok) throw new Error(`tenant artifact put: object upload failed (HTTP ${uploaded.status})`);
70040
- if (body.size !== info.size || body.sha256 !== sha2563 || typeof body.expiresAt !== "string" || !Number.isFinite(Date.parse(body.expiresAt))) throw new Error("tenant artifact put: Hub returned inconsistent artifact metadata");
70041
- return { artifactId: body.artifactId, repo, stage, size: info.size, sha256: sha2563, expiresAt: body.expiresAt };
70700
+ if (body.size !== info.size || body.sha256 !== sha2564 || typeof body.expiresAt !== "string" || !Number.isFinite(Date.parse(body.expiresAt))) throw new Error("tenant artifact put: Hub returned inconsistent artifact metadata");
70701
+ return { artifactId: body.artifactId, repo, stage, size: info.size, sha256: sha2564, expiresAt: body.expiresAt };
70042
70702
  }
70043
70703
 
70044
70704
  // src/tenant-logs.ts
@@ -70376,7 +71036,7 @@ function renderVerifySecrets(body) {
70376
71036
  }
70377
71037
 
70378
71038
  // src/command-register-developer.ts
70379
- var import_node_fs64 = require("node:fs");
71039
+ var import_node_fs65 = require("node:fs");
70380
71040
  init_clean_exit();
70381
71041
  init_cli_shared();
70382
71042
  init_error_codes();
@@ -70409,12 +71069,12 @@ async function resolveWhoami(deps) {
70409
71069
  }
70410
71070
 
70411
71071
  // src/command-register-developer.ts
70412
- var import_node_path61 = require("node:path");
71072
+ var import_node_path62 = require("node:path");
70413
71073
 
70414
71074
  // src/doc-refs-command-source.ts
70415
71075
  var import_node_child_process21 = require("node:child_process");
70416
- var import_node_fs60 = require("node:fs");
70417
- var import_node_path55 = require("node:path");
71076
+ var import_node_fs61 = require("node:fs");
71077
+ var import_node_path56 = require("node:path");
70418
71078
  var BUNDLE_MANIFEST_TIMEOUT_MS = 12e4;
70419
71079
  var BUNDLE_MANIFEST_MAX_BUFFER = 64 * 1024 * 1024;
70420
71080
  var flagsOf = (options) => new Set((options ?? []).flatMap((option) => String(option?.flags ?? "").match(/--[\w-]+/g) ?? []));
@@ -70435,7 +71095,7 @@ function inventoryFromManifest(manifest, source) {
70435
71095
  function readBundleManifest(bundle, exec3 = import_node_child_process21.execFileSync) {
70436
71096
  try {
70437
71097
  const text = exec3(process.execPath, [bundle, "commands", "--all", "--json"], {
70438
- cwd: (0, import_node_path55.dirname)(bundle),
71098
+ cwd: (0, import_node_path56.dirname)(bundle),
70439
71099
  encoding: "utf8",
70440
71100
  windowsHide: true,
70441
71101
  timeout: BUNDLE_MANIFEST_TIMEOUT_MS,
@@ -70451,19 +71111,19 @@ function readBundleManifest(bundle, exec3 = import_node_child_process21.execFile
70451
71111
  function isRunningBundle(bundle, argv1 = process.argv[1], platform2 = process.platform) {
70452
71112
  if (!argv1) return false;
70453
71113
  const norm2 = (path2) => {
70454
- const absolute = (0, import_node_path55.resolve)(path2);
71114
+ const absolute = (0, import_node_path56.resolve)(path2);
70455
71115
  return platform2 === "win32" ? absolute.toLowerCase() : absolute;
70456
71116
  };
70457
71117
  const running = norm2(argv1);
70458
- if (!["main.cjs", "index.cjs"].includes((0, import_node_path55.basename)(running))) return false;
70459
- return (0, import_node_path55.dirname)(running) === (0, import_node_path55.dirname)(norm2(bundle));
71118
+ if (!["main.cjs", "index.cjs"].includes((0, import_node_path56.basename)(running))) return false;
71119
+ return (0, import_node_path56.dirname)(running) === (0, import_node_path56.dirname)(norm2(bundle));
70460
71120
  }
70461
71121
  function resolveCommandInventory(deps) {
70462
- const exists = deps.exists ?? import_node_fs60.existsSync;
71122
+ const exists = deps.exists ?? import_node_fs61.existsSync;
70463
71123
  const read = deps.read ?? ((bundle) => readBundleManifest(bundle));
70464
71124
  const warnings = [];
70465
71125
  if (deps.from) {
70466
- const bundle = (0, import_node_path55.isAbsolute)(deps.from) ? deps.from : (0, import_node_path55.resolve)(deps.root, deps.from);
71126
+ const bundle = (0, import_node_path56.isAbsolute)(deps.from) ? deps.from : (0, import_node_path56.resolve)(deps.root, deps.from);
70467
71127
  if (!exists(bundle)) {
70468
71128
  throw new Error(`docs refs --commands-from: ${bundle} does not exist \u2014 name the CLI bundle to read commands from (usually cli/dist/main.cjs)`);
70469
71129
  }
@@ -70473,7 +71133,7 @@ function resolveCommandInventory(deps) {
70473
71133
  }
70474
71134
  return { inventory: inventory2, warnings };
70475
71135
  }
70476
- const treeBundle = (0, import_node_path55.join)(deps.root, "cli", "dist", "main.cjs");
71136
+ const treeBundle = (0, import_node_path56.join)(deps.root, "cli", "dist", "main.cjs");
70477
71137
  if (!exists(treeBundle) || isRunningBundle(treeBundle, deps.argv1, deps.platform)) {
70478
71138
  return { inventory: deps.inProcess(), warnings };
70479
71139
  }
@@ -70590,28 +71250,28 @@ async function executeWaveLand(plan, deps) {
70590
71250
 
70591
71251
  // src/dist-drift.ts
70592
71252
  var import_node_child_process22 = require("node:child_process");
70593
- var import_node_crypto24 = require("node:crypto");
70594
- var import_node_fs62 = require("node:fs");
71253
+ var import_node_crypto25 = require("node:crypto");
71254
+ var import_node_fs63 = require("node:fs");
70595
71255
  var import_node_os27 = require("node:os");
70596
- var import_node_path57 = require("node:path");
71256
+ var import_node_path58 = require("node:path");
70597
71257
 
70598
71258
  // ../scripts/distribution-digest.mjs
70599
- var import_node_crypto23 = require("node:crypto");
70600
- var import_node_fs61 = require("node:fs");
70601
- var import_node_path56 = require("node:path");
71259
+ var import_node_crypto24 = require("node:crypto");
71260
+ var import_node_fs62 = require("node:fs");
71261
+ var import_node_path57 = require("node:path");
70602
71262
  var slash = (value) => value.replaceAll("\\", "/");
70603
71263
  function repoPath(root, declaredPath, label) {
70604
- const absoluteRoot = (0, import_node_path56.resolve)(root);
70605
- const target = (0, import_node_path56.resolve)(root, declaredPath);
70606
- if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path56.sep}`)) {
71264
+ const absoluteRoot = (0, import_node_path57.resolve)(root);
71265
+ const target = (0, import_node_path57.resolve)(root, declaredPath);
71266
+ if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path57.sep}`)) {
70607
71267
  throw new Error(`${label} ${declaredPath} escapes the repository root`);
70608
71268
  }
70609
71269
  return target;
70610
71270
  }
70611
71271
  function digestFiles(files) {
70612
- const hash = (0, import_node_crypto23.createHash)("sha256");
71272
+ const hash = (0, import_node_crypto24.createHash)("sha256");
70613
71273
  for (const file of [...files].sort((a, b) => a.relative.localeCompare(b.relative))) {
70614
- const content = file.stat.isSymbolicLink() ? Buffer.from((0, import_node_fs61.readlinkSync)(file.absolute), "utf8") : (0, import_node_fs61.readFileSync)(file.absolute);
71274
+ const content = file.stat.isSymbolicLink() ? Buffer.from((0, import_node_fs62.readlinkSync)(file.absolute), "utf8") : (0, import_node_fs62.readFileSync)(file.absolute);
70615
71275
  hash.update(file.relative, "utf8");
70616
71276
  hash.update("\0");
70617
71277
  hash.update(file.stat.isSymbolicLink() ? "symlink" : "file", "utf8");
@@ -70626,8 +71286,8 @@ function digestFiles(files) {
70626
71286
  function digestPackedFiles(packageRoot, packedFiles) {
70627
71287
  return digestFiles(packedFiles.map((path2) => {
70628
71288
  const absolute = repoPath(packageRoot, path2, "packed artifact identity path");
70629
- if (!(0, import_node_fs61.existsSync)(absolute)) throw new Error(`packed artifact identity path ${path2} does not exist`);
70630
- return { absolute, relative: slash(path2), stat: (0, import_node_fs61.lstatSync)(absolute) };
71289
+ if (!(0, import_node_fs62.existsSync)(absolute)) throw new Error(`packed artifact identity path ${path2} does not exist`);
71290
+ return { absolute, relative: slash(path2), stat: (0, import_node_fs62.lstatSync)(absolute) };
70631
71291
  }));
70632
71292
  }
70633
71293
 
@@ -70640,10 +71300,10 @@ var DIST_ARTIFACTS = [
70640
71300
  ];
70641
71301
  var BOM_DIST_TREE_ID = "mmi-cli-dist";
70642
71302
  var ABSENT = "absent";
70643
- var sha2562 = (bytes) => `sha256:${(0, import_node_crypto24.createHash)("sha256").update(bytes).digest("hex")}`;
71303
+ var sha2563 = (bytes) => `sha256:${(0, import_node_crypto25.createHash)("sha256").update(bytes).digest("hex")}`;
70644
71304
  function artifactDrift(path2, committedBytes, rebuiltBytes) {
70645
- const committed = committedBytes ? sha2562(committedBytes) : ABSENT;
70646
- const rebuiltExpected = rebuiltBytes ? sha2562(rebuiltBytes) : ABSENT;
71305
+ const committed = committedBytes ? sha2563(committedBytes) : ABSENT;
71306
+ const rebuiltExpected = rebuiltBytes ? sha2563(rebuiltBytes) : ABSENT;
70647
71307
  return { path: path2, status: committed === rebuiltExpected ? "current" : "stale", committed, rebuiltExpected };
70648
71308
  }
70649
71309
  function distTreeIdentity(labels, rebuilt, committed, digest) {
@@ -70712,13 +71372,13 @@ function renderDistDriftReceipt(receipt) {
70712
71372
  return lines2;
70713
71373
  }
70714
71374
  function readOrNull(path2) {
70715
- return (0, import_node_fs62.existsSync)(path2) ? (0, import_node_fs62.readFileSync)(path2) : null;
71375
+ return (0, import_node_fs63.existsSync)(path2) ? (0, import_node_fs63.readFileSync)(path2) : null;
70716
71376
  }
70717
71377
  function walkFiles(root) {
70718
71378
  const files = [];
70719
71379
  const walk2 = (directory) => {
70720
- for (const entry of (0, import_node_fs62.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
70721
- const child2 = (0, import_node_path57.join)(directory, entry.name);
71380
+ for (const entry of (0, import_node_fs63.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
71381
+ const child2 = (0, import_node_path58.join)(directory, entry.name);
70722
71382
  if (entry.isDirectory()) walk2(child2);
70723
71383
  else files.push(child2);
70724
71384
  }
@@ -70728,10 +71388,10 @@ function walkFiles(root) {
70728
71388
  }
70729
71389
  function bomPathFor(root) {
70730
71390
  try {
70731
- const registry2 = JSON.parse((0, import_node_fs62.readFileSync)((0, import_node_path57.join)(root, "surfaces.json"), "utf8"));
70732
- return (0, import_node_path57.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
71391
+ const registry2 = JSON.parse((0, import_node_fs63.readFileSync)((0, import_node_path58.join)(root, "surfaces.json"), "utf8"));
71392
+ return (0, import_node_path58.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
70733
71393
  } catch {
70734
- return (0, import_node_path57.join)(root, "distribution-bom.json");
71394
+ return (0, import_node_path58.join)(root, "distribution-bom.json");
70735
71395
  }
70736
71396
  }
70737
71397
  function rebuildTo(packageRoot, outDir) {
@@ -70744,40 +71404,40 @@ function rebuildTo(packageRoot, outDir) {
70744
71404
  });
70745
71405
  }
70746
71406
  function runDistStatus(root) {
70747
- const stage = (0, import_node_fs62.mkdtempSync)((0, import_node_path57.join)((0, import_node_os27.tmpdir)(), "mmi-dist-drift-"));
71407
+ const stage = (0, import_node_fs63.mkdtempSync)((0, import_node_path58.join)((0, import_node_os27.tmpdir)(), "mmi-dist-drift-"));
70748
71408
  let overlayCount = 0;
70749
71409
  try {
70750
- const cliOut = (0, import_node_path57.join)(stage, "cli-dist");
70751
- const hubOut = (0, import_node_path57.join)(stage, "hub-dist");
70752
- rebuildTo((0, import_node_path57.join)(root, "cli"), cliOut);
70753
- rebuildTo((0, import_node_path57.join)(root, "updater"), hubOut);
71410
+ const cliOut = (0, import_node_path58.join)(stage, "cli-dist");
71411
+ const hubOut = (0, import_node_path58.join)(stage, "hub-dist");
71412
+ rebuildTo((0, import_node_path58.join)(root, "cli"), cliOut);
71413
+ rebuildTo((0, import_node_path58.join)(root, "updater"), hubOut);
70754
71414
  const outDirFor = (packageDir) => packageDir === "cli" ? cliOut : hubOut;
70755
71415
  const rebuilt = (path2) => {
70756
71416
  const spec = DIST_ARTIFACTS.find((entry) => entry.path === path2);
70757
- return spec ? readOrNull((0, import_node_path57.join)(outDirFor(spec.packageDir), spec.output)) : null;
71417
+ return spec ? readOrNull((0, import_node_path58.join)(outDirFor(spec.packageDir), spec.output)) : null;
70758
71418
  };
70759
- const committed = (path2) => readOrNull((0, import_node_path57.join)(root, path2));
70760
- const tree = (path2) => readOrNull((0, import_node_path57.join)(root, path2));
70761
- const distRoot = (0, import_node_path57.join)(root, "cli", "dist");
70762
- const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path57.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
70763
- const bom = JSON.parse((0, import_node_fs62.readFileSync)(bomPathFor(root), "utf8"));
71419
+ const committed = (path2) => readOrNull((0, import_node_path58.join)(root, path2));
71420
+ const tree = (path2) => readOrNull((0, import_node_path58.join)(root, path2));
71421
+ const distRoot = (0, import_node_path58.join)(root, "cli", "dist");
71422
+ const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path58.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
71423
+ const bom = JSON.parse((0, import_node_fs63.readFileSync)(bomPathFor(root), "utf8"));
70764
71424
  const digest = (entries) => {
70765
- const overlay = (0, import_node_path57.join)(stage, `overlay-${overlayCount++}`);
71425
+ const overlay = (0, import_node_path58.join)(stage, `overlay-${overlayCount++}`);
70766
71426
  for (const entry of entries) {
70767
- const target = (0, import_node_path57.join)(overlay, entry.path);
70768
- (0, import_node_fs62.mkdirSync)((0, import_node_path57.dirname)(target), { recursive: true });
70769
- (0, import_node_fs62.writeFileSync)(target, entry.bytes);
71427
+ const target = (0, import_node_path58.join)(overlay, entry.path);
71428
+ (0, import_node_fs63.mkdirSync)((0, import_node_path58.dirname)(target), { recursive: true });
71429
+ (0, import_node_fs63.writeFileSync)(target, entry.bytes);
70770
71430
  }
70771
71431
  return digestPackedFiles(overlay, entries.map((entry) => entry.path));
70772
71432
  };
70773
71433
  return computeDistDriftReceipt({ committed, tree, rebuilt, distTree, bom, digest });
70774
71434
  } finally {
70775
- (0, import_node_fs62.rmSync)(stage, { recursive: true, force: true });
71435
+ (0, import_node_fs63.rmSync)(stage, { recursive: true, force: true });
70776
71436
  }
70777
71437
  }
70778
71438
 
70779
71439
  // src/doc-refs-live.ts
70780
- var import_node_path58 = require("node:path");
71440
+ var import_node_path59 = require("node:path");
70781
71441
  var concrete = (value) => !!value && !/[<>{}$]/.test(value);
70782
71442
  var liveOption = (tokens2, flag) => {
70783
71443
  const index = tokens2.indexOf(flag);
@@ -70850,7 +71510,7 @@ async function runLiveDocRefs(root, deps, live) {
70850
71510
  const offline = runDocRefs(root, { ...deps, runbooksOnly: true });
70851
71511
  const read = deps.readFile ?? readFileOrNull;
70852
71512
  const docs = Object.fromEntries((deps.listDocs ?? defaultListDocs)(root).flatMap((doc) => {
70853
- const body = read((0, import_node_path58.join)(root, doc));
71513
+ const body = read((0, import_node_path59.join)(root, doc));
70854
71514
  return body !== null && isRunbook(doc, body) && !deps.isIgnored?.([doc]).has(doc) ? [[doc, body]] : [];
70855
71515
  }));
70856
71516
  const remote = await checkRunbookLive(docs, live);
@@ -71061,7 +71721,7 @@ async function diagnoseHub(deps) {
71061
71721
 
71062
71722
  // src/schedules-lift-command.ts
71063
71723
  var import_promises12 = require("node:fs/promises");
71064
- var import_node_path59 = require("node:path");
71724
+ var import_node_path60 = require("node:path");
71065
71725
  init_clean_exit();
71066
71726
  init_cli_shared();
71067
71727
  var DEFAULT_WORKFLOWS_DIR = ".github/workflows";
@@ -71090,7 +71750,7 @@ async function readWorkflowFiles(dir) {
71090
71750
  const files = [];
71091
71751
  for (const name of names.sort()) {
71092
71752
  if (!/\.ya?ml$/.test(name)) continue;
71093
- files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises12.readFile)((0, import_node_path59.join)(dir, name), "utf8") });
71753
+ files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises12.readFile)((0, import_node_path60.join)(dir, name), "utf8") });
71094
71754
  }
71095
71755
  return files;
71096
71756
  }
@@ -71173,8 +71833,8 @@ function registerSchedulesLiftCommand(program3, deps = {}) {
71173
71833
 
71174
71834
  // src/spawn-policy-core.ts
71175
71835
  var import_node_child_process24 = require("node:child_process");
71176
- var import_node_fs63 = require("node:fs");
71177
- var import_node_path60 = require("node:path");
71836
+ var import_node_fs64 = require("node:fs");
71837
+ var import_node_path61 = require("node:path");
71178
71838
  var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
71179
71839
  var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
71180
71840
  var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
@@ -71260,7 +71920,7 @@ function runSpawnPolicy(root) {
71260
71920
  for (const file of files) {
71261
71921
  let raw;
71262
71922
  try {
71263
- raw = (0, import_node_fs63.readFileSync)((0, import_node_path60.join)(root, file), "utf8");
71923
+ raw = (0, import_node_fs64.readFileSync)((0, import_node_path61.join)(root, file), "utf8");
71264
71924
  } catch {
71265
71925
  continue;
71266
71926
  }
@@ -71280,19 +71940,19 @@ function runSpawnPolicy(root) {
71280
71940
  function registerDeveloperCommands(program3) {
71281
71941
  const rules = program3.command("rules").description("org-managed .gitignore delivery");
71282
71942
  rules.command("gitignore").option("--write", "upsert the managed block into .gitignore (default: check only, non-zero exit on drift)").option("--json", "machine-readable output").description("verify (or --write) this repo's org-managed .gitignore block matches the SSOT").action((opts) => {
71283
- const path2 = (0, import_node_path61.join)(process.cwd(), ".gitignore");
71284
- const current = (0, import_node_fs64.existsSync)(path2) ? (0, import_node_fs64.readFileSync)(path2, "utf8") : null;
71943
+ const path2 = (0, import_node_path62.join)(process.cwd(), ".gitignore");
71944
+ const current = (0, import_node_fs65.existsSync)(path2) ? (0, import_node_fs65.readFileSync)(path2, "utf8") : null;
71285
71945
  const plan = planManagedGitignore(current);
71286
71946
  const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
71287
71947
  if (opts.json) {
71288
- if (opts.write && plan.changed) (0, import_node_fs64.writeFileSync)(path2, plan.content, "utf8");
71948
+ if (opts.write && plan.changed) (0, import_node_fs65.writeFileSync)(path2, plan.content, "utf8");
71289
71949
  console.log(JSON.stringify(plan, null, 2));
71290
71950
  if (!opts.write && plan.changed) process.exitCode = 1;
71291
71951
  return;
71292
71952
  }
71293
71953
  if (opts.write) {
71294
71954
  if (plan.changed) {
71295
- (0, import_node_fs64.writeFileSync)(path2, plan.content, "utf8");
71955
+ (0, import_node_fs65.writeFileSync)(path2, plan.content, "utf8");
71296
71956
  console.log(`mmi-cli devops org rules gitignore: updated .gitignore (${drift})`);
71297
71957
  } else {
71298
71958
  console.log("mmi-cli devops org rules gitignore: up to date");
@@ -71609,7 +72269,7 @@ ${r.stderr ?? ""}`).catch(() => "");
71609
72269
  var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
71610
72270
  var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
71611
72271
  function envHealLockPath(home) {
71612
- return (0, import_node_path62.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
72272
+ return (0, import_node_path63.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
71613
72273
  }
71614
72274
  async function withEnvHealLock(what, run) {
71615
72275
  try {
@@ -71747,7 +72407,7 @@ function mmiDoctorDeps(opts = {}) {
71747
72407
  );
71748
72408
  const result = applyPluginCachePlan(
71749
72409
  plan,
71750
- (p) => (0, import_node_fs65.rmSync)(p, { recursive: true }),
72410
+ (p) => (0, import_node_fs66.rmSync)(p, { recursive: true }),
71751
72411
  stagingApplyFsGuard(configRoot)
71752
72412
  );
71753
72413
  return {
@@ -71782,7 +72442,7 @@ function mmiDoctorDeps(opts = {}) {
71782
72442
  // has no generated routing index would
71783
72443
  // get a permanent — demanding an artifact it never asked for.
71784
72444
  docsIndexState: (root) => {
71785
- if (!(0, import_node_fs65.existsSync)((0, import_node_path62.join)(root, DOCS_INDEX_PATH))) return void 0;
72445
+ if (!(0, import_node_fs66.existsSync)((0, import_node_path63.join)(root, DOCS_INDEX_PATH))) return void 0;
71786
72446
  const real = createDocsIndexDeps(root);
71787
72447
  let docs;
71788
72448
  const listDocs = () => docs ??= real.listDocs();
@@ -71791,7 +72451,7 @@ function mmiDoctorDeps(opts = {}) {
71791
72451
  },
71792
72452
  // #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
71793
72453
  healDocsIndex: (root) => {
71794
- if (!(0, import_node_fs65.existsSync)((0, import_node_path62.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
72454
+ if (!(0, import_node_fs66.existsSync)((0, import_node_path63.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
71795
72455
  const real = createDocsIndexDeps(root);
71796
72456
  let docs;
71797
72457
  const listDocs = () => docs ??= real.listDocs();
@@ -72193,7 +72853,9 @@ registerPreAction.call(program2, "preAction", async (_thisCommand, actionCommand
72193
72853
  "vars create",
72194
72854
  "vars update",
72195
72855
  "vars delete",
72196
- "repo archive-status"
72856
+ "repo archive-status",
72857
+ // #9788: the lifecycle read validates its manifest/window/bounds before its scoped mint.
72858
+ "read merge-queue lifecycle"
72197
72859
  ]);
72198
72860
  if (LAZY_APP_MINT_COMMANDS.has(commandPath4)) return;
72199
72861
  try {
@@ -72592,7 +73254,7 @@ project.command("set [owner/repo]").description("upsert project META (idempotent
72592
73254
  if (dupe) return fail(`org project set: KEY "${dupe}" was passed to both --var and --set; --set is an alias of --var, so pass each KEY once`);
72593
73255
  if (o.secretsFile) {
72594
73256
  try {
72595
- vars.push(`secrets=${(0, import_node_fs65.readFileSync)(o.secretsFile, "utf8")}`);
73257
+ vars.push(`secrets=${(0, import_node_fs66.readFileSync)(o.secretsFile, "utf8")}`);
72596
73258
  } catch (e) {
72597
73259
  return fail(`org project set: cannot read --secrets-file ${o.secretsFile}: ${e.message}`);
72598
73260
  }
@@ -72984,16 +73646,16 @@ function ciAuditDeps2() {
72984
73646
  // gate re-seed step is skipped gracefully rather than failing mid-run.
72985
73647
  readSeedFile: (path2) => {
72986
73648
  if (!root) return null;
72987
- const fullPath = (0, import_node_path62.join)(root, path2);
72988
- return (0, import_node_fs65.existsSync)(fullPath) ? (0, import_node_fs65.readFileSync)(fullPath, "utf8") : null;
73649
+ const fullPath = (0, import_node_path63.join)(root, path2);
73650
+ return (0, import_node_fs66.existsSync)(fullPath) ? (0, import_node_fs66.readFileSync)(fullPath, "utf8") : null;
72989
73651
  }
72990
73652
  };
72991
73653
  }
72992
73654
  function hubRoot2() {
72993
- const fromPkg = (0, import_node_path62.join)(__dirname, "..", "..");
73655
+ const fromPkg = (0, import_node_path63.join)(__dirname, "..", "..");
72994
73656
  const marker = "skills/bootstrap/seeds/manifest.json";
72995
- if ((0, import_node_fs65.existsSync)((0, import_node_path62.join)(fromPkg, marker))) return fromPkg;
72996
- if ((0, import_node_fs65.existsSync)((0, import_node_path62.join)(process.cwd(), marker))) return process.cwd();
73657
+ if ((0, import_node_fs66.existsSync)((0, import_node_path63.join)(fromPkg, marker))) return fromPkg;
73658
+ if ((0, import_node_fs66.existsSync)((0, import_node_path63.join)(process.cwd(), marker))) return process.cwd();
72997
73659
  return null;
72998
73660
  }
72999
73661
  registerQueryCommands(program2);
@@ -73046,6 +73708,11 @@ var varsProjects = async () => fetchProjectsList(registryClientDeps(await loadCo
73046
73708
  var varsActor = () => ({ credential: "app_installation", ...activeAppQuotaScope() ? { quotaScope: activeAppQuotaScope() } : {} });
73047
73709
  registerDevopsVarsCommands(program2, { appClient: (commandPath4) => varsAppClient(commandPath4)(), projects: varsProjects, actor: varsActor });
73048
73710
  registerRepoArchiveStatusCommand(program2, { appClient: (commandPath4) => varsAppClient(commandPath4)(), projects: varsProjects, actor: varsActor });
73711
+ registerMergeQueueLifecycleCommand(program2.commands.find((c) => c.name() === "read"), {
73712
+ appClient: async (commandPath4) => createGitHubClient({ token: async () => activeAppToken() ?? (await mintCommandWriteToken(appActorDeps(), commandPath4)).token }),
73713
+ projects: varsProjects,
73714
+ actor: varsActor
73715
+ });
73049
73716
  ci.command("reconcile").description("audit + optionally apply merge settings and product ruleset activation (master-admin)").option("--json", "machine-readable output").option("--repo <owner/repo>", "reconcile one repo instead of the full registry").option("--apply", "PATCH merge settings + activate product ruleset when missing (master role required); activation is skipped while the repo's gate has never passed (#3694)").option("--park-ruleset", "stop the product ruleset enforcing without deleting it \u2014 keeps its required contexts (master role required)").action(async (o) => {
73050
73717
  if (o.apply && o.parkRuleset) {
73051
73718
  return fail("ci reconcile: --apply and --park-ruleset ask for opposite things; pass one");
@@ -73145,10 +73812,10 @@ access.command("audit").description("audit collaborator roles + train-branch pus
73145
73812
  targets = resolution.targets;
73146
73813
  }
73147
73814
  const derivedMatrix = registryProjects ? accessMatrixFromProjects(registryProjects) : {};
73148
- const fileMatrix = (0, import_node_fs65.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs65.readFileSync)("access-matrix.json", "utf8")) : {};
73815
+ const fileMatrix = (0, import_node_fs66.existsSync)("access-matrix.json") ? loadAccessMatrix((0, import_node_fs66.readFileSync)("access-matrix.json", "utf8")) : {};
73149
73816
  const matrix = mergeAccessMatrix(fileMatrix, derivedMatrix);
73150
73817
  const dataAccess = registryProjects ? dataAccessContractsFromProjects(registryProjects) : { consumers: {} };
73151
- const sanctioned = (0, import_node_fs65.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs65.readFileSync)("access-matrix.json", "utf8")) : {};
73818
+ const sanctioned = (0, import_node_fs66.existsSync)("access-matrix.json") ? loadSanctionedAdmins((0, import_node_fs66.readFileSync)("access-matrix.json", "utf8")) : {};
73152
73819
  const report = await auditOrgAccess(targets, deps, matrix, dataAccess, sanctioned);
73153
73820
  console.log(o.json ? JSON.stringify(report, null, 2) : renderAccessReport(report));
73154
73821
  if (!report.ok) process.exitCode = 1;
@@ -73185,16 +73852,16 @@ function directoryBytes(path2) {
73185
73852
  let total = 0;
73186
73853
  let entries;
73187
73854
  try {
73188
- entries = (0, import_node_fs65.readdirSync)(path2, { withFileTypes: true });
73855
+ entries = (0, import_node_fs66.readdirSync)(path2, { withFileTypes: true });
73189
73856
  } catch {
73190
73857
  return 0;
73191
73858
  }
73192
73859
  for (const entry of entries) {
73193
- const child2 = (0, import_node_path62.join)(path2, entry.name);
73860
+ const child2 = (0, import_node_path63.join)(path2, entry.name);
73194
73861
  if (entry.isDirectory()) total += directoryBytes(child2);
73195
73862
  else {
73196
73863
  try {
73197
- total += (0, import_node_fs65.statSync)(child2).size;
73864
+ total += (0, import_node_fs66.statSync)(child2).size;
73198
73865
  } catch {
73199
73866
  }
73200
73867
  }
@@ -73202,25 +73869,25 @@ function directoryBytes(path2) {
73202
73869
  return total;
73203
73870
  }
73204
73871
  function listDirEntries(dir) {
73205
- return (0, import_node_fs65.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
73872
+ return (0, import_node_fs66.readdirSync)(dir, { withFileTypes: true }).map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));
73206
73873
  }
73207
73874
  function readInstalledPluginRefs(configRoot) {
73208
73875
  const p = installedPluginsPathForConfig(configRoot);
73209
- if (!(0, import_node_fs65.existsSync)(p)) return [];
73876
+ if (!(0, import_node_fs66.existsSync)(p)) return [];
73210
73877
  try {
73211
- return installedPluginPaths((0, import_node_fs65.readFileSync)(p, "utf8"));
73878
+ return installedPluginPaths((0, import_node_fs66.readFileSync)(p, "utf8"));
73212
73879
  } catch {
73213
73880
  return null;
73214
73881
  }
73215
73882
  }
73216
73883
  function pluginCacheFsDeps(configRoot, dirBytes) {
73217
73884
  return {
73218
- exists: (p) => (0, import_node_fs65.existsSync)(p),
73219
- listVersionDirs: (root) => (0, import_node_fs65.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
73885
+ exists: (p) => (0, import_node_fs66.existsSync)(p),
73886
+ listVersionDirs: (root) => (0, import_node_fs66.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name),
73220
73887
  dirBytes,
73221
- listStagingDirs: (root) => (0, import_node_fs65.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
73888
+ listStagingDirs: (root) => (0, import_node_fs66.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
73222
73889
  try {
73223
- return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path62.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs65.statSync)(p).mtimeMs) };
73890
+ return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path63.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs66.statSync)(p).mtimeMs) };
73224
73891
  } catch {
73225
73892
  return { name: d.name, mtimeMs: Date.now() };
73226
73893
  }
@@ -73234,10 +73901,10 @@ function stagingApplyFsGuard(configRoot) {
73234
73901
  return {
73235
73902
  referencedPaths: () => readInstalledPluginRefs(configRoot),
73236
73903
  mtimeMs: (name) => {
73237
- const p = (0, import_node_path62.join)(stagingRoot, name);
73238
- if (!(0, import_node_fs65.existsSync)(p)) return null;
73904
+ const p = (0, import_node_path63.join)(stagingRoot, name);
73905
+ if (!(0, import_node_fs66.existsSync)(p)) return null;
73239
73906
  try {
73240
- return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs65.statSync)(q).mtimeMs);
73907
+ return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs66.statSync)(q).mtimeMs);
73241
73908
  } catch {
73242
73909
  return null;
73243
73910
  }
@@ -73263,7 +73930,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
73263
73930
  { withBytes: true, configRoot, includeStaging: surface !== "codex" }
73264
73931
  );
73265
73932
  const anythingToDelete = plan.prune.length > 0 || plan.staging.length > 0;
73266
- const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs65.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
73933
+ const result = o.apply && anythingToDelete ? applyPluginCachePlan(plan, (p) => (0, import_node_fs66.rmSync)(p, { recursive: true, force: true }), stagingApplyFsGuard(configRoot)) : void 0;
73267
73934
  const warnings = plan.prune.length > 0 ? [CONCURRENT_SESSION_WARNING] : [];
73268
73935
  if (o.json) console.log(JSON.stringify({ ...plan, warnings, applied: result ?? null }));
73269
73936
  else console.log(renderPluginCachePlan(plan, result));
@@ -73273,7 +73940,7 @@ program2.command("archive-prune").description("inventory worktree evidence archi
73273
73940
  let primary = o.repoRoot;
73274
73941
  if (!primary) {
73275
73942
  const common = await gitOut(["rev-parse", "--path-format=absolute", "--git-common-dir"]);
73276
- primary = common ? (0, import_node_path62.dirname)(common) : "";
73943
+ primary = common ? (0, import_node_path63.dirname)(common) : "";
73277
73944
  }
73278
73945
  if (!primary) {
73279
73946
  const detail = "archive-prune: not inside a git checkout (pass --repo-root <primary checkout>)";