r-ship · git:20261023.6c2abfd · 2026-10-23 · sha256 2eb09e2a399bac3d

r-ship git:20261023.6c2abfdA

Immutable. This exact content never changes and is served at /api/v1/blob/2eb09e2a399bac3d.

---
name: r-ship
description: >
  Use when a plan is built and verified and you want to cut a release: preflight, verification, release prep, and publish.
---

You are running the /rdf:r-ship release workflow. This is a composite
workflow that verifies readiness, dispatches quality gates, prepares
release artifacts, and publishes a PR.

## Arguments

$ARGUMENTS (optional, any order):

- base branch override (default: auto-detect via
  `git symbolic-ref refs/remotes/origin/HEAD` or fall back to `main`)
- `--version X.Y.Z`: the version this run ships. When absent, Setup prompts
  for it (see *Target version* below)

## Setup

- Read .rdf/governance/index.md to understand the project
- Load governance/verification.md for project-specific release checks
- Load governance/conventions.md for commit/changelog format
- Determine project name, version, and branch from governance index

### Target version

Resolve the version *being shipped* before any gate runs. The on-disk version
still names the **previous** release until Stage 3 bumps it, so a gate that
reads it judges the wrong release.

1. If `$ARGUMENTS` carries `--version X.Y.Z`, that is the target.
2. Otherwise read the on-disk version (root `VERSION` file, or the project's
   in-file `VERSION=` variable) and prompt, defaulting to the next minor:
   `Target version for this release [next minor <MAJOR>.<MINOR+1>.0]:`
3. If no on-disk version is readable and the user supplies none, record
   `VERSION: unknown`.

Write the answer to the progress file's `VERSION:` field. Stages 1d and 3c
read the target from there, so a resumed session gates the same release.

## Resume Detection

Source `~/.rdf/state/rdf-bus.sh` and call `rdf_session_init`. If the helper is
absent (helpers not yet bootstrapped: fresh plugin install before its first
session restart), skip resume detection and proceed without session-scoped
progress; restart the session (plugin) or run `rdf deploy claude-code`
(checkout) to deliver them. Then look for
`.rdf/work-output/ship-progress-${RDF_SESSION_ID}.md`. If not found, glob
`.rdf/work-output/ship-progress-*.md` and present candidates ordered by mtime.
If exactly one un-suffixed `.rdf/work-output/ship-progress.md` exists (legacy
from pre-3.1.0), prompt: "Found legacy progress file. Import? [Y/n]".

If a progress file is found:

1. Read the STAGE and STATUS fields
2. Offer: "Found interrupted ship session at stage {stage}. Resume from here? [Y/n]"
3. If Y: skip stages already marked complete, resume from the current stage
4. If N: delete `ship-progress-${RDF_SESSION_ID}.md` and start fresh

After each stage completes, write state to `.rdf/work-output/ship-progress-${RDF_SESSION_ID}.md`:
```
STAGE: {preflight|verify|prep|publish|report}
STATUS: {complete|in-progress}
PR_URL: {if created}
VERSION: {target release version, resolved at Setup}
```

## Stage 1: Preflight

Run all four checks and display results as a task list. Each check
is pass/fail: a checked box means the gate passed.

### 1a. Plan Completion Check
- Source `~/.rdf/state/rdf-bus.sh`; `rdf_session_init`. Resolve the active
  plan: `plan_path="$(rdf_active_plan_path)"`.
- Read `$plan_path`: verify ALL phases are marked complete.
- If any phases are incomplete, report which ones and STOP.
- If no active plan resolves, skip this check (ad-hoc release).

### 1b. Working Tree Check
- Run `git status` (never `-uall`), `git diff --stat`
- If there are uncommitted changes, list them and ask the user
  whether to commit, stash, or abort
- If working tree is clean, proceed

### 1c. Branch Check
- Verify current branch is not `main`/`master` (should be a
  feature/release branch)
- Identify base branch for PR target
- Run `git log --oneline $(git merge-base HEAD origin/{base})..HEAD`
  to summarize branch changes

### 1d. Platform Triage

Per-minor gate (D4, `docs/specs/2026-09-02-platform-alignment-spike-design.md`
§6): a minor release must not ship without a current platform re-triage
verdict; patch releases are unaffected.

- Split the **target version** from Setup (the progress file's `VERSION:`
  field) into `MAJOR.MINOR.PATCH`. Do not read the on-disk `VERSION` here;
  Stage 3 has not bumped it yet, so it names the previous release and would
  gate the wrong one.
- If the target is `unknown`, mark `[x]` with *(skipped: no target version)*
  and skip the remaining steps below.
- If `PATCH` is non-zero the gate does not apply: mark
  `[x] **Platform triage**: *(skipped: patch release X.Y.Z)*` and skip the
  remaining steps below.
- Otherwise grep `docs/platform-triage.md` for a block matching
  `^## <MAJOR.MINOR> \(`:
  - Present: `[x] **Platform triage**: \`<MAJOR.MINOR>\` ledger current`.
  - Absent: `[ ] **Platform triage**: no \`## <MAJOR.MINOR> (<date>)\` block
    in \`docs/platform-triage.md\`` and
    `> **Blocked** (preflight failed): platform triage missing for
    <MAJOR.MINOR>`.

### Preflight Display

Present results as a task list with inline code for values:

```
### Preflight: target `3.7.0`
- [x] **Plan**: all phases complete (`12`/`12`)
- [x] **Working tree**: clean
- [ ] **Branch**: on `main` (*expected a feature/release branch*)
- [x] **Platform triage**: `3.7` ledger current
```

A patch target renders the last line as
`- [x] **Platform triage**: *(skipped: patch release 3.7.1)*`.

If a check fails, use an unchecked box and add an italic reason.
If a check is skipped, use a checked box with *(skipped)* annotation.

> **Blocked** (preflight failed): {reason}

Use a blockquote for any blocker that halts the workflow.

## Stage 2: Verification (dispatches subagents)

Dispatch two subagents in parallel:

### 2a. QA Verification
Dispatch qa subagent with:
- Scope: full branch diff against base
- Instruction: run every check in governance/verification.md
- Instruction: produce structured pass/fail report

### 2b. Reviewer Sentinel
Dispatch reviewer subagent in sentinel mode with:
- Scope: full branch diff against base
- Instruction: run all 3 passes (anti-slop, regression, security)
- Instruction: produce structured verdict report

### 2c. Gate Evaluation
- Collect both subagent reports
- If QA reports FAIL: show failures, ask user to fix or override
- If reviewer reports MUST-FIX: show findings, ask user to fix or
  override
- If both pass: proceed to Stage 3
- User can override gate failures with explicit confirmation

### Gate Display

Present gate results as a task list: checked = passed, unchecked =
failed or pending:

```
### Verification Gates
- [x] **QA**: *PASS*, `672` tests, `0` failures
- [ ] **Reviewer**: *MUST-FIX*, 2 findings (1 security, 1 regression)
```

If a gate fails, show the findings immediately below in a blockquote:

> **QA Gate Failed**: 3 test failures
> - `test/core.bats` line 42: expected `0`, got `1`
> - `test/trust.bats` line 118: timeout after 30s
> - `test/cli.bats` line 55: missing output line

Ask user to fix or explicitly override.

## Stage 3: Release Prep (main context)

### 3a. Changelog Generation
- Read existing CHANGELOG (and CHANGELOG.RELEASE if present)
- Generate entries from `git log` since last tag/release
- Use governance/conventions.md for changelog format
- Deduplicate entries (absorbs v2 rel-chg-dedup logic)
- Present changelog diff to user for approval

### 3b. AI Attribution Scrub
- If governance requires it (check conventions.md for attribution
  policy): grep for Co-Authored-By, AI attribution, Claude/Anthropic
  references in staged files
- Report any findings and offer to remove them

### 3c. Version String Verification
- Grep for version strings across the codebase (VERSION file,
  package.json, setup.py, Cargo.toml, etc.)
- Verify all version references are consistent
- Verify the on-disk version now equals the target recorded at Setup; this is
  what proves Stage 1d gated the release actually being shipped
- If mismatches found, report and ask user to fix

### 3d. Commit Release Prep
- Stage changelog updates and any scrub fixes
- Commit with project's message format from governance/conventions.md
- Push branch to origin

### 3e. Living-Spec Fold (docs/specs/CURRENT.md)

Fold this release's outcome into the maintained current-state spec. **Skipped
when `rdf_active_tier` is `bugfix`** (a defect fix does not change the
architecture) and when the plan File Map cannot be parsed (degrade, never
block the release).

1. `source ~/.rdf/state/rdf-bus.sh; tier="$(rdf_active_tier)"`. If `bugfix`, skip.
2. Read `docs/specs/CURRENT.md` (create with a living-spec header if absent).
3. Derive an ADDED / MODIFIED / REMOVED delta from the shipped plan's File
   Map (New→ADDED, Modified→MODIFIED, Deleted→REMOVED) plus the changelog
   entries from Stage 3a (one-line summaries). If the File Map cannot be
   parsed, emit a notice and skip.
4. Present the delta to the user for approval (a few bullets; keep it light,
   this is cheap, not ceremony).
5. On approval, prepend a `## <version> (<date>)` block to CURRENT.md and
   stage it with the release commit (amend Stage 3d's commit or a follow-up).

OpenSpec precedent; this must stay lightweight, never ceremony.

### Clear the active-plan and tier pointers

After the release tag/commit lands, clear the session pointers:

```bash
source ~/.rdf/state/rdf-bus.sh
rdf_clear_active_plan
rdf_clear_active_tier
```

The plan file in `docs/plans/` is retained as historical record; only
the session-scoped pointers are removed so the next planning session
starts clean with no stale tier.

### Release Prep Display

Present release prep results as a task list:

```
### Release Prep
- [x] **Changelog**: updated, `12` entries added to `CHANGELOG`
- [x] **Attribution scrub**: clean, no AI references found
- [ ] **Version strings**: *mismatch*: `VERSION` says `2.0.2`, `setup.py` says `2.0.1`
- [x] **Commit**: `a1b2c3d` pushed to `origin/2.0.2`
```

If version mismatches are found, use a blockquote:

> **Version Mismatch**: resolve before proceeding
> - `files/apf`: `VERSION="2.0.2"`
> - `setup.py`: `version="2.0.1"`

## Stage 4: Publish (main context, user confirmation required)

### 4a. Create PR
- Use `gh pr create` with:
  - Short title (under 70 chars) summarizing the release
  - Structured body with ## Summary (from changelog), ## Verification
    (QA + reviewer verdicts), ## Test Plan
- If PR already exists for this branch, print URL and skip creation
- Capture PR number and URL

### 4b. Monitor CI (if configured)
- Check for CI status checks on the PR
- If CI is running, poll periodically (up to 10 minutes)
- Report CI results as they complete
- If CI fails, report which checks failed

### 4c. User Confirmation
- Present final summary and ask user to confirm merge readiness
- Do NOT auto-merge; the user decides

### Publish Display

After PR creation, show a brief confirmation. Use inline code for
the PR URL and number so they stand out:

```
### PR Created
**PR** `#42`: {title}
**URL**: {url}
```

For CI monitoring, use a task list that updates as checks complete:

```
### CI Status
- [x] **lint**: *pass* (12s)
- [x] **test-debian12**: *pass* (1m 42s)
- [ ] **test-rocky9**: *running*
- [ ] **test-centos7**: *pending*
```

When CI completes, collapse into a single line in the final report.

## Stage 5: Final Report

Present a structured summary using tables, task lists, and
blockquotes. This is the final output the user sees; it must be
scannable at a glance.

```
## Ship Report: {project} `{version}`

| Property | Value | Property | Value |
|----------|-------|----------|-------|
| **PR** | {url} | **Commits** | `{count}` |
| **Branch** | `{branch}` -> `{base}` | **HEAD** | `{hash}` |

---

### Verification Gates
- [x] **QA**: *PASS*, `{test_count}` tests, `{fail_count}` failures
- [x] **Reviewer**: *APPROVE* ({summary})

### Release Prep
- [x] **Changelog**: updated, `{entry_count}` entries
- [x] **Attribution scrub**: clean
- [x] **Version strings**: consistent (`{version}`)
- [x] **Commit**: `{hash}` pushed to `origin/{branch}`

### CI
- [x] **Status**: *pass*, all checks green

---

### Verdict
**READY TO MERGE**: all gates passed, PR awaiting review
```

**Verdict styling**: use bold for the verdict keyword and a brief
reason. Three possible verdicts:

- **READY TO MERGE**: all gates passed, no action needed
- **ACTION NEEDED**: non-blocking findings or overridden gates
- **BLOCKED**: unresolved gate failures

When the verdict is not READY TO MERGE, use a blockquote to
highlight what needs attention:

> **ACTION NEEDED**: reviewer flagged 1 concern (overridden by user)

When the verdict is BLOCKED, list the blockers:

> **BLOCKED**: cannot merge
> - QA gate: *FAIL*, 3 test failures
> - Version strings: *mismatch* in `setup.py`

After presenting the final report, output the completion handoff:

> **Released**: PR `{url}`
> Merge when CI passes. Pipeline complete.

Delete `.rdf/work-output/ship-progress-${RDF_SESSION_ID}.md` after successful completion.

## Formatting Guide

All output from this workflow uses the markdown primitives below.
Follow these consistently across every stage.

| Primitive | Syntax | Use in this workflow |
|-----------|--------|----------------------|
| **Table** | `\| col \| col \|` | Ship report summary, PR metadata |
| **Task list** | `- [x]` / `- [ ]` | Preflight checks, gate results, release prep, CI status |
| **Blockquote** | `>` | Blockers, warnings, gate failures, version mismatches |
| **Bold** | `**text**` | Labels (PR, Branch, QA), verdict keywords |
| **Italic** | `*text*` | Status keywords (*PASS*, *FAIL*, *running*, *skipped*) |
| **Inline code** | `` `text` `` | Versions, hashes, branch names, paths, commands, counts |
| **Rule** | `---` | Section dividers in final report |

**Do NOT use** (not rendered in Claude Code):
HTML tags, `<details>`, ANSI color codes, Mermaid diagrams, footnotes.

**Conventions:**
- Task lists give instant pass/fail state: checked = passed,
  unchecked = failed or pending
- Blockquotes draw the eye to blockers; use them for anything that
  halts the workflow or needs user action
- Inline code makes values scannable; always wrap versions,
  hashes, branch names, paths, and commands
- Bold labels on the left, values on the right, consistent
  across task lists and tables
- Italic for status keywords only, to keep them visually distinct
  from labels and values

## Constraints
- Never auto-merge; the user must confirm
- Never force-push
- Always show verification results before creating PR
- If governance/verification.md is missing, warn but proceed with
  default checks (lint, test execution)
- Respect project commit protocol from governance/conventions.md