Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/commands/prm.md
1 change: 1 addition & 0 deletions .claude/skills/prm
1 change: 1 addition & 0 deletions .claude/skills/sizing
1 change: 1 addition & 0 deletions .codex/skills/prm
1 change: 1 addition & 0 deletions .codex/skills/sizing
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ via `/route`, and it routes to the right agent; normal usage never says
"PyAutoBrain". A few commands are compositions rather than single agents:
`/docs` and `/research` route through the dev-flow with their PyAutoMind
work-type fixed (no dedicated conductor — added only on demonstrated need, never
for symmetry); `/wake_up` composes sync + `/health` + `/hygiene`; `/brain
for symmetry); `/wake_up` composes sync + `/health` + `/hygiene`; `/prm` composes the
end-of-task wrap-up (CI green → merge → `ship_*` completion); `/brain
<agent>` is the raw passthrough. Every command routes **through** the Brain;
none replaces it.

Expand Down
9 changes: 9 additions & 0 deletions skills/COMMANDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,15 @@ call, so the Brain is not bypassed):
local-only steps. Interactive/terminal only (the automated morning webhooks are
separate).

- **`/prm`** — the wrap-up shortcut: *"PR, CI green, then merge"*. Watches a
feature PR's checks until **every** workflow run and **every** matrix leg for
the head sha is green, merges in library-first order, then hands back to the
`ship_*` completion steps (Shipped comment, `lifecycle.py record`, cleanup).
Three keystrokes for the sentence the human types at the end of nearly every
task. Owns no gate of its own — it refuses on red/pending/conflicting and on an
unmerged upstream library PR, and asks before closing the issue. Pure `gh`, so
it runs on the CLI, mobile Claude Code chat and Codex alike.

**5. Maintenance doors** — periodic sweeps that reason about accumulated debris
and then execute their own cleanup after per-bucket human confirmation. They own
no agent, but unlike the composition doors they *mutate*, so each is
Expand Down
14 changes: 14 additions & 0 deletions skills/prm/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
name: prm
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.
---

# /prm — PR, CI green, then merge

Follow [`prm.md`](prm.md) exactly; gh mechanics in [`reference.md`](reference.md).

Composition door — it owns no agent and re-derives nothing: the merge gates come
from `/ship_library` / `/ship_workspace`, the CI verdict from GitHub Actions.
Typing `/prm` **is** the merge authorization; the skill still refuses to merge on
red, pending, conflicting, or an unmerged upstream library PR, and still asks
before closing the issue.
113 changes: 113 additions & 0 deletions skills/prm/prm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# /prm — PR, CI green, then merge

The end-of-task shortcut for "PR CI green then merge" — three keystrokes instead
of the sentence. It watches the feature PR's checks, merges the moment they are
genuinely green, and finishes the ship (Mind record, issue comment, cleanup).

Shared routing context: `PyAutoBrain/skills/COMMANDS.md`.
gh mechanics + snippets: [`reference.md`](reference.md).

## Principle: compose, don't recompute

Every rule here already has an owner. The merge gates belong to
[`../ship_library/ship_library.md`](../ship_library/ship_library.md) and
[`../ship_workspace/ship_workspace.md`](../ship_workspace/ship_workspace.md)
(library-first gate, issue completion, Mind state); the verdict belongs to GitHub
Actions. `/prm` only sequences them. It never re-runs the readiness gate, never
edits code to make a check pass, and never opens a PR — that is `/ship_*`.

## Usage

```
/prm # the PR for the current branch / current task
/prm 380 # PR #380 in the current or inferred repo
/prm PyAutoArray#42 # or Jammy2211/PyAutoArray#42, or the full PR URL
/prm --no-wait # judge CI once and report; merge only if already green
```

## Environment: runs anywhere gh is authenticated

Local CLI, mobile Claude Code chat, and Codex all work — every step is `gh`, no
checkout required. Detect which you are in:

- **Local** — `$PYAUTO_ROOT` (default `~/Code/PyAutoLabs`) holds the sibling
repos. Branch detection and post-merge worktree cleanup are available.
- **Remote (mobile/codex)** — no multi-repo checkout. Resolve the PR from the
argument or by listing candidates (below), and **skip the local-only cleanup**
with a one-line note. Never `cd` into a repo that isn't there.

## The routine

### 1. Resolve the target PR(s)

In order: explicit argument → current branch (`gh pr view --json` in the repo you
are in) → the claimed task in `PyAutoMind/active.md` (its `library-pr:` /
`workspace-pr:` entries; on mobile read it with `gh api`) → `gh pr list` across
the claimed repos. If more than one candidate survives, **list them numbered and
ask once** — never guess which PR to merge. Report each target as
`owner/repo#N — title — branch` before doing anything.

A task that shipped both a library and a workspace PR is **one** `/prm` run over
both, merged in gate order (step 4).

### 2. Judge CI honestly — every run, every leg

A head sha triggers **two** runs of each workflow (`push` and `pull_request`),
each with its own matrix legs. One green row is not "CI green". Enumerate every
run for the head sha and every job inside it (snippets in `reference.md`), and
treat any run that is not `completed` as *not ready* — not "green so far".
`mergeStateStatus=UNSTABLE` is the tell that something is still pending or red.

Also read `mergeable` and `mergeStateStatus`: `CONFLICTING` / `BEHIND` /
`BLOCKED` stops the run with the reason, whatever the checks say.

### 3. Wait, or stop

- **Pending** — poll every ~90s, one compact line per poll (`3/4 legs done`).
Cap at ~30 min; then report where it stands and stop rather than spinning.
Under `--no-wait`, skip the loop: report and stop.
- **Red** — **fetch the failing job's log immediately** (GitHub purges the blob;
once purged you can never name what broke), quote the failing step, and stop.
Do not merge, do not re-run, do not "wait for the flake to pass". Offer the
next door: `/bug` for a real failure, or a named re-run if the user judges it a
known flake — their call, not yours.
- **No checks configured** — say so explicitly and ask before merging.

### 4. Merge

Green on every leg → merge, in this order:

1. **Library PR first.** The workspace PR may not merge until its upstream
library PR is `MERGED` — the library-first gate
([`../ship_workspace/reference.md`](../ship_workspace/reference.md)). Refuse
otherwise; there is no `--auto`-flag workaround.
2. `gh pr merge <n> --merge` per PR (add `-R owner/repo` when you have no
checkout), then confirm the resulting state is `MERGED` — a queued or
auto-merge state is not a merge. Do **not** pass `--delete-branch`: it deletes
the local branch too, which fails or orphans a task worktree. Branch deletion
belongs to the post-merge cleanup in step 5.

Never force, never override a protection, never rewrite history. If a merge is
refused by GitHub, report the reason verbatim and stop.

### 5. Finish the ship

Hand back to the ship skills' completion steps — do not re-invent them:

- Post the "Shipped" comment on the issue (templates: `../ship_library/reference.md`
→ "Issue comments + Mind state").
- Write the dated completion record — `PyAutoMind/scripts/lifecycle.py record`
(also refreshes the index and prunes the `active.md` entry) — and push Mind.
- **Ask before closing the issue.** Merging is what `/prm` was typed for; closing
is a separate decision the human makes.
- **Local only:** offer the post-merge cleanup (worktree removal, local branch
deletion) per the ship skills; on mobile/codex note it as still pending.

## Notes

- `/prm` merges an **existing** PR. No PR yet → `/ship_library` or
`/ship_workspace` first; `/prm` will say so rather than opening one.
- It never bypasses the Heart readiness gate — that gate ran at ship time, and a
red Heart is not something a merge shortcut may re-judge.
- Under a `--auto` workflow run, merge stays human: `/prm` is a human-typed door
and is never invoked by the autonomous queue.
109 changes: 109 additions & 0 deletions skills/prm/reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# /prm — reference

Mechanics for [`prm.md`](prm.md). Lazy-loaded: read it when you actually run the
step. `gh` in this workspace is old (2.4.0) — it has **no `gh search`** and some
newer flags are absent, so everything below sticks to `gh pr` / `gh api`.

## Resolving the PR

```bash
# From the current branch (local checkout)
gh pr view --json number,url,title,headRefName,state,labels

# Explicit target, no checkout needed (mobile / Codex)
gh pr view <n> -R <owner>/<repo> --json number,url,title,headRefName,state

# Candidates when nothing is claimed: open PRs in one repo
gh pr list -R <owner>/<repo> --state open --json number,title,headRefName,labels

# The claimed task's PRs, read straight from Mind without a checkout
gh api repos/PyAutoLabs/PyAutoMind/contents/active.md --jq '.content' \
| base64 -d | grep -iE 'library-pr:|workspace-pr:|issue:| - '
```

Feature PRs from `/ship_*` carry the `pending-release` label — a useful filter,
never a merge blocker (the label describes the release state of the change, not
the mergeability of the PR).

## CI: every run, every leg

A single head sha gets **two** runs of each workflow (`push` + `pull_request`,
created seconds apart), each with its own matrix legs (e.g. py3.12 + py3.13). So
one `success` row is 1 of 4 signals. Enumerate all of them:

```bash
repo=<owner>/<repo>; pr=<n>
sha=$(gh api repos/$repo/pulls/$pr --jq '.head.sha')

# Every run for the sha
gh api "repos/$repo/actions/runs?head_sha=$sha" \
--jq '.workflow_runs[] | "\(.name) [\(.event)]: \(.status)/\(.conclusion)"'

# Every job within every run
for run in $(gh api "repos/$repo/actions/runs?head_sha=$sha" --jq '.workflow_runs[].id'); do
gh api repos/$repo/actions/runs/$run/jobs --jq '.jobs[] | " \(.name)=\(.conclusion)"'
done

# Not-ready test: non-zero means keep waiting, NOT "green so far"
gh api "repos/$repo/actions/runs?head_sha=$sha" \
--jq '[.workflow_runs[] | select(.status!="completed")] | length'
```

Cross-check the PR's own view — mergeability is independent of the checks:

```bash
gh pr view $pr -R $repo --json state,mergeable,mergeStateStatus,reviewDecision,statusCheckRollup
```

- `mergeStateStatus`: `CLEAN` → mergeable · `UNSTABLE` → a check is pending or
failed · `BLOCKED` → protection rule / review missing · `BEHIND` → base moved ·
`DIRTY` → conflicts. Only `CLEAN` merges without a human decision.
- `mergeable`: `CONFLICTING` stops the run regardless of check colour.

An empty run list is not green — it means no workflow fired for that sha (a
docs-only path filter, a skipped event, or Actions being down). Say which.

## A red leg: grab the log *now*

GitHub purges job logs; once purged, `BlobNotFound` leaves the failure
unnameable forever. Fetch before reporting:

```bash
job=$(gh api repos/$repo/actions/runs/$run/jobs --jq '.jobs[] | select(.conclusion=="failure") | .id')
gh api repos/$repo/actions/jobs/$job/logs > /tmp/prm-failing-job.log
grep -nE 'FAILED|Error|assert|Traceback' /tmp/prm-failing-job.log | tail -40
```

Report the failing job name, the failing step, and the quoted error. A failure
that matches a known flake is still a failure — the user decides whether to
re-run; `/prm` does not decide that for them.

## Merging in gate order

```bash
# 1. library PR
gh pr merge <lib_n> -R <owner>/<lib_repo> --merge
gh pr view <lib_n> -R <owner>/<lib_repo> --json state --jq '.state' # must print MERGED

# 2. only then the workspace PR
gh pr view <ws_n> -R <owner>/<ws_repo> --json body --jq '.body' | grep -iE 'PyAuto[A-Za-z]+/pull/[0-9]+'
gh pr merge <ws_n> -R <owner>/<ws_repo> --merge
```

The library-first gate is `../ship_workspace/reference.md` → "Library-first merge
gate": a workspace PR linked to an upstream library PR may only merge once that
PR reads `MERGED`. There is no workaround — not `--auto`, not `--admin`.

## Finishing

Completion is the ship skills' contract, unchanged:

- "Shipped" comment template → `../ship_library/reference.md` → "Issue comments +
Mind state".
- `python3 PyAutoMind/scripts/lifecycle.py record --prompt <bare-filename>` —
the argument is the **bare prompt filename**, not a path — then commit and push
Mind (on `main`, and check that first).
- Closing the issue is a separate human decision: ask, don't assume.
- Local post-merge cleanup (worktree removal, local + remote branch deletion) →
the `ship_library` / `ship_workspace` cleanup sections. On mobile/Codex, say
it is still pending rather than pretending it ran.
3 changes: 3 additions & 0 deletions skills/ship_library/ship_library.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,7 @@ next human (or queued) launch.

- This skill ships **library source only** — workspace scripts/notebooks go
through `/ship_workspace`.
- Waiting on CI before the merge is [`/prm`](../prm/prm.md) — the wrap-up door
that judges every run/leg, merges in library-first order, and returns here for
the completion steps.
- Never skip the readiness gate; never `--no-verify`; fix the underlying issue.
3 changes: 3 additions & 0 deletions skills/ship_workspace/ship_workspace.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,5 +99,8 @@ and prunes the `active.md` entry), and push Mind. Templates in
## Notes

- Workspace scripts/notebooks/configs only — never library source.
- If CI is still running when step 5 arrives, [`/prm`](../prm/prm.md) is the door
that waits for every run/leg, merges behind the library-first gate, and comes
back to step 6.
- Only edit `scripts/`; notebooks are regenerated.
- Never skip the readiness gate or the library-first merge gate.
Loading