@mutmutco/claude-plugin 4.6.42 → 4.6.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/cli/dist/main.cjs +94 -20
- package/cli/package.json +1 -1
- package/package.json +1 -1
- package/skills/bootstrap/seeds/gate.template.yml +3 -2
- package/skills/bootstrap/seeds/manifest.json +2 -2
- package/skills/bootstrap/seeds/readme-mmi-developer-environment.block.md +1 -1
- package/skills/bootstrap/seeds/runbook-unaffected.json +6 -6
- package/skills/hotfix/SKILL.md +1 -1
- package/skills/hotfix/suite.md +1 -2
- package/skills/org-guide/references/design.md +5 -2
- package/skills/org-guide/references/work-and-releases.md +6 -5
- package/skills/rcand/SKILL.md +1 -1
- package/skills/rcand/suite.md +1 -4
- package/skills/release/SKILL.md +1 -1
- package/skills/release/suite.md +2 -6
- package/skills/release/verification.md +1 -1
package/cli/dist/main.cjs
CHANGED
|
@@ -25273,6 +25273,31 @@ function missingPlaceholders(rendered) {
|
|
|
25273
25273
|
for (const m of rendered.matchAll(PLACEHOLDER_RE)) out.add(m[1]);
|
|
25274
25274
|
return [...out];
|
|
25275
25275
|
}
|
|
25276
|
+
function seedMatchesReleaseTrack(seed, releaseTrack) {
|
|
25277
|
+
if (!seed.releaseTracks?.length) return true;
|
|
25278
|
+
return releaseTrack != null && seed.releaseTracks.includes(releaseTrack);
|
|
25279
|
+
}
|
|
25280
|
+
var TRAIN_ONLY_BEGIN = "mmi-train-only:start";
|
|
25281
|
+
var TRAIN_ONLY_END = "mmi-train-only:end";
|
|
25282
|
+
function renderSelfSeedForTrack(content, releaseTrack) {
|
|
25283
|
+
if (releaseTrack !== "single") return content;
|
|
25284
|
+
const lines2 = content.split(/(?<=\n)/);
|
|
25285
|
+
const out = [];
|
|
25286
|
+
let skipping = false;
|
|
25287
|
+
for (const line of lines2) {
|
|
25288
|
+
if (!skipping && line.includes(TRAIN_ONLY_BEGIN)) {
|
|
25289
|
+
skipping = true;
|
|
25290
|
+
continue;
|
|
25291
|
+
}
|
|
25292
|
+
if (skipping) {
|
|
25293
|
+
if (line.includes(TRAIN_ONLY_END)) skipping = false;
|
|
25294
|
+
continue;
|
|
25295
|
+
}
|
|
25296
|
+
out.push(line);
|
|
25297
|
+
}
|
|
25298
|
+
if (skipping) throw new Error(`unterminated ${TRAIN_ONLY_BEGIN} region`);
|
|
25299
|
+
return out.join("");
|
|
25300
|
+
}
|
|
25276
25301
|
function isPropagatableSeed(seed) {
|
|
25277
25302
|
return seed.ownership === "org" && seed.source === "self" || seed.managedBlock != null;
|
|
25278
25303
|
}
|
|
@@ -26639,6 +26664,7 @@ function withDerivedRepoVars(vars, parsed, cls, releaseTrack, requiredCheckBranc
|
|
|
26639
26664
|
out.CLASS ??= cls;
|
|
26640
26665
|
out.PROJECT_OWNER ??= parsed.owner;
|
|
26641
26666
|
const track = releaseTrack ?? resolveBootstrapReleaseTrack(cls);
|
|
26667
|
+
out.RELEASE_TRACK ??= track;
|
|
26642
26668
|
const runtime = out.GATE_RUNTIME === "python" ? "python" : "node";
|
|
26643
26669
|
for (const [key, value] of Object.entries(gateSeedVars(cls, track, runtime, requiredCheckBranches, requiredChecks))) {
|
|
26644
26670
|
out[key] ??= value;
|
|
@@ -26998,7 +27024,10 @@ function renderBootstrapAncillaryPlan(labels, environments, exceptions = []) {
|
|
|
26998
27024
|
].join("\n");
|
|
26999
27025
|
}
|
|
27000
27026
|
function resolveSeedContent(seed, vars, readFile11) {
|
|
27001
|
-
if (seed.source === "self")
|
|
27027
|
+
if (seed.source === "self") {
|
|
27028
|
+
const own = readFile11(seed.target);
|
|
27029
|
+
return own == null ? null : renderSelfSeedForTrack(own, vars.RELEASE_TRACK);
|
|
27030
|
+
}
|
|
27002
27031
|
if (seed.source.startsWith("seed:")) {
|
|
27003
27032
|
const tmpl = readFile11(`skills/bootstrap/seeds/${seed.source.slice("seed:".length)}`);
|
|
27004
27033
|
return tmpl == null ? null : renderSeed(tmpl, vars);
|
|
@@ -28527,10 +28556,10 @@ var rollout_plan_default = {
|
|
|
28527
28556
|
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)."
|
|
28528
28557
|
},
|
|
28529
28558
|
baseline: {
|
|
28530
|
-
version: "4.6.
|
|
28531
|
-
tag: "v4.6.
|
|
28532
|
-
commit: "
|
|
28533
|
-
npm: "@mutmutco/cli@4.6.
|
|
28559
|
+
version: "4.6.44",
|
|
28560
|
+
tag: "v4.6.44",
|
|
28561
|
+
commit: "2ce6b7e5afc3",
|
|
28562
|
+
npm: "@mutmutco/cli@4.6.44"
|
|
28534
28563
|
},
|
|
28535
28564
|
exitCriterion: "fleet-n-of-n",
|
|
28536
28565
|
hubOnlyShortcut: "forbidden",
|
|
@@ -28547,14 +28576,14 @@ var rollout_plan_default = {
|
|
|
28547
28576
|
repo: "mutmutco/mmi-hub",
|
|
28548
28577
|
role: "canary",
|
|
28549
28578
|
schedule: "train",
|
|
28550
|
-
v3Target: "v4.6.
|
|
28579
|
+
v3Target: "v4.6.44"
|
|
28551
28580
|
}
|
|
28552
28581
|
],
|
|
28553
28582
|
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.",
|
|
28554
28583
|
rollback: {
|
|
28555
28584
|
independent: true,
|
|
28556
|
-
mechanism: "npm dist-tag latest -> 4.6.
|
|
28557
|
-
v3Target: "v4.6.
|
|
28585
|
+
mechanism: "npm dist-tag latest -> 4.6.44 and redeploy the Hub Lambda from tag v4.6.44 (2ce6b7e5afc3); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
28586
|
+
v3Target: "v4.6.44 (@mutmutco/cli@4.6.44, tag commit 2ce6b7e5afc3 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
28558
28587
|
}
|
|
28559
28588
|
},
|
|
28560
28589
|
{
|
|
@@ -31670,17 +31699,17 @@ function isBundleOnlySuiteMember(repo, suite) {
|
|
|
31670
31699
|
if (!suite) return false;
|
|
31671
31700
|
return repo.split("/")[1]?.toLowerCase() !== suite.toLowerCase();
|
|
31672
31701
|
}
|
|
31702
|
+
var MM_GOES_LIVE = "MM-Work and its modules merge to development and it goes live; there is no release, rc or candidate stage.";
|
|
31673
31703
|
function suiteMemberRefusal(repo, meta, lane, solo = false, cut) {
|
|
31674
31704
|
const suite = meta?.suite;
|
|
31675
31705
|
if (!suite) return null;
|
|
31676
31706
|
if (!isBundleOnlySuiteMember(repo, suite)) {
|
|
31677
31707
|
if (solo || cut?.token === SUITE_LEAD_TOKEN && cut.repo.toLowerCase() === repo.toLowerCase()) return null;
|
|
31678
|
-
|
|
31679
|
-
return `${repo} is the ${suite} suite lead; a plain ${lane} would cut it without its members: ${route}. A lead-only ${lane} needs --suite-solo --owner-go.`;
|
|
31708
|
+
return `${repo} is the ${suite} suite lead; ${MM_GOES_LIVE} There is no ${lane} to run: land the change through a normal PR to development and state what landed.`;
|
|
31680
31709
|
}
|
|
31681
31710
|
if (authorisedSuiteCut(repo, meta, cut)) return null;
|
|
31682
31711
|
if (lane !== "rcand" && solo) return null;
|
|
31683
|
-
return `${repo} is part of the ${suite} suite;
|
|
31712
|
+
return `${repo} is part of the ${suite} suite; ${MM_GOES_LIVE} A suite member never asks for, proposes or prompts an rc, release or hotfix: there is none, so state what landed and stop.`;
|
|
31684
31713
|
}
|
|
31685
31714
|
|
|
31686
31715
|
// src/tenant-control-parse.ts
|
|
@@ -42162,6 +42191,31 @@ function compactRouteManifest(manifest, onlyHouse) {
|
|
|
42162
42191
|
}
|
|
42163
42192
|
};
|
|
42164
42193
|
}
|
|
42194
|
+
function filterCompactRouteManifest(output, match) {
|
|
42195
|
+
return { ...output, routes: output.routes.filter((route) => match.test(route.path)) };
|
|
42196
|
+
}
|
|
42197
|
+
function pruneManifestTree(node, match) {
|
|
42198
|
+
const subcommands = node.subcommands.map((child2) => pruneManifestTree(child2, match)).filter((child2) => child2 !== void 0);
|
|
42199
|
+
if (match.test(node.name) || match.test(node.path) || subcommands.length > 0) {
|
|
42200
|
+
return { ...node, subcommands };
|
|
42201
|
+
}
|
|
42202
|
+
return void 0;
|
|
42203
|
+
}
|
|
42204
|
+
function filterTreeAndIndex(tree, index, match) {
|
|
42205
|
+
return {
|
|
42206
|
+
tree: pruneManifestTree(tree, match) ?? { ...tree, subcommands: [] },
|
|
42207
|
+
index: index.filter((leaf) => match.test(leaf.name) || match.test(leaf.path))
|
|
42208
|
+
};
|
|
42209
|
+
}
|
|
42210
|
+
function filterCommandManifest(manifest, match) {
|
|
42211
|
+
const full = filterTreeAndIndex(manifest.tree, manifest.index, match);
|
|
42212
|
+
const primary = filterTreeAndIndex(manifest.primary_tree, manifest.primary_index, match);
|
|
42213
|
+
return { ...manifest, tree: full.tree, index: full.index, primary_tree: primary.tree, primary_index: primary.index };
|
|
42214
|
+
}
|
|
42215
|
+
function filterPrimaryCommandManifest(manifest, match) {
|
|
42216
|
+
const filtered = filterTreeAndIndex(manifest.tree, manifest.index, match);
|
|
42217
|
+
return { ...manifest, tree: filtered.tree, index: filtered.index };
|
|
42218
|
+
}
|
|
42165
42219
|
var DOOR_DECLARATIONS = [
|
|
42166
42220
|
{ door: "board", question: "What is on my board \u2014 and what is next?", path: "board read", source: "the live GitHub Project v2 board, read through the Hub board API" },
|
|
42167
42221
|
{ door: "registry", question: "What repos and projects exist, with what META?", path: "org project list", source: "the Hub org registry (DDB)" },
|
|
@@ -51008,7 +51062,7 @@ async function verifyBootstrap(repo, repoClass, deps, releaseTrack) {
|
|
|
51008
51062
|
detail: `expected: ${requiredProductRulesetRef} (apply as an active repo ruleset after bootstrap)`
|
|
51009
51063
|
});
|
|
51010
51064
|
}
|
|
51011
|
-
if (repoClass === "deployable") {
|
|
51065
|
+
if (repoClass === "deployable" && effectiveTrack !== "single") {
|
|
51012
51066
|
const trainScript = "scripts/next-version.mjs";
|
|
51013
51067
|
checks.push({ ok: await contentExists2(deps, repo, baseBranch, trainScript), label: `train tooling script exists: ${trainScript}` });
|
|
51014
51068
|
}
|
|
@@ -51734,7 +51788,9 @@ function registerBootstrapCommands(program3) {
|
|
|
51734
51788
|
for (const repo of targets) {
|
|
51735
51789
|
const cls = classOf(repo);
|
|
51736
51790
|
const meta = metaOf(repo);
|
|
51737
|
-
const
|
|
51791
|
+
const track = resolveReleaseTrack(metaWithDeclaredClass(meta, cls), void 0, repo);
|
|
51792
|
+
const seeds = byteComparableSeeds(manifest, cls).filter((s) => seedMatchesReleaseTrack(s, track));
|
|
51793
|
+
const trackHubContents = new Map([...hubContents].map(([t, c]) => [t, c == null ? c : renderSelfSeedForTrack(c, track)]));
|
|
51738
51794
|
seedsPerRepo = Math.max(seedsPerRepo, seeds.length);
|
|
51739
51795
|
const baseBranch = cls === "content" ? "main" : "development";
|
|
51740
51796
|
const reads = [];
|
|
@@ -51749,9 +51805,8 @@ function registerBootstrapCommands(program3) {
|
|
|
51749
51805
|
}
|
|
51750
51806
|
reads.push({ target: seed.target, content });
|
|
51751
51807
|
}
|
|
51752
|
-
findings.push(...auditRepoSeedDrift(repo, seeds,
|
|
51808
|
+
findings.push(...auditRepoSeedDrift(repo, seeds, trackHubContents, reads));
|
|
51753
51809
|
findings.push(...declaredRenderWaivers(manifest, repo));
|
|
51754
|
-
const track = resolveReleaseTrack(metaWithDeclaredClass(meta, cls), void 0, repo);
|
|
51755
51810
|
const github = await readBootstrapGitHubFacts(repo, track, controlClient);
|
|
51756
51811
|
const deployFacts = await fetchDeployFactsBySlug((repo.split("/").pop() ?? repo).toLowerCase(), registryClientDeps(await loadConfig()));
|
|
51757
51812
|
const controlRows = planBootstrapControlPlane({
|
|
@@ -52176,6 +52231,7 @@ function registerBootstrapCommands(program3) {
|
|
|
52176
52231
|
if (!seed.classes.includes(o.class)) continue;
|
|
52177
52232
|
if (!seedMatchesDeployModel(seed, applyDeployModel)) continue;
|
|
52178
52233
|
if (!seedMatchesProjectType(seed, applyProjectType)) continue;
|
|
52234
|
+
if (!seedMatchesReleaseTrack(seed, vars.RELEASE_TRACK)) continue;
|
|
52179
52235
|
const waiver = seed.waivers?.[slug];
|
|
52180
52236
|
if (waiver) {
|
|
52181
52237
|
actions.push({ action: "skip", target: seed.target, ownership: seed.ownership, reason: `waived: ${waiver}` });
|
|
@@ -52670,7 +52726,12 @@ LIVE apply to ${repo}:
|
|
|
52670
52726
|
const readSeedFile2 = (path2) => (0, import_node_fs40.existsSync)(path2) ? (0, import_node_fs40.readFileSync)(path2, "utf8") : null;
|
|
52671
52727
|
const isWorkflowSeed = seed.target.startsWith(".github/workflows/");
|
|
52672
52728
|
const { projects, classOf, inClassRepos, canarySlug, sourcePilot } = await propagationScope(seed.classes, `target '${seed.target}'`);
|
|
52673
|
-
const
|
|
52729
|
+
const trackOf = (repo) => resolveReleaseTrack(
|
|
52730
|
+
projects.find((p) => (p.repos ?? []).some((r) => r.toLowerCase() === repo.toLowerCase())),
|
|
52731
|
+
void 0,
|
|
52732
|
+
repo
|
|
52733
|
+
);
|
|
52734
|
+
const repos = inClassRepos.filter((repo) => seedMatchesReleaseTrack(seed, trackOf(repo))).map((repo) => {
|
|
52674
52735
|
const slug = repo.split("/").pop().toLowerCase();
|
|
52675
52736
|
const waiver = seed.waivers?.[slug];
|
|
52676
52737
|
return { repo, slug, waiver };
|
|
@@ -52718,7 +52779,7 @@ LIVE apply to ${repo}:
|
|
|
52718
52779
|
}
|
|
52719
52780
|
desired = resolved.content;
|
|
52720
52781
|
} else {
|
|
52721
|
-
desired = hubContent;
|
|
52782
|
+
desired = renderSelfSeedForTrack(hubContent, trackOf(r.repo));
|
|
52722
52783
|
}
|
|
52723
52784
|
desiredByRepo.set(r.repo, desired);
|
|
52724
52785
|
const drift = compareSeedBytes(desired, content);
|
|
@@ -59669,7 +59730,7 @@ function branchRulesReadHint(path2) {
|
|
|
59669
59730
|
const match = new RegExp(`^repos/(${NAME})/(${NAME})/(?:rules/branches/[^/?#]+|branches/[^/?#]+/protection(?:/|$)|rulesets(?:/|$))`).exec(path2.split("?")[0]);
|
|
59670
59731
|
if (!match) return "";
|
|
59671
59732
|
const slug = `${match[1]}/${match[2]}`;
|
|
59672
|
-
return ` No raw rules, rulesets or branch-protection read is on the App allowlist, so retrying path or query variants cannot work \u2014 inspect a PR's required status contexts with \`MMI_ACTOR=app mmi-cli devops pr checks-required <pr> --repo ${slug} --json\`, or verify repository ruleset and branch-protection health with \`mmi-cli devops bootstrap verify --repo ${slug} --json\` and \`mmi-cli devops ci audit --repo ${slug} --json
|
|
59733
|
+
return ` No raw rules, rulesets or branch-protection read is on the App allowlist, so retrying path or query variants cannot work \u2014 inspect a PR's required status contexts with \`MMI_ACTOR=app mmi-cli devops pr checks-required <pr> --repo ${slug} --json\`, or verify repository ruleset and branch-protection health with \`mmi-cli devops bootstrap verify --repo ${slug} --json\` and \`mmi-cli devops ci audit --repo ${slug} --json\`. Stepping around it with a raw \`gh api repos/${slug}/rulesets/<id> > file\` is not a substitute \u2014 when that read fails it leaves an EMPTY file, and a later require/JSON.parse throws 'Unexpected end of JSON input' that looks like corrupt data while the real refusal is only on stderr; check the exit code and file size, or ask a human token holder for the raw ruleset body.`;
|
|
59673
59734
|
}
|
|
59674
59735
|
function commitsReadHint(path2) {
|
|
59675
59736
|
const clean4 = path2.split("?")[0];
|
|
@@ -73693,11 +73754,24 @@ function registerDeveloperCommands(program3) {
|
|
|
73693
73754
|
console.log("mmi-cli devops org rules gitignore: up to date");
|
|
73694
73755
|
}
|
|
73695
73756
|
});
|
|
73696
|
-
program3.command("commands").description("print the grouped agentic-coding map; JSON defaults to bounded routes").option("--all", "human: include operational depth; JSON: emit the exhaustive schema-2 manifest").option("--json", "machine-readable progressive discovery").option("--primary", "with --json, emit the compatibility schema-2 primary tree and index").addOption(new Option("--house <house>", "with --json, limit routes to one house").choices([...HOUSE_ROOTS, "core"])).action((o) => {
|
|
73757
|
+
program3.command("commands").description("print the grouped agentic-coding map; JSON defaults to bounded routes").option("--all", "human: include operational depth; JSON: emit the exhaustive schema-2 manifest").option("--json", "machine-readable progressive discovery; the compact default emits an OBJECT {schema_version, scope:'routes', routes[{path,house,summary,detail}], houses, doors, next{full:'mmi-cli commands --all --json'}} \u2014 never a top-level `commands` array; --all --json emits the exhaustive schema-2 manifest {scope:'all', tree, index, houses, doors, flat_alias_cut, primary_tree, primary_index, error_codes}").option("--primary", "with --json, emit the compatibility schema-2 primary tree and index").addOption(new Option("--house <house>", "with --json, limit routes to one house").choices([...HOUSE_ROOTS, "core"])).addOption(new Option("--match <regex>", "with --json, keep only routes/commands whose path or name matches this regular expression (case-insensitive)")).action((o) => {
|
|
73697
73758
|
if (o.house && !o.json) {
|
|
73698
73759
|
fail("commands: --house requires --json", { code: ERROR_CODES.ERR_CONFLICTING_FLAGS, offending_flag: "--house" });
|
|
73699
73760
|
return;
|
|
73700
73761
|
}
|
|
73762
|
+
if (o.match && !o.json) {
|
|
73763
|
+
fail("commands: --match requires --json", { code: ERROR_CODES.ERR_CONFLICTING_FLAGS, offending_flag: "--match" });
|
|
73764
|
+
return;
|
|
73765
|
+
}
|
|
73766
|
+
let match;
|
|
73767
|
+
if (o.match) {
|
|
73768
|
+
try {
|
|
73769
|
+
match = new RegExp(o.match, "i");
|
|
73770
|
+
} catch {
|
|
73771
|
+
fail(`commands: --match '${o.match}' is not a valid regular expression`, { code: ERROR_CODES.ERR_INVALID_INPUT, offending_flag: "--match" });
|
|
73772
|
+
return;
|
|
73773
|
+
}
|
|
73774
|
+
}
|
|
73701
73775
|
const conflicting = [o.all ? "--all" : "", o.primary ? "--primary" : "", o.house ? "--house" : ""].filter(Boolean);
|
|
73702
73776
|
if (o.json && conflicting.length > 1) {
|
|
73703
73777
|
fail(`commands: ${conflicting.join(" and ")} are mutually exclusive`, { code: ERROR_CODES.ERR_CONFLICTING_FLAGS, offending_flag: conflicting.join(",") });
|
|
@@ -73705,7 +73779,7 @@ function registerDeveloperCommands(program3) {
|
|
|
73705
73779
|
}
|
|
73706
73780
|
const manifest = buildCommandManifest(program3);
|
|
73707
73781
|
if (o.json) {
|
|
73708
|
-
const output = o.all ? manifest : o.primary ? primaryCommandManifest(manifest) : compactRouteManifest(manifest, o.house);
|
|
73782
|
+
const output = o.all ? match ? filterCommandManifest(manifest, match) : manifest : o.primary ? match ? filterPrimaryCommandManifest(primaryCommandManifest(manifest), match) : primaryCommandManifest(manifest) : match ? filterCompactRouteManifest(compactRouteManifest(manifest, o.house), match) : compactRouteManifest(manifest, o.house);
|
|
73709
73783
|
consoleIo.log(JSON.stringify(output, null, 2));
|
|
73710
73784
|
return;
|
|
73711
73785
|
}
|
package/cli/package.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
name: gate
|
|
2
2
|
# Org-standard green-before-merge gate for product repos (#1333). Runs on PRs, train-branch pushes,
|
|
3
|
-
# and v* tags so /release and /hotfix can discover required contexts on the tagged SHA
|
|
3
|
+
# and v* tags so /release and /hotfix can discover required contexts on the tagged SHA (repos with a
|
|
4
|
+
# release train only; MM-Work and its modules have none — they merge to development and it goes live).
|
|
4
5
|
#
|
|
5
6
|
# Runner (#9248, owner 29 Oct 2026): included GitHub-hosted capacity is FIRST CHOICE for CI — this gate
|
|
6
7
|
# runs on `ubuntu-latest`, which costs included Enterprise minutes, not spend. Self-hosted lanes
|
|
@@ -196,7 +197,7 @@ jobs:
|
|
|
196
197
|
- run: {{GATE_INSTALL_CMD}}
|
|
197
198
|
if: ${{ steps.development-gate.outputs.reuse != 'true' }}{{GATE_JEV_FETCH_YAML}}{{GATE_CHECK_POLICY_STEP_YAML}}
|
|
198
199
|
# #3178: the check runs under the org wall-clock budget — a hung suite dies at the ceiling instead
|
|
199
|
-
# of squatting a runner lane for GitHub's 6h default. ci-audit
|
|
200
|
+
# of squatting a runner lane for GitHub's 6h default. ci-audit (and, on repos that have one, the release train) enforce this step.
|
|
200
201
|
# #9293: PRs fail fast, merge_group and tags run everything. pytest reads PYTEST_ADDOPTS natively; a
|
|
201
202
|
# Node check script opts in with MMI_FAIL_FAST (`vitest run ${MMI_FAIL_FAST:+--bail=1}`, or
|
|
202
203
|
# `--fail-fast` on the org node runner). #9295: on PRs the Jev plan step exports JEV_PYTEST_ARGS
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_comment": "Bootstrap seed manifest (#201) — the machine-readable contract of what /bootstrap and `mmi-cli devops bootstrap --apply` (#202) stamp into a target repo. Consumed by the CLI (loadBootstrapSeeds). ownership: 'org' = org-delivered, OVERWRITTEN on upgrade (the org owns it); 'repo' = created ONCE on a fresh bootstrap, never clobbered on upgrade (the repo owns its content). source: 'self' = copy MMI-Hub's own current file verbatim; 'seed:<file>' = render the named template in this dir with {{PLACEHOLDERS}}; 'managed-block' = merge the legacy org-managed .gitignore block in place. managedBlock declares a separately sourced, exact-marker-bounded Hub-owned region inside a repo-owned containing file; apply/propagate reconcile only that region and refuse malformed markers. NOTE: this manifest seeds org-managed PRODUCT assets only — it never seeds AGENTS.md / CLAUDE.md / .claude/settings.json (personal agent guides) or a legacy repo-local control-plane marker. classes: which repo classes receive this seed.",
|
|
2
|
+
"_comment": "Bootstrap seed manifest (#201) — the machine-readable contract of what /bootstrap and `mmi-cli devops bootstrap --apply` (#202) stamp into a target repo. Consumed by the CLI (loadBootstrapSeeds). ownership: 'org' = org-delivered, OVERWRITTEN on upgrade (the org owns it); 'repo' = created ONCE on a fresh bootstrap, never clobbered on upgrade (the repo owns its content). source: 'self' = copy MMI-Hub's own current file verbatim; 'seed:<file>' = render the named template in this dir with {{PLACEHOLDERS}}; 'managed-block' = merge the legacy org-managed .gitignore block in place. managedBlock declares a separately sourced, exact-marker-bounded Hub-owned region inside a repo-owned containing file; apply/propagate reconcile only that region and refuse malformed markers. NOTE: this manifest seeds org-managed PRODUCT assets only — it never seeds AGENTS.md / CLAUDE.md / .claude/settings.json (personal agent guides) or a legacy repo-local control-plane marker. classes: which repo classes receive this seed. releaseTracks (#10076): when set, only repos on those registry release tracks receive it; a 'self' seed's '# mmi-train-only:start' ... ':end' line regions are dropped for single-track repos.",
|
|
3
3
|
"placeholders": ["OWNER", "REPO", "REPO_SLUG", "REPO_NAME", "CLASS", "GATE_CMD", "GATE_PUSH_BRANCHES_YAML", "GATE_FULL_RUN_BRANCH", "GATE_RULESET_BRANCH_REFS_JSON", "GATE_RULESET_CONTEXTS_JSON", "PROJECT_OWNER", "PROJECT_NUMBER", "PROJECT_ID", "STATUS_FIELD_ID", "STATUS_TODO", "STATUS_IN_PROGRESS", "STATUS_IN_REVIEW", "STATUS_DONE", "STACK", "REGION"],
|
|
4
4
|
"seeds": [
|
|
5
5
|
{ "target": ".github/ISSUE_TEMPLATE/bug.yml", "source": "self", "ownership": "org", "classes": ["deployable", "content"] },
|
|
6
6
|
{ "target": ".github/ISSUE_TEMPLATE/feature.yml", "source": "self", "ownership": "org", "classes": ["deployable", "content"] },
|
|
7
7
|
{ "target": ".github/ISSUE_TEMPLATE/task.yml", "source": "self", "ownership": "org", "classes": ["deployable", "content"] },
|
|
8
8
|
{ "target": ".github/ISSUE_TEMPLATE/config.yml", "source": "self", "ownership": "repo", "classes": ["deployable", "content"] },
|
|
9
|
-
{ "target": "scripts/next-version.mjs", "source": "self", "ownership": "org", "classes": ["deployable"] },
|
|
9
|
+
{ "target": "scripts/next-version.mjs", "source": "self", "ownership": "org", "classes": ["deployable"], "releaseTracks": ["full", "direct", "trunk"] },
|
|
10
10
|
{ "target": ".github/workflows/gate.yml", "source": "seed:gate.template.yml", "ownership": "repo", "classes": ["deployable"], "waivers": { "mmi-katip": "gates through py-gate and web-gate (in-job path filters, #3836) and its registry requiredChecks name exactly those two; a generic Node gate.yml here is red noise on every PR and would be re-created by every apply (#6333)." } },
|
|
11
11
|
{ "target": ".github/workflows/agent-pr.yml", "source": "self", "ownership": "org", "classes": ["deployable"], "waivers": { "jerv-jervcode": "pins this workflow's EXACT step shapes in its own scripts/workflow-boundary-core.mjs — a literal repositories: Jerv-JervCode token scope, and whole-step equality for the surface and verdict legs. #4040 widened the org seed to the same hardened boundary (pull_request_target off the trusted base, an immutable BASE..HEAD compare with a file-count equality wall, a per-repo App token scope, and a certified-head merge), so the two now agree on substance. They cannot agree on bytes: a seed serving 16 repos resolves its token scope and its required contexts at run time, which exact-step equality rejects by construction. The waiver is permanent by design, not a deferral — lifting it would mean weakening the strictest repo to match a fleet file." } },
|
|
12
12
|
{ "target": ".github/rulesets/mmi-product-required-checks.json", "source": "seed:mmi-product-required-checks.template.json", "ownership": "org", "classes": ["deployable"] },
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
Use the [canonical MMI developer-environment guide](https://github.com/mutmutco/MMI-Hub/blob/main/docs/Architecture/agentic-dev-environment.md) for shared setup and daily agent work.
|
|
5
5
|
|
|
6
6
|
Daily loop: `mmi-cli oracle issue create` → `mmi-cli oracle board claim <n>` (stamps the mmi-claim lease) → `mmi-cli devops pr create` → `mmi-cli devops pr land <pr>`.
|
|
7
|
-
Any org member with repository write (developer) or repository admin (project admin) can run it, with no vault access; release trains stay org-owner only.
|
|
7
|
+
Any org member with repository write (developer) or repository admin (project admin) can run it, with no vault access; Hub release trains (repos that have one) stay org-owner only; MM-Work and its modules have none — merge and go live.
|
|
8
8
|
<!-- mmi-developer-environment:end -->
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
".github/workflows/agent-pr.yml": {
|
|
3
|
-
"blob": "
|
|
4
|
-
"reason": "
|
|
3
|
+
"blob": "228f2f3289b187e47c91d88c8582c7d856708a3b",
|
|
4
|
+
"reason": "train-only markers around the release-align replay, its comment and the scripts/next-version.mjs org-owned entry, stripped for single-track repos (#10076, #10085); the #7717 note says it applies only on repos with a release train (#10071); guarded paths for train repos, permissions and operator responsibilities unchanged"
|
|
5
5
|
},
|
|
6
6
|
"scripts/next-version.mjs": {
|
|
7
|
-
"blob": "
|
|
8
|
-
"reason": "comment-only
|
|
7
|
+
"blob": "7faba0bb3ee48074702da48d149657820d74de1a",
|
|
8
|
+
"reason": "comment-only header: the train applies only to repos with a release lane; MM-Work and its modules merge to development and it goes live with no release, rc or candidate stage (#10071); no runtime or operator behaviour change"
|
|
9
9
|
},
|
|
10
10
|
"README.md": {
|
|
11
|
-
"blob": "
|
|
12
|
-
"reason": "the developer-environment seed block now
|
|
11
|
+
"blob": "46f5e07288931eee1c26362219b9d4271a5ed49e",
|
|
12
|
+
"reason": "the developer-environment seed block now says Hub release trains (repos that have one) stay org-owner only and MM-Work and its modules have none — merge and go live (#10071); no runbook step changes"
|
|
13
13
|
}
|
|
14
14
|
}
|
package/skills/hotfix/SKILL.md
CHANGED
|
@@ -15,7 +15,7 @@ Pushing to `main` and production needs Jervaise's yes unless a standing authoriz
|
|
|
15
15
|
|
|
16
16
|
## Lane
|
|
17
17
|
|
|
18
|
-
MM-Work
|
|
18
|
+
MM-Work and its modules have no hotfix: a fix merges to development and it goes live ([suite.md](suite.md)). Every other repo, lane from META:
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
21
|
mmi-cli oracle org project get {owner}/{repo} --json
|
package/skills/hotfix/suite.md
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
MM-Work and its modules (MM-Account, MM-Chat, MM-Inspo, MM-Moments, MM-PM, MM-Strategy, MMI-Junior, MMI-Katip, MMI-Pulse, MMI-Scout, MM-Talent) merge to development and it goes live. No release, rc or candidate stage (owner ruling 2026-11-05): `development` is their only branch, and a merged change is live once its deploy is green. There is no suite rcand, suite release or suite hotfix for them and no `--suite-solo` path: never run `/release`, `/rcand` or `/hotfix` in these repos (the CLI refuses), and never tell the owner a landed change waits on a release. An urgent fix is a normal PR to development. A suite member never asks for, proposes or prompts an rc, release or hotfix — there is none; state what landed and stop.
|
|
@@ -164,8 +164,11 @@ Craft essentials every agent applies (owner, 2026-10-25, #8839):
|
|
|
164
164
|
it serves `<pack-dir>` on `127.0.0.1`, measures each named board per width and theme, runs
|
|
165
165
|
`impeccable detect --json` over the board files and writes `check.json`. `--boards` takes one
|
|
166
166
|
comma-separated list (an unquoted shell glob passes only its first match); the server serves only
|
|
167
|
-
files directly inside `<pack-dir>`;
|
|
168
|
-
|
|
167
|
+
files directly inside `<pack-dir>`; BRP rhythm and sibling alignment are measured against the kit
|
|
168
|
+
space scale read live from the page's `:root` `--space-*` / `--mm-space-*` tokens (kit 4px fallback
|
|
169
|
+
steps when a board defines none), so off-scale padding/gap/sibling-margin values and 1–3px sibling
|
|
170
|
+
edge near-misses are named in `check.json` `detail.rhythm` / `detail.align` and fail the run
|
|
171
|
+
(exit 0 clean, 2 with faults — no standing caveat). Boards that load `/kit/kit.css` and
|
|
169
172
|
`/kit/kit.js` are canvas boards: use `review.mjs <folder> --no-open`, which serves the kit beside
|
|
170
173
|
each board on `127.0.0.1` and runs live-DOM `impeccable detect` per board.
|
|
171
174
|
`artifacts/review/kit-assets.mjs`, `review.mjs`'s kit and board serving, and
|
|
@@ -27,8 +27,8 @@ mmi-cli devops pr land <pr-number>
|
|
|
27
27
|
|
|
28
28
|
Board writes (attach, claim stamp, status move) gate on org owner, repo admin or repository write, so
|
|
29
29
|
these roles pass. Where the GitHub App route is absent the writes go as the person's own GitHub login
|
|
30
|
-
(one note says so); an agent host with the App route writes as the App. MMI-Hub's own merges and
|
|
31
|
-
release trains stay org-owner only.
|
|
30
|
+
(one note says so); an agent host with the App route writes as the App. MMI-Hub's own merges and the Hub
|
|
31
|
+
release trains (repos that have one) stay org-owner only; MM-Work and its modules have none: merge and go live.
|
|
32
32
|
|
|
33
33
|
Start with `mmi-cli onboard` and the [board skill](../../board/SKILL.md).
|
|
34
34
|
Reading or discussing the board is read-only until the developer authorizes work.
|
|
@@ -169,8 +169,9 @@ development. A trunk repository follows its single branch and declared workflow
|
|
|
169
169
|
No box stage.
|
|
170
170
|
- Trunk track: one main branch, without an rc/development promotion train. Follow the repository's
|
|
171
171
|
declared publishing workflow rather than manufacturing train branches.
|
|
172
|
-
-
|
|
173
|
-
|
|
172
|
+
- MM-Work and its modules (registry `releaseTrack: single`) are not on any of these trains: MM-Work and
|
|
173
|
+
its modules merge to development and it goes live. No release, rc or candidate stage; `development`
|
|
174
|
+
is their only branch, and release, rcand and hotfix refuse. The Hub's own release train never applies to them.
|
|
174
175
|
- Urgent already-merged fixes: use [hotfix](../../hotfix/SKILL.md), which carries selected development
|
|
175
176
|
fixes directly to main on either track.
|
|
176
177
|
|
|
@@ -199,7 +200,7 @@ refresh tags with `git fetch origin --tags --force` (or `git fetch origin '+refs
|
|
|
199
200
|
never chain a plain tag fetch with `&&` ahead of a read-only `git log <tag>..origin/<branch>` range (#9517).
|
|
200
201
|
A Hub summary never mentions a Jerv-side project; the train
|
|
201
202
|
refuses such a line.
|
|
202
|
-
|
|
203
|
+
MM-Work and its modules never cut a release, so they write no release summary: MM-Work and its modules merge to development and it goes live. No release, rc or candidate stage, no suite cut and no `--suite-solo` path (owner ruling 2026-11-05). Details: the release skill's suite.md.
|
|
203
204
|
|
|
204
205
|
Announcement delivery uses the developer's GitHub-authenticated Hub session. The Hub checks project
|
|
205
206
|
authority, resolves the registered channel, and reads the MMI Future Slack key inside its vault boundary.
|
package/skills/rcand/SKILL.md
CHANGED
|
@@ -13,7 +13,7 @@ Merge `development → rc`, tag `vX.Y.0-rc.N`, push the tag, wait for required c
|
|
|
13
13
|
|
|
14
14
|
## Lane
|
|
15
15
|
|
|
16
|
-
MM-Work
|
|
16
|
+
MM-Work and its modules have no rcand: they merge to development and it goes live ([suite.md](suite.md)). Every other repo: Name the lane from META:
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
19
|
mmi-cli oracle org project get {owner}/{repo} --json
|
package/skills/rcand/suite.md
CHANGED
|
@@ -1,4 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
Support sessions (MMI-Hub, Jerv-Hub, Jerv-JervCode) are not suite members and hold standing owner authorization (2026-10-25) to cut and ship releases at their discretion during suite runs without repeated per-release approval, and under the owner's 2026-11-01 ruling each support session strives during a suite run to finish its own board and cut a patch release for landed-but-unreleased merges so suite members pick the update up in that run instead of waiting for a later cut, keeping all standard release preflight, gate, and verification checks.
|
|
4
|
-
|
|
1
|
+
MM-Work and its modules (MM-Account, MM-Chat, MM-Inspo, MM-Moments, MM-PM, MM-Strategy, MMI-Junior, MMI-Katip, MMI-Pulse, MMI-Scout, MM-Talent) merge to development and it goes live. No release, rc or candidate stage (owner ruling 2026-11-05): `development` is their only branch, and a merged change is live once its deploy is green. There is no suite rcand, suite release or suite hotfix for them and no `--suite-solo` path: never run `/release`, `/rcand` or `/hotfix` in these repos (the CLI refuses), and never tell the owner a landed change waits on a release. An urgent fix is a normal PR to development. A suite member never asks for, proposes or prompts an rc, release or hotfix — there is none; state what landed and stop.
|
package/skills/release/SKILL.md
CHANGED
|
@@ -11,7 +11,7 @@ Bare `minor|major|patch` (default `patch`), never a flag. A production release n
|
|
|
11
11
|
|
|
12
12
|
## Lane
|
|
13
13
|
|
|
14
|
-
Read `mmi-cli oracle org project get {owner}/{repo} --json` and name the lane ([verification.md](verification.md) Lane rules); unreadable META → stop.
|
|
14
|
+
Read `mmi-cli oracle org project get {owner}/{repo} --json` and name the lane ([verification.md](verification.md) Lane rules); unreadable META → stop. MM-Work and its modules merge to development and it goes live. No release, rc or candidate stage: release, rcand and hotfix refuse there, and a merged change is live once its deploy is green ([suite.md](suite.md)). A `trunk` repo has no release lane either and follows its own declared publishing workflow. Never tell the owner a landed change waits on a release. [vercel.md](vercel.md) for Vercel.
|
|
15
15
|
|
|
16
16
|
## Preflight
|
|
17
17
|
|
package/skills/release/suite.md
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
MM-Work and
|
|
1
|
+
MM-Work and its modules (MM-Account, MM-Chat, MM-Inspo, MM-Moments, MM-PM, MM-Strategy, MMI-Junior, MMI-Katip, MMI-Pulse, MMI-Scout, MM-Talent) merge to development and it goes live. No release, rc or candidate stage (owner ruling 2026-11-05): `development` is their only branch, and a merged change is live once its deploy is green. There is no suite rcand, suite release or suite hotfix for them and no `--suite-solo` path: never run `/release`, `/rcand` or `/hotfix` in these repos (the CLI refuses), and never tell the owner a landed change waits on a release. An urgent fix is a normal PR to development. A suite member never asks for, proposes or prompts an rc, release or hotfix — there is none; state what landed and stop.
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The Hub's own release train (MMI-Hub, Jerv-Hub, Jerv-JervCode and other non-MM repos with a release lane) is unchanged and never applies to MM-Work or its modules.
|
|
4
4
|
|
|
5
5
|
Support sessions (MMI-Hub, Jerv-Hub, Jerv-JervCode) are not suite members and hold standing owner authorization (2026-10-25) to cut and ship releases at their discretion during suite runs without repeated per-release approval, and under the owner's 2026-11-01 ruling each support session strives during a suite run to finish its own board and cut a patch release for landed-but-unreleased merges so suite members pick the update up in that run instead of waiting for a later cut, keeping all standard release preflight, gate, and verification checks.
|
|
6
|
-
|
|
7
|
-
Every new mm-work suite Release (including solo and hotfix releases) requires both `--announce-summary-file <path>` (English) and `--announce-summary-file-tr <path>` (Turkish, "sen" form) in the update-notes template. The canonical wording lives in the [MM-Work GUIDE, "Update notes"](https://github.com/mutmutco/MM-Work/blob/development/packages/mmwork-ds/GUIDE.md#update-notes); this repo links it and never copies it. Each file has the section word `Fixed` / `Düzeldi` (what works now, never the problem: "Drive: Scans of large drives now finish without timing out."; a line without now / again / no longer, or artık / yeniden / tekrar / yine, is refused) and/or `New` / `Yeni` (what you can now do) on its own line, Fixed first, each only when it has lines. Under them go 1–8 lines of `<Area>: <sentence>.`, each at most 120 characters, with no issue numbers, versions or commit prefixes. Off-template or missing files refuse before tagging. Do not write the head (app · date · time) or the thanks line: MM-Work renders both from the Release date and credits. Suite release also requires a valid `--member-summary-file <owner/repo=path>` and `--member-summary-file-tr <owner/repo=path>` for every member being released, before promoting any member. Carried, held and already-released members need none, and existing Releases can resume without new files.
|
|
8
|
-
The `/user-releases` feed serves template notes as `updateNotes: {en?, tr?: {fixed[], new[]}}`. Older title-and-notes bodies keep `plainWords` and get `updateNotes: null`.
|
|
9
|
-
|
|
@@ -8,7 +8,7 @@ Do not use a `--jq` formatter, raw `gh` reads (`gh run list`, `gh run list --com
|
|
|
8
8
|
|
|
9
9
|
## Release summary file
|
|
10
10
|
|
|
11
|
-
Required for MMI-Hub and any product whose META carries `releaseChannel
|
|
11
|
+
Required for MMI-Hub and any product whose META carries `releaseChannel`. MM-Work and its modules never cut a release, so they never write one ([suite.md](suite.md)). Run `mkdir -p .jerv/tmp` and write 3–6 lines to a file made by `mktemp .jerv/tmp/release-summary.XXXXXX` (scratch lives under `.jerv/tmp` in every repo; a repo-root `tmp/` may not exist and a host guards root writes).
|
|
12
12
|
|
|
13
13
|
- MMI-Hub: neutral Hub-subsystem terms, never a product or brand name or a Jerv-side project (the train refuses one). Build every line from `git log --stat --format='%h %s%n%b' origin/main..origin/development`; a line you cannot point to there stays out, and every cited `#N` must appear there.
|
|
14
14
|
- Product: in `releaseLanguage` (default `en`), plain words on what changed and why it matters; no prefixes, issue/PR numbers, file names or jargon.
|