@mutmutco/kilo-plugin 3.79.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 (40) hide show
  1. package/agent/reviewer.md +108 -0
  2. package/package.json +23 -0
  3. package/scripts/command-ladder-core.mjs +334 -0
  4. package/scripts/command-ladder-gate.mjs +126 -0
  5. package/scripts/deny-gate-crash.mjs +179 -0
  6. package/scripts/edit-tool-paths.mjs +113 -0
  7. package/scripts/env-write-lint.mjs +137 -0
  8. package/scripts/hook-io.mjs +17 -0
  9. package/scripts/hook-policy.mjs +73 -0
  10. package/scripts/hook-run.mjs +170 -0
  11. package/scripts/hook-trace.mjs +108 -0
  12. package/scripts/pretooluse-shell-gates.mjs +420 -0
  13. package/scripts/secret-echo-lint.mjs +170 -0
  14. package/scripts/secret-redact.mjs +537 -0
  15. package/scripts/throttle-core.mjs +324 -0
  16. package/scripts/validate-hook.mjs +156 -0
  17. package/scripts/vault-edit-gate.mjs +94 -0
  18. package/server.mjs +237 -0
  19. package/skills/bootstrap/SKILL.md +493 -0
  20. package/skills/bootstrap/seeds/Dockerfile.template +30 -0
  21. package/skills/bootstrap/seeds/README.template.md +36 -0
  22. package/skills/bootstrap/seeds/architecture.template.md +34 -0
  23. package/skills/bootstrap/seeds/decisions-readme.template.md +46 -0
  24. package/skills/bootstrap/seeds/docker-compose.template.yml +26 -0
  25. package/skills/bootstrap/seeds/gate.template.yml +90 -0
  26. package/skills/bootstrap/seeds/google-login.template.md +33 -0
  27. package/skills/bootstrap/seeds/manifest.json +26 -0
  28. package/skills/bootstrap/seeds/mmi-product-required-checks.template.json +23 -0
  29. package/skills/browser-automation/SKILL.md +93 -0
  30. package/skills/doctor/SKILL.md +76 -0
  31. package/skills/epic/SKILL.md +87 -0
  32. package/skills/hotfix/SKILL.md +113 -0
  33. package/skills/mmi/SKILL.md +400 -0
  34. package/skills/onboard/SKILL.md +70 -0
  35. package/skills/rcand/SKILL.md +194 -0
  36. package/skills/release/SKILL.md +546 -0
  37. package/skills/resume/SKILL.md +68 -0
  38. package/skills/secrets/SKILL.md +157 -0
  39. package/skills/stage/SKILL.md +151 -0
  40. package/skills/worktree/SKILL.md +86 -0
@@ -0,0 +1,493 @@
1
+ ---
2
+ name: bootstrap
3
+ description: Provision a repo into the org with board, registry, rules, and plugin setup.
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 <repo> [--execute]` consumes it). Every seed carries an **ownership**:
20
+
21
+ - **`org`** — org-delivered, **overwritten on upgrade** (the org owns it): the issue templates, the gate
22
+ workflow, and the org-managed `.gitignore` block. `source: self` = copied verbatim from MMI-Hub's own
23
+ current file. Personal **agent guides** (`AGENTS.md`/`CLAUDE.md`/`.claude/settings.json`) are **never**
24
+ seeded — they are developer-owned and gitignored, carried per machine by each developer's own plugins
25
+ (Jervaise's ride Jerv PowerTools), not delivered, overwritten, or fanned out by MMI; the org push ruleset
26
+ `mmi-no-agent-files-org` even blocks committing them. Board moves are central (the Hub webhook), so **no
27
+ per-repo board workflow is stamped**.
28
+ - **`repo`** — created **once on a fresh bootstrap**, **never clobbered on upgrade** (the repo owns its
29
+ content): `README.md`, `architecture.md`.
30
+ These render `seeds/*.template.*` with `{{PLACEHOLDERS}}`.
31
+
32
+ So **create** stamps every seed; **upgrade** refreshes the `org` seeds and adds any *missing* `repo` seeds
33
+ without overwriting existing repo-owned files (D35: legacy docs are archived + written fresh, never carried
34
+ over verbatim). The manifest's `labels` list is the canonical label set — three: `bug`, `feature`, `task`.
35
+ Priority is a **board field**, never a label (#1454 retired the four `priority:*` labels; `bootstrap verify`
36
+ checks they are gone). The per-step instructions below
37
+ are the manual path; `bootstrap apply <repo> [--execute]` automates them from this manifest.
38
+
39
+ **Release gap — a new topology value must reach TWO surfaces, and a local dev build only fixes one (#2928).**
40
+ A new `--project-type`, `--deploy-model`, or `--release-track` lands on `development` but is not live until a
41
+ release train ships it — in **both** of these places:
42
+
43
+ 1. **The published/installed `mmi-cli`**, which rejects the unknown enum with no hint that it merely lags
44
+ `development`. A local dev build routes around this:
45
+ `node <MMI-Hub checkout>/cli/dist/index.cjs bootstrap apply ...` from a `development` checkout (rebuild
46
+ `dist` first: `npm --prefix cli ci && npm --prefix cli run build`).
47
+ 2. **The deployed registry Lambda**, which carries its *own* copies of the enums
48
+ (`infra/src/registry-route.ts` validates `projectType` **and** `deployModel`; `releaseTrack` is CLI-side
49
+ only) and **deploys from `main`**. A local dev build does nothing for this one.
50
+
51
+ So the local dev build is **not** a workaround for a just-merged enum value — it gets you further and then
52
+ fails at the step that matters. The CLI cheerfully accepts the new value, sends it, and the API rejects it:
53
+
54
+ ```
55
+ ddb register <slug> (failed: HTTP 400 — projectType must be one of:
56
+ web-app, hub-service, content, desktop-game, non-deployable, cli-tool, worker)
57
+ ```
58
+
59
+ That failure lands **after** seeding, labels, and the ruleset, so the repo is left half-provisioned with an
60
+ almost-empty registry META row. **Bootstrapping against a topology value merged since the last train requires
61
+ a `/release` to `main` first.** The local dev build alone suffices only for values already live in the API.
62
+
63
+ ## Step 0 — capability shape (confirm the four axes before any mutation)
64
+
65
+ Bootstrap is **destructive to undo**: a wrong shape means converting in place later — deleting protected
66
+ branches, temporarily disabling the org-wide `mmi-train-floor` ruleset (which affects every repo for that
67
+ window), and tearing down train artifacts (#1450). So **before Step 0a touches anything**,
68
+ surface and **confirm all four topology axes with the master** — present the recommended default for each,
69
+ never apply it silently. Use the structured-question UI; one question per axis (or one grouped confirm).
70
+
71
+ - **`class`** — `deployable` (ships an app/service; full or direct train) · `content` (docs/content;
72
+ trunk, `main`-only). *Controls the branch model.* Recommend from what the repo **is**, not a fixed default.
73
+ - **`project-type`** — e.g. `web-app`, `cli-tool`, `worker`, `desktop-app`, `desktop-game`, `mobile-app`, `content`. *Controls which Hub
74
+ services attach.*
75
+ - **`deploy-model`** — e.g. `tenant-container`, `none`, `content`. *Controls the deploy path.*
76
+ - **`release-track`** — `full` (development·rc·main) · `direct` (development·main, skips rc) · `trunk`
77
+ (`main` only). *The branch set follows the track, not just the class (#1097).*
78
+
79
+
80
+ For a **deployable** repo, also capture the **gate runtime** (`node` | `python`), the **check command**, and
81
+ the **working directory** — these feed the product gate (`--var GATE_RUNTIME=`, `--var GATE_CMD=`,
82
+ `--var GATE_WORKDIR=`, and `--var GATE_CACHE_DEP_PATH=` for a non-root Node lockfile; see Step 5 / the
83
+ "Product gate + required checks" note below). Default to `node` at the repo root; recommend `python` when the
84
+ repo's app is Python.
85
+
86
+ A **content/trunk** choice provisions: `main`-only (no `rc`), **no** `.github/workflows/gate.yml` and **no**
87
+ product required-check ruleset, and a `projects.json` entry with `branch: main` (which marks it a content-class
88
+ repo). A clean content repo then verifies green — `bootstrap verify --class content` no longer reports
89
+ the deployable gate checks as FAIL (#1450).
90
+
91
+ Record the confirmed axes; Step 0a runs `verify --class <confirmed>`, Step 0b creates the repo on the
92
+ track's default branch, and the apply flags (`--project-type` / `--deploy-model` / `--release-track`) carry
93
+ the confirmed values — no silent defaulting.
94
+
95
+ ## Step 0a — no-mutation verifier
96
+
97
+ Before mutating anything, run the verifier so the current gaps are concrete:
98
+ ```bash
99
+ mmi-cli bootstrap verify "$OWNER/$REPO" --class deployable --json
100
+ # or for content repos:
101
+ mmi-cli bootstrap verify "$OWNER/$REPO" --class content --json
102
+ ```
103
+
104
+ Run it again after Step 7. A repo is not ready for real developers until every check is green, or the report
105
+ names an explicitly manual-only item that the master has accepted for that repo.
106
+
107
+ **Two verify FAILs are un-run manual steps or API lag, not code bugs — diagnose them as such, but never as
108
+ acceptable end states (#2928):**
109
+ - `branch protection exists` / `push allowlist configured` — **`bootstrap apply` does not apply branch
110
+ protection.** That is **Step 2b**, and it is manual, so these FAIL until you run it. **Step 2b is
111
+ mandatory**: these are security controls, and they must go green (or be an explicitly master-accepted
112
+ manual item per the rule above) before the bootstrap is complete. A red branch-protection or
113
+ push-allowlist check is an **unfinished repo**, never a known-benign FAIL to sign off around.
114
+ - `README has Agent context section — README.md not readable via API` on a README that demonstrably has the
115
+ section — GitHub's contents API lags for a minute or so right after a merge. **Re-run before believing a
116
+ content-read failure**; it clears on its own.
117
+
118
+ ## Step 0b — create the repo (when it does not exist yet)
119
+
120
+ If `$OWNER/$REPO` is not on GitHub yet, create it and seed an initial commit **before** Step 1 — a brand-new
121
+ repo has no commits, so there is no branch to push and no default branch to set. **Create the first commit
122
+ through the contents API, on every track** (#3655) — an API write is not a push, so neither the #1660
123
+ protected-branch guard nor `mmi-train-floor` objects, and it is the same server-side mechanism Step 1 uses
124
+ for `rc`/`main` (#3433). `$FIRST` is the track's first branch: `development` for **full**/**direct**,
125
+ `main` for **trunk**:
126
+
127
+ ```bash
128
+ FIRST=development # full / direct
129
+ FIRST=main # trunk (content)
130
+
131
+ gh repo create "$OWNER/$REPO" --private
132
+ gh api -X PUT "repos/$OWNER/$REPO/contents/.gitkeep" \
133
+ -f message="chore: initial commit" -f content="Cg==" -f branch="$FIRST"
134
+ ```
135
+
136
+ On an empty repo that PUT creates `$FIRST` **and leaves it the default branch**, so no `gh repo edit
137
+ --default-branch` is needed here. Skip this step when the repo already exists with the track's default branch.
138
+
139
+ For a **content** repo the API form is not merely tidier, it is the only thing that works: `main` is that
140
+ track's first and only branch, so the initial commit IS a push to `main` and the #1660 guard fences it — and
141
+ the deny's own advice ("land through a CI-gated PR to development") is meaningless on a repo with no commits,
142
+ no `development`, and no CI (#3541).
143
+
144
+ The old recipe here was a local `git init` + empty commit + `git push` for deployable repos, opening with
145
+ `gh repo create --private --confirm`. **That flag no longer exists** — `gh` removed it, so the first command
146
+ of the first step errored out. The local-git ritual it opened is also unnecessary: the API form above does
147
+ the same job for `development` in one call.
148
+
149
+ ## Step 1 — branches
150
+
151
+ Determine the repo's **release track** first — the branch set follows the track, not just the class (#1097),
152
+ so a direct-track repo never gets a stray `rc` the `mmi-train-floor` ruleset can't clean up:
153
+
154
+ - **full** (deployable default) — three permanent branches, default `development`: `development`, `rc`, `main`.
155
+ - **direct** (deployable, skips rc — e.g. cli-tool/worker/Hub) — two permanent branches, default
156
+ `development`: `development`, `main`.
157
+ - **trunk** (content) — one permanent branch, default `main`: `main` only.
158
+
159
+ Ensure exactly the track's permanent branches exist and set the default branch — create only what the track
160
+ uses; never create an `rc` for a direct-track repo.
161
+
162
+ **Create `rc`/`main` server-side, not with `git push` (#3433).** The local **#1660 protected-branch push
163
+ guard** fences every push to `main`/`master`/`rc`, including the legitimate *creation* of those refs on a
164
+ brand-new repo — so a `git push origin development rc main` cannot be run as written. Creating the refs
165
+ through the API is not a force-push, so both the #1660 guard and the `mmi-train-floor` ruleset allow it, and
166
+ it behaves the same on every machine. `development` (or `main`, for a content repo) already exists from
167
+ Step 0b:
168
+ ```bash
169
+ sha=$(gh api "repos/$OWNER/$REPO/git/ref/heads/development" --jq '.object.sha')
170
+
171
+ # full — development (Step 0b) + rc + main
172
+ gh api "repos/$OWNER/$REPO/git/refs" -f ref=refs/heads/rc -f sha="$sha"
173
+ gh api "repos/$OWNER/$REPO/git/refs" -f ref=refs/heads/main -f sha="$sha"
174
+ gh repo edit "$OWNER/$REPO" --default-branch development
175
+
176
+ # direct — development (Step 0b) + main, NO rc
177
+ gh api "repos/$OWNER/$REPO/git/refs" -f ref=refs/heads/main -f sha="$sha"
178
+ gh repo edit "$OWNER/$REPO" --default-branch development
179
+
180
+ # trunk (content) — main only; Step 0b already created it with `-b main`
181
+ gh repo edit "$OWNER/$REPO" --default-branch main
182
+ ```
183
+ Re-running a create on a ref that already exists returns `422 Reference already exists` — that is the
184
+ idempotent no-op, not a failure. Verify with `gh api "repos/$OWNER/$REPO/branches" --jq '.[].name'`.
185
+
186
+ ## Step 2 — authority (org Ruleset)
187
+
188
+ Confirm the org-level rulesets already target this repo (they apply org-wide): **`mmi-branch-protection`**
189
+ PR-gates `development` and blocks force-push/deletion there (bypass = org admins + the GitHub App 3026732),
190
+ and **`mmi-train-floor`** blocks force-push/deletion on `rc`/`main` (no PR rule — the train pushes merges and
191
+ tags directly; no bypass). (Definitions mirror live in `.github/rulesets/mmi-branch-protection.json` and
192
+ `.github/rulesets/mmi-train-floor.json`.) MMI-Hub additionally has its own repository ruleset,
193
+ `.github/rulesets/mmi-hub-required-checks.json`, for the Hub-only `cli`, `infra`, and `docs` jobs; do not
194
+ apply those contexts org-wide unless every target repo exposes them.
195
+
196
+ ## Step 2b — lock the train branches (who can push)
197
+
198
+ The ruleset says *a PR is required*; this says *who may merge it*. Apply classic branch protection on
199
+ `development`/`rc`/`main` for deployable repos, or just `main` for content repos, with **"Restrict who can
200
+ push"** = the master + the App (the repo's full-write people get added at Step 4b). Everyone else is
201
+ `write`-locked on protected branches: they push feature branches and open PRs but cannot merge there. Run per
202
+ protected branch (App token):
203
+
204
+ ```bash
205
+ BRANCHES="development rc main" # full
206
+ BRANCHES="development main" # direct (no rc)
207
+ BRANCHES="main" # trunk / content
208
+
209
+ mapfile -t MASTER_USERS < <(gh api --paginate "orgs/$OWNER/members?role=admin" --jq '.[].login')
210
+ if [ "${#MASTER_USERS[@]}" -eq 0 ]; then
211
+ echo "No org owners resolved for $OWNER; stop before writing branch protection." >&2
212
+ exit 1
213
+ fi
214
+ 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));")
215
+
216
+ for b in $BRANCHES; do
217
+ node - "$USERS_JSON" <<'NODE' | gh api --method PUT repos/$OWNER/$REPO/branches/$b/protection --input -
218
+ const users = JSON.parse(process.argv[2]);
219
+ process.stdout.write(JSON.stringify({
220
+ required_status_checks: null,
221
+ enforce_admins: false,
222
+ required_pull_request_reviews: null,
223
+ restrictions: { users, teams: [], apps: ['mmi-github-app'] },
224
+ }, null, 2));
225
+ NODE
226
+ done
227
+ ```
228
+
229
+ Only the master (sole repo `admin`) can change this afterward. Full grant/lock mechanics + inspect commands:
230
+ `docs/Guides/repo-access.md`.
231
+
232
+ ## Step 3 — attach to the repo's Project (one board per repo; confirm)
233
+
234
+ **One board per repo, named after it** — that is the convention the org actually runs, and what the rest of
235
+ the system assumes: the registry META carries a single `projectId` per `PROJECT#<slug>`, and the Hub webhook
236
+ adds each new issue to that one board. There are no division boards; a `<CATEGORY>`-prefixed default would
237
+ resolve to nothing (#3542). Still **confirm with the master** — repos and projects are genuinely **not 1:1**,
238
+ and joining an existing board remains a legitimate answer for a repo that belongs to an existing effort.
239
+ ```bash
240
+ gh project list --owner "$PROJECT_OWNER" --format json # existing boards to choose from
241
+ ```
242
+ - **Attach** to the chosen project:
243
+ ```bash
244
+ gh project link "$PROJECT_NUMBER" --owner "$PROJECT_OWNER" --repo "$OWNER/$REPO"
245
+ ```
246
+ - **Create** the chosen board if it doesn't exist yet — clone an existing board so the 4-lane `Status` field
247
+ (`Todo · In Progress · In Review · Done`) **and its built-in workflows carry over**:
248
+ ```bash
249
+ gh project copy <existing-project#> --source-owner "$PROJECT_OWNER" \
250
+ --target-owner "$PROJECT_OWNER" --title "<Project>"
251
+ ```
252
+ Then **interview the master** for the seed short description + README (what it tracks, member repos, links
253
+ to the repos' `README.md`/`architecture.md`); set them via the `updateProjectV2` mutation
254
+ (`shortDescription`, `readme`). If cloning, verify the built-in workflows survived (next note).
255
+ - **Built-in workflows:** `mmi-cli bootstrap verify` checks these Project workflows are enabled:
256
+ `Auto-add sub-issues to project`, `Auto-archive items`, `Item added to project`, and `Item closed`. The
257
+ Todo/In Progress/In Review moves are **central** (the Hub webhook); the built-in `Item closed` sets `Done`
258
+ on merge. GitHub's public GraphQL schema exposes delete/read surfaces for Project workflows but no
259
+ create/update/enable mutation; if any required workflow is missing or disabled, repair it in the project's
260
+ **Workflows** settings before calling the repo ready for developers.
261
+
262
+ Record the chosen `projectNumber`/`projectId` plus Status/Priority field ids in the Hub registry META
263
+ (`PROJECT#<slug>`) by re-running `mmi-cli bootstrap apply --execute` **after** `gh project link`. Apply reads
264
+ the repo's linked board and derives all of them itself (#3543) — pass `--var PROJECT_ID=<node id>` only to
265
+ override, which is also what you need when the repo is linked to more than one board and apply therefore
266
+ refuses to guess. **Verify the row afterwards** (`mmi-cli org project get <owner/repo>`): a registered repo
267
+ whose META carries no `projectId`/`statusFieldId` looks finished everywhere else while the Hub webhook has
268
+ 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
269
+ `Status: Todo`.
270
+
271
+ **Register the project in the Hub registry.** The same registry META row carries `{name, slug, projectId,
272
+ repos[]}`; do not append to a committed `projects.json`. Repo wikis and doc freshness are owned by
273
+ the repo itself. If attaching this repo to an existing project, merge this repo into that project's `repos[]`
274
+ instead of creating a new project.
275
+
276
+ ## Step 4 — vault tiers + deploy substrate
277
+
278
+ Provision the repo's vault namespace and deploy substrate from the Hub, not from repo-local Actions
279
+ secrets. Runtime config names live in the two-tier vault (`/mmi-future/<slug>/dev|rc|main/*`) and are
280
+ managed through `/secrets`; never use `gh secret set` for product runtime config.
281
+
282
+ The default `tenant-container` substrate is a **Hetzner box** (`hetzner-ssh`): the box writes the release
283
+ `.env` from the registry + vault and runs the container via docker-compose, deployed over the Hub's bounded
284
+ SSH lane. Do **not** create an AWS OIDC deploy role or a repo deploy Action for it. **Ask the master for the
285
+ box assignment** — the `sshHost` (and the loopback port) per stage — then write the `DEPLOY#<stage>` rows with
286
+ `mmi-cli org project set-deploy <owner/repo> --stage <dev|rc|main> --ssh-host <host> [--port <p>]` (defaults:
287
+ `substrate: hetzner-ssh`, deploy path `/opt/mmi/<slug>/<stage>`, service = slug, ssh-user `root`). Without those
288
+ rows the tenant cannot deploy (`tenant-deploy.yml` errors on missing `DEPLOY#` coords), so do not skip this.
289
+ Keep every runtime config value in the vault; never paste secret values into logs.
290
+
291
+ Only an AWS `tenant-container` (the exception) provisions the reusable tenant stack: release bucket,
292
+ per-stage OIDC deploy role, and constrained service-control role/document. Either way the deploy path
293
+ trusts the Hub's central `tenant-deploy.yml` / `tenant-control.yml` environments, not a product repo deploy
294
+ workflow.
295
+
296
+ ## Step 4b — developer access
297
+
298
+ Grant each developer their **org membership + repo access** through the App (`gh api` with the App token):
299
+ `write` for a developer; for a **full-write** member ("project-admin") also add them to the train-branch
300
+ push allowlist from Step 2b (so they can merge protected development PRs). GitHub's
301
+ collaborator list + the per-branch allowlist are the record — no separate roster. Exact commands:
302
+ `docs/Guides/repo-access.md`.
303
+
304
+ ## Step 4c — CI trigger, labels, templates
305
+
306
+ - **Enable workflow triggers** — a freshly-provisioned repo can have GitHub Actions auto-trigger stuck off
307
+ (push/PR never run workflows though dispatch does). Toggle it off→on:
308
+ ```bash
309
+ gh api -X PUT repos/$OWNER/$REPO/actions/permissions -F enabled=false
310
+ gh api -X PUT repos/$OWNER/$REPO/actions/permissions -F enabled=true -f allowed_actions=selected
311
+ ```
312
+ - **Self-hosted CI is the org default** — the `mmi-automation` runner group is org-wide (all repos), so a
313
+ new repo needs no manual group join. Any CI workflow it adds uses
314
+ `runs-on: [self-hosted, linux, x64, mmi-live]` (never `ubuntu-*`/`windows-*`/`macos-*`, which bill
315
+ GitHub-hosted minutes); jobs needing system packages run in a `container:`. The runner has **six
316
+ concurrent job lanes** — independent checks belong in separate parallel jobs (each with its own
317
+ `runs-on`), never chained into one serial job or throttled with unneeded `concurrency` groups. See
318
+ `docs/Guides/gh-runner-runbook.md`.
319
+ - **Product gate + required checks (#1333, stack-aware #1550)** — deployable repos bootstrap-seed
320
+ `.github/workflows/gate.yml` (single `gate` job — the simple default for one check command; a repo
321
+ that grows several independent suites splits them into parallel jobs across the runner's twelve lanes,
322
+ as MMI-Hub's `cli`/`infra`/`docs` gate does) and a ruleset reference at
323
+ `.github/rulesets/mmi-product-required-checks.json`. The gate is stack-aware: capture the repo's
324
+ **runtime** (`node` | `python`), its **check command**, and its **working directory** at interview time
325
+ and pass them as `--var GATE_RUNTIME=node|python`, `--var GATE_CMD=...`, `--var GATE_WORKDIR=...` (and
326
+ `--var GATE_CACHE_DEP_PATH=<path/to/package-lock.json>` when the app's Node lockfile is not at the repo
327
+ root). Defaults: `node` runtime, `npm run check` / `npm ci` at the repo root; a Python repo defaults to
328
+ `pytest` / `pip install -e ".[dev]"` with `--var GATE_PY_VERSION=` (3.11 default). The runtime selects
329
+ which setup step (`setup-node` vs `setup-python`) the rendered `if:` fires. The check command runs
330
+ under the org **wall-clock budget** (#3178): the render pins `run-with-budget` to the CLI's blessed SHA
331
+ with `--var GATE_MAX_SECONDS=` (300s onboarding default — tighten once the gate is measured, via
332
+ `--var` or `org project set <repo> --var gate={"maxSeconds":N}`); `ci audit` and the release train
333
+ both enforce the step, so do not remove it. After apply,
334
+ master-admin must **activate** that JSON as a repository ruleset (GitHub → Settings → Rules → Rulesets →
335
+ Import/create from the committed reference) so the `gate` context is required on train branches. MMI-Hub
336
+ keeps its own three-job gate (`cli`/`infra`/`docs`) — never apply the product ruleset there.
337
+ - **A brand-new repo cannot pass the gate you just installed — its first commit must carry a real project
338
+ (#2928).** The seeded `gate.yml` runs `GATE_INSTALL_CMD` + `GATE_CMD` (`npm ci` + `npm run check` by
339
+ default) **unconditionally**. An empty repo has no `package.json`, so the gate **fails on the seed PR
340
+ itself** — and since the seed PR is what *installs* the gate, this is the normal first-bootstrap path, not
341
+ an edge case. The repo cannot go green and `bootstrap verify` cannot pass until the first commit contains a
342
+ real project satisfying `GATE_CMD`: a `package.json` with a `check` script (or the Python equivalent), plus
343
+ its lockfile. Landing that minimal project is **part of finishing the bootstrap**, not follow-up work —
344
+ otherwise the gate is unpassable by construction.
345
+ **The `check` must be able to fail.** It has to actually compile or lint the code being committed — a
346
+ script that exits 0 without looking at anything does **not** satisfy `GATE_CMD` and must never be committed
347
+ to force a green gate. The goal is a gate that catches a broken change, not a green tick wired to nothing.
348
+ A typecheck plus a lint is enough to start. **Do not seed a test suite to satisfy `GATE_CMD`** — tests are
349
+ opt-in org-wide (#3562); a new repo seeds tests only for paths its own `test-policy.json` marks mandatory,
350
+ and an empty `mandatory` array is the normal answer.
351
+ - **Standard labels** — type labels only (the issue templates reference them). **Priority is a Project
352
+ field, not a label** (#416): never seed `priority:*` labels; `--priority` writes the board field.
353
+ ```bash
354
+ gh label create bug --color d73a4a --description "Something is broken or behaving wrong" -R $OWNER/$REPO
355
+ gh label create feature --color a2eeef --description "New capability or enhancement" -R $OWNER/$REPO
356
+ gh label create task --color 0052cc --description "Task, chore, or improvement" -R $OWNER/$REPO
357
+ ```
358
+ - **Board view (Priority chip)** — the Project's board view must **show the Priority field on cards**, not
359
+ the Labels field (GitHub's API can't set view columns): Board view → **Fields** → remove **Labels**, add
360
+ **Priority**. Otherwise cards show label soup and no priority chip. Strip any legacy `priority:*` /
361
+ taxonomy labels with `mmi-cli board doctor --fix`.
362
+ - **Org App credentials** — nothing to register per repo (#494). Board moves are central (the Hub webhook
363
+ moves Todo/In Progress/In Review for every repo) and the org App token is minted inside the Hub's own
364
+ central workflows, so `MMI_APP_ID` / `MMI_APP_PRIVATE_KEY` live only on the Hub — a product repo seeds no
365
+ App var/secret.
366
+ - **Issue templates** — seed `.github/ISSUE_TEMPLATE/` (Bug · Feature · Task + `config.yml`).
367
+ - **Merge settings (org canon)** — every repo: auto-merge on, squash on, delete-branch-on-merge on, so a
368
+ green allowlisted PR can land itself and never leaves a dead branch:
369
+ `gh api -X PATCH repos/$OWNER/$REPO -f allow_auto_merge=true -f allow_squash_merge=true -f delete_branch_on_merge=true`
370
+ - **Cursor environment** — `.cursor/environment.json` is retired org-wide (#2501): it caused Cursor to
371
+ auto-spawn cloud agents nobody asked for. Bootstrap no longer seeds it and no
372
+ repo should carry a tracked copy.
373
+
374
+ ## Step 5 — install the plugin + seed docs
375
+
376
+ - Bootstrap does **not** seed `.claude/settings.json` or any agent guide (hub-v3 WS4). The developer installs
377
+ the org plugins per machine: `mmi@mutmutco` (its SessionStart hook carries the org tooling + skills) and
378
+ `superpowers@claude-plugins-official` (Anthropic's skills framework — TDD, debugging, subagent dev).
379
+ Personal agent guides (`AGENTS.md`/`CLAUDE.md`) are developer-owned and gitignored — MMI never delivers,
380
+ overwrites, or deletes them; Jervaise's guide rides Jerv PowerTools.
381
+ - Seed `README.md` + `architecture.md` from the templates, then **fill them before finishing** — `bootstrap
382
+ verify` now fails on any leftover `(placeholder)` or `{{TOKEN}}` (#1520). Fill the code-local fields from
383
+ what this bootstrap already knows: **Stack / Run locally / Verify** from the Step-4c gate + install commands
384
+ and the repo's actual code; **Gotchas** and the architecture **Overview / Build & deploy** from the confirmed
385
+ Step-0 axes (class, project-type, deploy-model). Do **not** write the release track, board number, or deploy
386
+ coords as a value — the templates point at `mmi-cli org project get` (registry SSOT, never copied, so it cannot drift).
387
+ 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).
388
+ - **`docs/index.md` is generated by `bootstrap apply`, once (#3545).** The seeded README's first agent
389
+ instruction is to read it, so it exists from the first commit and `bootstrap verify` requires it. Apply
390
+ **creates** it and never rewrites it: apply sees the seed list, not the tree, so regenerating an index in a
391
+ repo that has since added its own docs would silently drop every hand-authored entry. Once the repo has
392
+ docs of its own, `mmi-cli docs index --write` owns it — that reads the whole tree, and `--check` gates drift.
393
+ - **Push the mandated fill past the active ruleset (#1807).** Deployable repos activate
394
+ `mmi-product-required-checks` during apply (its `bypass_actors` is empty by design), so the `gate` check is
395
+ required on `development`/`main` before the seeded README + architecture have ever produced a green run. The
396
+ fill above (#1520) then cannot be pushed, and not even `gh pr merge --admin` clears it (GH013 on push /
397
+ GraphQL rule violation on admin-merge). Sanctioned final step: in GitHub Settings > Rules > Rulesets >
398
+ `mmi-product-required-checks`, set **Enforcement** to **Disabled**, push/merge the filled `README.md` +
399
+ `architecture.md`, then set **Enforcement** back to **Active**. Programmatic equivalent: PUT the ruleset
400
+ with `enforcement: disabled` (a PATCH is rejected, #917/#922), push the fill, then PUT it back to
401
+ `active` (or re-run `bootstrap apply`, which re-activates the ruleset idempotently). Never leave enforcement
402
+ disabled.
403
+ - `.claude/settings.local.json` is local-only and gitignored; bootstrap seeds no committed `.claude/settings.json`.
404
+ - **No agent guide is committed — none, anywhere (#2921).** `mmi-no-agent-files-org` is active with no bypass
405
+ and restricts `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.claude/**`, `.codex/**`, `.agents/**` **and
406
+ `.cursor/rules/**`**. Bootstrap used to seed `.cursor/rules/<repo-slug>.mdc`, which the wall refuses with a
407
+ 409 — it wrote a file its own ruleset bans, then verified the file was there. That seed, its template, and
408
+ the verify check are gone. Repo-specific agent guidance belongs in **`README.md` § Agent context** (stack,
409
+ verify commands, gotchas) with the deep build truth in `architecture.md`; agents read those. Personal agent
410
+ guides stay developer-owned and gitignored, carried per machine by each developer's own plugins.
411
+
412
+ ## Step 6 — register Hub META
413
+
414
+ Do not seed a product repo control-plane config file. `mmi-cli` carries the Hub API endpoint and resolves board,
415
+ deploy, secret, and project state from the Hub registry at runtime.
416
+
417
+ Choose the v2 shape explicitly. Use `--project-type web-app --deploy-model tenant-container` for ordinary
418
+ web tenants, `--project-type desktop-game --deploy-model none --clear-web-profile` for a desktop game,
419
+ `--project-type desktop-app --deploy-model none --clear-web-profile` for a packaged desktop application
420
+ (swap in `--deploy-model registry-publish` when it also publishes a package alongside its installer),
421
+ `--project-type mobile-app --deploy-model none --clear-web-profile` for a phone app distributed through the
422
+ app stores, and
423
+ `--class content --project-type content --deploy-model content --clear-web-profile` for a content/KB repo.
424
+ Run the apply path with the board variables discovered above, or register the same values with
425
+ `mmi-cli org project set` from the Hub or from the target project checkout:
426
+
427
+ ```bash
428
+ mmi-cli bootstrap apply "$OWNER/$REPO" --class deployable \
429
+ --project-type web-app --deploy-model tenant-container --execute \
430
+ --var PROJECT_OWNER="$PROJECT_OWNER" \
431
+ --var PROJECT_NUMBER="$PROJECT_NUMBER" \
432
+ --var PROJECT_ID="$PROJECT_ID" \
433
+ --var STATUS_FIELD_ID="$STATUS_FIELD_ID" \
434
+ --var STATUS_TODO="$STATUS_TODO" \
435
+ --var STATUS_IN_PROGRESS="$STATUS_IN_PROGRESS" \
436
+ --var STATUS_IN_REVIEW="$STATUS_IN_REVIEW" \
437
+ --var STATUS_DONE="$STATUS_DONE"
438
+ ```
439
+
440
+ Tenant-container repos must carry a `docker-compose.yml` and Dockerfile that build from the shipped source
441
+ archive; the train does not ship a prebuilt `dist/`. **Bootstrap seeds both files** for
442
+ `deployModel: tenant-container` from `skills/bootstrap/seeds/` (rendered from
443
+ `docs/Reference/tenant-runtime/docker-compose.yml` and `docs/Reference/tenant-runtime/Dockerfile`). The box
444
+ writes the release `.env` from the registry + vault at deploy time; the compose file carries `env_file: .env`
445
+ and the app reads plain env vars — it must **not** self-load SSM and must **not** ship a committed `.env`.
446
+
447
+ For a `web-app` that declares `oauth` META, print the canonical OAuth surface and provision the client once:
448
+
449
+ ```bash
450
+ mmi-cli org oauth plan --repo "$OWNER/$REPO" # the exact JS origins + redirect URIs + canonical SSM keys
451
+ ```
452
+
453
+ Register those JS origins + `/api/auth/callback` redirect URIs on the Console client (master, per
454
+ `docs/Guides/oauth-provision.md`), then store the creds in the canonical keys in one step:
455
+
456
+ ```bash
457
+ mmi-cli org oauth set-creds --repo "$OWNER/$REPO" < client.json # the Console "Download JSON" file
458
+ ```
459
+
460
+ The keys are the one stageless pair `GOOGLE_CLIENT_ID` + `GOOGLE_CLIENT_SECRET` at the slug root — every
461
+ stage reads it (#2244/#2528). Never a staged `{dev,rc,main}/GOOGLE_*`, `GOOGLE_OAUTH_CLIENT_*`, or `prod/`
462
+ variant; the runtime reads only the canonical names and declare-first rejects the rest.
463
+
464
+ Local `/stage` is optional product-owned configuration. A repo that wants `/stage` may carry a local
465
+ `stage` block, but that file must not contain board, deploy, or secret registry facts.
466
+
467
+ **Stage port block:** run `mmi-cli stage port-range <Repo>` to assign (idempotently) the repo's local port block
468
+ from the central registry. Use `$STAGE_PORT` in `stage.up` / `healthUrl`; `/stage` then picks a free port
469
+ inside the block so a dev can run several projects/versions locally without collisions.
470
+
471
+ ## Step 7 — seed the org-managed .gitignore block
472
+
473
+ The org-managed `.gitignore` block is delivered by the `managed-block` bootstrap seed (`skills/bootstrap/seeds/manifest.json`),
474
+ which merges the canonical block into the repo's `.gitignore` in place, preserving the repo's own ignore lines.
475
+ The `doctor` SessionStart heal keeps it current thereafter. The block carries **only** org-universal ignores —
476
+ never agent guides or a spine. The fanout pipeline that used to push this block via App-token PRs is retired
477
+ (Hub#3010), and the whole-spine fanout was retired earlier (hub-v3 WS4.2 #2219).
478
+
479
+ ## Step 8 — report
480
+
481
+ Repo, default branch, ruleset applied, train branches locked (push allowlist), project attached/created
482
+ (+ info seeded, Status lanes and Labels field verified), secrets set (names only), developer access, plugin
483
+ installed, docs seeded, registry META written, issue templates committed, org App credentials registered,
484
+ org-managed `.gitignore` block seeded, and the final `mmi-cli bootstrap verify "$OWNER/$REPO" --class ... --json` result.
485
+
486
+ ## Retro — one check before you finish
487
+ Before your final report, answer one question honestly: did **this skill's own instructions** misfire
488
+ this run — ambiguous wording, a misleading message, or an environment failure it should have warned
489
+ about? (Process only — never the user's code or task; e.g. an ambiguous seed, registry, or OIDC step, or
490
+ a guard that fired on a healthy repo.) If yes, file **one** lesson and move on; a clean run is silent
491
+ (hard cap: one per run). It lands on the Hub board (deduped) and is fixed only via a reviewed PR — never
492
+ edit the skill live; the retro is advisory, so if the call fails, note it and continue:
493
+ `mmi-cli skill-lesson --skill bootstrap --title "<what misfired>" --body "<what; evidence; proposed amendment>"`
@@ -0,0 +1,30 @@
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
+ # Private GitHub Packages opt-in. Requires compose build.secrets and registry requiredBuildSecrets.
10
+ # RUN --mount=type=secret,id=NODE_AUTH_TOKEN \
11
+ # npmrc="$(mktemp)"; trap 'rm -f "$npmrc"' EXIT; \
12
+ # printf '//npm.pkg.github.com/:_authToken=%s\n' "$(cat /run/secrets/NODE_AUTH_TOKEN)" > "$npmrc"; \
13
+ # NPM_CONFIG_USERCONFIG="$npmrc" npm ci
14
+ RUN npm ci
15
+
16
+ FROM node:24-alpine AS build
17
+ WORKDIR /app
18
+ COPY --from=deps /app/node_modules ./node_modules
19
+ COPY . .
20
+ RUN npm run build
21
+ RUN npm prune --omit=dev
22
+
23
+ FROM node:24-alpine AS runtime
24
+ WORKDIR /app
25
+ ENV NODE_ENV=production
26
+ COPY --from=build /app/package*.json ./
27
+ COPY --from=build /app/node_modules ./node_modules
28
+ COPY --from=build /app/dist ./dist
29
+ EXPOSE 3000
30
+ 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'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
+ - **Docs entry point:** read `docs/index.md` first — the generated routing index (`mmi-cli docs index
22
+ --write`); `--check` gates drift, so it can never fall out of sync with the `docs/` tree.
23
+ - **Durable WHY:** `docs/decisions/` — one file per decision, prose only for what was chosen and
24
+ rejected; never a description of current state.
25
+ - **Current state:** code + `mmi-cli org project get` — release track, board, and deploy coords are
26
+ registry facts, resolved live, never copied into a doc where they can go stale.
27
+ - **GitHub wikis are retired org-wide** — this repo does not publish to a `.wiki.git`; do not create one.
28
+ - **Stack:** (languages, frameworks, major services)
29
+ - **Run locally:** (install, dev server, `/stage` if non-obvious)
30
+ - **Verify before done:** (exact commands — test, lint, typecheck, repo gate script)
31
+ - **Architecture:** deep build/deploy shape → `architecture.md`
32
+ - **Gotchas:** (ports, env from vault not files, Windows/shell quirks specific to this repo)
33
+
34
+ ## Start
35
+
36
+ (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 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,46 @@
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 (or by the docs janitor as a backstop) and merged
46
+ by a human.