@mutmutco/codex-plugin 4.5.226 → 4.5.227
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
|
@@ -16032,6 +16032,9 @@ function partitionByRequired(entries, requiredContexts) {
|
|
|
16032
16032
|
ignoredFailures.push(entry.name);
|
|
16033
16033
|
}
|
|
16034
16034
|
}
|
|
16035
|
+
if (relevant.length && [...requiredContexts].some((name) => !entries.some((entry) => entry.name === name))) {
|
|
16036
|
+
relevant.push("pending");
|
|
16037
|
+
}
|
|
16035
16038
|
return { relevant, ignoredFailures };
|
|
16036
16039
|
}
|
|
16037
16040
|
function parseNdjsonLines(stdout) {
|
|
@@ -27088,10 +27091,10 @@ var rollout_plan_default = {
|
|
|
27088
27091
|
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)."
|
|
27089
27092
|
},
|
|
27090
27093
|
baseline: {
|
|
27091
|
-
version: "4.5.
|
|
27092
|
-
tag: "v4.5.
|
|
27093
|
-
commit: "
|
|
27094
|
-
npm: "@mutmutco/cli@4.5.
|
|
27094
|
+
version: "4.5.227",
|
|
27095
|
+
tag: "v4.5.227",
|
|
27096
|
+
commit: "9498ff4da322",
|
|
27097
|
+
npm: "@mutmutco/cli@4.5.227"
|
|
27095
27098
|
},
|
|
27096
27099
|
exitCriterion: "fleet-n-of-n",
|
|
27097
27100
|
hubOnlyShortcut: "forbidden",
|
|
@@ -27108,14 +27111,14 @@ var rollout_plan_default = {
|
|
|
27108
27111
|
repo: "mutmutco/mmi-hub",
|
|
27109
27112
|
role: "canary",
|
|
27110
27113
|
schedule: "train",
|
|
27111
|
-
v3Target: "v4.5.
|
|
27114
|
+
v3Target: "v4.5.227"
|
|
27112
27115
|
}
|
|
27113
27116
|
],
|
|
27114
27117
|
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.",
|
|
27115
27118
|
rollback: {
|
|
27116
27119
|
independent: true,
|
|
27117
|
-
mechanism: "npm dist-tag latest -> 4.5.
|
|
27118
|
-
v3Target: "v4.5.
|
|
27120
|
+
mechanism: "npm dist-tag latest -> 4.5.227 and redeploy the Hub Lambda from tag v4.5.227 (9498ff4da322); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
27121
|
+
v3Target: "v4.5.227 (@mutmutco/cli@4.5.227, tag commit 9498ff4da322 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
27119
27122
|
}
|
|
27120
27123
|
},
|
|
27121
27124
|
{
|
|
@@ -49623,15 +49626,40 @@ var PR_LAND_STATE_READ_RETRIES = 3;
|
|
|
49623
49626
|
var PR_LAND_STATE_READ_DELAY_MS = 15e3;
|
|
49624
49627
|
var PR_LAND_MERGE_RETRY_DELAY_MS = 3e4;
|
|
49625
49628
|
async function mergeAutoWithTransientRetry(prNumber, repo, deps) {
|
|
49626
|
-
const
|
|
49627
|
-
|
|
49628
|
-
const ready = await deps.probeMergeReady(prNumber, repo).catch(() => ({ open: false, mergeable: false, checksPassing: false }));
|
|
49629
|
-
if (!ready.open || !ready.mergeable || !ready.checksPassing) return first;
|
|
49629
|
+
const now = deps.now ?? (() => Date.now());
|
|
49630
|
+
const deadline = now() + PR_LAND_ENQUEUE_TIMEOUT_MS;
|
|
49630
49631
|
const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
|
|
49631
|
-
await
|
|
49632
|
-
|
|
49633
|
-
|
|
49634
|
-
|
|
49632
|
+
const first = await deps.mergeAuto(prNumber, repo);
|
|
49633
|
+
let result = first;
|
|
49634
|
+
let transientRetried = false;
|
|
49635
|
+
while (result.mergeStatus === "failed") {
|
|
49636
|
+
const ready = await deps.probeMergeReady(prNumber, repo).catch(() => ({ open: false, mergeable: false, checksPassing: false, checksPending: false }));
|
|
49637
|
+
if (ready.open && ready.checksPending) {
|
|
49638
|
+
if (now() >= deadline) break;
|
|
49639
|
+
await sleep2(Math.min(PR_LAND_POLL_MS, deadline - now()));
|
|
49640
|
+
if (now() >= deadline) break;
|
|
49641
|
+
const checks = await deps.waitForChecks(prNumber, repo, deadline - now());
|
|
49642
|
+
if (checks.status !== "success" && checks.status !== "skipped") {
|
|
49643
|
+
return { mergeStatus: "failed", error: `${result.error ?? "merge failed"}
|
|
49644
|
+
registration wait: checks-wait ${checks.status}: ${checks.detail ?? checks.reason ?? "unknown"}` };
|
|
49645
|
+
}
|
|
49646
|
+
if (now() >= deadline) break;
|
|
49647
|
+
} else {
|
|
49648
|
+
if (!ready.open || !ready.mergeable || !ready.checksPassing || transientRetried) return result;
|
|
49649
|
+
transientRetried = true;
|
|
49650
|
+
if (now() >= deadline) break;
|
|
49651
|
+
await sleep2(Math.min(PR_LAND_MERGE_RETRY_DELAY_MS, deadline - now()));
|
|
49652
|
+
if (now() >= deadline) break;
|
|
49653
|
+
}
|
|
49654
|
+
const retried = await deps.mergeAuto(prNumber, repo);
|
|
49655
|
+
result = retried.mergeStatus !== "failed" ? retried : {
|
|
49656
|
+
mergeStatus: "failed",
|
|
49657
|
+
error: `merge retry after transient failure also failed: first attempt: ${first.error ?? "unknown error"}
|
|
49658
|
+
retry: ${retried.error ?? "unknown error"}`
|
|
49659
|
+
};
|
|
49660
|
+
}
|
|
49661
|
+
return result.mergeStatus === "failed" ? { ...result, error: `${result.error ?? "merge failed"}
|
|
49662
|
+
required-check registration did not settle within the enqueue deadline` } : result;
|
|
49635
49663
|
}
|
|
49636
49664
|
var AUTO_MERGE_CONFIRM_RETRIES = 3;
|
|
49637
49665
|
var AUTO_MERGE_CONFIRM_DELAY_MS = 15e3;
|
|
@@ -50025,15 +50053,15 @@ function invisibleTrailer(sha, line) {
|
|
|
50025
50053
|
return {
|
|
50026
50054
|
kind: "malformed-override-trailer",
|
|
50027
50055
|
paths: [],
|
|
50028
|
-
detail: `OVERRIDE TRAILER GIT CANNOT SEE \u2014 commit ${sha.slice(0, 8)}
|
|
50029
|
-
|
|
50030
|
-
|
|
50031
|
-
|
|
50032
|
-
|
|
50033
|
-
|
|
50034
|
-
|
|
50035
|
-
|
|
50036
|
-
|
|
50056
|
+
detail: `OVERRIDE TRAILER GIT CANNOT SEE \u2014 commit ${sha.slice(0, 8)}
|
|
50057
|
+
Recovery (trailer): create a LATER commit on this branch with \`${TRAILER_KEY}: <reason>\`
|
|
50058
|
+
in the final trailer block, separated from the body by a blank line. Use \`Key: value\` lines
|
|
50059
|
+
only in that block; indent continuation lines and keep prose such as \`Closes #123\` above it.
|
|
50060
|
+
Verify: \`git log -1 --format='%(trailers:key=${TRAILER_KEY})' HEAD\` must show the waiver.
|
|
50061
|
+
Then run \`mmi-cli tests policy --json --base origin/development\` from the selected worktree root
|
|
50062
|
+
to refresh its receipt before retrying the focused test. Changing location alone cannot fix this trailer.
|
|
50063
|
+
Rejected line: ${line}
|
|
50064
|
+
Git's trailer parser reports no waiver; a regex-only reason is not git-auditable (#3628).
|
|
50037
50065
|
The nearest waiver wins, and it supersedes this one \u2014 no force-push, no re-filed branch.`
|
|
50038
50066
|
};
|
|
50039
50067
|
}
|
|
@@ -50911,9 +50939,8 @@ function ghFailureReason(e) {
|
|
|
50911
50939
|
const err = e;
|
|
50912
50940
|
const head = String(err?.message ?? "").split("\n")[0]?.trim() ?? "";
|
|
50913
50941
|
const detail = `${err?.stderr ?? ""}
|
|
50914
|
-
${err?.stdout ?? ""}`.split("\n").map((line) => line.trim()).
|
|
50915
|
-
|
|
50916
|
-
return detail || head || "unknown error";
|
|
50942
|
+
${err?.stdout ?? ""}`.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).join("\n");
|
|
50943
|
+
return (detail && head ? `${head} \u2014 ${detail}` : detail || head || "unknown error").replace(/\x1b\[[0-9;]*m/g, "").replace(/\b(?:gh[pousr]_|github_pat_|AKIA|sk-)[\w-]+/g, "[redacted]").replace(/\b(authorization:\s*)(?:[^\n]*)/gi, "$1[redacted]");
|
|
50917
50944
|
}
|
|
50918
50945
|
var defaultGhMergeAutoIo = {
|
|
50919
50946
|
gh: async (args, timeoutMs) => (await execFileP("gh", args, { timeout: timeoutMs })).stdout
|
|
@@ -51066,13 +51093,7 @@ async function mergeAutoEnqueueWithBody(prNumber, args, method, io, bodyFile, me
|
|
|
51066
51093
|
error: stateRead.ok ? message2.split("\n")[0] : `could not confirm PR state after local branch cleanup warning: ${stateRead.error}`
|
|
51067
51094
|
};
|
|
51068
51095
|
}
|
|
51069
|
-
|
|
51070
|
-
return { mergeStatus: "failed", error: ghFailureReason(e) };
|
|
51071
|
-
}
|
|
51072
|
-
return {
|
|
51073
|
-
mergeStatus: "failed",
|
|
51074
|
-
error: `${ghFailureReason(e)} \u2014 auto-merge could not be armed yet (the PR's required checks have not registered); re-run the same --auto merge once they appear`
|
|
51075
|
-
};
|
|
51096
|
+
return { mergeStatus: "failed", error: ghFailureReason(e) };
|
|
51076
51097
|
}
|
|
51077
51098
|
return confirmEnqueueOutcome();
|
|
51078
51099
|
}
|
|
@@ -56773,9 +56794,10 @@ ${list}`);
|
|
|
56773
56794
|
},
|
|
56774
56795
|
fetchTrainAuthority: async (repo) => fetchTrainAuthority(repo, registryClientDeps(await loadConfig())),
|
|
56775
56796
|
resolveCiPolicy: (repo) => resolveRepoMergeCiPolicy(repo, ciAuditDeps()),
|
|
56776
|
-
waitForChecks: async (prNumber, repo) => {
|
|
56797
|
+
waitForChecks: async (prNumber, repo, timeoutMs) => {
|
|
56777
56798
|
const requiredContexts = await fetchRequiredCheckContexts(repo, "development").catch(() => null);
|
|
56778
56799
|
return waitForPrChecks({
|
|
56800
|
+
timeoutMs,
|
|
56779
56801
|
resolvePolicy: () => resolveRepoMergeCiPolicy(repo, ciAuditDeps()),
|
|
56780
56802
|
// #3024: REST-only wait loop — runPrLand's resolveRepo already guarantees `repo` is set.
|
|
56781
56803
|
pollChecks: () => pollRestPrChecks(prNumber, repo, void 0, requiredContexts),
|
|
@@ -56817,7 +56839,8 @@ ${list}`);
|
|
|
56817
56839
|
return {
|
|
56818
56840
|
open: snapshot?.state === "open",
|
|
56819
56841
|
mergeable: snapshot?.mergeable === "MERGEABLE",
|
|
56820
|
-
checksPassing: checks === "success"
|
|
56842
|
+
checksPassing: checks === "success",
|
|
56843
|
+
checksPending: checks === "pending" || checks === "failing" || checks === "no-checks-reported"
|
|
56821
56844
|
};
|
|
56822
56845
|
},
|
|
56823
56846
|
readPrState: async (prNumber, repo) => {
|
|
@@ -62732,19 +62755,32 @@ function ghExitCodeOf(error) {
|
|
|
62732
62755
|
}
|
|
62733
62756
|
async function runBrokerGh(run, spec, write = (chunk) => process.stdout.write(chunk)) {
|
|
62734
62757
|
const env = brokerChildEnv();
|
|
62758
|
+
let output;
|
|
62735
62759
|
try {
|
|
62736
|
-
|
|
62737
|
-
return 0;
|
|
62760
|
+
output = redactPrivateCredential(await run(spec.args, { timeoutMs: spec.timeoutMs, env }), env);
|
|
62738
62761
|
} catch (e) {
|
|
62739
62762
|
const err = e;
|
|
62740
62763
|
if (typeof err.stdout === "string" && err.stdout) process.stderr.write(redactPrivateCredential(err.stdout, env));
|
|
62741
62764
|
const code = ghExitCodeOf(e);
|
|
62742
62765
|
if (code !== null) {
|
|
62743
62766
|
if (typeof err.stderr === "string" && err.stderr) process.stderr.write(redactPrivateCredential(err.stderr, env));
|
|
62767
|
+
process.stderr.write(`read: gh ${spec.args.slice(0, 2).join(" ")} failed on the App pool (exit ${code}) \u2014 inspect stderr; retry the same typed read and only parse stdout after success
|
|
62768
|
+
`);
|
|
62744
62769
|
return code;
|
|
62745
62770
|
}
|
|
62746
62771
|
throw new Error(`read: gh ${spec.args.slice(0, 2).join(" ")} did not finish on the App pool (${err.message ?? e}) \u2014 re-run the same command to refresh (same custody); reads never fall back to personal`);
|
|
62747
62772
|
}
|
|
62773
|
+
if (spec.args.includes("--json")) {
|
|
62774
|
+
try {
|
|
62775
|
+
JSON.parse(output);
|
|
62776
|
+
} catch {
|
|
62777
|
+
process.stderr.write(`read: gh ${spec.args.slice(0, 2).join(" ")} returned ${output.trim() ? "invalid" : "empty"} JSON on the App pool \u2014 retry the same typed read; only parse stdout after success
|
|
62778
|
+
`);
|
|
62779
|
+
return 1;
|
|
62780
|
+
}
|
|
62781
|
+
}
|
|
62782
|
+
write(output);
|
|
62783
|
+
return 0;
|
|
62748
62784
|
}
|
|
62749
62785
|
async function runBrokerRestChecks(deps, spec, resolveRepo2) {
|
|
62750
62786
|
const repo = await resolveRepo2(spec.repo);
|
package/cli/package.json
CHANGED
package/package.json
CHANGED
package/skills/hotfix/SKILL.md
CHANGED
|
@@ -29,7 +29,15 @@ mmi-cli devops train doctor --lane hotfix --heal --json
|
|
|
29
29
|
|
|
30
30
|
Parse stdout only; never `2>&1`. `ready` false → stop and quote every blocker's `code` and `remedy`; an unread origin is never green. Findings carry `code`, `severity`, `source`, `remedy`, `anchor`.
|
|
31
31
|
|
|
32
|
-
Piping `mmi-cli devops api get` into a JSON parser: a refusal exits 1 with empty stdout, so guard both
|
|
32
|
+
Piping `mmi-cli devops api get` into a JSON parser: a refusal exits 1 with empty stdout, so guard both exit status and nonempty output. For one job's runner and timestamps, read the allowlisted `repos/{owner}/{repo}/actions/runs/{id}/jobs` and filter `jobs[]` by `id`; `actions/jobs/{id}` is refused (only its `/logs`, with `--out`). Do not retry that refused endpoint or broaden the allowlist.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# Set repo, run_id and job_id to the target repository, run and job.
|
|
36
|
+
out=$(MMI_ACTOR=app mmi-cli devops api get "repos/$repo/actions/runs/$run_id/jobs") &&
|
|
37
|
+
[ -n "$out" ] && printf '%s' "$out" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{const j=JSON.parse(s).jobs.find(j=>String(j.id)===process.argv[1]);if(!j)throw new Error("Job not found in run jobs");console.log(JSON.stringify({runner_name:j.runner_name,started_at:j.started_at,completed_at:j.completed_at,conclusion:j.conclusion}))})' "$job_id"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Do not parse stdout after a failing read, suppress its stderr, or merge stderr into the JSON.
|
|
33
41
|
|
|
34
42
|
## Apply
|
|
35
43
|
|
|
@@ -18,11 +18,15 @@ Use host worktrees and MMI cleanup; no personal Jerv is required. At handoff, st
|
|
|
18
18
|
session; never copy them forward from an earlier handoff, even labelled unverified (#9039).
|
|
19
19
|
Report skill misfires to Hub
|
|
20
20
|
with `mmi-cli learning skill-lesson --skill <name> --title <summary> --body-file <path>`.
|
|
21
|
-
Under the App pool,
|
|
21
|
+
Under the App pool, on raw `gh`
|
|
22
22
|
`--jq`/`-q`/`--template`/`--web` are refused: use
|
|
23
23
|
`MMI_ACTOR=app mmi-cli devops read issue view <id> --repo <owner/repo> --json comments`
|
|
24
24
|
(or `pr`/`run view` with their fields); filter returned JSON locally, e.g. by adding
|
|
25
25
|
`--jq '<expr>'` to that typed read (runs the local jq, never inside gh).
|
|
26
|
+
For the last N comments (here, 3), use the typed JSON read with local filtering, not raw gh formatters:
|
|
27
|
+
```bash
|
|
28
|
+
MMI_ACTOR=app mmi-cli devops read issue view <id> --repo <owner/repo> --json comments --jq '.comments[-3:][] | {createdAt, body}'
|
|
29
|
+
```
|
|
26
30
|
For run logs, never native `gh run view --log`/`--log-failed`: use
|
|
27
31
|
`MMI_ACTOR=app mmi-cli devops read run log <runId> --repo <owner/repo> [--failed | --job <name>] [--tail N]`.
|
|
28
32
|
A refused command writes no output, so a `> file` redirect leaves it empty or missing: read the
|