Skip to content

Commit 1baff71

Browse files
Jammy2211claude
authored andcommitted
feat(skills): /prm — the "PR, CI green, then merge" wrap-up door
Adds a composition skill for the sentence typed at the end of nearly every task. `/prm` resolves the feature PR (argument / current branch / the claimed task's `library-pr:` + `workspace-pr:` in Mind's active.md), judges CI over EVERY workflow run for the head sha and EVERY matrix leg inside it, merges in library-first order, then hands back to the ship skills' completion steps. It owns no gate of its own: it refuses on red, pending, conflicting, or an unmerged upstream library PR, fetches a failing job's log before GitHub purges the blob, and asks before closing the issue — typing /prm authorizes the merge, not the close. Pure `gh` (2.4.0-safe: no `gh search`), so it runs on the CLI, mobile Claude Code chat and Codex alike; the committed .claude/.codex discovery links are what make it register in web/cloud sessions. - skills/prm/{SKILL.md,prm.md,reference.md} — 127-line mandatory load - skills/COMMANDS.md: registered as a tier-4 composition door - AGENTS.md: named in the command-surface prose - ship_library / ship_workspace: point their merge step at /prm - regenerated committed discovery (also picks up the missing `sizing` links) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrgmL8WMK82GxQJjPL8ZCs
1 parent 52461b2 commit 1baff71

12 files changed

Lines changed: 258 additions & 1 deletion

File tree

.claude/commands/prm.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../skills/prm/prm.md

.claude/skills/prm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../skills/prm

.claude/skills/sizing

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../skills/sizing

.codex/skills/prm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../skills/prm

.codex/skills/sizing

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../skills/sizing

AGENTS.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,8 @@ via `/route`, and it routes to the right agent; normal usage never says
169169
"PyAutoBrain". A few commands are compositions rather than single agents:
170170
`/docs` and `/research` route through the dev-flow with their PyAutoMind
171171
work-type fixed (no dedicated conductor — added only on demonstrated need, never
172-
for symmetry); `/wake_up` composes sync + `/health` + `/hygiene`; `/brain
172+
for symmetry); `/wake_up` composes sync + `/health` + `/hygiene`; `/prm` composes the
173+
end-of-task wrap-up (CI green → merge → `ship_*` completion); `/brain
173174
<agent>` is the raw passthrough. Every command routes **through** the Brain;
174175
none replaces it.
175176

skills/COMMANDS.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,15 @@ call, so the Brain is not bypassed):
8585
local-only steps. Interactive/terminal only (the automated morning webhooks are
8686
separate).
8787

88+
- **`/prm`** — the wrap-up shortcut: *"PR, CI green, then merge"*. Watches a
89+
feature PR's checks until **every** workflow run and **every** matrix leg for
90+
the head sha is green, merges in library-first order, then hands back to the
91+
`ship_*` completion steps (Shipped comment, `lifecycle.py record`, cleanup).
92+
Three keystrokes for the sentence the human types at the end of nearly every
93+
task. Owns no gate of its own — it refuses on red/pending/conflicting and on an
94+
unmerged upstream library PR, and asks before closing the issue. Pure `gh`, so
95+
it runs on the CLI, mobile Claude Code chat and Codex alike.
96+
8897
**5. Maintenance doors** — periodic sweeps that reason about accumulated debris
8998
and then execute their own cleanup after per-bucket human confirmation. They own
9099
no agent, but unlike the composition doors they *mutate*, so each is

skills/prm/SKILL.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
name: prm
3+
description: The wrap-up shortcut — "PR, CI green, then merge". Watch a feature PR's checks until every workflow run and every matrix leg is green, then merge it (library-first gate honoured) and finish the ship — Mind record, issue comment, cleanup. Use when the user says merge this PR once CI is green, or types /prm. Runs anywhere gh is authenticated: CLI, mobile Claude Code chat, Codex.
4+
---
5+
6+
# /prm — PR, CI green, then merge
7+
8+
Follow [`prm.md`](prm.md) exactly; gh mechanics in [`reference.md`](reference.md).
9+
10+
Composition door — it owns no agent and re-derives nothing: the merge gates come
11+
from `/ship_library` / `/ship_workspace`, the CI verdict from GitHub Actions.
12+
Typing `/prm` **is** the merge authorization; the skill still refuses to merge on
13+
red, pending, conflicting, or an unmerged upstream library PR, and still asks
14+
before closing the issue.

skills/prm/prm.md

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# /prm — PR, CI green, then merge
2+
3+
The end-of-task shortcut for "PR CI green then merge" — three keystrokes instead
4+
of the sentence. It watches the feature PR's checks, merges the moment they are
5+
genuinely green, and finishes the ship (Mind record, issue comment, cleanup).
6+
7+
Shared routing context: `PyAutoBrain/skills/COMMANDS.md`.
8+
gh mechanics + snippets: [`reference.md`](reference.md).
9+
10+
## Principle: compose, don't recompute
11+
12+
Every rule here already has an owner. The merge gates belong to
13+
[`../ship_library/ship_library.md`](../ship_library/ship_library.md) and
14+
[`../ship_workspace/ship_workspace.md`](../ship_workspace/ship_workspace.md)
15+
(library-first gate, issue completion, Mind state); the verdict belongs to GitHub
16+
Actions. `/prm` only sequences them. It never re-runs the readiness gate, never
17+
edits code to make a check pass, and never opens a PR — that is `/ship_*`.
18+
19+
## Usage
20+
21+
```
22+
/prm # the PR for the current branch / current task
23+
/prm 380 # PR #380 in the current or inferred repo
24+
/prm PyAutoArray#42 # or Jammy2211/PyAutoArray#42, or the full PR URL
25+
/prm --no-wait # judge CI once and report; merge only if already green
26+
```
27+
28+
## Environment: runs anywhere gh is authenticated
29+
30+
Local CLI, mobile Claude Code chat, and Codex all work — every step is `gh`, no
31+
checkout required. Detect which you are in:
32+
33+
- **Local**`$PYAUTO_ROOT` (default `~/Code/PyAutoLabs`) holds the sibling
34+
repos. Branch detection and post-merge worktree cleanup are available.
35+
- **Remote (mobile/codex)** — no multi-repo checkout. Resolve the PR from the
36+
argument or by listing candidates (below), and **skip the local-only cleanup**
37+
with a one-line note. Never `cd` into a repo that isn't there.
38+
39+
## The routine
40+
41+
### 1. Resolve the target PR(s)
42+
43+
In order: explicit argument → current branch (`gh pr view --json` in the repo you
44+
are in) → the claimed task in `PyAutoMind/active.md` (its `library-pr:` /
45+
`workspace-pr:` entries; on mobile read it with `gh api`) → `gh pr list` across
46+
the claimed repos. If more than one candidate survives, **list them numbered and
47+
ask once** — never guess which PR to merge. Report each target as
48+
`owner/repo#N — title — branch` before doing anything.
49+
50+
A task that shipped both a library and a workspace PR is **one** `/prm` run over
51+
both, merged in gate order (step 4).
52+
53+
### 2. Judge CI honestly — every run, every leg
54+
55+
A head sha triggers **two** runs of each workflow (`push` and `pull_request`),
56+
each with its own matrix legs. One green row is not "CI green". Enumerate every
57+
run for the head sha and every job inside it (snippets in `reference.md`), and
58+
treat any run that is not `completed` as *not ready* — not "green so far".
59+
`mergeStateStatus=UNSTABLE` is the tell that something is still pending or red.
60+
61+
Also read `mergeable` and `mergeStateStatus`: `CONFLICTING` / `BEHIND` /
62+
`BLOCKED` stops the run with the reason, whatever the checks say.
63+
64+
### 3. Wait, or stop
65+
66+
- **Pending** — poll every ~90s, one compact line per poll (`3/4 legs done`).
67+
Cap at ~30 min; then report where it stands and stop rather than spinning.
68+
Under `--no-wait`, skip the loop: report and stop.
69+
- **Red****fetch the failing job's log immediately** (GitHub purges the blob;
70+
once purged you can never name what broke), quote the failing step, and stop.
71+
Do not merge, do not re-run, do not "wait for the flake to pass". Offer the
72+
next door: `/bug` for a real failure, or a named re-run if the user judges it a
73+
known flake — their call, not yours.
74+
- **No checks configured** — say so explicitly and ask before merging.
75+
76+
### 4. Merge
77+
78+
Green on every leg → merge, in this order:
79+
80+
1. **Library PR first.** The workspace PR may not merge until its upstream
81+
library PR is `MERGED` — the library-first gate
82+
([`../ship_workspace/reference.md`](../ship_workspace/reference.md)). Refuse
83+
otherwise; there is no `--auto`-flag workaround.
84+
2. `gh pr merge <n> --merge` per PR (add `-R owner/repo` when you have no
85+
checkout), then confirm the resulting state is `MERGED` — a queued or
86+
auto-merge state is not a merge. Do **not** pass `--delete-branch`: it deletes
87+
the local branch too, which fails or orphans a task worktree. Branch deletion
88+
belongs to the post-merge cleanup in step 5.
89+
90+
Never force, never override a protection, never rewrite history. If a merge is
91+
refused by GitHub, report the reason verbatim and stop.
92+
93+
### 5. Finish the ship
94+
95+
Hand back to the ship skills' completion steps — do not re-invent them:
96+
97+
- Post the "Shipped" comment on the issue (templates: `../ship_library/reference.md`
98+
→ "Issue comments + Mind state").
99+
- Write the dated completion record — `PyAutoMind/scripts/lifecycle.py record`
100+
(also refreshes the index and prunes the `active.md` entry) — and push Mind.
101+
- **Ask before closing the issue.** Merging is what `/prm` was typed for; closing
102+
is a separate decision the human makes.
103+
- **Local only:** offer the post-merge cleanup (worktree removal, local branch
104+
deletion) per the ship skills; on mobile/codex note it as still pending.
105+
106+
## Notes
107+
108+
- `/prm` merges an **existing** PR. No PR yet → `/ship_library` or
109+
`/ship_workspace` first; `/prm` will say so rather than opening one.
110+
- It never bypasses the Heart readiness gate — that gate ran at ship time, and a
111+
red Heart is not something a merge shortcut may re-judge.
112+
- Under a `--auto` workflow run, merge stays human: `/prm` is a human-typed door
113+
and is never invoked by the autonomous queue.

skills/prm/reference.md

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# /prm — reference
2+
3+
Mechanics for [`prm.md`](prm.md). Lazy-loaded: read it when you actually run the
4+
step. `gh` in this workspace is old (2.4.0) — it has **no `gh search`** and some
5+
newer flags are absent, so everything below sticks to `gh pr` / `gh api`.
6+
7+
## Resolving the PR
8+
9+
```bash
10+
# From the current branch (local checkout)
11+
gh pr view --json number,url,title,headRefName,state,labels
12+
13+
# Explicit target, no checkout needed (mobile / Codex)
14+
gh pr view <n> -R <owner>/<repo> --json number,url,title,headRefName,state
15+
16+
# Candidates when nothing is claimed: open PRs in one repo
17+
gh pr list -R <owner>/<repo> --state open --json number,title,headRefName,labels
18+
19+
# The claimed task's PRs, read straight from Mind without a checkout
20+
gh api repos/PyAutoLabs/PyAutoMind/contents/active.md --jq '.content' \
21+
| base64 -d | grep -iE 'library-pr:|workspace-pr:|issue:| - '
22+
```
23+
24+
Feature PRs from `/ship_*` carry the `pending-release` label — a useful filter,
25+
never a merge blocker (the label describes the release state of the change, not
26+
the mergeability of the PR).
27+
28+
## CI: every run, every leg
29+
30+
A single head sha gets **two** runs of each workflow (`push` + `pull_request`,
31+
created seconds apart), each with its own matrix legs (e.g. py3.12 + py3.13). So
32+
one `success` row is 1 of 4 signals. Enumerate all of them:
33+
34+
```bash
35+
repo=<owner>/<repo>; pr=<n>
36+
sha=$(gh api repos/$repo/pulls/$pr --jq '.head.sha')
37+
38+
# Every run for the sha
39+
gh api "repos/$repo/actions/runs?head_sha=$sha" \
40+
--jq '.workflow_runs[] | "\(.name) [\(.event)]: \(.status)/\(.conclusion)"'
41+
42+
# Every job within every run
43+
for run in $(gh api "repos/$repo/actions/runs?head_sha=$sha" --jq '.workflow_runs[].id'); do
44+
gh api repos/$repo/actions/runs/$run/jobs --jq '.jobs[] | " \(.name)=\(.conclusion)"'
45+
done
46+
47+
# Not-ready test: non-zero means keep waiting, NOT "green so far"
48+
gh api "repos/$repo/actions/runs?head_sha=$sha" \
49+
--jq '[.workflow_runs[] | select(.status!="completed")] | length'
50+
```
51+
52+
Cross-check the PR's own view — mergeability is independent of the checks:
53+
54+
```bash
55+
gh pr view $pr -R $repo --json state,mergeable,mergeStateStatus,reviewDecision,statusCheckRollup
56+
```
57+
58+
- `mergeStateStatus`: `CLEAN` → mergeable · `UNSTABLE` → a check is pending or
59+
failed · `BLOCKED` → protection rule / review missing · `BEHIND` → base moved ·
60+
`DIRTY` → conflicts. Only `CLEAN` merges without a human decision.
61+
- `mergeable`: `CONFLICTING` stops the run regardless of check colour.
62+
63+
An empty run list is not green — it means no workflow fired for that sha (a
64+
docs-only path filter, a skipped event, or Actions being down). Say which.
65+
66+
## A red leg: grab the log *now*
67+
68+
GitHub purges job logs; once purged, `BlobNotFound` leaves the failure
69+
unnameable forever. Fetch before reporting:
70+
71+
```bash
72+
job=$(gh api repos/$repo/actions/runs/$run/jobs --jq '.jobs[] | select(.conclusion=="failure") | .id')
73+
gh api repos/$repo/actions/jobs/$job/logs > /tmp/prm-failing-job.log
74+
grep -nE 'FAILED|Error|assert|Traceback' /tmp/prm-failing-job.log | tail -40
75+
```
76+
77+
Report the failing job name, the failing step, and the quoted error. A failure
78+
that matches a known flake is still a failure — the user decides whether to
79+
re-run; `/prm` does not decide that for them.
80+
81+
## Merging in gate order
82+
83+
```bash
84+
# 1. library PR
85+
gh pr merge <lib_n> -R <owner>/<lib_repo> --merge
86+
gh pr view <lib_n> -R <owner>/<lib_repo> --json state --jq '.state' # must print MERGED
87+
88+
# 2. only then the workspace PR
89+
gh pr view <ws_n> -R <owner>/<ws_repo> --json body --jq '.body' | grep -iE 'PyAuto[A-Za-z]+/pull/[0-9]+'
90+
gh pr merge <ws_n> -R <owner>/<ws_repo> --merge
91+
```
92+
93+
The library-first gate is `../ship_workspace/reference.md` → "Library-first merge
94+
gate": a workspace PR linked to an upstream library PR may only merge once that
95+
PR reads `MERGED`. There is no workaround — not `--auto`, not `--admin`.
96+
97+
## Finishing
98+
99+
Completion is the ship skills' contract, unchanged:
100+
101+
- "Shipped" comment template → `../ship_library/reference.md` → "Issue comments +
102+
Mind state".
103+
- `python3 PyAutoMind/scripts/lifecycle.py record --prompt <bare-filename>`
104+
the argument is the **bare prompt filename**, not a path — then commit and push
105+
Mind (on `main`, and check that first).
106+
- Closing the issue is a separate human decision: ask, don't assume.
107+
- Local post-merge cleanup (worktree removal, local + remote branch deletion) →
108+
the `ship_library` / `ship_workspace` cleanup sections. On mobile/Codex, say
109+
it is still pending rather than pretending it ran.

0 commit comments

Comments
 (0)