Skip to content

Commit d025ee3

Browse files
Jammy2211claude
authored andcommitted
feat(prm): make /prm the full task close-out, not just the merge
/prm was stopping at the merge and handing back to the ship skills. It is now the last thing a human types for a task: merge, then close the task out end to end — Shipped comment, issue closed, PyAutoMind prompt moved active/ → complete/ (claim released, Mind pushed), worktree removed, merged branches deleted, and a ledger of what ran and what was skipped. Typing /prm authorizes all of it, so the run no longer stops to ask again. The close-out order is forced by the tooling and documented as such: prove every branch merged -> issue -> Mind record -> worktree_remove -> branches. worktree_remove refuses while active.md still claims the task, which is exactly why the Mind step precedes it. Guards that stop the run instead of pressing on: - an unmerged sibling branch with no open PR (the shipped-in-waves trap — a complete/ record is a write-up, not a merge receipt) - a worktree_remove refusal (dirty repo, stale claim); never PYAUTO_WT_FORCE=1 - a worktree holding gitignored data products — the one question /prm asks, and only when such files exist (2026-07-09: a reduced dataset + archive cache were destroyed by exactly this cleanup) reference.md gains the mechanics: per-branch merge proof, `gh api -X PATCH` for the close (gh issue close is broken in 2.4.0), lifecycle.py record with the BARE --prompt filename plus the three effects to verify, worktree_root_path and the data-product check, and remote branch deletion proven with ls-remote. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrgmL8WMK82GxQJjPL8ZCs
1 parent 95a7fd2 commit d025ee3

7 files changed

Lines changed: 194 additions & 49 deletions

File tree

AGENTS.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ via `/route`, and it routes to the right agent; normal usage never says
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
172172
for symmetry); `/wake_up` composes sync + `/health` + `/hygiene`; `/prm` composes the
173-
end-of-task wrap-up (CI green → merge → `ship_*` completion); `/brain
173+
end-of-task close-out (CI green → merge → issue closed → Mind `active/`
174+
`complete/` → worktree and branches removed); `/brain
174175
<agent>` is the raw passthrough. Every command routes **through** the Brain;
175176
none replaces it.
176177

skills/COMMANDS.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,14 +85,20 @@ 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.
88+
- **`/prm`** — the wrap-up door and **full task close-out**: *"PR, CI green, then
89+
merge"* — the last thing a human types for a task. Watches the feature PR's
90+
checks until **every** workflow run and **every** matrix leg for the head sha is
91+
green, merges in library-first order, then closes the task out end to end:
92+
Shipped comment, issue closed, `lifecycle.py record` moving the prompt
93+
`active/``complete/` (claim released, Mind pushed), `worktree_remove`, merged
94+
branches deleted, and a ledger of what it did. Typing `/prm` authorizes all of
95+
it, so it does not stop to ask again. Owns no gate of its own — it refuses on
96+
red/pending/conflicting checks, an unmerged upstream library PR, an unmerged
97+
sibling branch (the shipped-in-waves trap), or a `worktree_remove` refusal, and
98+
asks exactly once before deleting a worktree holding irreplaceable data
99+
products. Mostly `gh`, so the merge/issue half runs on the CLI, mobile Claude
100+
Code chat and Codex alike; the worktree half is local-only and is reported as
101+
outstanding elsewhere.
96102

97103
**5. Maintenance doors** — periodic sweeps that reason about accumulated debris
98104
and then execute their own cleanup after per-bucket human confirmation. They own

skills/prm/SKILL.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
---
22
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.
3+
description: The last thing you type for a task — "PR, CI green, then merge", then the full close-out. Watches a feature PR's checks until every workflow run and every matrix leg is green, merges (library-first gate honoured), closes the issue, moves the PyAutoMind prompt active/ → complete/, removes the task worktree and deletes the merged branches. Use when the user says merge this PR once CI is green, close this task out, or types /prm. Runs anywhere gh is authenticated: CLI, mobile Claude Code chat, Codex.
44
---
55

6-
# /prm — PR, CI green, then merge
6+
# /prm — PR, CI green, then merge, then close the task out
77

8-
Follow [`prm.md`](prm.md) exactly; gh mechanics in [`reference.md`](reference.md).
8+
Follow [`prm.md`](prm.md) exactly; gh + close-out mechanics in
9+
[`reference.md`](reference.md).
910

1011
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.
12+
from `/ship_library` / `/ship_workspace`, the CI verdict from GitHub Actions, the
13+
lifecycle from PyAutoMind. Typing `/prm` authorizes the **whole** close-out —
14+
merge, issue close, `active/``complete/`, worktree and branch removal — so it
15+
runs to the end without asking again. It still refuses on red, pending,
16+
conflicting, an unmerged upstream library PR, or an unmerged sibling branch, and
17+
asks once before deleting a worktree that holds irreplaceable data products.

skills/prm/prm.md

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
# /prm — PR, CI green, then merge
22

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).
3+
The end-of-task shortcut for "PR CI green then merge" — and the **full task
4+
close-out**: the last thing you type for a task. It watches the feature PR's
5+
checks, merges the moment they are genuinely green, then closes the task out
6+
completely — issue closed, PyAutoMind moved `active/``complete/`, worktree
7+
removed, branches deleted — and hands back a ledger of what it did.
68

79
Shared routing context: `PyAutoBrain/skills/COMMANDS.md`.
810
gh mechanics + snippets: [`reference.md`](reference.md).
@@ -90,18 +92,54 @@ Green on every leg → merge, in this order:
9092
Never force, never override a protection, never rewrite history. If a merge is
9193
refused by GitHub, report the reason verbatim and stop.
9294

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.
95+
### 5. Close the task out
96+
97+
Typing `/prm` authorizes the whole close-out — merge **and** issue close **and**
98+
cleanup. Run all of it without asking again; the only questions are the guards in
99+
step 6. Order is forced by the tooling, so do not reorder:
100+
101+
1. **Prove every branch merged, per repo.** The task may have shipped in waves —
102+
a completion record is a write-up, not a merge receipt. For each repo the task
103+
claims, `merge-base --is-ancestor origin/feature/<task> origin/main` and
104+
`rev-list --count origin/main..origin/feature/<task>` (want 0). Any repo with
105+
unmerged commits and no open PR → **stop the close-out** and report it; a
106+
half-merged task must not be recorded complete.
107+
2. **Issue** — post the "Shipped" comment (template: `../ship_library/reference.md`
108+
→ "Issue comments + Mind state"), then close it. `gh issue close` is broken in
109+
this gh; use the REST path in [`reference.md`](reference.md).
110+
3. **Mind: `active/``complete/`** — draft the completion body, then
111+
`lifecycle.py record <slug> --date … --from-file … --prompt <bare-filename>
112+
--apply`. The `--prompt` argument is a **bare filename**; a path silently
113+
no-ops. Verify all three effects (record has `## Original prompt`, the
114+
`active/` prompt is gone, the `## <task>` entry left `active.md`), run
115+
`lifecycle.py check`, then commit and push Mind — on `main`, and note that
116+
`prompt_sync_push` stages `-A`, so check for unrelated work first.
117+
4. **Worktree**`worktree_remove <task>` (source `bin/worktree.sh`, `PYAUTO_MAIN`
118+
set), never `rm -rf`. It refuses on a dirty repo and on a claim still
119+
registered in `active.md` — which is exactly why step 3 comes first.
120+
5. **Branches** — delete the remote `feature/<task>` per proven-merged repo, plus
121+
any local branch left in the canonical checkout. Never delete a branch whose
122+
merge you did not prove in sub-step 1.
123+
6. **Report the ledger** — PRs merged, issue closed, record path, `active.md`
124+
released, worktree removed, branches deleted, and anything skipped.
125+
126+
**Remote (mobile/codex):** sub-steps 1, 2 and 5 run over `gh`/`git ls-remote` as
127+
usual. Mind (3) works if PyAutoMind is checked out; otherwise say the record is
128+
pending. The worktree (4) is local-only — name it as outstanding rather than
129+
implying it ran.
130+
131+
### 6. The only guards that stop you
132+
133+
Stop and report instead of pressing on when:
134+
135+
- a branch in the task is **unmerged** with no open PR (step 5.1) — the waves trap;
136+
- `worktree_remove` **refuses** (dirty repo, stale claim) — fix the cause, never
137+
`PYAUTO_WT_FORCE=1` your way past it;
138+
- the worktree holds **gitignored data products** (reduced datasets, caches,
139+
`output/` fits) — removal destroys them and they may not be cheaply
140+
re-derivable. List them with sizes and **ask once**: delete, or keep the
141+
worktree and finish everything else. This is the one question `/prm` asks, and
142+
only when such files exist.
105143

106144
## Notes
107145

@@ -111,3 +149,5 @@ Hand back to the ship skills' completion steps — do not re-invent them:
111149
red Heart is not something a merge shortcut may re-judge.
112150
- Under a `--auto` workflow run, merge stays human: `/prm` is a human-typed door
113151
and is never invoked by the autonomous queue.
152+
- A task with no issue, no Mind prompt, or no worktree (a direct wiring change,
153+
say) simply skips those sub-steps and says so — it is not an error.

skills/prm/reference.md

Lines changed: 107 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -94,16 +94,110 @@ The library-first gate is `../ship_workspace/reference.md` → "Library-first me
9494
gate": a workspace PR linked to an upstream library PR may only merge once that
9595
PR reads `MERGED`. There is no workaround — not `--auto`, not `--admin`.
9696

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.
97+
## The close-out
98+
99+
### 1. Prove every branch merged (before recording anything)
100+
101+
A `complete/` record is a write-up, not a merge receipt — a task that shipped in
102+
waves gets its record on the first wave while later branches keep living on
103+
origin. Prove it per repo, never from the record and never from a clean
104+
`git status`:
105+
106+
```bash
107+
git -C <repo> fetch origin --quiet
108+
git merge-base --is-ancestor origin/feature/<task> origin/main && echo MERGED || echo UNMERGED
109+
git rev-list --count origin/main..origin/feature/<task> # want 0
110+
```
111+
112+
`UNMERGED` with no open PR → stop the close-out and report which repo. Squash
113+
merges break `--is-ancestor`; fall back to the PR's `state=MERGED`.
114+
115+
### 2. Issue: comment, then close
116+
117+
`gh issue close` prints its usage string and exits non-zero in this gh (2.4.0) —
118+
use the REST path:
119+
120+
```bash
121+
gh issue comment <n> -R <owner>/<repo> --body "$(cat <<'EOF'
122+
## Shipped
123+
<summary, PR links, what changed>
124+
EOF
125+
)"
126+
gh api -X PATCH repos/<owner>/<repo>/issues/<n> -f state=closed --jq .state # → "closed"
127+
```
128+
129+
### 3. Mind: active/ → complete/
130+
131+
```bash
132+
cd $PYAUTO_MAIN/PyAutoMind
133+
git rev-parse --abbrev-ref HEAD # must be main — check BEFORE writing
134+
python3 scripts/lifecycle.py record <slug> --date <YYYY-MM-DD> --from-file <body.md> --prompt <prompt.md> --apply
135+
```
136+
137+
`--prompt` takes a **bare filename** (it resolves as `active/<prompt>`); a path
138+
like `active/foo.md` becomes `active/active/foo.md`, **exits 0 anyway**, and
139+
leaves you with a record missing `## Original prompt` plus an orphan in
140+
`active/`. Success prints `(+folds active/<name>)`. Verify all three effects —
141+
none of them is announced on failure:
142+
143+
```bash
144+
grep -c "## Original prompt" complete/<YYYY>/<MM>/<slug>.md # want 1
145+
ls active/<prompt>.md # want "No such file"
146+
grep -n "^## <task>" active.md # want no match
147+
python3 scripts/lifecycle.py check # want clean exit
148+
```
149+
150+
Then commit + push. `prompt_sync_push` runs `git add -A`, so check for unrelated
151+
work first and use explicit pathspecs if any exists:
152+
153+
```bash
154+
git status --short # unrelated work?
155+
source scripts/prompt_sync.sh && prompt_sync_push "complete: <task>"
156+
```
157+
158+
### 4. Worktree
159+
160+
Removal deletes the whole task root — **including gitignored `output/`,
161+
`cache/`, and downloaded data** that only live there (a reduced dataset + a
162+
55-frame archive cache were destroyed this way on 2026-07-09). Look before you
163+
remove, and ask once if anything real is there:
164+
165+
```bash
166+
root=${PYAUTO_WT_ROOT:-$HOME/Code/PyAutoLabs-wt}/<task> # worktree_root_path
167+
du -sh "$root"/*/output "$root"/*/cache 2>/dev/null
168+
git -C "$root/<repo>" status --porcelain --ignored | grep '^!!' | head -20
169+
```
170+
171+
Then remove it properly — never `rm -rf`:
172+
173+
```bash
174+
export PYAUTO_MAIN=$HOME/Code/PyAutoLabs
175+
source PyAutoBrain/bin/worktree.sh && worktree_remove <task>
176+
```
177+
178+
It **refuses** on a dirty repo, and on a merged task whose `active.md` claim is
179+
still registered — that refusal means step 3 has not finished, so fix the cause.
180+
`PYAUTO_WT_FORCE=1` exists for abandoned/unmerged work only; a close-out never
181+
needs it. A `PyAutoLabs-wt/<task>/` dir whose worktrees are already gone survives as a shell
182+
of symlinks + `activate.sh` and `git worktree list` will not name it — a
183+
directory listing is the only way to find it.
184+
185+
### 5. Branches
186+
187+
Local branches go with the worktree; the remote ones do not:
188+
189+
```bash
190+
git -C <repo> push origin --delete feature/<task>
191+
git -C <repo> branch -d feature/<task> # if one survives in the canonical checkout
192+
git -C <repo> fetch --prune
193+
```
194+
195+
Only branches proven merged in step 1. `git ls-remote --heads origin feature/<task>`
196+
is the ground truth that the delete landed.
197+
198+
### 6. The ledger
199+
200+
Report, per line: PR(s) merged (URL + `MERGED`), issue closed (number + state),
201+
record path under `complete/<YYYY>/<MM>/`, `active.md` claim released, worktree
202+
removed, branches deleted, and **anything skipped** with the reason. A close-out
203+
that quietly skipped a step reads exactly like one that finished.

skills/ship_library/ship_library.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ next human (or queued) launch.
112112

113113
- This skill ships **library source only** — workspace scripts/notebooks go
114114
through `/ship_workspace`.
115-
- Waiting on CI before the merge is [`/prm`](../prm/prm.md) — the wrap-up door
116-
that judges every run/leg, merges in library-first order, and returns here for
117-
the completion steps.
115+
- Waiting on CI before the merge is [`/prm`](../prm/prm.md) — the close-out door
116+
that judges every run/leg, merges in library-first order, then runs this
117+
skill's completion contract itself (Shipped comment, issue closed, Mind record,
118+
worktree + branches removed).
118119
- Never skip the readiness gate; never `--no-verify`; fix the underlying issue.

skills/ship_workspace/ship_workspace.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ and prunes the `active.md` entry), and push Mind. Templates in
100100

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

0 commit comments

Comments
 (0)