@mutmutco/pi-plugin 4.5.174 → 4.5.176

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.
@@ -5,115 +5,53 @@ description: Ship rc or direct-track development to main and production.
5
5
 
6
6
  **Host-native invocation:** Claude `/mmi:release` · Codex `$mmi:release` · jervcode `/release` · Hermes skill_view. A backticked `/name` in this doc names the matching workflow (this skill or a sibling), not a literal command.
7
7
 
8
- **Argument:** `/release minor`, `/release major`, or `/release patch` — a bare bump word, never a `--flag` or env var. No argument → `patch`; a `/release patch` from development is a legitimate ordinary release, `/hotfix` stays the cherry-pick promotion lane.
9
-
10
- **Bump rules** ([docs/versioning.md](../../docs/versioning.md)): patch is the default and needs nothing. `minor` only for a real feature or significant change, with a one-line reason exported as `MMI_BUMP_REASON`. `major` only with Jervaise's fresh approval in this turn: export `MMI_BUMP_REASON` and `MMI_MAJOR_APPROVED=<exact target version>` (e.g. `5.0.0`) only after that approval, never earlier or from a prior turn. A breaking signal in the range (`feat!:`, `BREAKING CHANGE:`) makes the train refuse until it is shimmed (patch) or declared an owner-gated major.
11
-
12
8
  # /release — ship to main + prod
13
9
 
14
- Full-track repos ship exactly what is on `rc` (never `development`): merge `rc → main`, tag `vX.Y.0`, publish the GitHub Release, deploy prod, roll `development` forward. Direct-track repos ship `development → main`, and the release event fires the repo's own deploy/publish workflow (MMI-Hub: `deploy.yml` + `publish.yml`). `rc` is ephemeral: `/rcand` creates its runtime, `/release` retires it after a confirmed prod deploy (`rcRetirement`). A prod release needs the authorized human's explicit go in the current turn — an agent never self-initiates it. The board needs no touch: items reached `Done` when their PRs merged to `development`.
10
+ Bare `minor|major|patch` (default `patch`), never a flag; needs Jervaise's yes (principle 12). `minor`: `MMI_BUMP_REASON`. `major`: also `MMI_MAJOR_APPROVED=<exact version>` after the yes ([docs/versioning.md](../../docs/versioning.md)).
15
11
 
16
12
  ## Lane
17
13
 
18
- For an MM-Work suite lead or member, read [suite.md](suite.md) before choosing a lane or preparing update notes.
19
-
20
- Read META and name the lane before describing or executing anything:
21
-
22
- ```bash
23
- mmi-cli oracle org project get {owner}/{repo} --json
24
- ```
25
-
26
- `releaseTrack: direct` → `development → main`, no `/rcand`. Absent → full (`rc → main`), except MMI-Hub, which is direct by the Hub-control special case. Full-track `--dev` ships `development → main` skipping rc and fails closed when `origin/rc` carries content not in `development`. Unreadable META → stop; never infer the lane from branch names, tags or release history.
27
-
28
- For `deployModel: vercel`, read [vercel.md](vercel.md) before applying or interpreting deployment status.
14
+ Read `mmi-cli oracle org project get {owner}/{repo} --json` and name the lane ([verification.md](verification.md) Lane rules); unreadable META → stop. [suite.md](suite.md) for MM-Work; [vercel.md](vercel.md) for Vercel.
29
15
 
30
16
  ## Preflight
31
17
 
32
- ```bash
33
- mmi-cli devops train doctor --lane release --heal --json
34
- ```
35
-
36
- The JSON is on stdout alone; a stale CLI first prints its self-update notice on stderr and re-runs itself, so parse stdout only, never `2>&1`. Read `ready`, then `findings[]`: each carries `code`, `severity` (`blocker` · `warning` · `healed` · `info`), `source` (`local` · `origin`), `remedy` and `anchor`. `ready: false` → stop and quote every blocker's `code` and `remedy`; an unread origin is never green. `--apply` re-runs this doctor as its step 0 with heal on.
18
+ `mmi-cli devops train doctor --lane release --heal --json` (stdout only): `ready`, `findings[]` (`code`, `severity`, `source`, `remedy`, `anchor`). `ready: false` → quote blockers' `code` and `remedy`.
37
19
 
38
20
  ## Apply
39
21
 
40
- Run from the primary checkout on the lane's start branch (`rc`; `development` for direct-track and `--dev`). A Jerv-Hub release on Windows must run under Git Bash (`"C:/Program Files/Git/bin/bash.exe" -lc "…"`) — the train refuses `--apply` before tagging when it is not. Export the bump intent once from the bare argument — `MMI_BUMP_INTENT=minor|major|patch`, unset → `patch`; `MMI_RELEASE_VERSION=X.Y.Z` only for an exact target the tag math cannot derive.
41
-
42
- Two cases need a summary file. (1) **MMI-Hub** — always: write a fresh 3–6 line neutral summary to `f=$(mkdir -p .jerv/tmp && mktemp .jerv/tmp/release-summary.XXXXXX)` — sourced from the range `origin/main..origin/development`, rewritten in Hub-subsystem terms, never a product or brand name, and never a Jerv-side project (the train refuses such a line). Hub scope is only `mutmutco/MMI-Hub`: never a product's board or a product's deploy state. **Build and verify every line from ONE command's output**, never from commit subjects or PR titles alone: `git log --stat --format='%h %s%n%b' origin/main..origin/development` gives each commit's subject, its full body (where a bundled PR records `Closes #N`) and the files it changed in a single pass. A line whose claim you cannot point to in that output does not go in the file, and every `#N` a Hub line cites must appear there: the train refuses `--apply` on a citation that ships in no commit of the cut. (2) **Any product repo whose META carries `releaseChannel`** (`mmi-cli oracle org project get {owner}/{repo} --json`) — required too: read `releaseLanguage` (default `en`) and write 3–6 lines IN THAT LANGUAGE, in plain words for the project's audience: what changed and why it matters to a user; no commit prefixes, no issue or PR numbers, no file names, no product-internal jargon. The train refuses `--apply` without the file, and raw release notes are never posted to a project channel. A repo with no `releaseChannel` announces nothing, but an mm-work suite repo still requires the summary for its GitHub Release. An mm-work suite repo's summary follows the update-notes template below instead of the 3–6 line rule.
22
+ From the primary checkout on the lane's start branch (Lane rules). Export `MMI_BUMP_INTENT`. MMI-Hub and `releaseChannel` products need a summary file ([verification.md](verification.md)); the Hub's is sourced from the range `origin/main..origin/development`, never a product or brand name, never a product's board.
43
23
 
44
24
  ```bash
45
25
  mkdir -p .jerv/tmp
46
26
  mktemp .jerv/tmp/release-receipt.XXXXXX
47
- ```
48
-
49
- Scratch lives under `.jerv/tmp` in every repo: a repo-root `tmp/` may not exist at all, and an agent host guards the primary checkout against root writes outside `.jerv/tmp` — either one aborts the release before its first command runs.
50
-
51
- Copy the returned pathname into `<result-path>` below; do not rely on shell variables surviving another tool call. For a product without a release channel:
52
-
53
- ```bash
54
- mmi-cli devops release --apply --watch --json --out "<result-path>"
55
- ```
56
-
57
- For MMI-Hub or a product with a release channel, also substitute the saved summary pathname:
58
-
59
- ```bash
60
27
  mmi-cli devops release --apply --watch --announce-summary-file "<summary-path>" --json --out "<result-path>"
61
28
  ```
62
29
 
63
- Add `--dev` for a full-track dev → main release. Keep `--watch` to block on the deploy/publish runs. `mmi-cli devops release --apply --ack <sha>[,<sha>…]` only when the authorized human has verified that a main-only commit the hotfix-coverage guard refused is already in the candidate — never ack to save time. The one command merges, runs the version fold (automatic, inside `mmi-cli devops release --apply`) that bumps the manifests unconditionally before the tag, pushes the tag first (`gate.yml` runs on `v*` tags, so the fold commit earns its required checks), waits for the required contexts, pushes `main`, publishes the Release, dispatches or correlates the deploy (`tenant-deploy.yml` only for tenant-container), announces only after the deploy and publish runs are green (the org alerts channel for the Hub; the project's own release channel for a product repo), re-syncs project info, and rolls `development` and `rc` forward. Independent legs run at the same time: deploy and publish are watched together, and once both settle the announcement, the alignment wait and the served-channel legs overlap; a `workflowRuns` row joined that way carries `parallel: true` beside its own conclusion. Nothing here is done by hand.
64
-
65
- **Progress narration.**
66
-
67
- Describe the release in plain words before each command: “Preparing release”, “Waiting for checks”, “Waiting for publishing”, or “Finishing the existing release”. Keep temporary-file preparation separate from the long-running release command so its tool title cannot become a temporary filename; carry the returned path explicitly into the release command when tool calls do not share shell variables. Keep stderr visible: the CLI prints `Release: ` stages and elapsed updates every 30 seconds during long operations, while JSON and `--out` remain the verification record. Never redirect both streams into the result file.
68
-
69
- Use the actual current stage, never a guessed sequence. Explain a pending result as “Publishing is still pending” or name the unfinished step, then narrate the sanctioned resume command as “Checking the remaining release steps”. Say “Release complete” only after the status fields prove every required follow-up complete. Temporary filenames, `XXXXXX`, receipts, and ledger folding are internal bookkeeping, never a user-facing status.
30
+ Run mktemp alone, copy its path; no channel: drop the summary flag. `mmi-cli devops release --apply --ack <sha>` only for a verified main-only commit the hotfix-coverage guard refused. It runs the version fold (automatic, inside `mmi-cli devops release --apply`) bumping manifests unconditionally, pushes the tag first (`gate.yml` runs on `v*` tags), deploys (`tenant-deploy.yml` for tenant-container), announces. Nothing by hand.
70
31
 
71
32
  ## Verify
72
33
 
73
- Slack announcements use the GitHub-authenticated Hub service and its server-side MMI vault access.
74
- Never install AWS, ask for AWS credentials, or copy the shared Slack key to unblock a developer.
75
- The Hub chooses the registered channel and checks project authority. A failed announcement keeps
76
- its summary file; an uncertain delivery is never automatically replayed.
77
-
78
- Read the saved result, never the exit code (exit `1` = a follow-up failed after promotion; a pending follow-up exits `0`, so a clean exit alone never means every leg resolved):
79
-
80
- - `releaseVerdict.releaseStatus` + `followUpStatus` — `succeeded` + `pending` is shipped with the follow-up unresolved: neither failed nor done. A `--resume` receipt carries the same `releaseVerdict` (`command: release-resume`), plus its own `mode` and `state`.
81
- - `deployStatus` — `pending` is never terminal; `promoted: true` holds even when a deploy failed.
82
- - For a `served-channel(<product>)` row, read [verification.md](verification.md) and verify the installer channel before declaring completion.
83
-
84
- - `workflowRuns` — every run on the release SHA (deploy-model runs, any `push: main` deploy, the repo's own `gate`). On this host, discover them with the bounded run-list read below; filter locally by the exact receipt SHA. If the list hits its 100-row cap or returns no matching SHA, treat the run set as unverified and stop rather than infer completeness. Watch each matching non-success entry to conclusion: `gh run watch <run-id> --repo {owner}/{repo} --exit-status --interval 30`. Name which run is red: `deploy.yml` / `publish.yml` / `tenant-deploy.yml` / `jerv-gateway` is a deploy or publish verdict; a `gate` push run is ordinary CI sharing the SHA.
85
- - With `--watch`, require every follow-up complete before declaring the cut done. For any pending ledger leg, read [recovery.md](recovery.md) before continuing or starting a new train.
86
- - `announceNote` — `announced` (to the alerts channel for the Hub, to the project release channel for a product repo), `skipped`, `not announced` (deploy or publish not green; the note says whether a `--resume` carrying the same `--announce-summary-file` will post it), or the failure note. The channel id itself is never printed.
87
- - `devRollForward` / `rcAlignment` — `pushed`, or `pr-pending` with the alignment PR to land by true merge (Merge floor). An enqueued auto-merge is not evidence of the method; before reporting, read the enqueued method back — `gh pr view <n> --json autoMergeRequest --jq .autoMergeRequest.mergeMethod` — and prove it is `MERGE`. An enqueued `SQUASH` is a stop under the Merge floor, not something to wait out.
88
- - `versionFold`, `rcRetirement`, `checkout` — `returned`, or the named reason you are still on `main`.
89
- - `localInstall` (MMI-Hub) — the train installs the release on this machine via `mmi-hub update`; never run `npm i -g`.
90
- For a private npm surface, read [verification.md](verification.md) before interpreting authentication or package visibility.
91
-
92
- Then verify Latest with this bounded one-row read; require `isLatest: true` and `tagName` equal to the resolved tag: `gh release list --repo {owner}/{repo} --limit 1 --json tagName,isLatest,publishedAt`.
93
-
94
- For the independent run recheck, read [verification.md](verification.md) and use its bounded release-window recipe.
95
-
96
- Four facts close the verdict: the `origin/main..origin/development` count, the tag on origin, Latest, and green runs on the release SHA.
34
+ Receipt fields (`served-channel`, npm, `jerv-gateway`): [verification.md](verification.md). Latest: `gh release list --repo {owner}/{repo} --limit 1 --json tagName,isLatest,publishedAt` → `isLatest: true`, resolved tag. Close on: `origin/main..origin/development` count, tag on origin, Latest, green runs on the SHA.
97
35
 
98
36
  ## Recovery
99
37
 
100
- On any failure, read [recovery.md](recovery.md).
38
+ On failure or a pending ledger leg: [recovery.md](recovery.md).
101
39
 
102
40
  ## Merge floor
103
41
 
104
- - **Never squash-merge a tagged commit (#3167).** A squash re-mints the SHA and orphans the tag from the branch lineage, so every tag-anchored check mis-reads forever after. Any merge that carries an already-tagged commit — the `main → development` roll-forward, an alignment PR, a hotfix fold PR — lands as a true merge: `mmi-cli devops pr merge <number> --auto --merge` (or `--wait --merge`); `--wait` alone still squashes. The hotfix main-base PR itself may squash: its tag lands after, on the merged `main` HEAD, and the `-x` trailer must survive the squash message.
42
+ - **Never squash-merge a tagged commit (#3167).** A squash re-mints the SHA and orphans the tag, so tag-anchored checks mis-read. Any merge that carries an already-tagged commit — the `main → development` roll-forward, an alignment PR, a hotfix fold PR — lands as a true merge: `mmi-cli devops pr merge <number> --auto --merge` (or `--wait --merge`); `--wait` alone still squashes. The hotfix main-base PR itself may squash: its tag lands after, on the merged `main` HEAD, and the `-x` trailer must survive the squash message.
105
43
  - **A refused train is a stop, never a license to finish by hand.** Every fail-closed halt — doctor blocker, authority probe, coverage guard, untolerated conflict, required checks, stray tag, ledger — ends the run. Fix the cause on `development` through a normal PR and rerun the train; never hand-resolve on `main`, hand-cherry-pick, bare-push a train branch, ack to save time, or bypass a check.
106
44
  - **Version probes anchor on the latest Release tag, not a branch (#2734).** A branch manifest or a stale local tag is not a version source; a surviving local tag silently mints the next version.
107
45
  - **One train at a time on a repo (#5380).** Two overlapping runs race the shared tag namespace; the ledger refuses the second — never start one to help the first.
108
- - **Tag and Release deletion are deletion-class:** the authorized human's explicit per-turn go, named in the guide entry that prescribes it.
46
+ - **Tag and Release deletion need Jervaise's yes (principle 12),** named in the guide entry that prescribes it.
109
47
 
110
48
  ## Report
111
49
 
112
- Title first, verdict first. Name the resolved tag (never a placeholder like `vX.Y.0`) and the Release URL, the receipt's status fields (`releaseStatus`, `followUpStatus`, `deployStatus`, `promoted`), and every workflow run row with its conclusion and evidence — use its URL when emitted, otherwise the receipt's explicit no-run-URL note — saying which run is red, and whether it is a deploy/publish verdict or ordinary CI — `announceNote`, the alignment note (`devRollForward`, `rcAlignment`, or the hotfix fold PR and whether it landed), the `checkout` clause, and the sanctioned dispatch mechanism you used. A pending follow-up is reported as unverified, never as success. Product names appear only when releasing that product's repo.
50
+ Verdict first: the resolved tag (never a placeholder like `vX.Y.0`), Release URL, `releaseStatus`, `followUpStatus`, `deployStatus`, `promoted`, each run's conclusion and URL (or the no-run-URL note), which run is red (deploy/publish verdict or ordinary CI), `announceNote`, the alignment note (`devRollForward`, `rcAlignment` or the fold PR), `checkout`, the dispatch used. Pending is unverified, never success. Product names only when releasing that product's repo.
113
51
 
114
52
  ## Retro
115
53
 
116
- Before the final report, answer one question honestly: did this skill's own instructions misfire this run — ambiguous wording, a misleading message, or an environment failure it should have warned about? Process only, never the user's code or task. If yes, file one lesson and move on; a clean run is silent (hard cap: one per run). It lands on the owning board, deduplicated, and is fixed only through a reviewed PR — never edit the skill live. The retro is advisory: if the call fails, note it and continue. File through MMI with the command below. The amendment itself stays reference-free: the skill-drift gate rejects issue references in any section outside Merge floor, so carry the evidence in words or point at the guide entry — never inline reference markers.
54
+ Before the final report: did this skill misfire? Process only. If yes, file one lesson (hard cap: one per run); a clean run is silent; if the call fails, note it and continue. Lessons are deduplicated on the owning board and change the skill only by reviewed PR, never live; no issue references outside Merge floor.
117
55
 
118
56
  ```bash
119
57
  mmi-cli learning skill-lesson --skill release --title "<what misfired>" --body "<what; evidence; proposed amendment>"
@@ -5,3 +5,74 @@ Do not use the raw `releases/latest` API route, a `--jq` formatter, or `gh run l
5
5
  - A `private` npm surface (`surfaces.json`): the doctor's `npm-token-rejected` finding comes first. A 401 from `npm whoami` through the `_org` `npm/NPM_TOKEN` vault hop means the token is rejected, and every authenticated `npm view` then 404s exactly like an absent package — rotate the token; only after a login is a restricted 404 "unverifiable from here", never "not published" (`publish-private-package`). On a repo whose workflows publish through Trusted Publishing and name no registry token, that row is `info`, not a warning: no lane consumes the org token, so it constrains your manual read only.
6
6
 
7
7
  - `served-channel(<product>)` row — the release's proof that new installs get it: the public `/release/version` pointer of the releasing repo's OWN channel (the Hub's `mmi`, or the product its registry `installer` declaration names; no declaration → no row), read after deploy and publish are green. `success` names the version read. `failure` is either a channel still on the previous version or a stale served process (the route answers 401/404): the note names what moves that channel and the exact remedy; run it, then re-read the pointer. `pending` means not probed (legs not green) or unreadable, never proof. The row is receipt evidence only, never a ledger leg, so it never blocks the next train.
8
+
9
+ ## Release summary file
10
+
11
+ Required for MMI-Hub and any product whose META carries `releaseChannel`; an mm-work suite repo needs one for its GitHub Release, following the update-notes template in [suite.md](suite.md). Write 3–6 lines to `f=$(mkdir -p .jerv/tmp && mktemp .jerv/tmp/release-summary.XXXXXX)`.
12
+
13
+ - MMI-Hub: neutral Hub-subsystem terms, never a product or brand name or a Jerv-side project (the train refuses one). Build every line from `git log --stat --format='%h %s%n%b' origin/main..origin/development`; a line you cannot point to there stays out, and every cited `#N` must appear there.
14
+ - Product: in `releaseLanguage` (default `en`), plain words on what changed and why it matters; no prefixes, issue/PR numbers, file names or jargon.
15
+
16
+ ## Receipt fields
17
+
18
+ Read the saved result, never the exit code (`1` = a follow-up failed after promotion; pending exits `0`).
19
+
20
+ - `releaseVerdict.releaseStatus` + `followUpStatus`: `succeeded` + `pending` is shipped, follow-up unresolved. `--resume` receipts carry the same `releaseVerdict` (`command: release-resume`) plus `mode` and `state`.
21
+ - `deployStatus`: `pending` is never terminal; `promoted: true` holds even when a deploy failed.
22
+ - `workflowRuns`: every run on the release SHA; `parallel: true` marks legs run together. Name the red run: `deploy.yml`, `publish.yml`, `tenant-deploy.yml` and `jerv-gateway` are deploy/publish verdicts; a `gate` push run is ordinary CI.
23
+ - `announceNote`: `announced`, `skipped`, `not announced` (deploy/publish not green; a `--resume` with the same `--announce-summary-file` posts it), or the failure note. A failed announcement keeps its summary file; an uncertain delivery is never replayed automatically. Never install AWS, ask for AWS credentials, or copy the Slack key.
24
+ - `devRollForward` / `rcAlignment`: `pushed`, or `pr-pending` with an alignment PR to land by true merge; read the method back with `gh pr view <n> --json autoMergeRequest --jq .autoMergeRequest.mergeMethod`: it must be `MERGE`, and `SQUASH` is a stop.
25
+ - `versionFold`, `rcRetirement`, `checkout`: `returned`, or the reason you are still on `main`.
26
+ - `localInstall` (MMI-Hub): the train installs via `mmi-hub update`; never `npm i -g`.
27
+
28
+ ## Lane rules
29
+
30
+ Full track ships exactly what is on `rc` (never `development`): `rc → main`. Direct track ships `development → main`, no `/rcand`; `releaseTrack: direct` in META, absent = full, and MMI-Hub is always direct. `--dev` on a full track ships `development → main` skipping rc and fails closed when `origin/rc` has content not in `development`. Never infer the lane from branch names, tags or history. Start branch: `rc`; `development` for direct-track and `--dev`. MMI-Hub release on Windows runs under Git Bash (`"C:/Program Files/Git/bin/bash.exe" -lc "…"`); the train refuses `--apply` otherwise. Export `MMI_RELEASE_VERSION=X.Y.Z` only for a target the tag math cannot derive. A breaking signal in the range (`feat!:`, `BREAKING CHANGE:`) makes the train refuse until shimmed (patch) or declared a major.
31
+
32
+
33
+ ## Detail moved from SKILL.md (verbatim)
34
+
35
+ **Argument:** `/release minor`, `/release major`, or `/release patch` — a bare bump word, never a `--flag` or env var. No argument → `patch`; a `/release patch` from development is a legitimate ordinary release, `/hotfix` stays the cherry-pick promotion lane.
36
+
37
+ **Bump rules** ([docs/versioning.md](../../docs/versioning.md)): patch is the default and needs nothing. `minor` only for a real feature or significant change, with a one-line reason exported as `MMI_BUMP_REASON`. `major` only with Jervaise's fresh approval in this turn: export `MMI_BUMP_REASON` and `MMI_MAJOR_APPROVED=<exact target version>` (e.g. `5.0.0`) only after that approval, never earlier or from a prior turn. A breaking signal in the range (`feat!:`, `BREAKING CHANGE:`) makes the train refuse until it is shimmed (patch) or declared an owner-gated major.
38
+
39
+ Full-track repos ship exactly what is on `rc` (never `development`): merge `rc → main`, tag `vX.Y.0`, publish the GitHub Release, deploy prod, roll `development` forward. Direct-track repos ship `development → main`, and the release event fires the repo's own deploy/publish workflow (MMI-Hub: `deploy.yml` + `publish.yml`). `rc` is ephemeral: `/rcand` creates its runtime, `/release` retires it after a confirmed prod deploy (`rcRetirement`). A prod release needs Jervaise's yes (principle 12). The board needs no touch: items reached `Done` when their PRs merged to `development`.
40
+
41
+ The JSON is on stdout alone; a stale CLI first prints its self-update notice on stderr and re-runs itself, so parse stdout only, never `2>&1`. Read `ready`, then `findings[]`: each carries `code`, `severity` (`blocker` · `warning` · `healed` · `info`), `source` (`local` · `origin`), `remedy` and `anchor`. `ready: false` → stop and quote every blocker's `code` and `remedy`; an unread origin is never green. `--apply` re-runs this doctor as its step 0 with heal on.
42
+
43
+ Run from the primary checkout on the lane's start branch (`rc`; `development` for direct-track and `--dev`). A Jerv-Hub release on Windows must run under Git Bash (`"C:/Program Files/Git/bin/bash.exe" -lc "…"`) — the train refuses `--apply` before tagging when it is not. Export the bump intent once from the bare argument — `MMI_BUMP_INTENT=minor|major|patch`, unset → `patch`; `MMI_RELEASE_VERSION=X.Y.Z` only for an exact target the tag math cannot derive.
44
+
45
+ Two cases need a summary file. (1) **MMI-Hub** — always: write a fresh 3–6 line neutral summary to `f=$(mkdir -p .jerv/tmp && mktemp .jerv/tmp/release-summary.XXXXXX)` — sourced from the range `origin/main..origin/development`, rewritten in Hub-subsystem terms, never a product or brand name, and never a Jerv-side project (the train refuses such a line). Hub scope is only `mutmutco/MMI-Hub`: never a product's board or a product's deploy state. **Build and verify every line from ONE command's output**, never from commit subjects or PR titles alone: `git log --stat --format='%h %s%n%b' origin/main..origin/development` gives each commit's subject, its full body (where a bundled PR records `Closes #N`) and the files it changed in a single pass. A line whose claim you cannot point to in that output does not go in the file, and every `#N` a Hub line cites must appear there: the train refuses `--apply` on a citation that ships in no commit of the cut. (2) **Any product repo whose META carries `releaseChannel`** (`mmi-cli oracle org project get {owner}/{repo} --json`) — required too: read `releaseLanguage` (default `en`) and write 3–6 lines IN THAT LANGUAGE, in plain words for the project's audience: what changed and why it matters to a user; no commit prefixes, no issue or PR numbers, no file names, no product-internal jargon. The train refuses `--apply` without the file, and raw release notes are never posted to a project channel. A repo with no `releaseChannel` announces nothing, but an mm-work suite repo still requires the summary for its GitHub Release. An mm-work suite repo's summary follows the update-notes template below instead of the 3–6 line rule.
46
+
47
+ ```bash
48
+ mkdir -p .jerv/tmp
49
+ mktemp .jerv/tmp/release-receipt.XXXXXX
50
+ ```
51
+
52
+ Scratch lives under `.jerv/tmp` in every repo: a repo-root `tmp/` may not exist at all, and an agent host guards the primary checkout against root writes outside `.jerv/tmp` — either one aborts the release before its first command runs.
53
+
54
+ Copy the returned pathname into `<result-path>` below; do not rely on shell variables surviving another tool call. For a product without a release channel:
55
+
56
+ ```bash
57
+ mmi-cli devops release --apply --watch --json --out "<result-path>"
58
+ ```
59
+
60
+ ```bash
61
+ mmi-cli devops release --apply --watch --announce-summary-file "<summary-path>" --json --out "<result-path>"
62
+ ```
63
+
64
+ Add `--dev` for a full-track dev → main release. Keep `--watch` to block on the deploy/publish runs. `mmi-cli devops release --apply --ack <sha>[,<sha>…]` only when Jervaise has verified that a main-only commit the hotfix-coverage guard refused is already in the candidate — never ack to save time. The one command merges, runs the version fold (automatic, inside `mmi-cli devops release --apply`) that bumps the manifests unconditionally before the tag, pushes the tag first (`gate.yml` runs on `v*` tags, so the fold commit earns its required checks), waits for the required contexts, pushes `main`, publishes the Release, dispatches or correlates the deploy (`tenant-deploy.yml` only for tenant-container), announces only after the deploy and publish runs are green (the org alerts channel for the Hub; the project's own release channel for a product repo), re-syncs project info, and rolls `development` and `rc` forward. Independent legs run at the same time: deploy and publish are watched together, and once both settle the announcement, the alignment wait and the served-channel legs overlap; a `workflowRuns` row joined that way carries `parallel: true` beside its own conclusion. Nothing here is done by hand.
65
+
66
+ Describe the release in plain words before each command: “Preparing release”, “Waiting for checks”, “Waiting for publishing”, or “Finishing the existing release”. Keep temporary-file preparation separate from the long-running release command so its tool title cannot become a temporary filename; carry the returned path explicitly into the release command when tool calls do not share shell variables. Keep stderr visible: the CLI prints `Release: ` stages and elapsed updates every 30 seconds during long operations, while JSON and `--out` remain the verification record. Never redirect both streams into the result file.
67
+
68
+ Use the actual current stage, never a guessed sequence. Explain a pending result as “Publishing is still pending” or name the unfinished step, then narrate the sanctioned resume command as “Checking the remaining release steps”. Say “Release complete” only after the status fields prove every required follow-up complete. Temporary filenames, `XXXXXX`, receipts, and ledger folding are internal bookkeeping, never a user-facing status.
69
+
70
+ - `workflowRuns` — every run on the release SHA (deploy-model runs, any `push: main` deploy, the repo's own `gate`). On this host, discover them with the bounded run-list read below; filter locally by the exact receipt SHA. If the list hits its 100-row cap or returns no matching SHA, treat the run set as unverified and stop rather than infer completeness. Watch each matching non-success entry to conclusion: `gh run watch <run-id> --repo {owner}/{repo} --exit-status --interval 30`. Name which run is red: `deploy.yml` / `publish.yml` / `tenant-deploy.yml` / `jerv-gateway` is a deploy or publish verdict; a `gate` push run is ordinary CI sharing the SHA.
71
+ - With `--watch`, require every follow-up complete before declaring the cut done. For any pending ledger leg, read [recovery.md](recovery.md) before continuing or starting a new train.
72
+ - `announceNote` — `announced` (to the alerts channel for the Hub, to the project release channel for a product repo), `skipped`, `not announced` (deploy or publish not green; the note says whether a `--resume` carrying the same `--announce-summary-file` will post it), or the failure note. The channel id itself is never printed.
73
+ - `devRollForward` / `rcAlignment` — `pushed`, or `pr-pending` with the alignment PR to land by true merge (Merge floor). An enqueued auto-merge is not evidence of the method; before reporting, read the enqueued method back — `gh pr view <n> --json autoMergeRequest --jq .autoMergeRequest.mergeMethod` — and prove it is `MERGE`. An enqueued `SQUASH` is a stop under the Merge floor, not something to wait out.
74
+ - `versionFold`, `rcRetirement`, `checkout` — `returned`, or the named reason you are still on `main`.
75
+ - `localInstall` (MMI-Hub) — the train installs the release on this machine via `mmi-hub update`; never run `npm i -g`.
76
+ For a private npm surface, read [verification.md](verification.md) before interpreting authentication or package visibility.
77
+
78
+ Then verify Latest with this bounded one-row read; require `isLatest: true` and `tagName` equal to the resolved tag: `gh release list --repo {owner}/{repo} --limit 1 --json tagName,isLatest,publishedAt`.
@@ -7,101 +7,39 @@ description: Manage the project vault and granted org-infra secrets.
7
7
 
8
8
  # /secrets — two-tier project secrets
9
9
 
10
- **Authority:** a project-admin self-serves their own product repo's full vault tree (`mmi-cli oracle org access
11
- role` → `project-admin` + `train`): stageless, `dev`, `rc`, and `main`. Do not redirect them to master for
12
- an own-project coordinate. Only org-infra grants and cross-project administration stay master-only.
13
-
14
- Secrets in the org split by **blast radius + who manages them** (not by storage — both tiers are SSM
15
- SecureString + KMS, and **a value is never echoed to chat or logs**):
16
-
17
- - **YOUR REPO** — `/mmi-future/<slug>/<KEY>` (the stageless canonical) plus
18
- `/mmi-future/<slug>/{dev,rc,main}/*` (staged per-stage overrides) for a repo you are project-admin of.
19
- You **self-serve your own repo's full tree** — the canonical root, dev, rc, AND main prod secrets — via
20
- `/secrets`, on your **GitHub role alone**, no AWS, no waiting on the master (#2032).
21
- - **ORG-INFRA** — the shared/infra namespaces (`/mmi-future/{shared,cloudflare,docs,mmi-hub}/*`), at
22
- **every** tier (their `dev/` holds Hetzner/AWS/CF tokens + Hub tooling). **Crown jewels, master-only.**
23
- A project-admin reaches one of these only via a master **grant**; the master is unrestricted (master ⊇
24
- project-admin).
25
-
26
- For org-infra grants (including read-only wildcards), read [reference.md](reference.md) before granting access.
27
-
28
- A bare `<KEY>` is the **stageless canonical** at the slug root — one value every stage shares (#2244)
29
- and the path deploys resolve (#2523). A staged `dev/<KEY>` is a **per-stage override** and WINS for that
30
- stage at deploy (#2522); `set`/`use` warn (names only) when an override shadows the canonical. The
31
- `dev`/non-`dev` `tier` field in older JSON is compatibility metadata, not an authority boundary: an
32
- own-project `main/SECRET_KEY_BASE` or `rc/DB_URL` remains project-admin self-service.
33
-
34
- **Declare-first (#2528):** the registry catalog is the SSOT; SSM is a projection of it. A `set` to a
35
- coordinate not declared in the catalog is **rejected** — the error names the attempted path and the fix.
36
- Declare the key first via `mmi-cli oracle org project set <owner/repo> --secrets-file <catalog.json>` (`stages: []`
37
- = the shared stageless canonical, `stages: ["dev"]` = a per-stage override), then retry. See what is
38
- declared with `mmi-cli vault secrets catalog` or `secrets list`; ask for a key you lack via `mmi-cli vault secrets
39
- request <KEY>`. There is no master bypass. `rm` of an undeclared path stays allowed, so cleanup keeps
40
- working. Google OAuth is one stageless pair per repo: bare `GOOGLE_CLIENT_ID` + `GOOGLE_CLIENT_SECRET`
41
- at the slug root (the staged `{dev,rc,main}/GOOGLE_*` shape is retired; `org oauth set-creds` writes the
42
- bare keys).
43
-
44
- All ops run through `mmi-cli vault secrets …`, which calls the org backend with the caller's `gh` token; the
45
- backend re-verifies **project-admin-of-this-repo** and the **vault namespace** server-side and does the scoped
46
- SSM op. This skill never touches AWS.
10
+ **Authority:** a project-admin (`mmi-cli oracle org access role` → `project-admin` + `train`) self-serves their own repo's full vault tree: stageless, `dev`, `rc`, `main`. Project-admin = repo `admin`, or `write`/`maintain` plus the registry `projectAdmins` entry; master = org owner. The backend checks both server-side. Only org-infra grants and cross-project administration are master-only. Every write is attributable (actor + repo + KEY name, never the value). A value is never shown (principle 11).
47
11
 
48
- ## Step 0 — orient
12
+ - **YOUR REPO** — `/mmi-future/<slug>/<KEY>` (stageless canonical, one value for every stage, the path deploys resolve) plus `/mmi-future/<slug>/{dev,rc,main}/*` (per-stage overrides). A staged `dev/<KEY>` WINS for that stage at deploy; `set`/`use` warn (names only) when an override shadows the canonical. The `dev`/non-`dev` `tier` field in older JSON is compatibility metadata, not an authority boundary.
13
+ - **ORG-INFRA** — `/mmi-future/{shared,cloudflare,docs,mmi-hub}/*` at every tier. Master-only; a project-admin reaches one only via a master **grant**. For grants (including read-only wildcards), read [reference.md](reference.md) first.
14
+
15
+ **Declare-first:** the registry catalog is the SSOT; SSM is its projection. A `set` to an undeclared coordinate is rejected (the error names the path and fix); no master bypass. Declare with `mmi-cli oracle org project set <owner/repo> --secrets-file <catalog.json>` (`stages: []` = stageless canonical, `stages: ["dev"]` = override), then retry. See declared keys with `mmi-cli vault secrets catalog` or `secrets list`; ask for a missing key with `mmi-cli vault secrets request <KEY>`. `rm` of an undeclared path stays allowed. Google OAuth is one stageless pair at the slug root, bare `GOOGLE_CLIENT_ID` + `GOOGLE_CLIENT_SECRET` (`org oauth set-creds` writes them; the staged shape is retired).
49
16
 
50
- Read the current repo + slug; the tier of a key follows from its name (above). `secrets list` shows what
51
- exists and which ones **you** can manage (a `*`), never values.
17
+ All ops run through `mmi-cli vault secrets …`, which calls the org backend with the caller's `gh` token; the backend re-verifies project-admin and namespace and does the scoped SSM op. This skill never touches AWS.
18
+
19
+ ## Step 0 — orient
52
20
 
53
21
  ```bash
54
- mmi-cli vault secrets list # names + tier + a * on the ones you can write. NEVER values.
22
+ mmi-cli vault secrets list # names + tier + * on those you can write; never values
55
23
  ```
56
24
 
57
- `secrets: no registry META` or `Hub API unreachable` → the repo is not registered with the Hub, GitHub auth
58
- is missing, or the Hub API is unavailable. Run `mmi-cli oracle org project get <owner/repo>` to distinguish those cases;
59
- a master-admin backfills the registry before secrets can be resolved.
25
+ `secrets: no registry META` or `Hub API unreachable` → repo unregistered, GitHub auth missing, or Hub down. Run `mmi-cli oracle org project get <owner/repo>` to tell which; a master-admin backfills the registry.
60
26
 
61
27
  ## Step 1 — the verb
62
28
 
63
- Default to the **current repo**; pass `--repo owner/Name` to target another (you must be its project-admin).
29
+ Current repo by default; `--repo owner/Name` targets another (you must be its project-admin).
64
30
 
65
31
  ```bash
66
- # CONSUME a secret keyless — injected into the command's env, NEVER printed (raw `secrets get` was
67
- # removed, the floor wins, #2844):
68
- mmi-cli vault secrets use SCRAPER_API_KEY -- <cmd>
69
-
70
- # Write / rotate — the VALUE is read from stdin, NEVER an argument (so it can't leak into shell history
71
- # or process args). Pipe it in:
72
- printf %s "$THE_VALUE" | mmi-cli vault secrets set SCRAPER_API_KEY
73
-
74
- # Validate a known provider key without printing its value:
75
- mmi-cli vault secrets verify RECALL_API_KEY
76
-
77
- # Remove:
32
+ mmi-cli vault secrets use SCRAPER_API_KEY -- <cmd> # consume keyless: injected into env, never printed
33
+ printf %s "$THE_VALUE" | mmi-cli vault secrets set SCRAPER_API_KEY # write/rotate: value from stdin, never an argument
34
+ mmi-cli vault secrets verify RECALL_API_KEY # validate a known provider key
78
35
  mmi-cli vault secrets rm SCRAPER_API_KEY
79
36
  ```
80
37
 
81
- **Never** pass a value as an argument (`secrets set KEY thevalue` is wrong — there is no value arg). The
82
- confirmation prints the **name and tier only**, never the value.
83
-
84
- On Windows, read [reference.md](reference.md) before wrapping a multiline command.
85
-
86
- For rotation, org-infra elevation, new-key coordinates, or tier-to-tier copies, read [reference.md](reference.md) before the operation.
38
+ There is no value argument; confirmation prints name and tier only. On Windows, read [reference.md](reference.md) before wrapping a multiline command. For rotation, org-infra elevation, new-key coordinates, or tier-to-tier copies, read [reference.md](reference.md) first.
87
39
 
88
- ## Notes
40
+ Runtime consumes declared keys through its scoped broker token (central deploy injects the declared stage set, no repo `.env`); CI reads keylessly through its OIDC role. Treat every secret as write-once.
89
41
 
90
- - **Never echo a value** — not in chat, a commit, a log, or an issue. No command emits a value to stdout;
91
- consume one keyless with `secrets use <KEY> -- <cmd>` (#2844). Treat every secret as write-once.
92
- - Authority is **GitHub**: project-admin = repo `admin`, or repo `write`/`maintain` plus the registry `projectAdmins` entry; master =
93
- org owner. The backend checks both with the org App token, so the decision is server-side (your token
94
- scope can't widen it).
95
- - Runtime consumes declared keys through its scoped broker token (and central deploy injects the declared
96
- stage set without a repo `.env`); CI reads keylessly through its OIDC role. Neither uses raw `/secrets`
97
- output. `secrets use <KEY>` is the safe local command-consumption path.
98
- - Every self-service write is **attributable** — the backend logs actor + repo + KEY name (never the value).
42
+ ## Retro
99
43
 
100
- ## Retro — one check before you finish
101
- Before your final report, answer one question honestly: did **this skill's own instructions** misfire
102
- this run — ambiguous wording, a misleading message, or an environment failure it should have warned
103
- about? (Process only — never the user's code or task; e.g. a canonical-name or tier mix-up, or a step
104
- that risked echoing a value.) If yes, file **one** lesson and move on; a clean run is silent (hard cap:
105
- one per run). It lands on the owning board (deduped) and is fixed only via a reviewed PR — never edit the
106
- skill live; the retro is advisory, so if the call fails, note it and continue. File through MMI:
44
+ If this skill's instructions misfired (process only, e.g. a canonical-name or tier mix-up), file one lesson (one per run; if the call fails, note it and continue):
107
45
  `mmi-cli learning skill-lesson --skill secrets --title "<what misfired>" --body "<what; evidence; proposed amendment>"`
@@ -65,3 +65,44 @@ mmi-cli vault secrets copy --from rc --to dev --keys RECALL_API_KEY --dry-run
65
65
  ```
66
66
  Prefer this over manual copy piping — audit-logged, blocklist enforced.
67
67
 
68
+
69
+
70
+ ## Detail moved from SKILL.md (verbatim)
71
+
72
+ **Authority:** a project-admin self-serves their own product repo's full vault tree (`mmi-cli oracle org access
73
+ role` → `project-admin` + `train`): stageless, `dev`, `rc`, and `main`. Do not redirect them to master for
74
+ an own-project coordinate. Only org-infra grants and cross-project administration stay master-only.
75
+
76
+ A bare `<KEY>` is the **stageless canonical** at the slug root — one value every stage shares (#2244)
77
+ and the path deploys resolve (#2523). A staged `dev/<KEY>` is a **per-stage override** and WINS for that
78
+ stage at deploy (#2522); `set`/`use` warn (names only) when an override shadows the canonical. The
79
+ `dev`/non-`dev` `tier` field in older JSON is compatibility metadata, not an authority boundary: an
80
+ own-project `main/SECRET_KEY_BASE` or `rc/DB_URL` remains project-admin self-service.
81
+
82
+ **Declare-first (#2528):** the registry catalog is the SSOT; SSM is a projection of it. A `set` to a
83
+ coordinate not declared in the catalog is **rejected** — the error names the attempted path and the fix.
84
+ Declare the key first via `mmi-cli oracle org project set <owner/repo> --secrets-file <catalog.json>` (`stages: []`
85
+ = the shared stageless canonical, `stages: ["dev"]` = a per-stage override), then retry. See what is
86
+ declared with `mmi-cli vault secrets catalog` or `secrets list`; ask for a key you lack via `mmi-cli vault secrets
87
+ request <KEY>`. There is no master bypass. `rm` of an undeclared path stays allowed, so cleanup keeps
88
+ working. Google OAuth is one stageless pair per repo: bare `GOOGLE_CLIENT_ID` + `GOOGLE_CLIENT_SECRET`
89
+ at the slug root (the staged `{dev,rc,main}/GOOGLE_*` shape is retired; `org oauth set-creds` writes the
90
+ bare keys).
91
+
92
+ ```bash
93
+ # CONSUME a secret keyless — injected into the command's env, NEVER printed (raw `secrets get` was
94
+ # removed, the floor wins, #2844):
95
+ mmi-cli vault secrets use SCRAPER_API_KEY -- <cmd>
96
+
97
+ **Never** pass a value as an argument (`secrets set KEY thevalue` is wrong — there is no value arg). The
98
+ confirmation prints the **name and tier only**, never the value.
99
+
100
+ - **Never echo a value** — not in chat, a commit, a log, or an issue. No command emits a value to stdout;
101
+ consume one keyless with `secrets use <KEY> -- <cmd>` (#2844). Treat every secret as write-once.
102
+ - Authority is **GitHub**: project-admin = repo `admin`, or repo `write`/`maintain` plus the registry `projectAdmins` entry; master =
103
+ org owner. The backend checks both with the org App token, so the decision is server-side (your token
104
+ scope can't widen it).
105
+ - Runtime consumes declared keys through its scoped broker token (and central deploy injects the declared
106
+ stage set without a repo `.env`); CI reads keylessly through its OIDC role. Neither uses raw `/secrets`
107
+ output. `secrets use <KEY>` is the safe local command-consumption path.
108
+ - Every self-service write is **attributable** — the backend logs actor + repo + KEY name (never the value).
@@ -7,26 +7,11 @@ description: Run a local test stage or personal cloud dev stage with --live.
7
7
 
8
8
  # /stage — local test environment
9
9
 
10
- A throwaway **local** environment to exercise the current branch — a dev server / local stack, plus
11
- Playwright or other tools where the project uses them. It is **off the promotion train**: any developer,
12
- no version effect, no deploy. It lives in a **gitignored** workspace, **force-kills the previous stage**
13
- before starting, and **auto-decommissions** when done.
14
-
15
- Use `/stage` as the normal path for local previews, smoke tests, and Playwright targets. If the user
16
- already asked to run or preview the app, that is enough authorization to start it; when intent is unclear,
17
- ask before creating or destroying a stage. Manual dev-server commands are allowed when the user explicitly
18
- wants a bypass or when a bounded diagnostic needs one, but say why. If the stage path itself fails, diagnose
19
- from `mmi-cli stage --json`, `tmp/stage/state.json`, process/port/container evidence, and file Hub/org-default
20
- friction instead of silently working around it.
21
-
22
- The stage recipe is the **registry-derived default** for plumbing-free product repos: a `tenant-container`
23
- repo that ships `docker-compose.yml` and has a Hub registry `portRange` needs **no committed config**.
24
- `mmi-cli stage` derives a default compose stage — run `docker compose build --no-cache`, run `docker compose
25
- up -d`, choose a free port from the registry range, and report the local URL (e.g. `http://127.0.0.1:3700/`).
26
- The commands are shell-aware and `--apply` runs them cross-shell. Board, deploy, secret, and project registry
27
- facts all live in the Hub registry — no repo-local control-plane file.
28
-
29
- For vault/env passthrough or build-time secret setup, read [reference.md](reference.md) before running the stage.
10
+ A throwaway **local** environment (dev server or local stack, plus Playwright where used) for the current branch. Off the promotion train: no version effect, no deploy. It lives in a gitignored workspace, force-kills the previous stage in this worktree before starting, and is decommissioned when done.
11
+
12
+ `/stage` is the normal path for previews, smoke tests and Playwright targets; a request to run or preview the app is enough to start it. A manual dev-server command is fine for a bounded diagnostic or a requested bypass; say why. If the stage path fails, diagnose from `mmi-cli stage --json`, `tmp/stage/state.json` and process/port/container evidence, and file Hub/org-default friction instead of silently working around it.
13
+
14
+ A `tenant-container` repo with `docker-compose.yml` and a Hub registry `portRange` needs no committed config: `mmi-cli stage` derives `docker compose build --no-cache`, `docker compose up -d`, a free port from the range, and the local URL (e.g. `http://127.0.0.1:3700/`). Board, deploy, secret and project facts live in the Hub registry, not a repo file. For vault/env passthrough or build-time secrets, read [reference.md](reference.md).
30
15
 
31
16
  ## Step 0 — inspect the plan
32
17
 
@@ -34,48 +19,21 @@ For vault/env passthrough or build-time secret setup, read [reference.md](refere
34
19
  mmi-cli stage --json
35
20
  ```
36
21
 
37
- The JSON reports `source` (`derived` / `local` / `none`) and, when derived, the local `url`. `source: none`
38
- means neither a usable local recipe nor a derivable default exists — the message names the missing fact
39
- (deployModel, `docker-compose.yml`, or registry `portRange`; `.env.example` is **not** required — #2655).
40
- On a `deployModel: vercel` repo the receipt also carries `preview` — the current branch's latest
41
- Vercel **Preview** deployment (`state`, `url`) read from the GitHub Deployments the Vercel GitHub app
42
- posts — and the text output ends with a `vercel preview (<branch>)` line. Report that URL as the branch
43
- preview alongside the local stage; no `preview.deployment` means the branch was not pushed yet or the
44
- Vercel GitHub app is not connected. There is no box stage on a vercel repo: `--live` refuses.
45
- When the gap is a missing Hub registry `portRange`, the JSON receipt also carries `recovery` with
46
- `mmi-cli stage port-range <owner/repo>` (#5539) — assign the block, then re-run `/stage`. That gap does not
47
- mean the repo's Hub registry/org project setup is missing; it means local stage derivation cannot pick a
48
- collision-safe port until META.portRange exists.
49
-
50
- ## Step 1 — run the stage
51
-
52
- A stage is **per worktree**. The CLI force-stops only the previous stage **in this worktree**, runs
53
- `stage.build`, starts `stage.up`, records `tmp/stage/state.json`, picks a free port from the registry
54
- range (skipping ports reserved by sibling worktrees), and polls `stage.healthUrl` when configured.
55
- Recipes run in Bash, including Git Bash on Windows. Use Bash syntax for build, up and teardown;
56
- legacy cmd `set VAR=value` and PowerShell `$env:VAR` recipes must be converted to Bash or Node scripts.
57
- Windows requires Git Bash or an absolute `SHELL` path to its `bash.exe`; there is no cmd fallback.
58
- Use `--port` to pin a port when needed:
22
+ `source` is `derived`/`local`/`none`; when derived, `url` is the local URL. `none` means no usable recipe or derivable default; the message names the missing fact (deployModel, `docker-compose.yml`, or registry `portRange`; `.env.example` is not required). A missing `portRange` adds `recovery` with `mmi-cli stage port-range <owner/repo>`: assign the block, re-run `/stage` (the Hub registry itself is fine; stage just cannot pick a collision-safe port).
59
23
 
60
- ```bash
61
- mmi-cli stage run --apply
62
- mmi-cli stage run --apply --port 5180
63
- ```
24
+ On `deployModel: vercel` the receipt carries `preview` (latest Vercel Preview `state`, `url` from GitHub Deployments); report that URL as the branch preview. No `preview.deployment` = branch unpushed or Vercel GitHub app not connected. There is no box stage on vercel: `--live` refuses.
64
25
 
65
- Use a larger bound when the repo's local stack is known to be slow:
66
- ```bash
67
- mmi-cli stage run --apply --timeout-ms 120000
68
- ```
26
+ ## Step 1 — run
69
27
 
70
- All stage artifacts (build output, screenshots, Playwright traces, local DB files) stay under `tmp/stage/`
71
- — never tracked.
28
+ A stage is per worktree: the CLI stops this worktree's previous stage, runs `stage.build`, starts `stage.up`, records `tmp/stage/state.json` (cwd, branch, commit), picks a free port from the registry range (skipping sibling worktrees' ports), and polls `stage.healthUrl` if set. Sibling worktrees keep their own stages; no need to stop one first. Recipes run in Bash (Git Bash on Windows, or an absolute `SHELL` path to its `bash.exe`; no cmd fallback), so convert `set VAR=value` or `$env:VAR` recipes to Bash or Node. For `jervaise`, continuity keys on branch; use a distinct North Star slug per parallel grind.
72
29
 
73
- A local stage is bound to the worktree that started it. Concurrent worktrees on one machine each keep
74
- their own stage on separate ports — you do not need to stop one before starting another. For `jervaise`, continuity
75
- keys on branch; use a distinct North Star slug per parallel grind or feature. Stage JSON/state records
76
- the starting `cwd` plus git branch/commit when available.
30
+ ```bash
31
+ mmi-cli stage run --apply
32
+ mmi-cli stage run --apply --port 5180 # pin a port
33
+ mmi-cli stage run --apply --timeout-ms 120000 # slow stacks
34
+ ```
77
35
 
78
- When grinding or issue acceptance criteria exist, read [smoke.md](smoke.md) and run its post-smoke panel before teardown.
36
+ All artifacts (build output, screenshots, traces, local DB files) stay under untracked `tmp/stage/`. When grinding or issue acceptance criteria exist, read [smoke.md](smoke.md) and run its post-smoke panel before teardown.
79
37
 
80
38
  ## Step 2 — stop when done
81
39
 
@@ -83,27 +41,15 @@ When grinding or issue acceptance criteria exist, read [smoke.md](smoke.md) and
83
41
  mmi-cli stage stop --apply
84
42
  ```
85
43
 
86
- Stop the stage when the work is done, before switching context, or before replacing it with another stage.
87
- If the user clearly wants the preview to stay up, leave it running and report that. The next `/stage` also
88
- stops the previous recorded stage before starting, so a stale server does not linger between runs. For the
89
- registry-derived Docker Compose default, stop also runs the recorded compose teardown (`docker compose down`)
90
- from the original stage working directory. Stop re-fetches the declared dev runtime secrets so that `down`
91
- can interpolate the same file (`${POSTGRES_PASSWORD:?}`, `${PORT:?}`) — secrets never come from the state
92
- file (#6351). A vault-native repo with no `.env` gets `PORT` defaulted to the bound stage port.
44
+ Stop when work is done, before switching context, or before another stage; leave it up if the user wants the preview, and report that. For the Docker Compose default, stop runs `docker compose down` from the original working directory, re-fetching the declared dev runtime secrets so `${POSTGRES_PASSWORD:?}` and `${PORT:?}` interpolate (secrets never come from the state file); a vault-native repo with no `.env` gets `PORT` defaulted to the bound port.
93
45
 
94
- For a personal cloud dev stage (`--live`), read [live.md](live.md) before offering, applying, or tearing it down.
46
+ For a personal cloud dev stage (`--live`), read [live.md](live.md) before offering, applying or tearing it down.
95
47
 
96
48
  ## Step 3 — report
97
49
 
98
- The stage URL, what's running (server + tools), the workspace path (`tmp/stage/`), and the teardown command.
50
+ Stage URL, what's running, workspace path (`tmp/stage/`), teardown command. For Playwright output placement, extra services, or stale Docker/env troubleshooting, read [reference.md](reference.md) Notes.
99
51
 
100
- For Playwright output placement, extra services, or stale Docker/env troubleshooting, read [reference.md](reference.md) Notes.
52
+ ## Retro
101
53
 
102
- ## Retro — one check before you finish
103
- Before your final report, answer one question honestly: did **this skill's own instructions** misfire
104
- this run — ambiguous wording, a misleading message, or an environment failure it should have warned
105
- about? (Process only — never the user's code or task; e.g. a teardown that left a port bound, or a
106
- Playwright output path aimed at the repo root.) If yes, file **one** lesson and move on; a clean run is
107
- silent (hard cap: one per run). It lands on the owning board (deduped) and is fixed only via a reviewed PR —
108
- never edit the skill live; the retro is advisory, so if the call fails, note it and continue. File through MMI:
54
+ If this skill's instructions misfired (process only, e.g. a teardown that left a port bound), file one lesson (one per run; if the call fails, note it and continue):
109
55
  `mmi-cli learning skill-lesson --skill stage --title "<what misfired>" --body "<what; evidence; proposed amendment>"`
@@ -47,3 +47,52 @@ or print the PAT.
47
47
  from an updated `.env.example` — it warns on stderr and keeps the stale file. Delete `.env` (or merge in the
48
48
  new keys by hand) when `.env.example` changes, then re-run `/stage`.
49
49
 
50
+
51
+
52
+ ## Detail moved from SKILL.md (verbatim)
53
+
54
+ The stage recipe is the **registry-derived default** for plumbing-free product repos: a `tenant-container`
55
+ repo that ships `docker-compose.yml` and has a Hub registry `portRange` needs **no committed config**.
56
+ `mmi-cli stage` derives a default compose stage — run `docker compose build --no-cache`, run `docker compose
57
+ up -d`, choose a free port from the registry range, and report the local URL (e.g. `http://127.0.0.1:3700/`).
58
+ The commands are shell-aware and `--apply` runs them cross-shell. Board, deploy, secret, and project registry
59
+ facts all live in the Hub registry — no repo-local control-plane file.
60
+
61
+ The JSON reports `source` (`derived` / `local` / `none`) and, when derived, the local `url`. `source: none`
62
+ means neither a usable local recipe nor a derivable default exists — the message names the missing fact
63
+ (deployModel, `docker-compose.yml`, or registry `portRange`; `.env.example` is **not** required — #2655).
64
+ On a `deployModel: vercel` repo the receipt also carries `preview` — the current branch's latest
65
+ Vercel **Preview** deployment (`state`, `url`) read from the GitHub Deployments the Vercel GitHub app
66
+ posts — and the text output ends with a `vercel preview (<branch>)` line. Report that URL as the branch
67
+ preview alongside the local stage; no `preview.deployment` means the branch was not pushed yet or the
68
+ Vercel GitHub app is not connected. There is no box stage on a vercel repo: `--live` refuses.
69
+ When the gap is a missing Hub registry `portRange`, the JSON receipt also carries `recovery` with
70
+ `mmi-cli stage port-range <owner/repo>` (#5539) — assign the block, then re-run `/stage`. That gap does not
71
+ mean the repo's Hub registry/org project setup is missing; it means local stage derivation cannot pick a
72
+ collision-safe port until META.portRange exists.
73
+
74
+ A stage is **per worktree**. The CLI force-stops only the previous stage **in this worktree**, runs
75
+ `stage.build`, starts `stage.up`, records `tmp/stage/state.json`, picks a free port from the registry
76
+ range (skipping ports reserved by sibling worktrees), and polls `stage.healthUrl` when configured.
77
+ Recipes run in Bash, including Git Bash on Windows. Use Bash syntax for build, up and teardown;
78
+ legacy cmd `set VAR=value` and PowerShell `$env:VAR` recipes must be converted to Bash or Node scripts.
79
+ Windows requires Git Bash or an absolute `SHELL` path to its `bash.exe`; there is no cmd fallback.
80
+ Use `--port` to pin a port when needed:
81
+
82
+ ```bash
83
+ mmi-cli stage run --apply
84
+ mmi-cli stage run --apply --port 5180
85
+ ```
86
+
87
+ A local stage is bound to the worktree that started it. Concurrent worktrees on one machine each keep
88
+ their own stage on separate ports — you do not need to stop one before starting another. For `jervaise`, continuity
89
+ keys on branch; use a distinct North Star slug per parallel grind or feature. Stage JSON/state records
90
+ the starting `cwd` plus git branch/commit when available.
91
+
92
+ Stop the stage when the work is done, before switching context, or before replacing it with another stage.
93
+ If the user clearly wants the preview to stay up, leave it running and report that. The next `/stage` also
94
+ stops the previous recorded stage before starting, so a stale server does not linger between runs. For the
95
+ registry-derived Docker Compose default, stop also runs the recorded compose teardown (`docker compose down`)
96
+ from the original stage working directory. Stop re-fetches the declared dev runtime secrets so that `down`
97
+ can interpolate the same file (`${POSTGRES_PASSWORD:?}`, `${PORT:?}`) — secrets never come from the state
98
+ file (#6351). A vault-native repo with no `.env` gets `PORT` defaulted to the bound stage port.