@mutmutco/claude-plugin 4.6.36 → 4.6.38

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
@@ -1229,8 +1229,6 @@ var init_house_map = __esm({
1229
1229
  // --- harbour — declared lanes -------------------------------------------------------------------
1230
1230
  "org schedules": "harbour",
1231
1231
  // register/run/park schedule rows under the one lane contract
1232
- intercom: "harbour",
1233
- // #9184: the guest coordination lane to the MM Coordinator — a bounded lane, not a claim on org truth
1234
1232
  // --- devops — shipping --------------------------------------------------------------------------
1235
1233
  pr: "devops",
1236
1234
  read: "devops",
@@ -1279,6 +1277,11 @@ var init_house_map = __esm({
1279
1277
  pickup: "learning",
1280
1278
  // cloud-agent adoption gate (#5708)
1281
1279
  // --- core — the front door itself ---------------------------------------------------------------
1280
+ // #9993: `intercom` is the guest's own coordination door, not a declared org lane — it reads and
1281
+ // writes nothing of this org's truth (no schedules, no registry, no board), it talks to the Jerv
1282
+ // Gateway as this caller's own GitHub identity. So it is core: `mmi-cli intercom guest …`, unprefixed.
1283
+ // The old `harbour intercom guest …` spelling still runs through HOUSE_ALIASES below.
1284
+ intercom: "core",
1282
1285
  commands: "core",
1283
1286
  whoami: "core",
1284
1287
  spawn: "core",
@@ -1303,7 +1306,9 @@ var init_house_map = __esm({
1303
1306
  HOUSE_ALIASES = {
1304
1307
  issue: ["devops"],
1305
1308
  // filing/managing issues is oracle, but reads as shipping work
1306
- "issue create": ["devops"]
1309
+ "issue create": ["devops"],
1310
+ intercom: ["harbour"]
1311
+ // #9993: `intercom` moved out of harbour to core; the old spelling still routes
1307
1312
  };
1308
1313
  }
1309
1314
  });
@@ -13690,7 +13695,7 @@ function isAgentScratchPath(path2) {
13690
13695
  return /^tmp_[^/]+$/.test(normalized);
13691
13696
  }
13692
13697
  var PUSH_WALL_AGENT_CONFIG_DIRS = [".cursor/rules/", ".codex/", ".agents/"];
13693
- var LIVE_SESSION_DIRS = [".jerv/", ".pi/"];
13698
+ var LIVE_SESSION_DIRS = [".jerv/", ".pi/", "review-state/"];
13694
13699
  function pathUnderDirPrefix(path2, dir) {
13695
13700
  return path2 === dir || path2 === dir.slice(0, -1) || path2.startsWith(dir) || path2.includes(`/${dir}`);
13696
13701
  }
@@ -14549,7 +14554,7 @@ function scanWorktreeTmpEvidence(worktreePath, newerThanMs, deps = {}) {
14549
14554
  function archiveWorktreeTmpArtifacts(args, deps = {}) {
14550
14555
  const copyFile = deps.copyFile ?? import_node_fs12.copyFileSync;
14551
14556
  const fileSize = deps.fileSize ?? ((path2) => (0, import_node_fs12.statSync)(path2).size);
14552
- const writeFile4 = deps.writeFile ?? ((path2, text) => (0, import_node_fs12.writeFileSync)(path2, text));
14557
+ const writeFile5 = deps.writeFile ?? ((path2, text) => (0, import_node_fs12.writeFileSync)(path2, text));
14553
14558
  const mkdirp = deps.mkdirp ?? ((path2) => {
14554
14559
  (0, import_node_fs12.mkdirSync)(path2, { recursive: true });
14555
14560
  });
@@ -14576,7 +14581,7 @@ function archiveWorktreeTmpArtifacts(args, deps = {}) {
14576
14581
  bytes += copied;
14577
14582
  }
14578
14583
  const manifest = (0, import_node_path9.join)((0, import_node_path9.dirname)(dest), "tmp-manifest.json");
14579
- writeFile4(manifest, `${JSON.stringify({ files: scan.entries.map(({ relPath, bytes: b }) => ({ relPath, bytes: b })), bytes, excluded: scan.excluded, skippedLinks: scan.links }, null, 2)}
14584
+ writeFile5(manifest, `${JSON.stringify({ files: scan.entries.map(({ relPath, bytes: b }) => ({ relPath, bytes: b })), bytes, excluded: scan.excluded, skippedLinks: scan.links }, null, 2)}
14580
14585
  `);
14581
14586
  return { status: "archived", path: dest, runScope, entries: scan.entries.length, bytes, manifest, excluded: scan.excluded, skippedLinks: scan.links };
14582
14587
  } catch (e) {
@@ -15261,8 +15266,8 @@ function resolveVerbAliasShim(argv) {
15261
15266
  }
15262
15267
 
15263
15268
  // src/command-composition.ts
15264
- var import_node_os28 = require("node:os");
15265
- var import_node_path64 = require("node:path");
15269
+ var import_node_os29 = require("node:os");
15270
+ var import_node_path65 = require("node:path");
15266
15271
 
15267
15272
  // src/board-read.ts
15268
15273
  var import_node_fs18 = require("node:fs");
@@ -15686,6 +15691,9 @@ var APP_SUBPROCESS_ROUTED_COMMANDS = /* @__PURE__ */ new Set([
15686
15691
  // #9871: queue enqueue + auto-merge fallback ride ghFilingAppEnv (#9490/#9528; receipt credential app_installation).
15687
15692
  "pr land"
15688
15693
  ]);
15694
+ function ineligibleActorNote(commandPath4) {
15695
+ return commandPath4 === "board move" ? "app actor: board move retains the existing process identity; status-write credential is selected separately and reported in its receipt" : `app actor: note, not a failure \u2014 '${commandPath4}' is personal-only (viewer/@me/authorship), so it ran on the personal token despite ${APP_ACTOR_ENV}=app`;
15696
+ }
15689
15697
  async function activateAppActor(commandPath4, env, mint, note = (line) => process.stderr.write(`${line}
15690
15698
  `)) {
15691
15699
  activeQuotaScope = void 0;
@@ -15698,7 +15706,7 @@ async function activateAppActor(commandPath4, env, mint, note = (line) => proces
15698
15706
  if (!APP_ELIGIBLE_COMMANDS.has(commandPath4) && !APP_WRITE_COMMANDS.has(commandPath4)) {
15699
15707
  if (requested === "app" && !notedIneligibleCommand && !APP_SUBPROCESS_ROUTED_COMMANDS.has(commandPath4)) {
15700
15708
  notedIneligibleCommand = true;
15701
- note(`app actor: note, not a failure \u2014 '${commandPath4}' is personal-only (viewer/@me/authorship), so it ran on the personal token despite ${APP_ACTOR_ENV}=app`);
15709
+ note(ineligibleActorNote(commandPath4));
15702
15710
  }
15703
15711
  return "personal";
15704
15712
  }
@@ -22762,23 +22770,8 @@ var BOARD_WRITE_REFUSAL_TYPES = /* @__PURE__ */ new Set(["INSUFFICIENT_SCOPES",
22762
22770
  async function updateItemSingleSelect(client, projectId, itemId, fieldId, optionId) {
22763
22771
  if (process.env.MMI_ACTOR?.trim() === "app") {
22764
22772
  const { writeHubBoardField: writeHubBoardField2 } = await Promise.resolve().then(() => (init_board_write(), board_write_exports));
22765
- try {
22766
- await writeHubBoardField2({ projectId, itemId, fieldId, optionId });
22767
- return { credential: "app_installation" };
22768
- } catch (error) {
22769
- const refusal = error;
22770
- if (refusal?.rateLimited !== true || refusal.status !== 503 && refusal.status !== 429) throw error;
22771
- try {
22772
- const identity = await githubKeyringTokenRead();
22773
- if (identity.state !== "ok") throw error;
22774
- const owner = createGitHubClient({ token: async () => identity.token });
22775
- await owner.graphql(UPDATE_ITEM_FIELD_MUTATION, { projectId, itemId, fieldId, optionId });
22776
- } catch {
22777
- throw error;
22778
- }
22779
- process.stderr.write("board write: Hub App allowance exhausted \u2014 wrote with your own GitHub login (#9053)\n");
22780
- return { credential: "user" };
22781
- }
22773
+ await writeHubBoardField2({ projectId, itemId, fieldId, optionId });
22774
+ return { credential: "app_installation" };
22782
22775
  }
22783
22776
  try {
22784
22777
  await client.graphql(UPDATE_ITEM_FIELD_MUTATION, { projectId, itemId, fieldId, optionId });
@@ -22923,19 +22916,20 @@ function renderBoardItem(item) {
22923
22916
  }
22924
22917
  return lines2.join("\n");
22925
22918
  }
22919
+ var CACHED_CONFIRM_CLAUSE = " \xB7 items closed/created after the build may still differ \u2014 confirm with --direct";
22926
22920
  function renderCachedBoardSource(report) {
22927
22921
  if (typeof report.stale?.ageMs === "number") {
22928
22922
  const ageSeconds = Math.floor(report.stale.ageMs / 1e3);
22929
- return `source: cache \xB7 built ${report.builtAt ? `${report.builtAt} (${ageSeconds}s old)` : `${ageSeconds}s ago`} \xB7 stale`;
22923
+ return `source: cache \xB7 built ${report.builtAt ? `${report.builtAt} (${ageSeconds}s old)` : `${ageSeconds}s ago`} \xB7 stale${CACHED_CONFIRM_CLAUSE}`;
22930
22924
  }
22931
22925
  if (report.builtAt) {
22932
22926
  const builtMs = Date.parse(report.builtAt);
22933
22927
  if (Number.isFinite(builtMs)) {
22934
- return `source: cache \xB7 built ${report.builtAt} (${Math.max(0, Math.floor((Date.now() - builtMs) / 1e3))}s old) \xB7 fresh`;
22928
+ return `source: cache \xB7 built ${report.builtAt} (${Math.max(0, Math.floor((Date.now() - builtMs) / 1e3))}s old) \xB7 fresh${CACHED_CONFIRM_CLAUSE}`;
22935
22929
  }
22936
- return `source: cache \xB7 built ${report.builtAt} \xB7 fresh`;
22930
+ return `source: cache \xB7 built ${report.builtAt} \xB7 fresh${CACHED_CONFIRM_CLAUSE}`;
22937
22931
  }
22938
- return "source: cache";
22932
+ return `source: cache${CACHED_CONFIRM_CLAUSE}`;
22939
22933
  }
22940
22934
  function renderBoardReport(report) {
22941
22935
  const lines2 = [`Board \xB7 ${report.project.title} \xB7 @${report.viewer}`, report.source === "cache" ? renderCachedBoardSource(report) : renderBoardSource()];
@@ -25230,6 +25224,20 @@ function upsertManagedSeedBlock(current, desired, begin, end) {
25230
25224
  action: "replace"
25231
25225
  };
25232
25226
  }
25227
+ async function headStrictlyBehind(execGit, head, remoteSha, defaultBranch) {
25228
+ try {
25229
+ await execGit(["fetch", "--no-tags", "--quiet", "origin", `refs/heads/${defaultBranch}`]);
25230
+ } catch (e) {
25231
+ return { superseded: false, unknown: `could not fetch origin/${defaultBranch} to check ancestry (${e.message})` };
25232
+ }
25233
+ try {
25234
+ await execGit(["merge-base", "--is-ancestor", head, remoteSha]);
25235
+ return { superseded: true };
25236
+ } catch (e) {
25237
+ if (e.code === 1) return { superseded: false };
25238
+ return { superseded: false, unknown: `could not prove whether this checkout is an ancestor of origin/${defaultBranch} (${e.message}) \u2014 a shallow checkout cannot prove ancestry, so fetch full history` };
25239
+ }
25240
+ }
25233
25241
  async function resolveHubSeedSource(execGit, defaultBranch = "development") {
25234
25242
  let status;
25235
25243
  try {
@@ -25257,9 +25265,19 @@ remedy (non-destructive): run this command from a clean dedicated worktree \u201
25257
25265
  return { ok: false, reason: `\`git ls-remote origin refs/heads/${defaultBranch}\` returned nothing \u2014 refusing to read seeds without a known upstream revision` };
25258
25266
  }
25259
25267
  if (head !== remoteSha) {
25268
+ const behind = await headStrictlyBehind(execGit, head, remoteSha, defaultBranch);
25269
+ if (behind.superseded) {
25270
+ return {
25271
+ ok: false,
25272
+ superseded: true,
25273
+ head,
25274
+ remoteSha,
25275
+ reason: `this checkout (${head}) is behind origin/${defaultBranch} (${remoteSha}) \u2014 a newer commit overtook this run, whose own queued run owns the tick; seeding nothing and exiting green`
25276
+ };
25277
+ }
25260
25278
  return {
25261
25279
  ok: false,
25262
- reason: `this checkout (${head}) is not origin/${defaultBranch} (${remoteSha}) \u2014 fast-forward to origin/${defaultBranch} first; a stale or ahead checkout must not seed the fleet`
25280
+ reason: `this checkout (${head}) is not origin/${defaultBranch} (${remoteSha}) \u2014 fast-forward to origin/${defaultBranch} first; a stale or ahead checkout must not seed the fleet${behind.unknown ? ` (${behind.unknown})` : ""}`
25263
25281
  };
25264
25282
  }
25265
25283
  return { ok: true, sha: head };
@@ -25302,6 +25320,8 @@ var MANAGED_GITIGNORE_LINES = [
25302
25320
  // (measured on the v3.120.0 Hub train), with no legal resolution: committing another host's runtime
25303
25321
  // state is not an option, so the operator's only move was to park the directory outside the repo.
25304
25322
  ".pi/",
25323
+ // #10003: reviewer worktrees — never committable, regenerate during review (same runtime class as `.pi/`).
25324
+ "/review-state/",
25305
25325
  ".aws-sam/",
25306
25326
  // jerv-cli lease markers are ephemeral runtime state and never tracked. Hub#2586.
25307
25327
  ".jerv-lease.json",
@@ -25427,7 +25447,7 @@ function extractForwardRefs(markdown) {
25427
25447
  });
25428
25448
  return refs;
25429
25449
  }
25430
- function checkPins(root, readFile10, docs) {
25450
+ function checkPins(root, readFile11, docs) {
25431
25451
  const findings = [];
25432
25452
  for (const [doc, markdown] of Object.entries(docs)) {
25433
25453
  for (const pin of extractPins(markdown)) {
@@ -25435,7 +25455,7 @@ function checkPins(root, readFile10, docs) {
25435
25455
  findings.push({ kind: "malformed-pin", doc, line: pin.line, detail: pin.text });
25436
25456
  continue;
25437
25457
  }
25438
- const source = readFile10((0, import_node_path16.join)(root, pin.file));
25458
+ const source = readFile11((0, import_node_path16.join)(root, pin.file));
25439
25459
  if (source == null) {
25440
25460
  findings.push({ kind: "missing-test", doc, line: pin.line, detail: pin.file });
25441
25461
  continue;
@@ -25731,15 +25751,15 @@ function checkRunbookCoverage(docs, base, git3) {
25731
25751
  }
25732
25752
  return findings;
25733
25753
  }
25734
- function checkSeedRunbookReview(root, base, readFile10, git3) {
25735
- const manifestJson = readFile10((0, import_node_path16.join)(root, "skills/bootstrap/seeds/manifest.json"));
25754
+ function checkSeedRunbookReview(root, base, readFile11, git3) {
25755
+ const manifestJson = readFile11((0, import_node_path16.join)(root, "skills/bootstrap/seeds/manifest.json"));
25736
25756
  if (!manifestJson) return [];
25737
25757
  const changed = new Set(git3(["diff", "--name-only", `${base}...HEAD`]).split(/\r?\n/).filter(Boolean));
25738
25758
  const touched = loadBootstrapSeeds(manifestJson).seeds.filter((seed) => isPropagatableSeed(seed) && changed.has(seedSourcePath(seed)));
25739
25759
  if (!touched.length) return [];
25740
25760
  let declarations = null;
25741
25761
  try {
25742
- declarations = JSON.parse(readFile10((0, import_node_path16.join)(root, RUNBOOK_UNAFFECTED_PATH)) ?? "null");
25762
+ declarations = JSON.parse(readFile11((0, import_node_path16.join)(root, RUNBOOK_UNAFFECTED_PATH)) ?? "null");
25743
25763
  } catch {
25744
25764
  declarations = null;
25745
25765
  }
@@ -25826,12 +25846,12 @@ function defaultTrackedFiles(root, firstSegments, exec3 = import_node_child_proc
25826
25846
  throw error;
25827
25847
  }
25828
25848
  }
25829
- function mergeGroupRunbookMessages(root, deps, readFile10, warnings) {
25849
+ function mergeGroupRunbookMessages(root, deps, readFile11, warnings) {
25830
25850
  const env = deps.env ?? process.env;
25831
25851
  if (env.GITHUB_EVENT_NAME !== "merge_group") return "";
25832
25852
  let source = "merge_group event payload";
25833
25853
  try {
25834
- const event = JSON.parse(readFile10(env.GITHUB_EVENT_PATH ?? "") ?? "");
25854
+ const event = JSON.parse(readFile11(env.GITHUB_EVENT_PATH ?? "") ?? "");
25835
25855
  const match = /^refs\/heads\/gh-readonly-queue\/.+\/pr-([1-9]\d*)-[a-f0-9]+$/.exec(event.merge_group?.head_ref ?? "");
25836
25856
  const repo = env.GITHUB_REPOSITORY;
25837
25857
  if (!match || !repo || !/^[\w.-]+\/[\w.-]+$/.test(repo)) throw new Error("invalid queue source");
@@ -25854,7 +25874,7 @@ function mergeGroupRunbookMessages(root, deps, readFile10, warnings) {
25854
25874
  }
25855
25875
  }
25856
25876
  function runDocRefs(root, deps = {}) {
25857
- const readFile10 = deps.readFile ?? readFileOrNull;
25877
+ const readFile11 = deps.readFile ?? readFileOrNull;
25858
25878
  const exists = deps.exists ?? import_node_fs19.existsSync;
25859
25879
  const listDocs = deps.listDocs ?? defaultListDocs;
25860
25880
  const isIgnored = deps.isIgnored ?? ((paths) => defaultIsIgnored(root, paths));
@@ -25868,11 +25888,11 @@ function runDocRefs(root, deps = {}) {
25868
25888
  const walked = listDocs(root);
25869
25889
  const ignoredDocs = walked.length ? isIgnored(walked) : /* @__PURE__ */ new Set();
25870
25890
  const docs = Object.fromEntries(
25871
- walked.filter((rel) => !ignoredDocs.has(rel)).map((rel) => [rel, readFile10((0, import_node_path16.join)(root, rel))]).filter(([rel, body]) => body != null && (!deps.runbooksOnly || isRunbook(rel, body)))
25891
+ walked.filter((rel) => !ignoredDocs.has(rel)).map((rel) => [rel, readFile11((0, import_node_path16.join)(root, rel))]).filter(([rel, body]) => body != null && (!deps.runbooksOnly || isRunbook(rel, body)))
25872
25892
  );
25873
25893
  const refResult = checkRefs(root, { exists, isIgnored, trackedFirstSegments }, docs);
25874
25894
  const findings = [
25875
- ...checkPins(root, readFile10, docs).findings,
25895
+ ...checkPins(root, readFile11, docs).findings,
25876
25896
  ...refResult.findings
25877
25897
  ];
25878
25898
  if (commandPaths == null) {
@@ -25886,7 +25906,7 @@ function runDocRefs(root, deps = {}) {
25886
25906
  findings.push(...checkCommands(docs, commandPaths).findings);
25887
25907
  }
25888
25908
  const runbooks = Object.fromEntries(Object.entries(docs).filter(([doc, body]) => isRunbook(doc, body)));
25889
- findings.push(...checkRunbookClaims(root, runbooks, { readFile: readFile10, exists, isIgnored, trackedFirstSegments, commandPaths, commandOptions: deps.commandOptions, commandSource: deps.commandSource }));
25909
+ findings.push(...checkRunbookClaims(root, runbooks, { readFile: readFile11, exists, isIgnored, trackedFirstSegments, commandPaths, commandOptions: deps.commandOptions, commandSource: deps.commandSource }));
25890
25910
  const warnings = [...refResult.warnings];
25891
25911
  if (deps.runbooksOnly && !Object.keys(runbooks).length) {
25892
25912
  warnings.push({ kind: "runbook-missing", doc: "docs/Guides/operator.md", line: 0, detail: "no operator runbook or <!-- runbook --> document exists" });
@@ -25894,9 +25914,9 @@ function runDocRefs(root, deps = {}) {
25894
25914
  if (deps.base) {
25895
25915
  const git3 = deps.git ?? ((args) => (0, import_node_child_process6.execFileSync)("git", args, { cwd: root, encoding: "utf8", maxBuffer: CHECK_IGNORE_MAX_BUFFER, windowsHide: true }));
25896
25916
  const stale = checkRunbookCoverage(runbooks, deps.base, git3);
25897
- const seedStale = checkSeedRunbookReview(root, deps.base, readFile10, git3);
25917
+ const seedStale = checkSeedRunbookReview(root, deps.base, readFile11, git3);
25898
25918
  const messages = git3(["log", "--format=%B", `${deps.base}..HEAD`]);
25899
- const sourceMessages = stale.length || seedStale.length ? mergeGroupRunbookMessages(root, deps, readFile10, warnings) : "";
25919
+ const sourceMessages = stale.length || seedStale.length ? mergeGroupRunbookMessages(root, deps, readFile11, warnings) : "";
25900
25920
  const allMessages = `${messages}
25901
25921
  ${sourceMessages}`;
25902
25922
  const override = /^Runbook-Unaffected:[ \t]*\S.*$/m.test(allMessages);
@@ -26855,22 +26875,22 @@ function renderBootstrapAncillaryPlan(labels, environments, exceptions = []) {
26855
26875
  ...exceptions.map(({ name, detail }) => ` OK environment ${name} (${detail})`)
26856
26876
  ].join("\n");
26857
26877
  }
26858
- function resolveSeedContent(seed, vars, readFile10) {
26859
- if (seed.source === "self") return readFile10(seed.target);
26878
+ function resolveSeedContent(seed, vars, readFile11) {
26879
+ if (seed.source === "self") return readFile11(seed.target);
26860
26880
  if (seed.source.startsWith("seed:")) {
26861
- const tmpl = readFile10(`skills/bootstrap/seeds/${seed.source.slice("seed:".length)}`);
26881
+ const tmpl = readFile11(`skills/bootstrap/seeds/${seed.source.slice("seed:".length)}`);
26862
26882
  return tmpl == null ? null : renderSeed(tmpl, vars);
26863
26883
  }
26864
26884
  return null;
26865
26885
  }
26866
- function resolveSeedWriteContent(seed, vars, readFile10, remoteContent) {
26886
+ function resolveSeedWriteContent(seed, vars, readFile11, remoteContent) {
26867
26887
  if (!seed.managedBlock) {
26868
- return { ok: true, content: resolveSeedContent(seed, vars, readFile10), managed: seed.source === "managed-block" };
26888
+ return { ok: true, content: resolveSeedContent(seed, vars, readFile11), managed: seed.source === "managed-block" };
26869
26889
  }
26870
- const base = remoteContent ?? resolveSeedContent(seed, vars, readFile10);
26890
+ const base = remoteContent ?? resolveSeedContent(seed, vars, readFile11);
26871
26891
  if (base == null) return { ok: true, content: null, managed: true };
26872
26892
  const blockSeed = { ...seed, source: seed.managedBlock.source, managedBlock: void 0 };
26873
- const desired = resolveSeedContent(blockSeed, vars, readFile10);
26893
+ const desired = resolveSeedContent(blockSeed, vars, readFile11);
26874
26894
  if (desired == null) return { ok: true, content: null, managed: true };
26875
26895
  const result = upsertManagedSeedBlock(base, desired, seed.managedBlock.begin, seed.managedBlock.end);
26876
26896
  return result.ok ? { ok: true, content: result.content, managed: true } : { ok: false, reason: result.reason, managed: true };
@@ -28385,10 +28405,10 @@ var rollout_plan_default = {
28385
28405
  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)."
28386
28406
  },
28387
28407
  baseline: {
28388
- version: "4.6.36",
28389
- tag: "v4.6.36",
28390
- commit: "d1c15e58d6e5",
28391
- npm: "@mutmutco/cli@4.6.36"
28408
+ version: "4.6.38",
28409
+ tag: "v4.6.38",
28410
+ commit: "975581f0f6ce",
28411
+ npm: "@mutmutco/cli@4.6.38"
28392
28412
  },
28393
28413
  exitCriterion: "fleet-n-of-n",
28394
28414
  hubOnlyShortcut: "forbidden",
@@ -28405,14 +28425,14 @@ var rollout_plan_default = {
28405
28425
  repo: "mutmutco/mmi-hub",
28406
28426
  role: "canary",
28407
28427
  schedule: "train",
28408
- v3Target: "v4.6.36"
28428
+ v3Target: "v4.6.38"
28409
28429
  }
28410
28430
  ],
28411
28431
  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.",
28412
28432
  rollback: {
28413
28433
  independent: true,
28414
- mechanism: "npm dist-tag latest -> 4.6.36 and redeploy the Hub Lambda from tag v4.6.36 (d1c15e58d6e5); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
28415
- v3Target: "v4.6.36 (@mutmutco/cli@4.6.36, tag commit d1c15e58d6e5 \u2014 last known-good release carrying the repo-index v4-only contract)"
28434
+ mechanism: "npm dist-tag latest -> 4.6.38 and redeploy the Hub Lambda from tag v4.6.38 (975581f0f6ce); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
28435
+ v3Target: "v4.6.38 (@mutmutco/cli@4.6.38, tag commit 975581f0f6ce \u2014 last known-good release carrying the repo-index v4-only contract)"
28416
28436
  }
28417
28437
  },
28418
28438
  {
@@ -43435,8 +43455,8 @@ function annotateChangeMeaning(changed, policy, read) {
43435
43455
  function isMeaningfulRow(file) {
43436
43456
  return file.meaningful !== false;
43437
43457
  }
43438
- function loadPolicy(root, readFile10 = readFileOrNull2) {
43439
- const raw = readFile10((0, import_node_path30.join)(root, POLICY_FILE));
43458
+ function loadPolicy(root, readFile11 = readFileOrNull2) {
43459
+ const raw = readFile11((0, import_node_path30.join)(root, POLICY_FILE));
43440
43460
  if (raw == null) return { mandatory: [], declared: false };
43441
43461
  return parsePolicy(raw, POLICY_FILE);
43442
43462
  }
@@ -44173,12 +44193,16 @@ async function submitQueueAdmission(pr, repo, head, read, write, options, resume
44173
44193
  receipt = { ...receipt, state: "pending", uuid: details.uuid };
44174
44194
  }
44175
44195
  const sleep2 = options.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
44176
- for (let attempt = 0; attempt < 3; attempt++) {
44177
- if (attempt) await sleep2(attempt * 1e3);
44196
+ const confirmDelays = [0, 1e3, 2e3, 4e3, 8e3, 15e3, 15e3, 15e3];
44197
+ let readFailure;
44198
+ for (const delay of confirmDelays) {
44199
+ if (delay) await sleep2(delay);
44200
+ readFailure = void 0;
44178
44201
  try {
44179
44202
  response = admissionResponse(await read([`repos/${repo}/pulls/${pr}/merge-async/${receipt.uuid}`, "--include", ...ASYNC_HEADERS]));
44180
44203
  } catch {
44181
- return reconcile("the saved operation cannot be read (including expired results)");
44204
+ readFailure = "the saved operation cannot be read (including expired results)";
44205
+ continue;
44182
44206
  }
44183
44207
  if (response.code !== 200) return reconcile(`the saved operation returned HTTP ${response.code}`);
44184
44208
  const { status, details } = response.body;
@@ -44220,6 +44244,7 @@ async function submitQueueAdmission(pr, repo, head, read, write, options, resume
44220
44244
  if (receipt.head !== head) return reconcile("the previous head completed admission; inspect current head and membership before continuing");
44221
44245
  return reconcile(`operation ${receipt.uuid} reported ${status}, but matching-head membership or merged state is unconfirmed`, status === "enqueued");
44222
44246
  }
44247
+ if (readFailure) return reconcile(readFailure);
44223
44248
  return markReenqueued({
44224
44249
  mergeStatus: "queue-admission-pending",
44225
44250
  retryable: false,
@@ -44442,8 +44467,8 @@ async function abandonQueueAdmission(pr, repo, options = {}) {
44442
44467
  }
44443
44468
  const receipt = journal.receipt;
44444
44469
  if (!receipt) return refuse("no queue-admission receipt exists for this PR; nothing to abandon");
44445
- if (!["pending", "enqueued"].includes(receipt.state)) {
44446
- return refuse(`the saved admission is '${receipt.state}', not a stuck non-terminal 'pending'/'enqueued' receipt; nothing to abandon`);
44470
+ if (!["pending", "enqueued"].includes(receipt.state) && !(receipt.state === "dispatching" && !receipt.uuid)) {
44471
+ return refuse(`the saved admission is '${receipt.state}'${receipt.uuid ? ` with operation ${receipt.uuid}` : ""}, not a stuck non-terminal 'pending'/'enqueued' receipt; nothing to abandon`);
44447
44472
  }
44448
44473
  let snapshot;
44449
44474
  try {
@@ -45777,20 +45802,20 @@ function buildPrLifecycle(state, manifest, joins) {
45777
45802
  if (member.pr !== state.number) continue;
45778
45803
  if (!member.admission_event_id) continue;
45779
45804
  const ep = episodes.find((x) => x.episode_id === member.admission_event_id);
45780
- 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);
45781
- if (!join54) continue;
45805
+ const join55 = 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);
45806
+ if (!join55) continue;
45782
45807
  if (!ep) {
45783
45808
  if (state.status === "complete") {
45784
- join54.status = "unresolved";
45785
- join54.reason = "admission-event-not-in-history";
45809
+ join55.status = "unresolved";
45810
+ join55.reason = "admission-event-not-in-history";
45786
45811
  } else {
45787
- join54.status = "unresolved";
45788
- join54.reason = "history-incomplete";
45812
+ join55.status = "unresolved";
45813
+ join55.reason = "history-incomplete";
45789
45814
  }
45790
45815
  continue;
45791
45816
  }
45792
- join54.status = "joined";
45793
- delete join54.reason;
45817
+ join55.status = "joined";
45818
+ delete join55.reason;
45794
45819
  ep.runs.push({ run_id: run.run_id, run_attempt: run.run_attempt, head_sha: run.head_sha });
45795
45820
  }
45796
45821
  }
@@ -46434,7 +46459,7 @@ function refuseNotClaimable(e, json) {
46434
46459
  process.exitCode = 1;
46435
46460
  return true;
46436
46461
  }
46437
- var BOARD_READ_HELP = "\nread is always the authoritative live GitHub Project v2 board (#4926).\n--direct skips the Hub snapshot and uses the existing direct GitHub GraphQL read immediately.\n--allow-partial applies to the paginated path and detail reads.\n\nWhen using Windows PowerShell 5.1, avoid shell redirects: PowerShell 5.1's `> file.json` is Out-File,\nwhich writes UTF-16LE with a BOM, and Node reading it as 'utf8' then fails JSON.parse at position 1\n(#5802). Use --out instead \u2014 the CLI writes the file itself as UTF-8:\n mmi-cli oracle board read --json --out .jerv/tmp/board.json\n\nThe --json report is an OBJECT \u2014 never an array:\n {project, viewer, repo, primary, secondary, items, warnings, partial[, source, builtAt, eventCursor, ...]}\n`primary` is this repo and `secondary` the other repos in scope; each is a bucket object\n {userOwned, claimable, taken, unownedInFlight}\nand each bucket is an array of items. Filter the BUCKET for ownership context:\n mmi-cli oracle board read --json --out .jerv/tmp/board.json\n node -e \"const b=require('./.jerv/tmp/board.json');console.log(b.primary.claimable.length)\"\n\n`items` (#9974) is that same flat row array, already on the full report: {ref,number,title,status,assignees,labels,bucket,claim,openPullRequests}\n node -e \"const b=require('./.jerv/tmp/board.json');for(const i of b.items)console.log(i.number,i.status,i.title)\"\nIt is built from the same flattener as --flat, so `const rows = j.items || j` is now safe on either form.\n\nAdd --flat with --json for that array as the WHOLE output (no project/viewer/warnings envelope):\n(#9595: claim is the parsed mmi-claim evidence object, null when unclaimed; openPullRequests lists the OPEN\n PRs whose closing-issue body links name the item. On the FULL JSON object that list lives at\n `report.pullRequests` \u2014 there is no top-level `openPullRequests`, and #9596 hand-written projections\n that assume one crash on `.filter`.)\n\nNever sniff the envelope \u2014 read `report.items` (or `primary`/`secondary`) by name. `j.items ?? j.rows ?? j`,\n`Object.values(j)` and any generic \"find the list\" wrapper are still guesses: before `items` existed they\nthrew `items is not iterable` before any row was read (#9956), and `j.rows` has never been a key. For a\nnumber|status|claim|title projection across non-Done rows, read --json --flat --out .jerv/tmp/board.json\nand filter that array locally (identical rows to `report.items`):\n mmi-cli oracle board read --json --flat --out .jerv/tmp/board.json\n\nA cached snapshot read renders `source: cache \xB7 built <timestamp> \xB7 fresh|stale` (#9584) and carries\n`builtAt` under --json, so a same-turn re-read of the same builtAt buys nothing new.\n\nUnder --json, parse stdout ONLY and check the exit code first. A failure still prints a\nhuman-readable reason on stderr, so never pipe 2>&1 into a JSON parser (#9151) \u2014 the merged\ntext is not JSON and hides the real board failure.\n\nA zero-byte or empty .jerv/tmp/*.json means the read FAILED \u2014 `Unexpected end of JSON input` is\nthe symptom of shell-redirected empty stdout, not malformed JSON \u2014 re-run with --out (which writes\nthe file only on success) and check the exit code before parsing.\n\nChanging an existing item's Status is a move, never a hand-written `gh api graphql`\n`updateProjectV2ItemFieldValue` mutation with copied `PVT_`/`PVTI_` node ids and a manual Status\nfield-id lookup:\n mmi-cli oracle board move <status> <ref>\n";
46462
+ var BOARD_READ_HELP = "\nread is always the authoritative live GitHub Project v2 board (#4926).\n--direct skips the Hub snapshot and uses the existing direct GitHub GraphQL read immediately.\n--allow-partial applies to the paginated path and detail reads.\n\nWhen using Windows PowerShell 5.1, avoid shell redirects: PowerShell 5.1's `> file.json` is Out-File,\nwhich writes UTF-16LE with a BOM, and Node reading it as 'utf8' then fails JSON.parse at position 1\n(#5802). Use --out instead \u2014 the CLI writes the file itself as UTF-8:\n mmi-cli oracle board read --json --out .jerv/tmp/board.json\n\nThe --json report is an OBJECT \u2014 never an array:\n {project, viewer, repo, primary, secondary, items, warnings, partial[, source, builtAt, eventCursor, ...]}\n`primary` is this repo and `secondary` the other repos in scope; each is a bucket object\n {userOwned, claimable, taken, unownedInFlight}\nand each bucket is an array of items. Filter the BUCKET for ownership context:\n mmi-cli oracle board read --json --out .jerv/tmp/board.json\n node -e \"const b=require('./.jerv/tmp/board.json');console.log(b.primary.claimable.length)\"\n\n`items` (#9974) is that same flat row array, already on the full report: {ref,number,title,status,assignees,labels,bucket,claim,openPullRequests}\n node -e \"const b=require('./.jerv/tmp/board.json');for(const i of b.items)console.log(i.number,i.status,i.title)\"\nIt is built from the same flattener as --flat, so `const rows = j.items || j` is now safe on either form.\n\nAdd --flat with --json for that array as the WHOLE output (no project/viewer/warnings envelope):\n(#9595: claim is the parsed mmi-claim evidence object, null when unclaimed; openPullRequests lists the OPEN\n PRs whose closing-issue body links name the item. On the FULL JSON object that list lives at\n `report.pullRequests` \u2014 there is no top-level `openPullRequests`, and #9596 hand-written projections\n that assume one crash on `.filter`.)\n\nNever sniff the envelope \u2014 read `report.items` (or `primary`/`secondary`) by name. `j.items ?? j.rows ?? j`,\n`Object.values(j)` and any generic \"find the list\" wrapper are still guesses: before `items` existed they\nthrew `items is not iterable` before any row was read (#9956), and `j.rows` has never been a key. For a\nnumber|status|claim|title projection across non-Done rows, read --json --flat --out .jerv/tmp/board.json\nand filter that array locally (identical rows to `report.items`):\n mmi-cli oracle board read --json --flat --out .jerv/tmp/board.json\n\nA cached snapshot read renders `source: cache \xB7 built <timestamp> \xB7 fresh|stale` (#9584) and carries\n`builtAt` under --json, so a same-turn re-read of the same builtAt buys nothing new. That age grades the\nCOPY, not its content: the line ends `\xB7 items closed/created after the build may still differ \u2014 confirm\nwith --direct` (#10028), so re-read with --direct before treating a row as still open or still present.\n\nUnder --json, parse stdout ONLY and check the exit code first. A failure still prints a\nhuman-readable reason on stderr, so never pipe 2>&1 into a JSON parser (#9151) \u2014 the merged\ntext is not JSON and hides the real board failure.\n\nA zero-byte or empty .jerv/tmp/*.json means the read FAILED \u2014 `Unexpected end of JSON input` is\nthe symptom of shell-redirected empty stdout, not malformed JSON \u2014 re-run with --out (which writes\nthe file only on success) and check the exit code before parsing.\n\nChanging an existing item's Status is a move, never a hand-written `gh api graphql`\n`updateProjectV2ItemFieldValue` mutation with copied `PVT_`/`PVTI_` node ids and a manual Status\nfield-id lookup:\n mmi-cli oracle board move <status> <ref>\n";
46438
46463
  function flattenBoardReport(report) {
46439
46464
  const closingByRef = /* @__PURE__ */ new Map();
46440
46465
  for (const pull of report.pullRequests ?? []) {
@@ -51136,6 +51161,13 @@ var declaredProjectAdmins = (meta) => {
51136
51161
  return Array.isArray(list) ? list.filter((entry) => typeof entry === "string") : [];
51137
51162
  };
51138
51163
  var execGitForSeedSource = async (args) => (await execFileP("git", args, { timeout: GIT_TIMEOUT_MS })).stdout;
51164
+ function reportSuperseded(prefix, source, json) {
51165
+ const head = source.head.slice(0, 8);
51166
+ const remote = source.remoteSha.slice(0, 8);
51167
+ const summary = `${prefix}: superseded \u2014 this checkout (${head}) is behind origin/development (${remote}); a newer run owns this tick, nothing seeded`;
51168
+ if (json) console.log(JSON.stringify({ status: "superseded", checkoutSha: source.head, remoteSha: source.remoteSha, summary }, null, 2));
51169
+ else console.log(summary);
51170
+ }
51139
51171
  async function gateRenderDiff(current, rendered) {
51140
51172
  const dir = (0, import_node_fs40.mkdtempSync)((0, import_node_path37.join)((0, import_node_os21.tmpdir)(), "mmi-gate-diff-"));
51141
51173
  const before = (0, import_node_path37.join)(dir, "committed-gate.yml");
@@ -51223,7 +51255,10 @@ async function propagationScope(classes, what) {
51223
51255
  }
51224
51256
  async function propagatePins(apply, json) {
51225
51257
  const source = await resolveHubSeedSource(execGitForSeedSource);
51226
- if (apply && !source.ok) return fail(`bootstrap propagate --pins --apply: ${source.reason}`);
51258
+ if (apply && !source.ok) {
51259
+ if (source.superseded) return reportSuperseded("bootstrap propagate --pins --apply", source, json);
51260
+ return fail(`bootstrap propagate --pins --apply: ${source.reason}`);
51261
+ }
51227
51262
  const { classOf, inClassRepos, canarySlug, sourcePilot } = await propagationScope(null, "hub action pins");
51228
51263
  const gh2 = async (args) => execFileP("gh", args, { timeout: 2e4 });
51229
51264
  const repos = [];
@@ -51654,7 +51689,7 @@ function registerBootstrapCommands(program3) {
51654
51689
  const slug = parsedRepo.slug;
51655
51690
  const onlyTarget = o.fromConfig && o.only.trim() === "gate.yml" ? ".github/workflows/gate.yml" : o.only.trim();
51656
51691
  const gh2 = async (args) => execFileP("gh", args, { timeout: 2e4 });
51657
- const readFile10 = (p) => (0, import_node_fs40.existsSync)(p) ? (0, import_node_fs40.readFileSync)(p, "utf8") : null;
51692
+ const readFile11 = (p) => (0, import_node_fs40.existsSync)(p) ? (0, import_node_fs40.readFileSync)(p, "utf8") : null;
51658
51693
  const enc = (p) => p.split("/").map(encodeURIComponent).join("/");
51659
51694
  const putSeed = async (target, content, ref, sha) => {
51660
51695
  const tmp = (0, import_node_path37.join)((0, import_node_os21.tmpdir)(), `mmi-seed-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
@@ -51826,7 +51861,7 @@ function registerBootstrapCommands(program3) {
51826
51861
  registryMeta?.requiredCheckBranches,
51827
51862
  registryMeta?.requiredChecks
51828
51863
  );
51829
- const rendered = resolveSeedContent(gateSeed, gateVars, readFile10);
51864
+ const rendered = resolveSeedContent(gateSeed, gateVars, readFile11);
51830
51865
  if (rendered === null || missingPlaceholders(rendered).length) return failGraceful("bootstrap apply: registry gate template is unreadable or has unfilled placeholders");
51831
51866
  const base = (await readTargetedSeedFiles(repo, scopedPlan, onlyTarget, scopedPlan.baseSha, controlClient)).base?.content ?? null;
51832
51867
  configGateAction = planGateFromConfig(base, rendered);
@@ -51930,7 +51965,7 @@ function registerBootstrapCommands(program3) {
51930
51965
  ...vars,
51931
51966
  ...registryMeta?.requiredChecks?.length ? { GATE_RULESET_CONTEXTS_JSON: rulesetVars.GATE_RULESET_CONTEXTS_JSON } : {},
51932
51967
  ...registryMeta?.requiredCheckBranches?.length ? { GATE_RULESET_BRANCH_REFS_JSON: rulesetVars.GATE_RULESET_BRANCH_REFS_JSON } : {}
51933
- } : vars, readFile10, preservedContent);
51968
+ } : vars, readFile11, preservedContent);
51934
51969
  if (!writeContent.ok) {
51935
51970
  return fail(`bootstrap apply: ${resolved.target}: ${writeContent.reason} \u2014 refusing to overwrite repo-owned content`);
51936
51971
  }
@@ -52076,7 +52111,7 @@ ${onlyManagedBlock ? `Only the marker-bounded Hub-managed block inside repo-owne
52076
52111
  }
52077
52112
  const rulesetSeed = repo.toLowerCase() === "mutmutco/mmi-hub" ? void 0 : manifest.seeds.find((s) => s.target === ".github/rulesets/mmi-product-required-checks.json");
52078
52113
  if (rulesetSeed) {
52079
- const rulesetContent = resolveSeedContent({ ...rulesetSeed, target: rulesetSeed.target.replace("{{REPO_SLUG}}", slug) }, vars, readFile10);
52114
+ const rulesetContent = resolveSeedContent({ ...rulesetSeed, target: rulesetSeed.target.replace("{{REPO_SLUG}}", slug) }, vars, readFile11);
52080
52115
  if (rulesetContent) {
52081
52116
  try {
52082
52117
  const client = controlClient;
@@ -52348,7 +52383,10 @@ LIVE apply to ${repo}:
52348
52383
  const manifestPath = "skills/bootstrap/seeds/manifest.json";
52349
52384
  if (!(0, import_node_fs40.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`);
52350
52385
  const seedSource = await resolveHubSeedSource(execGitForSeedSource);
52351
- if (!seedSource.ok) return fail(`bootstrap propagate: ${seedSource.reason}`);
52386
+ if (!seedSource.ok) {
52387
+ if (seedSource.superseded) return reportSuperseded("bootstrap propagate", seedSource, o.json);
52388
+ return fail(`bootstrap propagate: ${seedSource.reason}`);
52389
+ }
52352
52390
  const manifest = loadBootstrapSeeds((0, import_node_fs40.readFileSync)(manifestPath, "utf8"));
52353
52391
  const propagatable = manifest.seeds.filter(isPropagatableSeed);
52354
52392
  if (!o.target) {
@@ -52868,32 +52906,183 @@ function registerDeployCommands(program3) {
52868
52906
  }
52869
52907
 
52870
52908
  // src/intercom-guest.ts
52909
+ var import_node_crypto19 = require("node:crypto");
52910
+ var import_promises5 = require("node:fs/promises");
52911
+ var import_node_os22 = require("node:os");
52912
+ var import_node_path38 = require("node:path");
52913
+ init_cli_shared();
52871
52914
  init_github_client();
52872
- var CANONICAL_COMMAND = "mmi-cli harbour intercom guest";
52915
+ var CANONICAL_COMMAND = "mmi-cli intercom guest";
52873
52916
  var INTERCOM_GUEST_VERBS = ["register", "read", "send", "join", "leave"];
52917
+ var DELIVERIES_LIMIT_MAX = 100;
52918
+ var DELIVERIES_LIMIT_DEFAULT = 20;
52919
+ function deliveriesLimit(limit) {
52920
+ if (limit === void 0 || !Number.isFinite(limit) || limit < 1) return DELIVERIES_LIMIT_DEFAULT;
52921
+ return Math.min(Math.floor(limit), DELIVERIES_LIMIT_MAX);
52922
+ }
52874
52923
  var INTERCOM_GUEST_CONTRACT = {
52875
- source: "mutmutco/Jerv-Hub#2522 (Jerv Gateway guest principal, GitHub-token verification, allowlist)",
52924
+ source: "mutmutco/Jerv-Hub#2727 published guest wire contract (gateway/v1: contract/operations.js, contract/authorization.json, src/http/handler.js)",
52925
+ contractVersion: "gateway/v1",
52926
+ baseUrl: "https://jerv-central.tail4ce549.ts.net",
52876
52927
  baseUrlEnv: "MMI_INTERCOM_GATEWAY_URL",
52877
- operations: null
52928
+ discovery: {
52929
+ operation: "intercom.directory",
52930
+ method: "POST",
52931
+ path: "/v1/intercom/directory",
52932
+ mutation: false,
52933
+ credential: "required",
52934
+ // Idle endpoints are included on purpose: the Coordinator is reachable while it is idle, and the
52935
+ // contract says to match its display name and take the id from the row, never to remember one.
52936
+ body: () => ({ limit: 200, includeIdle: true })
52937
+ },
52938
+ operations: {
52939
+ // #9993 — the guest's first contact. `displayName` is the gh login; the Gateway appends 8 hex of the
52940
+ // endpoint id only when the name collides. HostKind/project are optional and omitted (the Gateway
52941
+ // stores `remote`). With a stored credential this same call rotates and resumes the endpoint.
52942
+ register: {
52943
+ operation: "intercom.register",
52944
+ method: "POST",
52945
+ path: "/v1/intercom/register",
52946
+ mutation: true,
52947
+ credential: "optional",
52948
+ needsLogin: true,
52949
+ body: (_request, context) => ({ displayName: context.login ?? "" })
52950
+ },
52951
+ // #9993 — `read` is the inbox: pending deliveries. Accepting them (intercom.accept) or acking them
52952
+ // (intercom.ack) is a separate, explicit call the contract documents under `cliVerbs.read.then`.
52953
+ read: {
52954
+ operation: "intercom.deliveries",
52955
+ method: "POST",
52956
+ path: "/v1/intercom/deliveries",
52957
+ mutation: false,
52958
+ credential: "required",
52959
+ body: (request) => ({ state: "pending", limit: deliveriesLimit(request.limit) })
52960
+ },
52961
+ // #9993 — the wire field is `text`. A body field named `message` is `unknown_field` at the Gateway.
52962
+ send: {
52963
+ operation: "intercom.send",
52964
+ method: "POST",
52965
+ path: "/v1/intercom/send",
52966
+ mutation: true,
52967
+ credential: "required",
52968
+ body: (request) => ({ to: request.to, text: request.message })
52969
+ },
52970
+ // #9993 — no Gateway operation admits a guest to a suite (Jerv-Hub#2524): a join is an ordinary
52971
+ // `send` to the Coordinator carrying the suite message convention. The suite name travels inside the
52972
+ // text because the convention carries no suite field.
52973
+ join: {
52974
+ operation: "intercom.send",
52975
+ method: "POST",
52976
+ path: "/v1/intercom/send",
52977
+ mutation: true,
52978
+ credential: "required",
52979
+ needsCoordinator: true,
52980
+ body: (request, context) => ({
52981
+ to: context.coordinator,
52982
+ text: `Suite join: ${context.endpointId ?? ""}, ${context.displayName ?? context.login ?? ""} guest, repo ${context.repo}, host ${context.host}, suite ${request.suite ?? ""}`
52983
+ })
52984
+ },
52985
+ // #9993 — leaving is the same shape, and the Coordinator marks the seat left.
52986
+ leave: {
52987
+ operation: "intercom.send",
52988
+ method: "POST",
52989
+ path: "/v1/intercom/send",
52990
+ mutation: true,
52991
+ credential: "required",
52992
+ needsCoordinator: true,
52993
+ body: (_request, context) => ({
52994
+ to: context.coordinator,
52995
+ text: `Suite leave: ${context.endpointId ?? ""}`
52996
+ })
52997
+ }
52998
+ }
52878
52999
  };
52879
53000
  var INTERCOM_GUEST_CONTRACT_MISSING = "intercom-guest-contract-missing";
52880
53001
  var INTERCOM_GUEST_BASE_URL_MISSING = "intercom-guest-base-url-missing";
52881
53002
  var INTERCOM_GUEST_NO_IDENTITY = "intercom-guest-no-identity";
53003
+ var INTERCOM_GUEST_INPUT_MISSING = "intercom-guest-input-missing";
53004
+ var INTERCOM_GUEST_NO_CREDENTIAL = "intercom-guest-no-credential";
53005
+ var INTERCOM_GUEST_COORDINATOR_MISSING = "intercom-guest-coordinator-missing";
53006
+ function intercomGuestStorePath(env = process.env) {
53007
+ const home = env.MMI_INTERCOM_HOME?.trim();
53008
+ return (0, import_node_path38.join)(home && home.length > 0 ? home : (0, import_node_path38.join)((0, import_node_os22.homedir)(), ".mmi", "intercom"), "guest.json");
53009
+ }
52882
53010
  function scrubToken(text, token) {
52883
53011
  if (!token) return text;
52884
53012
  return text.split(token).join("***");
52885
53013
  }
53014
+ function scrubSecrets(text, secrets) {
53015
+ return secrets.reduce((acc, secret) => scrubToken(acc, secret), text);
53016
+ }
52886
53017
  function missingContractRefusal(verb) {
52887
53018
  return [
52888
- `${CANONICAL_COMMAND} ${verb}: not sent \u2014 the Jerv Gateway guest contract has not landed (${INTERCOM_GUEST_CONTRACT.source}).`,
52889
- `Its guest operations (${INTERCOM_GUEST_VERBS.join(", ")}) publish no method, path or field shape yet, so this client refuses`,
52890
- "instead of guessing an endpoint: a guessed call would come back 404/403 and read as a permission or allowlist answer",
52891
- "when the real answer is that the contract is missing.",
52892
- `To switch every verb on: land the guest principal operations in ${INTERCOM_GUEST_CONTRACT.source}, then fill`,
52893
- "`INTERCOM_GUEST_CONTRACT.operations` in cli/src/intercom-guest.ts \u2014 that object is the one place the wire shape lives.",
53019
+ `${CANONICAL_COMMAND} ${verb}: not sent \u2014 this CLI build carries no Jerv Gateway operation for the '${verb}' verb (${INTERCOM_GUEST_CONTRACT.source}).`,
53020
+ "The published guest contract publishes the method, path and field shape for every verb this client knows",
53021
+ `(${INTERCOM_GUEST_VERBS.join(", ")}), so an empty entry means the contract moved and this build did not follow it \u2014`,
53022
+ "this client refuses instead of guessing an endpoint: a guessed call would come back 404/403 and read as a permission",
53023
+ "or allowlist answer when the real answer is that the operation is unknown here.",
53024
+ "To fix it: check the published contract against `INTERCOM_GUEST_CONTRACT.operations` in cli/src/intercom-guest.ts",
53025
+ "\u2014 that object is the one place the wire shape lives.",
52894
53026
  "Nothing was read and nothing was sent: no token was touched and no request was made."
52895
53027
  ].join(" ");
52896
53028
  }
53029
+ function missingInput(request) {
53030
+ if (request.verb === "send") {
53031
+ if (!request.to?.trim()) return "`--to <address>` is required";
53032
+ if (!request.message?.trim()) return "`--message <text>` is required";
53033
+ }
53034
+ if (request.verb === "join" && !request.suite?.trim()) return "`--suite <name>` is required";
53035
+ return void 0;
53036
+ }
53037
+ function coordinatorEndpointId(directoryBody) {
53038
+ let parsed;
53039
+ try {
53040
+ parsed = JSON.parse(directoryBody);
53041
+ } catch {
53042
+ return void 0;
53043
+ }
53044
+ const data = parsed?.data ?? parsed;
53045
+ const items = data?.items;
53046
+ if (!Array.isArray(items)) return void 0;
53047
+ const candidates = items.filter((item) => Boolean(item) && typeof item === "object");
53048
+ const named = candidates.filter((item) => typeof item.displayName === "string" && /^Coordinator(\s|$)/.test(item.displayName) && item.self !== true);
53049
+ const exact = named.find((item) => item.displayName === "Coordinator") ?? named[0];
53050
+ return typeof exact?.endpointId === "string" && exact.endpointId.length > 0 ? exact.endpointId : void 0;
53051
+ }
53052
+ async function readStore(path2) {
53053
+ try {
53054
+ const parsed = JSON.parse(await (0, import_promises5.readFile)(path2, "utf8"));
53055
+ return parsed && typeof parsed === "object" ? parsed : void 0;
53056
+ } catch {
53057
+ return void 0;
53058
+ }
53059
+ }
53060
+ async function writeStore(path2, entry) {
53061
+ await (0, import_promises5.mkdir)((0, import_node_path38.dirname)(path2), { recursive: true, mode: 448 });
53062
+ await (0, import_promises5.writeFile)(path2, `${JSON.stringify(entry, null, 2)}
53063
+ `, { encoding: "utf8", mode: 384 });
53064
+ await (0, import_promises5.chmod)(path2, 384).catch(() => {
53065
+ });
53066
+ }
53067
+ function registrationEntry(gateway, body, previous) {
53068
+ let parsed;
53069
+ try {
53070
+ parsed = JSON.parse(body);
53071
+ } catch {
53072
+ return void 0;
53073
+ }
53074
+ const data = parsed?.data ?? parsed;
53075
+ const credential = typeof data?.credential === "string" ? data.credential : previous?.credential;
53076
+ if (!credential) return void 0;
53077
+ return {
53078
+ gateway,
53079
+ endpointId: typeof data?.endpointId === "string" ? data.endpointId : previous?.endpointId,
53080
+ deviceId: typeof data?.deviceId === "string" ? data.deviceId : previous?.deviceId,
53081
+ credential,
53082
+ displayName: typeof data?.displayName === "string" ? data.displayName : previous?.displayName,
53083
+ epoch: typeof data?.epoch === "number" ? data.epoch : previous?.epoch
53084
+ };
53085
+ }
52897
53086
  async function runIntercomGuest(request, deps = {}) {
52898
53087
  const verb = request.verb;
52899
53088
  const contract = deps.contract ?? INTERCOM_GUEST_CONTRACT;
@@ -52901,61 +53090,156 @@ async function runIntercomGuest(request, deps = {}) {
52901
53090
  if (!operation) {
52902
53091
  return { ok: false, verb, code: INTERCOM_GUEST_CONTRACT_MISSING, message: missingContractRefusal(verb), tokenRead: false };
52903
53092
  }
53093
+ const inputProblem = missingInput(request);
53094
+ if (inputProblem) {
53095
+ return {
53096
+ ok: false,
53097
+ verb,
53098
+ code: INTERCOM_GUEST_INPUT_MISSING,
53099
+ message: `${CANONICAL_COMMAND} ${verb}: not sent \u2014 ${inputProblem}. Nothing was read and no request was made.`,
53100
+ tokenRead: false
53101
+ };
53102
+ }
52904
53103
  const env = deps.env ?? process.env;
52905
- const baseUrl = env[contract.baseUrlEnv]?.trim();
53104
+ const baseUrl = (env[contract.baseUrlEnv]?.trim() || contract.baseUrl).replace(/\/+$/, "");
52906
53105
  if (!baseUrl) {
52907
53106
  return {
52908
53107
  ok: false,
52909
53108
  verb,
52910
53109
  code: INTERCOM_GUEST_BASE_URL_MISSING,
52911
- message: `${CANONICAL_COMMAND} ${verb}: not sent \u2014 ${contract.baseUrlEnv} is not set, and the Jerv Gateway base URL has no default while the contract is unlanded (${contract.source}). Nothing was read and no request was made.`,
53110
+ message: `${CANONICAL_COMMAND} ${verb}: not sent \u2014 ${contract.baseUrlEnv} is empty and the published contract names no base URL either (${contract.source}). Nothing was read and no request was made.`,
52912
53111
  tokenRead: false
52913
53112
  };
52914
53113
  }
53114
+ const storePath = deps.storePath ?? intercomGuestStorePath(env);
53115
+ const store = await readStore(storePath);
52915
53116
  const identity = await (deps.readToken ?? githubKeyringTokenRead)();
52916
53117
  if (identity.state !== "ok") {
52917
53118
  return {
52918
53119
  ok: false,
52919
53120
  verb,
52920
53121
  code: INTERCOM_GUEST_NO_IDENTITY,
52921
- message: scrubToken(
52922
- `${CANONICAL_COMMAND} ${verb}: not sent \u2014 the guest identity is the caller's own \`gh\` login and it could not be read (${identity.detail}). Run \`gh auth login\`, then retry. No request was made.`
53122
+ message: scrubSecrets(
53123
+ `${CANONICAL_COMMAND} ${verb}: not sent \u2014 the guest identity is the caller's own \`gh\` login and it could not be read (${identity.detail}). Run \`gh auth login\`, then retry. No request was made.`,
53124
+ []
52923
53125
  ),
52924
53126
  tokenRead: false
52925
53127
  };
52926
53128
  }
52927
53129
  const token = identity.token;
52928
- const fetchImpl = deps.fetchImpl ?? ((url, init2) => fetch(url, init2));
52929
- const headers = { accept: "application/json", authorization: `Bearer ${token}` };
52930
- const init = { method: operation.method, headers };
52931
- if (operation.body) {
52932
- headers["content-type"] = "application/json";
52933
- init.body = JSON.stringify(operation.body(request));
53130
+ let login;
53131
+ if (operation.needsLogin) {
53132
+ login = await (deps.readLogin ?? githubLogin)();
53133
+ if (!login) {
53134
+ return {
53135
+ ok: false,
53136
+ verb,
53137
+ code: INTERCOM_GUEST_NO_IDENTITY,
53138
+ message: scrubSecrets(
53139
+ `${CANONICAL_COMMAND} ${verb}: not sent \u2014 ${operation.operation} needs your GitHub login as \`displayName\` and \`gh api user\` returned none. Run \`gh auth login\`, then retry. No request was made.`,
53140
+ [token]
53141
+ ),
53142
+ tokenRead: true
53143
+ };
53144
+ }
52934
53145
  }
52935
- let response;
52936
- try {
52937
- response = await fetchImpl(new URL(operation.path, baseUrl).toString(), init);
52938
- } catch (error) {
52939
- const detail = error instanceof Error ? error.message : String(error);
52940
- return { ok: false, verb, code: "intercom-guest-transport-failed", message: scrubToken(`${CANONICAL_COMMAND} ${verb}: the Gateway call failed in transport (${detail}).`, token), tokenRead: true };
53146
+ const credential = store?.credential;
53147
+ if (operation.credential === "required" && !credential) {
53148
+ return {
53149
+ ok: false,
53150
+ verb,
53151
+ code: INTERCOM_GUEST_NO_CREDENTIAL,
53152
+ message: scrubSecrets(
53153
+ `${CANONICAL_COMMAND} ${verb}: not sent \u2014 no intercom credential is stored for this machine (${storePath}). ${operation.operation} requires the \`Jerv-Intercom-Credential\` the Gateway returns exactly once, so run \`${CANONICAL_COMMAND} register\` first. Nothing was read and no request was made.`,
53154
+ [token]
53155
+ ),
53156
+ tokenRead: true
53157
+ };
52941
53158
  }
52942
- const text = await response.text().catch(() => "");
52943
- if (!response.ok) {
53159
+ const fetchImpl = deps.fetchImpl ?? ((url, init) => fetch(url, init));
53160
+ const newKey = deps.newIdempotencyKey ?? (() => (0, import_node_crypto19.randomUUID)());
53161
+ const secrets = [token, credential].filter((secret) => Boolean(secret));
53162
+ const callOne = async (op, body2) => {
53163
+ const headers = {
53164
+ accept: "application/json",
53165
+ "content-type": "application/json",
53166
+ "jerv-contract-version": contract.contractVersion,
53167
+ "jerv-guest-github-token": token
53168
+ };
53169
+ if (credential && op.credential !== "never") headers["jerv-intercom-credential"] = credential;
53170
+ if (op.mutation) headers["idempotency-key"] = newKey();
53171
+ let response;
53172
+ try {
53173
+ response = await fetchImpl(new URL(op.path, baseUrl).toString(), { method: op.method, headers, body: JSON.stringify(body2) });
53174
+ } catch (error) {
53175
+ return { transport: error instanceof Error ? error.message : String(error) };
53176
+ }
53177
+ const text = await response.text().catch(() => "");
53178
+ return { status: response.status, ok: response.ok, text };
53179
+ };
53180
+ let context = {
53181
+ login,
53182
+ credential,
53183
+ endpointId: store?.endpointId,
53184
+ displayName: store?.displayName,
53185
+ repo: env.GITHUB_REPOSITORY?.trim() || "unknown",
53186
+ host: (0, import_node_os22.hostname)()
53187
+ };
53188
+ if (operation.needsCoordinator) {
53189
+ const directory = await callOne(contract.discovery, contract.discovery.body?.(request, context) ?? {});
53190
+ if ("transport" in directory) {
53191
+ return { ok: false, verb, code: "intercom-guest-transport-failed", message: scrubSecrets(`${CANONICAL_COMMAND} ${verb}: the Gateway call failed in transport (${directory.transport}).`, secrets), tokenRead: true };
53192
+ }
53193
+ if (!directory.ok) {
53194
+ return {
53195
+ ok: false,
53196
+ verb,
53197
+ code: `intercom-guest-http-${directory.status}`,
53198
+ message: scrubSecrets(`${CANONICAL_COMMAND} ${verb}: ${contract.discovery.operation} answered HTTP ${directory.status}${directory.text ? ` \u2014 ${directory.text.slice(0, 600)}` : ""}`, secrets),
53199
+ tokenRead: true
53200
+ };
53201
+ }
53202
+ const coordinator = coordinatorEndpointId(directory.text);
53203
+ if (!coordinator) {
53204
+ return {
53205
+ ok: false,
53206
+ verb,
53207
+ code: INTERCOM_GUEST_COORDINATOR_MISSING,
53208
+ message: scrubSecrets(
53209
+ `${CANONICAL_COMMAND} ${verb}: ${contract.discovery.operation} listed no live endpoint named \`Coordinator\`, so there is nobody to send to and nothing was sent. The Coordinator's endpoint id comes from that directory, never from this client.`,
53210
+ secrets
53211
+ ),
53212
+ tokenRead: true
53213
+ };
53214
+ }
53215
+ context = { ...context, coordinator };
53216
+ }
53217
+ const body = operation.body ? operation.body(request, context) : {};
53218
+ const result = await callOne(operation, body);
53219
+ if ("transport" in result) {
53220
+ return { ok: false, verb, code: "intercom-guest-transport-failed", message: scrubSecrets(`${CANONICAL_COMMAND} ${verb}: the Gateway call failed in transport (${result.transport}).`, secrets), tokenRead: true };
53221
+ }
53222
+ if (!result.ok) {
52944
53223
  return {
52945
53224
  ok: false,
52946
53225
  verb,
52947
- code: `intercom-guest-http-${response.status}`,
52948
- message: scrubToken(`${CANONICAL_COMMAND} ${verb}: the Gateway answered HTTP ${response.status}${text ? ` \u2014 ${text.slice(0, 600)}` : ""}`, token),
53226
+ code: `intercom-guest-http-${result.status}`,
53227
+ message: scrubSecrets(`${CANONICAL_COMMAND} ${verb}: the Gateway answered HTTP ${result.status}${result.text ? ` \u2014 ${result.text.slice(0, 600)}` : ""}`, secrets),
52949
53228
  tokenRead: true
52950
53229
  };
52951
53230
  }
52952
- return { ok: true, verb, status: response.status, body: scrubToken(text, token) };
53231
+ if (operation.operation === "intercom.register") {
53232
+ const entry = registrationEntry(baseUrl, result.text, store);
53233
+ if (entry) await writeStore(storePath, entry).catch(() => {
53234
+ });
53235
+ }
53236
+ return { ok: true, verb, status: result.status, body: scrubSecrets(result.text, secrets) };
52953
53237
  }
52954
53238
  function renderIntercomGuestResult(result) {
52955
53239
  return result.ok ? result.body : result.message;
52956
53240
  }
52957
53241
  function registerIntercomGuestCommands(program3, io = {}) {
52958
- const guest = program3.command("intercom").description("Coordinate with the MM Coordinator through the Jerv Gateway intercom (guest identity; Jerv-Hub#2522)").command("guest").description("Guest coordination verbs: register, read, send, join, leave \u2014 coordination only, never orders");
53242
+ const guest = program3.command("intercom").description("Coordinate with the MM Coordinator through the Jerv Gateway intercom (guest identity; Jerv-Hub#2727)").command("guest").description("Guest coordination verbs: register, read, send, join, leave \u2014 coordination only, never orders");
52959
53243
  const out = io.out ?? ((line) => process.stdout.write(`${line}
52960
53244
  `));
52961
53245
  const err = io.err ?? ((line) => process.stderr.write(`${line}
@@ -52970,19 +53254,21 @@ function registerIntercomGuestCommands(program3, io = {}) {
52970
53254
  return;
52971
53255
  }
52972
53256
  err(renderIntercomGuestResult(result));
52973
- setExitCode(result.code === INTERCOM_GUEST_CONTRACT_MISSING || result.code === INTERCOM_GUEST_BASE_URL_MISSING ? 2 : 1);
53257
+ setExitCode(
53258
+ result.code === INTERCOM_GUEST_CONTRACT_MISSING || result.code === INTERCOM_GUEST_BASE_URL_MISSING || result.code === INTERCOM_GUEST_NO_CREDENTIAL ? 2 : 1
53259
+ );
52974
53260
  };
52975
- guest.command("register").description("Register this guest endpoint with the Gateway using your `gh auth` login").action(async () => {
53261
+ guest.command("register").description("Register this guest endpoint with the Gateway, using your `gh` login as the display name").action(async () => {
52976
53262
  await run({ verb: "register" });
52977
53263
  });
52978
- guest.command("read").description("Read messages waiting for this guest (default 20)").option("--limit <n>", "how many messages to look at", "20").action(async (options) => {
52979
- const limit = Number(options.limit ?? 20);
52980
- await run({ verb: "read", limit: Number.isFinite(limit) && limit > 0 ? Math.min(limit, 200) : 20 });
53264
+ guest.command("read").description(`Read the deliveries waiting for this guest endpoint (default ${DELIVERIES_LIMIT_DEFAULT}, at most ${DELIVERIES_LIMIT_MAX})`).option("--limit <n>", `how many deliveries to look at (1-${DELIVERIES_LIMIT_MAX})`, String(DELIVERIES_LIMIT_DEFAULT)).action(async (options) => {
53265
+ const limit = Number(options.limit ?? DELIVERIES_LIMIT_DEFAULT);
53266
+ await run({ verb: "read", limit: deliveriesLimit(limit) });
52981
53267
  });
52982
- guest.command("send").description("Send a coordination message to a peer endpoint (e.g. the MM Coordinator)").requiredOption("--to <address>", "peer endpoint address").requiredOption("--message <text>", "the coordination message").action(async (options) => {
53268
+ guest.command("send").description("Send a coordination message to the MM Coordinator").requiredOption("--to <address>", "peer endpoint id or display name (the MM Coordinator)").requiredOption("--message <text>", "the coordination message").action(async (options) => {
52983
53269
  await run({ verb: "send", to: options.to, message: options.message });
52984
53270
  });
52985
- guest.command("join").description("Request to join a suite").requiredOption("--suite <name>", "suite name").action(async (options) => {
53271
+ guest.command("join").description("Request to join a suite (a message to the MM Coordinator, who grants it)").requiredOption("--suite <name>", "suite name").action(async (options) => {
52986
53272
  await run({ verb: "join", suite: options.suite });
52987
53273
  });
52988
53274
  guest.command("leave").description("Leave the suite and withdraw this guest endpoint").action(async () => {
@@ -52997,7 +53283,7 @@ init_github_client();
52997
53283
  init_cli_shared();
52998
53284
 
52999
53285
  // src/suite-status.ts
53000
- var import_node_crypto19 = require("node:crypto");
53286
+ var import_node_crypto20 = require("node:crypto");
53001
53287
  var SUITE_REPO = "mutmutco/MM-Work";
53002
53288
  var REPOS = {
53003
53289
  "mmi-scout": "mutmutco/MMI-Scout",
@@ -53025,7 +53311,7 @@ function parseSuiteMapping(body) {
53025
53311
  return void 0;
53026
53312
  }
53027
53313
  }
53028
- var sha2562 = (b) => (0, import_node_crypto19.createHash)("sha256").update(b).digest("hex");
53314
+ var sha2562 = (b) => (0, import_node_crypto20.createHash)("sha256").update(b).digest("hex");
53029
53315
  async function proveBundle(url, tag, fetchUrl) {
53030
53316
  const get = async (u) => {
53031
53317
  try {
@@ -53099,7 +53385,7 @@ async function stageStatus(member, stage, deps) {
53099
53385
  if (pin) {
53100
53386
  served = pin;
53101
53387
  source = "registry-pin";
53102
- } else if (stage === "dev" || !member.bundles[stage]) {
53388
+ } else {
53103
53389
  try {
53104
53390
  const status = await deps.deploy(member.repo, stage);
53105
53391
  deployStatus = status;
@@ -53245,10 +53531,12 @@ async function newestGreen(deps, repo) {
53245
53531
  async function readSuiteDev(deps) {
53246
53532
  const report = await deps.status();
53247
53533
  return Promise.all(report.members.map(async (m) => {
53248
- const st = m.stages.find((s) => s.stage === "dev");
53534
+ const track = deps.releaseTrack ? await deps.releaseTrack(m.repo).catch(() => void 0) : void 0;
53535
+ const stage = track === "single" ? "main" : "dev";
53536
+ const st = m.stages.find((s) => s.stage === stage);
53249
53537
  const row = { repo: m.repo, head: st.head, served: st.served };
53250
- if (st.source === "n/a") return { ...row, state: "converged", note: "no dev stage" };
53251
- if (st.lag === false) return { ...row, state: "converged", note: "dev serves the development head" };
53538
+ if (st.source === "n/a") return { ...row, state: "converged", note: `no ${stage} stage` };
53539
+ if (st.lag === false) return { ...row, state: "converged", note: `${stage} serves the development head` };
53252
53540
  let green;
53253
53541
  try {
53254
53542
  green = await newestGreen(deps, m.repo);
@@ -53256,10 +53544,10 @@ async function readSuiteDev(deps) {
53256
53544
  return { ...row, state: "unknown", note: `checks unreadable (${e.message})` };
53257
53545
  }
53258
53546
  const last = st.deployStatus?.lastRun;
53259
- if (st.served === "unknown") return { ...row, green, state: "unknown", note: `dev version unproven (health probe failed or no deploy record)${last?.url ? `; last deploy ${last.conclusion ?? last.status}: ${last.url}` : ""}` };
53260
- if (same(st.served, green)) return { ...row, green, state: "converged", note: `dev serves the newest green SHA; head ${short2(st.head)} is not green yet` };
53547
+ if (st.served === "unknown") return { ...row, green, state: "unknown", note: `${stage} version unproven (health probe failed or no deploy record)${last?.url ? `; last deploy ${last.conclusion ?? last.status}: ${last.url}` : ""}` };
53548
+ if (same(st.served, green)) return { ...row, green, state: "converged", note: `${stage} serves the newest green SHA; head ${short2(st.head)} is not green yet` };
53261
53549
  if (!green) return { ...row, state: "blocked", note: `no green SHA in the last ${WALK} development commits` };
53262
- if (last?.conclusion === "failure") return { ...row, green, state: "blocked", note: `last dev deploy failed: ${last.url ?? "no run url"}` };
53550
+ if (last?.conclusion === "failure") return { ...row, green, state: "blocked", note: `last ${stage} deploy failed: ${last.url ?? "no run url"}` };
53263
53551
  return { ...row, green, state: "lagging", note: `newest green ${short2(green)} not deployed yet; the dev auto-deploy sweep (every 15 min) picks it up` };
53264
53552
  }));
53265
53553
  }
@@ -53281,8 +53569,8 @@ async function waitSuiteDev(deps, opts) {
53281
53569
 
53282
53570
  // src/suite-train.ts
53283
53571
  var import_node_fs41 = require("node:fs");
53284
- var import_node_path38 = require("node:path");
53285
- var import_node_crypto20 = require("node:crypto");
53572
+ var import_node_path39 = require("node:path");
53573
+ var import_node_crypto21 = require("node:crypto");
53286
53574
 
53287
53575
  // src/kit-version.ts
53288
53576
  var parse = (v) => /^(\d+)\.(\d+)\.(\d+)(-.+)?$/.exec(typeof v === "string" ? v.trim() : "");
@@ -53383,8 +53671,8 @@ function readSuiteCut(path2) {
53383
53671
  return record2;
53384
53672
  }
53385
53673
  function writeSuiteCut(path2, record2) {
53386
- (0, import_node_fs41.mkdirSync)((0, import_node_path38.dirname)(path2), { recursive: true });
53387
- const temp = `${path2}.${(0, import_node_crypto20.randomBytes)(8).toString("hex")}.tmp`;
53674
+ (0, import_node_fs41.mkdirSync)((0, import_node_path39.dirname)(path2), { recursive: true });
53675
+ const temp = `${path2}.${(0, import_node_crypto21.randomBytes)(8).toString("hex")}.tmp`;
53388
53676
  try {
53389
53677
  (0, import_node_fs41.writeFileSync)(temp, JSON.stringify(record2, null, 2), { flag: "wx" });
53390
53678
  (0, import_node_fs41.renameSync)(temp, path2);
@@ -53443,7 +53731,7 @@ async function retireFailedLeadRcLedger(deps, ownerGo) {
53443
53731
  if (ledger.repo !== SUITE_REPO || ledger.lane !== "rcand" || ledger.phases.promotion.state === "complete" || !states.includes("failed")) return void 0;
53444
53732
  if (!ownerGo) throw new Error(`the lead's failed rcand ledger for ${ledger.tag} blocks every train; retire it with suite abandon --owner-go`);
53445
53733
  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`);
53446
- const archive = (0, import_node_path38.join)((0, import_node_path38.dirname)(deps.ledgerPath), `phases.archive-${ledger.tag}.json`);
53734
+ const archive = (0, import_node_path39.join)((0, import_node_path39.dirname)(deps.ledgerPath), `phases.archive-${ledger.tag}.json`);
53447
53735
  if ((0, import_node_fs41.existsSync)(archive)) throw new Error(`ledger archive already exists at ${archive}; inspect before retiring again`);
53448
53736
  await deps.markAbandoned(SUITE_REPO, ledger.tag, ledger.tagSha);
53449
53737
  await fence(() => (0, import_node_fs41.renameSync)(deps.ledgerPath, archive));
@@ -53810,7 +54098,7 @@ Each member's live release must serve <data>/main/module.js at its main pin path
53810
54098
  }
53811
54099
  return record2;
53812
54100
  }
53813
- var hotfixRecordPath = (deps) => (0, import_node_path38.join)((0, import_node_path38.dirname)(deps.recordPath), "hotfix.json");
54101
+ var hotfixRecordPath = (deps) => (0, import_node_path39.join)((0, import_node_path39.dirname)(deps.recordPath), "hotfix.json");
53814
54102
  function readSuiteHotfix(path2) {
53815
54103
  let raw;
53816
54104
  try {
@@ -54125,20 +54413,20 @@ async function runRcDataSwitch(deps, opts) {
54125
54413
 
54126
54414
  // src/suite-train-io.ts
54127
54415
  var import_node_child_process19 = require("node:child_process");
54128
- var import_node_path51 = require("node:path");
54129
- var import_node_fs55 = require("node:fs");
54130
54416
  var import_node_path52 = require("node:path");
54417
+ var import_node_fs55 = require("node:fs");
54418
+ var import_node_path53 = require("node:path");
54131
54419
  init_cli_shared();
54132
54420
 
54133
54421
  // src/command-register-train-operations.ts
54134
- var import_promises9 = require("node:fs/promises");
54422
+ var import_promises10 = require("node:fs/promises");
54135
54423
  init_clean_exit();
54136
54424
  init_cli_shared();
54137
54425
  init_client_version();
54138
54426
  init_house_map();
54139
54427
 
54140
54428
  // src/hotfix-apply.ts
54141
- var import_promises5 = require("node:fs/promises");
54429
+ var import_promises6 = require("node:fs/promises");
54142
54430
 
54143
54431
  // src/release-announce.ts
54144
54432
  var ANNOUNCE_REPO = "mutmutco/MMI-Hub";
@@ -54481,8 +54769,8 @@ async function preflightHotfixBatchPicks(deps, sources, pickTolerated) {
54481
54769
  if (sources.length === 0) return { failures: [], skipped: [] };
54482
54770
  const { mkdtemp: mkdtemp2, rm: rm2 } = await import("node:fs/promises");
54483
54771
  const { tmpdir: tmpdir13 } = await import("node:os");
54484
- const { join: join54 } = await import("node:path");
54485
- const dir = await mkdtemp2(join54(tmpdir13(), "mmi-hotfix-preflight-"));
54772
+ const { join: join55 } = await import("node:path");
54773
+ const dir = await mkdtemp2(join55(tmpdir13(), "mmi-hotfix-preflight-"));
54486
54774
  const wt = { run: (file, args) => deps.run(file, file === "git" ? ["-C", dir, ...args] : args) };
54487
54775
  const failures = [];
54488
54776
  const skipped = [];
@@ -55194,7 +55482,7 @@ async function runHotfixReleaseLocked(deps, versionInput, options, doctor) {
55194
55482
  }
55195
55483
  let lines2;
55196
55484
  try {
55197
- lines2 = summaryFileLines(await (deps.readFile ?? ((p) => (0, import_promises5.readFile)(p, "utf8")))(options.announceSummaryFile));
55485
+ lines2 = summaryFileLines(await (deps.readFile ?? ((p) => (0, import_promises6.readFile)(p, "utf8")))(options.announceSummaryFile));
55198
55486
  } catch (e) {
55199
55487
  throw new Error(`could not read --announce-summary-file ${options.announceSummaryFile}: ${e.message} \u2014 see https://github.com/mutmutco/MMI-Hub/blob/development/docs/Guides/train-troubleshooting.md#announce-summary-missing`);
55200
55488
  }
@@ -55820,8 +56108,8 @@ function checkHotfixCarries(options) {
55820
56108
 
55821
56109
  // src/merge-cleanup.ts
55822
56110
  var import_node_fs42 = require("node:fs");
55823
- var import_node_path39 = require("node:path");
55824
- var import_node_os22 = require("node:os");
56111
+ var import_node_path40 = require("node:path");
56112
+ var import_node_os23 = require("node:os");
55825
56113
  init_cli_shared();
55826
56114
  init_github_client();
55827
56115
 
@@ -56228,8 +56516,8 @@ function resolveSquashMergeBodyText(commits, allowedClosing, cwd) {
56228
56516
  return rewritten === base ? null : rewritten;
56229
56517
  }
56230
56518
  function writeSquashBodyFile(body) {
56231
- const dir = (0, import_node_fs42.mkdtempSync)((0, import_node_path39.join)((0, import_node_os22.tmpdir)(), "mmi-squash-body-"));
56232
- const path2 = (0, import_node_path39.join)(dir, "body.txt");
56519
+ const dir = (0, import_node_fs42.mkdtempSync)((0, import_node_path40.join)((0, import_node_os23.tmpdir)(), "mmi-squash-body-"));
56520
+ const path2 = (0, import_node_path40.join)(dir, "body.txt");
56233
56521
  (0, import_node_fs42.writeFileSync)(path2, body.endsWith("\n") ? body : `${body}
56234
56522
  `, "utf8");
56235
56523
  return { path: path2, cleanup: () => {
@@ -56485,9 +56773,9 @@ async function deleteMergedRemoteBranch(options) {
56485
56773
  init_github_client();
56486
56774
 
56487
56775
  // src/release-custody.ts
56488
- var import_node_crypto21 = require("node:crypto");
56776
+ var import_node_crypto22 = require("node:crypto");
56489
56777
  var import_node_fs43 = require("node:fs");
56490
- var import_node_path40 = require("node:path");
56778
+ var import_node_path41 = require("node:path");
56491
56779
  init_github_client();
56492
56780
 
56493
56781
  // src/release-custody-bootstrap.ts
@@ -56709,7 +56997,7 @@ var ReleaseCustodyError = class extends Error {
56709
56997
  this.name = "ReleaseCustodyError";
56710
56998
  }
56711
56999
  };
56712
- var custodyDigest = (value) => (0, import_node_crypto21.createHash)("sha256").update(value).digest("hex");
57000
+ var custodyDigest = (value) => (0, import_node_crypto22.createHash)("sha256").update(value).digest("hex");
56713
57001
  function parseCustody(raw, repo) {
56714
57002
  const s = JSON.parse(raw);
56715
57003
  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) {
@@ -56810,7 +57098,7 @@ var ReleaseCustodySession = class _ReleaseCustodySession {
56810
57098
  }
56811
57099
  }
56812
57100
  let state = {
56813
- ...found.state ?? { schema: "mmi-release-custody/v1", repo, precutId: (0, import_node_crypto21.randomUUID)(), effects: {}, ...await initialize() },
57101
+ ...found.state ?? { schema: "mmi-release-custody/v1", repo, precutId: (0, import_node_crypto22.randomUUID)(), effects: {}, ...await initialize() },
56814
57102
  revision: (found.state?.revision ?? 0) + 1,
56815
57103
  controller
56816
57104
  };
@@ -56871,7 +57159,7 @@ var ReleaseCustodySession = class _ReleaseCustodySession {
56871
57159
  proof: { kind: "ledger-lifecycle", controller: state.controller }
56872
57160
  };
56873
57161
  }
56874
- return { ...state, ledger: raw, archives, precutId: state.ledger && !raw ? (0, import_node_crypto21.randomUUID)() : state.precutId };
57162
+ return { ...state, ledger: raw, archives, precutId: state.ledger && !raw ? (0, import_node_crypto22.randomUUID)() : state.precutId };
56875
57163
  });
56876
57164
  }
56877
57165
  async preserveLocal(raw) {
@@ -56987,10 +57275,10 @@ function localRaw(path2) {
56987
57275
  function hydrateLocal(path2, raw) {
56988
57276
  const previous = localRaw(path2);
56989
57277
  if (previous === raw) return;
56990
- (0, import_node_fs43.mkdirSync)((0, import_node_path40.dirname)(path2), { recursive: true });
57278
+ (0, import_node_fs43.mkdirSync)((0, import_node_path41.dirname)(path2), { recursive: true });
56991
57279
  if (previous !== null) {
56992
57280
  const ledger = parseReleaseLedger(previous);
56993
- const archive = (0, import_node_path40.join)((0, import_node_path40.dirname)(path2), `phases.local-${ledger.tagSha}-${custodyDigest(previous)}.json`);
57281
+ const archive = (0, import_node_path41.join)((0, import_node_path41.dirname)(path2), `phases.local-${ledger.tagSha}-${custodyDigest(previous)}.json`);
56994
57282
  if (!(0, import_node_fs43.existsSync)(archive)) (0, import_node_fs43.writeFileSync)(archive, previous, { flag: "wx" });
56995
57283
  if (raw === null) (0, import_node_fs43.renameSync)(path2, `${path2}.retired-${custodyDigest(previous)}`);
56996
57284
  }
@@ -57105,7 +57393,7 @@ function withHubReleaseCustody(deps) {
57105
57393
  if (!authority.ok || !authority.train || authority.authenticatedDelegation !== true) {
57106
57394
  throw new ReleaseCustodyError("repository custody requires the server-verified owner-triggered train session");
57107
57395
  }
57108
- const controller = { ...runIdentity, token: (0, import_node_crypto21.randomUUID)(), ...process.env.GITHUB_JOB ? { job: process.env.GITHUB_JOB } : {} };
57396
+ const controller = { ...runIdentity, token: (0, import_node_crypto22.randomUUID)(), ...process.env.GITHUB_JOB ? { job: process.env.GITHUB_JOB } : {} };
57109
57397
  const raw = localRaw(path2);
57110
57398
  const requiredContexts = () => discoverRequiredCheckContexts(
57111
57399
  deps,
@@ -57186,7 +57474,7 @@ function withHubReleaseCustody(deps) {
57186
57474
 
57187
57475
  // src/project-info-sync.ts
57188
57476
  var import_node_fs44 = require("node:fs");
57189
- var import_node_path41 = require("node:path");
57477
+ var import_node_path42 = require("node:path");
57190
57478
  var UPDATE_PROJECT_INFO = `mutation($projectId: ID!, $shortDescription: String!, $readme: String!) {
57191
57479
  updateProjectV2(input: { projectId: $projectId, shortDescription: $shortDescription, readme: $readme }) {
57192
57480
  projectV2 { id }
@@ -57233,7 +57521,7 @@ function sharedName(entries, fallback) {
57233
57521
  }
57234
57522
  function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
57235
57523
  if (!project2.projectId) throw new Error(`org project sync-info: ${targetRepo2} registry META has no projectId`);
57236
- const readmePath = (0, import_node_path41.join)(repoRoot2, "README.md");
57524
+ const readmePath = (0, import_node_path42.join)(repoRoot2, "README.md");
57237
57525
  if (!(0, import_node_fs44.existsSync)(readmePath)) throw new Error(`org project sync-info: ${targetRepo2} has no README.md`);
57238
57526
  const entries = entriesFor(project2, projects);
57239
57527
  const memberRepos = [...new Set(entries.flatMap((entry) => entry.repos ?? []))].filter((repo) => /^[^/]+\/[^/]+$/.test(repo)).sort((a, b) => a.localeCompare(b));
@@ -57259,8 +57547,8 @@ function buildProjectInfoSyncPlan(targetRepo2, project2, projects, repoRoot2) {
57259
57547
  const targetBase = `https://github.com/${targetRepo2}`;
57260
57548
  const targetBranch = branchFor(targetRepo2, projects);
57261
57549
  const orgDocs = [
57262
- (0, import_node_fs44.existsSync)((0, import_node_path41.join)(repoRoot2, "architecture.md")) ? `- [Hub architecture](${targetBase}/blob/${targetBranch}/architecture.md)` : "",
57263
- (0, import_node_fs44.existsSync)((0, import_node_path41.join)(repoRoot2, "docs", "Architecture", "agentic-dev-environment.md")) ? `- [Agentic development environment](${targetBase}/blob/${targetBranch}/docs/Architecture/agentic-dev-environment.md)` : ""
57550
+ (0, import_node_fs44.existsSync)((0, import_node_path42.join)(repoRoot2, "architecture.md")) ? `- [Hub architecture](${targetBase}/blob/${targetBranch}/architecture.md)` : "",
57551
+ (0, import_node_fs44.existsSync)((0, import_node_path42.join)(repoRoot2, "docs", "Architecture", "agentic-dev-environment.md")) ? `- [Agentic development environment](${targetBase}/blob/${targetBranch}/docs/Architecture/agentic-dev-environment.md)` : ""
57264
57552
  ].filter(Boolean);
57265
57553
  if (orgDocs.length) lines2.push("", "## Organisation docs", "", ...orgDocs);
57266
57554
  return { projectId: project2.projectId, projectName, targetRepo: targetRepo2, memberRepos, shortDescription, readme: `${lines2.join("\n")}
@@ -57283,7 +57571,7 @@ async function syncProjectInfo(plan, client, apply) {
57283
57571
 
57284
57572
  // src/train-commands.ts
57285
57573
  var import_node_fs45 = require("node:fs");
57286
- var import_node_path42 = require("node:path");
57574
+ var import_node_path43 = require("node:path");
57287
57575
  init_cli_shared();
57288
57576
  init_clean_exit();
57289
57577
  init_client_version();
@@ -57298,7 +57586,7 @@ function resolveReleaseBumpIntent(raw) {
57298
57586
  }
57299
57587
  function readRepoVersion() {
57300
57588
  try {
57301
- return JSON.parse((0, import_node_fs45.readFileSync)((0, import_node_path42.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
57589
+ return JSON.parse((0, import_node_fs45.readFileSync)((0, import_node_path43.join)(process.cwd(), ".claude-plugin", "plugin.json"), "utf8")).version || void 0;
57302
57590
  } catch {
57303
57591
  return void 0;
57304
57592
  }
@@ -57408,8 +57696,8 @@ function releaseProgressVerdict(status, subject = "Release") {
57408
57696
  }
57409
57697
 
57410
57698
  // src/suite-pin.ts
57411
- var import_promises6 = require("node:fs/promises");
57412
- var import_node_path43 = require("node:path");
57699
+ var import_promises7 = require("node:fs/promises");
57700
+ var import_node_path44 = require("node:path");
57413
57701
  var SuitePinError = class _SuitePinError extends Error {
57414
57702
  diagnostic;
57415
57703
  constructor(repo, tag, error) {
@@ -57477,8 +57765,8 @@ async function openSuiteCutPinPr(deps, input) {
57477
57765
  if (input.carriedOnly) return { status: "unchanged" };
57478
57766
  throw new Error("pins already match without a merged cut PR \u2014 provenance unverified");
57479
57767
  }
57480
- await (0, import_promises6.mkdir)((0, import_node_path43.join)(deps.root, "tmp"), { recursive: true });
57481
- scratch = await (0, import_promises6.mkdtemp)((0, import_node_path43.join)(deps.root, "tmp", "suite-pin-"));
57768
+ await (0, import_promises7.mkdir)((0, import_node_path44.join)(deps.root, "tmp"), { recursive: true });
57769
+ scratch = await (0, import_promises7.mkdtemp)((0, import_node_path44.join)(deps.root, "tmp", "suite-pin-"));
57482
57770
  const run = (file, args) => deps.run(file, args, scratch);
57483
57771
  await run("git", ["clone", "--quiet", "--branch", prs.length ? branch : "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
57484
57772
  if (prs.length) {
@@ -57490,8 +57778,8 @@ async function openSuiteCutPinPr(deps, input) {
57490
57778
  await run("git", ["switch", "-c", branch]);
57491
57779
  }
57492
57780
  for (const stage of ["rc", "main"]) {
57493
- const path2 = (0, import_node_path43.join)(scratch, `${stage}-pins.json`);
57494
- await (0, import_promises6.writeFile)(path2, JSON.stringify(input.pins.map((p) => ({ module: p.module, url: p[stage] }))));
57781
+ const path2 = (0, import_node_path44.join)(scratch, `${stage}-pins.json`);
57782
+ await (0, import_promises7.writeFile)(path2, JSON.stringify(input.pins.map((p) => ({ module: p.module, url: p[stage] }))));
57495
57783
  await run("node", ["scripts/pin-modules.mjs", "--stage", stage, "--pins", path2]);
57496
57784
  }
57497
57785
  await run("git", ["add", "seed/modules.json"]);
@@ -57521,7 +57809,7 @@ async function openSuiteCutPinPr(deps, input) {
57521
57809
  } catch (e) {
57522
57810
  throw new Error(`suite cut pin PR FAILED: ${e.message}; merge/verify the single ${SUITE_REPO} development pin PR, then rerun the cut (never recut member tags)`);
57523
57811
  } finally {
57524
- if (scratch) await (0, import_promises6.rm)(scratch, { recursive: true, force: true });
57812
+ if (scratch) await (0, import_promises7.rm)(scratch, { recursive: true, force: true });
57525
57813
  }
57526
57814
  }
57527
57815
  async function openSuiteRestorePinPr(deps, input) {
@@ -57543,13 +57831,13 @@ async function openSuiteRestorePinPr(deps, input) {
57543
57831
  }
57544
57832
  });
57545
57833
  if (!todo.length) return void 0;
57546
- await (0, import_promises6.mkdir)((0, import_node_path43.join)(deps.root, "tmp"), { recursive: true });
57547
- scratch = await (0, import_promises6.mkdtemp)((0, import_node_path43.join)(deps.root, "tmp", "suite-pin-"));
57834
+ await (0, import_promises7.mkdir)((0, import_node_path44.join)(deps.root, "tmp"), { recursive: true });
57835
+ scratch = await (0, import_promises7.mkdtemp)((0, import_node_path44.join)(deps.root, "tmp", "suite-pin-"));
57548
57836
  const run = (file, args) => deps.run(file, args, scratch);
57549
57837
  await run("git", ["clone", "--quiet", "--branch", "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
57550
57838
  await run("git", ["switch", "-c", branch]);
57551
- const path2 = (0, import_node_path43.join)(scratch, "main-pins.json");
57552
- await (0, import_promises6.writeFile)(path2, JSON.stringify(todo.map((p) => ({ module: p.module, url: p.prior }))));
57839
+ const path2 = (0, import_node_path44.join)(scratch, "main-pins.json");
57840
+ await (0, import_promises7.writeFile)(path2, JSON.stringify(todo.map((p) => ({ module: p.module, url: p.prior }))));
57553
57841
  await run("node", ["scripts/pin-modules.mjs", "--stage", "main", "--pins", path2]);
57554
57842
  await run("git", ["add", "seed/modules.json"]);
57555
57843
  await run("git", ["commit", "-m", `Restore main pins of abandoned suite cut ${input.cut.slice(0, 12)} (#7758)`]);
@@ -57574,7 +57862,7 @@ async function openSuiteRestorePinPr(deps, input) {
57574
57862
  } catch (e) {
57575
57863
  throw new Error(`suite restore pin PR FAILED: ${e.message}; rerun suite abandon --owner-go (the record is kept until the restore PR exists)`);
57576
57864
  } finally {
57577
- if (scratch) await (0, import_promises6.rm)(scratch, { recursive: true, force: true });
57865
+ if (scratch) await (0, import_promises7.rm)(scratch, { recursive: true, force: true });
57578
57866
  }
57579
57867
  }
57580
57868
  async function openSuitePinPr(deps, input) {
@@ -57622,8 +57910,8 @@ async function openSuitePinPr(deps, input) {
57622
57910
  if (newer.length) return { status: "superseded", pr: newer[0].url, url: pin };
57623
57911
  const open2 = onBranch.find((p) => p.state === "OPEN");
57624
57912
  if (open2 && tagOf(open2.title ?? "") === tag) return { status: "pending", pr: open2.url, url: pin };
57625
- await (0, import_promises6.mkdir)((0, import_node_path43.join)(deps.root, "tmp"), { recursive: true });
57626
- scratch = await (0, import_promises6.mkdtemp)((0, import_node_path43.join)(deps.root, "tmp", "suite-pin-"));
57913
+ await (0, import_promises7.mkdir)((0, import_node_path44.join)(deps.root, "tmp"), { recursive: true });
57914
+ scratch = await (0, import_promises7.mkdtemp)((0, import_node_path44.join)(deps.root, "tmp", "suite-pin-"));
57627
57915
  const run = (file, args) => deps.run(file, args, scratch);
57628
57916
  await run("git", ["clone", "--quiet", "--branch", "development", "--single-branch", `https://github.com/${SUITE_REPO}.git`, "."]);
57629
57917
  await run("git", ["switch", "-c", branch]);
@@ -57650,7 +57938,7 @@ async function openSuitePinPr(deps, input) {
57650
57938
  } finally {
57651
57939
  if (scratch) {
57652
57940
  try {
57653
- await (0, import_promises6.rm)(scratch, { recursive: true, force: true });
57941
+ await (0, import_promises7.rm)(scratch, { recursive: true, force: true });
57654
57942
  } catch (e) {
57655
57943
  const cleanupFailure = failure ?? new SuitePinError(repo, tag, e);
57656
57944
  cleanupFailure.diagnostic.cleanupFailed = true;
@@ -57676,7 +57964,7 @@ function prTitleRefusal(title) {
57676
57964
  // src/command-register-collaboration.ts
57677
57965
  var import_node_child_process18 = require("node:child_process");
57678
57966
  var import_node_fs54 = require("node:fs");
57679
- var import_promises8 = require("node:fs/promises");
57967
+ var import_promises9 = require("node:fs/promises");
57680
57968
  init_clean_exit();
57681
57969
  init_cli_shared();
57682
57970
  init_error_codes();
@@ -57699,11 +57987,11 @@ function spawnDetachedSelf(args, deps, opts = {}) {
57699
57987
  }
57700
57988
 
57701
57989
  // src/command-register-collaboration.ts
57702
- var import_node_path50 = require("node:path");
57990
+ var import_node_path51 = require("node:path");
57703
57991
  init_house_map();
57704
57992
 
57705
57993
  // src/issue-body.ts
57706
- var import_node_os23 = require("node:os");
57994
+ var import_node_os24 = require("node:os");
57707
57995
  init_error_codes();
57708
57996
  var TextArgError = class extends Error {
57709
57997
  constructor(message2, code, offendingFlag) {
@@ -57716,7 +58004,7 @@ var TextArgError = class extends Error {
57716
58004
  offendingFlag;
57717
58005
  };
57718
58006
  function emptyStdinMessage(fileFlag) {
57719
- if ((0, import_node_os23.platform)() === "win32") {
58007
+ if ((0, import_node_os24.platform)() === "win32") {
57720
58008
  return `${fileFlag} - read empty stdin (on Windows, ${fileFlag} - is unreliable through the npm .cmd shim \u2014 use ${fileFlag} <path>, or pipe to \`node cli/dist/index.cjs\` directly)`;
57721
58009
  }
57722
58010
  return `${fileFlag} - read empty stdin (nothing piped \u2014 pass a heredoc/pipe, or ${fileFlag} <path>)`;
@@ -57817,7 +58105,7 @@ init_github_quota();
57817
58105
 
57818
58106
  // src/issue-commands.ts
57819
58107
  var import_node_fs46 = require("node:fs");
57820
- var import_node_crypto22 = require("node:crypto");
58108
+ var import_node_crypto23 = require("node:crypto");
57821
58109
  init_cli_shared();
57822
58110
  init_clean_exit();
57823
58111
  init_error_codes();
@@ -58294,7 +58582,7 @@ function rowIdempotencyKey(batchKey, spec) {
58294
58582
  const identity = `${spec.type}
58295
58583
  ${spec.title.trim()}
58296
58584
  ${spec.body ?? ""}`;
58297
- const hash = (0, import_node_crypto22.createHash)("sha256").update(identity).digest("hex").slice(0, 16);
58585
+ const hash = (0, import_node_crypto23.createHash)("sha256").update(identity).digest("hex").slice(0, 16);
58298
58586
  return `${batchKey}:${hash}`;
58299
58587
  }
58300
58588
  var BATCH_SPEC_KEYS = /* @__PURE__ */ new Set(["type", "title", "body", "priority", "labels", "label", "parent", "repo", "surface"]);
@@ -58478,6 +58766,11 @@ ${lines2}`);
58478
58766
  if (deps.attach) {
58479
58767
  const attached = await deps.attach(row.number, rowRepo(spec), rowPriority);
58480
58768
  row.onBoard = attached.onBoard;
58769
+ if (attached.prioritySet === false) {
58770
+ row.status = "partial";
58771
+ row.prioritySet = false;
58772
+ row.priorityError = attached.priorityError;
58773
+ }
58481
58774
  if (attached.projectItemId) row.projectItemId = attached.projectItemId;
58482
58775
  if (attached.boardAttach) {
58483
58776
  row.boardAttach = attached.boardAttach;
@@ -58754,7 +59047,7 @@ ${lines2}`, {
58754
59047
 
58755
59048
  // src/post-merge-recon.ts
58756
59049
  var import_node_fs47 = require("node:fs");
58757
- var import_node_path44 = require("node:path");
59050
+ var import_node_path45 = require("node:path");
58758
59051
 
58759
59052
  // src/cross-repo-filing-issue.ts
58760
59053
  init_github_client();
@@ -58924,7 +59217,7 @@ function buildPostMergeReconRecovery(input) {
58924
59217
  }
58925
59218
  function writePostMergeReconRecovery(cwd, recovery) {
58926
59219
  const path2 = postMergeReconStatePath(cwd, recovery.repo, recovery.pr);
58927
- (0, import_node_fs47.mkdirSync)((0, import_node_path44.dirname)(path2), { recursive: true });
59220
+ (0, import_node_fs47.mkdirSync)((0, import_node_path45.dirname)(path2), { recursive: true });
58928
59221
  (0, import_node_fs47.writeFileSync)(path2, `${JSON.stringify(recovery, null, 2)}
58929
59222
  `, "utf8");
58930
59223
  return path2;
@@ -58964,7 +59257,7 @@ function postMergeReconWarnings(input) {
58964
59257
  }
58965
59258
 
58966
59259
  // src/pr-commands.ts
58967
- var import_promises7 = require("node:fs/promises");
59260
+ var import_promises8 = require("node:fs/promises");
58968
59261
  init_cli_shared();
58969
59262
 
58970
59263
  // src/api-read.ts
@@ -60457,10 +60750,10 @@ function registerPrLifecycleCommands(program3) {
60457
60750
  let body;
60458
60751
  try {
60459
60752
  if (o.title || o.titleFile) {
60460
- title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises7.readFile, readStdin });
60753
+ title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises8.readFile, readStdin });
60461
60754
  }
60462
60755
  if (o.body || o.bodyFile) {
60463
- body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises7.readFile, readStdin });
60756
+ body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises8.readFile, readStdin });
60464
60757
  }
60465
60758
  } catch (e) {
60466
60759
  return fail(`pr edit: ${e.message}`);
@@ -60491,7 +60784,7 @@ function registerPrLifecycleCommands(program3) {
60491
60784
  }
60492
60785
  let body;
60493
60786
  try {
60494
- body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises7.readFile, readStdin });
60787
+ body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises8.readFile, readStdin });
60495
60788
  } catch (e) {
60496
60789
  return fail(`pr comment: ${e.message}`);
60497
60790
  }
@@ -60776,8 +61069,8 @@ function registerPrLifecycleCommands(program3) {
60776
61069
  // src/review-verdict.ts
60777
61070
  var import_node_child_process15 = require("node:child_process");
60778
61071
  var import_node_fs49 = require("node:fs");
60779
- var import_node_os24 = require("node:os");
60780
- var import_node_path45 = require("node:path");
61072
+ var import_node_os25 = require("node:os");
61073
+ var import_node_path46 = require("node:path");
60781
61074
  init_cli_shared();
60782
61075
  var REVIEW_VERDICT_MARKER = "<!-- zeroci-review v1 -->";
60783
61076
  var REVIEW_VERDICTS = ["PROCEED", "CORRECT", "ESCALATE"];
@@ -60942,8 +61235,8 @@ async function readPrIssueComments(number, repo) {
60942
61235
  return comments;
60943
61236
  }
60944
61237
  async function postPrCommentFromFile(number, repo, body) {
60945
- const dir = (0, import_node_fs49.mkdtempSync)((0, import_node_path45.join)((0, import_node_os24.tmpdir)(), "mmi-review-verdict-"));
60946
- const path2 = (0, import_node_path45.join)(dir, "body.md");
61238
+ const dir = (0, import_node_fs49.mkdtempSync)((0, import_node_path46.join)((0, import_node_os25.tmpdir)(), "mmi-review-verdict-"));
61239
+ const path2 = (0, import_node_path46.join)(dir, "body.md");
60947
61240
  try {
60948
61241
  (0, import_node_fs49.writeFileSync)(path2, body, "utf8");
60949
61242
  const { stdout } = await execFileP("gh", ["pr", "comment", number, "--repo", repo, "--body-file", path2], { timeout: GH_MUTATION_TIMEOUT_MS });
@@ -61439,17 +61732,17 @@ function createPreparationDeps(input, io = {}) {
61439
61732
 
61440
61733
  // src/worktree-residue-sweep.ts
61441
61734
  var import_node_fs53 = require("node:fs");
61442
- var import_node_path49 = require("node:path");
61735
+ var import_node_path50 = require("node:path");
61443
61736
 
61444
61737
  // src/worktree-merge-cleanup.ts
61445
61738
  var import_node_fs52 = require("node:fs");
61446
- var import_node_path48 = require("node:path");
61739
+ var import_node_path49 = require("node:path");
61447
61740
  init_cli_shared();
61448
61741
 
61449
61742
  // src/stage-runner.ts
61450
61743
  var import_node_child_process17 = require("node:child_process");
61451
61744
  var import_node_fs51 = require("node:fs");
61452
- var import_node_path47 = require("node:path");
61745
+ var import_node_path48 = require("node:path");
61453
61746
  var import_node_net = require("node:net");
61454
61747
  var import_node_util5 = require("node:util");
61455
61748
 
@@ -61460,7 +61753,7 @@ function normalizeEol2(s) {
61460
61753
 
61461
61754
  // src/env-detection.ts
61462
61755
  var import_node_fs50 = require("node:fs");
61463
- var import_node_path46 = require("node:path");
61756
+ var import_node_path47 = require("node:path");
61464
61757
  var SKIPPED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", ".jerv", ".pi", ".venv", "venv"]);
61465
61758
  function isEnvBaseName(name) {
61466
61759
  return name.toLowerCase() === ".env";
@@ -61478,13 +61771,13 @@ function findEnvFiles(root) {
61478
61771
  const relPath = rel ? `${rel}/${entry.name}` : entry.name;
61479
61772
  if (entry.isDirectory()) {
61480
61773
  if (SKIPPED_DIRS.has(entry.name)) continue;
61481
- walk2((0, import_node_path46.join)(dir, entry.name), relPath);
61774
+ walk2((0, import_node_path47.join)(dir, entry.name), relPath);
61482
61775
  continue;
61483
61776
  }
61484
61777
  if (isEnvBaseName(entry.name)) {
61485
61778
  let isDir = false;
61486
61779
  try {
61487
- isDir = (0, import_node_fs50.lstatSync)((0, import_node_path46.join)(dir, entry.name)).isDirectory();
61780
+ isDir = (0, import_node_fs50.lstatSync)((0, import_node_path47.join)(dir, entry.name)).isDirectory();
61488
61781
  } catch {
61489
61782
  }
61490
61783
  if (!isDir) found.push(relPath);
@@ -61623,10 +61916,10 @@ var DOCKER_TIMEOUT_MS = 15e3;
61623
61916
  function stageBash() {
61624
61917
  if (process.platform !== "win32") return "bash";
61625
61918
  const configured = process.env.SHELL;
61626
- if (configured && (0, import_node_path47.isAbsolute)(configured) && /[/\\]bash(?:\.exe)?$/i.test(configured) && !/[/\\](?:System32|Sysnative)[/\\]bash(?:\.exe)?$/i.test(configured) && (0, import_node_fs51.existsSync)(configured)) return configured;
61919
+ if (configured && (0, import_node_path48.isAbsolute)(configured) && /[/\\]bash(?:\.exe)?$/i.test(configured) && !/[/\\](?:System32|Sysnative)[/\\]bash(?:\.exe)?$/i.test(configured) && (0, import_node_fs51.existsSync)(configured)) return configured;
61627
61920
  const candidates = [
61628
- (0, import_node_path47.join)(process.env.ProgramFiles || "C:\\Program Files", "Git", "bin", "bash.exe"),
61629
- ...process.env.LOCALAPPDATA ? [(0, import_node_path47.join)(process.env.LOCALAPPDATA, "Programs", "Git", "bin", "bash.exe")] : []
61921
+ (0, import_node_path48.join)(process.env.ProgramFiles || "C:\\Program Files", "Git", "bin", "bash.exe"),
61922
+ ...process.env.LOCALAPPDATA ? [(0, import_node_path48.join)(process.env.LOCALAPPDATA, "Programs", "Git", "bin", "bash.exe")] : []
61630
61923
  ];
61631
61924
  const bash = candidates.find((path2) => (0, import_node_fs51.existsSync)(path2));
61632
61925
  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.");
@@ -61766,7 +62059,7 @@ function appendForceRecreate(up) {
61766
62059
  return `${up.trimEnd()} --force-recreate`;
61767
62060
  }
61768
62061
  function stageStatePath(cwd = process.cwd()) {
61769
- return (0, import_node_path47.join)(cwd, "tmp", "stage", "state.json");
62062
+ return (0, import_node_path48.join)(cwd, "tmp", "stage", "state.json");
61770
62063
  }
61771
62064
  function normPath(path2) {
61772
62065
  return path2.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
@@ -61983,7 +62276,7 @@ function stageProcessEnv(stagePort, extraEnv) {
61983
62276
  }
61984
62277
  function composeResolvesPort(cwd) {
61985
62278
  if (process.env.PORT) return true;
61986
- const envFile = (0, import_node_path47.join)(cwd, ".env");
62279
+ const envFile = (0, import_node_path48.join)(cwd, ".env");
61987
62280
  return (0, import_node_fs51.existsSync)(envFile) && envFileKeys((0, import_node_fs51.readFileSync)(envFile, "utf8")).has("PORT");
61988
62281
  }
61989
62282
  function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
@@ -61999,13 +62292,13 @@ function stageComposeEnv(config, stagePort, vaultEnvMerge, cwd) {
61999
62292
  function stageComposeFiles(cwd) {
62000
62293
  const files = ["docker-compose.yml"];
62001
62294
  for (const conventional of ["docker-compose.override.yml", "docker-compose.override.yaml"]) {
62002
- if ((0, import_node_fs51.existsSync)((0, import_node_path47.join)(cwd, conventional))) files.push(conventional);
62295
+ if ((0, import_node_fs51.existsSync)((0, import_node_path48.join)(cwd, conventional))) files.push(conventional);
62003
62296
  }
62004
62297
  return [...files, RUNTIME_ENV_OVERRIDE_RELPATH];
62005
62298
  }
62006
62299
  async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
62007
62300
  const keys = Object.keys(vaultEnvMerge ?? {});
62008
- if (!keys.length || !(0, import_node_fs51.existsSync)((0, import_node_path47.join)(cwd, "docker-compose.yml"))) return {};
62301
+ if (!keys.length || !(0, import_node_fs51.existsSync)((0, import_node_path48.join)(cwd, "docker-compose.yml"))) return {};
62009
62302
  let configJson;
62010
62303
  try {
62011
62304
  const { stdout } = await execFileP3("docker", ["compose", "config", "--format", "json"], {
@@ -62021,15 +62314,15 @@ async function prepareRuntimeEnvPassthrough(cwd, composeEnv, vaultEnvMerge) {
62021
62314
  }
62022
62315
  const body = renderRuntimeEnvOverride(runtimeEnvMarkedServices(configJson), keys, runtimeEnvExcludes(configJson), runtimeEnvSelects(configJson));
62023
62316
  if (!body) return {};
62024
- const overridePath = (0, import_node_path47.join)(cwd, RUNTIME_ENV_OVERRIDE_RELPATH);
62025
- (0, import_node_fs51.mkdirSync)((0, import_node_path47.join)(cwd, "tmp", "stage"), { recursive: true });
62317
+ const overridePath = (0, import_node_path48.join)(cwd, RUNTIME_ENV_OVERRIDE_RELPATH);
62318
+ (0, import_node_fs51.mkdirSync)((0, import_node_path48.join)(cwd, "tmp", "stage"), { recursive: true });
62026
62319
  (0, import_node_fs51.writeFileSync)(overridePath, body, "utf8");
62027
62320
  return stageComposeFileEnv(stageComposeFiles(cwd));
62028
62321
  }
62029
62322
  async function ensureStageRuntimeEnv(config, opts, cwd) {
62030
62323
  if (!config.ensureEnv) return;
62031
- const target = (0, import_node_path47.join)(cwd, config.ensureEnv.target);
62032
- const example = (0, import_node_path47.join)(cwd, config.ensureEnv.example);
62324
+ const target = (0, import_node_path48.join)(cwd, config.ensureEnv.target);
62325
+ const example = (0, import_node_path48.join)(cwd, config.ensureEnv.example);
62033
62326
  if (!(0, import_node_fs51.existsSync)(target) && (0, import_node_fs51.existsSync)(example)) {
62034
62327
  (0, import_node_fs51.copyFileSync)(example, target);
62035
62328
  } else if ((0, import_node_fs51.existsSync)(target) && (0, import_node_fs51.existsSync)(example)) {
@@ -62383,7 +62676,7 @@ function normPath2(p) {
62383
62676
  return p.replace(/\\/g, "/").replace(/\/+$/, "");
62384
62677
  }
62385
62678
  function unlinkNodeModulesJunction(wtPath) {
62386
- const nm = (0, import_node_path48.join)(wtPath, "node_modules");
62679
+ const nm = (0, import_node_path49.join)(wtPath, "node_modules");
62387
62680
  try {
62388
62681
  if ((0, import_node_fs52.lstatSync)(nm).isSymbolicLink()) (0, import_node_fs52.rmdirSync)(nm);
62389
62682
  return { ok: true };
@@ -62520,7 +62813,7 @@ async function preCleanWorktreeForRemoval(wtPath, execGit) {
62520
62813
  }
62521
62814
  async function listNestedIgnoredNodeModules(wtPath, execGit) {
62522
62815
  const out = await execGit(["-C", wtPath, "ls-files", "--others", "--ignored", "--exclude-standard", "--directory"]).catch(() => "");
62523
- return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0, import_node_path48.join)(wtPath, line.slice(0, -1)));
62816
+ return out.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.endsWith("node_modules/") && line !== "node_modules/").map((line) => (0, import_node_path49.join)(wtPath, line.slice(0, -1)));
62524
62817
  }
62525
62818
  function safeRemoveTree(path2) {
62526
62819
  const stat4 = (0, import_node_fs52.lstatSync)(path2);
@@ -62533,7 +62826,7 @@ function safeRemoveTree(path2) {
62533
62826
  return;
62534
62827
  }
62535
62828
  if (stat4.isDirectory()) {
62536
- for (const entry of (0, import_node_fs52.readdirSync)(path2)) safeRemoveTree((0, import_node_path48.join)(path2, entry));
62829
+ for (const entry of (0, import_node_fs52.readdirSync)(path2)) safeRemoveTree((0, import_node_path49.join)(path2, entry));
62537
62830
  (0, import_node_fs52.rmdirSync)(path2);
62538
62831
  return;
62539
62832
  }
@@ -62541,7 +62834,7 @@ function safeRemoveTree(path2) {
62541
62834
  }
62542
62835
  async function removeWorktreeNodeModules(wtPath) {
62543
62836
  try {
62544
- safeRemoveTree((0, import_node_path48.join)(wtPath, "node_modules"));
62837
+ safeRemoveTree((0, import_node_path49.join)(wtPath, "node_modules"));
62545
62838
  return { ok: true };
62546
62839
  } catch (e) {
62547
62840
  if (e.code === "ENOENT") return { ok: true };
@@ -62586,7 +62879,7 @@ function unlinkEscapingReparsePoints(root, primaryRoot) {
62586
62879
  return { ok: false, error: `cannot scan ${normPath2(dir)} for reparse points: ${errorMessage(e)}` };
62587
62880
  }
62588
62881
  for (const entry of entries) {
62589
- const child2 = (0, import_node_path48.join)(dir, entry.name);
62882
+ const child2 = (0, import_node_path49.join)(dir, entry.name);
62590
62883
  if (entry.isSymbolicLink()) {
62591
62884
  let target = "";
62592
62885
  try {
@@ -62913,7 +63206,7 @@ async function cleanupPrMergeLocalBranch(branch, options) {
62913
63206
  const preHelperGuard = unlinkEscapingReparsePoints(wtPath, options.primaryRoot);
62914
63207
  if (!preHelperGuard.ok) return refuseReparseEscape(preHelperGuard.error);
62915
63208
  unlinkedReparsePoints.push(...preHelperGuard.unlinked);
62916
- if (pathExists((0, import_node_path48.join)(wtPath, "node_modules"))) {
63209
+ if (pathExists((0, import_node_path49.join)(wtPath, "node_modules"))) {
62917
63210
  const nmRemoved = await (options.removeRealNodeModules ?? removeWorktreeNodeModules)(wtPath);
62918
63211
  if (!nmRemoved.ok) {
62919
63212
  report.worktree = {
@@ -63049,35 +63342,35 @@ function renderPrMergeCleanupLines(cleanup) {
63049
63342
  // src/worktree-residue-sweep.ts
63050
63343
  var RESIDUE_LEDGER_FILE = "mmi-worktree-residue.json";
63051
63344
  function canonicalWorktreeRoot(primary) {
63052
- return (0, import_node_path49.join)((0, import_node_path49.dirname)(primary), "mmi-worktrees", (0, import_node_path49.basename)(primary));
63345
+ return (0, import_node_path50.join)((0, import_node_path50.dirname)(primary), "mmi-worktrees", (0, import_node_path50.basename)(primary));
63053
63346
  }
63054
63347
  function lastSegment(p) {
63055
- return (0, import_node_path49.basename)(p.split(/[\\/]+/).filter(Boolean).pop() ?? p);
63348
+ return (0, import_node_path50.basename)(p.split(/[\\/]+/).filter(Boolean).pop() ?? p);
63056
63349
  }
63057
63350
  function readProvenance(folder, gitCommonDir, exists) {
63058
63351
  const found = {
63059
63352
  path: folder,
63060
63353
  gitdirPresent: false,
63061
- registrationId: (0, import_node_path49.basename)(folder),
63354
+ registrationId: (0, import_node_path50.basename)(folder),
63062
63355
  registrationPresent: false
63063
63356
  };
63064
63357
  try {
63065
- const pointer = /^\s*gitdir:\s*(.+?)\s*$/m.exec((0, import_node_fs53.readFileSync)((0, import_node_path49.join)(folder, ".git"), "utf8"));
63358
+ const pointer = /^\s*gitdir:\s*(.+?)\s*$/m.exec((0, import_node_fs53.readFileSync)((0, import_node_path50.join)(folder, ".git"), "utf8"));
63066
63359
  if (pointer) {
63067
63360
  const target = pointer[1];
63068
- found.gitdir = (0, import_node_path49.isAbsolute)(target) ? target : (0, import_node_path49.join)(folder, target);
63361
+ found.gitdir = (0, import_node_path50.isAbsolute)(target) ? target : (0, import_node_path50.join)(folder, target);
63069
63362
  found.gitdirPresent = exists(found.gitdir);
63070
63363
  found.registrationId = lastSegment(found.gitdir);
63071
63364
  }
63072
63365
  } catch {
63073
63366
  }
63074
63367
  try {
63075
- found.registrationPresent = exists((0, import_node_path49.join)(gitCommonDir, "worktrees", found.registrationId));
63368
+ found.registrationPresent = exists((0, import_node_path50.join)(gitCommonDir, "worktrees", found.registrationId));
63076
63369
  } catch {
63077
63370
  }
63078
63371
  if (found.gitdir && found.gitdirPresent) {
63079
63372
  try {
63080
- const head = (0, import_node_fs53.readFileSync)((0, import_node_path49.join)(found.gitdir, "HEAD"), "utf8").trim();
63373
+ const head = (0, import_node_fs53.readFileSync)((0, import_node_path50.join)(found.gitdir, "HEAD"), "utf8").trim();
63081
63374
  const branch = /^ref:\s*(.+?)\s*$/.exec(head);
63082
63375
  if (branch) found.head = branch[1].startsWith("refs/heads/") ? branch[1].slice("refs/heads/".length) : `ref ${branch[1]}`;
63083
63376
  else if (/^[0-9a-f]{7,64}$/i.test(head)) found.head = `detached ${head.slice(0, 12)}`;
@@ -63096,7 +63389,7 @@ function norm2(p) {
63096
63389
  return process.platform === "win32" ? s.toLowerCase() : s;
63097
63390
  }
63098
63391
  function residueLedgerPath(gitCommonDir) {
63099
- return (0, import_node_path49.join)(gitCommonDir, RESIDUE_LEDGER_FILE);
63392
+ return (0, import_node_path50.join)(gitCommonDir, RESIDUE_LEDGER_FILE);
63100
63393
  }
63101
63394
  function readPaths(file, deps) {
63102
63395
  try {
@@ -63109,7 +63402,7 @@ function readPaths(file, deps) {
63109
63402
  function writePaths(file, paths, deps) {
63110
63403
  try {
63111
63404
  const write = deps.writeLedger ?? ((f, t) => {
63112
- (0, import_node_fs53.mkdirSync)((0, import_node_path49.dirname)(f), { recursive: true });
63405
+ (0, import_node_fs53.mkdirSync)((0, import_node_path50.dirname)(f), { recursive: true });
63113
63406
  (0, import_node_fs53.writeFileSync)(f, t, "utf8");
63114
63407
  });
63115
63408
  write(file, `${JSON.stringify([...new Set(paths)], null, 1)}
@@ -63169,7 +63462,7 @@ async function sweepWorktreeResidue(gitCommonDir, registered, deps = {}) {
63169
63462
  } catch {
63170
63463
  }
63171
63464
  for (const name of children) {
63172
- const child2 = (0, import_node_path49.join)(root, name);
63465
+ const child2 = (0, import_node_path50.join)(root, name);
63173
63466
  const key = norm2(child2);
63174
63467
  if (live.has(key) || key === primary || keptKeys.has(key) || !isDir(child2)) continue;
63175
63468
  let entries;
@@ -63221,10 +63514,10 @@ function argvWantsJson2() {
63221
63514
  return process.argv.some((a) => a === "--json" || a.startsWith("--json="));
63222
63515
  }
63223
63516
  function hubRoot() {
63224
- const fromPkg = (0, import_node_path50.join)(__dirname, "..", "..");
63517
+ const fromPkg = (0, import_node_path51.join)(__dirname, "..", "..");
63225
63518
  const marker = "skills/bootstrap/seeds/manifest.json";
63226
- if ((0, import_node_fs54.existsSync)((0, import_node_path50.join)(fromPkg, marker))) return fromPkg;
63227
- if ((0, import_node_fs54.existsSync)((0, import_node_path50.join)(process.cwd(), marker))) return process.cwd();
63519
+ if ((0, import_node_fs54.existsSync)((0, import_node_path51.join)(fromPkg, marker))) return fromPkg;
63520
+ if ((0, import_node_fs54.existsSync)((0, import_node_path51.join)(process.cwd(), marker))) return process.cwd();
63228
63521
  return null;
63229
63522
  }
63230
63523
  function ciAuditDeps() {
@@ -63236,7 +63529,7 @@ function ciAuditDeps() {
63236
63529
  getProjectMeta: async (slug) => fetchProjectBySlug(slug, registryClientDeps(await cfgPromise)),
63237
63530
  readSeedFile: (path2) => {
63238
63531
  if (!root) return null;
63239
- const fullPath = (0, import_node_path50.join)(root, path2);
63532
+ const fullPath = (0, import_node_path51.join)(root, path2);
63240
63533
  return (0, import_node_fs54.existsSync)(fullPath) ? (0, import_node_fs54.readFileSync)(fullPath, "utf8") : null;
63241
63534
  }
63242
63535
  };
@@ -63276,8 +63569,10 @@ async function attachToProject(issueNumber, repo, priority) {
63276
63569
  await setBoardItemPriority(defaultGitHubClient(), cfg, projectItemId2, priority);
63277
63570
  } catch (e) {
63278
63571
  const err = e;
63279
- process.stderr.write(`warning: issue #${issueNumber} board Priority not set: ${(err.stderr || err.message || String(e)).trim()}
63572
+ const priorityError = (err.stderr || err.message || String(e)).trim();
63573
+ process.stderr.write(`warning: issue #${issueNumber} board Priority not set: ${priorityError}; issue and board attachment already exist; do not recreate the issue
63280
63574
  `);
63575
+ return { projectItemId: projectItemId2, onBoard: true, credential: "app_installation", prioritySet: false, priorityError };
63281
63576
  }
63282
63577
  }
63283
63578
  return { projectItemId: projectItemId2, onBoard: true, credential: "app_installation" };
@@ -63461,7 +63756,7 @@ function registerCollaborationCommands(program3) {
63461
63756
  try {
63462
63757
  title = await resolveIssueTitle(
63463
63758
  { title: opts.title, titleFile: opts.titleFile },
63464
- { readFile: import_promises8.readFile, readStdin }
63759
+ { readFile: import_promises9.readFile, readStdin }
63465
63760
  );
63466
63761
  } catch (e) {
63467
63762
  return fail(
@@ -63515,8 +63810,8 @@ function registerCollaborationCommands(program3) {
63515
63810
  let surfaceInferred;
63516
63811
  try {
63517
63812
  issueType = resolveCreateType(o.type, "issue create", o.label);
63518
- title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises8.readFile, readStdin });
63519
- body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises8.readFile, readStdin });
63813
+ title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises9.readFile, readStdin });
63814
+ body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises9.readFile, readStdin });
63520
63815
  if (o.idempotencyKey) body = appendIdempotencyMarker(body, o.idempotencyKey);
63521
63816
  priority = resolveCreatePriority(o.priority, "issue create");
63522
63817
  extraLabels = [...o.label ?? []];
@@ -63627,12 +63922,13 @@ function registerCollaborationCommands(program3) {
63627
63922
  `);
63628
63923
  const linkRecovery = parentRecoveryRef !== void 0 && !parent ? `mmi-cli oracle issue link-child ${parentRecoveryRef} ${childRef}` : void 0;
63629
63924
  console.log(JSON.stringify({
63630
- status: boardRecovery || linkRecovery ? "partial" : "created",
63925
+ status: boardRecovery || linkRecovery || attached.prioritySet === false ? "partial" : "created",
63631
63926
  ...created,
63632
63927
  label: issueType,
63633
63928
  priority,
63634
63929
  projectItemId,
63635
63930
  onBoard,
63931
+ ...attached.prioritySet === false ? { prioritySet: false, priorityError: attached.priorityError } : {},
63636
63932
  ...attachCredential ? { credential: attachCredential } : {},
63637
63933
  // #5489: partial receipt when the GitHub issue landed but Project v2 attach hit GraphQL quota.
63638
63934
  ...!onBoard ? {
@@ -63831,7 +64127,7 @@ function registerCollaborationCommands(program3) {
63831
64127
  }
63832
64128
  let body;
63833
64129
  try {
63834
- body = o.edit !== void 0 ? "" : await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises8.readFile, readStdin });
64130
+ body = o.edit !== void 0 ? "" : await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises9.readFile, readStdin });
63835
64131
  } catch (e) {
63836
64132
  return fail(`issue comment: ${e.message}`);
63837
64133
  }
@@ -63910,8 +64206,8 @@ ${list}`);
63910
64206
  let title;
63911
64207
  const sourceRepo = o.repo ?? await resolveRepo(void 0);
63912
64208
  try {
63913
- title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises8.readFile, readStdin });
63914
- body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises8.readFile, readStdin });
64209
+ title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises9.readFile, readStdin });
64210
+ body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises9.readFile, readStdin });
63915
64211
  priority = resolveCreatePriority(o.priority, "report");
63916
64212
  if (!ISSUE_TYPES.includes(o.type)) {
63917
64213
  throw new Error(`unknown issue type "${o.type}" \u2014 expected one of: ${ISSUE_TYPES.join(", ")}`);
@@ -63965,8 +64261,8 @@ ${list}`);
63965
64261
  let args;
63966
64262
  try {
63967
64263
  skill = assertSkillName(o.skill);
63968
- rawBody = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises8.readFile, readStdin });
63969
- const rawTitle = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises8.readFile, readStdin });
64264
+ rawBody = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises9.readFile, readStdin });
64265
+ const rawTitle = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises9.readFile, readStdin });
63970
64266
  title = buildSkillLessonTitle(skill, rawTitle);
63971
64267
  priority = resolveCreatePriority(o.priority, "skill-lesson");
63972
64268
  body = buildSkillLessonBody(rawBody, sourceRepo, pluginSha);
@@ -64019,16 +64315,17 @@ ${list}`);
64019
64315
  }
64020
64316
  }
64021
64317
  const created = requireGhCreateOk(await ghCreate(args), "skill-lesson");
64022
- const { projectItemId, onBoard } = await attachToProject(created.number, targetRepo2, priority);
64023
- console.log(JSON.stringify({ ...created, projectItemId, onBoard }));
64318
+ const attached = await attachToProject(created.number, targetRepo2, priority);
64319
+ const { projectItemId, onBoard } = attached;
64320
+ console.log(JSON.stringify({ ...created, projectItemId, onBoard, ...attached.prioritySet === false ? { status: "partial", prioritySet: false, priorityError: attached.priorityError } : {} }));
64024
64321
  });
64025
64322
  const pr = program3.command("pr").description("pull requests \u2014 reliable create with structured output");
64026
64323
  withExamples(pr.command("create").description("create a PR and print {number,url} JSON").option("--title <title>", "PR title").option("--title-file <path|->", "read the PR title from a UTF-8 file, or from stdin with -").option("--body <body>", "PR body (markdown)").option("--body-file <path|->", "read PR body from a UTF-8 file, or from stdin with -").option("--base <branch>", "base branch (defaults to the repo default)").option("--head <branch>", "head branch (defaults to the current branch)").option("--repo <owner/repo>", "target repo (defaults to the current repo)").option("--draft", "open the PR in draft state (#2667)").option("--no-zeroci", "compatibility option; pr create does not mint receipts").option("--json", "machine-readable output (default; accepted for parity)").action(async (o) => {
64027
64324
  let body;
64028
64325
  let title;
64029
64326
  try {
64030
- title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises8.readFile, readStdin });
64031
- body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises8.readFile, readStdin });
64327
+ title = await resolveIssueTitle({ title: o.title, titleFile: o.titleFile }, { readFile: import_promises9.readFile, readStdin });
64328
+ body = await resolveIssueBody({ body: o.body, bodyFile: o.bodyFile }, { readFile: import_promises9.readFile, readStdin });
64032
64329
  } catch (e) {
64033
64330
  return fail(`pr create: ${e.message}`, e instanceof TextArgError ? { code: e.code, offending_flag: e.offendingFlag } : void 0);
64034
64331
  }
@@ -64122,11 +64419,11 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
64122
64419
  }
64123
64420
  });
64124
64421
  async function listCiWorkflowPaths(cwd = process.cwd()) {
64125
- const wfDir = (0, import_node_path50.join)(cwd, ".github", "workflows");
64422
+ const wfDir = (0, import_node_path51.join)(cwd, ".github", "workflows");
64126
64423
  if (!(0, import_node_fs54.existsSync)(wfDir)) return [];
64127
64424
  return (0, import_node_fs54.readdirSync)(wfDir).filter((name) => /\.(ya?ml)$/i.test(name)).filter((name) => {
64128
64425
  try {
64129
- return workflowReportsPrChecks((0, import_node_fs54.readFileSync)((0, import_node_path50.join)(wfDir, name), "utf8"));
64426
+ return workflowReportsPrChecks((0, import_node_fs54.readFileSync)((0, import_node_path51.join)(wfDir, name), "utf8"));
64130
64427
  } catch {
64131
64428
  return true;
64132
64429
  }
@@ -64381,7 +64678,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
64381
64678
  }
64382
64679
  class PrHeadBehindBaseError extends Error {
64383
64680
  }
64384
- pr.command("land <numbers...>").description("agent merge path (#1440): train probe \u2014 checks-wait \u2014 merge --auto \u2014 poll enqueued \u2014 development PRs only. Several PR numbers land together (#8322): all are enqueued, waited on together, a queue-ejected PR is updated and re-enqueued once, a queue merge is followed by worktree/branch cleanup and board Done, and one table (or --json) reports each outcome").option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the PR repo)").option("--enqueue-only", "return once queue membership is confirmed; without a merge queue, land normally").option("--abandon-admission", "one stuck-receipt recovery, one PR per call: journal the saved non-terminal (pending/enqueued) queue-admission receipt terminal ONLY after positively observing the PR OPEN at the receipt head and absent from the queue; any unprovable observation refuses and changes nothing (#9951). The next pr land then submits a new intent with no new head").option("--no-require-train", "skip train-authority preflight (not recommended for autonomous agents)").option("--force", "silence the leftover-scratch warning (scratch never blocks, #8785) and acknowledge a negated-closing-keyword (#3718) or ambiguous-cross-repo-closing (#4279) refusal").option("--without-review <reason>", "compatibility option; shared merge helpers do not require a review verdict").option("--timeout <minutes>", `merge-queue wait per PR (default ${PR_LAND_QUEUE_TIMEOUT_MS / 6e4} minutes, one window per ${PR_LAND_QUEUE_BATCH_SIZE} PRs \u2014 per-repo ruleset check_response_timeout_minutes is 15 default with 30/45/60 overrides in .github/rulesets/mmi-development-merge-queue.json; agents never edit the ruleset to shorten a queue wait)`).option("--preserve-worktree", "after a queue merge keep the local worktree and branch (skips the post-merge cleanup)").option("--expect-head <sha>", "pin the PR head to this full 40-hex SHA (#8456): checked before landing, after checks and by GitHub at enqueue (--match-head-commit); never updates a BEHIND head and never retries an ejection \u2014 it stops with pinnedHead.refused instead. One PR only").action(async (numbers, opts) => {
64681
+ pr.command("land <numbers...>").description("agent merge path (#1440): train probe \u2014 checks-wait \u2014 merge --auto \u2014 poll enqueued \u2014 development PRs only. Several PR numbers land together (#8322): all are enqueued, waited on together, a queue-ejected PR is updated and re-enqueued once, a queue merge is followed by worktree/branch cleanup and board Done, and one table (or --json) reports each outcome").option("--json", "machine-readable output").option("--repo <owner/repo>", "target repo (defaults to the PR repo)").option("--enqueue-only", "return once queue membership is confirmed; without a merge queue, land normally").option("--abandon-admission", "one stuck-receipt recovery, one PR per call: journal the saved non-terminal (pending/enqueued, or a dispatching send that saved no operation UUID after its sender died \u2014 #9995) queue-admission receipt terminal ONLY after positively observing the PR OPEN at the receipt head and absent from the queue; any unprovable observation refuses and changes nothing (#9951). The next pr land then submits a new intent with no new head").option("--no-require-train", "skip train-authority preflight (not recommended for autonomous agents)").option("--force", "silence the leftover-scratch warning (scratch never blocks, #8785) and acknowledge a negated-closing-keyword (#3718) or ambiguous-cross-repo-closing (#4279) refusal").option("--without-review <reason>", "compatibility option; shared merge helpers do not require a review verdict").option("--timeout <minutes>", `merge-queue wait per PR (default ${PR_LAND_QUEUE_TIMEOUT_MS / 6e4} minutes, one window per ${PR_LAND_QUEUE_BATCH_SIZE} PRs \u2014 per-repo ruleset check_response_timeout_minutes is 15 default with 30/45/60 overrides in .github/rulesets/mmi-development-merge-queue.json; agents never edit the ruleset to shorten a queue wait)`).option("--preserve-worktree", "after a queue merge keep the local worktree and branch (skips the post-merge cleanup)").option("--expect-head <sha>", "pin the PR head to this full 40-hex SHA (#8456): checked before landing, after checks and by GitHub at enqueue (--match-head-commit); never updates a BEHIND head and never retries an ejection \u2014 it stops with pinnedHead.refused instead. One PR only").action(async (numbers, opts) => {
64385
64682
  const timeoutMinutes = opts.timeout === void 0 ? void 0 : Number(opts.timeout);
64386
64683
  if (timeoutMinutes !== void 0 && !(timeoutMinutes > 0)) return fail("pr land: --timeout must be a positive number of minutes");
64387
64684
  if (opts.abandonAdmission) {
@@ -64722,7 +65019,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
64722
65019
  const remote = foreignCwd ? `https://github.com/${targetRepo2}.git` : "origin";
64723
65020
  let foreignCheckout;
64724
65021
  if (foreignCwd) {
64725
- const sibling = (0, import_node_path50.join)((0, import_node_path50.dirname)(beforeWorktrees[0]?.path || startingPath || process.cwd()), targetRepo2.split("/")[1]);
65022
+ const sibling = (0, import_node_path51.join)((0, import_node_path51.dirname)(beforeWorktrees[0]?.path || startingPath || process.cwd()), targetRepo2.split("/")[1]);
64726
65023
  const siblingRepo = repoFromRemoteUrl(await gitOut(["-C", sibling, "remote", "get-url", "origin"]).catch(() => ""));
64727
65024
  if (siblingRepo?.toLowerCase() === targetRepo2.toLowerCase()) {
64728
65025
  const siblingWorktrees = await execFileP("git", ["-C", sibling, "worktree", "list", "--porcelain"], { timeout: GIT_TIMEOUT_MS }).then((r) => parseGitWorktreePorcelain(r.stdout)).catch(() => void 0);
@@ -64956,7 +65253,7 @@ ${preparation.findings.map((finding) => `[${finding.check}/${finding.state}] ${f
64956
65253
  let residueSweep;
64957
65254
  if (!(foreignCwd && !foreignCheckout)) {
64958
65255
  try {
64959
- gitCommonDir = (0, import_node_path50.resolve)(primaryRoot, (await execFileP("git", ["-C", primaryRoot, "rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS })).stdout.trim());
65256
+ gitCommonDir = (0, import_node_path51.resolve)(primaryRoot, (await execFileP("git", ["-C", primaryRoot, "rev-parse", "--git-common-dir"], { timeout: GIT_TIMEOUT_MS })).stdout.trim());
64960
65257
  residueSweep = await sweepWorktreeResidue(gitCommonDir, beforeWorktrees.map((w) => w.path));
64961
65258
  } catch {
64962
65259
  }
@@ -65176,7 +65473,7 @@ function trainGitArgs(args) {
65176
65473
  async function announceSummaryLineCountRefusal(path2) {
65177
65474
  let content;
65178
65475
  try {
65179
- content = await (0, import_promises9.readFile)(path2, "utf8");
65476
+ content = await (0, import_promises10.readFile)(path2, "utf8");
65180
65477
  } catch (e) {
65181
65478
  return `release: could not read --announce-summary-file ${path2}: ${e.message} \u2014 see https://github.com/mutmutco/MMI-Hub/blob/development/docs/Guides/train-troubleshooting.md#announce-summary-missing`;
65182
65479
  }
@@ -65188,7 +65485,7 @@ async function announceSummaryLineCountRefusal(path2) {
65188
65485
  async function announceSummaryRefusal(path2, isHub = false, isWorkSuite = false) {
65189
65486
  let lines2;
65190
65487
  try {
65191
- lines2 = summaryFileLines(await (0, import_promises9.readFile)(path2, "utf8"));
65488
+ lines2 = summaryFileLines(await (0, import_promises10.readFile)(path2, "utf8"));
65192
65489
  } catch (e) {
65193
65490
  return `release: could not read --announce-summary-file ${path2}: ${e.message} \u2014 see https://github.com/mutmutco/MMI-Hub/blob/development/docs/Guides/train-troubleshooting.md#announce-summary-missing`;
65194
65491
  }
@@ -65326,8 +65623,8 @@ function trainApplyDeps() {
65326
65623
  }
65327
65624
  return body.status;
65328
65625
  },
65329
- readFile: (path2) => (0, import_promises9.readFile)(path2, "utf8"),
65330
- removeFile: (path2) => (0, import_promises9.unlink)(path2),
65626
+ readFile: (path2) => (0, import_promises10.readFile)(path2, "utf8"),
65627
+ removeFile: (path2) => (0, import_promises10.unlink)(path2),
65331
65628
  // #6521/#6523: the project's own release channel AND releaseLanguage, read from registry META in ONE
65332
65629
  // checked read. Same shape as fetchEdgeDomains below: a FAILED read refuses by name so the announce
65333
65630
  // note reports a failed read, never a project that configured nothing.
@@ -66255,7 +66552,7 @@ function rcRunCauseFromLog(log) {
66255
66552
  function memberAnnounceSummaryFile(repo, summaryFile, memberSummaryFiles) {
66256
66553
  return repo === SUITE_REPO ? summaryFile : memberSummaryFiles[repo];
66257
66554
  }
66258
- var suiteStateDir = (root) => (0, import_node_path52.join)(resolvePrimaryGitDir(root), "mmi-runtime", "suite");
66555
+ var suiteStateDir = (root) => (0, import_node_path53.join)(resolvePrimaryGitDir(root), "mmi-runtime", "suite");
66259
66556
  async function runSuiteMemberRcand(repo, previousRc, rcWasAbsent) {
66260
66557
  const deps = trainApplyDeps();
66261
66558
  deps.suiteCut = suiteCutContext(repo, SUITE_REPO);
@@ -66302,16 +66599,16 @@ async function rcandInCheckout(repo, deps, previousRc, rcWasAbsent) {
66302
66599
  }
66303
66600
  }
66304
66601
  function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr, memberSummaryFilesTr = {}) {
66305
- const recordPath = (0, import_node_path52.join)(suiteStateDir(root), "cut.json");
66306
- const base = (0, import_node_path52.join)(suiteStateDir(root), "checkouts");
66602
+ const recordPath = (0, import_node_path53.join)(suiteStateDir(root), "cut.json");
66603
+ const base = (0, import_node_path53.join)(suiteStateDir(root), "checkouts");
66307
66604
  const checkout = async (repo) => {
66308
66605
  if (repo === SUITE_REPO) return root;
66309
- const target = (0, import_node_path52.join)(base, repo.split("/")[1]);
66606
+ const target = (0, import_node_path53.join)(base, repo.split("/")[1]);
66310
66607
  if (!(0, import_node_fs55.existsSync)(target)) {
66311
66608
  (0, import_node_fs55.mkdirSync)(base, { recursive: true });
66312
66609
  await exec("git", ["clone", "--quiet", "--branch", "development", `https://github.com/${repo}.git`, target], root, WATCH_TIMEOUT);
66313
66610
  }
66314
- if (!(0, import_node_fs55.existsSync)((0, import_node_path52.join)(target, ".git"))) throw new Error(`${repo}: suite checkout has no .git at ${target}; stop`);
66611
+ if (!(0, import_node_fs55.existsSync)((0, import_node_path53.join)(target, ".git"))) throw new Error(`${repo}: suite checkout has no .git at ${target}; stop`);
66315
66612
  const origin = await exec("git", ["remote", "get-url", "origin"], target);
66316
66613
  if (!origin.toLowerCase().endsWith(`/${repo.toLowerCase()}.git`) && !origin.toLowerCase().endsWith(`/${repo.toLowerCase()}`))
66317
66614
  throw new Error(`${repo}: suite checkout origin mismatch at ${target}; stop`);
@@ -66375,7 +66672,7 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
66375
66672
  if (repo === SUITE_REPO) return inTrain(repo, (deps) => rcandInCheckout(repo, deps, previousRc, rcWasAbsent));
66376
66673
  const target = await checkout(repo);
66377
66674
  return new Promise((resolveResult, reject2) => {
66378
- const child2 = (0, import_node_child_process19.fork)((0, import_node_path51.resolve)(process.argv[1]), ["devops", "suite", "member-rcand-internal", repo, previousRc ?? "", ...rcWasAbsent ? ["--rc-was-absent"] : []], {
66675
+ const child2 = (0, import_node_child_process19.fork)((0, import_node_path52.resolve)(process.argv[1]), ["devops", "suite", "member-rcand-internal", repo, previousRc ?? "", ...rcWasAbsent ? ["--rc-was-absent"] : []], {
66379
66676
  cwd: target,
66380
66677
  stdio: ["ignore", "inherit", "inherit", "ipc"],
66381
66678
  execArgv: []
@@ -66619,7 +66916,7 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
66619
66916
  if (parseSuiteMapping(body)) return;
66620
66917
  const dir = suiteStateDir(root);
66621
66918
  (0, import_node_fs55.mkdirSync)(dir, { recursive: true });
66622
- const file = (0, import_node_path52.join)(dir, `notes-${leadTag}.md`);
66919
+ const file = (0, import_node_path53.join)(dir, `notes-${leadTag}.md`);
66623
66920
  (0, import_node_fs55.writeFileSync)(file, `${body}
66624
66921
 
66625
66922
  ${suiteMappingBlock(leadTag, mapping)}`);
@@ -66863,6 +67160,8 @@ function registerSuiteCommands(program3) {
66863
67160
  const status = suiteStatusDeps(github, registry2);
66864
67161
  const deps = {
66865
67162
  status: () => readSuiteStatus(status),
67163
+ // #10020: a single-track member's one live stage is `main`; judge that row instead of the dev one.
67164
+ releaseTrack: status.releaseTrack,
66866
67165
  commits: async (repo, limit) => (await github.rest("GET", `repos/${repo}/commits?sha=development&per_page=${limit}`) ?? []).map((c) => c.sha),
66867
67166
  checkRuns: async (repo, sha) => (await github.rest("GET", `repos/${repo}/commits/${sha}/check-runs?per_page=100`))?.check_runs ?? [],
66868
67167
  requiredChecks: async (repo) => {
@@ -66996,8 +67295,8 @@ function registerJevCommands(program3) {
66996
67295
  // src/packages-adopt-export.ts
66997
67296
  var import_node_child_process20 = require("node:child_process");
66998
67297
  var import_node_fs57 = require("node:fs");
66999
- var import_node_os25 = require("node:os");
67000
- var import_node_path53 = require("node:path");
67298
+ var import_node_os26 = require("node:os");
67299
+ var import_node_path54 = require("node:path");
67001
67300
  var import_node_util6 = require("node:util");
67002
67301
  init_cli_shared();
67003
67302
 
@@ -67617,25 +67916,25 @@ function expectedFromRun(run, opts) {
67617
67916
  }
67618
67917
  async function adoptExport(opts, deps) {
67619
67918
  if (!/^sha256:[0-9a-f]{64}$/.test(opts.digest)) throw new Error("--digest must be sha256:<64 hex>, from the export run summary");
67620
- const packagePath = (0, import_node_path53.join)(opts.dir, "package.json");
67621
- const lockPath = (0, import_node_path53.join)(opts.dir, "package-lock.json");
67919
+ const packagePath = (0, import_node_path54.join)(opts.dir, "package.json");
67920
+ const lockPath = (0, import_node_path54.join)(opts.dir, "package-lock.json");
67622
67921
  const packageText = (0, import_node_fs57.readFileSync)(packagePath, "utf8");
67623
67922
  const lockText = (0, import_node_fs57.readFileSync)(lockPath, "utf8");
67624
- const work = (0, import_node_fs57.mkdtempSync)((0, import_node_path53.join)((0, import_node_os25.tmpdir)(), "mmi-adopt-export-"));
67923
+ const work = (0, import_node_fs57.mkdtempSync)((0, import_node_path54.join)((0, import_node_os26.tmpdir)(), "mmi-adopt-export-"));
67625
67924
  try {
67626
67925
  const run = JSON.parse((await deps.ghApi(`repos/${EXPORT_REPO}/actions/runs/${opts.run}`)).toString("utf8"));
67627
- (0, import_node_fs57.writeFileSync)((0, import_node_path53.join)(work, "expected.json"), JSON.stringify(expectedFromRun(run, opts)));
67628
- (0, import_node_fs57.writeFileSync)((0, import_node_path53.join)(work, "provider.json"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}`));
67629
- (0, import_node_fs57.writeFileSync)((0, import_node_path53.join)(work, "artifact.zip"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}/zip`));
67630
- const script = (0, import_node_path53.join)(work, "package_export.py");
67926
+ (0, import_node_fs57.writeFileSync)((0, import_node_path54.join)(work, "expected.json"), JSON.stringify(expectedFromRun(run, opts)));
67927
+ (0, import_node_fs57.writeFileSync)((0, import_node_path54.join)(work, "provider.json"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}`));
67928
+ (0, import_node_fs57.writeFileSync)((0, import_node_path54.join)(work, "artifact.zip"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}/zip`));
67929
+ const script = (0, import_node_path54.join)(work, "package_export.py");
67631
67930
  (0, import_node_fs57.writeFileSync)(script, package_export_default);
67632
67931
  await deps.python(script, ["verify", work, opts.ds, opts.screens]);
67633
- const verified = (0, import_node_path53.join)(work, "verified");
67634
- const metadata = Object.fromEntries(PACKAGES.map(({ name, short: short3 }) => [name, JSON.parse((0, import_node_fs57.readFileSync)((0, import_node_path53.join)(verified, `${short3}.metadata.json`), "utf8"))]));
67932
+ const verified = (0, import_node_path54.join)(work, "verified");
67933
+ const metadata = Object.fromEntries(PACKAGES.map(({ name, short: short3 }) => [name, JSON.parse((0, import_node_fs57.readFileSync)((0, import_node_path54.join)(verified, `${short3}.metadata.json`), "utf8"))]));
67635
67934
  const plan = planAdoption(packageText, lockText, metadata);
67636
67935
  for (const { name } of plan.result.adopted) {
67637
67936
  const short3 = PACKAGES.find((p) => p.name === name).short;
67638
- await deps.npm(["cache", "add", (0, import_node_path53.join)(verified, `${short3}.tgz`)], opts.dir);
67937
+ await deps.npm(["cache", "add", (0, import_node_path54.join)(verified, `${short3}.tgz`)], opts.dir);
67639
67938
  }
67640
67939
  (0, import_node_fs57.writeFileSync)(packagePath, plan.packageText);
67641
67940
  (0, import_node_fs57.writeFileSync)(lockPath, plan.lockText);
@@ -67825,8 +68124,8 @@ async function collectWaveStatus(deps) {
67825
68124
  init_github_client();
67826
68125
  init_cli_shared();
67827
68126
  var import_node_fs59 = require("node:fs");
67828
- var import_node_os26 = require("node:os");
67829
- var import_node_path54 = require("node:path");
68127
+ var import_node_os27 = require("node:os");
68128
+ var import_node_path55 = require("node:path");
67830
68129
  var GC_GH_TIMEOUT_MS3 = 2e4;
67831
68130
  var defaultStatusDeps = {
67832
68131
  repo: resolveRepo,
@@ -68045,10 +68344,10 @@ async function collectOnboardStatus(opts = {}) {
68045
68344
  else if (top) nextCommand = `mmi-cli oracle board claim ${top.number} # ${top.title}`;
68046
68345
  else nextCommand = "mmi-cli oracle board read \u2014 no claimable items found";
68047
68346
  }
68048
- const home = (0, import_node_os26.homedir)();
68347
+ const home = (0, import_node_os27.homedir)();
68049
68348
  const plugin = onboardPluginGate({
68050
- readKnown: () => readFileSyncSafe((0, import_node_path54.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs59.readFileSync),
68051
- readSettings: () => readFileSyncSafe((0, import_node_path54.join)(home, ".claude", "settings.json"), import_node_fs59.readFileSync)
68349
+ readKnown: () => readFileSyncSafe((0, import_node_path55.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs59.readFileSync),
68350
+ readSettings: () => readFileSyncSafe((0, import_node_path55.join)(home, ".claude", "settings.json"), import_node_fs59.readFileSync)
68052
68351
  });
68053
68352
  return {
68054
68353
  guide: "Read the MMI org-guide skill before project work; load only the relevant topic. Use mmi-help for a short introduction.",
@@ -70685,7 +70984,7 @@ function registerConnectionsOauth(program3, deps) {
70685
70984
  }
70686
70985
 
70687
70986
  // src/schedules-commands.ts
70688
- var import_promises10 = require("node:fs/promises");
70987
+ var import_promises11 = require("node:fs/promises");
70689
70988
  init_clean_exit();
70690
70989
  init_github_client();
70691
70990
  init_cli_shared();
@@ -70865,9 +71164,9 @@ function registerSchedulesCommands(program3) {
70865
71164
  await failGraceful("org schedules --doc: refusing to regenerate the doc from an incomplete read (see warnings above).");
70866
71165
  return;
70867
71166
  }
70868
- const docText = await (0, import_promises10.readFile)(o.doc, "utf8");
71167
+ const docText = await (0, import_promises11.readFile)(o.doc, "utf8");
70869
71168
  const spliced = spliceDoc(docText, renderDocSection(entries, now.toISOString()));
70870
- await (0, import_promises10.writeFile)(o.doc, spliced, "utf8");
71169
+ await (0, import_promises11.writeFile)(o.doc, spliced, "utf8");
70871
71170
  reportParked(parkedLines);
70872
71171
  reportDrift(drift);
70873
71172
  console.log(`org schedules: wrote ${entries.length} entries into ${o.doc}`);
@@ -71180,7 +71479,7 @@ function registerSessionReport(program3) {
71180
71479
 
71181
71480
  // src/stage-commands.ts
71182
71481
  var import_node_fs60 = require("node:fs");
71183
- var import_node_path55 = require("node:path");
71482
+ var import_node_path56 = require("node:path");
71184
71483
  init_cli_shared();
71185
71484
  init_clean_exit();
71186
71485
 
@@ -71428,8 +71727,8 @@ function registerStageCommands(program3) {
71428
71727
  const repo = Array.isArray(project2?.repos) && typeof project2.repos[0] === "string" ? project2.repos[0] : slug;
71429
71728
  const resolution = decideStage({
71430
71729
  registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
71431
- hasCompose: (0, import_node_fs60.existsSync)((0, import_node_path55.join)(process.cwd(), "docker-compose.yml")),
71432
- hasEnvExample: (0, import_node_fs60.existsSync)((0, import_node_path55.join)(process.cwd(), ".env.example")),
71730
+ hasCompose: (0, import_node_fs60.existsSync)((0, import_node_path56.join)(process.cwd(), "docker-compose.yml")),
71731
+ hasEnvExample: (0, import_node_fs60.existsSync)((0, import_node_path56.join)(process.cwd(), ".env.example")),
71433
71732
  repo
71434
71733
  });
71435
71734
  if (resolution.source === "derived" && resolution.derived) {
@@ -71721,21 +72020,21 @@ vercel preview (${preview.branch}): ${d.state}${d.url ? ` \u2014 ${d.url}` : ""}
71721
72020
  }
71722
72021
 
71723
72022
  // src/tenant-artifact.ts
71724
- var import_node_crypto23 = require("node:crypto");
72023
+ var import_node_crypto24 = require("node:crypto");
71725
72024
  var import_node_fs61 = require("node:fs");
71726
- var import_promises11 = require("node:fs/promises");
71727
- var import_node_path56 = require("node:path");
72025
+ var import_promises12 = require("node:fs/promises");
72026
+ var import_node_path57 = require("node:path");
71728
72027
  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}$/;
71729
72028
  var MAX_BYTES2 = 5 * 1024 * 1024 * 1024;
71730
72029
  async function sha256File(path2) {
71731
- const hash = (0, import_node_crypto23.createHash)("sha256");
72030
+ const hash = (0, import_node_crypto24.createHash)("sha256");
71732
72031
  for await (const chunk of (0, import_node_fs61.createReadStream)(path2)) hash.update(chunk);
71733
72032
  return hash.digest("hex");
71734
72033
  }
71735
72034
  async function putTenantArtifact(repo, stage, inputPath, deps) {
71736
72035
  if (!["dev", "rc", "main"].includes(stage)) throw new Error("tenant artifact put: <stage> must be dev, rc, or main");
71737
- const path2 = (0, import_node_path56.resolve)(inputPath);
71738
- const info = await (0, import_promises11.stat)(path2);
72036
+ const path2 = (0, import_node_path57.resolve)(inputPath);
72037
+ const info = await (0, import_promises12.stat)(path2);
71739
72038
  if (!info.isFile()) throw new Error("tenant artifact put: input path must be a file");
71740
72039
  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`);
71741
72040
  const sha2564 = await sha256File(path2);
@@ -72214,12 +72513,12 @@ async function resolveWhoami(deps) {
72214
72513
  }
72215
72514
 
72216
72515
  // src/command-register-developer.ts
72217
- var import_node_path63 = require("node:path");
72516
+ var import_node_path64 = require("node:path");
72218
72517
 
72219
72518
  // src/doc-refs-command-source.ts
72220
72519
  var import_node_child_process21 = require("node:child_process");
72221
72520
  var import_node_fs62 = require("node:fs");
72222
- var import_node_path57 = require("node:path");
72521
+ var import_node_path58 = require("node:path");
72223
72522
  var IN_PROCESS_SOURCE = "in-process";
72224
72523
  var BUNDLE_MANIFEST_TIMEOUT_MS = 12e4;
72225
72524
  var BUNDLE_MANIFEST_MAX_BUFFER = 64 * 1024 * 1024;
@@ -72241,7 +72540,7 @@ function inventoryFromManifest(manifest, source) {
72241
72540
  function readBundleManifest(bundle, exec3 = import_node_child_process21.execFileSync) {
72242
72541
  try {
72243
72542
  const text = exec3(process.execPath, [bundle, "commands", "--all", "--json"], {
72244
- cwd: (0, import_node_path57.dirname)(bundle),
72543
+ cwd: (0, import_node_path58.dirname)(bundle),
72245
72544
  encoding: "utf8",
72246
72545
  windowsHide: true,
72247
72546
  timeout: BUNDLE_MANIFEST_TIMEOUT_MS,
@@ -72257,16 +72556,16 @@ function readBundleManifest(bundle, exec3 = import_node_child_process21.execFile
72257
72556
  function isRunningBundle(bundle, argv1 = process.argv[1], platform2 = process.platform) {
72258
72557
  if (!argv1) return false;
72259
72558
  const norm3 = (path2) => {
72260
- const absolute = (0, import_node_path57.resolve)(path2);
72559
+ const absolute = (0, import_node_path58.resolve)(path2);
72261
72560
  return platform2 === "win32" ? absolute.toLowerCase() : absolute;
72262
72561
  };
72263
72562
  const running = norm3(argv1);
72264
- if (!["main.cjs", "index.cjs"].includes((0, import_node_path57.basename)(running))) return false;
72265
- return (0, import_node_path57.dirname)(running) === (0, import_node_path57.dirname)(norm3(bundle));
72563
+ if (!["main.cjs", "index.cjs"].includes((0, import_node_path58.basename)(running))) return false;
72564
+ return (0, import_node_path58.dirname)(running) === (0, import_node_path58.dirname)(norm3(bundle));
72266
72565
  }
72267
72566
  function nameUnknownCommandSources(findings, source, root) {
72268
- const checkoutBundle = (0, import_node_path57.resolve)(root, "cli", "dist", "main.cjs");
72269
- const recovery = source !== IN_PROCESS_SOURCE && (0, import_node_path57.resolve)(source) === checkoutBundle ? "; rebuild with `npm --prefix cli run build` and retry, or pass `--commands-from <bundle>`" : "";
72567
+ const checkoutBundle = (0, import_node_path58.resolve)(root, "cli", "dist", "main.cjs");
72568
+ const recovery = source !== IN_PROCESS_SOURCE && (0, import_node_path58.resolve)(source) === checkoutBundle ? "; rebuild with `npm --prefix cli run build` and retry, or pass `--commands-from <bundle>`" : "";
72270
72569
  for (const finding of findings) {
72271
72570
  if (finding.kind !== "unknown-command") continue;
72272
72571
  finding.commandSource = source;
@@ -72278,7 +72577,7 @@ function resolveCommandInventory(deps) {
72278
72577
  const read = deps.read ?? ((bundle) => readBundleManifest(bundle));
72279
72578
  const warnings = [];
72280
72579
  if (deps.from) {
72281
- const bundle = (0, import_node_path57.isAbsolute)(deps.from) ? deps.from : (0, import_node_path57.resolve)(deps.root, deps.from);
72580
+ const bundle = (0, import_node_path58.isAbsolute)(deps.from) ? deps.from : (0, import_node_path58.resolve)(deps.root, deps.from);
72282
72581
  if (!exists(bundle)) {
72283
72582
  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)`);
72284
72583
  }
@@ -72288,7 +72587,7 @@ function resolveCommandInventory(deps) {
72288
72587
  }
72289
72588
  return { inventory: inventory2, warnings };
72290
72589
  }
72291
- const treeBundle = (0, import_node_path57.join)(deps.root, "cli", "dist", "main.cjs");
72590
+ const treeBundle = (0, import_node_path58.join)(deps.root, "cli", "dist", "main.cjs");
72292
72591
  if (!exists(treeBundle)) {
72293
72592
  return { inventory: deps.inProcess(), warnings };
72294
72593
  }
@@ -72408,26 +72707,26 @@ async function executeWaveLand(plan, deps) {
72408
72707
 
72409
72708
  // src/dist-drift.ts
72410
72709
  var import_node_child_process22 = require("node:child_process");
72411
- var import_node_crypto25 = require("node:crypto");
72710
+ var import_node_crypto26 = require("node:crypto");
72412
72711
  var import_node_fs64 = require("node:fs");
72413
- var import_node_os27 = require("node:os");
72414
- var import_node_path59 = require("node:path");
72712
+ var import_node_os28 = require("node:os");
72713
+ var import_node_path60 = require("node:path");
72415
72714
 
72416
72715
  // ../scripts/distribution-digest.mjs
72417
- var import_node_crypto24 = require("node:crypto");
72716
+ var import_node_crypto25 = require("node:crypto");
72418
72717
  var import_node_fs63 = require("node:fs");
72419
- var import_node_path58 = require("node:path");
72718
+ var import_node_path59 = require("node:path");
72420
72719
  var slash = (value) => value.replaceAll("\\", "/");
72421
72720
  function repoPath(root, declaredPath, label) {
72422
- const absoluteRoot = (0, import_node_path58.resolve)(root);
72423
- const target = (0, import_node_path58.resolve)(root, declaredPath);
72424
- if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path58.sep}`)) {
72721
+ const absoluteRoot = (0, import_node_path59.resolve)(root);
72722
+ const target = (0, import_node_path59.resolve)(root, declaredPath);
72723
+ if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path59.sep}`)) {
72425
72724
  throw new Error(`${label} ${declaredPath} escapes the repository root`);
72426
72725
  }
72427
72726
  return target;
72428
72727
  }
72429
72728
  function digestFiles(files) {
72430
- const hash = (0, import_node_crypto24.createHash)("sha256");
72729
+ const hash = (0, import_node_crypto25.createHash)("sha256");
72431
72730
  for (const file of [...files].sort((a, b) => a.relative.localeCompare(b.relative))) {
72432
72731
  const content = file.stat.isSymbolicLink() ? Buffer.from((0, import_node_fs63.readlinkSync)(file.absolute), "utf8") : (0, import_node_fs63.readFileSync)(file.absolute);
72433
72732
  hash.update(file.relative, "utf8");
@@ -72458,7 +72757,7 @@ var DIST_ARTIFACTS = [
72458
72757
  ];
72459
72758
  var BOM_DIST_TREE_ID = "mmi-cli-dist";
72460
72759
  var ABSENT = "absent";
72461
- var sha2563 = (bytes) => `sha256:${(0, import_node_crypto25.createHash)("sha256").update(bytes).digest("hex")}`;
72760
+ var sha2563 = (bytes) => `sha256:${(0, import_node_crypto26.createHash)("sha256").update(bytes).digest("hex")}`;
72462
72761
  function artifactDrift(path2, committedBytes, rebuiltBytes) {
72463
72762
  const committed = committedBytes ? sha2563(committedBytes) : ABSENT;
72464
72763
  const rebuiltExpected = rebuiltBytes ? sha2563(rebuiltBytes) : ABSENT;
@@ -72536,7 +72835,7 @@ function walkFiles(root) {
72536
72835
  const files = [];
72537
72836
  const walk2 = (directory) => {
72538
72837
  for (const entry of (0, import_node_fs64.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
72539
- const child2 = (0, import_node_path59.join)(directory, entry.name);
72838
+ const child2 = (0, import_node_path60.join)(directory, entry.name);
72540
72839
  if (entry.isDirectory()) walk2(child2);
72541
72840
  else files.push(child2);
72542
72841
  }
@@ -72546,10 +72845,10 @@ function walkFiles(root) {
72546
72845
  }
72547
72846
  function bomPathFor(root) {
72548
72847
  try {
72549
- const registry2 = JSON.parse((0, import_node_fs64.readFileSync)((0, import_node_path59.join)(root, "surfaces.json"), "utf8"));
72550
- return (0, import_node_path59.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
72848
+ const registry2 = JSON.parse((0, import_node_fs64.readFileSync)((0, import_node_path60.join)(root, "surfaces.json"), "utf8"));
72849
+ return (0, import_node_path60.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
72551
72850
  } catch {
72552
- return (0, import_node_path59.join)(root, "distribution-bom.json");
72851
+ return (0, import_node_path60.join)(root, "distribution-bom.json");
72553
72852
  }
72554
72853
  }
72555
72854
  function rebuildTo(packageRoot, outDir) {
@@ -72562,28 +72861,28 @@ function rebuildTo(packageRoot, outDir) {
72562
72861
  });
72563
72862
  }
72564
72863
  function runDistStatus(root) {
72565
- const stage = (0, import_node_fs64.mkdtempSync)((0, import_node_path59.join)((0, import_node_os27.tmpdir)(), "mmi-dist-drift-"));
72864
+ const stage = (0, import_node_fs64.mkdtempSync)((0, import_node_path60.join)((0, import_node_os28.tmpdir)(), "mmi-dist-drift-"));
72566
72865
  let overlayCount = 0;
72567
72866
  try {
72568
- const cliOut = (0, import_node_path59.join)(stage, "cli-dist");
72569
- const hubOut = (0, import_node_path59.join)(stage, "hub-dist");
72570
- rebuildTo((0, import_node_path59.join)(root, "cli"), cliOut);
72571
- rebuildTo((0, import_node_path59.join)(root, "updater"), hubOut);
72867
+ const cliOut = (0, import_node_path60.join)(stage, "cli-dist");
72868
+ const hubOut = (0, import_node_path60.join)(stage, "hub-dist");
72869
+ rebuildTo((0, import_node_path60.join)(root, "cli"), cliOut);
72870
+ rebuildTo((0, import_node_path60.join)(root, "updater"), hubOut);
72572
72871
  const outDirFor = (packageDir) => packageDir === "cli" ? cliOut : hubOut;
72573
72872
  const rebuilt = (path2) => {
72574
72873
  const spec = DIST_ARTIFACTS.find((entry) => entry.path === path2);
72575
- return spec ? readOrNull((0, import_node_path59.join)(outDirFor(spec.packageDir), spec.output)) : null;
72874
+ return spec ? readOrNull((0, import_node_path60.join)(outDirFor(spec.packageDir), spec.output)) : null;
72576
72875
  };
72577
- const committed = (path2) => readOrNull((0, import_node_path59.join)(root, path2));
72578
- const tree = (path2) => readOrNull((0, import_node_path59.join)(root, path2));
72579
- const distRoot = (0, import_node_path59.join)(root, "cli", "dist");
72580
- const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path59.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
72876
+ const committed = (path2) => readOrNull((0, import_node_path60.join)(root, path2));
72877
+ const tree = (path2) => readOrNull((0, import_node_path60.join)(root, path2));
72878
+ const distRoot = (0, import_node_path60.join)(root, "cli", "dist");
72879
+ const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path60.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
72581
72880
  const bom = JSON.parse((0, import_node_fs64.readFileSync)(bomPathFor(root), "utf8"));
72582
72881
  const digest = (entries) => {
72583
- const overlay = (0, import_node_path59.join)(stage, `overlay-${overlayCount++}`);
72882
+ const overlay = (0, import_node_path60.join)(stage, `overlay-${overlayCount++}`);
72584
72883
  for (const entry of entries) {
72585
- const target = (0, import_node_path59.join)(overlay, entry.path);
72586
- (0, import_node_fs64.mkdirSync)((0, import_node_path59.dirname)(target), { recursive: true });
72884
+ const target = (0, import_node_path60.join)(overlay, entry.path);
72885
+ (0, import_node_fs64.mkdirSync)((0, import_node_path60.dirname)(target), { recursive: true });
72587
72886
  (0, import_node_fs64.writeFileSync)(target, entry.bytes);
72588
72887
  }
72589
72888
  return digestPackedFiles(overlay, entries.map((entry) => entry.path));
@@ -72595,7 +72894,7 @@ function runDistStatus(root) {
72595
72894
  }
72596
72895
 
72597
72896
  // src/doc-refs-live.ts
72598
- var import_node_path60 = require("node:path");
72897
+ var import_node_path61 = require("node:path");
72599
72898
  var concrete = (value) => !!value && !/[<>{}$]/.test(value);
72600
72899
  var liveOption = (tokens2, flag) => {
72601
72900
  const index = tokens2.indexOf(flag);
@@ -72668,7 +72967,7 @@ async function runLiveDocRefs(root, deps, live) {
72668
72967
  const offline = runDocRefs(root, { ...deps, runbooksOnly: true });
72669
72968
  const read = deps.readFile ?? readFileOrNull;
72670
72969
  const docs = Object.fromEntries((deps.listDocs ?? defaultListDocs)(root).flatMap((doc) => {
72671
- const body = read((0, import_node_path60.join)(root, doc));
72970
+ const body = read((0, import_node_path61.join)(root, doc));
72672
72971
  return body !== null && isRunbook(doc, body) && !deps.isIgnored?.([doc]).has(doc) ? [[doc, body]] : [];
72673
72972
  }));
72674
72973
  const remote = await checkRunbookLive(docs, live);
@@ -72717,13 +73016,13 @@ init_cli_shared();
72717
73016
  // src/edge-tunnel.ts
72718
73017
  var HOSTNAME_RE = /^(?=.{1,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)(\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+$/;
72719
73018
  var UPSTREAM_RE = /^https?:\/\/[^/\s]+(?::\d+)?(?:\/.*)?$/;
72720
- function tunnelNameFromHostname(hostname4) {
72721
- return hostname4.replace(/\./g, "-").slice(0, 63);
73019
+ function tunnelNameFromHostname(hostname5) {
73020
+ return hostname5.replace(/\./g, "-").slice(0, 63);
72722
73021
  }
72723
- function planInfraTunnel(hostname4, upstream) {
72724
- const host = hostname4.trim().toLowerCase();
73022
+ function planInfraTunnel(hostname5, upstream) {
73023
+ const host = hostname5.trim().toLowerCase();
72725
73024
  const origin = upstream.trim();
72726
- if (!HOSTNAME_RE.test(host)) throw new Error(`invalid hostname ${JSON.stringify(hostname4)}`);
73025
+ if (!HOSTNAME_RE.test(host)) throw new Error(`invalid hostname ${JSON.stringify(hostname5)}`);
72727
73026
  if (!UPSTREAM_RE.test(origin)) throw new Error(`invalid upstream ${JSON.stringify(upstream)} \u2014 expected http(s)://host:port`);
72728
73027
  const tunnelName = tunnelNameFromHostname(host);
72729
73028
  const configYaml = [
@@ -72878,8 +73177,8 @@ async function diagnoseHub(deps) {
72878
73177
  }
72879
73178
 
72880
73179
  // src/schedules-lift-command.ts
72881
- var import_promises12 = require("node:fs/promises");
72882
- var import_node_path61 = require("node:path");
73180
+ var import_promises13 = require("node:fs/promises");
73181
+ var import_node_path62 = require("node:path");
72883
73182
  init_clean_exit();
72884
73183
  init_cli_shared();
72885
73184
  var DEFAULT_WORKFLOWS_DIR = ".github/workflows";
@@ -72901,14 +73200,14 @@ var RegistryUnreachableError = class extends Error {
72901
73200
  async function readWorkflowFiles(dir) {
72902
73201
  let names;
72903
73202
  try {
72904
- names = await (0, import_promises12.readdir)(dir);
73203
+ names = await (0, import_promises13.readdir)(dir);
72905
73204
  } catch {
72906
73205
  return [];
72907
73206
  }
72908
73207
  const files = [];
72909
73208
  for (const name of names.sort()) {
72910
73209
  if (!/\.ya?ml$/.test(name)) continue;
72911
- files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises12.readFile)((0, import_node_path61.join)(dir, name), "utf8") });
73210
+ files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises13.readFile)((0, import_node_path62.join)(dir, name), "utf8") });
72912
73211
  }
72913
73212
  return files;
72914
73213
  }
@@ -72992,7 +73291,7 @@ function registerSchedulesLiftCommand(program3, deps = {}) {
72992
73291
  // src/spawn-policy-core.ts
72993
73292
  var import_node_child_process24 = require("node:child_process");
72994
73293
  var import_node_fs65 = require("node:fs");
72995
- var import_node_path62 = require("node:path");
73294
+ var import_node_path63 = require("node:path");
72996
73295
  var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
72997
73296
  var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
72998
73297
  var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
@@ -73078,7 +73377,7 @@ function runSpawnPolicy(root) {
73078
73377
  for (const file of files) {
73079
73378
  let raw;
73080
73379
  try {
73081
- raw = (0, import_node_fs65.readFileSync)((0, import_node_path62.join)(root, file), "utf8");
73380
+ raw = (0, import_node_fs65.readFileSync)((0, import_node_path63.join)(root, file), "utf8");
73082
73381
  } catch {
73083
73382
  continue;
73084
73383
  }
@@ -73098,7 +73397,7 @@ function runSpawnPolicy(root) {
73098
73397
  function registerDeveloperCommands(program3) {
73099
73398
  const rules = program3.command("rules").description("org-managed .gitignore delivery");
73100
73399
  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) => {
73101
- const path2 = (0, import_node_path63.join)(process.cwd(), ".gitignore");
73400
+ const path2 = (0, import_node_path64.join)(process.cwd(), ".gitignore");
73102
73401
  const current = (0, import_node_fs66.existsSync)(path2) ? (0, import_node_fs66.readFileSync)(path2, "utf8") : null;
73103
73402
  const plan = planManagedGitignore(current);
73104
73403
  const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
@@ -73428,12 +73727,12 @@ ${r.stderr ?? ""}`).catch(() => "");
73428
73727
  var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
73429
73728
  var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
73430
73729
  function envHealLockPath(home) {
73431
- return (0, import_node_path64.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
73730
+ return (0, import_node_path65.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
73432
73731
  }
73433
73732
  async function withEnvHealLock(what, run) {
73434
73733
  try {
73435
73734
  return await withFileLock(
73436
- envHealLockPath((0, import_node_os28.homedir)()),
73735
+ envHealLockPath((0, import_node_os29.homedir)()),
73437
73736
  { staleMs: ENV_HEAL_LOCK_STALE_MS, maxWaitMs: ENV_HEAL_LOCK_MAX_WAIT_MS, label: "mmi env-heal lock" },
73438
73737
  run
73439
73738
  );
@@ -73531,7 +73830,7 @@ function mmiDoctorDeps(opts = {}) {
73531
73830
  const configRoot = surfaceConfigRoot(surface);
73532
73831
  const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
73533
73832
  const plan = buildPluginCachePlan(
73534
- (0, import_node_os28.homedir)(),
73833
+ (0, import_node_os29.homedir)(),
73535
73834
  running,
73536
73835
  pluginCacheFsDeps(configRoot, () => 0),
73537
73836
  { configRoot, includeStaging: surface !== "codex" }
@@ -73559,7 +73858,7 @@ function mmiDoctorDeps(opts = {}) {
73559
73858
  const running = surface === "codex" ? installedActivePluginVersion(surface) : runningPluginVersion(process.env, resolveClientVersion());
73560
73859
  const installed = installedActivePluginVersion(surface);
73561
73860
  const plan = buildPluginCachePlan(
73562
- (0, import_node_os28.homedir)(),
73861
+ (0, import_node_os29.homedir)(),
73563
73862
  running,
73564
73863
  pluginCacheFsDeps(configRoot, () => 0),
73565
73864
  { configRoot, includeStaging: surface !== "codex", installedVersion: installed }
@@ -73601,7 +73900,7 @@ function mmiDoctorDeps(opts = {}) {
73601
73900
  // has no generated routing index would
73602
73901
  // get a permanent — demanding an artifact it never asked for.
73603
73902
  docsIndexState: (root) => {
73604
- if (!(0, import_node_fs67.existsSync)((0, import_node_path64.join)(root, DOCS_INDEX_PATH))) return void 0;
73903
+ if (!(0, import_node_fs67.existsSync)((0, import_node_path65.join)(root, DOCS_INDEX_PATH))) return void 0;
73605
73904
  const real = createDocsIndexDeps(root);
73606
73905
  let docs;
73607
73906
  const listDocs = () => docs ??= real.listDocs();
@@ -73610,7 +73909,7 @@ function mmiDoctorDeps(opts = {}) {
73610
73909
  },
73611
73910
  // #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
73612
73911
  healDocsIndex: (root) => {
73613
- if (!(0, import_node_fs67.existsSync)((0, import_node_path64.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
73912
+ if (!(0, import_node_fs67.existsSync)((0, import_node_path65.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
73614
73913
  const real = createDocsIndexDeps(root);
73615
73914
  let docs;
73616
73915
  const listDocs = () => docs ??= real.listDocs();
@@ -74841,16 +75140,16 @@ function ciAuditDeps2() {
74841
75140
  // gate re-seed step is skipped gracefully rather than failing mid-run.
74842
75141
  readSeedFile: (path2) => {
74843
75142
  if (!root) return null;
74844
- const fullPath = (0, import_node_path64.join)(root, path2);
75143
+ const fullPath = (0, import_node_path65.join)(root, path2);
74845
75144
  return (0, import_node_fs67.existsSync)(fullPath) ? (0, import_node_fs67.readFileSync)(fullPath, "utf8") : null;
74846
75145
  }
74847
75146
  };
74848
75147
  }
74849
75148
  function hubRoot2() {
74850
- const fromPkg = (0, import_node_path64.join)(__dirname, "..", "..");
75149
+ const fromPkg = (0, import_node_path65.join)(__dirname, "..", "..");
74851
75150
  const marker = "skills/bootstrap/seeds/manifest.json";
74852
- if ((0, import_node_fs67.existsSync)((0, import_node_path64.join)(fromPkg, marker))) return fromPkg;
74853
- if ((0, import_node_fs67.existsSync)((0, import_node_path64.join)(process.cwd(), marker))) return process.cwd();
75151
+ if ((0, import_node_fs67.existsSync)((0, import_node_path65.join)(fromPkg, marker))) return fromPkg;
75152
+ if ((0, import_node_fs67.existsSync)((0, import_node_path65.join)(process.cwd(), marker))) return process.cwd();
74854
75153
  return null;
74855
75154
  }
74856
75155
  registerQueryCommands(program2);
@@ -75064,7 +75363,7 @@ function directoryBytes(path2) {
75064
75363
  return 0;
75065
75364
  }
75066
75365
  for (const entry of entries) {
75067
- const child2 = (0, import_node_path64.join)(path2, entry.name);
75366
+ const child2 = (0, import_node_path65.join)(path2, entry.name);
75068
75367
  if (entry.isDirectory()) total += directoryBytes(child2);
75069
75368
  else {
75070
75369
  try {
@@ -75094,7 +75393,7 @@ function pluginCacheFsDeps(configRoot, dirBytes) {
75094
75393
  dirBytes,
75095
75394
  listStagingDirs: (root) => (0, import_node_fs67.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
75096
75395
  try {
75097
- return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path64.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs67.statSync)(p).mtimeMs) };
75396
+ return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path65.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs67.statSync)(p).mtimeMs) };
75098
75397
  } catch {
75099
75398
  return { name: d.name, mtimeMs: Date.now() };
75100
75399
  }
@@ -75108,7 +75407,7 @@ function stagingApplyFsGuard(configRoot) {
75108
75407
  return {
75109
75408
  referencedPaths: () => readInstalledPluginRefs(configRoot),
75110
75409
  mtimeMs: (name) => {
75111
- const p = (0, import_node_path64.join)(stagingRoot, name);
75410
+ const p = (0, import_node_path65.join)(stagingRoot, name);
75112
75411
  if (!(0, import_node_fs67.existsSync)(p)) return null;
75113
75412
  try {
75114
75413
  return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs67.statSync)(q).mtimeMs);
@@ -75131,7 +75430,7 @@ program2.command("plugin-prune").description(`prune stale cached MMI plugin vers
75131
75430
  return;
75132
75431
  }
75133
75432
  const plan = buildPluginCachePlan(
75134
- (0, import_node_os28.homedir)(),
75433
+ (0, import_node_os29.homedir)(),
75135
75434
  running,
75136
75435
  pluginCacheFsDeps(configRoot, directoryBytes),
75137
75436
  { withBytes: true, configRoot, includeStaging: surface !== "codex" }
@@ -75147,7 +75446,7 @@ program2.command("archive-prune").description("inventory worktree evidence archi
75147
75446
  let primary = o.repoRoot;
75148
75447
  if (!primary) {
75149
75448
  const common = await gitOut(["rev-parse", "--path-format=absolute", "--git-common-dir"]);
75150
- primary = common ? (0, import_node_path64.dirname)(common) : "";
75449
+ primary = common ? (0, import_node_path65.dirname)(common) : "";
75151
75450
  }
75152
75451
  if (!primary) {
75153
75452
  const detail = "archive-prune: not inside a git checkout (pass --repo-root <primary checkout>)";