@mutmutco/claude-plugin 4.6.37 → 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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mmi",
3
3
  "displayName": "MMI",
4
- "version": "4.6.37",
4
+ "version": "4.6.38",
5
5
  "mmiCompat": "4.x",
6
6
  "description": "MMI workflow skills and org gates delivery.",
7
7
  "author": {
package/cli/dist/main.cjs CHANGED
@@ -15691,6 +15691,9 @@ var APP_SUBPROCESS_ROUTED_COMMANDS = /* @__PURE__ */ new Set([
15691
15691
  // #9871: queue enqueue + auto-merge fallback ride ghFilingAppEnv (#9490/#9528; receipt credential app_installation).
15692
15692
  "pr land"
15693
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
+ }
15694
15697
  async function activateAppActor(commandPath4, env, mint, note = (line) => process.stderr.write(`${line}
15695
15698
  `)) {
15696
15699
  activeQuotaScope = void 0;
@@ -15703,7 +15706,7 @@ async function activateAppActor(commandPath4, env, mint, note = (line) => proces
15703
15706
  if (!APP_ELIGIBLE_COMMANDS.has(commandPath4) && !APP_WRITE_COMMANDS.has(commandPath4)) {
15704
15707
  if (requested === "app" && !notedIneligibleCommand && !APP_SUBPROCESS_ROUTED_COMMANDS.has(commandPath4)) {
15705
15708
  notedIneligibleCommand = true;
15706
- 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));
15707
15710
  }
15708
15711
  return "personal";
15709
15712
  }
@@ -22767,23 +22770,8 @@ var BOARD_WRITE_REFUSAL_TYPES = /* @__PURE__ */ new Set(["INSUFFICIENT_SCOPES",
22767
22770
  async function updateItemSingleSelect(client, projectId, itemId, fieldId, optionId) {
22768
22771
  if (process.env.MMI_ACTOR?.trim() === "app") {
22769
22772
  const { writeHubBoardField: writeHubBoardField2 } = await Promise.resolve().then(() => (init_board_write(), board_write_exports));
22770
- try {
22771
- await writeHubBoardField2({ projectId, itemId, fieldId, optionId });
22772
- return { credential: "app_installation" };
22773
- } catch (error) {
22774
- const refusal = error;
22775
- if (refusal?.rateLimited !== true || refusal.status !== 503 && refusal.status !== 429) throw error;
22776
- try {
22777
- const identity = await githubKeyringTokenRead();
22778
- if (identity.state !== "ok") throw error;
22779
- const owner = createGitHubClient({ token: async () => identity.token });
22780
- await owner.graphql(UPDATE_ITEM_FIELD_MUTATION, { projectId, itemId, fieldId, optionId });
22781
- } catch {
22782
- throw error;
22783
- }
22784
- process.stderr.write("board write: Hub App allowance exhausted \u2014 wrote with your own GitHub login (#9053)\n");
22785
- return { credential: "user" };
22786
- }
22773
+ await writeHubBoardField2({ projectId, itemId, fieldId, optionId });
22774
+ return { credential: "app_installation" };
22787
22775
  }
22788
22776
  try {
22789
22777
  await client.graphql(UPDATE_ITEM_FIELD_MUTATION, { projectId, itemId, fieldId, optionId });
@@ -22928,19 +22916,20 @@ function renderBoardItem(item) {
22928
22916
  }
22929
22917
  return lines2.join("\n");
22930
22918
  }
22919
+ var CACHED_CONFIRM_CLAUSE = " \xB7 items closed/created after the build may still differ \u2014 confirm with --direct";
22931
22920
  function renderCachedBoardSource(report) {
22932
22921
  if (typeof report.stale?.ageMs === "number") {
22933
22922
  const ageSeconds = Math.floor(report.stale.ageMs / 1e3);
22934
- 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}`;
22935
22924
  }
22936
22925
  if (report.builtAt) {
22937
22926
  const builtMs = Date.parse(report.builtAt);
22938
22927
  if (Number.isFinite(builtMs)) {
22939
- 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}`;
22940
22929
  }
22941
- return `source: cache \xB7 built ${report.builtAt} \xB7 fresh`;
22930
+ return `source: cache \xB7 built ${report.builtAt} \xB7 fresh${CACHED_CONFIRM_CLAUSE}`;
22942
22931
  }
22943
- return "source: cache";
22932
+ return `source: cache${CACHED_CONFIRM_CLAUSE}`;
22944
22933
  }
22945
22934
  function renderBoardReport(report) {
22946
22935
  const lines2 = [`Board \xB7 ${report.project.title} \xB7 @${report.viewer}`, report.source === "cache" ? renderCachedBoardSource(report) : renderBoardSource()];
@@ -25235,6 +25224,20 @@ function upsertManagedSeedBlock(current, desired, begin, end) {
25235
25224
  action: "replace"
25236
25225
  };
25237
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
+ }
25238
25241
  async function resolveHubSeedSource(execGit, defaultBranch = "development") {
25239
25242
  let status;
25240
25243
  try {
@@ -25262,9 +25265,19 @@ remedy (non-destructive): run this command from a clean dedicated worktree \u201
25262
25265
  return { ok: false, reason: `\`git ls-remote origin refs/heads/${defaultBranch}\` returned nothing \u2014 refusing to read seeds without a known upstream revision` };
25263
25266
  }
25264
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
+ }
25265
25278
  return {
25266
25279
  ok: false,
25267
- 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})` : ""}`
25268
25281
  };
25269
25282
  }
25270
25283
  return { ok: true, sha: head };
@@ -28392,10 +28405,10 @@ var rollout_plan_default = {
28392
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)."
28393
28406
  },
28394
28407
  baseline: {
28395
- version: "4.6.37",
28396
- tag: "v4.6.37",
28397
- commit: "94211a24e48a",
28398
- npm: "@mutmutco/cli@4.6.37"
28408
+ version: "4.6.38",
28409
+ tag: "v4.6.38",
28410
+ commit: "975581f0f6ce",
28411
+ npm: "@mutmutco/cli@4.6.38"
28399
28412
  },
28400
28413
  exitCriterion: "fleet-n-of-n",
28401
28414
  hubOnlyShortcut: "forbidden",
@@ -28412,14 +28425,14 @@ var rollout_plan_default = {
28412
28425
  repo: "mutmutco/mmi-hub",
28413
28426
  role: "canary",
28414
28427
  schedule: "train",
28415
- v3Target: "v4.6.37"
28428
+ v3Target: "v4.6.38"
28416
28429
  }
28417
28430
  ],
28418
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.",
28419
28432
  rollback: {
28420
28433
  independent: true,
28421
- mechanism: "npm dist-tag latest -> 4.6.37 and redeploy the Hub Lambda from tag v4.6.37 (94211a24e48a); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
28422
- v3Target: "v4.6.37 (@mutmutco/cli@4.6.37, tag commit 94211a24e48a \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)"
28423
28436
  }
28424
28437
  },
28425
28438
  {
@@ -46446,7 +46459,7 @@ function refuseNotClaimable(e, json) {
46446
46459
  process.exitCode = 1;
46447
46460
  return true;
46448
46461
  }
46449
- 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";
46450
46463
  function flattenBoardReport(report) {
46451
46464
  const closingByRef = /* @__PURE__ */ new Map();
46452
46465
  for (const pull of report.pullRequests ?? []) {
@@ -51148,6 +51161,13 @@ var declaredProjectAdmins = (meta) => {
51148
51161
  return Array.isArray(list) ? list.filter((entry) => typeof entry === "string") : [];
51149
51162
  };
51150
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
+ }
51151
51171
  async function gateRenderDiff(current, rendered) {
51152
51172
  const dir = (0, import_node_fs40.mkdtempSync)((0, import_node_path37.join)((0, import_node_os21.tmpdir)(), "mmi-gate-diff-"));
51153
51173
  const before = (0, import_node_path37.join)(dir, "committed-gate.yml");
@@ -51235,7 +51255,10 @@ async function propagationScope(classes, what) {
51235
51255
  }
51236
51256
  async function propagatePins(apply, json) {
51237
51257
  const source = await resolveHubSeedSource(execGitForSeedSource);
51238
- 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
+ }
51239
51262
  const { classOf, inClassRepos, canarySlug, sourcePilot } = await propagationScope(null, "hub action pins");
51240
51263
  const gh2 = async (args) => execFileP("gh", args, { timeout: 2e4 });
51241
51264
  const repos = [];
@@ -52360,7 +52383,10 @@ LIVE apply to ${repo}:
52360
52383
  const manifestPath = "skills/bootstrap/seeds/manifest.json";
52361
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`);
52362
52385
  const seedSource = await resolveHubSeedSource(execGitForSeedSource);
52363
- 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
+ }
52364
52390
  const manifest = loadBootstrapSeeds((0, import_node_fs40.readFileSync)(manifestPath, "utf8"));
52365
52391
  const propagatable = manifest.seeds.filter(isPropagatableSeed);
52366
52392
  if (!o.target) {
@@ -53359,7 +53385,7 @@ async function stageStatus(member, stage, deps) {
53359
53385
  if (pin) {
53360
53386
  served = pin;
53361
53387
  source = "registry-pin";
53362
- } else if (stage === "dev" || !member.bundles[stage]) {
53388
+ } else {
53363
53389
  try {
53364
53390
  const status = await deps.deploy(member.repo, stage);
53365
53391
  deployStatus = status;
@@ -53505,10 +53531,12 @@ async function newestGreen(deps, repo) {
53505
53531
  async function readSuiteDev(deps) {
53506
53532
  const report = await deps.status();
53507
53533
  return Promise.all(report.members.map(async (m) => {
53508
- 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);
53509
53537
  const row = { repo: m.repo, head: st.head, served: st.served };
53510
- if (st.source === "n/a") return { ...row, state: "converged", note: "no dev stage" };
53511
- 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` };
53512
53540
  let green;
53513
53541
  try {
53514
53542
  green = await newestGreen(deps, m.repo);
@@ -53516,10 +53544,10 @@ async function readSuiteDev(deps) {
53516
53544
  return { ...row, state: "unknown", note: `checks unreadable (${e.message})` };
53517
53545
  }
53518
53546
  const last = st.deployStatus?.lastRun;
53519
- 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}` : ""}` };
53520
- 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` };
53521
53549
  if (!green) return { ...row, state: "blocked", note: `no green SHA in the last ${WALK} development commits` };
53522
- 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"}` };
53523
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` };
53524
53552
  }));
53525
53553
  }
@@ -58738,6 +58766,11 @@ ${lines2}`);
58738
58766
  if (deps.attach) {
58739
58767
  const attached = await deps.attach(row.number, rowRepo(spec), rowPriority);
58740
58768
  row.onBoard = attached.onBoard;
58769
+ if (attached.prioritySet === false) {
58770
+ row.status = "partial";
58771
+ row.prioritySet = false;
58772
+ row.priorityError = attached.priorityError;
58773
+ }
58741
58774
  if (attached.projectItemId) row.projectItemId = attached.projectItemId;
58742
58775
  if (attached.boardAttach) {
58743
58776
  row.boardAttach = attached.boardAttach;
@@ -63536,8 +63569,10 @@ async function attachToProject(issueNumber, repo, priority) {
63536
63569
  await setBoardItemPriority(defaultGitHubClient(), cfg, projectItemId2, priority);
63537
63570
  } catch (e) {
63538
63571
  const err = e;
63539
- 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
63540
63574
  `);
63575
+ return { projectItemId: projectItemId2, onBoard: true, credential: "app_installation", prioritySet: false, priorityError };
63541
63576
  }
63542
63577
  }
63543
63578
  return { projectItemId: projectItemId2, onBoard: true, credential: "app_installation" };
@@ -63887,12 +63922,13 @@ function registerCollaborationCommands(program3) {
63887
63922
  `);
63888
63923
  const linkRecovery = parentRecoveryRef !== void 0 && !parent ? `mmi-cli oracle issue link-child ${parentRecoveryRef} ${childRef}` : void 0;
63889
63924
  console.log(JSON.stringify({
63890
- status: boardRecovery || linkRecovery ? "partial" : "created",
63925
+ status: boardRecovery || linkRecovery || attached.prioritySet === false ? "partial" : "created",
63891
63926
  ...created,
63892
63927
  label: issueType,
63893
63928
  priority,
63894
63929
  projectItemId,
63895
63930
  onBoard,
63931
+ ...attached.prioritySet === false ? { prioritySet: false, priorityError: attached.priorityError } : {},
63896
63932
  ...attachCredential ? { credential: attachCredential } : {},
63897
63933
  // #5489: partial receipt when the GitHub issue landed but Project v2 attach hit GraphQL quota.
63898
63934
  ...!onBoard ? {
@@ -64279,8 +64315,9 @@ ${list}`);
64279
64315
  }
64280
64316
  }
64281
64317
  const created = requireGhCreateOk(await ghCreate(args), "skill-lesson");
64282
- const { projectItemId, onBoard } = await attachToProject(created.number, targetRepo2, priority);
64283
- 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 } : {} }));
64284
64321
  });
64285
64322
  const pr = program3.command("pr").description("pull requests \u2014 reliable create with structured output");
64286
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) => {
@@ -67123,6 +67160,8 @@ function registerSuiteCommands(program3) {
67123
67160
  const status = suiteStatusDeps(github, registry2);
67124
67161
  const deps = {
67125
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,
67126
67165
  commits: async (repo, limit) => (await github.rest("GET", `repos/${repo}/commits?sha=development&per_page=${limit}`) ?? []).map((c) => c.sha),
67127
67166
  checkRuns: async (repo, sha) => (await github.rest("GET", `repos/${repo}/commits/${sha}/check-runs?per_page=100`))?.check_runs ?? [],
67128
67167
  requiredChecks: async (repo) => {
package/cli/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.6.37",
3
+ "version": "4.6.38",
4
4
  "description": "MMI Future CLI — the org dev toolbox and shared cross-IDE engine for every registry-declared MMI coding surface.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/claude-plugin",
3
- "version": "4.6.37",
3
+ "version": "4.6.38",
4
4
  "description": "MMI workflow skills and org gates delivery.",
5
5
  "author": {
6
6
  "name": "MMI Future",