@mutmutco/codex-plugin 4.5.100 → 4.5.101

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.5.100",
3
+ "version": "4.5.101",
4
4
  "mmiCompat": "4.x",
5
5
  "description": "MMI workflow skills and org gates delivery.",
6
6
  "author": {
package/cli/dist/main.cjs CHANGED
@@ -6043,7 +6043,7 @@ function isRetryableGitHubWaitReadError(e) {
6043
6043
  const codes = /* @__PURE__ */ new Set();
6044
6044
  if (typeof status === "number" && status > 0) codes.add(status);
6045
6045
  for (const m of text.matchAll(/\bHTTP\/?\d*(?:\.\d)?\s+(\d{3})\b/g)) codes.add(Number(m[1]));
6046
- const rateLimited = e?.rateLimited === true || /API rate limit already exceeded|rate limit already exceeded|RATE_LIMITED|secondary rate|abuse detection/i.test(text);
6046
+ const rateLimited = e?.rateLimited === true || /rate limit (?:already )?exceeded|RATE_LIMITED|secondary rate|abuse detection/i.test(text);
6047
6047
  if ([401, 404, 409, 422].some((c) => codes.has(c))) return false;
6048
6048
  if (codes.has(403) && !rateLimited) return false;
6049
6049
  if (codes.has(429) || rateLimited) return true;
@@ -17604,10 +17604,10 @@ var rollout_plan_default = {
17604
17604
  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)."
17605
17605
  },
17606
17606
  baseline: {
17607
- version: "4.5.100",
17608
- tag: "v4.5.100",
17609
- commit: "ba367dd0d814",
17610
- npm: "@mutmutco/cli@4.5.100"
17607
+ version: "4.5.101",
17608
+ tag: "v4.5.101",
17609
+ commit: "a3c82f0f7aa3",
17610
+ npm: "@mutmutco/cli@4.5.101"
17611
17611
  },
17612
17612
  exitCriterion: "fleet-n-of-n",
17613
17613
  hubOnlyShortcut: "forbidden",
@@ -17624,14 +17624,14 @@ var rollout_plan_default = {
17624
17624
  repo: "mutmutco/mmi-hub",
17625
17625
  role: "canary",
17626
17626
  schedule: "train",
17627
- v3Target: "v4.5.100"
17627
+ v3Target: "v4.5.101"
17628
17628
  }
17629
17629
  ],
17630
17630
  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.",
17631
17631
  rollback: {
17632
17632
  independent: true,
17633
- mechanism: "npm dist-tag latest -> 4.5.100 and redeploy the Hub Lambda from tag v4.5.100 (ba367dd0d814); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
17634
- v3Target: "v4.5.100 (@mutmutco/cli@4.5.100, tag commit ba367dd0d814 \u2014 last known-good release carrying the repo-index v4-only contract)"
17633
+ mechanism: "npm dist-tag latest -> 4.5.101 and redeploy the Hub Lambda from tag v4.5.101 (a3c82f0f7aa3); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
17634
+ v3Target: "v4.5.101 (@mutmutco/cli@4.5.101, tag commit a3c82f0f7aa3 \u2014 last known-good release carrying the repo-index v4-only contract)"
17635
17635
  }
17636
17636
  },
17637
17637
  {
@@ -21823,7 +21823,7 @@ async function runTenantControl(deps, options) {
21823
21823
  const watch = options.watch ?? tenantControlWatches(action);
21824
21824
  const base = { command: "tenant-control", repo, stage, action };
21825
21825
  const since = (deps.now ?? Date.now)();
21826
- const d = await deps.dispatchTenantControl({ repo, stage, action, lines: options.lines, since: options.logSince, until: options.logUntil, task: options.task, artifact: options.artifact, confirmDataLoss: options.confirmDataLoss });
21826
+ const d = await deps.dispatchTenantControl({ repo, stage, action, lines: options.lines, edge: options.edge, since: options.logSince, until: options.logUntil, task: options.task, artifact: options.artifact, confirmDataLoss: options.confirmDataLoss });
21827
21827
  if (!d.ok) {
21828
21828
  const transport = d.category === "transport-failed";
21829
21829
  return {
@@ -25676,29 +25676,33 @@ Cut by ${formatSessionIdentity(cutBy)}
25676
25676
  `;
25677
25677
  }
25678
25678
  var PLAIN_WORDS_HEADING = "## In plain words";
25679
+ var PLAIN_WORDS_HEADING_TR = "## Sade s\xF6zlerle";
25679
25680
  var PLAIN_WORDS_MARKER = "<!-- mmi:big -->";
25680
- function buildPlainWordsSection(lines2, big) {
25681
+ function buildPlainWordsSection(lines2, big, heading = PLAIN_WORDS_HEADING) {
25681
25682
  const [title, ...notes] = lines2;
25682
- const body = [PLAIN_WORDS_HEADING, title ?? "", ...notes.slice(0, 5).map((n) => `- ${n}`)];
25683
+ const body = [heading, title ?? "", ...notes.slice(0, 5).map((n) => `- ${n}`)];
25683
25684
  if (big) body.push(PLAIN_WORDS_MARKER);
25684
25685
  return body.join("\n");
25685
25686
  }
25686
25687
  function noChangePlainWordsLine(suiteTag) {
25687
- return `No changes to this module in this release; ships with the MM-Work suite ${suiteTag}.`;
25688
+ return suiteTag ? `No changes to this module in this release; ships with the MM-Work suite ${suiteTag}.` : "No changes to this module in this release.";
25688
25689
  }
25689
25690
  function upsertPlainWordsSection(body, section) {
25690
- const lines2 = body.split(/\r?\n/);
25691
- const at = lines2.findIndex((l) => l.trim() === PLAIN_WORDS_HEADING);
25692
25691
  let rest = body;
25693
- if (at >= 0) {
25694
- let end = lines2.length;
25695
- for (let i = at + 1; i < lines2.length; i++) {
25696
- if (lines2[i].trim().startsWith("## ")) {
25697
- end = i;
25698
- break;
25692
+ for (const heading of [PLAIN_WORDS_HEADING, PLAIN_WORDS_HEADING_TR]) {
25693
+ const lines2 = rest.split(/\r?\n/);
25694
+ let stripping = false;
25695
+ let found = false;
25696
+ const kept = lines2.filter((line) => {
25697
+ if (line.trim() === heading) {
25698
+ stripping = true;
25699
+ found = true;
25700
+ return false;
25699
25701
  }
25700
- }
25701
- rest = [...lines2.slice(0, at), ...lines2.slice(end)].join("\n");
25702
+ if (line.trim().startsWith("## ")) stripping = false;
25703
+ return !stripping;
25704
+ });
25705
+ if (found) rest = kept.join("\n");
25702
25706
  }
25703
25707
  const trimmedRest = rest.trim();
25704
25708
  return trimmedRest ? `${section}
@@ -25711,8 +25715,11 @@ function plainWordsSummaryLines(content) {
25711
25715
  async function writePlainWordsIfSuiteMember(deps, args) {
25712
25716
  if (args.meta?.suite !== "mm-work") return;
25713
25717
  try {
25714
- const lines2 = args.summaryLines && args.summaryLines.length > 0 ? args.summaryLines : [noChangePlainWordsLine(args.suiteTag ?? args.tag)];
25715
- const section = buildPlainWordsSection(lines2, args.big === true);
25718
+ const lines2 = args.summaryLines && args.summaryLines.length > 0 ? args.summaryLines : [noChangePlainWordsLine(args.suiteTag)];
25719
+ const section = [
25720
+ buildPlainWordsSection(lines2, args.big === true),
25721
+ ...args.summaryLinesTr?.length ? [buildPlainWordsSection(args.summaryLinesTr, false, PLAIN_WORDS_HEADING_TR)] : []
25722
+ ].join("\n\n");
25716
25723
  const body = clean2(await deps.run("gh", ["release", "view", args.tag, "--repo", args.repo, "--json", "body", "-q", ".body"]));
25717
25724
  await deps.run("gh", ["release", "edit", args.tag, "--repo", args.repo, "--notes", upsertPlainWordsSection(body, section)]);
25718
25725
  } catch {
@@ -25721,7 +25728,8 @@ async function writePlainWordsIfSuiteMember(deps, args) {
25721
25728
  async function writePlainWordsSection(deps, options, repo, meta, tag) {
25722
25729
  try {
25723
25730
  const summaryLines = options.announceSummaryFile ? plainWordsSummaryLines(await (deps.readFile ?? ((p) => (0, import_promises3.readFile)(p, "utf8")))(options.announceSummaryFile)) : void 0;
25724
- await writePlainWordsIfSuiteMember(deps, { repo, tag, meta, summaryLines, big: options.big, suiteTag: options.suiteTag });
25731
+ const summaryLinesTr = options.announceSummaryFileTr ? plainWordsSummaryLines(await (deps.readFile ?? ((p) => (0, import_promises3.readFile)(p, "utf8")))(options.announceSummaryFileTr)) : void 0;
25732
+ await writePlainWordsIfSuiteMember(deps, { repo, tag, meta, summaryLines, summaryLinesTr, big: options.big, suiteTag: options.suiteTag });
25725
25733
  } catch {
25726
25734
  }
25727
25735
  }
@@ -47518,7 +47526,7 @@ function memberAnnounceSummaryFile(repo, summaryFile, memberSummaryFiles) {
47518
47526
  return repo === SUITE_REPO ? summaryFile : memberSummaryFiles[repo];
47519
47527
  }
47520
47528
  var suiteStateDir = (root) => (0, import_node_path43.join)(resolvePrimaryGitDir(root), "mmi-runtime", "suite");
47521
- function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}) {
47529
+ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr, memberSummaryFilesTr = {}) {
47522
47530
  const recordPath = (0, import_node_path43.join)(suiteStateDir(root), "cut.json");
47523
47531
  const base = (0, import_node_path43.join)(suiteStateDir(root), "checkouts");
47524
47532
  const checkout = async (repo) => {
@@ -47629,7 +47637,12 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}) {
47629
47637
  });
47630
47638
  const release = async (repo, expected, rcSha, opts) => inTrain(repo, async (deps) => {
47631
47639
  await prepareRcCheckout((args) => deps.run("git", args), repo);
47632
- const plainWords = { announceSummaryFile: memberAnnounceSummaryFile(repo, summaryFile, memberSummaryFiles), big: opts?.big, suiteTag: opts?.suiteTag };
47640
+ const plainWords = {
47641
+ announceSummaryFile: memberAnnounceSummaryFile(repo, summaryFile, memberSummaryFiles),
47642
+ announceSummaryFileTr: memberAnnounceSummaryFile(repo, summaryFileTr, memberSummaryFilesTr),
47643
+ big: opts?.big,
47644
+ suiteTag: opts?.suiteTag
47645
+ };
47633
47646
  const current = await tagSha(repo, expected);
47634
47647
  if (current) {
47635
47648
  await deps.run("git", ["fetch", "origin", "--tags"]);
@@ -47870,6 +47883,12 @@ function parseMemberSummaryFiles(entries) {
47870
47883
  }
47871
47884
  return memberSummaryFiles;
47872
47885
  }
47886
+ function validateSuiteReleaseSummary(path2) {
47887
+ if (!path2) throw new Error("suite release requires --announce-summary-file before promoting any member");
47888
+ const lines2 = (0, import_node_fs44.readFileSync)(path2, "utf8").split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
47889
+ if (lines2[0]?.startsWith("No changes to this module")) throw new Error("suite release requires a real MM-Work title, not the no-change placeholder");
47890
+ if (lines2.length < 3 || lines2.length > 6) throw new Error("suite release announcement needs 3\u20136 curated lines before promoting any member");
47891
+ }
47873
47892
  function suiteRcDataProdDeps() {
47874
47893
  return {
47875
47894
  fetch: async (url, init) => {
@@ -47964,18 +47983,17 @@ function registerSuiteCommands(program3) {
47964
47983
  return fail(`suite abandon: ${e.message}`);
47965
47984
  }
47966
47985
  });
47967
- suite.command("release").description("verify the cut and unchanged live backends, ship members then MM-Work, announce once").option("--owner-go", "explicit owner approval to release the tested candidate").option("--announce-summary-file <path>", "curated suite announcement, posted once after the train; also this cut's plain-words summary for MM-Work itself").option("--member-summary-file <owner/repo=path>", "repeatable: a member's OWN plain-words summary (#7510) \u2014 never the suite lead's summary reused; a member with no entry still ships, with the coordinator-ruled no-change line", (val, prev = []) => [...prev, val], []).option("--big", "marks MM-Work's own release big for the plain-words section (#7510); a member is never marked big through this flag \u2014 it is big only via the read route's automatic minor/major detection").option("--json", "structured receipt").action(async (o) => {
47986
+ suite.command("release").description("verify the cut and unchanged live backends, ship members then MM-Work, announce once").option("--owner-go", "explicit owner approval to release the tested candidate").option("--announce-summary-file <path>", "curated suite announcement, posted once after the train; also this cut's plain-words summary for MM-Work itself").option("--member-summary-file <owner/repo=path>", "repeatable: a member's OWN plain-words summary (#7510) \u2014 never the suite lead's summary reused; a member with no entry still ships, with the coordinator-ruled no-change line", (val, prev = []) => [...prev, val], []).option("--announce-summary-file-tr <path>", "Turkish plain-words summary for MM-Work itself").option("--member-summary-file-tr <owner/repo=path>", "repeatable: a member's own Turkish plain-words summary", (val, prev = []) => [...prev, val], []).option("--big", "marks MM-Work's own release big for the plain-words section (#7510); a member is never marked big through this flag \u2014 it is big only via the read route's automatic minor/major detection").option("--json", "structured receipt").action(async (o) => {
47968
47987
  try {
47969
47988
  if (!o.ownerGo) throw new Error("suite release requires --owner-go; nothing was written");
47970
- if (!o.announceSummaryFile) throw new Error("suite release requires --announce-summary-file before promoting any member");
47971
- const lines2 = (0, import_node_fs44.readFileSync)(o.announceSummaryFile, "utf8").split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
47972
- if (lines2.length < 3 || lines2.length > 6) throw new Error("suite release announcement needs 3\u20136 curated lines before promoting any member");
47989
+ validateSuiteReleaseSummary(o.announceSummaryFile);
47973
47990
  const memberSummaryFiles = parseMemberSummaryFiles(o.memberSummaryFile ?? []);
47991
+ const memberSummaryFilesTr = parseMemberSummaryFiles(o.memberSummaryFileTr ?? []);
47974
47992
  const root = await leadRoot();
47975
47993
  const unlock = await acquireTrainWriterLock(root, "suite release");
47976
47994
  let result;
47977
47995
  try {
47978
- result = await suiteRelease(suiteTrainIo(root, o.announceSummaryFile, memberSummaryFiles), true, o.big === true);
47996
+ result = await suiteRelease(suiteTrainIo(root, o.announceSummaryFile, memberSummaryFiles, o.announceSummaryFileTr, memberSummaryFilesTr), true, o.big === true);
47979
47997
  } finally {
47980
47998
  await unlock();
47981
47999
  }
@@ -54316,7 +54334,7 @@ async function reportWrite(label, res) {
54316
54334
  return failGraceful(`${label}: HTTP ${res.status}${detail ? ` \u2014 ${detail}` : ""}`);
54317
54335
  }
54318
54336
  var tenant = program2.command("tenant").description("tenant runtime control through Hub authority");
54319
- tenant.command("control <owner/repo> <stage> <action>").description("bounded tenant control plus value-free vault/broker verification; dev/main retire needs master and exact data-loss confirmation").option("--watch", "block on the dispatched run and report its conclusion (status/retire/verify-secrets/verify-broker/logs watch by default)").option("--lines <n>", "logs only: trailing lines of the tenant service to return (1-2000, default 200)").option("--since <utc>", "logs only: read the journal from this UTC time, e.g. 2026-10-07T13:10Z (includes replaced containers)").option("--until <utc>", "logs only: read the journal up to this UTC time").option("--task <name>", "run-task only: registry-declared task name (never a command)").option("--artifact <id>", "run-task only: opaque id returned by tenant artifact put").option("--confirm-data-loss <owner/repo/stage>", "dev/main retire only: exact target to decommission; without it show the dry-run plan").option("--json", "machine-readable output").action(async (repo, stage, action, o) => {
54337
+ tenant.command("control <owner/repo> <stage> <action>").description("bounded tenant control plus value-free vault/broker verification; dev/main retire needs master and exact data-loss confirmation").option("--watch", "block on the dispatched run and report its conclusion (status/retire/verify-secrets/verify-broker/logs watch by default)").option("--lines <n>", "logs only: trailing lines of the tenant service to return (1-2000, default 200)").option("--since <utc>", "logs only: read the journal from this UTC time, e.g. 2026-10-07T13:10Z (includes replaced containers)").option("--until <utc>", "logs only: read the journal up to this UTC time").option("--edge", "logs only: Caddy journal for this stage\u2019s hosts (default window: last 15 minutes)").option("--task <name>", "run-task only: registry-declared task name (never a command)").option("--artifact <id>", "run-task only: opaque id returned by tenant artifact put").option("--confirm-data-loss <owner/repo/stage>", "dev/main retire only: exact target to decommission; without it show the dry-run plan").option("--json", "machine-readable output").action(async (repo, stage, action, o) => {
54320
54338
  try {
54321
54339
  if (o.confirmDataLoss !== void 0 && (action !== "retire" || stage === "rc")) return fail("runtime tenant control: --confirm-data-loss is dev/main retire-only");
54322
54340
  if (action === "retire" && (stage === "dev" || stage === "main")) {
@@ -54329,6 +54347,7 @@ Dry run: pass --confirm-data-loss ${repo}/${stage} to decommission.`);
54329
54347
  return;
54330
54348
  }
54331
54349
  }
54350
+ if (o.edge && action !== "logs") return fail("runtime tenant control: --edge is valid only for the logs action");
54332
54351
  let lines2;
54333
54352
  if (o.lines !== void 0) {
54334
54353
  if (action !== "logs") return fail("runtime tenant control: --lines is valid only for the logs action");
@@ -54345,7 +54364,7 @@ Dry run: pass --confirm-data-loss ${repo}/${stage} to decommission.`);
54345
54364
  } else if (o.task || o.artifact) {
54346
54365
  return fail("runtime tenant control: --task/--artifact are valid only for run-task");
54347
54366
  }
54348
- const result = await runTenantControl(trainApplyDeps(), { repo, stage, action, watch: o.watch, lines: lines2, logSince: o.since, logUntil: o.until, task: o.task, artifact: o.artifact, confirmDataLoss: o.confirmDataLoss });
54367
+ const result = await runTenantControl(trainApplyDeps(), { repo, stage, action, watch: o.watch, lines: lines2, edge: o.edge, logSince: o.since, logUntil: o.until, task: o.task, artifact: o.artifact, confirmDataLoss: o.confirmDataLoss });
54349
54368
  if (!o.json && action === "verify-secrets" && result.secrets) {
54350
54369
  const body = { ok: result.conclusion === "success", secrets: result.secrets, ssmStatus: result.conclusion === "success" ? "Success" : "Failed", raw: result.secretsRaw };
54351
54370
  const { lines: lines3, failure } = renderVerifySecrets(body);
package/cli/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.5.100",
3
+ "version": "4.5.101",
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.5.100",
3
+ "version": "4.5.101",
4
4
  "description": "MMI workflow skills and org gates delivery.",
5
5
  "author": {
6
6
  "name": "MMI Future",
@@ -109,6 +109,7 @@ commit, so build and verify the lines from one pass over that range —
109
109
  subject, body and changed files — never from subjects, PR titles or memory; a line whose claim is not
110
110
  in that output does not go in the file. A Hub summary never mentions a Jerv-side project; the train
111
111
  refuses such a line.
112
+ MM-Work suite release also requires a real `--announce-summary-file` title (never `No changes to this module`); optional `--announce-summary-file-tr` and repeatable `--member-summary-file-tr owner/repo=path` add Turkish plain words alongside English member files supplied with `--member-summary-file`.
112
113
 
113
114
  Announcement delivery uses the developer's GitHub-authenticated Hub session. The Hub checks project
114
115
  authority, resolves the registered channel, and reads the MMI Future Slack key inside its vault boundary.
@@ -15,6 +15,9 @@ Full-track repos ship exactly what is on `rc` (never `development`): merge `rc
15
15
 
16
16
  In the suite lead (MM-Work) a plain rcand, release or hotfix refuses: run `mmi-cli devops suite rcand --owner-go` or `mmi-cli devops suite release --owner-go` instead (a lead-only cut needs `--suite-solo --owner-go`). A suite member cannot run standalone rcand, release or hotfix: cut from the suite lead; only a backward-compatible solo backend release/hotfix with explicit owner approval may pass `--suite-solo --owner-go`, and its MM-Work development pin PR must land before the suite is in sync.
17
17
 
18
+ Suite release requires `--announce-summary-file <path>` with 3–6 curated lines and refuses before promotion when it is missing or its title starts with `No changes to this module`.
19
+ Use `--member-summary-file <owner/repo=path>` for English member notes, `--announce-summary-file-tr <path>` for MM-Work's Turkish notes and repeatable `--member-summary-file-tr <owner/repo=path>` for Turkish member notes; members without English files keep the no-change line.
20
+
18
21
  Read META and name the lane before describing or executing anything:
19
22
 
20
23
  ```bash