@mutmutco/cli 3.128.0 → 3.129.0

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.
Files changed (2) hide show
  1. package/dist/main.cjs +14 -139
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -16037,10 +16037,10 @@ var rollout_plan_default = {
16037
16037
  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)."
16038
16038
  },
16039
16039
  baseline: {
16040
- version: "3.128.0",
16041
- tag: "v3.128.0",
16042
- commit: "95241fa22be9",
16043
- npm: "@mutmutco/cli@3.128.0"
16040
+ version: "3.129.0",
16041
+ tag: "v3.129.0",
16042
+ commit: "7067f7164642",
16043
+ npm: "@mutmutco/cli@3.129.0"
16044
16044
  },
16045
16045
  exitCriterion: "fleet-n-of-n",
16046
16046
  hubOnlyShortcut: "forbidden",
@@ -16057,14 +16057,14 @@ var rollout_plan_default = {
16057
16057
  repo: "mutmutco/mmi-hub",
16058
16058
  role: "canary",
16059
16059
  schedule: "train",
16060
- v3Target: "v3.128.0"
16060
+ v3Target: "v3.129.0"
16061
16061
  }
16062
16062
  ],
16063
16063
  rollbackTrigger: "Any red inside the post-cut soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a v3 client refused while the compat window must still admit it (SUPPORTED_MINOR_WINDOW=2, MIN_CLIENT_VERSION 0.0.0 \u2014 D6a), or npm consumer install/doctor failure on the v4 dist.",
16064
16064
  rollback: {
16065
16065
  independent: true,
16066
- mechanism: "npm dist-tag latest -> 3.128.0 and redeploy the Hub Lambda from tag v3.128.0 (95241fa22be9); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
16067
- v3Target: "v3.128.0 (@mutmutco/cli@3.128.0, tag commit 95241fa22be9 \u2014 the preserved latest-v3 distribution, D6b)"
16066
+ mechanism: "npm dist-tag latest -> 3.129.0 and redeploy the Hub Lambda from tag v3.129.0 (7067f7164642); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
16067
+ v3Target: "v3.129.0 (@mutmutco/cli@3.129.0, tag commit 7067f7164642 \u2014 the preserved latest-v3 distribution, D6b)"
16068
16068
  }
16069
16069
  },
16070
16070
  {
@@ -22817,9 +22817,6 @@ var BOARD_GIT_TIMEOUT_MS = 1e4;
22817
22817
  var WRITE_PROBE_CONCURRENCY = 8;
22818
22818
  var CLAIM_CONCURRENCY = 5;
22819
22819
  var BOARD_ACTIVE_READ_MAX_PAGES = 100;
22820
- var BOARD_PROJECTION_STALE_MS = 24 * 60 * 6e4;
22821
- var BOARD_PROJECTION_RECONCILE_STALE_MS = 3 * 60 * 6e4;
22822
- var BOARD_PROJECTION_TIMEOUT_MS = 8e3;
22823
22820
  var execFileP4 = (file, args, options = {}) => (
22824
22821
  // encoding 'utf8' guarantees string output at runtime; the cast pins the type (promisify's
22825
22822
  // overloads widen to string|Buffer when options is spread in).
@@ -23087,7 +23084,7 @@ function renderBoardItem(item) {
23087
23084
  return lines.join("\n");
23088
23085
  }
23089
23086
  function renderBoardReport(report) {
23090
- const lines = [`Board \xB7 ${report.project.title} \xB7 @${report.viewer}`, renderBoardSource(report)];
23087
+ const lines = [`Board \xB7 ${report.project.title} \xB7 @${report.viewer}`, renderBoardSource()];
23091
23088
  renderScope(lines, "PRIMARY", report.repo, report.primary, report.viewer);
23092
23089
  renderScope(lines, "SECONDARY", "Other repos on this project", report.secondary, report.viewer);
23093
23090
  if (report.warnings.length) {
@@ -23191,132 +23188,13 @@ async function resolveWritableReposForClaimables(items, client) {
23191
23188
  function writableOrUnknown(writable) {
23192
23189
  return /* @__PURE__ */ new Set([...writable.repos, ...writable.unknown]);
23193
23190
  }
23194
- async function defaultFetchBoardProjection(owner, number, baseUrl) {
23195
- if (!baseUrl) throw new Error("Hub API URL not configured");
23196
- const token = await hubAuthToken({ baseUrl, githubToken });
23197
- if (!token) throw new Error("no Hub session token");
23198
- const url = new URL("/board-projection", `${baseUrl.replace(/\/$/, "")}/`);
23199
- url.searchParams.set("owner", owner);
23200
- url.searchParams.set("number", String(number));
23201
- const res = await fetch(url, {
23202
- method: "GET",
23203
- headers: { ...clientVersionHeaders(), Authorization: `Bearer ${token}` },
23204
- signal: AbortSignal.timeout(BOARD_PROJECTION_TIMEOUT_MS)
23205
- });
23206
- if (res.status === 404) return null;
23207
- if (res.status === 426) throw new Error(upgradeRequiredError(res, await res.json().catch(() => null)));
23208
- if (!res.ok) throw new Error(`board projection HTTP ${res.status}`);
23209
- return await res.json();
23210
- }
23211
- function projectionItems(row) {
23212
- if (!row.items || typeof row.items !== "object" || Array.isArray(row.items)) {
23213
- throw new Error("malformed projection items");
23214
- }
23215
- const items = [];
23216
- for (const raw of Object.values(row.items)) {
23217
- if (!raw || typeof raw !== "object") throw new Error("malformed projection item");
23218
- if (raw.tombstone === true) continue;
23219
- const item = raw;
23220
- if (typeof item.itemId !== "string" || typeof item.contentId !== "string" || item.contentType !== "Issue" && item.contentType !== "PullRequest" || typeof item.repository !== "string" || typeof item.number !== "number" || !Number.isFinite(item.number) || typeof item.ref !== "string" || typeof item.url !== "string" || typeof item.title !== "string" || typeof item.state !== "string" || !BOARD_STATUSES.includes(item.status) || !Array.isArray(item.assignees) || item.assignees.some((value) => typeof value !== "string") || !Array.isArray(item.labels) || item.labels.some((value) => typeof value !== "string") || typeof item.sourceTimestamp !== "string") {
23221
- throw new Error("malformed projection item");
23222
- }
23223
- if (item.state.toUpperCase() === "CLOSED" || item.status === "Done") continue;
23224
- items.push(withLoopState({
23225
- itemId: item.itemId,
23226
- contentId: item.contentId,
23227
- contentType: item.contentType,
23228
- repository: item.repository,
23229
- number: item.number,
23230
- ref: item.ref,
23231
- url: item.url,
23232
- title: item.title,
23233
- state: item.state,
23234
- // #4441: the reconciler stamps each item's content updatedAt as sourceTimestamp — the same
23235
- // idle-age evidence the live read carries, so projection reads derive staleness too.
23236
- updatedAt: item.sourceTimestamp,
23237
- status: item.status,
23238
- statusOptionId: item.statusOptionId,
23239
- priority: item.priority,
23240
- priorityOptionId: item.priorityOptionId,
23241
- assignees: [...item.assignees],
23242
- labels: [...item.labels],
23243
- type: item.type
23244
- }));
23245
- }
23246
- return items;
23247
- }
23248
- async function readViewerLogin(client) {
23249
- const user = await client.rest("GET", "user");
23250
- if (!user?.login) throw new Error("viewer login unavailable");
23251
- return user.login;
23252
- }
23253
- function projectionFreshness(row, now) {
23254
- const projectedAtMs = Date.parse(row.watermark?.projectedAt);
23255
- const sourceTimestampMs = Date.parse(row.watermark?.sourceTimestamp);
23256
- if (!Number.isFinite(projectedAtMs) || !Number.isFinite(sourceTimestampMs)) {
23257
- throw new Error("malformed projection watermark");
23258
- }
23259
- const projectedAgeMs = Math.max(0, now - projectedAtMs);
23260
- const sourceAgeMs = Math.max(0, now - sourceTimestampMs);
23261
- return {
23262
- projectedAt: row.watermark.projectedAt,
23263
- projectedAgeMs,
23264
- sourceTimestamp: row.watermark.sourceTimestamp,
23265
- sourceAgeMs,
23266
- // #4774: health must agree with BOTH ages a caller can see on the source line — a projection whose
23267
- // rebuild (projectedAt) went stale is exactly as unreliable as one whose source events did, even
23268
- // while recent webhook activity keeps sourceAgeMs looking fresh.
23269
- health: row.watermark.health === "stale" || sourceAgeMs > BOARD_PROJECTION_STALE_MS || projectedAgeMs > BOARD_PROJECTION_RECONCILE_STALE_MS ? "stale" : "fresh"
23270
- };
23271
- }
23272
- function renderBoardSource(report) {
23273
- if (report.source === "projection" && report.freshness) {
23274
- return `source: projection \xB7 projected ${formatClaimAge(report.freshness.projectedAgeMs)} ago \xB7 source events through ${report.freshness.sourceTimestamp} \xB7 health ${report.freshness.health}`;
23275
- }
23276
- return `source: live${report.sourceReason ? ` (${report.sourceReason})` : ""}`;
23191
+ function renderBoardSource() {
23192
+ return "source: live";
23277
23193
  }
23278
23194
  async function readBoard(options, deps = {}) {
23279
23195
  const cfg = resolveBoardConfig(options.config);
23280
23196
  const client = deps.client ?? defaultGitHubClient();
23281
- let collected;
23282
- let source = "live";
23283
- let freshness = null;
23284
- let sourceReason;
23285
- if (!options.live) {
23286
- try {
23287
- const baseUrl = options.config.sagaApiUrl;
23288
- const row = await (deps.fetchProjection ?? defaultFetchBoardProjection)(cfg.projectOwner, cfg.projectNumber, baseUrl);
23289
- if (!row) {
23290
- sourceReason = "projection absent";
23291
- } else if (row.schemaVersion !== 1) {
23292
- sourceReason = `unsupported projection schema ${String(row.schemaVersion)}`;
23293
- } else if (row.watermark?.health === "oversize") {
23294
- sourceReason = "projection oversize";
23295
- } else {
23296
- const [viewer, repo] = await Promise.all([
23297
- readViewerLogin(client),
23298
- resolveCurrentRepo(options, deps)
23299
- ]);
23300
- const now = (deps.now ?? Date.now)();
23301
- collected = {
23302
- items: projectionItems(row),
23303
- viewer,
23304
- repo,
23305
- projectId: cfg.projectId,
23306
- projectTitle: String(cfg.projectNumber),
23307
- warnings: [],
23308
- partial: false
23309
- };
23310
- freshness = projectionFreshness(row, now);
23311
- source = "projection";
23312
- }
23313
- } catch {
23314
- sourceReason = "projection read error";
23315
- }
23316
- }
23317
- if (!collected) {
23318
- collected = await collectBoardItems(cfg, { repo: options.repo, allowPartial: options.allowPartial, activeOnly: true }, deps);
23319
- }
23197
+ const collected = await collectBoardItems(cfg, { repo: options.repo, allowPartial: options.allowPartial, activeOnly: true }, deps);
23320
23198
  const writable = await resolveWritableReposForClaimables(collected.items, client);
23321
23199
  collected.warnings.push(...writable.warnings);
23322
23200
  collected.partial = collected.partial || writable.partial;
@@ -23328,9 +23206,7 @@ async function readBoard(options, deps = {}) {
23328
23206
  ...groups,
23329
23207
  warnings: collected.warnings,
23330
23208
  partial: collected.partial,
23331
- source,
23332
- freshness,
23333
- ...sourceReason ? { sourceReason } : {}
23209
+ source: "live"
23334
23210
  };
23335
23211
  if (options.includeBundleDetails || options.includeAllBodies) {
23336
23212
  await attachBundleDetails(report, client, options.allowPartial ?? false, { all: options.includeAllBodies });
@@ -32691,8 +32567,7 @@ function registerBoardCommands(program3) {
32691
32567
  repo: o.repo,
32692
32568
  includeBundleDetails: o.bundleDetails,
32693
32569
  includeAllBodies: o.bodies,
32694
- allowPartial: o.allowPartial,
32695
- live: o.live
32570
+ allowPartial: o.allowPartial
32696
32571
  });
32697
32572
  console.log(o.json ? JSON.stringify(report) : renderBoardReport(report));
32698
32573
  } catch (e) {
@@ -32703,7 +32578,7 @@ function registerBoardCommands(program3) {
32703
32578
  return alreadyClaimed ? `Check ${ref}: claimed and In Progress, no live contest - claim would renew the lease (nothing written)` : `Check ${ref}: free - claim would proceed (nothing written)`;
32704
32579
  }
32705
32580
  const board = program3.command("board").description("read, claim, show, and move Project v2 work items for the current repo");
32706
- board.command("read", { isDefault: true }).description("read the board and print user-owned, claimable, and taken items").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo (defaults to git origin)").option("--bundle-details", "fetch body/comments only for user-owned and claimable issues").option("--bodies", "fetch body/comments for EVERY scoped row, including taken and unowned in-flight ones \u2014 for consumers that scope by Status rather than ownership (#4861); implies --bundle-details and costs one extra read per row").option("--allow-partial", "return partial board results when later page/detail reads fail").option("--live", "bypass the projection and read the authoritative GitHub Project v2 board").addHelpText("after", "\nBy default, read uses the explicitly-stale Hub projection and prints its watermark. --live is authoritative.\n--allow-partial applies to the live paginated path and detail reads; the projection row itself is atomic.\n").action((o) => runBoardRead(o));
32581
+ board.command("read", { isDefault: true }).description("read the board and print user-owned, claimable, and taken items").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo (defaults to git origin)").option("--bundle-details", "fetch body/comments only for user-owned and claimable issues").option("--bodies", "fetch body/comments for EVERY scoped row, including taken and unowned in-flight ones \u2014 for consumers that scope by Status rather than ownership (#4861); implies --bundle-details and costs one extra read per row").option("--allow-partial", "return partial board results when later page/detail reads fail").addHelpText("after", "\nread is always the authoritative live GitHub Project v2 board (#4926).\n--allow-partial applies to the paginated path and detail reads.\n").action((o) => runBoardRead(o));
32707
32582
  withExamples(mutating(
32708
32583
  board.command("claim <issues...>").description("claim issues: assign them and move their Project v2 Status to In Progress \u2014 idempotent, so an item already yours and In Progress succeeds unchanged (one or more refs)").option("--json", "machine-readable output").option("--repo <owner/repo>", "current repo for local issue numbers (defaults to git origin)").option("--for <login>", "assign to this login instead of @me \u2014 agent claims on behalf of the master").option("--force", "take an item already claimed by another lane that shows live evidence of active work (#3727)").option("--check", "read-only: run every claim gate and report the verdict, writing nothing \u2014 exits 1 with the same refusal a real claim would raise (#4511)").option("--allow-partial", "return success JSON if assignment succeeds but the status move fails"),
32709
32584
  (_opts, args) => ({ command: "board claim", issues: args[0] ?? [] })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "3.128.0",
3
+ "version": "3.129.0",
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",