# Ship Loop Pr

> Push + open PR child loop. Pre-conditions all five inner layers (build, test, sanity, watch, smoke) green AND staged/uncommitted local changes detected. Refuses to push from main branch. On non-fast-forward push rejection, rebases on origin and retries once. Records PR URL into ship-loop-state.json. Use standalone via /loop ship-loop-pr or wired into the master orchestrator.

- Skill: `cleanexpo/ship-loop-pr` (Agent Skill)
- Install (CLI): `npx skillmds@latest add cleanexpo/ship-loop-pr`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cleanexpo/ship-loop-pr/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: cleanexpo (https://skillmd.com/u/cleanexpo)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/cleanexpo/ship-loop-pr

---


# ship-loop-pr — push + open PR child loop

## Activation

- Standalone: `/loop ship-loop-pr` (manual push from CEO)
- Orchestrated: invoked by `ship-loop-master` when:
  - All five inner layers `green` (build, test, sanity, watch, smoke)
  - `git status --short` shows staged or unstaged changes (something to push)
  - `pr.state === 'not_opened'`

## Pre-flight (refuse to run if any fail)

1. `git rev-parse --abbrev-ref HEAD` — branch name. If `main` or `master`, refuse with `## P0 — ship-loop-pr refused: branch is main` and exit
2. `git status --short` — if empty, write `{"reason":"clean_tree","skipped":true}` and return without changing PR state
3. `gh auth status` — must be authenticated
4. Read state file — confirm all five inner layers `green`; refuse if any `red`

## Process

### Step 1: Stage + commit if needed

If `git status --short` shows unstaged changes:

- Stage only files relevant to current work (refuse `git add -A` to avoid grabbing accidental scratchpad files; use explicit paths from `git diff --name-only`)
- If no commit yet on this branch (HEAD === origin/main): create commit
- Commit message templated from:
  - Linear ticket from state (`SYN-XXX`)
  - List of changed file paths (top 5)
  - Pre-gate results summary (build/test/sanity/smoke all green)

```
fix(<scope>): <SYN-XXX> — <ticket title>

Files changed:
<top 5 file paths>

Verification:
- npm run type-check → 0 errors
- npm run lint → 0 errors
- npm test → <X> passed / 0 failed
- npm run build → ✓ Compiled successfully
- npm audit --omit=dev → 0 HIGH

Tracked: <SYN-XXX>
```

### Step 2: Push

```bash
git push -u origin <branch>
```

If rejected with `non-fast-forward`:

- `git fetch origin`
- `git rebase origin/<branch>`
- If rebase has conflicts: STOP, escalate `## P1 — Rebase conflict on push`, do NOT push, do NOT force
- If rebase clean: re-attempt push (one retry only)

### Step 3: Open PR

```bash
gh pr create --repo CleanExpo/Synthex \
  --title "<title from commit>" \
  --body "$(cat <<'EOF'
## Summary
<bulleted summary from commit message>

## Verification
<gate results from state file>

## Linear
<SYN-XXX>

🤖 Generated by ship-loop-pr
EOF
)"
```

If `gh pr create` returns "pull request already exists for branch":

- `gh pr view --json url,number` to capture existing PR
- Update state with the existing URL/number
- Skip create (treat as success)

### Step 4: Update state

Atomic update to `layers.pr`:

```json
{
  "state": "opened",
  "url": "https://github.com/CleanExpo/Synthex/pull/<num>",
  "number": <num>,
  "opened_at": "<iso>",
  "ci_state": "pending",
  "branch": "<branch>"
}
```

The `ci_state` will be polled by master loop on subsequent ticks via `gh pr checks <num>` (master invokes a quick check rather than re-firing this skill — efficient).

## Recipe priorities for this loop

- #11 (non-fast-forward) — rebase + retry
- #12 (PR already exists) — capture existing URL, skip create

## Hard rules compliance

- **No force push** — even on rebase conflict, never `git push --force`. Conflicts escalate.
- **No `git add -A`** — explicit file paths only, prevents accidental commit of scratchpad outputs
- **No push from main** — pre-flight refuses
- **Commit messages cite Linear ticket** — required by CLAUDE.md hard rule

## Verification

- Run on a clean branch with one staged commit; expect successful push + PR open
- Run on clean tree (no changes); expect skip with reason
- Run on `main`; expect P0 escalation refusal
- Run with sanity layer red in state; expect skip (master shouldn't have invoked, but defense-in-depth)

## Out of scope

- PR review request (gh pr edit --add-reviewer) — separate skill if needed
- PR template enforcement — relies on existing `.github/pull_request_template.md` if present
- Auto-tagging issues / setting milestones / setting projects on the PR — defer to senior-pm skill or Linear's GitHub integration

