examples/before-after.md
# Worked example — a finished lane → branch, commit, drafted PR, push
## Context
The web lane finished and `refactor-publish-checklist` returned **GO**. The user says "ship it."
Current branch is `main`.
## 1. GO guard
```
$ node ~/.claude/skills/refactor-publish-checklist/scripts/checklist.mjs --state /repo
{ "verdict": "GO", "blockers": [] }
```
Clear to ship.
## 2. Branch (off main)
```
$ git switch -c refactor/web-refactor-web
Switched to a new branch 'refactor/web-refactor-web'
```
## 3. Draft
```
$ node scripts/draft-ship.mjs --target /repo
{
"branch": "refactor/web-refactor-web",
"commit": "refactor(web): web lane cleanup (behavior preserved)\n\nBehavior-preserving refactor via refactor-chain. Every step re-ran the recorded\nbaseline and matched; review gate passed; publish checklist GO.\n\n- web-01-structure: standardized src/ layout, added services/ layer\n- web-02-modules: grouped feature code under modules/\n- web-03-components: split common/layout/business tiers\n- web-04-layout: extracted shared layout shells\n- web-05-naming: use- prefix on composables, consistent names\n- code-principles: one-responsibility tidy on 3 modules\n\nSelf-healed steps (drift caught & re-done): refactor-web-03-components, refactor-web-05-naming.\n\nCo-Authored-By: Claude <noreply@anthropic.com>",
"prTitle": "refactor(web): web lane cleanup — behavior preserved",
"prBody": "## What & why\nBehavior-preserving refactor produced by the refactor-chain pipeline...\n## Changes\n- ...\n## Safety\n- Baseline: recorded; every step verified against it.\n- Review gate: passed.\n..."
}
```
The drafted commit + PR body are shown to the user, who approves them unchanged.
## 4. Commit
```
$ git add -A
$ git commit -F <(drafted commit message)
[refactor/web-refactor-web 9f1c2ab] refactor(web): web lane cleanup (behavior preserved)
41 files changed, 512 insertions(+), 498 deletions(-)
```
## 5. Push & open the PR (GitHub CLI present)
```
$ git push -u origin refactor/web-refactor-web
$ gh pr create --title "refactor(web): web lane cleanup — behavior preserved" --body-file <(drafted PR body)
https://github.com/acme/app/pull/482
```
(No `gh`? The skill instead prints the PR body and the compare URL
`https://github.com/acme/app/compare/refactor/web-refactor-web` for the user to paste.)
## 6. CI
> Pushed and opened PR #482. CI is running now — watch it on the PR's Checks tab (or `gh run watch`).
> I did NOT merge and did NOT force-push; the merge is your call.
## 7. Handoff
`refactor-improve` is invoked to record the run outcome (2 self-heals on the web lane) to
`history.jsonl` for the history prior.
## Why this is a good ship
- Gated on GO — a NO-GO run would have been refused here.
- Off `main`, one clean commit, conventional message + co-author trailer.
- Host-agnostic: `gh` was a convenience; the plain-git path (push + paste) works identically on GitLab.
- No merge, no force-push — the human stays in control of landing it.
references/method.md
# refactor-ship — full method: the deterministic, host-agnostic finish
This skill lands a verified refactor. It is deterministic (same run → same shape of commit/PR),
source-control-agnostic (plain `git`), and safe (never merges, never force-pushes, only ships on GO).
## Preconditions (hard)
1. `refactor-publish-checklist` returned **GO**. Re-verify with
`node ~/.claude/skills/refactor-publish-checklist/scripts/checklist.mjs --state <target>` if unsure.
NO-GO → stop, surface blockers, do not ship.
2. Working tree contains the finished refactor. `git status` should show the intended changes only.
## Sequence
### 1. Branch (never the default branch)
- `git rev-parse --abbrev-ref HEAD`. If it's `main`/`master`/`develop`, create a branch:
`git switch -c <type>/<lane>-<slug>` where `<type>` = `fix` for the debug lane, else `refactor`.
- If already on a feature branch, use it.
### 2. Draft the messages
- `node scripts/draft-ship.mjs --target <target>` returns `{ branch, commit, prTitle, prBody }`,
built from `state.json` (completed steps, self-heals, baseline, gate) + the change write-up +
the audit trail.
- **Conventional commit** shape: `refactor(<lane>): <summary> (behavior preserved)` with a body of
per-step bullets and a self-heal note. Debug lane uses `fix(...)`.
- Always append the `Co-Authored-By: Claude <noreply@anthropic.com>` trailer (the user's global
git convention requires a Claude co-author trailer).
- **Show the drafted commit + PR body to the user and allow edits before committing.**
### 3. Commit
- `git add -A`
- `git commit -m "<subject>" -m "<body...>"` (or a here-doc/file for the multi-line body).
- One clean commit for the refactor is preferred; if the run naturally has meaningful sub-commits
from checkpoints, keep them coherent — but never leave WIP/checkpoint noise in history.
### 4. Push & open the PR/MR
- `git push -u origin <branch>` (plain push — never `--force`).
- Open the request host-agnostically:
- GitHub CLI present → `gh pr create --title "<prTitle>" --body "<prBody>"`.
- GitLab CLI present → `glab mr create --title "<prTitle>" --description "<prBody>"`.
- Neither present → print `prTitle` + `prBody` and the compare URL (`git remote get-url origin`
→ construct the `/compare/<branch>` link) for the human to open in the web UI.
- PR body follows the user's convention: end with the generated-with trailer.
### 5. Let CI run
- Do NOT block waiting for CI. Report that the push triggered CI and where to watch it
(the PR/MR checks tab, or `gh run watch` / `glab ci status` if the user wants to follow).
### 6. Hand off to the retro
- Invoke `refactor-improve` so the run's outcome is recorded to `history.jsonl` (the history prior).
(The orchestrator's `reset` also appends the retro; `refactor-improve` adds the human-facing
self-improvement note.)
## Guardrails baked in
- **GO-gated:** never ship a NO-GO run.
- **No merge, no force-push:** the human merges; history is never rewritten by this skill.
- **No default-branch commits:** always a branch.
- **Portable:** `git` is the contract; `gh`/`glab` are optional conveniences.
- **Human-in-the-loop on messages:** the commit/PR text is shown for edits before it's committed.
## Output
Fill `templates/output.md`: the final commit message and the PR/MR summary, plus the ship record
(branch, push status, PR/MR link or paste-ready body, CI location).
scripts/checklist.mjs
#!/usr/bin/env node
/**
* refactor-ship — step checklist as JSON. Zero deps.
* The commit/PR-body drafting helper lives in draft-ship.mjs (this skill's main script).
* node checklist.mjs -> prints the step checklist as JSON
*/
const SKILL = "refactor-ship";
const PHASE = "ship";
const STEPS = [
{ id: 1, step: "guard-go", detail: "Confirm refactor-publish-checklist returned GO; else stop and surface blockers. Never ship a NO-GO." },
{ id: 2, step: "branch", detail: "If on default branch, create refactor/<lane>-<slug>; never commit the refactor to main/master." },
{ id: 3, step: "draft", detail: "node draft-ship.mjs --target <dir> -> conventional-commit message + PR/MR body; show for edits." },
{ id: 4, step: "commit", detail: "git add -A; commit with the drafted message + Co-Authored-By trailer." },
{ id: 5, step: "push-pr", detail: "Push the branch; open PR/MR via gh/glab if present, else print body + compare URL." },
{ id: 6, step: "ci", detail: "Let CI run (no wait-block); report where to watch it. Never merge, never force-push." },
{ id: 7, step: "handoff", detail: "Trigger refactor-improve (improvement retro) to close the run." },
];
process.stdout.write(JSON.stringify({ skill: SKILL, phase: PHASE, sourceControl: "agnostic (native git; gh/glab optional)", steps: STEPS }, null, 2) + "\n");
scripts/draft-ship.mjs
#!/usr/bin/env node
/**
* refactor-ship — draft the commit message + PR/MR body from run state. Zero deps.
* node draft-ship.mjs --target <dir> -> { branch, commit, prTitle, prBody }
* node draft-ship.mjs --checklist -> prints this skill's step checklist as JSON
*
* Source-control-agnostic: emits text only; the skill runs the actual git commands.
* Never merges, never force-pushes — this script only drafts strings.
*/
import { existsSync, readFileSync } from "node:fs";
import { join } from "node:path";
const SKILL = "refactor-ship";
const PHASE = "ship";
const STEPS = [
{ id: 1, step: "guard-go", detail: "Confirm refactor-publish-checklist returned GO; else stop, surface blockers." },
{ id: 2, step: "branch", detail: "If on default branch, create refactor/<lane>-<slug>; never commit refactor to default." },
{ id: 3, step: "draft", detail: "Draft conventional-commit message + PR/MR body from state + write-up + audit trail; show for edits." },
{ id: 4, step: "commit", detail: "git add -A; commit with the message + Co-Authored-By trailer." },
{ id: 5, step: "push-pr", detail: "Push branch; open PR/MR via gh/glab if present, else print body + compare URL." },
{ id: 6, step: "ci", detail: "Let CI run (no wait-block); report where to watch it." },
{ id: 7, step: "handoff", detail: "Trigger refactor-improve (improvement retro)." },
];
const slug = (s) => String(s || "refactor").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
const typeFor = (lane) => (lane === "debug" ? "fix" : "refactor");
function draft(dir) {
const stFile = join(dir, ".refactor-chain", "state.json");
if (!existsSync(stFile)) return { error: "no state.json — run the chain first" };
const s = JSON.parse(readFileSync(stFile, "utf8"));
const lane = s.diagnosis?.lane || "code";
const done = (s.steps || []).filter((x) => x.status === "done" && x.kind !== "gate");
const scope = lane === "code" ? "" : `(${lane})`;
const type = typeFor(lane);
const branch = `${type}/${slug(lane)}-${slug(s.diagnosis?.case || "run")}`;
const bullets = done.map((x) => `- ${x.skill.replace(/^refactor-/, "")}: ${x.notes?.[x.notes.length - 1] || "applied, behavior verified"}`);
const healed = done.filter((x) => (x.retries || 0) > 0).map((x) => x.skill);
const commit = [
`${type}${scope}: ${lane} lane cleanup (behavior preserved)`,
"",
"Behavior-preserving refactor via refactor-chain. Every step re-ran the recorded",
"baseline and matched; review gate passed; publish checklist GO.",
"",
...bullets,
"",
healed.length ? `Self-healed steps (drift caught & re-done): ${healed.join(", ")}.` : "No drift; all steps clean on first apply.",
"",
"Co-Authored-By: Claude <noreply@anthropic.com>",
].join("\n");
const prTitle = `${type}${scope}: ${lane} lane cleanup — behavior preserved`;
const prBody = [
"## What & why",
"Behavior-preserving refactor produced by the refactor-chain pipeline.",
"See the change write-up for the plain-language summary and the audit trail for the evidence log.",
"",
"## Changes",
...bullets,
"",
"## Safety",
`- Baseline: recorded${s.baseline ? "" : " (MISSING — investigate)"}; every step verified against it.`,
`- Review gate: ${(s.steps || []).find((x) => x.kind === "gate")?.status === "done" ? "passed" : "NOT passed — do not merge"}.`,
healed.length ? `- Self-healed: ${healed.join(", ")}.` : "- No drift; clean on first apply.",
"",
"## Docs",
"- Change write-up: `.refactor-chain/change-report.md`",
"- Durable artifacts synced (architecture/flows/permissions/tests-map as applicable).",
"- Audit trail: `.refactor-chain/audit-log.jsonl` (hash-linked).",
"",
"🤖 Generated with refactor-chain",
].join("\n");
return { branch, defaultBranchGuard: "do not commit to main/master", commit, prTitle, prBody };
}
const argv = process.argv.slice(2);
if (argv.includes("--checklist")) {
process.stdout.write(JSON.stringify({ skill: SKILL, phase: PHASE, steps: STEPS }, null, 2) + "\n");
} else {
const ti = argv.indexOf("--target");
const dir = ti >= 0 ? argv[ti + 1] : process.cwd();
process.stdout.write(JSON.stringify({ skill: SKILL, ...draft(dir) }, null, 2) + "\n");
}
SKILL.md
---
name: refactor-ship
description: "Use this skill as the deterministic finish of a refactor-chain run — commit the refactor with a clear conventional message, draft a PR summary from the change write-up and audit trail, push, and let CI run. It is source-control-agnostic: it uses plain git (works with GitHub, GitLab, or any remote) rather than any one host's API. It only ships on a GO from refactor-publish-checklist. Trigger phrases: \"commit and open a PR\", \"ship it\", \"push this refactor\", \"create the pull request\", \"finish and merge-request this\", \"wrap it up and send it\". This is the ship phase of the refactor-chain pipeline; it runs after refactor-publish-checklist returns GO. It performs git actions (commit/branch/push) — it does not edit product code, and it never merges or force-pushes on its own."
---
# Ship — refactor-chain · ship
**Bundle:** refactor-chain (self-diagnosing, self-healing fix-it pipeline).
**Phase:** ship · **Prerequisite:** refactor-publish-checklist returned **GO** · **Next:** refactor-improve (the retro).
**Adaptivity / conditional:** repo-agnostic and source-control-agnostic — native `git` only, so it works on GitHub, GitLab, Bitbucket, or a bare remote. Uses a host CLI (`gh`/`glab`) only if present; otherwise it prints the PR/MR body for the human to paste.
## Purpose
The refactor is done and verified; this skill lands it. Deterministically: it creates (or confirms)
a branch, commits the whole change with a clean conventional-commit message, drafts a
pull/merge-request summary from the change write-up + audit trail, pushes, and lets CI take over.
It stays source-control-agnostic by using plain `git` — no dependency on any single host's API —
and it refuses to run until the publish checklist says GO, so it never ships a half-finished run.
## When to use
- `refactor-publish-checklist` returned **GO** and the change is ready to leave your hands.
- Someone says "ship it", "commit and open a PR", "push this", "create the merge request".
- The orchestrator reaches the end of the ship phase with a clean go/no-go.
- Do NOT run on a NO-GO — clear the blockers first.
## What I'll tell you (plain-language / ADHD-friendly)
- "The checklist says GO, so I'll land this: make a branch, commit with a clear message, draft the PR summary, and push. I won't merge — that stays your call."
- "I'm putting the refactor on a branch called `refactor/web-lane-structure` so `main` stays untouched until you're ready."
- "Here's the commit message and the PR summary I drafted from the write-up — want any edits before I push?"
- "Pushed. CI is running now. I did NOT merge or force-push — the PR is open for review at the link above."
- "No GitHub/GitLab CLI here, so I've printed the PR summary for you to paste into the web UI."
## Method
1. **Guard on GO.** Confirm `refactor-publish-checklist` returned GO (re-run its
`checklist.mjs --state <target>` if unsure). If NO-GO, stop and surface the blockers — do not ship.
2. **Branch.** If on the default branch (`main`/`master`), create a descriptive branch
(`refactor/<lane>-<slug>`) so the default branch stays clean. Never commit the refactor directly
to the default branch.
3. **Draft the messages.** Run `node scripts/draft-ship.mjs --target <target>` to generate a
conventional-commit message and a PR/MR body from `state.json`, the change write-up, and the
audit trail. Show them to the user for edits before committing.
4. **Commit.** `git add -A` then commit with the drafted message. Include the required
`Co-Authored-By` trailer per the user's global git convention.
5. **Push & open the PR/MR.** Push the branch. If `gh`/`glab` is available, open the PR/MR with
the drafted body; otherwise print the body and the compare URL for the human to open it.
6. **Let CI run.** Do not wait-block on CI; report that it's running and where to watch it.
7. **Hand off to the retro.** Trigger `refactor-improve` (self-improvement) to record the run's outcome.
## Guardrails
- **Ships only on GO.** Never commit/push a run the publish checklist rejected.
- **Never merges. Never force-pushes.** Opens the PR/MR for human review; the merge is the human's decision.
- Never commits the refactor directly to the default branch — always a branch.
- Uses plain `git` for portability; a host CLI is a convenience, never a requirement.
- Show the drafted commit + PR body before committing; let the user edit. Include the
`Co-Authored-By: Claude ...` trailer the user's git convention requires.
## Verify
- Plain: "The refactor is committed on its own branch with a clear message, the PR/MR is open (or its text is ready to paste), CI is running, and nothing was merged or forced."
- Technical: `git log` shows one clean commit with the conventional message + co-author trailer on
a non-default branch; the remote branch exists after push; the PR/MR body matches the write-up;
no `--force`/merge happened; `refactor-improve` was invoked.
## Resources
- `references/method.md` — the deterministic ship sequence, conventional-commit rules, and the
host-agnostic fallback (print-and-paste when no `gh`/`glab`).
- `examples/before-after.md` — worked example: a finished lane → branch, commit, drafted PR body, push.
- `scripts/draft-ship.mjs` — drafts the commit message + PR/MR body from run state (also prints the step checklist as JSON with `--checklist`).
- `templates/output.md` — the commit-message + PR/MR-summary scaffold.
## Chain position
Runs at the **end of the ship** phase, gated by `refactor-publish-checklist`'s GO. It consumes the
change write-up + audit trail to draft the PR, lands the branch, and hands off to `refactor-improve`
for the improvement retro that closes the run.
templates/output.md
# Ship Record — <project name>
> Refactor-chain · ship phase · lane `<lane>` · run <date> · gated on publish-checklist: **GO**
## Branch
- `<type>/<lane>-<slug>` (off `<default branch>` — default branch untouched)
## Commit message (shown to user before committing)
```
<type>(<lane>): <summary> (behavior preserved)
Behavior-preserving refactor via refactor-chain. Every step re-ran the recorded
baseline and matched; review gate passed; publish checklist GO.
- <step>: <what>
- <step>: <what>
<self-heal note, or "No drift; all steps clean on first apply.">
Co-Authored-By: Claude <noreply@anthropic.com>
```
## PR / MR summary
**Title:** `<type>(<lane>): <summary> — behavior preserved`
**Body:**
```
## What & why
<one-paragraph from the change write-up>
## Changes
- <step>: <what>
## Safety
- Baseline recorded; every step verified against it.
- Review gate: passed.
- Self-healed: <list, or "none">.
## Docs
- Change write-up · durable artifacts synced · audit trail (hash-linked).
🤖 Generated with refactor-chain
```
## Ship status
- Push: `<pushed to origin/<branch> | printed for manual push>`
- PR/MR: `<link | paste-ready body printed (no host CLI)>`
- Merge: **not merged** (human decision) · Force-push: **never**
- CI: running — watch at `<PR checks tab / gh run watch / glab ci status>`
## Handoff
- `refactor-improve` invoked to record the run outcome (history prior).