@mutmutco/claude-plugin 3.131.0

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 (46) hide show
  1. package/.claude-plugin/plugin.json +11 -0
  2. package/.pi-plugin/package.json +23 -0
  3. package/bin/mmi-cli +6 -0
  4. package/bin/mmi-cli.cmd +3 -0
  5. package/bin/mmi-hook +2 -0
  6. package/bin/mmi-hook-console.cmd +10 -0
  7. package/bin/mmi-hook.exe +0 -0
  8. package/hooks/hooks.json +38 -0
  9. package/package.json +22 -0
  10. package/scripts/command-ladder-core.mjs +334 -0
  11. package/scripts/command-ladder-gate.mjs +126 -0
  12. package/scripts/deny-gate-crash.mjs +179 -0
  13. package/scripts/edit-tool-paths.mjs +113 -0
  14. package/scripts/env-write-lint.mjs +137 -0
  15. package/scripts/hook-io.mjs +22 -0
  16. package/scripts/hook-policy.mjs +73 -0
  17. package/scripts/hook-run.mjs +437 -0
  18. package/scripts/hook-trace.mjs +151 -0
  19. package/scripts/pretooluse-shell-gates.mjs +424 -0
  20. package/scripts/secret-echo-lint.mjs +177 -0
  21. package/scripts/secret-redact.mjs +552 -0
  22. package/scripts/throttle-core.mjs +324 -0
  23. package/scripts/validate-hook.mjs +156 -0
  24. package/scripts/vault-edit-gate.mjs +94 -0
  25. package/skills/bootstrap/SKILL.md +550 -0
  26. package/skills/bootstrap/seeds/Dockerfile.template +30 -0
  27. package/skills/bootstrap/seeds/README.template.md +37 -0
  28. package/skills/bootstrap/seeds/architecture.template.md +34 -0
  29. package/skills/bootstrap/seeds/decisions-readme.template.md +45 -0
  30. package/skills/bootstrap/seeds/docker-compose.template.yml +26 -0
  31. package/skills/bootstrap/seeds/gate.template.yml +85 -0
  32. package/skills/bootstrap/seeds/google-login.template.md +33 -0
  33. package/skills/bootstrap/seeds/manifest.json +26 -0
  34. package/skills/bootstrap/seeds/mmi-product-required-checks.template.json +23 -0
  35. package/skills/browser-automation/SKILL.md +95 -0
  36. package/skills/epic/SKILL.md +104 -0
  37. package/skills/hotfix/SKILL.md +165 -0
  38. package/skills/mmi/SKILL.md +404 -0
  39. package/skills/mmi-doctor/SKILL.md +63 -0
  40. package/skills/onboard/SKILL.md +85 -0
  41. package/skills/rcand/SKILL.md +208 -0
  42. package/skills/release/SKILL.md +599 -0
  43. package/skills/resume/SKILL.md +90 -0
  44. package/skills/secrets/SKILL.md +159 -0
  45. package/skills/stage/SKILL.md +153 -0
  46. package/skills/worktree/SKILL.md +151 -0
@@ -0,0 +1,34 @@
1
+ # {{REPO_NAME}} — Architecture
2
+
3
+ > How this repo is **built** — a short, stable entry, not a runtime inventory. Pair with `README.md`
4
+ > (what it *is*). State current truth, inspect don't narrate: no change-comments or version-era labels
5
+ > (history is in git). (Write fresh — D35.)
6
+
7
+ ## What this repo produces
8
+
9
+ (What ships from this repo — the product/service/library, in one or two bullets.)
10
+
11
+ ## Layout
12
+
13
+ ```
14
+ (top-level dir) (what it is, one line)
15
+ (top-level dir) (what it is, one line)
16
+ ```
17
+
18
+ ## Stack
19
+
20
+ (Languages, frameworks, datastores, major services — inspect the lockfile/manifest, don't assume.)
21
+
22
+ ## Build & deploy
23
+
24
+ - **Class + release track + stages:** `mmi-cli oracle org project get` (registry SSOT — full = development/rc/main,
25
+ direct = development/main, trunk = main; never copied here, so it cannot go stale).
26
+ - **Deploys run centrally** via the Hub (`tenant-deploy.yml`); this repo carries no deploy files, unless
27
+ noted otherwise below.
28
+ - (Build/test commands, CI gate, deploy target.)
29
+
30
+ ## Conventions
31
+
32
+ Follows the org spine (carried by the MMI plugin per machine). Repo-specific agent guidance:
33
+ `README.md` § Agent context — no agent guide is committed to the repo (the org `mmi-no-agent-files-org`
34
+ ruleset blocks every agent-guide path).
@@ -0,0 +1,45 @@
1
+ # Decision records
2
+
3
+ Prose in this tree is only for **why** — why we chose a shape, what we rejected, what constraint
4
+ bound us. It never describes current implementation and never claims to be current. Code (and
5
+ generated refs) are the only truth about *what is*.
6
+
7
+ ## One file per decision
8
+
9
+ `docs/decisions/YYYY-MM-DD-<slug>.md`
10
+
11
+ - **Date** in the filename is the day the decision was recorded (UTC calendar date).
12
+ - **Slug** is a short kebab-case label for the subject (`single-aws-account`, `cowork-plugin-boundary`).
13
+
14
+ ## Frontmatter
15
+
16
+ ```yaml
17
+ ---
18
+ date: YYYY-MM-DD
19
+ sha: <git commit sha at which this was true>
20
+ status: decided | superseded
21
+ subjects:
22
+ - <topic or path this decision binds>
23
+ ---
24
+ ```
25
+
26
+ | Field | Meaning |
27
+ | --- | --- |
28
+ | `date` | Same calendar day as the filename prefix. |
29
+ | `sha` | The commit that embodies or records the decision — a pin in history, not a claim that the tree still looks like that. |
30
+ | `status` | `decided` (in force) or `superseded` (replaced by a later record; leave the old file in place). |
31
+ | `subjects` | Short list of topics, paths, or surfaces the decision constrains. |
32
+
33
+ ## Body
34
+
35
+ 1. `# <Title>` — the decision in one line (what was chosen, not how it works today).
36
+ 2. `## Why` — the rationale and the binding constraints.
37
+ 3. `## Rejected alternatives` — each alternative as a bullet: what it was, why it lost.
38
+
39
+ Do not narrate live topology, versions, box coordinates, or "how to run it." If a reader needs
40
+ current state, they read the code or a generated ref — not this file.
41
+
42
+ ## Ownership
43
+
44
+ Bootstrap seeds this README once (`ownership: repo`) and never clobbers it. Individual decision
45
+ files are authored in the same PR as the change and merged by a human. There is no scheduled docs-janitor backstop (Hub#4119/#4164).
@@ -0,0 +1,26 @@
1
+ services:
2
+ app:
3
+ build:
4
+ context: .
5
+ dockerfile: Dockerfile
6
+ # Build-time secret opt-in:
7
+ # secrets:
8
+ # - NODE_AUTH_TOKEN
9
+ restart: unless-stopped
10
+ # The box control document writes registry coords + stage-scoped SSM secret values into the
11
+ # release ./.env before `docker compose up`; env_file is what delivers them into the container.
12
+ env_file: .env
13
+ environment:
14
+ NODE_ENV: production
15
+ MMI_STAGE: ${MMI_STAGE:-dev}
16
+ MMI_PORT: ${MMI_PORT:-3000}
17
+ PORT: ${PORT:-3000}
18
+ MMI_EDGE_DOMAIN: ${MMI_EDGE_DOMAIN:-}
19
+ ports:
20
+ - "${MMI_PORT:-3000}:${PORT:-3000}"
21
+
22
+ # Build-time secret opt-in. Requires registry META:
23
+ # requiredBuildSecrets: ["NODE_AUTH_TOKEN=@github-packages-token"]
24
+ # secrets:
25
+ # NODE_AUTH_TOKEN:
26
+ # environment: NODE_AUTH_TOKEN
@@ -0,0 +1,85 @@
1
+ name: gate
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.
4
+ #
5
+ # Runner: self-hosted mmi-runner (mmi-live) — see docs/Guides/gh-runner-runbook.md in MMI-Hub.
6
+ # #4118: if GATE_CMD runs tsgo or other memory-heavy typecheck / browser work, pin this job to
7
+ # runs-on: [self-hosted, linux, x64, mmi-heavy]
8
+ # instead — standard lanes are MemoryMax=1200M and will cgroup-OOM those workloads.
9
+ # Stack-aware (#1550): the runtime is rendered into the setup-step `if:` — both the Node and Python
10
+ # setup steps live in the file but only the one matching GATE_RUNTIME runs. Knobs (override at
11
+ # bootstrap with --var):
12
+ # GATE_RUNTIME — node | python (selects which setup step fires)
13
+ # GATE_CMD — the check command ({{GATE_CMD}})
14
+ # GATE_INSTALL_CMD — the dependency-install command ({{GATE_INSTALL_CMD}})
15
+ # GATE_WORKDIR — app working directory when not the repo root ({{GATE_WORKDIR}})
16
+ # GATE_CACHE_DEP_PATH— npm lockfile path for the setup-node cache ({{GATE_CACHE_DEP_PATH}})
17
+ # GATE_PY_VERSION — Python version for setup-python ({{GATE_PY_VERSION}})
18
+ # GATE_MAX_SECONDS — run-with-budget wall-clock ceiling in seconds ({{GATE_MAX_SECONDS}}); 300 default,
19
+ # tighten per repo (org project set --var gate={"maxSeconds":N}) once measured (#3178)
20
+ # GATE_PUSH_BRANCHES_YAML — YAML list of push branches that trigger the workflow file (rendered by
21
+ # bootstrap from the release track; not usually hand-passed)
22
+ # GATE_FULL_RUN_BRANCH — train branch whose PUSHES run the full gate job (development on full/direct;
23
+ # main on trunk). `main` is ALWAYS included in the job `if:` below so a required-
24
+ # checks ruleset on main cannot be silently skipped by a future bootstrap (#4113).
25
+ # GATE_BUDGET_SHA is rendered by the CLI from its blessed run-with-budget pin — not an operator knob.
26
+ # (seed touch: keep kilo mirror + BOM digests in lockstep after classifier land.)
27
+ on:
28
+ pull_request:
29
+ push:
30
+ branches: {{GATE_PUSH_BRANCHES_YAML}}
31
+ tags: ['v*']
32
+
33
+ env:
34
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
35
+
36
+ # #3001: cancel superseded runs — agent push-bursts queue a full fresh run per push.
37
+ # Per-PR group only (a repo-wide group hits GitHub's one-pending-slot limit and cancels
38
+ # innocent queued runs — MMC-ZuberShade#880). Pushes group by ref: each tag and the train
39
+ # branch supersede only themselves.
40
+ concurrency:
41
+ group: gate-${{ github.event.pull_request.number || github.ref }}
42
+ cancel-in-progress: true
43
+
44
+ permissions:
45
+ contents: read
46
+
47
+ jobs:
48
+ gate:
49
+ # #4113: bake `main` into the template permanently. GATE_FULL_RUN_BRANCH selects the train push
50
+ # that runs the full job; main must never become skippable via bootstrap vars alone while the
51
+ # org ruleset still requires `gate` on main.
52
+ if: ${{ github.event_name != 'push' || github.ref_name == '{{GATE_FULL_RUN_BRANCH}}' || github.ref_name == 'main' || github.ref_type == 'tag' }}
53
+ runs-on: [self-hosted, linux, x64, mmi-live]
54
+ defaults:
55
+ run: { working-directory: {{GATE_WORKDIR}} }
56
+ steps:
57
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
58
+ - if: ${{ '{{GATE_RUNTIME}}' == 'node' }}
59
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
60
+ with: { node-version: 24, cache: npm, cache-dependency-path: {{GATE_CACHE_DEP_PATH}} }
61
+ - if: ${{ '{{GATE_RUNTIME}}' == 'python' }}
62
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
63
+ with: { python-version: '{{GATE_PY_VERSION}}' }
64
+ # MMI-Hub#3446: assert the runtime toolchain is on PATH BEFORE the install step. A runner whose
65
+ # node/npm (or python) is missing, or whose setup step raced, fails `{{GATE_INSTALL_CMD}}` with a
66
+ # bare `command not found` (exit 127) that reads like a broken diff and abandons a correct PR in an
67
+ # autonomous merge loop. This names the RUNNER instead — a distinguishable infra failure that
68
+ # `mmi-cli devops pr checks-wait` classifies as rerunnable (title `CI runner toolchain`), mirroring the
69
+ # disk (#3366) and browser (#808) preflights. No test ran; the diff is not implicated.
70
+ - name: Toolchain preflight
71
+ if: ${{ '{{GATE_RUNTIME}}' == 'node' }}
72
+ run: |
73
+ node -v && npm -v || { echo "::error title=CI runner toolchain::RUNNER NOT PROVISIONED — node/npm not on PATH after setup-node. This is the runner, not your diff; no install or test ran. Re-run the job (a fresh lane usually clears it); if it persists the mmi-runner node toolchain needs attention (docs/Guides/gh-runner-runbook.md in MMI-Hub)."; exit 1; }
74
+ - name: Toolchain preflight
75
+ if: ${{ '{{GATE_RUNTIME}}' == 'python' }}
76
+ run: |
77
+ python3 --version || { echo "::error title=CI runner toolchain::RUNNER NOT PROVISIONED — python3 not on PATH after setup-python. This is the runner, not your diff; no install or test ran. Re-run the job; if it persists the mmi-runner python toolchain needs attention (docs/Guides/gh-runner-runbook.md in MMI-Hub)."; exit 1; }
78
+ - run: {{GATE_INSTALL_CMD}}
79
+ # #3178: the check runs under the org wall-clock budget — a hung suite dies at the ceiling instead
80
+ # of squatting a runner lane for GitHub's 6h default. ci-audit + the release train enforce this step.
81
+ - uses: mutmutco/MMI-Hub/.github/actions/run-with-budget@{{GATE_BUDGET_SHA}}
82
+ with:
83
+ command: {{GATE_CMD}}
84
+ max-seconds: {{GATE_MAX_SECONDS}}
85
+ working-directory: {{GATE_WORKDIR}}
@@ -0,0 +1,33 @@
1
+ # Google login — {{REPO_NAME}}
2
+
3
+ This repo has a Google OAuth client provisioned by the org — one client spanning local/`/stage`, dev, rc, and
4
+ prod. Adding Google login here is **self-serve**: you do not need master-admin help.
5
+
6
+ ## Reach the creds (from SSM — never in git, never printed)
7
+ ```bash
8
+ # Confirm they resolve for your repo (names only, no value):
9
+ mmi-cli vault secrets list
10
+ # Consume them keyless in a command — injected into its env, never printed (raw `secrets get` was removed, #2844):
11
+ mmi-cli vault secrets use GOOGLE_CLIENT_ID -- <cmd>
12
+ mmi-cli vault secrets use GOOGLE_CLIENT_SECRET -- <cmd>
13
+ ```
14
+ The canonical keys are the stageless `GOOGLE_CLIENT_ID` + `GOOGLE_CLIENT_SECRET` pair at the project vault
15
+ root. The project-admin can manage them for their own repo; runtime and CI read them keylessly. Never bake a secret into an
16
+ image or commit it. The remote fileless deploy writes no release `.env`; **do not commit or hand-edit a `.env`**.
17
+ The only `.env` you may create is the gitignored one `/stage` makes from `.env.example` for local dev.
18
+
19
+ ## The one rule that makes every environment work
20
+ Build the OAuth `redirect_uri` from the **incoming request** — never a hardcoded base URL:
21
+ - callback path: `/api/auth/callback`
22
+ - derive scheme + host from `X-Forwarded-Proto` / `X-Forwarded-Host` (set by the Caddy proxy), falling back
23
+ to the `Host` header.
24
+
25
+ The client's loopback redirect URIs are registered **port-agnostic**, so any local port works; dev/rc/prod
26
+ URIs are registered for both `mutatismutandis.co` and `mutmut.co`, so the deploy train works unchanged.
27
+
28
+ ## Reference implementation + full guide
29
+ Inspect this repo's expected URIs and confirm the client is port-agnostic:
30
+ ```bash
31
+ mmi-cli vault org oauth plan # the canonical JS origins + redirect URIs + SSM cred params
32
+ mmi-cli vault org oauth verify # probes an arbitrary :9123 loopback — no redirect_uri_mismatch = good
33
+ ```
@@ -0,0 +1,26 @@
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 org-managed .gitignore block in place (preserves the repo's own ignore lines) — this is how the org-managed .gitignore block reaches every repo now that the fanout pipeline is retired (Hub#3010); the doctor SessionStart heal keeps it current thereafter. 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.",
3
+ "placeholders": ["OWNER", "REPO", "REPO_SLUG", "REPO_NAME", "CLASS", "GATE_CMD", "GATE_PUSH_BRANCHES_YAML", "GATE_FULL_RUN_BRANCH", "GATE_RULESET_BRANCH_REFS_JSON", "PROJECT_OWNER", "PROJECT_NUMBER", "PROJECT_ID", "STATUS_FIELD_ID", "STATUS_TODO", "STATUS_IN_PROGRESS", "STATUS_IN_REVIEW", "STATUS_DONE", "STACK", "REGION"],
4
+ "seeds": [
5
+ { "target": ".github/ISSUE_TEMPLATE/bug.yml", "source": "self", "ownership": "org", "classes": ["deployable", "content"] },
6
+ { "target": ".github/ISSUE_TEMPLATE/feature.yml", "source": "self", "ownership": "org", "classes": ["deployable", "content"] },
7
+ { "target": ".github/ISSUE_TEMPLATE/task.yml", "source": "self", "ownership": "org", "classes": ["deployable", "content"] },
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"] },
10
+ { "target": ".github/workflows/gate.yml", "source": "seed:gate.template.yml", "ownership": "repo", "classes": ["deployable"] },
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
+ { "target": ".github/rulesets/mmi-product-required-checks.json", "source": "seed:mmi-product-required-checks.template.json", "ownership": "org", "classes": ["deployable"] },
13
+ { "target": ".gitignore", "source": "managed-block", "ownership": "org", "classes": ["deployable", "content"] },
14
+ { "target": "README.md", "source": "seed:README.template.md", "ownership": "repo", "classes": ["deployable", "content"] },
15
+ { "target": "architecture.md", "source": "seed:architecture.template.md", "ownership": "repo", "classes": ["deployable", "content"] },
16
+ { "target": "docs/decisions/README.md", "source": "seed:decisions-readme.template.md", "ownership": "repo", "classes": ["deployable", "content"] },
17
+ { "target": "docs/Guides/google-login.md", "source": "seed:google-login.template.md", "ownership": "repo", "classes": ["deployable"], "projectTypes": ["web-app"] },
18
+ { "target": "docker-compose.yml", "source": "seed:docker-compose.template.yml", "ownership": "repo", "classes": ["deployable"], "deployModels": ["tenant-container"] },
19
+ { "target": "Dockerfile", "source": "seed:Dockerfile.template", "ownership": "repo", "classes": ["deployable"], "deployModels": ["tenant-container"] }
20
+ ],
21
+ "labels": [
22
+ { "name": "bug", "color": "d73a4a", "description": "Something is broken or behaving wrong" },
23
+ { "name": "feature", "color": "a2eeef", "description": "New capability or enhancement" },
24
+ { "name": "task", "color": "0052cc", "description": "Task, chore, or improvement" }
25
+ ]
26
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "_comment": "Repository-level ruleset requiring the product gate job (#1333). Apply via GitHub repo rulesets (master-admin) after bootstrap seeds gate.yml — the job name gate must match this context.",
3
+ "name": "mmi-product-required-checks",
4
+ "target": "branch",
5
+ "enforcement": "active",
6
+ "conditions": {
7
+ "ref_name": {
8
+ "include": {{GATE_RULESET_BRANCH_REFS_JSON}},
9
+ "exclude": []
10
+ }
11
+ },
12
+ "rules": [
13
+ {
14
+ "type": "required_status_checks",
15
+ "parameters": {
16
+ "strict_required_status_checks_policy": false,
17
+ "required_status_checks": [
18
+ { "context": "gate" }
19
+ ]
20
+ }
21
+ }
22
+ ]
23
+ }
@@ -0,0 +1,95 @@
1
+ ---
2
+ name: browser-automation
3
+ description: Use DOM-first Playwright MCP for browser work.
4
+ ---
5
+
6
+ **Host-native invocation:** Claude `/mmi:browser-automation` · Codex `$mmi:browser-automation` · jervcode/Kimi `/skill:browser-automation` · Kilo `skill` tool. A backticked `/name` in this doc names the matching workflow (this skill or a sibling), not a literal command.
7
+
8
+ # Browser automation — DOM-first Playwright MCP
9
+
10
+ Org standard for agent browser work on Claude Code. **Playwright** is the engine; agents interact through **structure-first** MCP tools, not pixels-first defaults.
11
+
12
+ ## Doctrine (non-negotiable)
13
+
14
+ 1. **Accessibility tree first** — semantic structure the agent can reason about (`browser_snapshot`, a11y refs).
15
+ 2. **DOM second** — selectors, snapshots, network when the tree is not enough.
16
+ 3. **Vision last** — screenshots only when tree + DOM cannot answer the question.
17
+ 4. **Prefer HTTP/OpenAPI** — call APIs directly when discovery or the task allows; do not drive UI for data you can fetch.
18
+
19
+ **Never** pass `--caps=vision` (or equivalent vision-first defaults) on Playwright MCP org-wide. Vision caps burn tokens, hide structure, and break on theme/layout drift.
20
+
21
+ ## When to use what
22
+
23
+ | Need | Use |
24
+ |------|-----|
25
+ | Local dev server + smoke on current branch | **`/stage`** — gitignored stack under `tmp/stage/`; see `skills/stage/SKILL.md` |
26
+ | Personal cloud dev preview of your branch | **`/stage --live`** — IP-gated dev stage; not rc/prod |
27
+ | Interactive UI debug, one-off flow, agent-driven clicks | **Playwright MCP** (this skill) — DOM-first, artifacts under `tmp/` |
28
+ | Durable hosted automation outside dev machines | **Stagehand + Browserbase** (production path) — explicit choice, not the default for every local task |
29
+
30
+ `/stage` and Playwright MCP **complement** each other. `/stage` spins the app; MCP drives the browser against a URL (often the stage URL).
31
+
32
+ ## MCP configuration (no vision)
33
+
34
+ ### Claude Code
35
+
36
+ Enable the official Playwright plugin (`playwright@claude-plugins-official`) via `/plugin`. Follow its DOM-first tools; do not enable vision-first modes for routine agent work. Point MCP output at `tmp/playwright-mcp` when the server accepts `--output-dir`.
37
+
38
+ Editor-host MCP configs (Cursor `.cursor/mcp.json`, Codex `config.toml`) are retired org surfaces (#2741/#2808) — bootstrap no longer seeds them; do not reintroduce them.
39
+
40
+ ## Playwright availability
41
+
42
+ Check the configured/global CLI before adding a temporary local dependency. On Windows PowerShell:
43
+
44
+ ```powershell
45
+ Get-Command playwright
46
+ playwright --version
47
+ ```
48
+
49
+ `node -e "require.resolve('playwright')"` only proves a Node module is installed in the current package; it
50
+ can fail while the global or editor-configured Playwright CLI works. Use the available CLI for smoke checks
51
+ and MCP setup. Temporary per-worktree installs are fallback-only, and should stay untracked.
52
+
53
+ ## Agent workflow (MCP)
54
+
55
+ 1. **Goal** — what observable outcome proves success?
56
+ 2. **Navigate** — open the target URL (often from `/stage` JSON: `mmi-cli stage --json`).
57
+ 3. **Snapshot** — a11y tree / DOM snapshot before interacting.
58
+ 4. **Act** — click, type, select using refs from the latest snapshot.
59
+ 5. **Re-snapshot** after navigation or major DOM changes (refs go stale).
60
+ 6. **Vision only if stuck** — one screenshot to disambiguate; then return to tree/DOM.
61
+
62
+ Core MCP loop:
63
+
64
+ ```
65
+ browser_navigate → browser_snapshot → browser_click / browser_type → browser_snapshot
66
+ ```
67
+
68
+ ## Artifacts and hygiene
69
+
70
+ - **All Playwright MCP output → `tmp/playwright-mcp/`** (pass `--output-dir tmp/playwright-mcp` when the MCP server supports it).
71
+ - **Never** leave traces, screenshots, or reports at the repo root.
72
+ - `.playwright-mcp/` at repo root is gitignored as a **safety net only** — not the canonical path.
73
+ - The housekeeping gate refuses tracked browser artifacts (`.playwright-mcp/`, `playwright-report/`, `test-results/`).
74
+
75
+ ## Anti-patterns (org-wide avoid)
76
+
77
+ - `--caps=vision` or screenshot-first wrappers for routine tasks
78
+ - Skyvern, Magnitude, LaVague, or other vision-first agent browsers as org defaults
79
+ - Committing `.playwright-mcp/`, `playwright-report/`, or `test-results/` from agent runs
80
+ - Replacing `/stage` with ad-hoc MCP servers for branch smoke (use `/stage` for the stack, MCP for the browser)
81
+
82
+ ## Related
83
+
84
+ - **`/stage`** — `skills/stage/SKILL.md`
85
+ - **`/grind`** (optional external tool) — use DOM-first browser checks in verification when criteria need UI proof
86
+
87
+ ## Retro — one check before you finish
88
+
89
+ Before your final report, answer one question honestly: did **this skill's own instructions** misfire
90
+ this run — ambiguous wording, a misleading MCP snippet, or an artifact path it should have warned
91
+ about? (Process only — never the user's code or task.) If yes, file **one** lesson and move on; a clean run is
92
+ silent (hard cap: one per run). It lands on the Hub board (deduped) and is fixed only via a reviewed PR —
93
+ never edit the skill live; the retro is advisory, so if the call fails, note it and continue:
94
+
95
+ `mmi-cli learning skill-lesson --skill browser-automation --title "<what misfired>" --body "<what; evidence; proposed amendment>"`
@@ -0,0 +1,104 @@
1
+ ---
2
+ name: epic
3
+ description: Decompose a vision or umbrella issue into linked child issues via one batch create.
4
+ ---
5
+
6
+ # /epic — decompose an umbrella into children
7
+
8
+ Turn one large item — a vision issue, an epic, an umbrella issue — into a set of native sub-issues in a
9
+ single pre-validated batch, each already linked to its parent. This is the slice-and-fan-out act done by
10
+ hand today: draft the children, validate them all, create them, confirm the tree.
11
+
12
+ ## Opening branch — create a new umbrella when none exists
13
+
14
+ When the request is a product intent rather than an existing issue, gather the intent first, read the
15
+ repo's live issue templates, draft the umbrella from the matching feature/task template, and show that
16
+ draft for the user's go. Only then create one issue with the `epic` label and an explicit Priority field;
17
+ re-read it from the board to prove the body, label, and priority landed. Stop there unless the user also
18
+ asked for and approved child decomposition.
19
+
20
+ ## Step 0 — load the umbrella whole
21
+
22
+ Read the parent end-to-end before slicing — body **and every comment**, treating later comments as
23
+ superseding:
24
+
25
+ ```bash
26
+ mmi-cli oracle board show <owner/repo#N> # status, body, and every comment for the umbrella
27
+ ```
28
+
29
+ Slice by deliverable, not by phase: each child must be independently claimable and land in its own PR.
30
+ Keep the parent as the umbrella — do not close it until the last child merges.
31
+
32
+ ## Step 1 — draft the batch file
33
+
34
+ Write a JSON array (one object per child) to a scratch path under `tmp/`. Each row:
35
+
36
+ ```json
37
+ [
38
+ { "type": "task", "title": "C1: extract the shared frame", "body": "…", "priority": "high",
39
+ "surface": "cli", "parent": "owner/repo#42" },
40
+ { "type": "feature", "title": "C2: wire the new command", "body": "…", "priority": "medium",
41
+ "parent": "owner/repo#42" }
42
+ ]
43
+ ```
44
+
45
+ - `type` — `bug` · `feature` · `task` (required; maps to the repo's issue-template label).
46
+ - `title` — required, non-empty; keep it a short, greppable deliverable.
47
+ - `parent` — the umbrella ref (`#N`, `owner/repo#N`, or URL); links each child as a native sub-issue on
48
+ create, so you do not need a separate `link-child` pass. Cross-repo is fine (a Hub umbrella can parent
49
+ product-repo children).
50
+ - `priority` — `urgent|high|medium|low` (defaults to `medium`); sets the board Priority **field**.
51
+ - `repo` — optional per-row target repo (`owner/repo`); defaults to `--repo`, then the current repo.
52
+ This is how one batch fans children out across repos under a single umbrella.
53
+ - `surface` — required when the target repo uses `surface:*` taxonomy; accepts the value with or without
54
+ the prefix. A top-level `--surface <value>` may supply the default for every applicable row.
55
+
56
+ The batch **pre-validates every row before creating any** — one bad row fails the whole set with an
57
+ aggregate error, so you fix all of them once. Get the user's go on the drafted set before writing.
58
+
59
+ ## Step 2 — create the children
60
+
61
+ ```bash
62
+ mmi-cli oracle issue create --type task --batch tmp/epic-children.json --surface cli
63
+ ```
64
+
65
+ `--type` is required by the create command but each row's own `type` governs; pass any valid type as the
66
+ placeholder. It prints per-row `{number,url}` results and fails loud on any misfire. Add
67
+ `--idempotency-key <key>` when a retried loop must not duplicate.
68
+
69
+ The idempotency lookup is find-before-create, not an atomic reservation. Run one batch writer at a time:
70
+ never submit concurrent creates with the same key. Size the calling command's wall-clock budget for the
71
+ whole sequential batch. If the command times out, its outcome is unknown — confirm the process has ended,
72
+ wait for writes to quiesce, inspect the children, then retry the missing rows with the same key.
73
+
74
+ ## Step 3 — confirm the tree
75
+
76
+ ```bash
77
+ mmi-cli oracle issue children <owner/repo#N> # each child: number/title/state/repo/boardStatus/linkedPrs
78
+ ```
79
+
80
+ Verify every intended child is linked and on the board as Todo. To link a child that already existed (not
81
+ part of the batch), use the inverse-friendly single link:
82
+
83
+ ```bash
84
+ mmi-cli oracle issue link-child <parent> <child>
85
+ ```
86
+
87
+ Each child is now a parallel item — fan them out one worktree + PR each. **When the last child merges,
88
+ close the umbrella** (its board `Done` follows automatically).
89
+
90
+ ## Notes
91
+
92
+ - Native sub-issues, not a body task-list: the parent renders a sub-issue checklist and each child renders
93
+ its parent — nothing to hand-maintain.
94
+ - Never free-type children with raw `gh issue create` — the batch path carries validation, board attach,
95
+ and structured output.
96
+
97
+ ## Retro — one check before you finish
98
+ Before your final report, answer one question honestly: did **this skill's own instructions** misfire
99
+ this run — ambiguous wording, a misleading message, or an environment failure it should have warned
100
+ about? (Process only — never the user's code or task; e.g. a batch schema that rejected a valid row, or a
101
+ link that attached to the wrong parent.) If yes, file **one** lesson and move on; a clean run is silent
102
+ (hard cap: one per run). It lands on the Hub board (deduped) and is fixed only via a reviewed PR — never
103
+ edit the skill live; the retro is advisory, so if the call fails, note it and continue:
104
+ `mmi-cli learning skill-lesson --skill epic --title "<what misfired>" --body "<what; evidence; proposed amendment>"`
@@ -0,0 +1,165 @@
1
+ ---
2
+ name: hotfix
3
+ description: Promote an already-merged development fix to main and production through the gated Hub train.
4
+ ---
5
+
6
+ **Host-native invocation:** Claude `/mmi:hotfix` · Codex `$mmi:hotfix` · jervcode/Kimi `/skill:hotfix` · Kilo `skill` tool. A backticked `/name` in this doc names the matching workflow (this skill or a sibling), not a literal command.
7
+
8
+ # /hotfix — promote a development fix to main + prod
9
+
10
+ A hotfix is promotion, not authoring. The fix must land on `development` through a normal issue,
11
+ worktree, PR, review, and CI gate first. Hub then ports that merged PR or SHA to a main-base hotfix PR,
12
+ preserves the cherry-pick audit trail, and performs the PATCH release. Never author directly from `main`.
13
+ Never author the fix on a branch off `main`. The main-base branch is only the orchestrated port of a fix
14
+ that is already merged to `development`.
15
+
16
+ The FIX is never back-merged: `development` already contains it, and `rc` absorbs it at the next
17
+ `/rcand`. The release hotfix-coverage guard blocks a later candidate that omits the production fix.
18
+
19
+ The VERSION FOLD is different, and `hotfix release` ports it for you (#4410). `hotfix start` commits the
20
+ fold onto the main-base branch, so after the tag `main` declares the new version while `development`
21
+ still declares the old one. The Hub's catalogs now pin exact npm packages (#4948), so there is no
22
+ branch/content lockstep red; the fold still must land so the next release starts from production's real
23
+ version, BOM, and package pins. Step 3 opens that development-base fold PR automatically; land it.
24
+
25
+ ## Merge floor (#3167 — non-negotiable)
26
+
27
+ - **Never squash-merge a tagged commit.** The hotfix PR itself may squash (step 2 — the tag lands *after*,
28
+ on the merged `main` HEAD, and the `-x` trailer must survive the squash message). But any merge that
29
+ carries an already-tagged commit — an alignment PR, a later `main → development` roll-forward — lands as a
30
+ true merge, never squash: a squash re-mints the SHA and orphans the tag from the branch lineage.
31
+ - **A refused release train is a stop, never a license to finish by hand.** A conflict on the port, a red
32
+ authority probe, a failed required check, a refused `hotfix release` — each ends the run. Fix the cause on
33
+ `development` through a normal PR and re-run the orchestrator; never hand-cherry-pick, bare-push, or
34
+ bypass the gate to finish.
35
+ - **Version probes anchor on the latest Release tag, not a branch.** `hotfix status` derives the PATCH from
36
+ the latest Release tag; branch manifests and stale local tags are not version sources.
37
+
38
+ ## Authority and preflight
39
+
40
+ Production changes require the authorized human's explicit approval in the current turn. Verify authority
41
+ and CLI health before starting:
42
+
43
+ ```bash
44
+ mmi-cli oracle org access role <owner/repo> --json
45
+ mmi-cli doctor --no-repo-writes
46
+ ```
47
+
48
+ Stop on a red authority or CLI-version result. The worktree must be clean; move scratch into `tmp/` or
49
+ gitignore it rather than widening the hotfix diff. A TRACKED path named in a `working tree must be clean
50
+ before …` refusal is not scratch: read both `git status --porcelain` columns, treat every state except
51
+ exactly ` M` as real work to commit or stash, and for ` M` discard only when
52
+ `git diff HEAD --numstat -- <paths>` is empty, and only with `git checkout -- <paths>`. Plain
53
+ `git diff --numstat` prints nothing for a merely staged change, so it cannot make that call (#4004).
54
+
55
+ ## 1. Start from the merged development fix
56
+
57
+ Use the orchestrator with an explicit source every time:
58
+
59
+ ```bash
60
+ mmi-cli devops hotfix start --from <development-pr-or-sha>[,<development-pr-or-sha>...] --json
61
+ ```
62
+
63
+ **One hotfix cycle carries as many merged fixes as you name.** `--from` takes a comma-separated list in
64
+ pick order, and every sha it picks goes into the PR's carries marker, so the release proves each one
65
+ present before tagging. Do not open one hotfix per fix: batch everything ready for production into the
66
+ single PATCH. A fix that only becomes ready mid-cycle rides the same tag — cherry-pick it with `-x` onto
67
+ a `hotfix/<tag>-<n>` continuation branch, merge that PR, and rerun `hotfix release` with the full
68
+ `--carries` list; the release pins the newest merged SHA (#1025).
69
+
70
+ `hotfix start` resolves the PATCH version, branches from `origin/main`, performs the cherry-pick with `-x`,
71
+ prepares Hub distribution artifacts when applicable (stamping BOM `sourceCommit` at the durable
72
+ `origin/main` merge-base so a squash merge cannot orphan it — #4361; and for `registry-publish`
73
+ consumers, folds the package version so upgrade probes observe the PATCH), pushes the branch, and opens
74
+ the main-base PR. Do not duplicate those steps with raw branch commands, manual cherry-picks, or raw
75
+ GitHub PR creation.
76
+
77
+ Conflicts confined to regenerable distribution / version-fold paths (e.g. Hub `distribution-bom.json`,
78
+ locked plugin manifests, consumer `package.json`) — and, for hub-serverless + registry-publish, shipped
79
+ skill roots `skills/` and `.kilo-plugin/skills/` (#4363 / #4471) — are auto-resolved (take the picked
80
+ side) and regenerated by the bump/fold step. Source TS under `src/` / `cli/src/` is never auto-resolved.
81
+
82
+ Before cutting the hotfix branch, `hotfix start` preflights every `--from` pick on an accumulating
83
+ `origin/main` tree (#4472). If any pick would hard-stop, it refuses with every failing pick and path and
84
+ prescribes **one** development-based cite-only synthesis PR for the whole batch (#4643; the old
85
+ "cut from origin/main" pattern #4467 wording is retired — a main-parented branch must never merge
86
+ into development, #4365/#4371): cut from `origin/development`, regenerate the conflicting content so
87
+ it cherry-picks cleanly onto `origin/main`, land it with the cite-only marker (the #5096 flow) — not N
88
+ serial ports. Behavior changes still land on `development` first through a normal PR, then rerun hotfix
89
+ start from the port merge SHA(s) — never hand-resolve onto main.
90
+
91
+ ## 2. Verify the hotfix PR
92
+
93
+ Run the repo's local gate against the hotfix branch, then obtain the independent reviewer required by the
94
+ active agent doctrine. Review the actual main-base diff and the original issue acceptance criteria.
95
+
96
+ Wait for required CI through Hub:
97
+
98
+ ```bash
99
+ mmi-cli devops pr checks-wait <hotfix-pr-number>
100
+ mmi-cli devops pr merge <number> --squash
101
+ ```
102
+
103
+ The main-base PR is the production gate. Do not bypass checks or protected-branch authority. After the
104
+ authorized merge, verify the merged main commit still carries the cherry-pick provenance; if the PR was
105
+ squashed, the source trailer must survive in the squash message so future coverage checks remain exact.
106
+ Because Hub's default branch is `development`, the main-base hotfix PR does not close a separate tracking
107
+ issue. The development PR should already have closed the work item; do not add raw GitHub issue operations.
108
+
109
+ ## 3. Run the gated release
110
+
111
+ Derive the tag from `mmi-cli devops hotfix status`, then release with the same declared source:
112
+
113
+ ```bash
114
+ mmi-cli devops hotfix status
115
+ mmi-cli devops hotfix release <vX.Y.Z> --carries <development-pr-or-sha>[,<development-pr-or-sha>...]
116
+ ```
117
+
118
+ Name **every** fix the cycle carries. Each is proven an ancestor of the tagged SHA before tagging; a
119
+ missing one refuses the release and names the continuation path.
120
+
121
+ For MMI-Hub, pass a fresh 2-4 line neutral summary file with `--announce-summary-file`; never reuse a
122
+ stale file and never put a product or brand name in the summary. Hub release scope never includes another
123
+ repo's board or `ds-propagate.yml`. The orchestrator creates the tag and GitHub Release, selects the
124
+ registry-declared deploy path, watches the release-triggered or central deployment, verifies distribution,
125
+ and announces best-effort. Do not hand-dispatch a second publish/deploy path: duplicate release events can
126
+ race or double-publish.
127
+
128
+ For a private package, trust the authenticated publish job and its logged package/version result; a bare
129
+ unauthenticated `npm view` cannot distinguish private from missing.
130
+
131
+ ## 4. Verify and report
132
+
133
+ Report the tag and Release URL, production run and URL, source development PR/SHA, hotfix PR, and these
134
+ branch facts:
135
+
136
+ - `main`: the tag commit is on `origin/main`.
137
+ - `development`: the source fix was already merged before the hotfix began.
138
+ - `rc`: intentionally untouched; the next release coverage gate prevents regression.
139
+ - the development fold PR the release opened (`hotfix-fold/<tag>`), and whether it landed.
140
+
141
+ Do not hand-open a manifest-alignment PR: the version fold is already ported by `hotfix release` — land
142
+ its PR instead of authoring one. That port is explicitly NOT a back-merge — nothing from `main` is
143
+ merged into `development`; the fold is regenerated on a branch cut from `development` itself. If the run
144
+ reported `development fold port FAILED`, follow the manual remedy it named; never improvise a merge from
145
+ `main`.
146
+
147
+ ### Development fold after a hotfix (#4517 / #4948)
148
+
149
+ The Hub's thin catalogs now pin exact npm packages, so the old main-anchored catalog-lockstep gate is
150
+ retired: an unreleased branch cannot deliver a package version the release train has not published.
151
+ Hotfix versioning still belongs on the main-base train, never in an improvised development prepare PR:
152
+
153
+ 1. `mmi-cli devops hotfix start --from <merged-dev-pr-or-sha>` (prepare + distribution bump on `hotfix/vX.Y.Z` from `main`)
154
+ 2. merge the hotfix PR → `mmi-cli devops hotfix release vX.Y.Z` (publish)
155
+ 3. land the automatic `hotfix-fold/<tag>` development fold PR (or follow its printed remedy)
156
+
157
+ The fold is now required for version/BOM/package-pin continuity, not to clear a branch-content gate.
158
+
159
+ ## Retro
160
+
161
+ If this skill's instructions themselves misfired, file one deduplicated lesson and continue:
162
+
163
+ ```bash
164
+ mmi-cli learning skill-lesson --skill hotfix --title "<what misfired>" --body "<what; evidence; proposed amendment>"
165
+ ```