@mutmutco/opencode-mmi 2.48.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 (39) hide show
  1. package/dist/index.d.ts +35 -0
  2. package/dist/index.js +194 -0
  3. package/package.json +44 -0
  4. package/skills/_shared/doctrine.md +238 -0
  5. package/skills/bootstrap/SKILL.md +419 -0
  6. package/skills/bootstrap/seeds/Dockerfile.template +25 -0
  7. package/skills/bootstrap/seeds/README.template.md +36 -0
  8. package/skills/bootstrap/seeds/architecture.template.md +19 -0
  9. package/skills/bootstrap/seeds/components.json.template +31 -0
  10. package/skills/bootstrap/seeds/cursor-environment.template.json +3 -0
  11. package/skills/bootstrap/seeds/cursor-rules.template.mdc +11 -0
  12. package/skills/bootstrap/seeds/design-system.paths.template.json +8 -0
  13. package/skills/bootstrap/seeds/docker-compose.template.yml +17 -0
  14. package/skills/bootstrap/seeds/gate.template.yml +42 -0
  15. package/skills/bootstrap/seeds/google-login.template.md +35 -0
  16. package/skills/bootstrap/seeds/manifest.json +32 -0
  17. package/skills/bootstrap/seeds/mcp-playwright.template.json +13 -0
  18. package/skills/bootstrap/seeds/mmi-product-required-checks.template.json +23 -0
  19. package/skills/browser-automation/SKILL.md +137 -0
  20. package/skills/build/SKILL.md +237 -0
  21. package/skills/build/references/halt-report.md +38 -0
  22. package/skills/build/references/loops.md +13 -0
  23. package/skills/build/references/worked-example.md +18 -0
  24. package/skills/build/templates/campaign-northstar.md +40 -0
  25. package/skills/coop/SKILL.md +77 -0
  26. package/skills/grind/SKILL.md +469 -0
  27. package/skills/grind/references/auto.md +107 -0
  28. package/skills/grind/references/build-notes.md +56 -0
  29. package/skills/grind/references/routing.md +76 -0
  30. package/skills/grind/references/verify.md +83 -0
  31. package/skills/grind/templates/saga-snapshot.md +28 -0
  32. package/skills/grind/templates/synthesize-panel.md +104 -0
  33. package/skills/handoff/SKILL.md +67 -0
  34. package/skills/hotfix/SKILL.md +219 -0
  35. package/skills/mmi/SKILL.md +372 -0
  36. package/skills/rcand/SKILL.md +169 -0
  37. package/skills/release/SKILL.md +309 -0
  38. package/skills/secrets/SKILL.md +137 -0
  39. package/skills/stage/SKILL.md +150 -0
@@ -0,0 +1,419 @@
1
+ ---
2
+ name: bootstrap
3
+ description: Provision a repo into the MMI org — branches, ruleset, the Project board, registry META, secrets + OIDC, the plugin, and doc seed. Master-admin only. Use when onboarding, adding, or provisioning a repo into the org, setting up a new project repo, making a repo a first-class org citizen, or invoking /bootstrap.
4
+ ---
5
+
6
+ # /bootstrap — provision a repo into the org
7
+
8
+ The one-time onboarding that turns a repo into a first-class org citizen. **Master-admin only, run from
9
+ `MMI-Hub` (the hub).** Every step operates org-level resources (Project, Ruleset, org secrets, member
10
+ access) **through the GitHub App's installation token** — not a human credential — so the gate is who may
11
+ invoke the skill (the master holds the App key), not the caller's GitHub role.
12
+
13
+ Bootstrap operates on the target repo entirely through the App — **no per-repo checkout**. The repo must
14
+ already be named on the taxonomy `<CATEGORY>-<PascalName>`.
15
+
16
+ ## Seed sources + create-vs-upgrade
17
+
18
+ The org-standard scaffolding is a **machine-readable manifest** — `skills/bootstrap/seeds/manifest.json`
19
+ (loaded by the CLI; `mmi-cli bootstrap --apply` consumes it). Every seed carries an **ownership**:
20
+
21
+ - **`org`** — org-delivered, **overwritten on upgrade** (the org owns it): `.claude/settings.json`, the issue
22
+ templates, and the spine `AGENTS.md`/`CLAUDE.md`. `source: self` = copied verbatim from MMI-Hub's own
23
+ current file, so the spine is **seeded at bootstrap** (a fresh repo verifies green immediately, #927); the
24
+ fanout pipeline (`.github/fanout-targets.json`) still owns ongoing spine-change **updates**. Board
25
+ moves are central (the Hub webhook), so **no per-repo board workflow is stamped**.
26
+ - **`repo`** — created **once on a fresh bootstrap**, **never clobbered on upgrade** (the repo owns its
27
+ content): `README.md`, `architecture.md`, `.cursor/environment.json`, `.cursor/rules/<slug>.mdc`.
28
+ These render `seeds/*.template.*` with `{{PLACEHOLDERS}}`.
29
+
30
+ So **create** stamps every seed; **upgrade** refreshes the `org` seeds and adds any *missing* `repo` seeds
31
+ without overwriting existing repo-owned files (D35: legacy docs are archived + written fresh, never carried
32
+ over verbatim). The manifest's `labels` list is the canonical 7-label set. The per-step instructions below
33
+ are the manual path; `--apply` automates them from this manifest.
34
+
35
+ **CLI-release gap.** A new topology value (a `--project-type`, `--deploy-model`, or `--release-track` added
36
+ since the last release train) lands on `development` but is not in the published/installed `mmi-cli` until a
37
+ release train ships it. So bootstrapping a repo that needs a just-merged topology requires a CLI that
38
+ includes it: run a `/release` train first, **or** invoke a local dev build —
39
+ `node <MMI-Hub checkout>/cli/dist/index.cjs bootstrap apply ...` from a `development` checkout (rebuild
40
+ `dist` first: `npm --prefix cli ci && npm --prefix cli run build`) — instead of the installed `mmi-cli`. The
41
+ installed CLI rejects an unknown enum with no hint that it merely lags `development`.
42
+
43
+ ## Step 0 — capability shape (confirm the four axes before any mutation)
44
+
45
+ Bootstrap is **destructive to undo**: a wrong shape means converting in place later — deleting protected
46
+ branches, temporarily disabling the org-wide `mmi-train-floor` ruleset (which affects every repo for that
47
+ window), and tearing down train artifacts (#1450, the MMD-UI case). So **before Step 0a touches anything**,
48
+ surface and **confirm all four topology axes with the master** — present the recommended default for each,
49
+ never apply it silently. Use the structured-question UI; one question per axis (or one grouped confirm).
50
+
51
+ - **`class`** — `deployable` (ships an app/service; full or direct train) · `content` (KB/docs/design-system;
52
+ trunk, `main`-only). *Controls the branch model.* Recommend from what the repo **is**, not a fixed default.
53
+ - **`project-type`** — e.g. `web-app`, `cli-tool`, `worker`, `desktop-game`, `content`. *Controls which Hub
54
+ services attach.*
55
+ - **`deploy-model`** — e.g. `tenant-container`, `none`, `content`. *Controls the deploy path.*
56
+ - **`release-track`** — `full` (development·rc·main) · `direct` (development·main, skips rc) · `trunk`
57
+ (`main` only). *The branch set follows the track, not just the class (#1097).*
58
+
59
+ **Dashboard opt-in (`--dashboard`, #1452).** Orthogonal to the four axes — an **additive** flag, not a new
60
+ type. Pass `--dashboard` when the repo is an MMI dashboard that consumes the `@mutmutco` design system; it
61
+ persists `dashboard: true` on the registry META and seeds one extra repo-owned file, `components.json`,
62
+ pre-pointed at the registry (`https://ui.mutatismutandis.co/r/{name}.json`) plus `design-system.paths.json`
63
+ (tsconfig path aliases → the gitignored `.mmi/design-system/components/` cache doctor maintains). Registry
64
+ component **files are not committed** — `mmi-cli doctor --apply` pulls them into `.mmi/` when stale. List
65
+ desired components in `components.json` → `mmi.installed`. It changes **no** other
66
+ axis — a dashboard is still the standard deployable `web-app` / `tenant-container` (commonly `direct` track,
67
+ like MMD-UI). The app UI itself is scaffolded from the MMD-UI `apps/starter` (a separate step), not copied by
68
+ bootstrap. Recommend it for a new dashboard repo; omit it everywhere else (a plain repo's META and seed set
69
+ are byte-identical to before this flag existed). After register, registry META `dashboard` is the SSOT —
70
+ `bootstrap apply` re-seeds dashboard-gated files when META is true (no `--dashboard` needed); toggle with
71
+ `mmi-cli project set <repo> --var dashboard=true|false` (project-admin or master). See the dashboard guide:
72
+ MM-KB `kb/guides/dashboard-ui.md`.
73
+
74
+ For a **deployable** repo, also capture the **gate runtime** (`node` | `python`), the **check command**, and
75
+ the **working directory** — these feed the product gate (`--var GATE_RUNTIME=`, `--var GATE_CMD=`,
76
+ `--var GATE_WORKDIR=`, and `--var GATE_CACHE_DEP_PATH=` for a non-root Node lockfile; see Step 5 / the
77
+ "Product gate + required checks" note below). Default to `node` at the repo root; recommend `python` when the
78
+ repo's app is Python.
79
+
80
+ A **content/trunk** choice provisions: `main`-only (no `rc`), **no** `.github/workflows/gate.yml` and **no**
81
+ product required-check ruleset, and a `projects.json` entry with `branch: main` (which derives the content
82
+ fanout lane). A clean content repo then verifies green — `bootstrap verify --class content` no longer reports
83
+ the deployable gate checks as FAIL (#1450).
84
+
85
+ Record the confirmed axes; Step 0a runs `verify --class <confirmed>`, Step 0b creates the repo on the
86
+ track's default branch, and the apply flags (`--project-type` / `--deploy-model` / `--release-track`) carry
87
+ the confirmed values — no silent defaulting.
88
+
89
+ ## Step 0a — no-mutation verifier
90
+
91
+ Before mutating anything, run the verifier so the current gaps are concrete:
92
+ ```bash
93
+ mmi-cli bootstrap verify "$OWNER/$REPO" --class deployable --json
94
+ # or for content repos:
95
+ mmi-cli bootstrap verify "$OWNER/$REPO" --class content --json
96
+ ```
97
+
98
+ Run it again after Step 7. A repo is not ready for real developers until every check is green, or the report
99
+ names an explicitly manual-only item that the master has accepted for that repo.
100
+
101
+ ## Step 0b — create the repo (when it does not exist yet)
102
+
103
+ If `$OWNER/$REPO` is not on GitHub yet, create it and seed an initial commit **before** Step 1 — a brand-new
104
+ repo has no commits, so there is no `development` branch to push and no default branch to set:
105
+ ```bash
106
+ gh repo create "$OWNER/$REPO" --private --confirm
107
+ tmpdir=$(mktemp -d)
108
+ git -C "$tmpdir" init -b development
109
+ git -C "$tmpdir" commit --allow-empty -m "chore: initial commit"
110
+ git -C "$tmpdir" remote add origin "https://github.com/$OWNER/$REPO.git"
111
+ git -C "$tmpdir" push -u origin development
112
+ rm -rf "$tmpdir"
113
+ gh repo edit "$OWNER/$REPO" --default-branch development
114
+ ```
115
+
116
+ For a **content** repo (`trunk` track), use `-b main` and push `main` instead. Skip this step when the repo
117
+ already exists with the track's default branch.
118
+
119
+ ## Step 1 — branches
120
+
121
+ Determine the repo's **release track** first — the branch set follows the track, not just the class (#1097),
122
+ so a direct-track repo never gets a stray `rc` the `mmi-train-floor` ruleset can't clean up:
123
+
124
+ - **full** (deployable default) — three permanent branches, default `development`: `development`, `rc`, `main`.
125
+ - **direct** (deployable, skips rc — e.g. cli-tool/worker/Hub) — two permanent branches, default
126
+ `development`: `development`, `main`.
127
+ - **trunk** (content) — one permanent branch, default `main`: `main` only.
128
+
129
+ Ensure exactly the track's permanent branches exist and set the default branch — create only what the track
130
+ uses; never push an `rc` for a direct-track repo:
131
+ ```bash
132
+ # full
133
+ git push origin development rc main # create any missing
134
+ gh repo edit "$OWNER/$REPO" --default-branch development
135
+
136
+ # direct
137
+ git push origin development main # create any missing — NO rc
138
+ gh repo edit "$OWNER/$REPO" --default-branch development
139
+
140
+ # trunk (content)
141
+ git push origin main # create if missing
142
+ gh repo edit "$OWNER/$REPO" --default-branch main
143
+ ```
144
+
145
+ ## Step 2 — authority (org Ruleset)
146
+
147
+ Confirm the org-level rulesets already target this repo (they apply org-wide): **`mmi-branch-protection`**
148
+ PR-gates `development` and blocks force-push/deletion there (bypass = org admins + the GitHub App 3026732),
149
+ and **`mmi-train-floor`** blocks force-push/deletion on `rc`/`main` (no PR rule — the train pushes merges and
150
+ tags directly; no bypass). (Definitions mirror live in `.github/rulesets/mmi-branch-protection.json` and
151
+ `.github/rulesets/mmi-train-floor.json`.) MMI-Hub additionally has its own repository ruleset,
152
+ `.github/rulesets/mmi-hub-required-checks.json`, for the Hub-only `cli`, `infra`, and `docs` jobs; do not
153
+ apply those contexts org-wide unless every target repo exposes them.
154
+
155
+ ## Step 2b — lock the train branches (who can push)
156
+
157
+ The ruleset says *a PR is required*; this says *who may merge it*. Apply classic branch protection on
158
+ `development`/`rc`/`main` for deployable repos, or just `main` for content repos, with **"Restrict who can
159
+ push"** = the master + the App (the repo's full-write people get added at Step 4b). Everyone else is
160
+ `write`-locked on protected branches: they push feature branches and open PRs but cannot merge there. Run per
161
+ protected branch (App token):
162
+
163
+ ```bash
164
+ BRANCHES="development rc main" # full
165
+ BRANCHES="development main" # direct (no rc)
166
+ BRANCHES="main" # trunk / content
167
+
168
+ mapfile -t MASTER_USERS < <(gh api --paginate "orgs/$OWNER/members?role=admin" --jq '.[].login')
169
+ if [ "${#MASTER_USERS[@]}" -eq 0 ]; then
170
+ echo "No org owners resolved for $OWNER; stop before writing branch protection." >&2
171
+ exit 1
172
+ fi
173
+ USERS_JSON=$(printf '%s\n' "${MASTER_USERS[@]}" | node -e "const fs=require('fs'); const users=fs.readFileSync(0,'utf8').trim().split(/\r?\n/).filter(Boolean); process.stdout.write(JSON.stringify(users));")
174
+
175
+ for b in $BRANCHES; do
176
+ node - "$USERS_JSON" <<'NODE' | gh api --method PUT repos/$OWNER/$REPO/branches/$b/protection --input -
177
+ const users = JSON.parse(process.argv[2]);
178
+ process.stdout.write(JSON.stringify({
179
+ required_status_checks: null,
180
+ enforce_admins: false,
181
+ required_pull_request_reviews: null,
182
+ restrictions: { users, teams: [], apps: ['mmi-github-app'] },
183
+ }, null, 2));
184
+ NODE
185
+ done
186
+ ```
187
+
188
+ Only the master (sole repo `admin`) can change this afterward. Full grant/lock mechanics + inspect commands:
189
+ `docs/Guides/repo-access.md`.
190
+
191
+ ## Step 3 — attach to the configured Project (default by prefix; confirm)
192
+
193
+ Division boards exist as defaults — the repo's `<CATEGORY>` prefix picks the starting recommendation
194
+ (`MMI-*` → the **MMI** project, `MMG-*` → **MMG**, `MMS`/`MMD`/`MMC` likewise, `MM-*` → **MM**). **Confirm
195
+ with the master** whether the repo joins that board, joins another existing board, or gets a dedicated
196
+ product/initiative board; repos/projects are **not 1:1**.
197
+ ```bash
198
+ gh project list --owner "$PROJECT_OWNER" --format json # existing boards to choose from
199
+ ```
200
+ - **Attach** to the chosen project:
201
+ ```bash
202
+ gh project link "$PROJECT_NUMBER" --owner "$PROJECT_OWNER" --repo "$OWNER/$REPO"
203
+ ```
204
+ - **Create** the chosen board if it doesn't exist yet — clone an existing board so the 4-lane `Status` field
205
+ (`Todo · In Progress · In Review · Done`) **and its built-in workflows carry over**:
206
+ ```bash
207
+ gh project copy <existing-project#> --source-owner "$PROJECT_OWNER" \
208
+ --target-owner "$PROJECT_OWNER" --title "<Project>"
209
+ ```
210
+ Then **interview the master** for the seed short description + README (what it tracks, member repos, links
211
+ to the repos' `README.md`/`architecture.md`); set them via the `updateProjectV2` mutation
212
+ (`shortDescription`, `readme`). If cloning, verify the built-in workflows survived (next note).
213
+ - **Built-in workflows:** `mmi-cli bootstrap verify` checks these Project workflows are enabled:
214
+ `Auto-add sub-issues to project`, `Auto-archive items`, `Item added to project`, and `Item closed`. The
215
+ Todo/In Progress/In Review moves are **central** (the Hub webhook); the built-in `Item closed` sets `Done`
216
+ on merge. GitHub's public GraphQL schema exposes delete/read surfaces for Project workflows but no
217
+ create/update/enable mutation; if any required workflow is missing or disabled, repair it in the project's
218
+ **Workflows** settings before calling the repo ready for developers.
219
+
220
+ Record the chosen `projectNumber`/`projectId` plus Status/Priority field ids in the Hub registry META
221
+ (`PROJECT#<slug>`) via `mmi-cli bootstrap apply --execute` or `mmi-cli project set` after the board is
222
+ known. Going forward the thin Lambda adds each new issue to that project on `issues.opened` and sets
223
+ `Status: Todo`.
224
+
225
+ **Register the project for cloud agents.** The same registry META row carries `{name, slug, projectId,
226
+ wikiRepo, repos[]}`; do not append to a committed `projects.json`. This is what makes `wiki-keeper` run for
227
+ the project (the launcher fans one run per registered project, generating its wiki + refreshing its board).
228
+ `kb-keeper`, `saga-keeper`, and `northstar-keeper` are org-wide and need no per-project entry. If attaching this repo to an
229
+ existing project, merge this repo into that project's `repos[]` instead of creating a new project.
230
+
231
+ ## Step 4 — vault tiers + deploy substrate
232
+
233
+ Provision the repo's vault namespace and deploy substrate from the Hub, not from repo-local Actions
234
+ secrets. Runtime config names live in the two-tier vault (`/mmi-future/<slug>/dev|rc|main/*`) and are
235
+ managed through `/secrets`; never use `gh secret set` for product runtime config.
236
+
237
+ The default `tenant-container` substrate is a **Hetzner box** (`hetzner-ssh`): the box writes the release
238
+ `.env` from the registry + vault and runs the container via docker-compose, deployed over the Hub's bounded
239
+ SSH lane. Do **not** create an AWS OIDC deploy role or a repo deploy Action for it. **Ask the master for the
240
+ box assignment** — the `sshHost` (and the loopback port) per stage — then write the `DEPLOY#<stage>` rows with
241
+ `mmi-cli project set-deploy <owner/repo> --stage <dev|rc|main> --ssh-host <host> [--port <p>]` (defaults:
242
+ `substrate: hetzner-ssh`, deploy path `/opt/mmi/<slug>/<stage>`, service = slug, ssh-user `root`). Without those
243
+ rows the tenant cannot deploy (`tenant-deploy.yml` errors on missing `DEPLOY#` coords), so do not skip this.
244
+ Keep every runtime config value in the vault; never paste secret values into logs.
245
+
246
+ Only an AWS `tenant-container` (the exception) provisions the reusable tenant stack: release bucket,
247
+ per-stage OIDC deploy role, and constrained service-control role/document. Either way the deploy path
248
+ trusts the Hub's central `tenant-deploy.yml` / `tenant-control.yml` environments, not a product repo deploy
249
+ workflow.
250
+
251
+ ## Step 4b — developer access
252
+
253
+ Grant each developer their **org membership + repo access** through the App (`gh api` with the App token):
254
+ `write` for a developer; for a **full-write** member ("project-admin") also add them to the train-branch
255
+ push allowlist from Step 2b (so they can merge protected development PRs). GitHub's
256
+ collaborator list + the per-branch allowlist are the record — no separate roster. Exact commands:
257
+ `docs/Guides/repo-access.md`.
258
+
259
+ ## Step 4c — CI trigger, labels, templates, wiki
260
+
261
+ - **Enable workflow triggers** — a freshly-provisioned repo can have GitHub Actions auto-trigger stuck off
262
+ (push/PR never run workflows though dispatch does). Toggle it off→on:
263
+ ```bash
264
+ gh api -X PUT repos/$OWNER/$REPO/actions/permissions -F enabled=false
265
+ gh api -X PUT repos/$OWNER/$REPO/actions/permissions -F enabled=true -f allowed_actions=selected
266
+ ```
267
+ - **Self-hosted CI is the org default** — the `mmi-automation` runner group is org-wide (all repos), so a
268
+ new repo needs no manual group join. Any CI workflow it adds uses
269
+ `runs-on: [self-hosted, linux, x64, mmi-live]` (never `ubuntu-*`/`windows-*`/`macos-*`, which bill
270
+ GitHub-hosted minutes); jobs needing system packages run in a `container:`. See
271
+ `docs/Guides/gh-runner-runbook.md`.
272
+ - **Product gate + required checks (#1333, stack-aware #1550)** — deployable repos bootstrap-seed
273
+ `.github/workflows/gate.yml` (single `gate` job) and a ruleset reference at
274
+ `.github/rulesets/mmi-product-required-checks.json`. The gate is stack-aware: capture the repo's
275
+ **runtime** (`node` | `python`), its **check command**, and its **working directory** at interview time
276
+ and pass them as `--var GATE_RUNTIME=node|python`, `--var GATE_CMD=...`, `--var GATE_WORKDIR=...` (and
277
+ `--var GATE_CACHE_DEP_PATH=<path/to/package-lock.json>` when the app's Node lockfile is not at the repo
278
+ root). Defaults: `node` runtime, `npm run check` / `npm ci` at the repo root; a Python repo defaults to
279
+ `pytest` / `pip install -e ".[dev]"` with `--var GATE_PY_VERSION=` (3.11 default). The runtime selects
280
+ which setup step (`setup-node` vs `setup-python`) the rendered `if:` fires. After apply,
281
+ master-admin must **activate** that JSON as a repository ruleset (GitHub → Settings → Rules → Rulesets →
282
+ Import/create from the committed reference) so the `gate` context is required on train branches. MMI-Hub
283
+ keeps its own three-job gate (`cli`/`infra`/`docs`) — never apply the product ruleset there.
284
+ - **Standard labels** — type labels only (the issue templates reference them). **Priority is a Project
285
+ field, not a label** (#416): never seed `priority:*` labels; `--priority` writes the board field.
286
+ ```bash
287
+ gh label create bug --color d73a4a --description "Something is broken or behaving wrong" -R $OWNER/$REPO
288
+ gh label create feature --color a2eeef --description "New capability or enhancement" -R $OWNER/$REPO
289
+ gh label create task --color 0052cc --description "Task, chore, or improvement" -R $OWNER/$REPO
290
+ ```
291
+ - **Board view (Priority chip)** — the Project's board view must **show the Priority field on cards**, not
292
+ the Labels field (GitHub's API can't set view columns): Board view → **Fields** → remove **Labels**, add
293
+ **Priority**. Otherwise cards show label soup and no priority chip. Strip any legacy `priority:*` /
294
+ taxonomy labels with `mmi-cli board prune-priority-labels`.
295
+ - **Org App credentials** — nothing to register per repo (#494). Board moves are central (the Hub webhook
296
+ moves Todo/In Progress/In Review for every repo) and the org App token is minted inside the Hub's own
297
+ central workflows, so `MMI_APP_ID` / `MMI_APP_PRIVATE_KEY` live only on the Hub — a product repo seeds no
298
+ App var/secret.
299
+ - **Issue templates** — seed `.github/ISSUE_TEMPLATE/` (Bug · Feature · Task + `config.yml`).
300
+ - **Merge settings (org canon)** — every repo: auto-merge on, squash on, delete-branch-on-merge on, so a
301
+ green allowlisted PR can land itself and never leaves a dead branch:
302
+ `gh api -X PATCH repos/$OWNER/$REPO -f allow_auto_merge=true -f allow_squash_merge=true -f delete_branch_on_merge=true`
303
+ - **Wiki** — `gh api -X PATCH repos/$OWNER/$REPO -F has_wiki=true`, then **create the wiki's first page once**
304
+ in the UI (an empty page is enough — GitHub blocks programmatic first-page creation). **MMI-Keeper**
305
+ generates and maintains the wiki thereafter (on release + weekly); no further manual edits.
306
+ - **Cursor environment** — commit `.cursor/environment.json` with the repo's dependency `install` command
307
+ (e.g. `npm ci`, `npm --prefix infra ci`, `pip install -r requirements.txt`). Cursor honors repo-level
308
+ config and auto-snapshots after install, so the repo's cloud agents (keepers) get fast cold starts with
309
+ no dashboard step. A repo needing system packages can instead use `build.dockerfile` (a committed
310
+ `.cursor/Dockerfile`, layer-cached). No snapshot-by-ID (that path needs the dashboard wizard).
311
+
312
+ ## Step 5 — install the plugin + seed docs
313
+
314
+ - Write the repo's `.claude/settings.json` (marketplace + `enabledPlugins`) so Claude Code prompts the
315
+ developer to install the org plugins: `mmi@mutmutco` (its SessionStart hook keeps the rules current) and
316
+ `superpowers@claude-plugins-official` (Anthropic's skills framework — TDD, debugging, subagent dev). Mirror
317
+ the hub's own `.claude/settings.json` verbatim. Fanout keeps this file current after bootstrap —
318
+ it is org-owned and delivered verbatim like the spine.
319
+ - Seed `README.md` + `architecture.md` from the templates, then **fill them before finishing** — `bootstrap
320
+ verify` now fails on any leftover `(placeholder)` or `{{TOKEN}}` (#1520). Fill the code-local fields from
321
+ what this bootstrap already knows: **Stack / Run locally / Verify** from the Step-4c gate + install commands
322
+ and the repo's actual code; **Gotchas** and the architecture **Overview / Build & deploy** from the confirmed
323
+ Step-0 axes (class, project-type, deploy-model). Do **not** write the release track, board number, or deploy
324
+ coords as a value — the templates point at `mmi-cli project get` (registry SSOT, never copied, so it cannot drift). Write
325
+ the delivered `AGENTS.md` / `CLAUDE.md` verbatim (org-owned, whole-file — identical in every repo; no markers,
326
+ no tail).
327
+ - **Push the mandated fill past the active ruleset (#1807).** Deployable repos activate
328
+ `mmi-product-required-checks` during apply (its `bypass_actors` is empty by design), so the `gate` check is
329
+ required on `development`/`main` before the seeded README + architecture have ever produced a green run. The
330
+ fill above (#1520) then cannot be pushed, and not even `gh pr merge --admin` clears it (GH013 on push /
331
+ GraphQL rule violation on admin-merge). Sanctioned final step: in GitHub Settings > Rules > Rulesets >
332
+ `mmi-product-required-checks`, set **Enforcement** to **Disabled**, push/merge the filled `README.md` +
333
+ `architecture.md`, then set **Enforcement** back to **Active**. Programmatic equivalent: PUT the ruleset
334
+ with `enforcement: disabled` (a PATCH is rejected, #917/#922), push the fill, then PUT it back to
335
+ `active` (or re-run `bootstrap apply`, which re-activates the ruleset idempotently). Never leave enforcement
336
+ disabled.
337
+ - Make sure the target repo's `.gitignore` does **not** ignore `.claude/settings.json`; only
338
+ `.claude/settings.local.json` is local-only.
339
+ - **Reserve the spine filenames.** `AGENTS.md` / `CLAUDE.md` are the org spine — never repo-specific. Seed
340
+ `.cursor/rules/<repo-slug>.mdc` (frontmatter `alwaysApply: true`) with repo-specific agent guidance (what
341
+ the repo is, services/deps, build/test, conventions). Include the saga startup pattern for non-Claude
342
+ agents: `mmi-cli saga health --json`, `mmi-cli saga show`, then explicit `mmi-cli saga note` at handoff or
343
+ meaningful checkpoints. This gives Cursor's setup agent a home so it does not hijack `AGENTS.md`; Cursor
344
+ cloud agents read `.cursor/rules` automatically.
345
+
346
+ ## Step 6 — register Hub META
347
+
348
+ Do not seed a product repo `.mmi/config.json`. `mmi-cli` carries the Hub API endpoint and resolves board,
349
+ deploy, secret, and project state from the Hub registry at runtime.
350
+
351
+ Choose the v2 shape explicitly. Use `--project-type web-app --deploy-model tenant-container` for ordinary
352
+ web tenants, `--project-type desktop-game --deploy-model none --clear-web-profile` for a desktop game, and
353
+ `--class content --project-type content --deploy-model content --clear-web-profile` for a content/KB repo.
354
+ Run the apply path with the board variables discovered above, or register the same values with
355
+ `mmi-cli project set` from the Hub or from the target project checkout:
356
+
357
+ ```bash
358
+ mmi-cli bootstrap apply "$OWNER/$REPO" --class deployable \
359
+ --project-type web-app --deploy-model tenant-container --execute \
360
+ --var PROJECT_OWNER="$PROJECT_OWNER" \
361
+ --var PROJECT_NUMBER="$PROJECT_NUMBER" \
362
+ --var PROJECT_ID="$PROJECT_ID" \
363
+ --var STATUS_FIELD_ID="$STATUS_FIELD_ID" \
364
+ --var STATUS_TODO="$STATUS_TODO" \
365
+ --var STATUS_IN_PROGRESS="$STATUS_IN_PROGRESS" \
366
+ --var STATUS_IN_REVIEW="$STATUS_IN_REVIEW" \
367
+ --var STATUS_DONE="$STATUS_DONE"
368
+ ```
369
+
370
+ Tenant-container repos must carry a `docker-compose.yml` and Dockerfile that build from the shipped source
371
+ archive; the train does not ship a prebuilt `dist/`. **Bootstrap seeds both files** for
372
+ `deployModel: tenant-container` from `skills/bootstrap/seeds/` (rendered from
373
+ `docs/Reference/tenant-runtime/docker-compose.yml` and `docs/Reference/tenant-runtime/Dockerfile`). The box
374
+ writes the release `.env` from the registry + vault at deploy time; the compose file carries `env_file: .env`
375
+ and the app reads plain env vars — it must **not** self-load SSM and must **not** ship a committed `.env`.
376
+
377
+ For a `web-app` that declares `oauth` META, print the canonical OAuth surface and provision the client once:
378
+
379
+ ```bash
380
+ mmi-cli oauth plan --repo "$OWNER/$REPO" # the exact JS origins + redirect URIs + canonical SSM keys
381
+ ```
382
+
383
+ Register those JS origins + `/api/auth/callback` redirect URIs on the Console client (master, per
384
+ `docs/Guides/oauth-provision.md`), then store the creds in the canonical keys in one step:
385
+
386
+ ```bash
387
+ mmi-cli oauth set-creds --repo "$OWNER/$REPO" < client.json # the Console "Download JSON" file
388
+ ```
389
+
390
+ The keys are `{dev,rc,main}/GOOGLE_CLIENT_ID|SECRET` — never a `GOOGLE_OAUTH_CLIENT_*` or `prod/` variant; the
391
+ runtime reads only the canonical names.
392
+
393
+ Local `/stage` is optional product-owned configuration. A repo that wants `/stage` may carry a local
394
+ `stage` block, but that file must not contain board, deploy, or secret registry facts.
395
+
396
+ **Stage port block:** run `mmi-cli port-range <Repo>` to assign (idempotently) the repo's local port block
397
+ from the central registry. Use `$STAGE_PORT` in `stage.up` / `healthUrl`; `/stage` then picks a free port
398
+ inside the block so a dev can run several projects/versions locally without collisions.
399
+
400
+ ## Step 7 — register for fanout
401
+
402
+ Add the repo to `.github/fanout-targets.json` so future org-spine changes reach it via App-token PRs. Use
403
+ `branch: "development"` for deployable repos and `branch: "main"` for content repos.
404
+
405
+ ## Step 8 — report
406
+
407
+ Repo, default branch, ruleset applied, train branches locked (push allowlist), project attached/created
408
+ (+ info seeded, Status lanes and Labels field verified), secrets set (names only), developer access, plugin
409
+ installed, docs seeded, registry META written, issue templates committed, org App credentials registered,
410
+ fanout registered, and the final `mmi-cli bootstrap verify "$OWNER/$REPO" --class ... --json` result.
411
+
412
+ ## Retro — one check before you finish
413
+ Before your final report, answer one question honestly: did **this skill's own instructions** misfire
414
+ this run — ambiguous wording, a misleading message, or an environment failure it should have warned
415
+ about? (Process only — never the user's code or task; e.g. an ambiguous seed, registry, or OIDC step, or
416
+ a guard that fired on a healthy repo.) If yes, file **one** lesson and move on; a clean run is silent
417
+ (hard cap: one per run). It lands on the Hub board (deduped) and is fixed only via a reviewed PR — never
418
+ edit the skill live; the retro is advisory, so if the call fails, note it and continue:
419
+ `mmi-cli skill-lesson --skill bootstrap --title "<what misfired>" --body "<what; evidence; proposed amendment>"`
@@ -0,0 +1,25 @@
1
+ # Tenant-container reference Dockerfile — seeded at bootstrap (#1593).
2
+ # Adapt package manager and build commands to the project; keep the image building from source.
3
+ FROM node:24-alpine AS deps
4
+ WORKDIR /app
5
+ COPY package.json package-lock.json ./
6
+ # npm workspaces: copy EVERY workspace package.json before `npm ci` so adding a workspace later
7
+ # does not break the image. Add one COPY line per workspace directory (apps/*, packages/*, …).
8
+ # COPY apps/my-app/package.json ./apps/my-app/
9
+ RUN npm ci
10
+
11
+ FROM node:24-alpine AS build
12
+ WORKDIR /app
13
+ COPY --from=deps /app/node_modules ./node_modules
14
+ COPY . .
15
+ RUN npm run build
16
+ RUN npm prune --omit=dev
17
+
18
+ FROM node:24-alpine AS runtime
19
+ WORKDIR /app
20
+ ENV NODE_ENV=production
21
+ COPY --from=build /app/package*.json ./
22
+ COPY --from=build /app/node_modules ./node_modules
23
+ COPY --from=build /app/dist ./dist
24
+ EXPOSE 3000
25
+ CMD ["node", "dist/index.js"]
@@ -0,0 +1,36 @@
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 it is
7
+
8
+ (Describe the product/service.)
9
+
10
+ ## Agent context
11
+
12
+ Read this section at the start of agent work in this repo.
13
+
14
+ - **Stack:** (languages, frameworks, major services)
15
+ - **Run locally:** (install, dev server, `/stage` if non-obvious)
16
+ - **Verify before done:** (exact commands — test, lint, typecheck, repo gate script)
17
+ - **Release track + coords:** `mmi-cli project get` (registry SSOT for release track, stages, board, and deploy coords; not copied here, so it cannot go stale).
18
+ - **Architecture:** deep build/deploy shape → `architecture.md`
19
+ - **Gotchas:** (ports, env from vault not files, Windows/shell quirks specific to this repo)
20
+
21
+ ## Develop
22
+
23
+ (How to run, build, and test locally. How it's *built* lives in `architecture.md`.)
24
+
25
+ ## Access
26
+
27
+ Repo access follows the MMI Future three-level model: read for org members, developer as GitHub `write`,
28
+ and project-admin as `write` plus train-branch allowlist (registry `projectAdmins` on this project).
29
+ What each level can **do** is in `AGENTS.md` § Authority and `docs/org-architecture.md` §4 in `MMI-Hub`;
30
+ agents check `mmi-cli access role <owner/repo> --json` before redirecting anyone to the master. Grant/revoke
31
+ mechanics (master-only) are `docs/Guides/repo-access.md` in `MMI-Hub`.
32
+
33
+ ## Org
34
+
35
+ Part of **Mutatis Mutandis** · class **{{CLASS}}** · board **{{PROJECT_OWNER}} #{{PROJECT_NUMBER}}**.
36
+ Agent rules: `AGENTS.md` (the org spine). Architecture: `architecture.md`.
@@ -0,0 +1,19 @@
1
+ # {{REPO_NAME}} — Architecture
2
+
3
+ > How this repo is **built** — present truth. Pair with `README.md` (what it *is*). State current truth;
4
+ > no change-comments or version-era labels (history is in git). (Write fresh — D35.)
5
+
6
+ ## Overview
7
+
8
+ (System shape: components, data flow, deploy model.)
9
+
10
+ ## Build & deploy
11
+
12
+ - **Class:** {{CLASS}}
13
+ - **Release track + stages:** `mmi-cli project get` (registry SSOT: full = development/rc/main, direct = development/main, trunk = main; never copied here).
14
+ - **Deploys run centrally** via the Hub (`tenant-deploy.yml`); this repo carries no deploy files.
15
+ - (Build/test commands, CI gate, deploy target.)
16
+
17
+ ## Conventions
18
+
19
+ Follows the org spine (`AGENTS.md`). Repo-specific agent guidance: `.cursor/rules/{{REPO_SLUG}}.mdc`.
@@ -0,0 +1,31 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "radix-nova",
4
+ "rsc": true,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "app/globals.css",
9
+ "baseColor": "neutral",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "iconLibrary": "lucide",
14
+ "rtl": false,
15
+ "aliases": {
16
+ "components": "@/components",
17
+ "utils": "@/lib/utils",
18
+ "ui": "@/components/ui",
19
+ "lib": "@/lib",
20
+ "hooks": "@/hooks"
21
+ },
22
+ "menuColor": "default",
23
+ "menuAccent": "subtle",
24
+ "registries": {
25
+ "@mutmutco": "https://ui.mutatismutandis.co/r/{name}.json"
26
+ },
27
+ "mmi": {
28
+ "cacheDir": ".mmi/design-system/components",
29
+ "installed": []
30
+ }
31
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "install": "{{INSTALL_CMD}}"
3
+ }
@@ -0,0 +1,11 @@
1
+ ---
2
+ alwaysApply: true
3
+ ---
4
+
5
+ # {{REPO_NAME}} — agent context
6
+
7
+ Read **`README.md` § Agent context** first (stack, verify commands, gotchas). Deep build truth: `architecture.md`.
8
+
9
+ Org behavior: `AGENTS.md` / `CLAUDE.md` — never edit here. This file is the Cursor always-on pointer + repo-specific notes only.
10
+
11
+ (Saga: plugin hooks call `mmi-cli` automatically; without plugin see `docs/Guides/cursor-saga.md` in MMI-Hub.)
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "compilerOptions": {
4
+ "paths": {
5
+ "@/components/ui/*": ["./.mmi/design-system/components/ui/*"]
6
+ }
7
+ }
8
+ }
@@ -0,0 +1,17 @@
1
+ services:
2
+ app:
3
+ build:
4
+ context: .
5
+ dockerfile: Dockerfile
6
+ restart: unless-stopped
7
+ # The box control document writes registry coords + stage-scoped SSM secret values into the
8
+ # release ./.env before `docker compose up`; env_file is what delivers them into the container.
9
+ env_file: .env
10
+ environment:
11
+ NODE_ENV: production
12
+ MMI_STAGE: ${MMI_STAGE:-dev}
13
+ MMI_PORT: ${MMI_PORT:-3000}
14
+ PORT: ${PORT:-3000}
15
+ MMI_EDGE_DOMAIN: ${MMI_EDGE_DOMAIN:-}
16
+ ports:
17
+ - "${MMI_PORT:-3000}:${PORT:-3000}"
@@ -0,0 +1,42 @@
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
+ # Stack-aware (#1550): the runtime is rendered into the setup-step `if:` — both the Node and Python
7
+ # setup steps live in the file but only the one matching GATE_RUNTIME runs. Knobs (override at
8
+ # bootstrap with --var):
9
+ # GATE_RUNTIME — node | python (selects which setup step fires)
10
+ # GATE_CMD — the check command ({{GATE_CMD}})
11
+ # GATE_INSTALL_CMD — the dependency-install command ({{GATE_INSTALL_CMD}})
12
+ # GATE_WORKDIR — app working directory when not the repo root ({{GATE_WORKDIR}})
13
+ # GATE_CACHE_DEP_PATH— npm lockfile path for the setup-node cache ({{GATE_CACHE_DEP_PATH}})
14
+ # GATE_PY_VERSION — Python version for setup-python ({{GATE_PY_VERSION}})
15
+ on:
16
+ pull_request:
17
+ push:
18
+ branches: {{GATE_PUSH_BRANCHES_YAML}}
19
+ tags: ['v*']
20
+
21
+ env:
22
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
23
+
24
+ permissions:
25
+ contents: read
26
+
27
+ jobs:
28
+ gate:
29
+ if: ${{ github.event_name != 'push' || github.ref_name == '{{GATE_FULL_RUN_BRANCH}}' || github.ref_type == 'tag' }}
30
+ runs-on: [self-hosted, linux, x64, mmi-live]
31
+ defaults:
32
+ run: { working-directory: {{GATE_WORKDIR}} }
33
+ steps:
34
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
35
+ - if: ${{ '{{GATE_RUNTIME}}' == 'node' }}
36
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
37
+ with: { node-version: 24, cache: npm, cache-dependency-path: {{GATE_CACHE_DEP_PATH}} }
38
+ - if: ${{ '{{GATE_RUNTIME}}' == 'python' }}
39
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
40
+ with: { python-version: '{{GATE_PY_VERSION}}' }
41
+ - run: {{GATE_INSTALL_CMD}}
42
+ - run: {{GATE_CMD}}