@mutmutco/cli 4.1.2 → 4.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/main.cjs +248 -48
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -12868,10 +12868,10 @@ var rollout_plan_default = {
12868
12868
  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)."
12869
12869
  },
12870
12870
  baseline: {
12871
- version: "4.1.2",
12872
- tag: "v4.1.2",
12873
- commit: "bb1a56a57cda",
12874
- npm: "@mutmutco/cli@4.1.2"
12871
+ version: "4.1.3",
12872
+ tag: "v4.1.3",
12873
+ commit: "3164484f3f37",
12874
+ npm: "@mutmutco/cli@4.1.3"
12875
12875
  },
12876
12876
  exitCriterion: "fleet-n-of-n",
12877
12877
  hubOnlyShortcut: "forbidden",
@@ -12888,14 +12888,14 @@ var rollout_plan_default = {
12888
12888
  repo: "mutmutco/mmi-hub",
12889
12889
  role: "canary",
12890
12890
  schedule: "train",
12891
- v3Target: "v4.1.2"
12891
+ v3Target: "v4.1.3"
12892
12892
  }
12893
12893
  ],
12894
12894
  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.",
12895
12895
  rollback: {
12896
12896
  independent: true,
12897
- mechanism: "npm dist-tag latest -> 4.1.2 and redeploy the Hub Lambda from tag v4.1.2 (bb1a56a57cda); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
12898
- v3Target: "v4.1.2 (@mutmutco/cli@4.1.2, tag commit bb1a56a57cda \u2014 last known-good release carrying the repo-index v4-only contract)"
12897
+ mechanism: "npm dist-tag latest -> 4.1.3 and redeploy the Hub Lambda from tag v4.1.3 (3164484f3f37); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
12898
+ v3Target: "v4.1.3 (@mutmutco/cli@4.1.3, tag commit 3164484f3f37 \u2014 last known-good release carrying the repo-index v4-only contract)"
12899
12899
  }
12900
12900
  },
12901
12901
  {
@@ -19389,8 +19389,10 @@ init_client_version();
19389
19389
  var BOARD_SNAPSHOT_TIMEOUT_MS = 25e3;
19390
19390
  function isSnapshotShape(body) {
19391
19391
  const b = body;
19392
+ const rate = b?.github?.rateLimit;
19393
+ const githubValid = b?.github === void 0 || b.github.credential === "app_installation" && Number.isFinite(rate?.limit) && Number.isFinite(rate?.remaining) && Number.isFinite(rate?.cost) && typeof rate?.resetAt === "string";
19392
19394
  return Boolean(
19393
- b && typeof b === "object" && typeof b.project?.id === "string" && typeof b.project?.title === "string" && typeof b.viewer === "string" && b.viewer.length > 0 && Array.isArray(b.nodes) && Array.isArray(b.writableRepos) && Array.isArray(b.unreadableRepos) && Array.isArray(b.pullRequests) && Array.isArray(b.warnings) && typeof b.partial === "boolean"
19395
+ b && typeof b === "object" && typeof b.project?.id === "string" && typeof b.project?.title === "string" && typeof b.viewer === "string" && b.viewer.length > 0 && Array.isArray(b.nodes) && Array.isArray(b.writableRepos) && Array.isArray(b.unreadableRepos) && Array.isArray(b.pullRequests) && Array.isArray(b.warnings) && typeof b.partial === "boolean" && githubValid
19394
19396
  );
19395
19397
  }
19396
19398
  async function fetchHubBoardSnapshot(request, deps) {
@@ -20173,6 +20175,10 @@ function renderBoardItem(item) {
20173
20175
  }
20174
20176
  function renderBoardReport(report) {
20175
20177
  const lines = [`Board \xB7 ${report.project.title} \xB7 @${report.viewer}`, renderBoardSource()];
20178
+ if (report.github) {
20179
+ const { limit, remaining, cost, resetAt } = report.github.rateLimit;
20180
+ lines.push(`github: app installation \xB7 GraphQL ${remaining}/${limit} remaining \xB7 cost ${cost} \xB7 resets ${resetAt}`);
20181
+ }
20176
20182
  renderScope(lines, "PRIMARY", report.repo, report.primary, report.viewer);
20177
20183
  renderScope(lines, "SECONDARY", "Other repos on this project", report.secondary, report.viewer);
20178
20184
  if (report.warnings.length) {
@@ -20285,6 +20291,7 @@ async function readBoard(options, deps = {}) {
20285
20291
  let collected;
20286
20292
  let writable;
20287
20293
  let pullRequests;
20294
+ let github;
20288
20295
  let snapshotFallback;
20289
20296
  const attempt = deps.snapshot ? await fetchHubBoardSnapshot(
20290
20297
  {
@@ -20329,6 +20336,7 @@ async function readBoard(options, deps = {}) {
20329
20336
  unknown: new Set(snapshot.unreadableRepos.map((entry) => entry.repo.toLowerCase()))
20330
20337
  };
20331
20338
  pullRequests = snapshot.pullRequests;
20339
+ github = snapshot.github;
20332
20340
  } else {
20333
20341
  if (attempt?.state === "unavailable") snapshotFallback = attempt.reason;
20334
20342
  collected = await collectBoardItems(cfg, { repo: options.repo, allowPartial: options.allowPartial, activeOnly: true }, deps);
@@ -20349,7 +20357,8 @@ async function readBoard(options, deps = {}) {
20349
20357
  warnings: collected.warnings,
20350
20358
  partial: collected.partial,
20351
20359
  source: "live",
20352
- ...pullRequests ? { pullRequests } : {}
20360
+ ...pullRequests ? { pullRequests } : {},
20361
+ ...github ? { github } : {}
20353
20362
  };
20354
20363
  if (options.includeBundleDetails || options.includeAllBodies) {
20355
20364
  await attachBundleDetails(report, client, options.allowPartial ?? false, { all: options.includeAllBodies });
@@ -25323,6 +25332,7 @@ function repoIndexV4BucketDigests(repo, chunks, embeddings) {
25323
25332
 
25324
25333
  // src/repo-index-cloud-client.ts
25325
25334
  var RETRY_ATTEMPTS2 = 3;
25335
+ var REPO_INDEX_GC_TIMEOUT_MS = 12e4;
25326
25336
  async function repoIndexSourceHostHeaders() {
25327
25337
  const { detectSurface: detectSurface2 } = await Promise.resolve().then(() => (init_plugin_guard_io(), plugin_guard_io_exports));
25328
25338
  return { [SOURCE_HOST_HEADER]: detectSurface2(process.env) };
@@ -25728,10 +25738,22 @@ async function statusRepoIndexCloud(repo, deps) {
25728
25738
  return { ok: false, error: e.message, code: "network" };
25729
25739
  }
25730
25740
  }
25741
+ function normalizeGcV4Repos(raw) {
25742
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
25743
+ const out = {};
25744
+ for (const [repo, value] of Object.entries(raw)) {
25745
+ if (!value || typeof value !== "object" || Array.isArray(value)) continue;
25746
+ const removed = Array.isArray(value.removed) ? value.removed.filter((k) => typeof k === "string") : [];
25747
+ const kept = Number(value.kept);
25748
+ out[repo] = { removed, kept: Number.isFinite(kept) ? kept : 0 };
25749
+ }
25750
+ return out;
25751
+ }
25731
25752
  async function gcRepoIndexCloud(deps) {
25732
25753
  if (!deps.baseUrl) return { ok: false, error: "Hub API URL not configured" };
25733
25754
  const token = await deps.token();
25734
25755
  if (!token) return { ok: false, error: "no Hub session token (run `gh auth login`)" };
25756
+ const timeoutMs = deps.timeoutMs ?? REPO_INDEX_GC_TIMEOUT_MS;
25735
25757
  try {
25736
25758
  const res = await fetchWithRetry(
25737
25759
  deps.fetch ?? fetch,
@@ -25741,13 +25763,32 @@ async function gcRepoIndexCloud(deps) {
25741
25763
  headers: { ...clientVersionHeaders(), Authorization: `Bearer ${token}`, "content-type": "application/json" },
25742
25764
  body: "{}"
25743
25765
  },
25744
- { attempts: RETRY_ATTEMPTS2, timeoutMs: deps.timeoutMs ?? REGISTRY_FETCH_TIMEOUT_MS, sleep: deps.retrySleep }
25766
+ // Single attempt: GC tombstones authorities and deletes S3 objects. A timed-out POST may still
25767
+ // finish on Hub; retrying would re-issue a destructive sweep without an authoritative receipt.
25768
+ { attempts: 1, timeoutMs, sleep: deps.retrySleep }
25745
25769
  );
25746
25770
  const body = await res.json().catch(() => ({}));
25747
25771
  if (!res.ok) return { ok: false, error: body.error ?? `gc HTTP ${res.status}` };
25748
- return { ok: true, removed: body.removed ?? [] };
25772
+ const removed = Array.isArray(body.removed) ? body.removed.filter((repo) => typeof repo === "string") : [];
25773
+ const kept = Number(body.kept);
25774
+ return {
25775
+ ok: true,
25776
+ removed,
25777
+ kept: Number.isFinite(kept) ? kept : 0,
25778
+ v4: {
25779
+ dryRun: body.v4?.dryRun === true,
25780
+ repos: normalizeGcV4Repos(body.v4?.repos)
25781
+ }
25782
+ };
25749
25783
  } catch (e) {
25750
- return { ok: false, error: e.message };
25784
+ const msg = e.message || String(e);
25785
+ if (/abort|timeout/i.test(msg)) {
25786
+ return {
25787
+ ok: false,
25788
+ error: `repo-index gc timed out after ${timeoutMs}ms \u2014 Hub may still have completed the destructive sweep; run \`mmi-cli oracle repo-index status --cloud --json\` and verify before retrying`
25789
+ };
25790
+ }
25791
+ return { ok: false, error: msg };
25751
25792
  }
25752
25793
  }
25753
25794
 
@@ -25894,6 +25935,16 @@ function buildGraphEdges(cwd, repo, commit, rosterRepos2) {
25894
25935
  }
25895
25936
 
25896
25937
  // src/repo-index-sync.ts
25938
+ function execFileUtf8(file, args) {
25939
+ return new Promise((resolve5, reject) => {
25940
+ (0, import_node_child_process15.execFile)(file, args, { encoding: "utf8", windowsHide: true }, (error, stdout) => {
25941
+ if (error) reject(error);
25942
+ else resolve5(String(stdout ?? ""));
25943
+ });
25944
+ });
25945
+ }
25946
+ var ESTATE_CLASSIFY_CONCURRENCY = 8;
25947
+ var ESTATE_HEALTHY_CLASSIFY_BUDGET_MS = 5 * 601e3;
25897
25948
  var COMMIT3 = /^[a-f0-9]{40}$/;
25898
25949
  var SHA256 = /^[a-f0-9]{64}$/;
25899
25950
  var UTC_MILLIS = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
@@ -25929,17 +25980,28 @@ function checkoutExactCommit(repo, dest, token, commit) {
25929
25980
  const head = git3(["rev-parse", "HEAD"]).trim().toLowerCase();
25930
25981
  if (head !== commit) throw new Error(`checkout of ${repo} resolved ${head}, not the requested commit ${commit}`);
25931
25982
  }
25932
- function remoteHead(repo, token) {
25983
+ async function remoteHead(repo, token) {
25933
25984
  const basic = Buffer.from(`x-access-token:${token}`, "utf8").toString("base64");
25934
- const output = (0, import_node_child_process15.execFileSync)(
25985
+ const stdout = await execFileUtf8(
25935
25986
  "git",
25936
- ["-c", `http.extraHeader=Authorization: Basic ${basic}`, "ls-remote", "--exit-code", `https://github.com/${repo}.git`, "HEAD"],
25937
- { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], windowsHide: true }
25987
+ ["-c", `http.extraHeader=Authorization: Basic ${basic}`, "ls-remote", "--exit-code", `https://github.com/${repo}.git`, "HEAD"]
25938
25988
  );
25939
- const match = String(output).match(/^([a-f0-9]{40})\s+HEAD$/m);
25989
+ const match = stdout.match(/^([a-f0-9]{40})\s+HEAD$/m);
25940
25990
  if (!match) throw new Error(`could not resolve remote HEAD for ${repo}`);
25941
25991
  return match[1];
25942
25992
  }
25993
+ async function mapLimit(items, limit, fn) {
25994
+ const out = new Array(items.length);
25995
+ let next = 0;
25996
+ const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {
25997
+ while (next < items.length) {
25998
+ const i = next++;
25999
+ out[i] = await fn(items[i], i);
26000
+ }
26001
+ });
26002
+ await Promise.all(workers);
26003
+ return out;
26004
+ }
25943
26005
  function verifiedReadyBase(statusValue, repo) {
25944
26006
  if (!statusValue || typeof statusValue !== "object" || Array.isArray(statusValue)) return null;
25945
26007
  const status = statusValue;
@@ -26041,11 +26103,18 @@ async function syncEstateRepoIndex(opts) {
26041
26103
  const busy = new Set(
26042
26104
  (opts.skipRepos ?? []).map((repo) => normalizeRepoIndexRepo(repo)).filter((repo) => repo !== null)
26043
26105
  );
26044
- for (const repo of repos) {
26106
+ const emit = (progress) => {
26107
+ try {
26108
+ opts.onClassify?.(progress);
26109
+ } catch {
26110
+ }
26111
+ };
26112
+ const classified = await mapLimit(repos, ESTATE_CLASSIFY_CONCURRENCY, async (repo) => {
26045
26113
  if (busy.has(repo)) {
26046
- drift.push({ repo, reason: "busy-elsewhere", action: "skip" });
26047
- skipped.push(`${repo}: a per-repo reconcile run is already publishing it`);
26048
- continue;
26114
+ const row2 = { repo, reason: "busy-elsewhere", action: "skip" };
26115
+ const warning = `${repo}: a per-repo reconcile run is already publishing it`;
26116
+ emit({ row: row2, warning });
26117
+ return { repo, row: row2, warning, base: null, expectedActiveDigest: void 0 };
26049
26118
  }
26050
26119
  let base = null;
26051
26120
  let expectedActiveDigest;
@@ -26068,7 +26137,7 @@ async function syncEstateRepoIndex(opts) {
26068
26137
  if (base && !forceFull) {
26069
26138
  if (!targetCommit) {
26070
26139
  try {
26071
- targetCommit = remoteHead(repo, opts.githubToken);
26140
+ targetCommit = await remoteHead(repo, opts.githubToken);
26072
26141
  } catch {
26073
26142
  targetCommit = void 0;
26074
26143
  }
@@ -26077,25 +26146,43 @@ async function syncEstateRepoIndex(opts) {
26077
26146
  (error) => ({ ok: false, error: error.message })
26078
26147
  );
26079
26148
  if (provenance.ok && !provenance.deltaCompatible) {
26080
- drift.push({ repo, reason: "incompatible-provenance", action: "needs-full-rebuild", activeCommit: base.commit, ...targetCommit ? { targetCommit } : {} });
26081
- needsFullRebuild.push(repo);
26082
- skipped.push(`${repo}: DRIFT incompatible index provenance \u2014 the active authority at ${base.commit} was not built by ${CURRENT_REPO_INDEX_PROVENANCE_TOKEN}; migrate it explicitly with \`oracle repo-index sync-estate --repo ${repo} --full-rebuild ${CURRENT_REPO_INDEX_PROVENANCE_TOKEN}\``);
26083
- continue;
26149
+ const row2 = {
26150
+ repo,
26151
+ reason: "incompatible-provenance",
26152
+ action: "needs-full-rebuild",
26153
+ activeCommit: base.commit,
26154
+ ...targetCommit ? { targetCommit } : {}
26155
+ };
26156
+ const warning = `${repo}: DRIFT incompatible index provenance \u2014 the active authority at ${base.commit} was not built by ${CURRENT_REPO_INDEX_PROVENANCE_TOKEN}; migrate it explicitly with \`oracle repo-index sync-estate --repo ${repo} --full-rebuild ${CURRENT_REPO_INDEX_PROVENANCE_TOKEN}\``;
26157
+ emit({ row: row2, warning });
26158
+ return { repo, row: row2, warning, base, expectedActiveDigest };
26084
26159
  }
26085
26160
  if (targetCommit && targetCommit === base.commit) {
26086
- drift.push({ repo, reason: "healthy", action: "skip", activeCommit: base.commit, targetCommit });
26087
- skipped.push(`${repo}: unchanged verified-ready authority at ${targetCommit}`);
26088
- continue;
26161
+ const row2 = { repo, reason: "healthy", action: "skip", activeCommit: base.commit, targetCommit };
26162
+ const warning = `${repo}: unchanged verified-ready authority at ${targetCommit}`;
26163
+ emit({ row: row2, warning });
26164
+ return { repo, row: row2, warning, base, expectedActiveDigest };
26089
26165
  }
26090
26166
  }
26091
- drift.push({
26167
+ const row = {
26092
26168
  repo,
26093
26169
  reason,
26094
26170
  action: "build",
26095
26171
  ...base ? { activeCommit: base.commit } : {},
26096
26172
  ...targetCommit ? { targetCommit } : {}
26097
- });
26098
- if (opts.plan) continue;
26173
+ };
26174
+ emit({ row });
26175
+ return { repo, row, base, expectedActiveDigest };
26176
+ });
26177
+ for (const entry of classified) {
26178
+ drift.push(entry.row);
26179
+ if (entry.warning) skipped.push(entry.warning);
26180
+ if (entry.row.action === "needs-full-rebuild") needsFullRebuild.push(entry.repo);
26181
+ }
26182
+ if (opts.plan) return answer();
26183
+ for (const entry of classified) {
26184
+ if (entry.row.action !== "build") continue;
26185
+ const { repo, base, expectedActiveDigest } = entry;
26099
26186
  const dir = (0, import_node_fs27.mkdtempSync)((0, import_node_path25.join)((0, import_node_os13.tmpdir)(), "mmi-repo-index-"));
26100
26187
  try {
26101
26188
  shallowClone(repo, dir, opts.githubToken);
@@ -26536,6 +26623,79 @@ function runSpawnPolicy(root) {
26536
26623
  var import_node_child_process17 = require("node:child_process");
26537
26624
  var import_node_fs30 = require("node:fs");
26538
26625
  var import_node_path27 = require("node:path");
26626
+
26627
+ // ../scripts/test-command-policy-core.mjs
26628
+ var TEST_COMMAND_CLASS = "test";
26629
+ function translateGlob(glob) {
26630
+ let out = "";
26631
+ for (let i = 0; i < glob.length; i += 1) {
26632
+ const char = glob[i];
26633
+ if (char === "*") {
26634
+ if (glob[i + 1] === "*") {
26635
+ if (glob[i + 2] === "/") {
26636
+ out += "(?:.*/)?";
26637
+ i += 2;
26638
+ } else {
26639
+ out += ".*";
26640
+ i += 1;
26641
+ }
26642
+ } else out += "[^/]*";
26643
+ } else if (char === "{") {
26644
+ const close = glob.indexOf("}", i);
26645
+ if (close === -1) out += "\\{";
26646
+ else {
26647
+ out += `(?:${glob.slice(i + 1, close).split(",").map(translateGlob).join("|")})`;
26648
+ i = close;
26649
+ }
26650
+ } else {
26651
+ out += /[.+?^${}()|[\]\\]/.test(char) ? `\\${char}` : char;
26652
+ }
26653
+ }
26654
+ return out;
26655
+ }
26656
+ function globToRegExp(glob) {
26657
+ return new RegExp(`^${translateGlob(glob)}$`);
26658
+ }
26659
+ function mandatoryGlobList(mandatory) {
26660
+ if (!Array.isArray(mandatory)) return [];
26661
+ return mandatory.map((entry) => typeof entry === "string" ? entry : entry?.glob).filter((glob) => typeof glob === "string");
26662
+ }
26663
+ function matchedMandatoryGlobs(paths, mandatory) {
26664
+ const globs = mandatoryGlobList(mandatory);
26665
+ const list = Array.isArray(paths) ? paths : [];
26666
+ return globs.filter((glob) => {
26667
+ const re = globToRegExp(glob);
26668
+ return list.some((path2) => re.test(path2));
26669
+ });
26670
+ }
26671
+ function evaluateTestCommandPolicy({ paths, mandatory, regulated = true } = {}) {
26672
+ const configuredMandatoryCount = mandatoryGlobList(mandatory).length;
26673
+ if (!regulated) {
26674
+ return {
26675
+ configuredMandatoryCount: 0,
26676
+ matchedMandatoryGlobs: [],
26677
+ matchedMandatoryCount: 0,
26678
+ testCommandsAllowed: true,
26679
+ reasonId: null,
26680
+ commandClasses: { allowed: [TEST_COMMAND_CLASS], refused: [] }
26681
+ };
26682
+ }
26683
+ const matched = matchedMandatoryGlobs(paths, mandatory);
26684
+ const testCommandsAllowed = matched.length > 0;
26685
+ return {
26686
+ configuredMandatoryCount,
26687
+ matchedMandatoryGlobs: matched,
26688
+ matchedMandatoryCount: matched.length,
26689
+ testCommandsAllowed,
26690
+ reasonId: testCommandsAllowed ? null : "test-command-outside-mandatory-zone",
26691
+ commandClasses: {
26692
+ allowed: testCommandsAllowed ? [TEST_COMMAND_CLASS] : [],
26693
+ refused: testCommandsAllowed ? [] : [TEST_COMMAND_CLASS]
26694
+ }
26695
+ };
26696
+ }
26697
+
26698
+ // src/test-policy-core.ts
26539
26699
  var POLICY_FILE = "test-policy.json";
26540
26700
  var TEST_RE = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
26541
26701
  var PY_TEST_RE = /(?:^|\/)test_[^/]*\.py$|_test\.py$/;
@@ -26581,7 +26741,7 @@ function translate(glob) {
26581
26741
  }
26582
26742
  return out;
26583
26743
  }
26584
- function globToRegExp(glob) {
26744
+ function globToRegExp2(glob) {
26585
26745
  return new RegExp(`^${translate(glob)}$`);
26586
26746
  }
26587
26747
  function isTestPath(path2) {
@@ -26815,7 +26975,7 @@ function isMeaningfulChange(path2, before, after) {
26815
26975
  return !a.every((t, k) => t.text === b[k].text && t.nl === b[k].nl);
26816
26976
  }
26817
26977
  function annotateChangeMeaning(changed, policy, read) {
26818
- const matchers = (policy.mandatory ?? []).map((m) => globToRegExp(m.glob));
26978
+ const matchers = (policy.mandatory ?? []).map((m) => globToRegExp2(m.glob));
26819
26979
  return changed.map((file) => {
26820
26980
  if (file.status !== "M" || !SUPPORTED_SOURCE.test(file.path)) return file;
26821
26981
  if (!matchers.some((re) => re.test(file.path)) && !isTestPath(file.path)) return file;
@@ -26853,7 +27013,7 @@ function removedPaths2(changed) {
26853
27013
  );
26854
27014
  }
26855
27015
  function classify(changed, policy, present = () => false) {
26856
- const matchers = (policy.mandatory ?? []).map((m) => ({ ...m, re: globToRegExp(m.glob) }));
27016
+ const matchers = (policy.mandatory ?? []).map((m) => ({ ...m, re: globToRegExp2(m.glob) }));
26857
27017
  const mandatoryHits = changed.filter((f) => matchers.some((m) => m.re.test(f.path)));
26858
27018
  const testChanges = changed.filter((f) => isTestPath(f.path));
26859
27019
  const addedTests = testChanges.filter((f) => f.status === "A");
@@ -27103,7 +27263,23 @@ function runTestPolicy(root, deps = {}) {
27103
27263
  };
27104
27264
  const blocking = sift([...refusal ? [refusal] : [], ...lookup.refusals, ...staleFindings]);
27105
27265
  const findings = blocking.length > 0 ? blocking : sift(evaluate(changed, policy, present));
27106
- const result = { ok: findings.length === 0, findings, changedCount: changed.length, base, ...counts };
27266
+ const commandPolicy = evaluateTestCommandPolicy({
27267
+ paths: changed.map((f) => f.path),
27268
+ mandatory: policy.mandatory,
27269
+ regulated: policy.declared !== false
27270
+ });
27271
+ const result = {
27272
+ ok: findings.length === 0,
27273
+ findings,
27274
+ changedCount: changed.length,
27275
+ base,
27276
+ ...counts,
27277
+ matchedMandatoryGlobs: commandPolicy.matchedMandatoryGlobs,
27278
+ matchedMandatoryCount: commandPolicy.matchedMandatoryCount,
27279
+ testCommandsAllowed: commandPolicy.testCommandsAllowed,
27280
+ testCommandReasonId: commandPolicy.reasonId,
27281
+ commandClasses: commandPolicy.commandClasses
27282
+ };
27107
27283
  if (override) {
27108
27284
  result.overriddenBy = override;
27109
27285
  result.waived = waived;
@@ -35568,6 +35744,7 @@ var surfaces_default = {
35568
35744
  targetPath: "packages/claude-plugin/scripts",
35569
35745
  include: [
35570
35746
  "pretooluse-shell-gates.mjs",
35747
+ "test-command-policy-core.mjs",
35571
35748
  "vault-edit-gate.mjs",
35572
35749
  "deny-gate-crash.mjs",
35573
35750
  "secret-echo-lint.mjs",
@@ -35694,6 +35871,7 @@ var surfaces_default = {
35694
35871
  targetPath: "packages/codex-plugin/scripts",
35695
35872
  include: [
35696
35873
  "pretooluse-shell-gates.mjs",
35874
+ "test-command-policy-core.mjs",
35697
35875
  "vault-edit-gate.mjs",
35698
35876
  "deny-gate-crash.mjs",
35699
35877
  "secret-echo-lint.mjs",
@@ -35812,6 +35990,7 @@ var surfaces_default = {
35812
35990
  targetPath: "packages/kimi-plugin/scripts",
35813
35991
  include: [
35814
35992
  "pretooluse-shell-gates.mjs",
35993
+ "test-command-policy-core.mjs",
35815
35994
  "vault-edit-gate.mjs",
35816
35995
  "deny-gate-crash.mjs",
35817
35996
  "secret-echo-lint.mjs",
@@ -35938,6 +36117,7 @@ var surfaces_default = {
35938
36117
  targetPath: "packages/cursor-plugin/scripts",
35939
36118
  include: [
35940
36119
  "pretooluse-shell-gates.mjs",
36120
+ "test-command-policy-core.mjs",
35941
36121
  "vault-edit-gate.mjs",
35942
36122
  "deny-gate-crash.mjs",
35943
36123
  "secret-echo-lint.mjs",
@@ -36060,6 +36240,7 @@ var surfaces_default = {
36060
36240
  targetPath: ".kilo-plugin/scripts",
36061
36241
  include: [
36062
36242
  "pretooluse-shell-gates.mjs",
36243
+ "test-command-policy-core.mjs",
36063
36244
  "vault-edit-gate.mjs",
36064
36245
  "deny-gate-crash.mjs",
36065
36246
  "secret-echo-lint.mjs",
@@ -36169,6 +36350,7 @@ var surfaces_default = {
36169
36350
  targetPath: ".pi-plugin/scripts",
36170
36351
  include: [
36171
36352
  "pretooluse-shell-gates.mjs",
36353
+ "test-command-policy-core.mjs",
36172
36354
  "vault-edit-gate.mjs",
36173
36355
  "deny-gate-crash.mjs",
36174
36356
  "secret-echo-lint.mjs",
@@ -36268,6 +36450,7 @@ var surfaces_default = {
36268
36450
  targetPath: "packages/hermes-plugin/scripts",
36269
36451
  include: [
36270
36452
  "pretooluse-shell-gates.mjs",
36453
+ "test-command-policy-core.mjs",
36271
36454
  "vault-edit-gate.mjs",
36272
36455
  "deny-gate-crash.mjs",
36273
36456
  "secret-echo-lint.mjs",
@@ -39553,7 +39736,15 @@ repoIndex.command("gc").description("remove v4 cloud authority material for repo
39553
39736
  consoleIo.log(JSON.stringify(res, null, 2));
39554
39737
  return;
39555
39738
  }
39556
- console.log(`repo-index: gc removed ${res.removed.length} orphan v4 authority record(s)`);
39739
+ let artifactsRemoved = 0;
39740
+ let artifactsKept = 0;
39741
+ for (const receipt of Object.values(res.v4.repos)) {
39742
+ artifactsRemoved += receipt.removed.length;
39743
+ artifactsKept += receipt.kept;
39744
+ }
39745
+ console.log(
39746
+ `repo-index: gc tombstoned ${res.removed.length} orphan authorit${res.removed.length === 1 ? "y" : "ies"}, removed ${artifactsRemoved} artifact(s); kept ${res.kept} authorit${res.kept === 1 ? "y" : "ies"}, ${artifactsKept} artifact(s)`
39747
+ );
39557
39748
  } catch (e) {
39558
39749
  return await failGraceful(e.message);
39559
39750
  }
@@ -39563,6 +39754,10 @@ repoIndex.command("sync-estate").description("Hub indexer: publish a pushed comm
39563
39754
  const cfg = await loadConfig();
39564
39755
  const gh = process.env.GH_TOKEN || process.env.GITHUB_TOKEN || "";
39565
39756
  if (!gh) await failGraceful("sync-estate needs GH_TOKEN or GITHUB_TOKEN with contents:read on target repos");
39757
+ if (!o.json) {
39758
+ console.log(`repo-index: pipeline provenance ${CURRENT_REPO_INDEX_PROVENANCE_TOKEN} \u2014 delta is the normal path${o.plan ? " (plan only: nothing was cloned, embedded or published)" : ""}`);
39759
+ }
39760
+ const streamedWarnings = /* @__PURE__ */ new Set();
39566
39761
  const res = await syncEstateRepoIndex({
39567
39762
  deps: registryClientDeps(cfg),
39568
39763
  repo: o.repo,
@@ -39570,26 +39765,30 @@ repoIndex.command("sync-estate").description("Hub indexer: publish a pushed comm
39570
39765
  fullRebuild: o.fullRebuild,
39571
39766
  skipRepos: o.skipRepo,
39572
39767
  plan: Boolean(o.plan),
39573
- githubToken: gh
39768
+ githubToken: gh,
39769
+ onClassify: o.json ? void 0 : ({ row, warning }) => {
39770
+ const at = row.targetCommit ? ` target=${row.targetCommit.slice(0, 12)}` : "";
39771
+ const active = row.activeCommit ? ` active=${row.activeCommit.slice(0, 12)}` : "";
39772
+ console.log(`repo-index: ${row.repo} ${row.reason} \u2192 ${row.action}${active}${at}`);
39773
+ if (warning) {
39774
+ streamedWarnings.add(warning);
39775
+ console.error(`repo-index: WARN ${warning}`);
39776
+ }
39777
+ }
39574
39778
  });
39575
39779
  if (o.json) {
39576
39780
  consoleIo.log(JSON.stringify(res, null, 2));
39577
39781
  if (!res.ok) process.exitCode = 1;
39578
39782
  return;
39579
39783
  }
39580
- console.log(`repo-index: pipeline provenance ${res.provenanceToken} \u2014 delta is the normal path${o.plan ? " (plan only: nothing was cloned, embedded or published)" : ""}`);
39581
- for (const row of res.drift) {
39582
- if (row.reason === "healthy") continue;
39583
- const at = row.targetCommit ? ` target=${row.targetCommit.slice(0, 12)}` : "";
39584
- const active = row.activeCommit ? ` active=${row.activeCommit.slice(0, 12)}` : "";
39585
- console.log(`repo-index: ${row.repo} ${row.reason} \u2192 ${row.action}${active}${at}`);
39586
- }
39587
39784
  for (const p of res.published) {
39588
39785
  const gap = p.embGap ?? Math.max(0, p.fileCount - (p.embCount ?? 0));
39589
39786
  console.log(`repo-index: published v4 ${p.repo} \u2014 ${p.fileCount} chunks emb=${p.embCount ?? 0}/${p.fileCount} gap=${gap} graph=${p.graphEdges ?? "unavailable"}`);
39590
39787
  if (p.metrics) console.log(`repo-index: ${formatV4BuildMetrics(p.repo, p.metrics)}`);
39591
39788
  }
39592
- for (const warning of res.skipped) console.error(`repo-index: WARN ${warning}`);
39789
+ for (const warning of res.skipped) {
39790
+ if (!streamedWarnings.has(warning)) console.error(`repo-index: WARN ${warning}`);
39791
+ }
39593
39792
  if (res.needsFullRebuild.length) {
39594
39793
  console.error(`repo-index: ${res.needsFullRebuild.length} repo(s) need an explicit tokened migration: ${res.needsFullRebuild.join(", ")}`);
39595
39794
  }
@@ -39668,7 +39867,7 @@ spawnCmd.command("policy").description("enforce the windowsHide contract across
39668
39867
  }
39669
39868
  });
39670
39869
  var tests = program2.command("tests").description("a repo's test-policy.json \u2014 the opt-in test contract and its enforcement");
39671
- tests.command("policy").description("enforce this repo's test-policy.json against the diff: a mandatory-zone change must carry a test, an unrequested new test file is refused, a `protected` test file may not be deleted or renamed away, and a `protected` entry naming a missing file is refused. Override any of them with a `Test-Policy-Override: <reason>` commit trailer (#3605)").option("--json", "machine-readable result: { ok, base, changedCount, findings[] }").option("--base <ref>", "comparison base (default: TEST_POLICY_BASE, then origin/development, then origin/main)").action(async (o) => {
39870
+ tests.command("policy").description("enforce this repo's test-policy.json against the diff: a mandatory-zone change must carry a test, an unrequested new test file is refused, a `protected` test file may not be deleted or renamed away, and a `protected` entry naming a missing file is refused. Override any of them with a `Test-Policy-Override: <reason>` commit trailer (#3605)").option("--json", "machine-readable result: { ok, base, changedCount, mandatoryCount, matchedMandatoryCount, matchedMandatoryGlobs, testCommandsAllowed, commandClasses, findings[] }").option("--base <ref>", "comparison base (default: TEST_POLICY_BASE, then origin/development, then origin/main)").action(async (o) => {
39672
39871
  try {
39673
39872
  const root = await repoRoot();
39674
39873
  const result = runTestPolicy(root, { base: o.base });
@@ -39684,8 +39883,9 @@ tests.command("policy").description("enforce this repo's test-policy.json agains
39684
39883
  return;
39685
39884
  }
39686
39885
  if (result.ok) {
39886
+ const commandVerdict = result.testCommandsAllowed ? "test commands: allowed" : `test commands: refused [${result.testCommandReasonId}]`;
39687
39887
  console.log(
39688
- `tests policy: OK (${result.changedCount} changed file(s); ${result.mandatoryCount} mandatory glob(s), ${result.protectedCount} protected file(s)).`
39888
+ `tests policy: OK (${result.changedCount} changed file(s); ${result.matchedMandatoryCount} of ${result.mandatoryCount} mandatory glob(s) matched, ${result.protectedCount} protected file(s); ${commandVerdict}).`
39689
39889
  );
39690
39890
  return;
39691
39891
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.1.2",
3
+ "version": "4.1.3",
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",