@mutmutco/codex-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.
- package/.codex-plugin/plugin.json +30 -0
- package/bin/mmi-cli +6 -0
- package/bin/mmi-cli.cmd +3 -0
- package/bin/mmi-hook +2 -0
- package/bin/mmi-hook-console.cmd +10 -0
- package/bin/mmi-hook.exe +0 -0
- package/hooks/codex-hooks.json +41 -0
- package/package.json +21 -0
- package/scripts/command-ladder-core.mjs +334 -0
- package/scripts/command-ladder-gate.mjs +126 -0
- package/scripts/deny-gate-crash.mjs +179 -0
- package/scripts/edit-tool-paths.mjs +113 -0
- package/scripts/env-write-lint.mjs +137 -0
- package/scripts/hook-io.mjs +22 -0
- package/scripts/hook-policy.mjs +73 -0
- package/scripts/hook-run.mjs +437 -0
- package/scripts/hook-trace.mjs +151 -0
- package/scripts/pretooluse-shell-gates.mjs +424 -0
- package/scripts/secret-echo-lint.mjs +177 -0
- package/scripts/secret-redact.mjs +552 -0
- package/scripts/throttle-core.mjs +324 -0
- package/scripts/validate-hook.mjs +156 -0
- package/scripts/vault-edit-gate.mjs +94 -0
- package/skills/bootstrap/SKILL.md +550 -0
- package/skills/bootstrap/seeds/Dockerfile.template +30 -0
- package/skills/bootstrap/seeds/README.template.md +37 -0
- package/skills/bootstrap/seeds/architecture.template.md +34 -0
- package/skills/bootstrap/seeds/decisions-readme.template.md +45 -0
- package/skills/bootstrap/seeds/docker-compose.template.yml +26 -0
- package/skills/bootstrap/seeds/gate.template.yml +85 -0
- package/skills/bootstrap/seeds/google-login.template.md +33 -0
- package/skills/bootstrap/seeds/manifest.json +26 -0
- package/skills/bootstrap/seeds/mmi-product-required-checks.template.json +23 -0
- package/skills/browser-automation/SKILL.md +95 -0
- package/skills/epic/SKILL.md +104 -0
- package/skills/hotfix/SKILL.md +165 -0
- package/skills/mmi/SKILL.md +404 -0
- package/skills/mmi-doctor/SKILL.md +63 -0
- package/skills/onboard/SKILL.md +85 -0
- package/skills/rcand/SKILL.md +208 -0
- package/skills/release/SKILL.md +599 -0
- package/skills/resume/SKILL.md +90 -0
- package/skills/secrets/SKILL.md +159 -0
- package/skills/stage/SKILL.md +153 -0
- package/skills/worktree/SKILL.md +151 -0
|
@@ -0,0 +1,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.)
|