@mutmutco/claude-plugin 4.5.161 → 4.5.163

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/cli/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.5.161",
3
+ "version": "4.5.163",
4
4
  "description": "MMI Future CLI — the org dev toolbox and shared cross-IDE engine for every registry-declared MMI coding surface.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/claude-plugin",
3
- "version": "4.5.161",
3
+ "version": "4.5.163",
4
4
  "description": "MMI workflow skills and org gates delivery.",
5
5
  "author": {
6
6
  "name": "MMI Future",
@@ -44,17 +44,7 @@ Refs are `[RepoName#N](url) · short title` — no `owner/` prefix, no `[type]`
44
44
  its essence. **Taken** renders id · status · owner only, never the title. Skip empty sections silently.
45
45
  One screen.
46
46
 
47
- > 👋 Welcome back, @dev — here's your board on **MMI-Hub**.
48
- >
49
- > **On your plate** — two train fixes in flight:
50
- > - [MMI-Hub#834](https://github.com/mutmutco/MMI-Hub/issues/834) · automated hotfix apply path
51
- > - [MMI-Hub#841](https://github.com/mutmutco/MMI-Hub/issues/841) · rcand stuck on required checks
52
- >
53
- > **Up for grabs** — nine open, I can take them in three PRs:
54
- > - [MMI-Hub#821](https://github.com/mutmutco/MMI-Hub/issues/821) · redesign tenant env-writer
55
- >
56
- > **Taken**
57
- > - MMI-Hub#827 · In Progress · @otherdev
47
+ For a rendering example, read [examples.md](examples.md).
58
48
 
59
49
  Status moves happen automatically as work flows — claim, PR open, merge. Never offer a status move.
60
50
 
@@ -114,9 +104,7 @@ the per-item results rather than assuming the batch took. **Route is `oracle boa
114
104
  no `oracle issue claim` and no flat `board claim` — both refuse. Guessing either burns a call.
115
105
 
116
106
  Then one worktree per batch, all batches concurrently. The host owns worktree mechanics; MMI owns board
117
- and PR facts. On Windows set `git config core.longpaths true` on the main checkout before the first
118
- `git worktree add` (#5499). Linked worktrees share one `.git`, so serialize `git branch -m` and other ref
119
- renames across them, or retry serially when the error names `.tmp-renamed-log` (#5485).
107
+ and PR facts. For worktree setup on Windows or concurrent ref renames, read [reference.md](reference.md).
120
108
 
121
109
  ## Step 5 — land one PR per batch
122
110
 
@@ -141,12 +129,7 @@ Materialize the body as a real UTF-8 file and pass its path; do not pipe it to `
141
129
  When an issue must stay open, write `Part of #N` or `Refs #N`. **Never** write `Does not close #N` —
142
130
  GitHub closes it anyway (JC#495).
143
131
 
144
- A repo running a guidance-learning gate also needs the body's learning line PLANNED here, not patched in
145
- after a red check: when the batch's touch-set intersects that repo's guidance files (Jerv-JervCode:
146
- `product/soul.md`, `product/runbooks/**`, `packages/*/skills/**/SKILL.md`), the body must carry a
147
- standalone `learning: none — <reason>` line unless a closing learning issue's `promote.globs` names a
148
- changed file. The line must stand alone on its own newline — a literal `\n` inside an inline edit fails
149
- the gate again.
132
+ When the repo runs a guidance-learning gate, read [reference.md](reference.md) before preparing the PR body.
150
133
 
151
134
  Landing requires a merged receipt before any cleanup: `state` `MERGED`, the head ref and OID matching the
152
135
  local task branch, and a clean worktree. Anything else means retain and report, never clean.
@@ -0,0 +1,12 @@
1
+ > 👋 Welcome back, @dev — here's your board on **MMI-Hub**.
2
+ >
3
+ > **On your plate** — two train fixes in flight:
4
+ > - [MMI-Hub#834](https://github.com/mutmutco/MMI-Hub/issues/834) · automated hotfix apply path
5
+ > - [MMI-Hub#841](https://github.com/mutmutco/MMI-Hub/issues/841) · rcand stuck on required checks
6
+ >
7
+ > **Up for grabs** — nine open, I can take them in three PRs:
8
+ > - [MMI-Hub#821](https://github.com/mutmutco/MMI-Hub/issues/821) · redesign tenant env-writer
9
+ >
10
+ > **Taken**
11
+ > - MMI-Hub#827 · In Progress · @otherdev
12
+
@@ -0,0 +1,11 @@
1
+ On Windows set `git config core.longpaths true` on the main checkout before the first
2
+ `git worktree add` (#5499). Linked worktrees share one `.git`, so serialize `git branch -m` and other ref
3
+ renames across them, or retry serially when the error names `.tmp-renamed-log` (#5485).
4
+
5
+ A repo running a guidance-learning gate also needs the body's learning line PLANNED here, not patched in
6
+ after a red check: when the batch's touch-set intersects that repo's guidance files (Jerv-JervCode:
7
+ `product/soul.md`, `product/runbooks/**`, `packages/*/skills/**/SKILL.md`), the body must carry a
8
+ standalone `learning: none — <reason>` line unless a closing learning issue's `promote.globs` names a
9
+ changed file. The line must stand alone on its own newline — a literal `\n` inside an inline edit fails
10
+ the gate again.
11
+
@@ -15,80 +15,7 @@ invoke the skill (the master holds the App key), not the caller's GitHub role.
15
15
  Bootstrap operates on the target repo entirely through the App — **no per-repo checkout**. The repo must
16
16
  already be named on the taxonomy `<CATEGORY>-<PascalName>`.
17
17
 
18
- ## Seed sources + create-vs-upgrade
19
-
20
- The org-standard scaffolding is a **machine-readable manifest** — `skills/bootstrap/seeds/manifest.json`
21
- (loaded by the CLI; `mmi-cli devops bootstrap apply <repo> [--execute]` consumes it). Every seed carries an **ownership**:
22
-
23
- - **`org`** — org-delivered, **overwritten on upgrade** (the org owns it): the issue templates, the gate
24
- workflow, and the org-managed `.gitignore` block. `source: self` = copied verbatim from MMI-Hub's own
25
- current file. Personal **agent guides** (`AGENTS.md`/`CLAUDE.md`/`.claude/settings.json`) are **never**
26
- seeded — they are developer-owned and gitignored, carried per machine by each developer's own plugins
27
- (Jervaise's ride Jerv PowerTools), not delivered, overwritten, or fanned out by MMI; the org push ruleset
28
- `mmi-no-agent-files-org` even blocks committing them. Board moves are central (the Hub webhook), so **no
29
- per-repo board workflow is stamped**.
30
- - **`repo`** — created **once on a fresh bootstrap**, **never clobbered on upgrade** (the repo owns its
31
- content): `README.md`, `architecture.md`.
32
- These render `seeds/*.template.*` with `{{PLACEHOLDERS}}`.
33
-
34
- So **create** stamps every seed; **upgrade** refreshes the `org` seeds and adds any *missing* `repo` seeds
35
- without overwriting existing repo-owned files (D35: legacy docs are archived + written fresh, never carried
36
- over verbatim). The manifest's `labels` list is the canonical label set — three: `bug`, `feature`, `task`.
37
- Priority is a **board field**, never a label (#1454 retired the four `priority:*` labels; `bootstrap verify`
38
- checks they are gone). The per-step instructions below
39
- are the manual path; `bootstrap apply <repo> [--execute]` automates them from this manifest.
40
- For a registry-configured gate, use `mmi-cli devops bootstrap apply <repo> --only gate.yml --from-config`
41
- (dry-run) then add `--execute` to propose the file on a review branch + PR. This uses the same
42
- registry `gate` render as `ci reconcile`. An unstamped legacy gate must first match the registry
43
- render exactly; the first PR then adopts it by adding a hashed render stamp. After adoption, changing
44
- the registry config produces an update PR if the committed stamp still verifies. A stamped gate edited
45
- by hand, or a divergent unstamped gate, is refused with a diff: make the registry config reproduce an
46
- unstamped file first, then re-run to adopt. Edit the registry config, never the stamped file.
47
-
48
- **`bootstrap apply` is a single-repo tool** — fresh bootstrap/onboarding, or refreshing one repo's own
49
- drifted seed. It is **never** the fleet-wide fan-out for an `org`-owned seed edit: that write path is
50
- propagation from a Hub merge (#4233), and the seeded `agent-pr.yml` itself now fails the merge gate on
51
- any repo that receives a hand-edit to an org-owned target path outside a propagation/bootstrap-delivery
52
- branch (#4241). Running `apply --execute` against every registry repo by hand to fan out a Hub edit is
53
- exactly the copy-per-repo pattern this rule exists to end — see Hub#4234.
54
-
55
- **Rollback is per-repo, never a second fleet overwrite (#4240).** When a propagated `org`-owned seed
56
- breaks a repo, the recovery is `mmi-cli devops bootstrap rollback <repo> --target <t> [--execute]` — it resolves
57
- the ONE merge commit that repo's `bootstrap propagate` (#4238) run actually landed (live from the repo's
58
- `seed-propagate-<slug>` merged-PR history, or replayed from a persisted propagate `--json` report via
59
- `--record`) and opens an ordinary revert PR of it, through that repo's own gate, on a `seed-rollback-<slug>`
60
- branch. It refuses — never guesses a commit — when no clean, single-file propagation record resolves for
61
- that repo+target. **Explicitly forbidden as a rollback path: a fleet-wide `bootstrap apply --execute` of
62
- "the old bytes"** — that is the second blind fleet overwrite #4233 rules out, and it races the propagation
63
- lane. **MMI-Hub stays the source of truth during a rollback**: a per-repo revert is the emergency stop, not
64
- the fix — closure is the Hub reverting (or fixing forward) the bad seed commit on `development`, after
65
- which reverted repos match the Hub again and the drift alarm closes itself. A per-repo revert with no
66
- Hub-side follow-up re-alarms as drift within a week, deliberately, so an emergency divergence can never
67
- silently become permanent.
68
-
69
- **Release gap — a new topology value must reach TWO surfaces, and a local dev build only fixes one (#2928).**
70
- A new `--project-type`, `--deploy-model`, or `--release-track` lands on `development` but is not live until a
71
- release train ships it — in **both** of these places:
72
-
73
- 1. **The published/installed `mmi-cli`**, which rejects the unknown enum with no hint that it merely lags
74
- `development`. A local dev build routes around this:
75
- `node <MMI-Hub checkout>/cli/dist/index.cjs bootstrap apply ...` from a `development` checkout (rebuild
76
- `dist` first: `npm --prefix cli ci && npm --prefix cli run build`).
77
- 2. **The deployed registry Lambda**, which carries its *own* copies of the enums
78
- (`infra/src/registry-route.ts` validates `projectType` **and** `deployModel`; `releaseTrack` is CLI-side
79
- only) and **deploys from `main`**. A local dev build does nothing for this one.
80
-
81
- So the local dev build is **not** a workaround for a just-merged enum value — it gets you further and then
82
- fails at the step that matters. The CLI cheerfully accepts the new value, sends it, and the API rejects it:
83
-
84
- ```
85
- ddb register <slug> (failed: HTTP 400 — projectType must be one of:
86
- web-app, hub-service, content, desktop-game, non-deployable, cli-tool, worker)
87
- ```
88
-
89
- That failure lands **after** seeding, labels, and the ruleset, so the repo is left half-provisioned with an
90
- almost-empty registry META row. **Bootstrapping against a topology value merged since the last train requires
91
- a `/release` to `main` first.** The local dev build alone suffices only for values already live in the API.
18
+ Read [reference.md](reference.md) when choosing create vs upgrade, refreshing seeds, rolling back propagation, or using newly released topology values.
92
19
 
93
20
  ## Step 0 — capability shape (confirm the four axes before any mutation)
94
21
 
@@ -154,336 +81,15 @@ acceptable end states (#2928):**
154
81
  section — GitHub's contents API lags for a minute or so right after a merge. **Re-run before believing a
155
82
  content-read failure**; it clears on its own.
156
83
 
157
- ## Step 0b — create the repo (when it does not exist yet)
158
-
159
- If `$OWNER/$REPO` is not on GitHub yet, create it and seed an initial commit **before** Step 1 — a brand-new
160
- repo has no commits, so there is no branch to push and no default branch to set. **Create the first commit
161
- through the contents API, on every track** (#3655) — an API write is not a push, so neither the #1660
162
- protected-branch guard nor `mmi-train-floor` objects, and it is the same server-side mechanism Step 1 uses
163
- for `rc`/`main` (#3433). `$FIRST` is the track's first branch: `development` for **full**/**direct**,
164
- `main` for **trunk**:
165
-
166
- ```bash
167
- FIRST=development # full / direct
168
- FIRST=main # trunk (content)
169
-
170
- # One-time namespace + first-ref creation uses the authenticated master-admin GitHub session. The current
171
- # mmi-cli has no App-backed command for a repository that does not exist; this is the named exception.
172
- gh repo create "$OWNER/$REPO" --private --disable-wiki
173
- gh api -X PUT "repos/$OWNER/$REPO/contents/.gitkeep" \
174
- -f message="chore: initial commit" -f content="Cg==" -f branch="$FIRST"
175
- ```
176
-
177
- `--disable-wiki` seeds `has_wiki=false` at creation: wikis are retired org-wide (compute-at-read, #4206),
178
- GitHub defaults new repos to `has_wiki=true`, and `bootstrap apply --execute` / `bootstrap reconcile --apply`
179
- enforce the same rule as drift checks afterwards.
180
-
181
- On an empty repo that PUT creates `$FIRST` **and leaves it the default branch**, so no `gh repo edit
182
- --default-branch` is needed here. Skip this step when the repo already exists with the track's default branch.
183
-
184
- The two `gh` writes above are authorized by the authenticated master-admin login and are the one-time
185
- bootstrap exception while no App-backed `mmi-cli` command owns a repository that does not yet exist. They
186
- are not a general write lane: after the namespace and first ref exist, use the App-backed
187
- `mmi-cli devops bootstrap apply --execute` path for managed seeds, labels, rulesets, and registry state.
188
-
189
- For a **content** repo the API form is not merely tidier, it is the only thing that works: `main` is that
190
- track's first and only branch, so the initial commit IS a push to `main` and the #1660 guard fences it — and
191
- the deny's own advice ("land through a CI-gated PR to development") is meaningless on a repo with no commits,
192
- no `development`, and no CI (#3541).
193
-
194
- The old recipe here was a local `git init` + empty commit + `git push` for deployable repos, opening with
195
- `gh repo create --private --confirm`. **That flag no longer exists** — `gh` removed it, so the first command
196
- of the first step errored out. The local-git ritual it opened is also unnecessary: the API form above does
197
- the same job for `development` in one call.
198
-
199
- ## Step 1 — branches
200
-
201
- Determine the repo's **release track** first — the branch set follows the track, not just the class (#1097),
202
- so a direct-track repo never gets a stray `rc` the `mmi-train-floor` ruleset can't clean up:
203
-
204
- - **full** (deployable default) — three permanent branches, default `development`: `development`, `rc`, `main`.
205
- - **direct** (deployable, skips rc — e.g. cli-tool/worker/Hub) — two permanent branches, default
206
- `development`: `development`, `main`.
207
- - **trunk** (content) — one permanent branch, default `main`: `main` only.
208
-
209
- Ensure exactly the track's permanent branches exist and set the default branch — create only what the track
210
- uses; never create an `rc` for a direct-track repo.
211
-
212
- **`bootstrap apply --execute` creates `rc`/`main` server-side from the track's first branch (#6070).** The
213
- local **#1660 protected-branch push guard** fences every push to `main`/`master`/`rc`, including the
214
- legitimate *creation* of those refs on a brand-new repo (#3433); a ref created through the API is not a
215
- force-push, so both the guard and the `mmi-train-floor` ruleset allow it. The apply's train-readiness plan
216
- prints one `lane-branch` row per track branch — `CREATE` (missing), `SKIP` (already exists), or `GAP`
217
- (`bootstrap-gap`: the first branch from Step 0b is missing, so there is no source ref) — and the live apply
218
- creates every `CREATE` row; a `422 Reference already exists` reply is the idempotent no-op, not a failure.
219
- `development` (or `main`, for a content repo) already exists from Step 0b. Only the default branch stays a
220
- `gh` write:
221
- ```bash
222
- gh repo edit "$OWNER/$REPO" --default-branch development # full / direct
223
- gh repo edit "$OWNER/$REPO" --default-branch main # trunk (content)
224
- ```
225
- Verify with `gh api "repos/$OWNER/$REPO/branches" --jq '.[].name'`, or read the `lane-branch` rows of the
226
- next dry-run `bootstrap apply`.
227
-
228
- ## Step 2 — authority (org Ruleset)
229
-
230
- Confirm the org-level rulesets already target this repo (they apply org-wide): **`mmi-branch-protection`**
231
- PR-gates `development` and blocks force-push/deletion there (bypass = org admins + the GitHub App 3026732),
232
- and **`mmi-train-floor`** blocks force-push/deletion on `rc`/`main` (no PR rule — the train pushes merges and
233
- tags directly; no bypass). (Definitions mirror live in `.github/rulesets/mmi-branch-protection.json` and
234
- `.github/rulesets/mmi-train-floor.json`.) MMI-Hub additionally has its own repository ruleset,
235
- `.github/rulesets/mmi-hub-required-checks.json`, for the Hub-only `cli`, `infra`, and `docs` jobs; do not
236
- apply those contexts org-wide unless every target repo exposes them.
237
-
238
- ## Step 2b — lock the train branches (who can push)
239
-
240
- The ruleset says *a PR is required*; this says *who may merge it*. `bootstrap apply --execute` applies the
241
- classic branch protection on `development`/`rc`/`main` for deployable repos, or just `main` for content
242
- repos, with **"Restrict who can push"** = the org owners + the App (#6070): the owners are resolved live
243
- from the GitHub org admins (`orgs/$OWNER/members?role=admin`), never a hard-coded user list — an empty
244
- resolution refuses to write an empty allowlist — and the App is `mmi-github-app`. The repo's full-write
245
- people get added at Step 4b. Everyone else is `write`-locked on protected branches: they push feature
246
- branches and open PRs but cannot merge there.
247
-
248
- The plan prints one `push-allowlist` row per track branch — `UPDATE` (preserve every existing
249
- review/status/admin/force-push bit and replace only the push restriction), `SKIP` (exact owners + App
250
- already present), or `GAP` (`bootstrap-gap`: the protection read failed; zero writes are performed while any
251
- control-plane read is unverified). Only the master (sole repo `admin`) can change this afterward. Full
252
- grant/lock mechanics + inspect commands: `docs/Guides/repo-access.md`.
253
-
254
- ## Step 3 — attach to the repo's Project (one board per repo; confirm)
255
-
256
- **One board per repo, named after it** — that is the convention the org actually runs, and what the rest of
257
- the system assumes: the registry META carries a single `projectId` per `PROJECT#<slug>`, and the Hub webhook
258
- adds each new issue to that one board. There are no division boards; a `<CATEGORY>`-prefixed default would
259
- resolve to nothing (#3542). Still **confirm with the master** — repos and projects are genuinely **not 1:1**,
260
- and joining an existing board remains a legitimate answer for a repo that belongs to an existing effort.
261
- ```bash
262
- gh project list --owner "$PROJECT_OWNER" --format json # existing boards to choose from
263
- ```
264
- - **Attach** to the chosen project:
265
- ```bash
266
- gh project link "$PROJECT_NUMBER" --owner "$PROJECT_OWNER" --repo "$OWNER/$REPO"
267
- ```
268
- - **Create** the chosen board if it doesn't exist yet — clone the **named template board**, never an
269
- arbitrary existing one, so the 4-lane `Status` field (`Todo · In Progress · In Review · Done`), the
270
- built-in workflows, **and** the org's view/card-field shape (#4093) all carry over instead of whatever
271
- the previous copy-of-a-copy happened to drift to:
272
- ```bash
273
- gh project copy 4 --source-owner "$PROJECT_OWNER" \
274
- --target-owner "$PROJECT_OWNER" --title "<Project>"
275
- ```
276
- **Project 4 (MMI-Hub's own board) is the template** (#4093) — it already carries the org-standard view
277
- triple (`List`/`Board`/`Roadmap`), Board grouping (columns=Status, swimlanes=Repository), and the
278
- canonical Board card-field set (`Title, Assignees, Status, Labels, Linked pull requests, Parent issue,
279
- Sub-issues progress, Priority`). `bootstrap verify` asserts all three against every board (next note); a
280
- freshly copied project starts green on them instead of inheriting a stale source's drift. Copying from
281
- anything else re-introduces the "5 card-field variants across 16 boards" problem #4093 was filed to end.
282
- Then **interview the master** for the seed short description + README (what it tracks, member repos, links
283
- to the repos' `README.md`/`architecture.md`); set them via the `updateProjectV2` mutation
284
- (`shortDescription`, `readme`). If cloning, verify the built-in workflows survived (next note).
285
- - **Built-in workflows:** `mmi-cli devops bootstrap verify` checks these Project workflows are enabled:
286
- `Auto-add sub-issues to project`, `Auto-archive items`, `Item added to project`, and `Item closed`. The
287
- Todo/In Progress/In Review moves are **central** (the Hub webhook); the built-in `Item closed` sets `Done`
288
- on merge. GitHub's public GraphQL schema exposes delete/read surfaces for Project workflows but no
289
- create/update/enable mutation; if any required workflow is missing or disabled, repair it in the project's
290
- **Workflows** settings before calling the repo ready for developers.
291
- - **Board shape (#4093):** `mmi-cli devops bootstrap verify` also checks the view triple (`List`/`Board`/`Roadmap`
292
- by name **and** layout), the Board view's grouping, and its card fields against the standard the template
293
- board (project 4) carries. Grouping (columns=`Status`, swimlanes=`Repository`) has **no** GraphQL create/
294
- update mutation (`ProjectV2ViewConfigurationInput` carries only `visibleFieldIds`) — a drifted swimlane or
295
- column can only be fixed in the UI: Board view → ⚙ (top-right) → Group by / Swimlanes → Save view. The
296
- view triple and the card fields (`Title, Assignees, Status, Labels, Linked pull requests, Parent issue,
297
- Sub-issues progress, Priority`) ARE API-writable (`createProjectV2View`/`updateProjectV2View`), so a
298
- drifted one can be fixed live instead of only reported.
299
-
300
- Record the chosen `projectNumber`/`projectId` plus Status/Priority field ids in the Hub registry META
301
- (`PROJECT#<slug>`) by re-running `mmi-cli devops bootstrap apply --execute` **after** `gh project link`. Apply reads
302
- the repo's linked board and derives all of them itself (#3543) — pass `--var PROJECT_ID=<node id>` only to
303
- override, which is also what you need when the repo is linked to more than one board and apply therefore
304
- refuses to guess. **Verify the row afterwards** (`mmi-cli oracle org project get <owner/repo>`): a registered repo
305
- whose META carries no `projectId`/`statusFieldId` looks finished everywhere else while the Hub webhook has
306
- nothing to move issues with, and `registry project board META exists` is the single check that says so. Going forward the thin Lambda adds each new issue to that project on `issues.opened` and sets
307
- `Status: Todo`.
308
-
309
- **Register the project in the Hub registry.** The same registry META row carries `{name, slug, projectId,
310
- repos[]}`; do not append to a committed `projects.json`. Repo wikis and doc freshness are owned by
311
- the repo itself. If attaching this repo to an existing project, merge this repo into that project's `repos[]`
312
- instead of creating a new project.
313
-
314
- ## Step 4 — vault tiers + deploy substrate
315
-
316
- Provision the repo's vault namespace and deploy substrate from the Hub, not from repo-local Actions
317
- secrets. Runtime config names live in the two-tier vault (`/mmi-future/<slug>/dev|rc|main/*`) and are
318
- managed through `/secrets`; never use `gh secret set` for product runtime config.
319
-
320
- The default `tenant-container` substrate is a **Hetzner box** (`hetzner-ssh`): the deploy streams the stage's
321
- vault values into a tmpfs runtime env (no `.env` file) and runs the container via docker-compose, deployed over the Hub's bounded
322
- SSH lane. Do **not** create an AWS OIDC deploy role or a repo deploy Action for it. **Ask the master for the
323
- box assignment** — the `sshHost` (and the loopback port) per stage — then write the `DEPLOY#<stage>` rows with
324
- `mmi-cli oracle org project set-deploy <owner/repo> --stage <dev|rc|main> --ssh-host <host> [--port <p>]` (defaults:
325
- `substrate: hetzner-ssh`, deploy path `/opt/mmi/<slug>/<stage>`, service = slug, ssh-user `root`). Without those
326
- rows the tenant cannot deploy (`tenant-deploy.yml` errors on missing `DEPLOY#` coords), so do not skip this.
327
- `bootstrap apply` prints one `deploy-row` row per track stage for container models (#6070): `SKIP` when the
328
- registry row exists, `GAP` (`bootstrap-gap`) with the exact `set-deploy` command otherwise (for
329
- `tenant-container` it carries `--no-env-file true`, matching the fileless compose seed; run it from the tenant
330
- checkout once that compose is on the stage branch) — a box
331
- assignment cannot be invented, so this row is the one train-readiness fact the apply never writes itself.
332
- Keep every runtime config value in the vault; never paste secret values into logs.
333
-
334
- Only an AWS `tenant-container` (the exception) provisions the reusable tenant stack: release bucket,
335
- per-stage OIDC deploy role, and constrained service-control role/document. Either way the deploy path
336
- trusts the Hub's central `tenant-deploy.yml` / `tenant-control.yml` environments, not a product repo deploy
337
- workflow.
338
-
339
- ## Step 4b — developer access
340
-
341
- **Repository Environments (#4458, plan-gating #5032)** — the track's deployment environments are
342
- control-plane state this skill's own `bootstrap verify` asserts, so live apply provisions them itself:
343
- `mmi-cli devops bootstrap apply --execute` creates `dev`/`rc`/`main` (per the release track: direct has no
344
- `rc`, trunk has `main` only) with a **deployment-branch policy = protected branches only** — the one
345
- protection the check exists to carry. On an org plan whose billing entitlement rejects protection rules
346
- (an explicit `wait_timer` or required `reviewers` comes back 422 naming the plan), apply provisions the
347
- settable policy and prints `plan-gated protections` for those rules; `bootstrap verify` then reports the
348
- environment rows as green with an `advisory:` detail naming what the plan cannot set — never a FAIL. The
349
- policy is set either way, so a repo on the lower plan is still protected and ready for developers.
350
-
351
- Grant each developer their **org membership + repo access** through the App (`gh api` with the App token):
352
- `write` for a developer; for a **full-write** member ("project-admin") also add them to the train-branch
353
- push allowlist from Step 2b (so they can merge protected development PRs). GitHub's
354
- collaborator list + the per-branch allowlist are the record — no separate roster. Exact commands:
355
- `docs/Guides/repo-access.md`.
356
-
357
- ## Step 4c — CI trigger, labels, templates
358
-
359
- - **Enable workflow triggers** — a freshly-provisioned repo can have GitHub Actions auto-trigger stuck off
360
- (push/PR never run workflows though dispatch does). Toggle it off→on:
361
- ```bash
362
- gh api -X PUT repos/$OWNER/$REPO/actions/permissions -F enabled=false
363
- gh api -X PUT repos/$OWNER/$REPO/actions/permissions -F enabled=true -f allowed_actions=selected
364
- ```
365
- - **Self-hosted CI is the org default** — the `mmi-automation` runner group is org-wide (all repos), so a
366
- new repo needs no manual group join. Any CI workflow it adds uses
367
- `runs-on: [self-hosted, linux, x64, mmi-live]` (never `ubuntu-*`/`windows-*`/`macos-*`, which bill
368
- GitHub-hosted minutes); jobs needing system packages run in a `container:`. The runner has **six
369
- concurrent job lanes** — independent checks belong in separate parallel jobs (each with its own
370
- `runs-on`), never chained into one serial job or throttled with unneeded `concurrency` groups. See
371
- `docs/Guides/gh-runner-runbook.md`.
372
- - **Product gate + required checks (#1333, stack-aware #1550)** — deployable repos bootstrap-seed
373
- `.github/workflows/gate.yml` (single `gate` job — the simple default for one check command; a repo
374
- that grows several independent suites splits them into parallel jobs across the runner's twelve lanes,
375
- as MMI-Hub's `cli`/`infra`/`docs` gate does) and a ruleset reference at
376
- `.github/rulesets/mmi-product-required-checks.json`. The gate is stack-aware: capture the repo's
377
- **runtime** (`node` | `python`), its **check command**, and its **working directory** at interview time
378
- and pass them as `--var GATE_RUNTIME=node|python`, `--var GATE_CMD=...`, `--var GATE_WORKDIR=...` (and
379
- `--var GATE_CACHE_DEP_PATH=<path/to/package-lock.json>` when the app's Node lockfile is not at the repo
380
- root). Defaults: `node` runtime, `npm run check` / `npm ci` at the repo root; a Python repo defaults to
381
- `pytest` / `pip install -e ".[dev]"` with `--var GATE_PY_VERSION=` (3.11 default). The runtime selects
382
- which setup step (`setup-node` vs `setup-python`) the rendered `if:` fires. The check command runs
383
- under the org **wall-clock budget** (#3178): the render pins `run-with-budget` to the CLI's blessed SHA
384
- with `--var GATE_MAX_SECONDS=` (300s onboarding default — tighten once the gate is measured, via
385
- `--var` or `org project set <repo> --var gate={"maxSeconds":N}`); `ci audit` and the release train
386
- both enforce the step, so do not remove it. The tenant-container product image build (#7542)
387
- shares that ceiling — no separate knob. Every trigger runs the full `GATE_CMD`. A repo whose checks
388
- need a service container or an extra budgeted step declares them in its registry gate config, never by
389
- hand-editing the rendered gate (#7407): `gate.services` is keyed by service name with `image`, `env`,
390
- `ports`, one-line `options` and `tmpfs` paths; `gate.steps` is a list of `{name, command, env,
391
- maxSeconds}` run after the main check under the same budget wrapper. Invalid declarations fail the
392
- render by field name; a repo that declares neither renders exactly as before. `bootstrap apply --execute` installs that JSON as the
393
- repository ruleset itself (#6070) and reports one `product-ruleset` row: `PARK` (the gate file is not on
394
- the base branch yet — installed disabled), `WAIT` (the gate exists but has not emitted a proven-green
395
- `gate` context — kept disabled; re-run apply after the gate passes), `ACTIVATE` (gate green — enforcement
396
- active), or `SKIP` (already exact). The activation boundary is fail-closed on purpose: a required context
397
- that cannot pass yet would lock the seed PR behind itself. `mmi-cli devops ci reconcile --apply --repo
398
- $OWNER/$REPO` remains the supported fallback; confirm `product required-check ruleset enforcement active`
399
- with `bootstrap verify` before reporting bootstrap complete. MMI-Hub keeps its own three-job gate
400
- (`cli`/`infra`/`docs`) — never apply the product ruleset there.
401
- - **The committed ruleset owns its up-to-date policy.** New repositories default to
402
- `strict_required_status_checks_policy: false`. Bootstrap and CI reconciliation preserve that policy
403
- when updating contexts, and converge the live ruleset to the committed setting. Repositories may
404
- explicitly require an up-to-date head. `pr merge` / `pr land` still update a BEHIND head from the base
405
- with a merge commit and re-wait once — see `docs/Guides/train-troubleshooting.md#head-behind-base`.
406
- - **A brand-new repo cannot pass the gate you just installed — its first commit must carry a real project
407
- (#2928).** The seeded `gate.yml` runs `GATE_INSTALL_CMD` + `GATE_CMD` (`npm ci` + `npm run check` by
408
- default) **unconditionally**. An empty repo has no `package.json`, so the gate **fails on the seed PR
409
- itself** — and since the seed PR is what *installs* the gate, this is the normal first-bootstrap path, not
410
- an edge case. The repo cannot go green and `bootstrap verify` cannot pass until the first commit contains a
411
- real project satisfying `GATE_CMD`: a `package.json` with a `check` script (or the Python equivalent), plus
412
- its lockfile. Landing that minimal project is **part of finishing the bootstrap**, not follow-up work —
413
- otherwise the gate is unpassable by construction.
414
- **The `check` must be able to fail.** It has to actually compile or lint the code being committed — a
415
- script that exits 0 without looking at anything does **not** satisfy `GATE_CMD` and must never be committed
416
- to force a green gate. The goal is a gate that catches a broken change, not a green tick wired to nothing.
417
- A typecheck plus a lint is enough to start. **Do not seed a test suite to satisfy `GATE_CMD`** — tests are
418
- opt-in org-wide (#3562); a new repo seeds tests only for paths its own `test-policy.json` marks mandatory,
419
- and an empty `mandatory` array is the normal answer.
420
- - **Standard labels** — type labels only (the issue templates reference them). **Priority is a Project
421
- field, not a label** (#416): never seed `priority:*` labels; `--priority` writes the board field.
422
- ```bash
423
- gh label create bug --color d73a4a --description "Something is broken or behaving wrong" -R $OWNER/$REPO
424
- gh label create feature --color a2eeef --description "New capability or enhancement" -R $OWNER/$REPO
425
- gh label create task --color 0052cc --description "Task, chore, or improvement" -R $OWNER/$REPO
426
- ```
427
- - **Board view (card fields, #4093)** — the Project's Board view must show the org-standard card set:
428
- `Title, Assignees, Status, Labels, Linked pull requests, Parent issue, Sub-issues progress, Priority`.
429
- Cloning from the template board (project 4, Step 3) already produces this; `bootstrap verify` asserts it
430
- (`Board view card fields match the org standard`) on every subsequent run, so drift shows up there rather
431
- than only at bootstrap time. Fix via Board view → **Fields**, or `updateProjectV2View(configuration:
432
- {visibleFieldIds:[...]})`. Strip any legacy `priority:*` / taxonomy labels with `mmi-cli oracle board doctor --fix`.
433
- - **Org App credentials** — nothing to register per repo (#494). Board moves are central (the Hub webhook
434
- moves Todo/In Progress/In Review for every repo) and the org App token is minted inside the Hub's own
435
- central workflows, so `MMI_APP_ID` / `MMI_APP_PRIVATE_KEY` live only on the Hub — a product repo seeds no
436
- App var/secret.
437
- - **Issue templates** — seed `.github/ISSUE_TEMPLATE/` (Bug · Feature · Task + `config.yml`).
438
- - **Merge settings (org canon)** — every repo: auto-merge on, squash on, automatic merged-branch deletion
439
- off. A merged remote branch survives until delayed cleanup proves no open PR still uses it as a base
440
- (#5789); deleting it at merge can irreversibly close stacked PRs:
441
- `gh api -X PATCH repos/$OWNER/$REPO -f allow_auto_merge=true -f allow_squash_merge=true -F delete_branch_on_merge=false`
442
- - **Cursor environment** — `.cursor/environment.json` is retired org-wide (#2501): it caused Cursor to
443
- auto-spawn cloud agents nobody asked for. Bootstrap no longer seeds it and no
444
- repo should carry a tracked copy.
445
-
446
- ## Step 5 — install the plugin + seed docs
447
-
448
- - Bootstrap does **not** seed `.claude/settings.json` or any agent guide (hub-v3 WS4). The developer installs
449
- the org plugins per machine: `mmi@mutmutco` (the plugin carries the org tooling + skills) and
450
- `superpowers@claude-plugins-official` (Anthropic's skills framework — TDD, debugging, subagent dev).
451
- Personal agent guides (`AGENTS.md`/`CLAUDE.md`) are developer-owned and gitignored — MMI never delivers,
452
- overwrites, or deletes them; Jervaise's guide rides Jerv PowerTools.
453
- - Seed `README.md` + `architecture.md` from the templates, then **fill them before finishing** — `bootstrap
454
- verify` now fails on any leftover `(placeholder)` or `{{TOKEN}}` (#1520). Fill the code-local fields from
455
- what this bootstrap already knows: **Stack / Run locally / Verify** from the Step-4c gate + install commands
456
- and the repo's actual code; **Gotchas** and the architecture **Overview / Build & deploy** from the confirmed
457
- Step-0 axes (class, project-type, deploy-model). Do **not** write the release track, board number, or deploy
458
- coords as a value — the templates point at `mmi-cli oracle org project get` (registry SSOT, never copied, so it cannot drift).
459
- Do not write `AGENTS.md` / `CLAUDE.md` — these are developer-owned, gitignored agent guides, never a bootstrapped repo file (the `mmi-no-agent-files-org` ruleset blocks committing them).
460
- - **`docs/index.md` is an optional generated routing index (#3545).** Apply may create `docs/index.md` once for link routing; it never rewrites it. Once the repo has docs of its own,
461
- `mmi-cli oracle docs index --write` owns the routing artifact and `--check` gates drift — it is not product
462
- current-state SSOT. Decision records under `docs/decisions/` remain append-only *why*.
463
- - `.claude/settings.local.json` is local-only and gitignored; bootstrap seeds no committed `.claude/settings.json`.
464
- - **No agent guide is committed — none, anywhere (#2921).** `mmi-no-agent-files-org` is active with no bypass
465
- and restricts `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.claude/**`, `.codex/**`, `.agents/**` **and
466
- `.cursor/rules/**`**. Bootstrap used to seed `.cursor/rules/<repo-slug>.mdc`, which the wall refuses with a
467
- 409 — it wrote a file its own ruleset bans, then verified the file was there. That seed, its template, and
468
- the verify check are gone. Repo-specific agent guidance belongs in **`README.md` § Agent context** (stack,
469
- verify commands, gotchas) with the deep build truth in `architecture.md`; agents read those. Personal agent
470
- guides stay developer-owned and gitignored, carried per machine by each developer's own plugins.
84
+ For a new repo, board selection/creation, access, gate setup, or manual provisioning, read [reference.md](reference.md) Steps 0b–5 before applying; upgrades can use the apply path below.
471
85
 
472
86
  ## Step 6 — register Hub META
473
87
 
474
88
  Do not seed a product repo control-plane config file. `mmi-cli` carries the Hub API endpoint and resolves board,
475
89
  deploy, secret, and project state from the Hub registry at runtime.
476
90
 
477
- Choose the v2 shape explicitly. Use `--project-type web-app --deploy-model tenant-container` for ordinary
478
- web tenants, `--project-type desktop-game --deploy-model none --clear-web-profile` for a desktop game,
479
- `--project-type desktop-app --deploy-model none --clear-web-profile` for a packaged desktop application
480
- (swap in `--deploy-model registry-publish` when it also publishes a package alongside its installer),
481
- `--project-type mobile-app --deploy-model none --clear-web-profile` for a phone app distributed through the
482
- app stores, and
483
- `--class content --project-type content --deploy-model content --clear-web-profile` for a content/KB repo.
484
- An **MM-Work module** adds `--var SUITE=mm-work` (#7944): it registers `suite: mm-work` and derives its
485
- own `dev.<sub>.mutmut.work` + `<sub>.mutmut.work` hosts, with no rc host (the suite serves its rc from the
486
- live host). Without it, apply derives the `*.mutatismutandis.co` web-app hosts, which a module never uses.
91
+ For project-type/deploy-model flag combinations or an MM-Work module, read [reference.md](reference.md) before choosing the apply flags.
92
+
487
93
  Run the apply path with the board variables discovered above, or register the same values with
488
94
  `mmi-cli oracle org project set` from the Hub or from the target project checkout:
489
95
 
@@ -507,26 +113,7 @@ the bootstrap seed Dockerfile and compose templates). The seeded compose is file
507
113
  `labels: { mmi.runtime-env: "true" }`, no `env_file`, and a loopback-only port; the deploy injects the stage's
508
114
  vault values at runtime (`DEPLOY#<stage>.noEnvFile: true`) and the app reads plain env vars — it must **not** self-load SSM and must **not** ship a committed `.env`.
509
115
 
510
- For a `suite: mm-work` tenant-container module, bootstrap apply selects the MM-Work module seed instead: a live `/health` server and `/module.js`, the example module scaffold, secret-backed image build, and `gate` plus `screens / screens`. It sets `requiredBuildSecrets: ["NODE_AUTH_TOKEN=@github-packages-token"]` and requires only `gate`. Before the first CI/deploy, an org admin must grant the repo Read on both `@mutmutco/mmwork-ds` and `@mutmutco/mmwork-screens` under Package settings → Manage Actions access (no GitHub REST API; verify names the manual step). Add `screens / screens` to `requiredChecks` only after the screens job is green, or every PR blocks.
511
-
512
- **Never provision a Google OAuth client for an MM-Work suite module, even if old META declares `oauth`.** Modules sign in through the MM-Work pass and reach Google via `ctx.act`; read the package's GUIDE. The suite seed omits the Google-login guide.
513
-
514
- For a non-suite `web-app` that declares `oauth` META, print the canonical OAuth surface and provision the client once:
515
-
516
- ```bash
517
- mmi-cli vault org oauth plan --repo "$OWNER/$REPO" # the exact JS origins + redirect URIs + canonical SSM keys
518
- ```
519
-
520
- Register those JS origins + `/api/auth/callback` redirect URIs on the Console client (master, per
521
- `docs/Guides/oauth-provision.md`), then store the creds in the canonical keys in one step:
522
-
523
- ```bash
524
- mmi-cli vault org oauth set-creds --repo "$OWNER/$REPO" < client.json # the Console "Download JSON" file
525
- ```
526
-
527
- The keys are the one stageless pair `GOOGLE_CLIENT_ID` + `GOOGLE_CLIENT_SECRET` at the slug root — every
528
- stage reads it (#2244/#2528). Never a staged `{dev,rc,main}/GOOGLE_*`, `GOOGLE_OAUTH_CLIENT_*`, or `prod/`
529
- variant; the runtime reads only the canonical names and declare-first rejects the rest.
116
+ For an MM-Work module or non-suite web-app with OAuth META, read [reference.md](reference.md) before the first CI/deploy or OAuth provisioning.
530
117
 
531
118
  Local `/stage` is optional product-owned configuration. A repo that wants `/stage` may carry a local
532
119
  `stage` block, but that file must not contain board, deploy, or secret registry facts.