@mutmutco/codex-plugin 4.3.5 → 4.3.7

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,224 +5,76 @@ description: Promote development to a release-candidate branch.
5
5
 
6
6
  **Host-native invocation:** Claude `/mmi:rcand` · Codex `$mmi:rcand` · Cursor `/rcand` · jervcode `/rcand` · 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:** `/rcand minor`, `/rcand major`, or `/rcand patch` — a bare bump word, never a `--flag` or env var. No argument → `patch`; declare `minor` or `major` for a planned feature cycle.
9
+
8
10
  # /rcand — promote development to release-candidate
9
11
 
10
- Merge `development → rc`, tag `vX.Y.0-rc.N`, push (the GitHub authority gate), then dispatch the rc deploy
11
- path. **Train-authority gated (D14):** the repo's project-admin or the master. Direct-track repos
12
- (product repos with `releaseTrack: direct`, plus MMI-Hub via the `isHubControlRepo` special-case) are the exception: they skip rc and `/rcand` refuses there;
13
- run `/release` from `development` instead. The board needs no touch
14
- here — items reach `Done` when their PR merges to `development` (native close → Done); `rc` is a deploy
15
- stage, not a lane.
12
+ Merge `development → rc`, tag `vX.Y.0-rc.N`, push the tag first, wait for the required checks, push the protected `rc` (the GitHub authority gate), then start the rc deploy. `rc` is a deploy stage, not a lane, and it is ephemeral: `/rcand` creates its runtime, `/release` ships exactly what is on `rc` and retires it. Train-authority gated: the repo's project-admin or the master. The board needs no touch: items reached `Done` when their PRs merged to `development`.
16
13
 
17
- Authority is **structural + server-checked**: step 0 asks the Hub (`mmi-cli oracle org access role`), and step 4
18
- pushes to the protected `rc` branch, whose per-repo allowlist carries the same people (master + that repo's
19
- project-admins). No `.env` role marker. Gate ordering: the tag lands the rc SHA
20
- for checks, and every deploy side-effect waits until the protected `rc` push accepts that checked SHA.
14
+ ## Lane
21
15
 
22
- ## Step 0 confirm lane + train-authority probe (server-side, D14)
16
+ Read META and name the lane before describing or executing anything:
23
17
 
24
- Before describing or executing any rc promotion, read the project META and name the lane (#5244):
25
18
  ```bash
26
19
  mmi-cli oracle org project get {owner}/{repo} --json
27
20
  ```
28
- `releaseTrack: direct` (product repos with explicit META, plus MMI-Hub via the `isHubControlRepo`
29
- special-case) means **no `rc` branch** — stop here and run `/release` from `development` instead.
30
- `mmi-cli devops rcand --apply` refuses direct-track after its own preflight; do not reach Step 1 on direct.
31
- Stop on an unreadable META record; never infer the lane from branch names or the existence of a stale `rc` ref.
32
21
 
33
- Then probe train authority:
22
+ `releaseTrack: direct` (product repos with explicit META, plus MMI-Hub) has no `rc`: stop here and run `/release` from `development`; `rcand --apply` refuses direct-track at its own preflight. Full-track runs from `development`, and a full-track repo may skip rc entirely with `/release --dev`. Unreadable META → stop; never infer the lane from branch names, a stale `rc` ref, or release history.
23
+
24
+ Authority is structural and server-checked: the doctor asks the Hub, and the protected `rc` push allowlist carries the same people (master + that repo's project-admins). Gate ordering: the tag lands the rc SHA for checks, and every deploy side-effect waits until the protected `rc` push accepts that checked SHA.
25
+
26
+ ## Preflight
34
27
 
35
28
  ```bash
36
- mmi-cli oracle org access role {owner}/{repo} --json # Hub-verified: { role, train }
37
- ```
38
- `train: false` — or ANY error (fail closed) → stop: a product repo's train belongs to that repo's
39
- **project-admin** (repo write + registry `projectAdmins`) or the master. When `project-admin` + `train`,
40
- proceed — do not redirect to the master (`AGENTS.md` § Authority). Errors → fix `gh auth` first,
41
- never proceed unverified.
42
-
43
- **CLI freshness (#2562):** check the running CLI is current BEFORE step 1 — `mmi-cli devops rcand --apply` refuses
44
- a stale CLI at its own step 0 (pre-mutation), but the manual steps below mutate local state earlier, so a
45
- stale CLI discovered late reads as a mid-train abort. When another train just published a new CLI (e.g. a
46
- Hub release), run `mmi-cli doctor --no-repo-writes` first. A stale-CLI refusal itself changes
47
- nothing; after updating, the rerun resumes any partial train state safely.
48
-
49
- **Run from the checkout that already has `development` — never a fresh isolated worktree (#2770).** This is
50
- a shared-branch train operation (merge/tag/push against origin's protected `rc`), not an isolated
51
- feature-branch edit. A background-job harness that isolates every task into a fresh worktree by default
52
- breaks here: `git worktree`s cannot have the same branch checked out twice, so a fresh worktree can't check
53
- out `development` while the primary checkout already has it. Exit any such worktree first and run from the
54
- primary checkout.
55
-
56
- ## Step 1 — development ahead of rc?
57
-
58
- Preconditions: on `development`, clean tree. The clean-tree check rejects UNTRACKED scratch too, not just
59
- modified tracked files — if `--apply` stops with `working tree must be clean before …`, run `git status
60
- --porcelain` on the paths it named and read both columns. For `?? .jerv/` / `?? .pi/` (org-managed agent
61
- scratch), run `mmi-cli doctor --apply` to write the managed `.gitignore` block — do not hand-edit
62
- `.gitignore` (#5409). The train CLI already ignores those live-session dirs (#5397); doctor is the durable
63
- heal so `git status` stays clean. For other `??` scratch, gitignore it (or move it to a gitignored path
64
- like `tmp/`). Treat every tracked state except exactly ` M` as real work to commit or stash;
65
- for ` M`, discard only when `git diff HEAD --numstat -- <paths>` is empty, and only with
66
- `git checkout -- <paths>`. Plain `git diff --numstat` prints nothing for a merely staged change, so it
67
- cannot tell real work from line-ending churn (#1472, #4004).
68
- ```bash
69
- git fetch origin
70
- git rev-list --count origin/rc..origin/development
29
+ mmi-cli devops train doctor --lane rcand --heal --json
71
30
  ```
72
- Dirty → stop. Count `0` → stop ("nothing to promote"). `>0` → capture the commit list for the report.
73
31
 
74
- > Verifying CLI/plugin-health before a train? Use `mmi-cli doctor --no-repo-writes`: it runs the
75
- > env/plugin repairs but never mutates the working tree, so a pending org-managed `.gitignore` repair is
76
- > reported with its follow-up command instead of dirtying the product checkout right before promotion.
77
- > Apply that follow-up after the train.
32
+ 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.
78
33
 
79
- ## Step 1b — registry + rc secret-name preflight
34
+ ## Apply
35
+
36
+ Run from the primary checkout on `development`. Export the bump intent once from the bare argument — `MMI_BUMP_INTENT=minor|major|patch`, unset → `patch` — and keep it exported through the following `/release`; `MMI_RELEASE_VERSION=X.Y.Z` opens an exact cycle for both `/rcand` and `/release` only when the tag math cannot derive it.
80
37
 
81
- Resolve the project META first; its `deployModel` decides the deploy path. `tenant-container` repos use the
82
- central tenant deployer and therefore need DEPLOY# coords. Serverless, `registry-publish`, and
83
- `solo-container` repos deploy from their own workflow, so do **not** dispatch `tenant-deploy.yml` for them.
84
- Direct-track repos (`releaseTrack: direct`, e.g. MMI-Hub) have no rc candidate path; `mmi-cli devops rcand --apply`
85
- refuses after this preflight.
86
- Verify META + required SSM secret names before touching `rc`:
87
38
  ```bash
88
- mmi-cli oracle org project get {owner}/{repo}
89
- mmi-cli vault secrets preflight --stage rc --repo {owner}/{repo}
39
+ r=$(mktemp tmp/rcand-receipt.XXXXXX)
40
+ mmi-cli devops rcand --apply --json --out "$r"
90
41
  ```
91
- Missing META or secret names → stop and repair the registry/secrets first.
92
42
 
93
- ## Step 2 merge development rc (never force)
43
+ Add `--watch` to block on the rc deploy; without it the deploy runs while you report and its verdict is
44
+ collected in Verify — verification is never skipped, only un-blocked. The one command fetches, merges `development → rc` (version-manifest conflicts resolve to the `development` side; any other conflicted path refuses), tags the next `-rc.N` (re-run-safe), pushes the tag first, discovers and waits for the required contexts on `rc`, pushes `rc`, and starts the deploy by model — `tenant-deploy.yml` centrally (keyless) for tenant-container; nothing for serverless, registry-publish or solo-container, whose own push-triggered workflow deploys. Never `git push` rc by hand, never force, never commit to `rc` outside that merge.
94
45
 
95
- ```bash
96
- git checkout rc
97
- git pull --ff-only origin rc
98
- git merge development --no-edit
99
- ```
100
- Conflict → `git merge --abort`, report paths, resolve on `development`, re-run.
46
+ ## Verify
101
47
 
102
- **Exception version manifests only.** When every conflicted path is a version manifest
103
- (`package.json` · `package-lock.json`), the incoming `development` side carries the org truth — the
104
- `/release` version fold (#976) bumps manifests on `main` and the release back-merge brings them to
105
- `development`, so `development` is always the newer side. Take it deterministically and continue (no
106
- hand-resolution, no abort):
107
- ```bash
108
- git checkout --theirs package.json package-lock.json && git add package.json package-lock.json
109
- git commit --no-edit
110
- ```
111
- Any other conflicted path in the same merge → abort as above. Tag (step 3) only AFTER the merge commit
112
- exists — a tag minted before the conflict resolution points at the wrong SHA.
48
+ Read the receipt at `$r`, never the exit code (exit `2` = promoted with the deploy unresolved; exit `1` = the deploy failed after promotion):
113
49
 
114
- ## Step 3 tag the rc
50
+ - `promoted``true` even when the deploy failed; promotion and deploy are separate outcomes.
51
+ - `tag` — the minted `vX.Y.0-rc.N`; `resumeNote` says an existing rc tag was resumed rather than minted.
52
+ - `deployStatus` — `pending` is never terminal. `runId` / `runUrl` / `workflowRuns` name the run: the dispatched `tenant-deploy.yml` for tenant-container, the auto-fired `deploy.yml` for serverless. Watch each non-success entry to conclusion: `gh run watch <run-id> --repo {owner}/{repo} --exit-status`.
53
+ - `environments` — the rc edge domain(s) from META, the env URL the report names.
54
+ - `checkout`, `localSync`, `ledger` — `returned` and phases-green, or the named reason you are still on `rc`.
115
55
 
116
- The shared helper derives the next rc tag from existing tags (re-run-safe `-rc.N` increments). `rc` mode
117
- reads `MMI_BUMP_INTENT`; unset it resolves to `patch` (#4929) — declare `minor`/`major` explicitly for a
118
- planned feature cycle:
119
- ```bash
120
- TAG=$(MMI_BUMP_INTENT=minor node scripts/next-version.mjs rc) # -> vX.Y.0-rc.N
121
- git tag "$TAG"
122
- ```
56
+ Then confirm origin: `git rev-list --count origin/rc..origin/development` is `0` and the rc tag is on origin. There is no GitHub Release at rc; the bounded Latest read belongs to `/release`. A red deploy after a green promotion is a deploy retry of the existing rc ref, never a re-tag or re-merge.
123
57
 
124
- ## Step 4 — push tag, wait for the REQUIRED checks, then push rc (the gate)
58
+ ## Recovery
125
59
 
126
- Required status checks are **per-repo branch protection, not a fixed list**MMI-Hub requires `cli` ·
127
- `infra` · `docs`, but a product repo may require different contexts or none at all (#1045). A fresh merge
128
- SHA has no check-runs yet, so when checks ARE required, pushing the branch *first* is structurally rejected
129
- until CI catches up (it then succeeds on a retry — avoidable noise). Push the **tag first**: it lands the
130
- SHA and triggers the repo's CI without touching the protected branch ref. Then probe what `rc` actually
131
- requires and wait only for those contexts.
60
+ Every refusal and every doctor blocker names its `code` and `anchor`: open `docs/Guides/train-troubleshooting.md#<code>` and follow that entry, nothing else. A candidate whose immutable rc tag passed policy but whose `origin/rc` was not pushed continues with `mmi-cli devops rcand --resume --watch --json --out <fresh-receipt>` the only continuation form: `--resume` never re-tags, and `--apply` mints the next `-rc.N` for a new cut only. A refused train is a stop (Merge floor): fix the cause on `development` through a normal PR, then rerun; a rejected protected push leaves the local tag for an authorized rerun, never a force.
132
61
 
133
- **Preflight (#5428):** before the immutable tag, `mmi-cli devops rcand --apply` inspects each required
134
- context's workflow `on:` triggers. If a required context (e.g. `py-gate` / `web-gate`) is pull_request-only
135
- and cannot fire on a tag push, the train **refuses before tagging** with a teaching error — never publish a
136
- candidate tag then wait on an event that cannot fire. Fix the gate to also trigger on `push: tags: ['v*']`
137
- (see MMI-Hub `gate.yml`), or remove those contexts from the rc ruleset (`ci audit` / reconcile).
62
+ ## Merge floor
138
63
 
139
- ```bash
140
- git push origin "vX.Y.0-rc.N" # lands the SHA + triggers the repo's CI
141
- SHA=$(git rev-parse rc)
142
- # discover the REQUIRED contexts on rc (classic protection + rulesets; 404 = none from that source):
143
- gh api repos/{owner}/{repo}/branches/rc/protection/required_status_checks --jq '[.contexts[]]'
144
- gh api repos/{owner}/{repo}/rules/branches/rc \
145
- --jq '[.[]|select(.type=="required_status_checks")|.parameters.required_status_checks[].context]'
146
- # ZERO required contexts -> push rc immediately (the GitHub push gate is the backstop).
147
- # Otherwise poll until every required context is "success" on $SHA — never a hard-coded list, and bound
148
- # the wait (~10 min): on timeout, stop with a clear failure naming the pending/failed contexts.
149
- gh api repos/{owner}/{repo}/commits/$SHA/check-runs \
150
- --jq '[.check_runs[]|{name:.name,conclusion:.conclusion}]'
151
- git push origin rc
152
- ```
153
- (`mmi-cli devops rcand --apply` performs this discovery + bounded wait itself.)
154
- Rejected (protected / not a bypass actor) → stop, nothing deployed, no board writes; leave the local tag
155
- for an authorized re-push, never force. Non-fast-forward → re-pull, re-run from step 1.
64
+ - **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.
65
+ - **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.
66
+ - **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.
67
+ - **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.
68
+ - **Tag and Release deletion are deletion-class:** the authorized human's explicit per-turn go, named in the guide entry that prescribes it.
156
69
 
157
- ## Step 5 — rc deploy (model-specific, non-blocking)
70
+ ## Report
158
71
 
159
- For `tenant-container` repos, dispatch the Hub **tenant-deploy.yml** workflow for rc (OIDC, keyless). Don't
160
- deploy by hand, and **don't block on it** — start the watch as a background task and move straight to
161
- Step 6:
162
- ```bash
163
- gh workflow run tenant-deploy.yml --repo mutmutco/MMI-Hub \
164
- -f slug={slug} -f repo={owner}/{repo} -f ref=rc -f stage=rc
165
- gh run watch "$(gh run list --workflow tenant-deploy.yml --limit 1 --json databaseId -q '.[0].databaseId')" \
166
- --exit-status # run this in the BACKGROUND (Bash run_in_background) — capture its URL for the report
167
- ```
72
+ 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.
168
73
 
169
- For serverless repos, do **not** dispatch `tenant-deploy.yml`: their own push-triggered workflow owns the
170
- rc deploy.
74
+ ## Retro
171
75
 
172
- `mmi-cli devops rcand --apply --json` returns the relevant run `runId` + `runUrl` (alongside `deployStatus`), so
173
- you never hand-correlate Actions. For tenant-container repos, that is the dispatched `tenant-deploy.yml`
174
- run. For Hub serverless, that is the auto-fired `deploy.yml` run from the protected `rc` push. Add
175
- `--watch` to block on the run and have `deployStatus` resolve to `success`/`failure`; `promoted: true`
176
- stays set either way — a failed **deploy** never undoes the **promotion**.
76
+ 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 Hub 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.
177
77
 
178
- The deploy runs while you report. When the background watch returns, surface the outcome:
179
- **green** → note the run + rc env URL; **red** → report plainly. The rc tag is already pushed, so the
180
- correct next action is a **deploy retry of the existing rc ref** after the Hub runtime is repaired — never
181
- a re-tag/re-merge:
182
- ```bash
183
- mmi-cli devops runtime tenant redeploy {owner}/{repo} rc --watch # re-dispatch tenant-deploy.yml for the promoted rc
184
- ```
185
- **Branch the recovery by failure class — a bare redeploy is NOT always the whole recovery.** For **exit 78**
186
- (`compose is fileless but DEPLOY#<stage>.noEnvFile is not true`) a plain redeploy fails identically: the
187
- box's baked `/opt/mmi-control/<slug>.sh` still carries the stale `noEnvFile`, so it writes no `.env` while
188
- the fileless compose expects passthrough. Fix the registry AND re-render the box control script first, then
189
- redeploy (`set-deploy` alone is not enough — the reconcile is the discoverable-but-non-obvious middle step):
190
78
  ```bash
191
- mmi-cli oracle org project set-deploy {owner}/{repo} --stage rc --no-env-file true # register the DEPLOY#rc fileless flag
192
- mmi-cli devops runtime tenant reconcile {owner}/{repo} rc --watch # Hub-authorized; project-admin needs no MMI-Hub Actions access
193
- mmi-cli devops runtime tenant redeploy {owner}/{repo} rc --watch # NOW the redeploy reads the corrected control script
194
- mmi-cli devops runtime tenant control {owner}/{repo} rc verify-broker --watch # value-free broker proof for runtime consumers
79
+ mmi-cli learning skill-lesson --skill rcand --title "<what misfired>" --body "<what; evidence; proposed amendment>"
195
80
  ```
196
- The reconcile must finish green before redeploy starts; `--watch` enforces that ordering. A failed rc
197
- dispatch stays fail-loud — rc is ephemeral and re-runnable, so the redeploy (after the reconcile step above
198
- for an exit-78 fileless mismatch) re-runs it.
199
-
200
- ## Step 6 — report
201
-
202
- Version `vX.Y.0-rc.N` · merged commits · rc deploy run + env URL.
203
-
204
- ## Notes
205
-
206
- - `rc` is NOT prod. Shipping `rc → main → prod` is `/release` (ships exactly what's on `rc`; never pulls
207
- `development`). Never force-push `rc`; never commit to `rc` outside the step-2 merge.
208
- - `rc` is **ephemeral**: `/rcand` creates the rc runtime, `/release` retires it after a confirmed prod
209
- deploy. A full-track repo can skip rc entirely with `/release --dev` (`development → main`); `/hotfix`
210
- always skips rc (it cherry-picks `development → main` directly).
211
- - **MAJOR / exact-target cycle:** for a release the tag math can't derive (a MAJOR like `2.0.0`, or skipping
212
- a version already on npm), export `MMI_RELEASE_VERSION=X.Y.Z` before `/rcand` — `next-version.mjs rc` then
213
- opens that exact cycle (validated to move strictly forward). Keep it exported through `/release`.
214
- - **`MMI_BUMP_INTENT=major|minor|patch` selects the increment for `next-version.mjs rc`** (Step 3); absent,
215
- it defaults to `patch` (#4929). **Export it for the whole run:** `mmi-cli devops rcand --apply` resolves
216
- the intent itself with the same default, so a one-off prefix on the `next-version.mjs` command leaves the
217
- apply deriving a different increment than the tag you minted. This is
218
- a different knob from `MMI_RELEASE_VERSION` above: `MMI_BUMP_INTENT` picks the increment,
219
- `MMI_RELEASE_VERSION` overrides the computed version entirely and is for the exceptional case only.
220
-
221
- ## Retro — one check before you finish
222
- Before your final report, answer one question honestly: did **this skill's own instructions** misfire
223
- this run — ambiguous wording, a misleading message, or an environment failure it should have warned
224
- about? (Process only — never the user's code or task; e.g. a misleading authority or gate message, or an
225
- ambiguous tag or push-order step.) If yes, file **one** lesson and move on; a clean run is silent (hard
226
- cap: one per run). It lands on the Hub board (deduped) and is fixed only via a reviewed PR — never edit
227
- the skill live; the retro is advisory, so if the call fails, note it and continue:
228
- `mmi-cli learning skill-lesson --skill rcand --title "<what misfired>" --body "<what; evidence; proposed amendment>"`