@mutmutco/pi-plugin 3.132.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.
- package/package.json +28 -0
- package/skills/bootstrap/SKILL.md +550 -0
- package/skills/bootstrap/seeds/Dockerfile.template +30 -0
- package/skills/bootstrap/seeds/README.template.md +37 -0
- package/skills/bootstrap/seeds/architecture.template.md +34 -0
- package/skills/bootstrap/seeds/decisions-readme.template.md +45 -0
- package/skills/bootstrap/seeds/docker-compose.template.yml +26 -0
- package/skills/bootstrap/seeds/gate.template.yml +85 -0
- package/skills/bootstrap/seeds/google-login.template.md +33 -0
- package/skills/bootstrap/seeds/manifest.json +26 -0
- package/skills/bootstrap/seeds/mmi-product-required-checks.template.json +23 -0
- package/skills/browser-automation/SKILL.md +95 -0
- package/skills/epic/SKILL.md +104 -0
- package/skills/hotfix/SKILL.md +165 -0
- package/skills/mmi/SKILL.md +405 -0
- package/skills/mmi-doctor/SKILL.md +68 -0
- package/skills/onboard/SKILL.md +85 -0
- package/skills/rcand/SKILL.md +208 -0
- package/skills/release/SKILL.md +600 -0
- package/skills/resume/SKILL.md +90 -0
- package/skills/secrets/SKILL.md +159 -0
- package/skills/stage/SKILL.md +153 -0
- package/skills/worktree/SKILL.md +151 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# {{REPO_NAME}}
|
|
2
|
+
|
|
3
|
+
> One paragraph: what this repo **is** — the product/service and who it's for. (Write fresh — D35: do
|
|
4
|
+
> not carry a legacy README over verbatim; the old docs are archived under `docs/Archive/`.)
|
|
5
|
+
|
|
6
|
+
## What's here
|
|
7
|
+
|
|
8
|
+
(One bullet per top-level dir/module: what it is, in one line — a map, not a tour.)
|
|
9
|
+
|
|
10
|
+
## Who runs it
|
|
11
|
+
|
|
12
|
+
(Owner/operator — who runs this day to day.) Access follows the MMI Future three-level model: read for
|
|
13
|
+
org members, developer as GitHub `write`, project-admin as `write` plus train-branch allowlist. Authority
|
|
14
|
+
detail → [org-architecture §4](https://github.com/mutmutco/MMI-Hub/blob/development/docs/org-architecture.md);
|
|
15
|
+
access runbook → [repo-access](https://github.com/mutmutco/MMI-Hub/blob/development/docs/Guides/repo-access.md).
|
|
16
|
+
|
|
17
|
+
## Agent context
|
|
18
|
+
|
|
19
|
+
Read this section at the start of agent work in this repo.
|
|
20
|
+
|
|
21
|
+
- **Structure search:** `mmi-cli oracle repo-index search <path|symbol|meaning>` — Hub cloud pointer hits
|
|
22
|
+
(Hub#4133). Prefer this over inventing wiki pages or trusting stale inventories under `docs/`.
|
|
23
|
+
- **Durable WHY:** `docs/decisions/` — one file per decision, prose only for what was chosen and
|
|
24
|
+
rejected; never a description of current state. Do not maintain living current-state under `docs/`.
|
|
25
|
+
- **Current state:** code + compute-at-read CLI (`mmi-cli oracle org project get`, `board`, `status`,
|
|
26
|
+
`org schedules`, …) — registry facts, resolved live. Optional generated `docs/index.md` is a
|
|
27
|
+
**routing** index only (`mmi-cli oracle docs index --check`), not product truth.
|
|
28
|
+
- **GitHub wikis are retired org-wide** — this repo does not publish to a `.wiki.git`; do not create one.
|
|
29
|
+
- **Stack:** (languages, frameworks, major services)
|
|
30
|
+
- **Run locally:** (install, dev server, `/stage` if non-obvious)
|
|
31
|
+
- **Verify before done:** (exact commands — test, lint, typecheck, repo gate script)
|
|
32
|
+
- **Architecture:** deep build/deploy shape → `architecture.md`
|
|
33
|
+
- **Gotchas:** (ports, env from vault not files, Windows/shell quirks specific to this repo)
|
|
34
|
+
|
|
35
|
+
## Start
|
|
36
|
+
|
|
37
|
+
(The one human-readable command/steps to get this running locally.)
|
|
@@ -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>"`
|