From db88f33b7b31a2f4f97fdb5a6cf1261f7cabd911 Mon Sep 17 00:00:00 2001 From: fullstackjam Date: Sat, 16 May 2026 21:54:14 +0800 Subject: [PATCH] chore(harness): encode post-merge cleanup into ship-pr + session-start Closes the last gap in the post-edit harness loop. Previously, after auto-merge landed a PR, the local feature branch lingered as an orphan and cleanup was oral tradition. Two complementary controls: - session-start.sh now runs a lightweight stale-branch sensor before the cache-warming section. It runs unconditionally (local + remote), fetches with --prune, and prints a one-line cleanup hint if the current branch's upstream ref is gone. No-op on main and on fresh unpushed branches. - ship-pr SKILL.md gains a Step 8 that polls `gh pr view` once after enabling auto-merge. If the PR landed immediately, the in-session cleanup runs right away. If still OPEN, the session-start sensor picks it up next time. docs/HARNESS.md gets two new rows so the controls are discoverable from the steering meta-doc. --- .claude/hooks/session-start.sh | 27 ++++++++++++++++++++++++--- .claude/skills/ship-pr/SKILL.md | 23 +++++++++++++++++++++++ docs/HARNESS.md | 2 ++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/.claude/hooks/session-start.sh b/.claude/hooks/session-start.sh index ad2fab0..04ef3c6 100755 --- a/.claude/hooks/session-start.sh +++ b/.claude/hooks/session-start.sh @@ -1,12 +1,33 @@ #!/bin/bash -set -euo pipefail +set -uo pipefail -# Only run in Claude Code on the web; skip on local machines. +cd "${CLAUDE_PROJECT_DIR:-$(pwd)}" + +# --- Stale-branch sensor (always runs, cheap, ~1s with --quiet fetch) --- +# After `ship-pr` flow / auto-merge with --delete-branch, the remote branch +# is gone but the local branch lingers. Detect and suggest cleanup so the +# next session doesn't start on an orphan branch. +if command -v git >/dev/null 2>&1 && git rev-parse --git-dir >/dev/null 2>&1; then + current=$(git symbolic-ref --quiet --short HEAD 2>/dev/null || true) + if [ -n "$current" ] && [ "$current" != "main" ]; then + # Prune local refs for deleted remote branches. Network call, but cheap + # against an already-cloned repo; silenced so we don't spam the session. + git fetch --prune --quiet origin 2>/dev/null || true + track=$(git rev-parse --symbolic-full-name --abbrev-ref "${current}@{upstream}" 2>/dev/null || true) + if [ -n "$track" ] && ! git show-ref --verify --quiet "refs/remotes/${track}"; then + echo "[openboot session-start] Branch '$current' tracks '$track' — gone on remote (likely merged)." >&2 + echo " Cleanup: git checkout main && git pull && git branch -d $current" >&2 + fi + fi +fi + +# --- Cache warming (Claude Code on the web only) --- +# Local users don't need this — their caches stay warm across sessions. if [ "${CLAUDE_CODE_REMOTE:-}" != "true" ]; then exit 0 fi -cd "${CLAUDE_PROJECT_DIR:-$(pwd)}" +set -e echo "[openboot session-start] Go version: $(go version)" diff --git a/.claude/skills/ship-pr/SKILL.md b/.claude/skills/ship-pr/SKILL.md index 592a27a..4d6833f 100644 --- a/.claude/skills/ship-pr/SKILL.md +++ b/.claude/skills/ship-pr/SKILL.md @@ -108,6 +108,29 @@ Return the PR URL and one sentence on what was queued. Example: > PR opened: . Auto-merge enabled — will land once the 6 required > checks pass. +### Step 8 — Post-merge cleanup (if already merged) + +Immediately re-check whether the PR has already landed — for small PRs +that hit `MERGEABLE` and pass CI in seconds, `gh pr merge --auto` can +complete before the user sees the PR URL: + +```bash +gh pr view --json state -q .state +``` + +If the output is `MERGED`, the remote branch has been deleted by +`--delete-branch`. Clean up the local side in the same session so the +user doesn't have to: + +```bash +git checkout main && git pull --quiet +git branch -d "$(git symbolic-ref --quiet --short @{-1} 2>/dev/null)" +``` + +If the output is `OPEN`, do nothing here. The +[`session-start.sh`](../../hooks/session-start.sh) stale-branch sensor +will catch it on the next session and prompt cleanup. + ## What NOT to do - **Do not skip step 2.** "It's a small change" is exactly when archtest diff --git a/docs/HARNESS.md b/docs/HARNESS.md index b05f75b..6a91c91 100644 --- a/docs/HARNESS.md +++ b/docs/HARNESS.md @@ -55,6 +55,8 @@ Three regulation categories: | Feedfwd. | Agent conventions | every AI turn | `CLAUDE.md`, `AGENTS.md` | | Feedfwd. | Skills | model-loaded | `.claude/skills/*` | | Feedfwd. | Session-start hook (warm caches, fetch deps) | every Claude session | `.claude/hooks/session-start.sh` | +| Feedback (agent) | Stale-branch sensor (suggests cleanup when current branch's upstream is gone) | every Claude session | `.claude/hooks/session-start.sh` | +| Feedfwd. | `ship-pr` skill — canonical PR flow incl. post-merge cleanup | model-loaded | `.claude/skills/ship-pr/SKILL.md` | | Feedback (agent) | `go vet` on edited package | after every Edit/Write/MultiEdit | `.claude/hooks/post-tool-use.sh` | | Feedback (agent) | `go vet ./...` + archtest | end of every Claude turn (if .go dirty) | `.claude/hooks/stop.sh` | | Maint. | `golangci-lint` on the staged diff | local git pre-commit | `scripts/hooks/pre-commit` |