@mutmutco/codex-plugin 4.6.17 → 4.6.18

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,6 +1,6 @@
1
1
  {
2
2
  "name": "mmi",
3
- "version": "4.6.17",
3
+ "version": "4.6.18",
4
4
  "mmiCompat": "4.x",
5
5
  "description": "MMI workflow skills and org gates delivery.",
6
6
  "author": {
package/cli/dist/main.cjs CHANGED
@@ -2998,9 +2998,9 @@ ${indent}`) + "'";
2998
2998
  return quotedString(value, ctx);
2999
2999
  }
3000
3000
  const indent = ctx.indent || (ctx.forceBlockIndent || containsDocumentMarker(value) ? " " : "");
3001
- const literal2 = blockQuote === "literal" ? true : blockQuote === "folded" || type === Scalar.Scalar.BLOCK_FOLDED ? false : type === Scalar.Scalar.BLOCK_LITERAL ? true : !lineLengthOverLimit(value, lineWidth, indent.length);
3001
+ const literal = blockQuote === "literal" ? true : blockQuote === "folded" || type === Scalar.Scalar.BLOCK_FOLDED ? false : type === Scalar.Scalar.BLOCK_LITERAL ? true : !lineLengthOverLimit(value, lineWidth, indent.length);
3002
3002
  if (!value)
3003
- return literal2 ? "|\n" : ">\n";
3003
+ return literal ? "|\n" : ">\n";
3004
3004
  let chomp;
3005
3005
  let endStart;
3006
3006
  for (endStart = value.length; endStart > 0; --endStart) {
@@ -3049,7 +3049,7 @@ ${indent}`) + "'";
3049
3049
  if (onComment)
3050
3050
  onComment();
3051
3051
  }
3052
- if (!literal2) {
3052
+ if (!literal) {
3053
3053
  const foldedValue = value.replace(/\n+/g, "\n$&").replace(/(?:^|\n)([\t ].*)(?:([\n\t ]*)\n(?![\n\t ]))?/g, "$1$2").replace(/\n+/g, `$&${indent}`);
3054
3054
  let literalFallback = false;
3055
3055
  const foldOptions = getFoldOptions(ctx, true);
@@ -13637,13 +13637,16 @@ function isExemptPorcelainLine(status, path2) {
13637
13637
  return isLiveSessionPath(path2);
13638
13638
  }
13639
13639
  function porcelainBlockingPaths(porcelain) {
13640
- const blocking = [];
13640
+ return porcelainBlockingEntries(porcelain).map((e) => e.path);
13641
+ }
13642
+ function porcelainBlockingEntries(porcelain) {
13643
+ const entries = [];
13641
13644
  for (const line of porcelain.split("\n")) {
13642
13645
  if (!line.trim()) continue;
13643
13646
  const { status, path: path2 } = splitPorcelainLine(line);
13644
- if (path2 !== "" && !isExemptPorcelainLine(status, path2)) blocking.push(path2);
13647
+ if (path2 !== "" && !isExemptPorcelainLine(status, path2)) entries.push({ status, path: path2 });
13645
13648
  }
13646
- return blocking;
13649
+ return entries;
13647
13650
  }
13648
13651
  function porcelainHasBlockingChanges(porcelain) {
13649
13652
  return porcelainBlockingPaths(porcelain).length > 0;
@@ -17607,6 +17610,279 @@ var REGISTRY_FETCH_TIMEOUT_MS = 8e3;
17607
17610
  init_fetch_retry();
17608
17611
  init_client_version();
17609
17612
 
17613
+ // src/hub-release-ci-authority.ts
17614
+ var HUB_RELEASE_REPO = "mutmutco/MMI-Hub";
17615
+ var HUB_RELEASE_WORKFLOW = ".github/workflows/hub-release.yml";
17616
+ var HUB_RELEASE_REF = "refs/heads/development";
17617
+ var WORKFLOW_REF = `${HUB_RELEASE_REPO}/${HUB_RELEASE_WORKFLOW}@${HUB_RELEASE_REF}`;
17618
+ var ID = /^[1-9][0-9]*$/;
17619
+ function hubReleaseCiRun(env) {
17620
+ if (env.MMI_HUB_RELEASE_CI !== "1" || env.GITHUB_ACTIONS !== "true") return null;
17621
+ if (env.GITHUB_REPOSITORY !== HUB_RELEASE_REPO || env.GITHUB_EVENT_NAME !== "workflow_dispatch") return null;
17622
+ if (env.GITHUB_WORKFLOW_REF !== WORKFLOW_REF || env.GITHUB_REF !== HUB_RELEASE_REF) return null;
17623
+ const runId = env.GITHUB_RUN_ID ?? "";
17624
+ const runAttempt2 = env.GITHUB_RUN_ATTEMPT ?? "";
17625
+ if (!ID.test(runId) || !ID.test(runAttempt2)) return null;
17626
+ return { runId: Number(runId), runAttempt: Number(runAttempt2) };
17627
+ }
17628
+
17629
+ // ../shared/runner-fallback.ts
17630
+ var HOSTED_RUNNER_LABEL_RE = /\b(?:ubuntu|windows|macos)-(?:latest|\d[\w.-]*)\b/i;
17631
+ var STANDARD_HOSTED_LABEL_RE = /^(?:ubuntu-(?:latest|24\.04|22\.04|24\.04-arm|22\.04-arm)|windows-(?:latest|2025|2022|11-arm)|macos-(?:latest|\d+|\d+-intel))$/i;
17632
+ var BLESSED_RUNNER_CHOICE_SHA = "cf35db6cdf053746bc5b2cd75f241a7d93cf7f48";
17633
+ function classifyRunnerCost(selector) {
17634
+ const raw = selector.replace(/\s+#.*$/gm, "").trim();
17635
+ if (STANDARD_HOSTED_LABEL_RE.test(raw.replace(/^(['"])(.*)\1$/, "$2"))) return "standard-hosted";
17636
+ if (HOSTED_RUNNER_LABEL_RE.test(raw)) return "hosted";
17637
+ const json = /^\$\{\{\s*fromJSON\('([^']+)'\)\s*\}\}$/.exec(raw);
17638
+ if (json) {
17639
+ try {
17640
+ const labels2 = JSON.parse(json[1]);
17641
+ return Array.isArray(labels2) && labels2.length > 0 && labels2.every((l) => typeof l === "string" && /^[\w-]+$/.test(l)) && labels2.some((l) => l.toLowerCase() === "self-hosted") ? "self-hosted" : "unknown";
17642
+ } catch {
17643
+ return "unknown";
17644
+ }
17645
+ }
17646
+ if (raw.includes("${{")) return "unknown";
17647
+ const labels = /^\[[^\[\]]+\]$/.test(raw) ? raw.slice(1, -1).split(",").map((s) => s.trim().replace(/^(['"])(.*)\1$/, "$2")) : /^(?:-\s*[^\n]+\s*)+$/.test(raw) ? raw.split("\n").map((s) => s.trim().replace(/^-\s*/, "").replace(/^(['"])(.*)\1$/, "$2")) : [raw.replace(/^(['"])(.*)\1$/, "$2")];
17648
+ return labels.every((l) => /^[\w-]+$/.test(l)) && labels.some((l) => l.toLowerCase() === "self-hosted") ? "self-hosted" : "unknown";
17649
+ }
17650
+ var STANDARD_HOSTED_ALLOWLIST = [
17651
+ ["mutmutco/MMI-Hub", ".github/workflows/launcher-build.yml", "build", "windows-latest"],
17652
+ ["mutmutco/Jerv-JervCode", ".github/workflows/release-archives.yml", "release-platform-arm64", "windows-11-arm"]
17653
+ ];
17654
+ var HOSTED_SPILL_RE = /^\$\{\{\s*(?:matrix\.[\w-]+\s*==\s*'[\w-]+'\s*&&\s*(?:'(?<leg>[^']+)'|fromJSON\(needs\.(?<legDecider>[\w-]+)\.outputs\.(?<legOutput>\w+_runs_on)\s*\|\|\s*(?<legOwned>'[^']+')\))\s*\|\|\s*)?fromJSON\(needs\.(?<decider>[\w-]+)\.outputs\.(?<output>\w+_runs_on)\s*\|\|\s*(?<owned>'[^']+')\)\s*\}\}$/;
17655
+ var RUNNER_CHOICE_ACTION = "mutmutco/MMI-Hub/.github/actions/runner-choice";
17656
+ function runnerChoiceProduces(job, decider, output, source) {
17657
+ const needs = job.needs;
17658
+ if (!(needs === decider || Array.isArray(needs) && needs.includes(decider))) return false;
17659
+ const producer = source.jobs.get(decider);
17660
+ if (!record(producer) || !record(producer.outputs) || !Array.isArray(producer.steps)) return false;
17661
+ const step = /^\$\{\{\s*steps\.([\w-]+)\.outputs\.runs-on\s*\}\}$/.exec(String(producer.outputs[output] ?? ""))?.[1];
17662
+ const steps = producer.steps.filter((s) => record(s) && s.id === step);
17663
+ if (steps.length !== 1 || !record(steps[0])) return false;
17664
+ const { uses } = steps[0], inputs = steps[0].with ?? {};
17665
+ const pinned = /^[0-9a-f]{40}$/.test(source.blessedSha) && uses === `${RUNNER_CHOICE_ACTION}@${source.blessedSha}`;
17666
+ if (!pinned && !(source.repo === "mutmutco/MMI-Hub" && uses === "./.github/actions/runner-choice")) return false;
17667
+ const hosted = record(inputs) ? inputs.hosted ?? "ubuntu-latest" : void 0;
17668
+ return typeof hosted === "string" && STANDARD_HOSTED_LABEL_RE.test(hosted);
17669
+ }
17670
+ function standardHostedLabelFor(repo, path2, jobId) {
17671
+ if (repo === "mutmutco/Jerv-Hub") return ["ubuntu-latest", "windows-latest", "macos-14"];
17672
+ return STANDARD_HOSTED_ALLOWLIST.find(([r, p, j]) => r === repo && p === path2 && j === jobId)?.[3];
17673
+ }
17674
+ var record = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
17675
+ function classifyJobRunnerCost(job, allowance2, source) {
17676
+ if (!record(job) || !Object.hasOwn(job, "runs-on")) return "unknown";
17677
+ const selector = job["runs-on"];
17678
+ const raw = typeof selector === "string" ? selector.trim() : Array.isArray(selector) && selector.length && selector.every((label) => typeof label === "string") ? JSON.stringify(selector) : "";
17679
+ if (!raw) return "unknown";
17680
+ const approved = (value) => Array.isArray(allowance2) && allowance2.includes(value);
17681
+ if (approved(raw) || allowance2 === "windows-11-arm" && raw === allowance2) return "standard-hosted";
17682
+ const fallback = /^\$\{\{\s*vars\.MMI_RUNNER_FALLBACK\s*==\s*'hosted'\s*&&\s*'([^']+)'\s*\|\|\s*(fromJSON\('[^']+'\))\s*\}\}$/.exec(raw);
17683
+ if (fallback && approved(fallback[1]) && classifyRunnerCost("${{ " + fallback[2] + " }}") === "self-hosted") return "standard-hosted";
17684
+ const layerZero = /^\$\{\{\s*vars\.[\w-]+\s*==\s*'off'\s*&&\s*(fromJSON\('[^']+'\))\s*\|\|\s*'([^']+)'\s*\}\}$/.exec(raw);
17685
+ if (layerZero && classifyRunnerCost("${{ " + layerZero[1] + " }}") === "self-hosted" && STANDARD_HOSTED_LABEL_RE.test(layerZero[2])) return "standard-hosted";
17686
+ const spill = HOSTED_SPILL_RE.exec(raw)?.groups;
17687
+ const legOk = (g) => g.legDecider ? classifyRunnerCost(`\${{ fromJSON(${g.legOwned}) }}`) === "self-hosted" && Boolean(source) && runnerChoiceProduces(job, g.legDecider, g.legOutput, source) : !g.leg || STANDARD_HOSTED_LABEL_RE.test(g.leg);
17688
+ if (spill && source && legOk(spill) && classifyRunnerCost(`\${{ fromJSON(${spill.owned}) }}`) === "self-hosted" && runnerChoiceProduces(job, spill.decider, spill.output, source)) return "standard-hosted";
17689
+ const direct = classifyRunnerCost(raw);
17690
+ const matrix = /^\$\{\{\s*matrix\.([\w-]+)(?:\.([\w-]+))?\s*\}\}$/.exec(raw);
17691
+ if (!matrix || direct === "hosted") return direct;
17692
+ if (!Object.hasOwn(job, "strategy") || !record(job.strategy) || !Object.hasOwn(job.strategy, "matrix") || !record(job.strategy.matrix)) return "unknown";
17693
+ const axes = job.strategy.matrix;
17694
+ if (Object.hasOwn(axes, "exclude")) return "unknown";
17695
+ let alternatives;
17696
+ if (Object.hasOwn(axes, "include")) {
17697
+ if (Object.keys(axes).length !== 1 || !Array.isArray(axes.include) || !axes.include.length) return "unknown";
17698
+ alternatives = axes.include.map((row) => record(row) && Object.hasOwn(row, matrix[1]) ? row[matrix[1]] : void 0);
17699
+ } else {
17700
+ const axis = axes[matrix[1]];
17701
+ if (!Object.hasOwn(axes, matrix[1]) || !Array.isArray(axis) || !axis.length) return "unknown";
17702
+ alternatives = axis;
17703
+ }
17704
+ if (matrix[2]) alternatives = alternatives.map((row) => record(row) && Object.hasOwn(row, matrix[2]) ? row[matrix[2]] : void 0);
17705
+ const costs = alternatives.map((value) => {
17706
+ if (typeof value === "string") {
17707
+ if (value.includes("${{")) return "unknown";
17708
+ if (approved(value) || !matrix[2] && allowance2 === "windows-latest" && value === allowance2) return "standard-hosted";
17709
+ return classifyRunnerCost(value);
17710
+ }
17711
+ if (!Array.isArray(value) || !value.length || !value.every((label) => typeof label === "string" && /^[\w-]+$/.test(label))) return "unknown";
17712
+ return classifyRunnerCost(JSON.stringify(value));
17713
+ });
17714
+ if (costs.includes("hosted")) return "hosted";
17715
+ if (costs.includes("unknown")) return "unknown";
17716
+ return costs.includes("standard-hosted") ? "standard-hosted" : "self-hosted";
17717
+ }
17718
+
17719
+ // ../shared/github-contents.ts
17720
+ function decodeGitHubContents(content) {
17721
+ return Buffer.from(content, "base64").toString("utf8");
17722
+ }
17723
+ function encodeGitHubContents(content) {
17724
+ return Buffer.from(content, "utf8").toString("base64");
17725
+ }
17726
+
17727
+ // ../shared/workflow-runner-source.ts
17728
+ var WORKFLOW_PATH = /^\.github\/workflows\/[\w.-]+\.ya?ml$/;
17729
+ var REPO = /^[A-Za-z0-9][A-Za-z0-9-]*\/[A-Za-z0-9_][\w.-]*$/;
17730
+ var REF = /^[\w./-]+$/;
17731
+ var MAX_FILES = 64;
17732
+ var MAX_DEPTH = 8;
17733
+ var WorkflowSourceError = class extends Error {
17734
+ /** Value-free category (safeReadCategory vocabulary) — routes/logs carry it, never a location or body. */
17735
+ category;
17736
+ constructor(location, category) {
17737
+ super(`${location}: ${category}`);
17738
+ this.category = category;
17739
+ }
17740
+ };
17741
+ function safeReadCategory(error) {
17742
+ const e = error;
17743
+ if (e?.rateLimited === true) return "rate-limited";
17744
+ if (typeof e?.status === "number" && Number.isInteger(e.status) && e.status >= 400 && e.status <= 599) return `HTTP ${e.status}`;
17745
+ if (e?.name === "TimeoutError" || e?.name === "AbortError" || e?.code === "ETIMEDOUT") return "read-timeout";
17746
+ if (e?.code === "ENOENT") return "reader-unavailable";
17747
+ if (e?.code === "ENOBUFS" || e?.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") return "reader-output-limit";
17748
+ if (error instanceof SyntaxError) return "invalid-json";
17749
+ return "read-failed";
17750
+ }
17751
+ function locationOf(file) {
17752
+ return `${file.repo}/${file.path}${file.ref ? `@${file.ref}` : ""}`;
17753
+ }
17754
+ function contentsPath(file) {
17755
+ if (!REPO.test(file.repo) || !(WORKFLOW_PATH.test(file.path) || file.path === ".github/workflows") || file.ref !== void 0 && !REF.test(file.ref)) {
17756
+ throw new WorkflowSourceError("workflow source", "invalid-location");
17757
+ }
17758
+ return `repos/${file.repo}/contents/${file.path}${file.ref ? `?ref=${encodeURIComponent(file.ref)}` : ""}`;
17759
+ }
17760
+ async function readSource(read, file) {
17761
+ const endpoint = contentsPath(file);
17762
+ try {
17763
+ return await read(endpoint);
17764
+ } catch (e) {
17765
+ throw new WorkflowSourceError(locationOf(file), safeReadCategory(e));
17766
+ }
17767
+ }
17768
+ async function readBody(read, file) {
17769
+ const value = await readSource(read, file);
17770
+ if (!value || value.encoding !== "base64" || typeof value.content !== "string" || !value.content.trim() || value.content.length > 15e5 || !/^[A-Za-z0-9+/=\r\n]+$/.test(value.content)) {
17771
+ throw new WorkflowSourceError(locationOf(file), "invalid-content");
17772
+ }
17773
+ return { ...file, body: decodeGitHubContents(value.content) };
17774
+ }
17775
+ function resolveCallee(caller, uses) {
17776
+ if (uses.startsWith("./") && WORKFLOW_PATH.test(uses.slice(2))) return { repo: caller.repo, path: uses.slice(2), ref: caller.ref };
17777
+ const remote = /^([\w.-]+\/[\w.-]+)\/(\.github\/workflows\/[\w.-]+\.ya?ml)@([\w./-]+)$/.exec(uses);
17778
+ return remote ? { repo: remote[1], path: remote[2], ref: remote[3] } : void 0;
17779
+ }
17780
+ async function readRunnerWorkflowClosure(parse2, read, repo, ref, roots, bounds) {
17781
+ let totalBodyBytes = 0;
17782
+ const readBounded = async (file) => {
17783
+ const child2 = await readBody(read, file);
17784
+ totalBodyBytes += Buffer.byteLength(child2.body, "utf8");
17785
+ if (bounds?.maxTotalBodyBytes !== void 0 && totalBodyBytes > bounds.maxTotalBodyBytes) {
17786
+ throw new WorkflowSourceError(locationOf(child2), "source-read-limit");
17787
+ }
17788
+ return child2;
17789
+ };
17790
+ let files;
17791
+ if (roots) {
17792
+ files = roots.map((file) => ({ ...file, repo, ref }));
17793
+ } else {
17794
+ const listing = await readSource(read, { repo, ref, path: ".github/workflows" });
17795
+ if (!Array.isArray(listing)) throw new WorkflowSourceError(`${repo}/.github/workflows`, "invalid-listing");
17796
+ const paths = listing.filter((f) => f?.type === "file" && /\.ya?ml$/i.test(f?.path ?? "")).map((f) => f.path);
17797
+ if (!paths.length || paths.length > MAX_FILES) throw new WorkflowSourceError(`${repo}/.github/workflows`, "empty-or-oversized-listing");
17798
+ files = [];
17799
+ for (const path2 of paths) files.push(await readBounded({ repo, ref, path: path2 }));
17800
+ }
17801
+ if (!files.length || files.length > MAX_FILES) throw new WorkflowSourceError("workflow source", "empty-or-oversized-listing");
17802
+ for (const file of files) contentsPath(file);
17803
+ const byLocation = new Map(files.map((file) => [locationOf(file), file]));
17804
+ const active = /* @__PURE__ */ new Set();
17805
+ const completed = /* @__PURE__ */ new Set();
17806
+ async function visit2(file, depth) {
17807
+ const key = locationOf(file);
17808
+ if (active.has(key)) throw new WorkflowSourceError(key, "reusable-cycle");
17809
+ if (depth > MAX_DEPTH) throw new WorkflowSourceError(key, "reusable-depth-limit");
17810
+ if (completed.has(key)) return;
17811
+ active.add(key);
17812
+ const jobs = parse2(file.body);
17813
+ if (!jobs) throw new WorkflowSourceError(key, "invalid-workflow-yaml");
17814
+ for (const job of jobs.values()) {
17815
+ if (!Object.hasOwn(job, "uses")) continue;
17816
+ const uses = job.uses;
17817
+ if (typeof uses !== "string" || Object.hasOwn(job, "runs-on")) throw new WorkflowSourceError(key, "unresolved-reusable-ref");
17818
+ const target = resolveCallee(file, uses);
17819
+ if (!target) throw new WorkflowSourceError(key, "unresolved-reusable-ref");
17820
+ if (bounds?.calleeAllowed && !bounds.calleeAllowed(target)) throw new WorkflowSourceError(key, "unauthorized-callee");
17821
+ const targetKey = locationOf(target);
17822
+ let child2 = byLocation.get(targetKey);
17823
+ if (!child2) {
17824
+ if (byLocation.size >= MAX_FILES) throw new WorkflowSourceError(key, "workflow-read-limit");
17825
+ child2 = await readBounded(target);
17826
+ byLocation.set(targetKey, child2);
17827
+ }
17828
+ await visit2(child2, depth + 1);
17829
+ }
17830
+ active.delete(key);
17831
+ completed.add(key);
17832
+ }
17833
+ for (const file of files) await visit2(file, 0);
17834
+ return [...byLocation.values()];
17835
+ }
17836
+ function classifyRunnerWorkflows(parse2, files) {
17837
+ const known = new Map(files.map((file) => [locationOf(file), file]));
17838
+ const parsed = new Map(files.map((file) => [locationOf(file), parse2(file.body)]));
17839
+ const resolvedCache = /* @__PURE__ */ new Map();
17840
+ function resolved(file, active) {
17841
+ const key = locationOf(file);
17842
+ if (active.has(key)) return false;
17843
+ if (resolvedCache.has(key)) return resolvedCache.get(key);
17844
+ const next = new Set(active).add(key);
17845
+ const jobs = parsed.get(key);
17846
+ const result = Boolean(jobs && [...jobs.values()].every((job) => {
17847
+ if (!Object.hasOwn(job, "uses")) return true;
17848
+ const uses = job.uses;
17849
+ if (typeof uses !== "string" || Object.hasOwn(job, "runs-on")) return false;
17850
+ const target = resolveCallee(file, uses);
17851
+ const child2 = target && known.get(locationOf(target));
17852
+ return Boolean(child2 && resolved(child2, next));
17853
+ }));
17854
+ resolvedCache.set(key, result);
17855
+ return result;
17856
+ }
17857
+ return files.flatMap((file) => {
17858
+ const jobs = parsed.get(locationOf(file));
17859
+ if (!jobs) return [{ repo: file.repo, path: file.path, job: "(invalid YAML)", cost: "unknown", callee: false }];
17860
+ return [...jobs].flatMap(([id, job]) => {
17861
+ const hasCallee = Object.hasOwn(job, "uses");
17862
+ const uses = hasCallee && typeof job.uses === "string" && !Object.hasOwn(job, "runs-on") ? job.uses : void 0;
17863
+ const target = uses && resolveCallee(file, uses);
17864
+ if (target && known.has(locationOf(target)) && resolved(file, /* @__PURE__ */ new Set())) return [];
17865
+ const cost = hasCallee ? "unknown" : classifyJobRunnerCost(job, standardHostedLabelFor(file.repo, file.path, id), { repo: file.repo, jobs, blessedSha: BLESSED_RUNNER_CHOICE_SHA });
17866
+ return [{ repo: file.repo, path: file.path, job: id, cost, callee: hasCallee }];
17867
+ });
17868
+ });
17869
+ }
17870
+ function runnerWorkflowFromContents(value) {
17871
+ const file = value;
17872
+ if (!file || typeof file.repo !== "string" || typeof file.path !== "string" || file.ref !== void 0 && typeof file.ref !== "string") {
17873
+ throw new WorkflowSourceError("workflow source", "invalid-content");
17874
+ }
17875
+ if (!REPO.test(file.repo) || !WORKFLOW_PATH.test(file.path) || file.ref !== void 0 && !REF.test(file.ref)) {
17876
+ throw new WorkflowSourceError("workflow source", "invalid-content");
17877
+ }
17878
+ if (file.encoding !== "base64" || typeof file.content !== "string" || !file.content.trim() || file.content.length > 15e5 || !/^[A-Za-z0-9+/=\r\n]+$/.test(file.content)) {
17879
+ throw new WorkflowSourceError("workflow source", "invalid-content");
17880
+ }
17881
+ const body = decodeGitHubContents(file.content);
17882
+ if (!body.trim()) throw new WorkflowSourceError("workflow source", "invalid-content");
17883
+ return { repo: file.repo, path: file.path, ref: file.ref, body };
17884
+ }
17885
+
17610
17886
  // ../shared/schedule-inventory.ts
17611
17887
  function classifyLlmValue(raw) {
17612
17888
  const value = raw.trim().toLowerCase();
@@ -18669,6 +18945,23 @@ async function tenantDeploy(payload, deps) {
18669
18945
  async function releaseAnnouncement(payload, deps) {
18670
18946
  return postJson("/release-announcement", payload, deps, "POST", { noRetry: true, timeoutMs: 3e4 });
18671
18947
  }
18948
+ async function trainWorkflowSourceClosure(repo, deps) {
18949
+ const verdict = await fetchTrainAuthority(repo, deps);
18950
+ if (!verdict.ok) throw new WorkflowSourceError("workflow source", "authority-unverified");
18951
+ const delegatedTrain = verdict.authority.train === true && verdict.authority.authenticatedDelegation === true;
18952
+ if (!delegatedTrain || repo.toLowerCase() === HUB_RELEASE_REPO.toLowerCase()) return void 0;
18953
+ const SAFE_CHECK = /^[A-Za-z0-9 -]{1,64}$/;
18954
+ const res = await postJson("/train-workflow-source", { repo }, deps, "POST", { noRetry: true, timeoutMs: 3e4 });
18955
+ if (res.status === 422) {
18956
+ const body2 = res.body;
18957
+ const check = typeof body2?.check === "string" && SAFE_CHECK.test(body2.check) ? body2.check : "read-failed";
18958
+ throw new WorkflowSourceError("workflow source", check);
18959
+ }
18960
+ if (!res.ok || res.status !== 200) throw new WorkflowSourceError("workflow source", "source-endpoint-unavailable");
18961
+ const body = res.body;
18962
+ if (!body || !Array.isArray(body.files)) throw new WorkflowSourceError("workflow source", "invalid-response");
18963
+ return body.files.map(runnerWorkflowFromContents);
18964
+ }
18672
18965
  async function actionsCanary(payload, deps) {
18673
18966
  return postJson("/actions-canary", payload, deps, "POST", { noRetry: true });
18674
18967
  }
@@ -24389,14 +24682,6 @@ async function archiveClosedDoneBoardItems(options, deps = {}) {
24389
24682
  return result;
24390
24683
  }
24391
24684
 
24392
- // src/github-contents.ts
24393
- function decodeGitHubContents(content) {
24394
- return Buffer.from(content, "base64").toString("utf8");
24395
- }
24396
- function encodeGitHubContents(content) {
24397
- return Buffer.from(content, "utf8").toString("base64");
24398
- }
24399
-
24400
24685
  // src/bootstrap-ruleset.ts
24401
24686
  var PRODUCT_RULESET_NAME = "mmi-product-required-checks";
24402
24687
  var PRODUCT_PENDING_RULESET_PREFIX = "mmi-product-required-checks-pending-";
@@ -25538,7 +25823,6 @@ var BLESSED_RUNNER_CHECK_POLICY_SHA = "67bb2012710a96d21be825c29a5d5c2b4b8001e2"
25538
25823
  var BLESSED_KIT_AGENT_FILES_SHA = "e976df208d891b215844386c1cac0f161946b6fa";
25539
25824
  var BLESSED_JEV_FAILURES_SHA = "d5278c6133318b33727483ddfdfddc8f841e3ef6";
25540
25825
  var BLESSED_VERDICT_RECEIPT_SHA = "";
25541
- var BLESSED_RUNNER_CHOICE_SHA = "cf35db6cdf053746bc5b2cd75f241a7d93cf7f48";
25542
25826
  var BLESSED_RUNBOOK_GUARD_SHA = "e661e0aedc0e58b73c738bf6cdfeac65e5914584";
25543
25827
  var REMOTE_USE = /^mutmutco\/MMI-Hub\/\.github\/actions\/run-with-budget@(\S+)$/;
25544
25828
  var LOCAL_USE = "./.github/actions/run-with-budget";
@@ -25705,9 +25989,9 @@ function checkGateBudget(files, opts = {}) {
25705
25989
  if (!rawMs) {
25706
25990
  failures.push(`${path2}: run-with-budget step has no max-seconds input \u2014 the budget step guards nothing`);
25707
25991
  } else {
25708
- const literal2 = /^['"]?(\d+)['"]?$/.exec(rawMs);
25709
- const seconds = literal2 ? Number(literal2[1]) : NaN;
25710
- if (!literal2 || seconds < 1) {
25992
+ const literal = /^['"]?(\d+)['"]?$/.exec(rawMs);
25993
+ const seconds = literal ? Number(literal[1]) : NaN;
25994
+ if (!literal || seconds < 1) {
25711
25995
  failures.push(`${path2}: max-seconds '${rawMs}' is not a literal positive integer`);
25712
25996
  } else if (seconds > MAX_SECONDS_CEILING) {
25713
25997
  failures.push(`${path2}: max-seconds ${seconds} exceeds the org ceiling ${MAX_SECONDS_CEILING} \u2014 a budget that generous guards nothing; measure the gate and set a real ceiling`);
@@ -27805,10 +28089,10 @@ var rollout_plan_default = {
27805
28089
  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)."
27806
28090
  },
27807
28091
  baseline: {
27808
- version: "4.6.17",
27809
- tag: "v4.6.17",
27810
- commit: "efe71da889a6",
27811
- npm: "@mutmutco/cli@4.6.17"
28092
+ version: "4.6.18",
28093
+ tag: "v4.6.18",
28094
+ commit: "24b7f2455176",
28095
+ npm: "@mutmutco/cli@4.6.18"
27812
28096
  },
27813
28097
  exitCriterion: "fleet-n-of-n",
27814
28098
  hubOnlyShortcut: "forbidden",
@@ -27825,14 +28109,14 @@ var rollout_plan_default = {
27825
28109
  repo: "mutmutco/mmi-hub",
27826
28110
  role: "canary",
27827
28111
  schedule: "train",
27828
- v3Target: "v4.6.17"
28112
+ v3Target: "v4.6.18"
27829
28113
  }
27830
28114
  ],
27831
28115
  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.",
27832
28116
  rollback: {
27833
28117
  independent: true,
27834
- mechanism: "npm dist-tag latest -> 4.6.17 and redeploy the Hub Lambda from tag v4.6.17 (efe71da889a6); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
27835
- v3Target: "v4.6.17 (@mutmutco/cli@4.6.17, tag commit efe71da889a6 \u2014 last known-good release carrying the repo-index v4-only contract)"
28118
+ mechanism: "npm dist-tag latest -> 4.6.18 and redeploy the Hub Lambda from tag v4.6.18 (24b7f2455176); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
28119
+ v3Target: "v4.6.18 (@mutmutco/cli@4.6.18, tag commit 24b7f2455176 \u2014 last known-good release carrying the repo-index v4-only contract)"
27836
28120
  }
27837
28121
  },
27838
28122
  {
@@ -32387,290 +32671,100 @@ var import_node_fs31 = require("node:fs");
32387
32671
  var import_promises4 = require("node:fs/promises");
32388
32672
  var import_node_path28 = require("node:path");
32389
32673
 
32390
- // ../shared/runner-fallback.ts
32391
- var HOSTED_RUNNER_LABEL_RE = /\b(?:ubuntu|windows|macos)-(?:latest|\d[\w.-]*)\b/i;
32392
- var STANDARD_HOSTED_LABEL_RE = /^(?:ubuntu-(?:latest|24\.04|22\.04|24\.04-arm|22\.04-arm)|windows-(?:latest|2025|2022|11-arm)|macos-(?:latest|\d+|\d+-intel))$/i;
32393
- function classifyRunnerCost(selector) {
32394
- const raw = selector.replace(/\s+#.*$/gm, "").trim();
32395
- if (STANDARD_HOSTED_LABEL_RE.test(raw.replace(/^(['"])(.*)\1$/, "$2"))) return "standard-hosted";
32396
- if (HOSTED_RUNNER_LABEL_RE.test(raw)) return "hosted";
32397
- const json = /^\$\{\{\s*fromJSON\('([^']+)'\)\s*\}\}$/.exec(raw);
32398
- if (json) {
32399
- try {
32400
- const labels2 = JSON.parse(json[1]);
32401
- return Array.isArray(labels2) && labels2.length > 0 && labels2.every((l) => typeof l === "string" && /^[\w-]+$/.test(l)) && labels2.some((l) => l.toLowerCase() === "self-hosted") ? "self-hosted" : "unknown";
32402
- } catch {
32403
- return "unknown";
32404
- }
32405
- }
32406
- if (raw.includes("${{")) return "unknown";
32407
- const labels = /^\[[^\[\]]+\]$/.test(raw) ? raw.slice(1, -1).split(",").map((s) => s.trim().replace(/^(['"])(.*)\1$/, "$2")) : /^(?:-\s*[^\n]+\s*)+$/.test(raw) ? raw.split("\n").map((s) => s.trim().replace(/^-\s*/, "").replace(/^(['"])(.*)\1$/, "$2")) : [raw.replace(/^(['"])(.*)\1$/, "$2")];
32408
- return labels.every((l) => /^[\w-]+$/.test(l)) && labels.some((l) => l.toLowerCase() === "self-hosted") ? "self-hosted" : "unknown";
32409
- }
32410
- var STANDARD_HOSTED_ALLOWLIST = [
32411
- ["mutmutco/MMI-Hub", ".github/workflows/launcher-build.yml", "build", "windows-latest"],
32412
- ["mutmutco/Jerv-JervCode", ".github/workflows/release-archives.yml", "release-platform-arm64", "windows-11-arm"]
32413
- ];
32414
- var HOSTED_SPILL_RE = /^\$\{\{\s*(?:matrix\.[\w-]+\s*==\s*'[\w-]+'\s*&&\s*(?:'(?<leg>[^']+)'|fromJSON\(needs\.(?<legDecider>[\w-]+)\.outputs\.(?<legOutput>\w+_runs_on)\s*\|\|\s*(?<legOwned>'[^']+')\))\s*\|\|\s*)?fromJSON\(needs\.(?<decider>[\w-]+)\.outputs\.(?<output>\w+_runs_on)\s*\|\|\s*(?<owned>'[^']+')\)\s*\}\}$/;
32415
- var RUNNER_CHOICE_ACTION = "mutmutco/MMI-Hub/.github/actions/runner-choice";
32416
- function runnerChoiceProduces(job, decider, output, source) {
32417
- const needs = job.needs;
32418
- if (!(needs === decider || Array.isArray(needs) && needs.includes(decider))) return false;
32419
- const producer = source.jobs.get(decider);
32420
- if (!record(producer) || !record(producer.outputs) || !Array.isArray(producer.steps)) return false;
32421
- const step = /^\$\{\{\s*steps\.([\w-]+)\.outputs\.runs-on\s*\}\}$/.exec(String(producer.outputs[output] ?? ""))?.[1];
32422
- const steps = producer.steps.filter((s) => record(s) && s.id === step);
32423
- if (steps.length !== 1 || !record(steps[0])) return false;
32424
- const { uses } = steps[0], inputs = steps[0].with ?? {};
32425
- const pinned = /^[0-9a-f]{40}$/.test(source.blessedSha) && uses === `${RUNNER_CHOICE_ACTION}@${source.blessedSha}`;
32426
- if (!pinned && !(source.repo === "mutmutco/MMI-Hub" && uses === "./.github/actions/runner-choice")) return false;
32427
- const hosted = record(inputs) ? inputs.hosted ?? "ubuntu-latest" : void 0;
32428
- return typeof hosted === "string" && STANDARD_HOSTED_LABEL_RE.test(hosted);
32429
- }
32430
- function standardHostedLabelFor(repo, path2, jobId) {
32431
- if (repo === "mutmutco/Jerv-Hub") return ["ubuntu-latest", "windows-latest", "macos-14"];
32432
- return STANDARD_HOSTED_ALLOWLIST.find(([r, p, j]) => r === repo && p === path2 && j === jobId)?.[3];
32433
- }
32434
- var record = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
32435
- function classifyJobRunnerCost(job, allowance2, source) {
32436
- if (!record(job) || !Object.hasOwn(job, "runs-on")) return "unknown";
32437
- const selector = job["runs-on"];
32438
- const raw = typeof selector === "string" ? selector.trim() : Array.isArray(selector) && selector.length && selector.every((label) => typeof label === "string") ? JSON.stringify(selector) : "";
32439
- if (!raw) return "unknown";
32440
- const approved = (value) => Array.isArray(allowance2) && allowance2.includes(value);
32441
- if (approved(raw) || allowance2 === "windows-11-arm" && raw === allowance2) return "standard-hosted";
32442
- const fallback = /^\$\{\{\s*vars\.MMI_RUNNER_FALLBACK\s*==\s*'hosted'\s*&&\s*'([^']+)'\s*\|\|\s*(fromJSON\('[^']+'\))\s*\}\}$/.exec(raw);
32443
- if (fallback && approved(fallback[1]) && classifyRunnerCost("${{ " + fallback[2] + " }}") === "self-hosted") return "standard-hosted";
32444
- const layerZero = /^\$\{\{\s*vars\.[\w-]+\s*==\s*'off'\s*&&\s*(fromJSON\('[^']+'\))\s*\|\|\s*'([^']+)'\s*\}\}$/.exec(raw);
32445
- if (layerZero && classifyRunnerCost("${{ " + layerZero[1] + " }}") === "self-hosted" && STANDARD_HOSTED_LABEL_RE.test(layerZero[2])) return "standard-hosted";
32446
- const spill = HOSTED_SPILL_RE.exec(raw)?.groups;
32447
- const legOk = (g) => g.legDecider ? classifyRunnerCost(`\${{ fromJSON(${g.legOwned}) }}`) === "self-hosted" && Boolean(source) && runnerChoiceProduces(job, g.legDecider, g.legOutput, source) : !g.leg || STANDARD_HOSTED_LABEL_RE.test(g.leg);
32448
- if (spill && source && legOk(spill) && classifyRunnerCost(`\${{ fromJSON(${spill.owned}) }}`) === "self-hosted" && runnerChoiceProduces(job, spill.decider, spill.output, source)) return "standard-hosted";
32449
- const direct = classifyRunnerCost(raw);
32450
- const matrix = /^\$\{\{\s*matrix\.([\w-]+)(?:\.([\w-]+))?\s*\}\}$/.exec(raw);
32451
- if (!matrix || direct === "hosted") return direct;
32452
- if (!Object.hasOwn(job, "strategy") || !record(job.strategy) || !Object.hasOwn(job.strategy, "matrix") || !record(job.strategy.matrix)) return "unknown";
32453
- const axes = job.strategy.matrix;
32454
- if (Object.hasOwn(axes, "exclude")) return "unknown";
32455
- let alternatives;
32456
- if (Object.hasOwn(axes, "include")) {
32457
- if (Object.keys(axes).length !== 1 || !Array.isArray(axes.include) || !axes.include.length) return "unknown";
32458
- alternatives = axes.include.map((row) => record(row) && Object.hasOwn(row, matrix[1]) ? row[matrix[1]] : void 0);
32459
- } else {
32460
- const axis = axes[matrix[1]];
32461
- if (!Object.hasOwn(axes, matrix[1]) || !Array.isArray(axis) || !axis.length) return "unknown";
32462
- alternatives = axis;
32463
- }
32464
- if (matrix[2]) alternatives = alternatives.map((row) => record(row) && Object.hasOwn(row, matrix[2]) ? row[matrix[2]] : void 0);
32465
- const costs = alternatives.map((value) => {
32466
- if (typeof value === "string") {
32467
- if (value.includes("${{")) return "unknown";
32468
- if (approved(value) || !matrix[2] && allowance2 === "windows-latest" && value === allowance2) return "standard-hosted";
32469
- return classifyRunnerCost(value);
32470
- }
32471
- if (!Array.isArray(value) || !value.length || !value.every((label) => typeof label === "string" && /^[\w-]+$/.test(label))) return "unknown";
32472
- return classifyRunnerCost(JSON.stringify(value));
32473
- });
32474
- if (costs.includes("hosted")) return "hosted";
32475
- if (costs.includes("unknown")) return "unknown";
32476
- return costs.includes("standard-hosted") ? "standard-hosted" : "self-hosted";
32477
- }
32478
-
32479
32674
  // src/workflow-runner-yaml.ts
32480
32675
  var import_yaml = __toESM(require_dist(), 1);
32481
- var literal = (node) => !(0, import_yaml.isAlias)(node) && !((0, import_yaml.isNode)(node) && (node.tag || "anchor" in node && node.anchor));
32482
- var literalMap = (node) => (0, import_yaml.isMap)(node) && literal(node) && node.items.every(({ key }) => (0, import_yaml.isScalar)(key) && literal(key) && typeof key.value === "string" && key.value !== "<<");
32483
- function parseRunnerJobs(source) {
32484
- if (!source || source.length > 15e5) return void 0;
32485
- try {
32486
- const doc = (0, import_yaml.parseDocument)(source, { uniqueKeys: true, stringKeys: true, merge: false, prettyErrors: false });
32487
- if (doc.errors.length || doc.warnings.length) return void 0;
32488
- let invalid = false;
32489
- let nodes = 0;
32490
- (0, import_yaml.visit)(doc, (_key, _node, path2) => {
32491
- if (++nodes > 1e5 || path2.length > 64) {
32492
- invalid = true;
32493
- return import_yaml.visit.BREAK;
32494
- }
32495
- });
32496
- if (invalid || !literalMap(doc.contents)) return void 0;
32497
- const jobs = doc.contents.get("jobs", true);
32498
- if (!literalMap(jobs) || !jobs.items.length) return void 0;
32499
- const result = /* @__PURE__ */ new Map();
32500
- for (const { key, value: job } of jobs.items) {
32501
- if (!(0, import_yaml.isScalar)(key) || typeof key.value !== "string" || !literalMap(job)) return void 0;
32502
- const projected = /* @__PURE__ */ Object.create(null);
32503
- for (const field of ["runs-on", "uses", "strategy", "needs", "outputs", "steps"]) {
32504
- if (!job.has(field)) continue;
32505
- const optional = field === "needs" || field === "outputs" || field === "steps";
32506
- const value = job.get(field, true);
32507
- if (value !== null && !(0, import_yaml.isNode)(value)) {
32508
- if (optional) continue;
32509
- return void 0;
32676
+
32677
+ // ../shared/workflow-runner-yaml-core.ts
32678
+ function createParseRunnerJobs(ops) {
32679
+ const literal = (node) => !ops.isAlias(node) && !(ops.isNode(node) && (node.tag || "anchor" in node && node.anchor));
32680
+ const literalMap = (node) => ops.isMap(node) && literal(node) && node.items.every(({ key }) => ops.isScalar(key) && literal(key) && typeof key.value === "string" && key.value !== "<<");
32681
+ return (source) => {
32682
+ if (!source || source.length > 15e5) return void 0;
32683
+ try {
32684
+ const doc = ops.parseDocument(source, { uniqueKeys: true, stringKeys: true, merge: false, prettyErrors: false });
32685
+ if (doc.errors.length || doc.warnings.length) return void 0;
32686
+ let invalid = false;
32687
+ let nodes = 0;
32688
+ ops.visit(doc, (_key, _node, path2) => {
32689
+ if (++nodes > 1e5 || path2.length > 64) {
32690
+ invalid = true;
32691
+ return ops.BREAK;
32510
32692
  }
32511
- (0, import_yaml.visit)(value, (_key, node) => {
32512
- if (!literal(node) || (0, import_yaml.isPair)(node) && (0, import_yaml.isScalar)(node.key) && node.key.value === "<<") {
32513
- invalid = true;
32514
- return import_yaml.visit.BREAK;
32693
+ });
32694
+ if (invalid || !literalMap(doc.contents)) return void 0;
32695
+ const jobs = doc.contents.get("jobs", true);
32696
+ if (!literalMap(jobs) || !jobs.items.length) return void 0;
32697
+ const result = /* @__PURE__ */ new Map();
32698
+ for (const { key, value: job } of jobs.items) {
32699
+ if (!ops.isScalar(key) || typeof key.value !== "string" || !literalMap(job)) return void 0;
32700
+ const projected = /* @__PURE__ */ Object.create(null);
32701
+ for (const field of ["runs-on", "uses", "strategy", "needs", "outputs", "steps"]) {
32702
+ if (!job.has(field)) continue;
32703
+ const optional = field === "needs" || field === "outputs" || field === "steps";
32704
+ const value = job.get(field, true);
32705
+ if (value !== null && !ops.isNode(value)) {
32706
+ if (optional) continue;
32707
+ return void 0;
32515
32708
  }
32516
- });
32517
- if (invalid && optional) {
32518
- invalid = false;
32519
- continue;
32709
+ ops.visit(value, (_key, node) => {
32710
+ if (!literal(node) || ops.isPair(node) && ops.isScalar(node.key) && node.key.value === "<<") {
32711
+ invalid = true;
32712
+ return ops.BREAK;
32713
+ }
32714
+ });
32715
+ if (invalid && optional) {
32716
+ invalid = false;
32717
+ continue;
32718
+ }
32719
+ if (invalid) return void 0;
32720
+ projected[field] = value === null ? null : value.toJS(doc, { maxAliasCount: 0 });
32520
32721
  }
32521
- if (invalid) return void 0;
32522
- projected[field] = value === null ? null : value.toJS(doc, { maxAliasCount: 0 });
32722
+ result.set(key.value, projected);
32523
32723
  }
32524
- result.set(key.value, projected);
32724
+ return result;
32725
+ } catch {
32726
+ return void 0;
32525
32727
  }
32526
- return result;
32527
- } catch {
32528
- return void 0;
32529
- }
32728
+ };
32530
32729
  }
32531
32730
 
32731
+ // src/workflow-runner-yaml.ts
32732
+ var parseRunnerJobs = createParseRunnerJobs({ isAlias: import_yaml.isAlias, isMap: import_yaml.isMap, isNode: import_yaml.isNode, isPair: import_yaml.isPair, isScalar: import_yaml.isScalar, parseDocument: import_yaml.parseDocument, visit: import_yaml.visit, BREAK: import_yaml.visit.BREAK });
32733
+
32532
32734
  // src/workflow-runner-source.ts
32533
- var WORKFLOW_PATH = /^\.github\/workflows\/[\w.-]+\.ya?ml$/;
32534
- var REPO = /^[A-Za-z0-9][A-Za-z0-9-]*\/[A-Za-z0-9_][\w.-]*$/;
32535
- var REF = /^[\w./-]+$/;
32536
- var MAX_FILES = 64;
32537
- var MAX_DEPTH = 8;
32538
- var WorkflowSourceError = class extends Error {
32539
- constructor(location, category) {
32540
- super(`${location}: ${category}`);
32541
- }
32542
- };
32543
- function safeReadCategory(error) {
32544
- const e = error;
32545
- if (e?.rateLimited === true) return "rate-limited";
32546
- if (typeof e?.status === "number" && Number.isInteger(e.status) && e.status >= 400 && e.status <= 599) return `HTTP ${e.status}`;
32547
- if (e?.name === "TimeoutError" || e?.name === "AbortError" || e?.code === "ETIMEDOUT") return "read-timeout";
32548
- if (e?.code === "ENOENT") return "reader-unavailable";
32549
- if (e?.code === "ENOBUFS" || e?.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") return "reader-output-limit";
32550
- if (error instanceof SyntaxError) return "invalid-json";
32551
- return "read-failed";
32552
- }
32553
- function locationOf(file) {
32554
- return `${file.repo}/${file.path}${file.ref ? `@${file.ref}` : ""}`;
32555
- }
32556
- function contentsPath(file) {
32557
- if (!REPO.test(file.repo) || !(WORKFLOW_PATH.test(file.path) || file.path === ".github/workflows") || file.ref !== void 0 && !REF.test(file.ref)) {
32558
- throw new WorkflowSourceError("workflow source", "invalid-location");
32559
- }
32560
- return `repos/${file.repo}/contents/${file.path}${file.ref ? `?ref=${encodeURIComponent(file.ref)}` : ""}`;
32561
- }
32562
- async function readSource(read, file) {
32563
- const endpoint = contentsPath(file);
32564
- try {
32565
- return await read(endpoint);
32566
- } catch (e) {
32567
- throw new WorkflowSourceError(locationOf(file), safeReadCategory(e));
32568
- }
32569
- }
32570
- async function readBody(read, file) {
32571
- const value = await readSource(read, file);
32572
- if (!value || value.encoding !== "base64" || typeof value.content !== "string" || !value.content.trim() || value.content.length > 15e5 || !/^[A-Za-z0-9+/=\r\n]+$/.test(value.content)) {
32573
- throw new WorkflowSourceError(locationOf(file), "invalid-content");
32574
- }
32575
- return { ...file, body: decodeGitHubContents(value.content) };
32576
- }
32577
- function resolveCallee(caller, uses) {
32578
- if (uses.startsWith("./") && WORKFLOW_PATH.test(uses.slice(2))) return { repo: caller.repo, path: uses.slice(2), ref: caller.ref };
32579
- const remote = /^([\w.-]+\/[\w.-]+)\/(\.github\/workflows\/[\w.-]+\.ya?ml)@([\w./-]+)$/.exec(uses);
32580
- return remote ? { repo: remote[1], path: remote[2], ref: remote[3] } : void 0;
32581
- }
32582
- async function readRunnerWorkflowClosure(read, repo, ref, roots) {
32583
- let files;
32584
- if (roots) {
32585
- files = roots.map((file) => ({ ...file, repo, ref }));
32586
- } else {
32587
- const listing = await readSource(read, { repo, ref, path: ".github/workflows" });
32588
- if (!Array.isArray(listing)) throw new WorkflowSourceError(`${repo}/.github/workflows`, "invalid-listing");
32589
- const paths = listing.filter((f) => f?.type === "file" && /\.ya?ml$/i.test(f?.path ?? "")).map((f) => f.path);
32590
- if (!paths.length || paths.length > MAX_FILES) throw new WorkflowSourceError(`${repo}/.github/workflows`, "empty-or-oversized-listing");
32591
- files = [];
32592
- for (const path2 of paths) files.push(await readBody(read, { repo, ref, path: path2 }));
32593
- }
32594
- if (!files.length || files.length > MAX_FILES) throw new WorkflowSourceError("workflow source", "empty-or-oversized-listing");
32595
- for (const file of files) contentsPath(file);
32596
- const byLocation = new Map(files.map((file) => [locationOf(file), file]));
32597
- const active = /* @__PURE__ */ new Set();
32598
- const completed = /* @__PURE__ */ new Set();
32599
- async function visit2(file, depth) {
32600
- const key = locationOf(file);
32601
- if (active.has(key)) throw new WorkflowSourceError(key, "reusable-cycle");
32602
- if (depth > MAX_DEPTH) throw new WorkflowSourceError(key, "reusable-depth-limit");
32603
- if (completed.has(key)) return;
32604
- active.add(key);
32605
- const jobs = parseRunnerJobs(file.body);
32606
- if (!jobs) throw new WorkflowSourceError(key, "invalid-workflow-yaml");
32607
- for (const job of jobs.values()) {
32608
- if (!Object.hasOwn(job, "uses")) continue;
32609
- const uses = job.uses;
32610
- if (typeof uses !== "string" || Object.hasOwn(job, "runs-on")) throw new WorkflowSourceError(key, "unresolved-reusable-ref");
32611
- const target = resolveCallee(file, uses);
32612
- if (!target) throw new WorkflowSourceError(key, "unresolved-reusable-ref");
32613
- const targetKey = locationOf(target);
32614
- let child2 = byLocation.get(targetKey);
32615
- if (!child2) {
32616
- if (byLocation.size >= MAX_FILES) throw new WorkflowSourceError(key, "workflow-read-limit");
32617
- child2 = await readBody(read, target);
32618
- byLocation.set(targetKey, child2);
32619
- }
32620
- await visit2(child2, depth + 1);
32621
- }
32622
- active.delete(key);
32623
- completed.add(key);
32624
- }
32625
- for (const file of files) await visit2(file, 0);
32626
- return [...byLocation.values()];
32735
+ function readRunnerWorkflowClosure2(read, repo, ref, roots, bounds) {
32736
+ return readRunnerWorkflowClosure(parseRunnerJobs, read, repo, ref, roots, bounds);
32627
32737
  }
32628
- function classifyRunnerWorkflows(files) {
32629
- const known = new Map(files.map((file) => [locationOf(file), file]));
32630
- const parsed = new Map(files.map((file) => [locationOf(file), parseRunnerJobs(file.body)]));
32631
- const resolvedCache = /* @__PURE__ */ new Map();
32632
- function resolved(file, active) {
32633
- const key = locationOf(file);
32634
- if (active.has(key)) return false;
32635
- if (resolvedCache.has(key)) return resolvedCache.get(key);
32636
- const next = new Set(active).add(key);
32637
- const jobs = parsed.get(key);
32638
- const result = Boolean(jobs && [...jobs.values()].every((job) => {
32639
- if (!Object.hasOwn(job, "uses")) return true;
32640
- const uses = job.uses;
32641
- if (typeof uses !== "string" || Object.hasOwn(job, "runs-on")) return false;
32642
- const target = resolveCallee(file, uses);
32643
- const child2 = target && known.get(locationOf(target));
32644
- return Boolean(child2 && resolved(child2, next));
32645
- }));
32646
- resolvedCache.set(key, result);
32647
- return result;
32648
- }
32649
- return files.flatMap((file) => {
32650
- const jobs = parsed.get(locationOf(file));
32651
- if (!jobs) return [{ repo: file.repo, path: file.path, job: "(invalid YAML)", cost: "unknown", callee: false }];
32652
- return [...jobs].flatMap(([id, job]) => {
32653
- const hasCallee = Object.hasOwn(job, "uses");
32654
- const uses = hasCallee && typeof job.uses === "string" && !Object.hasOwn(job, "runs-on") ? job.uses : void 0;
32655
- const target = uses && resolveCallee(file, uses);
32656
- if (target && known.has(locationOf(target)) && resolved(file, /* @__PURE__ */ new Set())) return [];
32657
- const cost = hasCallee ? "unknown" : classifyJobRunnerCost(job, standardHostedLabelFor(file.repo, file.path, id), { repo: file.repo, jobs, blessedSha: BLESSED_RUNNER_CHOICE_SHA });
32658
- return [{ repo: file.repo, path: file.path, job: id, cost, callee: hasCallee }];
32659
- });
32660
- });
32738
+ function classifyRunnerWorkflows2(files) {
32739
+ return classifyRunnerWorkflows(parseRunnerJobs, files);
32661
32740
  }
32662
32741
 
32663
32742
  // src/actions-billing-preflight.ts
32664
32743
  async function assertActionsJobsCanStart(deps, targetRepo2) {
32665
- const read = deps.readWorkflowContents ?? (async (path2) => JSON.parse(await deps.run("gh", ["api", path2])));
32666
32744
  let files;
32667
32745
  try {
32668
- files = await readRunnerWorkflowClosure(read, targetRepo2);
32746
+ const served = await deps.readTrainWorkflowClosure?.(targetRepo2);
32747
+ if (served && !served.some((f) => f.repo.toLowerCase() === targetRepo2.toLowerCase())) {
32748
+ throw new WorkflowSourceError("workflow source", "invalid-response");
32749
+ }
32750
+ if (served) {
32751
+ files = served;
32752
+ } else {
32753
+ const read = deps.readWorkflowContents ?? (async (path2) => JSON.parse(await deps.run("gh", ["api", path2])));
32754
+ try {
32755
+ files = await readRunnerWorkflowClosure2(read, targetRepo2);
32756
+ } catch (e) {
32757
+ const detail = e instanceof WorkflowSourceError ? e.message : "workflow source: read-failed";
32758
+ throw new Error("release refused (#8911): workflow source inventory unreadable; " + detail + ". Resolve this source read before tagging.");
32759
+ }
32760
+ }
32669
32761
  } catch (e) {
32670
- const detail = e instanceof WorkflowSourceError ? e.message : "workflow source: read-failed";
32671
- throw new Error("release refused (#8911): workflow source inventory unreadable; " + detail + ". Resolve this source read before tagging.");
32762
+ if (e instanceof WorkflowSourceError) {
32763
+ throw new Error("release refused (#8911): workflow source inventory unreadable; " + e.message + ". Resolve this source read before tagging.");
32764
+ }
32765
+ throw e;
32672
32766
  }
32673
- const unresolved = classifyRunnerWorkflows(files).filter((f) => f.cost !== "self-hosted" && f.cost !== "standard-hosted");
32767
+ const unresolved = classifyRunnerWorkflows2(files).filter((f) => f.cost !== "self-hosted" && f.cost !== "standard-hosted");
32674
32768
  if (unresolved.length) throw new Error("release refused (#8911): paid or unresolved runner path(s): " + unresolved.map((f) => f.repo + "/" + f.path + ":" + f.job).join(", ") + ". Standard included GitHub-hosted labels are allowed (#8977, Jerv-Hub#2459); only larger/paid runners and unresolved selectors/callees are refused. Resolve them before tagging; no paid capacity probe is permitted.");
32675
32769
  process.stderr.write("actions preflight: " + targetRepo2 + " source selectors are self-hosted or standard included GitHub-hosted (#8977); no canary dispatch; the org spending limit guards over-allowance spend (unprovable from source); access/isolation/execution unproven (#8958)\n");
32676
32770
  }
@@ -35904,18 +35998,23 @@ async function runTrainDoctor(input) {
35904
35998
  }
35905
35999
  add(healedBlock ? { code: "scratch-untracked", severity: "healed", source: "local", title: `wrote the org-managed .gitignore block for ${liveScratch.join(", ")} (#5409)`, remedy: "nothing \u2014 the live-session dirs are ignored from now on" } : { code: "scratch-untracked", severity: "warning", source: "local", title: `untracked live-session scratch: ${liveScratch.join(", ")}`, remedy: "run `mmi-cli devops train doctor --heal` (or `mmi-cli doctor --apply`) to write the org-managed .gitignore block (#5409); the train gate already exempts these (#5397)" });
35906
36000
  }
35907
- const blocking = porcelainBlockingPaths(porcelain);
35908
- const modifiedOnly = new Set(porcelainRows.filter((l) => l.startsWith(" M ")).map((l) => l.slice(3)));
36001
+ const blockingEntries = porcelainBlockingEntries(porcelain);
36002
+ const modifiedOnly = new Set(blockingEntries.filter((e) => e.status === " M").map((e) => e.path));
35909
36003
  const churn = [];
35910
- for (const path2 of blocking.filter((p) => modifiedOnly.has(p))) {
36004
+ for (const path2 of blockingEntries.map((e) => e.path).filter((p) => modifiedOnly.has(p))) {
35911
36005
  try {
35912
36006
  if (!clean2(await train.run("git", ["diff", "HEAD", "--numstat", "--", path2]))) churn.push(path2);
35913
36007
  } catch {
35914
36008
  }
35915
36009
  }
35916
- const dirty = blocking.filter((p) => !churn.includes(p));
35917
- if (dirty.length) {
35918
- add({ code: "tree-dirty-work", severity: "blocker", source: "local", title: `working tree carries tracked changes \u2014 blocking: ${dirty.join(", ")}`, remedy: "commit or stash the work, then rerun; the train refuses a dirty tree before any mutation" });
36010
+ const dirty = blockingEntries.filter((e) => !churn.includes(e.path));
36011
+ const dirtyTracked = dirty.filter((e) => e.status !== "??");
36012
+ const dirtyUntracked = dirty.filter((e) => e.status === "??");
36013
+ if (dirtyTracked.length) {
36014
+ add({ code: "tree-dirty-work", severity: "blocker", source: "local", title: `working tree carries tracked changes \u2014 blocking: ${dirtyTracked.map((e) => e.path).join(", ")}`, remedy: "commit or stash the work, then rerun; the train refuses a dirty tree before any mutation" });
36015
+ }
36016
+ if (dirtyUntracked.length) {
36017
+ add({ code: "tree-dirty-work", severity: "blocker", source: "local", title: `working tree carries untracked content \u2014 blocking: ${dirtyUntracked.map((e) => e.path).join(", ")}`, remedy: "inspect ownership first \u2014 another session's content stays untouched; commit intended new source through its owning lane; gitignore or move only verified own scratch to the gitignored repo-root tmp/, then rerun" });
35919
36018
  }
35920
36019
  if (churn.length) {
35921
36020
  const restored = [];
@@ -39318,7 +39417,7 @@ var SHARED_ACTION_PINS = [
39318
39417
  { path: "npm-publish", floor: NPM_PUBLISH_SAFE_FLOOR_SHA }
39319
39418
  ];
39320
39419
  function auditRunnerFallbacks(repo, files, closure) {
39321
- return classifyRunnerWorkflows(closure ?? files.map((file) => ({ ...file, repo }))).map((f) => ({
39420
+ return classifyRunnerWorkflows2(closure ?? files.map((file) => ({ ...file, repo }))).map((f) => ({
39322
39421
  ok: f.cost === "self-hosted" || f.cost === "standard-hosted",
39323
39422
  label: f.cost === "standard-hosted" ? `runner uses approved standard-hosted source (#8912/#8958/#8977): ${f.repo}/${f.path}:${f.job}` : `runner is self-hosted only (#8911): ${f.repo}/${f.path}:${f.job}`,
39324
39423
  detail: f.callee ? "unknown callee runner policy; source must be read at its referenced revision" : f.cost === "hosted" ? "larger/paid GitHub-hosted runner is forbidden; only standard included GitHub-hosted labels are allowed (#8977, Jerv-Hub#2459)" : f.cost === "unknown" ? "unknown runner selector; source unresolved" : f.cost === "standard-hosted" ? "standard GitHub-hosted labels within included Enterprise minutes only (#8977, Jerv-Hub#2459); the org spending limit guards over-allowance spend (unprovable from source); runner access and isolation not established" : "self-hosted selection only; runner access and isolation not established",
@@ -40381,7 +40480,7 @@ async function auditRepoCi(repo, callerDeps) {
40381
40480
  const workflows = await getBranchWorkflows();
40382
40481
  if (workflows) {
40383
40482
  try {
40384
- const closure = workflows.length ? await readRunnerWorkflowClosure((path2) => callerDeps.client.rest("GET", path2), repo, baseBranch, workflows) : [];
40483
+ const closure = workflows.length ? await readRunnerWorkflowClosure2((path2) => callerDeps.client.rest("GET", path2), repo, baseBranch, workflows) : [];
40385
40484
  checks.push(...auditRunnerFallbacks(repo, workflows, closure));
40386
40485
  } catch (e) {
40387
40486
  checks.push({ ok: false, label: "runner workflow source is readable (#8958)", detail: e instanceof WorkflowSourceError ? e.message : "workflow source: read-failed" });
@@ -54222,22 +54321,6 @@ async function deleteMergedRemoteBranch(options) {
54222
54321
  // src/command-register-train-operations.ts
54223
54322
  init_github_client();
54224
54323
 
54225
- // src/hub-release-ci-authority.ts
54226
- var HUB_RELEASE_REPO = "mutmutco/MMI-Hub";
54227
- var HUB_RELEASE_WORKFLOW = ".github/workflows/hub-release.yml";
54228
- var HUB_RELEASE_REF = "refs/heads/development";
54229
- var WORKFLOW_REF = `${HUB_RELEASE_REPO}/${HUB_RELEASE_WORKFLOW}@${HUB_RELEASE_REF}`;
54230
- var ID = /^[1-9][0-9]*$/;
54231
- function hubReleaseCiRun(env) {
54232
- if (env.MMI_HUB_RELEASE_CI !== "1" || env.GITHUB_ACTIONS !== "true") return null;
54233
- if (env.GITHUB_REPOSITORY !== HUB_RELEASE_REPO || env.GITHUB_EVENT_NAME !== "workflow_dispatch") return null;
54234
- if (env.GITHUB_WORKFLOW_REF !== WORKFLOW_REF || env.GITHUB_REF !== HUB_RELEASE_REF) return null;
54235
- const runId = env.GITHUB_RUN_ID ?? "";
54236
- const runAttempt2 = env.GITHUB_RUN_ATTEMPT ?? "";
54237
- if (!ID.test(runId) || !ID.test(runAttempt2)) return null;
54238
- return { runId: Number(runId), runAttempt: Number(runAttempt2) };
54239
- }
54240
-
54241
54324
  // src/release-custody.ts
54242
54325
  var import_node_crypto18 = require("node:crypto");
54243
54326
  var import_node_fs41 = require("node:fs");
@@ -62670,6 +62753,9 @@ function trainApplyDeps() {
62670
62753
  return withHubReleaseCustody({
62671
62754
  // #8958: same authenticated bounded read boundary as devops api; no raw gh contents subprocess.
62672
62755
  readWorkflowContents: (path2) => defaultGitHubClient().rest("GET", path2, { timeoutMs: GH_TRAIN_TIMEOUT_MS }),
62756
+ // #9755: delegated Jerv trains require the Hub's sourceSha-pinned closure and fail closed on errors.
62757
+ // Humans and the Hub binding retain the caller-side read above; no producer token is widened.
62758
+ readTrainWorkflowClosure: async (repo) => trainWorkflowSourceClosure(repo, registryClientDeps(await loadConfig())),
62673
62759
  // #7027: every mutating train verb holds this checkout's train writer lock for its whole run.
62674
62760
  trainWriter: (verb, cwd) => acquireTrainWriterLock(cwd, `mmi-cli devops ${verb}`),
62675
62761
  run: async (file, args) => {
package/cli/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.6.17",
3
+ "version": "4.6.18",
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/codex-plugin",
3
- "version": "4.6.17",
3
+ "version": "4.6.18",
4
4
  "description": "MMI workflow skills and org gates delivery.",
5
5
  "author": {
6
6
  "name": "MMI Future",