@mutmutco/claude-plugin 3.131.0

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