@mutmutco/cli 4.6.29 → 4.6.31
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/dist/main.cjs +173 -50
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -24526,19 +24526,7 @@ function formatBulkClaimSummary(results) {
|
|
|
24526
24526
|
const notClaimed = skipped.map((r) => `${r.ref}${/already claimed by /i.test(r.reason) ? " (held by another lane)" : ""}`);
|
|
24527
24527
|
return `claimed ${landed.length} of ${results.length}: ${landed.length ? `${landed.join(", ")} landed` : "none landed"}; not claimed: ${notClaimed.join(", ")}`;
|
|
24528
24528
|
}
|
|
24529
|
-
async function
|
|
24530
|
-
const cfg = resolveBoardConfig(options.config);
|
|
24531
|
-
const { collected, snapshot } = await collectBoardPreferringSnapshot(cfg, options, deps);
|
|
24532
|
-
const selectors = [];
|
|
24533
|
-
const seen = /* @__PURE__ */ new Set();
|
|
24534
|
-
for (const raw of options.selectors) {
|
|
24535
|
-
const selector = parseIssueSelector(raw, collected.repo, options.repo);
|
|
24536
|
-
const key = `${selector.repo.toLowerCase()}#${selector.number}`;
|
|
24537
|
-
if (seen.has(key)) continue;
|
|
24538
|
-
seen.add(key);
|
|
24539
|
-
selectors.push(selector);
|
|
24540
|
-
}
|
|
24541
|
-
const ctx = await prepareClaimContext(options, selectors, deps, collected, snapshot);
|
|
24529
|
+
async function runBulkClaims(ctx, selectors, options) {
|
|
24542
24530
|
const results = new Array(selectors.length);
|
|
24543
24531
|
let next = 0;
|
|
24544
24532
|
const worker = async () => {
|
|
@@ -24555,6 +24543,71 @@ async function claimBoardIssues(options, deps = {}) {
|
|
|
24555
24543
|
}
|
|
24556
24544
|
};
|
|
24557
24545
|
await Promise.all(Array.from({ length: Math.min(CLAIM_CONCURRENCY, selectors.length) }, () => worker()));
|
|
24546
|
+
return results;
|
|
24547
|
+
}
|
|
24548
|
+
function claimArgsError(refs, all) {
|
|
24549
|
+
if (all && refs.length) return `--all claims the whole board; drop the explicit refs (${refs.join(" ")}) or drop --all`;
|
|
24550
|
+
if (!all && !refs.length) return "missing issue ref: pass one or more refs, or --all for every open claimable item on the board";
|
|
24551
|
+
return void 0;
|
|
24552
|
+
}
|
|
24553
|
+
async function claimAllBoardIssues(options, deps = {}) {
|
|
24554
|
+
const cfg = resolveBoardConfig(options.config);
|
|
24555
|
+
const { collected, snapshot } = await collectBoardPreferringSnapshot(cfg, options, deps);
|
|
24556
|
+
const ctx = await prepareClaimContext(options, [], deps, collected, snapshot);
|
|
24557
|
+
const { primary } = ctx.report;
|
|
24558
|
+
const assignee = options.assignee ?? "@me";
|
|
24559
|
+
const login = assignee === "@me" ? ctx.report.viewer : assignee.replace(/^@/, "");
|
|
24560
|
+
const selectors = [];
|
|
24561
|
+
const skipped = [];
|
|
24562
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24563
|
+
for (const item of [...primary.userOwned, ...primary.claimable, ...primary.unownedInFlight, ...primary.taken]) {
|
|
24564
|
+
const key = item.ref.toLowerCase();
|
|
24565
|
+
if (seen.has(key)) continue;
|
|
24566
|
+
seen.add(key);
|
|
24567
|
+
if (item.contentType !== "Issue") {
|
|
24568
|
+
skipped.push({ ref: item.ref, reason: "not an issue" });
|
|
24569
|
+
continue;
|
|
24570
|
+
}
|
|
24571
|
+
if (item.state !== "OPEN") {
|
|
24572
|
+
skipped.push({ ref: item.ref, reason: `issue is ${item.state.toLowerCase()} but its card is ${item.status}` });
|
|
24573
|
+
continue;
|
|
24574
|
+
}
|
|
24575
|
+
if (item.status === "In Review") {
|
|
24576
|
+
skipped.push({ ref: item.ref, reason: "In Review \u2014 a whole-board claim does not pull review cards back to In Progress; claim it by ref to reclaim" });
|
|
24577
|
+
continue;
|
|
24578
|
+
}
|
|
24579
|
+
const verdict = evaluateClaim(item, login);
|
|
24580
|
+
if (!verdict.ok) {
|
|
24581
|
+
skipped.push({ ref: item.ref, reason: verdict.reason.replace(/ \(never chain separate claims.*$/, "") });
|
|
24582
|
+
continue;
|
|
24583
|
+
}
|
|
24584
|
+
selectors.push({ repo: item.repository, number: item.number });
|
|
24585
|
+
}
|
|
24586
|
+
ctx.selectedRefs = new Set(selectors.map((s) => `${s.repo}#${s.number}`.toLowerCase()));
|
|
24587
|
+
const results = await runBulkClaims(ctx, selectors, options);
|
|
24588
|
+
return {
|
|
24589
|
+
viewer: ctx.report.viewer,
|
|
24590
|
+
repo: ctx.report.repo,
|
|
24591
|
+
results,
|
|
24592
|
+
skipped,
|
|
24593
|
+
failed: results.filter((result) => !result.claimed).length,
|
|
24594
|
+
...ctx.report.warnings.length ? { warnings: [...ctx.report.warnings] } : {}
|
|
24595
|
+
};
|
|
24596
|
+
}
|
|
24597
|
+
async function claimBoardIssues(options, deps = {}) {
|
|
24598
|
+
const cfg = resolveBoardConfig(options.config);
|
|
24599
|
+
const { collected, snapshot } = await collectBoardPreferringSnapshot(cfg, options, deps);
|
|
24600
|
+
const selectors = [];
|
|
24601
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24602
|
+
for (const raw of options.selectors) {
|
|
24603
|
+
const selector = parseIssueSelector(raw, collected.repo, options.repo);
|
|
24604
|
+
const key = `${selector.repo.toLowerCase()}#${selector.number}`;
|
|
24605
|
+
if (seen.has(key)) continue;
|
|
24606
|
+
seen.add(key);
|
|
24607
|
+
selectors.push(selector);
|
|
24608
|
+
}
|
|
24609
|
+
const ctx = await prepareClaimContext(options, selectors, deps, collected, snapshot);
|
|
24610
|
+
const results = await runBulkClaims(ctx, selectors, options);
|
|
24558
24611
|
return {
|
|
24559
24612
|
viewer: ctx.report.viewer,
|
|
24560
24613
|
repo: ctx.report.repo,
|
|
@@ -25980,7 +26033,7 @@ var BLESSED_RUN_WITH_BUDGET_SHA = "cb31a94c89729beac83500ad4d301132dcfe2e4c";
|
|
|
25980
26033
|
var BLESSED_RUNNER_GATE_SHA = "7f9a25df2ce7198c67407b42d0c548f75ce3f430";
|
|
25981
26034
|
var BLESSED_RUNNER_CHECK_POLICY_SHA = "67bb2012710a96d21be825c29a5d5c2b4b8001e2";
|
|
25982
26035
|
var BLESSED_KIT_AGENT_FILES_SHA = "e976df208d891b215844386c1cac0f161946b6fa";
|
|
25983
|
-
var BLESSED_JEV_FAILURES_SHA = "
|
|
26036
|
+
var BLESSED_JEV_FAILURES_SHA = "20ce7d5e0d85cdc7e14bbacbc8b6afdabee6bd41";
|
|
25984
26037
|
var BLESSED_VERDICT_RECEIPT_SHA = "";
|
|
25985
26038
|
var BLESSED_RUNBOOK_GUARD_SHA = "e661e0aedc0e58b73c738bf6cdfeac65e5914584";
|
|
25986
26039
|
var REMOTE_USE = /^mutmutco\/MMI-Hub\/\.github\/actions\/run-with-budget@(\S+)$/;
|
|
@@ -28248,10 +28301,10 @@ var rollout_plan_default = {
|
|
|
28248
28301
|
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)."
|
|
28249
28302
|
},
|
|
28250
28303
|
baseline: {
|
|
28251
|
-
version: "4.6.
|
|
28252
|
-
tag: "v4.6.
|
|
28253
|
-
commit: "
|
|
28254
|
-
npm: "@mutmutco/cli@4.6.
|
|
28304
|
+
version: "4.6.31",
|
|
28305
|
+
tag: "v4.6.31",
|
|
28306
|
+
commit: "53e22eaf158d",
|
|
28307
|
+
npm: "@mutmutco/cli@4.6.31"
|
|
28255
28308
|
},
|
|
28256
28309
|
exitCriterion: "fleet-n-of-n",
|
|
28257
28310
|
hubOnlyShortcut: "forbidden",
|
|
@@ -28268,14 +28321,14 @@ var rollout_plan_default = {
|
|
|
28268
28321
|
repo: "mutmutco/mmi-hub",
|
|
28269
28322
|
role: "canary",
|
|
28270
28323
|
schedule: "train",
|
|
28271
|
-
v3Target: "v4.6.
|
|
28324
|
+
v3Target: "v4.6.31"
|
|
28272
28325
|
}
|
|
28273
28326
|
],
|
|
28274
28327
|
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.",
|
|
28275
28328
|
rollback: {
|
|
28276
28329
|
independent: true,
|
|
28277
|
-
mechanism: "npm dist-tag latest -> 4.6.
|
|
28278
|
-
v3Target: "v4.6.
|
|
28330
|
+
mechanism: "npm dist-tag latest -> 4.6.31 and redeploy the Hub Lambda from tag v4.6.31 (53e22eaf158d); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
28331
|
+
v3Target: "v4.6.31 (@mutmutco/cli@4.6.31, tag commit 53e22eaf158d \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
28279
28332
|
}
|
|
28280
28333
|
},
|
|
28281
28334
|
{
|
|
@@ -39363,8 +39416,9 @@ function blessedHubActionPins(constants = gate_budget_exports) {
|
|
|
39363
39416
|
}
|
|
39364
39417
|
return pins;
|
|
39365
39418
|
}
|
|
39419
|
+
var PIN_COMMIT_FORMAT = "commit-format:runbook-unaffected";
|
|
39366
39420
|
function pinTableDigest(pins) {
|
|
39367
|
-
return (0, import_node_crypto11.createHash)("sha1").update([...pins].map(([a, s]) => `${a}@${s}`).sort().join("\n")).digest("hex");
|
|
39421
|
+
return (0, import_node_crypto11.createHash)("sha1").update([PIN_COMMIT_FORMAT, ...[...pins].map(([a, s]) => `${a}@${s}`).sort()].join("\n")).digest("hex");
|
|
39368
39422
|
}
|
|
39369
39423
|
var HUB_ACTION_USE = /^(\s*-?\s*uses:\s*['"]?mutmutco\/mmi-hub\/\.github\/actions\/)([\w.-]+)@([^\s'"#]+)([^\n]*)$/i;
|
|
39370
39424
|
function staleHubActionPins(body, pins = blessedHubActionPins()) {
|
|
@@ -39484,7 +39538,9 @@ async function runPinTick(input) {
|
|
|
39484
39538
|
if (!next.changes.length) continue;
|
|
39485
39539
|
lines2.push(...next.changes.map((c) => `- \`${path2}:${c.line}\` ${c.action}: \`${c.from.slice(0, 12)}\` \u2192 \`${c.to.slice(0, 12)}\``));
|
|
39486
39540
|
const tmp = (0, import_node_path29.join)((0, import_node_os17.tmpdir)(), `mmi-pin-propagate-${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}.json`);
|
|
39487
|
-
const put = { message: `ci: pin MMI-Hub actions in ${path2} to the blessed SHAs
|
|
39541
|
+
const put = { message: `ci: pin MMI-Hub actions in ${path2} to the blessed SHAs
|
|
39542
|
+
|
|
39543
|
+
Runbook-Unaffected: SHA-only MMI-Hub action pin move; no workflow step changes`, content: encodeGitHubContents(next.body), branch };
|
|
39488
39544
|
if (file.sha) put.sha = file.sha;
|
|
39489
39545
|
(0, import_node_fs32.writeFileSync)(tmp, JSON.stringify(put), "utf8");
|
|
39490
39546
|
try {
|
|
@@ -42451,9 +42507,19 @@ async function runPrLandOnce(prNumber, options, deps) {
|
|
|
42451
42507
|
}
|
|
42452
42508
|
}
|
|
42453
42509
|
}
|
|
42454
|
-
const
|
|
42510
|
+
const isConflicting = (state) => state.mergeStateStatus === "DIRTY" || state.mergeable === "CONFLICTING";
|
|
42511
|
+
const conflictError = () => `pr land #${prNumber}: branch conflicts with base \u2014 \`git fetch origin\`, merge origin/development into the branch (never rebase a pushed branch), resolve the conflicts, regenerate any checked-in artifacts, commit and push, then re-run \`mmi-cli devops pr land ${prNumber} --repo ${repo}\` once${hasQueue ? `; ${prQueueReadHint(prNumber, repo)}` : ""}`;
|
|
42512
|
+
const readPrState = async () => {
|
|
42513
|
+
const state = await deps.readPrState(prNumber, repo);
|
|
42514
|
+
if (!isConflicting(state)) return state;
|
|
42515
|
+
await (deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms))))(PR_HEAD_UPDATE_API_READ_DELAY_MS);
|
|
42516
|
+
return deps.readPrState(prNumber, repo);
|
|
42517
|
+
};
|
|
42518
|
+
let prState;
|
|
42519
|
+
const checksWaitError = async (checksWait) => {
|
|
42455
42520
|
if (checksWait.status === "conflicting") {
|
|
42456
|
-
|
|
42521
|
+
prState = await readPrState();
|
|
42522
|
+
return isConflicting(prState) ? conflictError() : void 0;
|
|
42457
42523
|
}
|
|
42458
42524
|
if (checksWait.status === "failure" || checksWait.status === "timeout" || checksWait.status === "rate-limited") {
|
|
42459
42525
|
return `checks-wait ${checksWait.status}${checksWait.detail ? `: ${checksWait.detail}` : ""}`;
|
|
@@ -42461,11 +42527,11 @@ async function runPrLandOnce(prNumber, options, deps) {
|
|
|
42461
42527
|
return void 0;
|
|
42462
42528
|
};
|
|
42463
42529
|
base.checksWait = await deps.waitForChecks(prNumber, repo);
|
|
42464
|
-
const waitError = checksWaitError(base.checksWait);
|
|
42465
|
-
if (waitError) return { ...base, error: hasQueue ? `${waitError} \u2014 ${prQueueReadHint(prNumber, repo)}` : waitError };
|
|
42530
|
+
const waitError = await checksWaitError(base.checksWait);
|
|
42531
|
+
if (waitError) return { ...base, error: hasQueue && base.checksWait.status !== "conflicting" ? `${waitError} \u2014 ${prQueueReadHint(prNumber, repo)}` : waitError };
|
|
42466
42532
|
const driftAfterChecks = await headDrift("after checks");
|
|
42467
42533
|
if (driftAfterChecks) return driftAfterChecks;
|
|
42468
|
-
|
|
42534
|
+
prState = prState ?? await readPrState();
|
|
42469
42535
|
if (!hasQueue && pinned && prState.mergeStateStatus === "BEHIND") {
|
|
42470
42536
|
return {
|
|
42471
42537
|
...base,
|
|
@@ -42480,16 +42546,11 @@ async function runPrLandOnce(prNumber, options, deps) {
|
|
|
42480
42546
|
return { ...base, error: `pr land #${prNumber}: ${e.message}` };
|
|
42481
42547
|
}
|
|
42482
42548
|
base.checksWait = await deps.waitForChecks(prNumber, repo);
|
|
42483
|
-
const reWaitError = checksWaitError(base.checksWait);
|
|
42484
|
-
if (reWaitError) return { ...base, error: hasQueue ? `${reWaitError} \u2014 ${prQueueReadHint(prNumber, repo)}` : reWaitError };
|
|
42485
|
-
prState = await
|
|
42486
|
-
}
|
|
42487
|
-
if (prState.mergeStateStatus === "DIRTY" || prState.mergeable === "CONFLICTING") {
|
|
42488
|
-
return {
|
|
42489
|
-
...base,
|
|
42490
|
-
error: `pr land #${prNumber}: branch conflicts with base \u2014 rebase/merge development, rebuild any checked-in artifacts, then re-push before landing${hasQueue ? `; ${prQueueReadHint(prNumber, repo)}` : ""}`
|
|
42491
|
-
};
|
|
42549
|
+
const reWaitError = await checksWaitError(base.checksWait);
|
|
42550
|
+
if (reWaitError) return { ...base, error: hasQueue && base.checksWait.status !== "conflicting" ? `${reWaitError} \u2014 ${prQueueReadHint(prNumber, repo)}` : reWaitError };
|
|
42551
|
+
prState = await readPrState();
|
|
42492
42552
|
}
|
|
42553
|
+
if (isConflicting(prState)) return { ...base, error: conflictError() };
|
|
42493
42554
|
if (hasQueue && !deps.enqueueQueue) throw new Error("queue-only enqueue adapter unavailable; no merge dispatched");
|
|
42494
42555
|
const merge = await mergeAutoWithTransientRetry(prNumber, repo, hasQueue ? { ...deps, mergeAuto: (pr, target) => deps.enqueueQueue(pr, target, pinned) } : deps);
|
|
42495
42556
|
base.mergeStatus = merge.mergeStatus;
|
|
@@ -44060,13 +44121,13 @@ async function inspectQueuePreparation(pr, repo, options) {
|
|
|
44060
44121
|
const parsed = parseRestClosingGuardInput({ state: pull.state, title: pull.title, body: pull.body }, commits, repo);
|
|
44061
44122
|
parsed.closing = closing;
|
|
44062
44123
|
const policy = { title: repository.squash_merge_commit_title, body: repository.squash_merge_commit_message };
|
|
44063
|
-
const preparation = (reason, targets, detail) => ({
|
|
44124
|
+
const preparation = (reason, targets, detail, nextAction) => ({
|
|
44064
44125
|
mergeStatus: "failed",
|
|
44065
44126
|
preparation: {
|
|
44066
44127
|
status: "preparation-needed",
|
|
44067
44128
|
reason,
|
|
44068
44129
|
issueTargets: targets.map((number) => `${repo}#${number}`),
|
|
44069
|
-
nextAction: reason === "queue-message" ? `PR author: if PR #${pr} is meant to close ${targets.length ? targets.map((number) => `#${number}`).join(", ") : "#N"}, add ${targets.length ? targets.map((number) => `\`Closes #${number}\``).join(", ") : "`Closes #N`"} to the PR body (\`mmi-cli devops pr edit ${pr} --repo ${repo} --body-file .jerv/tmp/pr-${pr}-body.md\`) and rerun \`mmi-cli devops pr land ${pr} --repo ${repo}\` without pushing a new head; if ${targets.length ? targets.map((number) => `#${number}`).join(", ") : "#N"} must stay open, remove the closing phrases from the source commit or PR text using ${targets.length ? targets.map((number) => `\`Part of #${number}\``).join(", ") : "`Part of #N`"} / ${targets.length ? targets.map((number) => `\`Refs #${number}\``).join(", ") : "`Refs #N`"}, then re-run checks and \`pr land\` with the prepared head. A queue cannot accept --squash-body-file.` : "PR author: prepare the source commit messages and required trailers without a body override; re-run checks and pr land. GitHub may append coauthors even to one source commit, so source consolidation alone does not prove the generated message passes test policy. The combined queue candidate must pass with the updated shared trailer reader. Keep repository settings unchanged."
|
|
44130
|
+
nextAction: nextAction ?? (reason === "queue-message" ? `PR author: if PR #${pr} is meant to close ${targets.length ? targets.map((number) => `#${number}`).join(", ") : "#N"}, add ${targets.length ? targets.map((number) => `\`Closes #${number}\``).join(", ") : "`Closes #N`"} to the PR body (\`mmi-cli devops pr edit ${pr} --repo ${repo} --body-file .jerv/tmp/pr-${pr}-body.md\`) and rerun \`mmi-cli devops pr land ${pr} --repo ${repo}\` without pushing a new head; if ${targets.length ? targets.map((number) => `#${number}`).join(", ") : "#N"} must stay open, remove the closing phrases from the source commit or PR text using ${targets.length ? targets.map((number) => `\`Part of #${number}\``).join(", ") : "`Part of #N`"} / ${targets.length ? targets.map((number) => `\`Refs #${number}\``).join(", ") : "`Refs #N`"}, then re-run checks and \`pr land\` with the prepared head. A queue cannot accept --squash-body-file.` : "PR author: prepare the source commit messages and required trailers without a body override; re-run checks and pr land. GitHub may append coauthors even to one source commit, so source consolidation alone does not prove the generated message passes test policy. The combined queue candidate must pass with the updated shared trailer reader. Keep repository settings unchanged.")
|
|
44070
44131
|
},
|
|
44071
44132
|
error: `queue preparation needed: ${detail}`
|
|
44072
44133
|
});
|
|
@@ -44149,7 +44210,17 @@ ${pull.body}`;
|
|
|
44149
44210
|
}
|
|
44150
44211
|
};
|
|
44151
44212
|
}
|
|
44152
|
-
|
|
44213
|
+
const trailerKeys = (message2) => {
|
|
44214
|
+
const keys = [...new Set(parseTrailers(message2.trim(), process.cwd()).map((trailer) => trailer.split(":")[0].trim()))];
|
|
44215
|
+
return keys.length ? keys.join(", ") : "none";
|
|
44216
|
+
};
|
|
44217
|
+
const source = options.bodyText != null ? "the explicit body override" : "the body composed from the source commit messages";
|
|
44218
|
+
const detail = `the requested sanitized body or trailer arrangement cannot be sent to queue admission: queue merge method ${method}, squash body policy ${policy.body ?? "unknown"}; required body is ${source} (trailer keys: ${trailerKeys(`${headline}
|
|
44219
|
+
|
|
44220
|
+
${requiredBody}`)}), but the message the queue would send carries trailer keys: ${trailerKeys(actualMessage)}`;
|
|
44221
|
+
const next = options.bodyText != null ? `PR author: a merge queue accepts no body override; drop --body/--squash-body-file and place the required trailer(s) where the queue's ${method}${method === "SQUASH" ? ` (${policy.body})` : ""} message reads them, then rerun \`mmi-cli devops pr land ${pr} --repo ${repo}\`. Keep repository settings unchanged.` : method !== "SQUASH" ? `PR author: the queue's ${method} method keeps the original commit messages, so the required trailer(s) must sit in a source commit message's final trailer block; amend the commit, push, re-run checks and \`mmi-cli devops pr land ${pr} --repo ${repo}\`. Keep repository settings unchanged.` : policy.body === "BLANK" ? `PR author: squash body policy BLANK sends an empty body, so no trailer from the commits or PR body reaches the queue commit; the waiver cannot survive queue admission with unchanged repository settings \u2014 remove the need for the override or ask a maintainer. Keep repository settings unchanged.` : `PR author: squash body policy ${policy.body} does not reproduce the composed body; consolidate the required trailer(s) into one source commit message's final trailer block, push, re-run checks and \`mmi-cli devops pr land ${pr} --repo ${repo}\`. Keep repository settings unchanged.`;
|
|
44222
|
+
const caveat = " GitHub may append coauthors even to one source commit, so source consolidation alone does not prove the generated message passes test policy; the combined queue candidate must pass with the shared trailer reader.";
|
|
44223
|
+
return { kind: "refused", result: preparation("queue-message-override", [], detail, next + caveat) };
|
|
44153
44224
|
}
|
|
44154
44225
|
return { kind: "ready", head, pull, read, queueRead, write, ...verdict.message ? { warning: verdict.message } : {} };
|
|
44155
44226
|
}
|
|
@@ -46215,9 +46286,41 @@ function registerBoardCommands(program3) {
|
|
|
46215
46286
|
}
|
|
46216
46287
|
});
|
|
46217
46288
|
withExamples(mutating(
|
|
46218
|
-
board.command("claim
|
|
46289
|
+
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, or --all for the whole board); the board scan rides the Hub snapshot, same as board read").addHelpText("after", "\nclaim reads the board through the same Hub snapshot leg as `board read` (the App-installation\ncredential, never your personal GraphQL pool); the direct user-auth read is an emergency fallback\nand is named in a Warning line after the verdict (#6162).\n\nevery claim stamps a lane-identity marker comment on the issue (`<!-- mmi-claim: \u2026 -->`,\nsurface/session@host) so other agents can attribute the hold (#3727). The session is the\nhost-exported id when the surface provides one, otherwise a per-process `synth-` fallback \u2014\na claim is never anonymous (#5245). `board show`, doctor and unclaim read the latest marker.\n\nsame-owner resume (#6035): when the prior marker was posted by YOUR login on THIS host and its\nlocal session is verifiably dead (transcript probe), the claim proceeds as a resume without\n--force and names the evidence. A live, foreign-host, or unprobeable prior lane still refuses.\n\nreclaim from In Review (#6339): an In Review item with no holder, or one held by the claiming\nlogin, is claimable \u2014 it moves back to In Progress and the receipt names the status it came from.\nAn In Review item another login holds is refused (ask the holder, or wait for the review to land),\nand Done stays refused. The board status is the authority; no PR state is consulted.\n\nwhole board (#9930): --all reads the board once and claims every open item of this repo's primary\nboard that the per-ref claim may take (yours, unassigned Todo, unowned In Progress), carrying each\nitem's branch/worktree lane evidence forward like a per-ref claim. It prints one line per item, then\nthe refs it skipped and why (assigned to another login, In Review, closed, not an issue). Without\n--force a live foreign lane is skipped with its reason, never stolen; --force takes it as the\nper-ref --force would. --all with explicit refs is an error.\n").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("--all", "claim every open claimable item on the current repo's board in one pass (combine with --force/--repo; not with explicit refs) (#9930)").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"),
|
|
46219
46290
|
(_opts, args) => ({ command: "oracle board claim", issues: args[0] ?? [] })
|
|
46220
|
-
).action(async (issueRefs, o) => {
|
|
46291
|
+
).action(async (issueRefs = [], o) => {
|
|
46292
|
+
const argsError = claimArgsError(issueRefs, o.all);
|
|
46293
|
+
if (argsError) return failGraceful(`oracle board claim failed: ${argsError}`);
|
|
46294
|
+
if (o.all) {
|
|
46295
|
+
try {
|
|
46296
|
+
const config = await loadConfigForBoardSelector2("", o.repo);
|
|
46297
|
+
const all = await claimAllBoardIssues({
|
|
46298
|
+
config,
|
|
46299
|
+
repo: o.repo,
|
|
46300
|
+
assignee: o.for,
|
|
46301
|
+
force: o.force,
|
|
46302
|
+
check: o.check,
|
|
46303
|
+
allowPartial: o.allowPartial
|
|
46304
|
+
}, { snapshot: registryClientDeps(config) });
|
|
46305
|
+
if (o.json) {
|
|
46306
|
+
console.log(JSON.stringify({ results: all.results, skipped: all.skipped }));
|
|
46307
|
+
printClaimWarnings(all.warnings, true);
|
|
46308
|
+
} else {
|
|
46309
|
+
for (const result of all.results) {
|
|
46310
|
+
console.log(result.claimed ? claimVerdict(result.ref, result) : `Skipped ${result.ref}: ${result.reason}`);
|
|
46311
|
+
}
|
|
46312
|
+
for (const skip of all.skipped) console.log(`Skipped ${skip.ref}: ${skip.reason}`);
|
|
46313
|
+
const landed = all.results.filter((r) => r.claimed).length;
|
|
46314
|
+
console.log(`claim --all: ${landed} of ${all.results.length} attempted item(s) claimed; ${all.skipped.length} not attempted`);
|
|
46315
|
+
printClaimWarnings(all.warnings);
|
|
46316
|
+
}
|
|
46317
|
+
if (all.failed > 0) process.exitCode = 1;
|
|
46318
|
+
} catch (e) {
|
|
46319
|
+
if (refuseRateLimited(e, o.json)) return;
|
|
46320
|
+
return failGraceful(`oracle board claim failed: ${e.message}`);
|
|
46321
|
+
}
|
|
46322
|
+
return;
|
|
46323
|
+
}
|
|
46221
46324
|
if (issueRefs.length === 1) {
|
|
46222
46325
|
const issueRef = issueRefs[0];
|
|
46223
46326
|
try {
|
|
@@ -46276,11 +46379,13 @@ function registerBoardCommands(program3) {
|
|
|
46276
46379
|
}), [
|
|
46277
46380
|
"mmi-cli oracle board claim 2680",
|
|
46278
46381
|
"mmi-cli oracle board claim 2680 2681 --for teammate-login",
|
|
46279
|
-
"mmi-cli oracle board claim 2680 --check"
|
|
46382
|
+
"mmi-cli oracle board claim 2680 --check",
|
|
46383
|
+
"mmi-cli oracle board claim --all --force"
|
|
46280
46384
|
], [
|
|
46281
46385
|
"Pass raw issue numbers/refs, not URLs.",
|
|
46282
46386
|
"Claim already assigns and moves Status to In Progress, so do not also board move it.",
|
|
46283
46387
|
"Multiple refs are handled as a batch and return per-item results.",
|
|
46388
|
+
"To claim the whole board, use --all (with --force to take live lanes) instead of a hand-built ref list.",
|
|
46284
46389
|
"--check is the live gate read (it calls GitHub); --dry-run only echoes the parsed argv plan.",
|
|
46285
46390
|
// #5552: agents guess `oracle issue claim`; that route does not exist — board claim is the only write.
|
|
46286
46391
|
"Never run `oracle issue claim` \u2014 claims are board mutations; only `oracle board claim <ref>` is valid.",
|
|
@@ -60790,9 +60895,13 @@ async function checkDocsIndexAtHead(opts, deps) {
|
|
|
60790
60895
|
}
|
|
60791
60896
|
|
|
60792
60897
|
// src/pr-create-preparation.ts
|
|
60898
|
+
function rejectionReason(error) {
|
|
60899
|
+
const text = (error instanceof Error ? error.message : String(error)).trim().split("\n")[0] || "no error detail";
|
|
60900
|
+
return text.length > 300 ? `${text.slice(0, 300)}\u2026` : text;
|
|
60901
|
+
}
|
|
60793
60902
|
async function preparePrCreate(input, deps = createPreparationDeps(input)) {
|
|
60794
60903
|
const observations = [];
|
|
60795
|
-
const refs = await deps.candidate().catch(() => ({ findings: [
|
|
60904
|
+
const refs = await deps.candidate().catch((error) => ({ findings: [`candidate refs unavailable (${rejectionReason(error)}); verify the repository and pushed head/base before retrying`] }));
|
|
60796
60905
|
observations.push(...refs.findings.map((detail) => ({ check: "refs", state: "unavailable", detail })));
|
|
60797
60906
|
if (refs.candidate) observations.push({ check: "refs", state: "passed", detail: `candidate ${refs.candidate.headSha} against ${refs.candidate.baseSha}` });
|
|
60798
60907
|
const reads = await Promise.allSettled([
|
|
@@ -60803,10 +60912,10 @@ async function preparePrCreate(input, deps = createPreparationDeps(input)) {
|
|
|
60803
60912
|
const [claims, docs, policy] = reads;
|
|
60804
60913
|
if (claims.status === "fulfilled") {
|
|
60805
60914
|
observations.push(...claims.value.findings.length ? claims.value.findings.map((detail) => ({ check: "claims", state: "failed", detail })) : [{ check: "claims", state: "passed", detail: "issue and claim evidence read" }]);
|
|
60806
|
-
} else observations.push({ check: "claims", state: "unavailable", detail:
|
|
60807
|
-
if (docs.status === "rejected") observations.push({ check: "docs", state: "unavailable", detail:
|
|
60915
|
+
} else observations.push({ check: "claims", state: "unavailable", detail: `issue/claim evidence unavailable (${rejectionReason(claims.reason)}); retry the read` });
|
|
60916
|
+
if (docs.status === "rejected") observations.push({ check: "docs", state: "unavailable", detail: `committed docs observation unavailable (${rejectionReason(docs.reason)}); retry the read` });
|
|
60808
60917
|
else if (docs.value) observations.push({ check: "docs", state: docs.value.state, detail: [docs.value.detail, docs.value.fix].filter(Boolean).join(" \u2014 ") });
|
|
60809
|
-
if (policy.status === "rejected") observations.push({ check: "policy", state: "unavailable", detail:
|
|
60918
|
+
if (policy.status === "rejected") observations.push({ check: "policy", state: "unavailable", detail: `committed test-policy observation unavailable (${rejectionReason(policy.reason)}); check policy validity and fetch full candidate history before retrying` });
|
|
60810
60919
|
else if (policy.value) observations.push(policy.value);
|
|
60811
60920
|
const findings = observations.filter((item) => item.state === "failed" || item.state === "unavailable");
|
|
60812
60921
|
let attempted = false;
|
|
@@ -60938,7 +61047,12 @@ function createPreparationDeps(input, io = {}) {
|
|
|
60938
61047
|
return result("failed", `${refusal.error} \u2014 ${refusal.preparation.nextAction}`);
|
|
60939
61048
|
}
|
|
60940
61049
|
}
|
|
60941
|
-
|
|
61050
|
+
let policy;
|
|
61051
|
+
try {
|
|
61052
|
+
policy = runTestPolicy(candidate.root, { candidate: { head: candidate.headSha, base: candidate.baseSha } });
|
|
61053
|
+
} catch (error) {
|
|
61054
|
+
return result("unavailable", `committed test policy could not be evaluated: ${rejectionReason(error)} \u2014 fix test-policy.json if it is malformed, or run \`git fetch --unshallow\` if a history read failed`);
|
|
61055
|
+
}
|
|
60942
61056
|
return result(policy.ok ? "passed" : "failed", policy.ok ? `committed test policy passed at ${candidate.headSha}` : policy.findings.map((finding) => finding.detail).join("\n"));
|
|
60943
61057
|
}
|
|
60944
61058
|
};
|
|
@@ -64968,9 +65082,13 @@ function projectInfoOutcome(projectInfoSync) {
|
|
|
64968
65082
|
}
|
|
64969
65083
|
function buildReleaseVerdict(commandName, result, projectInfoSync) {
|
|
64970
65084
|
const alignmentPending = result.devRollForward?.status === "pr-pending" || result.rcAlignment?.status === "pr-pending";
|
|
65085
|
+
const deployOutcome = reduceFollowUpOutcomes([
|
|
65086
|
+
deployFollowUpOutcome(result.deployStatus),
|
|
65087
|
+
deployFollowUpOutcome(aggregateWorkflowRuns(result.workflowRuns ?? []))
|
|
65088
|
+
]);
|
|
64971
65089
|
const followUpStatus = deriveTrainFollowUpStatus({
|
|
64972
65090
|
projectInfo: projectInfoOutcome(projectInfoSync),
|
|
64973
|
-
deploy:
|
|
65091
|
+
deploy: deployOutcome,
|
|
64974
65092
|
rcRetirement: result.rcRetirement === "failed" ? result.rcRetirementCategory === "wait-timeout" ? "unresolved" : "failure" : "ok",
|
|
64975
65093
|
alignment: alignmentPending ? "unresolved" : "ok",
|
|
64976
65094
|
foldPort: "ok"
|
|
@@ -64990,9 +65108,14 @@ function buildReleaseVerdict(commandName, result, projectInfoSync) {
|
|
|
64990
65108
|
}
|
|
64991
65109
|
function buildResumeReleaseVerdict(result, projectInfoSync) {
|
|
64992
65110
|
const resumeStatus = resumeFollowUpOf(result.state);
|
|
65111
|
+
const deployOutcome = reduceFollowUpOutcomes([
|
|
65112
|
+
// No dispatch (e.g. an alignment-only resume) keeps the old 'ok' deploy dimension.
|
|
65113
|
+
deployFollowUpOutcome(result.dispatch?.deployStatus ?? "success"),
|
|
65114
|
+
deployFollowUpOutcome(aggregateWorkflowRuns(result.dispatch?.workflowRuns ?? []))
|
|
65115
|
+
]);
|
|
64993
65116
|
const followUpStatus = deriveTrainFollowUpStatus({
|
|
64994
65117
|
projectInfo: projectInfoOutcome(projectInfoSync),
|
|
64995
|
-
deploy:
|
|
65118
|
+
deploy: deployOutcome,
|
|
64996
65119
|
rcRetirement: "ok",
|
|
64997
65120
|
alignment: resumeStatus === "failed" ? "failure" : resumeStatus === "pending" ? "unresolved" : "ok",
|
|
64998
65121
|
foldPort: "ok"
|
package/package.json
CHANGED