Skip to content
Back to skills

Rig Task

ASecurity

Implement one unit of work end-to-end — from a tracker issue OR an ad-hoc description: spec review, TDD (RED→GREEN→REFACTOR), pre-PR self-review, open a PR, then drive the review-bot loop to clean. Runs start→finish in one shot by default; `start`/`finish` are optional phases for pause/resume. Sibling to /rig-epic (one unit vs many). Never auto-merges unless `--auto-merge` is passed (then CI is the merge gate). Triggers on: 'implement', 'implement this', 'work on', 'pick up', 'start task', 's...

  • 3 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 19, 2026
ai-agentsrustgobashtestinggit

Works with

  • terminal
  • mcp

Security analysis

A100/100

Scanned September 19, 2026

npx -y skills add agent-rig/rig --skill rig-task --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Rig Task?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Rig Task
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/agent-rig-rig-task/badge)](https://www.skillsdirectory.com/skills/agent-rig-rig-task)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: rig-task
description: "Implement one unit of work end-to-end — from a tracker issue OR an ad-hoc description: spec review, TDD (RED→GREEN→REFACTOR), pre-PR self-review, open a PR, then drive the review-bot loop to clean. Runs start→finish in one shot by default; `start`/`finish` are optional phases for pause/resume. Sibling to /rig-epic (one unit vs many). Never auto-merges unless `--auto-merge` is passed (then CI is the merge gate). Triggers on: 'implement', 'implement this', 'work on', 'pick up', 'start task', 'start <ISSUE>', 'finish task'."
argument-hint: "[start|finish] [<issue-id> | \"<description>\"] [--local] [--base <ref>] [--auto-merge] — no subcommand runs start→finish in one shot. e.g. 'start ABC-18', 'start \"add dark mode\"', 'finish'."
---

# rig-task — implement one unit of work end-to-end

Take a single unit of work — a tracker issue **or** an ad-hoc "implement this"
with no issue filed — code it test-first, open a PR, drive it to a clean review,
and hand back. **Never auto-merges unless `--auto-merge` is passed** (then CI
gates the merge — see Step 5). This is the orchestrator over the kit's
building blocks: it delegates isolation to `/rig-worktree`, review to
`/rig-review`, and the fix loop to `/rig-review fix`, so each concern lives in
one place.

It's the single-unit sibling of `/rig-epic` (one unit vs many interleaved), and
mirrors its lifecycle: **`start`** gets you to an open PR, **`finish`** drives
the review to clean. By default a bare invocation runs both in one continuous
session — the phases exist for when you want to open the PR, let CI / the review
bot run, and `finish` later. `/rig-sprint` calls this once per item.

## Configuration

Reads `.rig/config.json` (defaults in parentheses):

- `tracker.provider` — `linear` | `github` | `none` (`none`). Selects how the
  spec is loaded and the PR is linked. `none` → the item is an ad-hoc task
  description; no ticket calls.
- `tracker.ticketPrefix` — e.g. `ABC-`; used to infer a ticket from the branch.
- `tracker.githubIntegration` — when `true`, GitHub drives PR/merge transitions
  (In Review, Done); rig still sets the start-of-work "In Progress" (Step 1),
  which GitHub can't observe before a PR exists. When `false`/absent, you may
  move states freely.
- `tracker.labelMapFile` (`.claude/label-mapping.md`) — label source of truth.
- `tracker.board` — GitHub Projects v2 identity (owner, projectNumber,
  statusOptions, closingKeyword). Used at PR-open (Step 4) to link the PR and
  move the board via the `rig-tracker` adapter. Absent → no board steps.
- `vcs.baseRef` (`origin/main`), `vcs.defaultBranch` (`main`),
  `vcs.branchConvention` (`{user}/{ticket}-{slug}`),
  `vcs.protectedBranchMergeQueue` (`false`).
- `runtime.packageManager` / `runtime.installCommand` — passed to `/rig-worktree`.
- `test.command` (`npm test`), `test.integrationCommand`, `test.e2eCommand`.
- `review.patternsFile` (`.claude/REVIEWER.md`), `review.bot`
  (`none`), `review.botRetrigger`, `review.maxRounds` (`5`).
- `style.guideFile` (`.claude/STYLE.md`) — the house style for the commit
  message, the PR body, and the hand-back line.
- `agents.architect` / `agents.qa` / `agents.coder` / `agents.reviewer`
  (default `rig-<role>`) — the registered agent names for each role.

**Unconfigured fallback:** with no profile, this runs with tracker `none` (ad-hoc
task), `npm test`, base `origin/main`, and a local-only review loop.

## Subcommands (phases)

`$ARGUMENTS` begins with an optional phase, then a target:

- **(no phase)** — run `start` then `finish` in one continuous session. The
  common case: "implement this."
- **`start [<issue-id> | "<description>"]`** — Steps 1–5: load/decide the spec,
  set up a worktree, TDD to green, pre-PR self-review, and **open the PR**. Then
  stop. Use to kick a unit off and let CI / the review bot run while you move on.
- **`finish [<issue-id | PR#>]`** — Steps 6–7: resolve the open PR (from the arg,
  the current branch, or the single open PR for this unit), drive the review-bot
  loop to a terminal state, and hand back. Resumes a unit `start` (or a teammate)
  opened.

**Target resolution:**
- An `<issue-id>` matching `tracker.ticketPrefix` (and tracker ≠ none) → load the
  spec from the tracker.
- A quoted `"<description>"` (or tracker = none) → **ad-hoc**: the description is
  the spec; no tracker record required. Optionally offer to file an issue at the
  end.
- Omitted → infer from the current branch's `{tracker.ticketPrefix}<N>` token;
  else ask.

`--local` — in the `finish` phase, force the local `/rig-review fix` loop even if
a cloud auto-fix workflow is enabled (see Step 6).

`--base <ref>` — branch the worktree from, and target the PR at, `<ref>` instead
of `vcs.baseRef`. A caller like `/rig-epic` passes the integration branch here so
the child stacks on it rather than the trunk.

`--auto-merge` — once the pre-PR self-review (Step 4.5) is clean, **squash-merge**
the PR: `gh pr merge <N> --squash --delete-branch --auto` so it lands **when CI /
required checks pass — CI is the merge gate, not a human**. **Always squash** —
into a stacked integration branch *or* the trunk (don't rebase-merge; many repos
disallow it). If there are **no** required checks (`--auto` can't arm / the PR is
already mergeable), merge directly (drop `--auto`). If `vcs.protectedBranchMergeQueue`
is true, pass `--auto` with **no** method flag (the queue decides). Without this
flag the skill never merges — it opens the PR and hands back. `/rig-epic` passes
`--auto-merge` per child so each lands on the integration branch on its own.

`--spec-cleared` — skip Step 2's blocking pause: run the spec review for its notes
but do **not** stop for a human to resolve blockers. Use when the caller already
front-loaded and cleared the spec (e.g. `/rig-epic run`'s front-loaded spec review
resolved every child's blockers up front), so this child doesn't re-pause on a
question already answered. Spec review still informs RED/GREEN; it just never gates.

Print the resolved unit + phase as the first output line, e.g.
`rig-task start: ABC-369 (from branch alice/abc-369-...)` or
`rig-task start: ad-hoc "add dark mode"`.

## Run state

This skill runs long enough to outlive its own transcript. A compaction between
Step 3 and Step 6 drops the acceptance criteria, the failing assertion, and
which review findings you already fixed. Keep them on disk instead, in a
validated run-state document. See [`docs/state.md`](../../docs/state.md).

Resolve the helper once: `<STATE>` = `bun <SCRIPTS>/rig-state.ts`, with
`<SCRIPTS>` resolved from the repo root — `.claude/scripts/` then
`.rig/scripts/`, first hit wins, as in [`/rig-worktree`](../rig-worktree/SKILL.md). If the script is absent, skip every state call below and run as
before — the state is an accelerator, not a dependency.

**Run id:** the ticket, lowercased (`abc-18`), or a kebab slug of the task in
ad-hoc mode. `start` and `finish` share it. That reuse is what lets `finish`
read what `start` recorded instead of reconstructing it.

Each step below ends with the patch it owes. Two rules govern what goes in:

- **Persist what a later step reads**, not what happened. Record the branch
  name, the failing assertion, and the reason you rejected an approach. Don't
  record a narration of Step 3.
- **A rejected patch means fix the patch**, not the state. The tool prints the
  reason and leaves the state untouched; correct it and retry. A rejection on
  `phase: pr-open` means a gate genuinely isn't met.

---
---
The steps below are grouped into the two phases. **Steps 1–5 are `start`;
Steps 6–7 are `finish`.** A bare `rig-task` runs all of them in order.

## Step 1 — Load the spec and set up a worktree

0. **Base guard — before any worktree or agent work.** If this item is an epic
   **child** — its tracker parent is an epic, or an integration branch
   `<parent-slug>-*` for its parent already exists on origin (`git ls-remote
   --heads origin`) — **and no stacked `--base` was given**, stop now: report that
   it must stack on `<integration-branch>` (or be driven by `/rig-epic`), and do
   **not** set up a worktree or spend spec-review/TDD. This catches an epic child
   mistakenly launched against the trunk before any agent runs. (When `--base` is
   supplied, a child is expected — proceed.)
1. **Load the spec.**
   - `tracker: linear` → fetch the issue via the Linear MCP `get_issue`; the
     description + acceptance criteria are the spec. **Set the issue to "In
     Progress"** now (Linear: `save_issue` with `state: "In Progress"`) — work
     is starting. Do this **even when `tracker.githubIntegration` is true**: a
     GitHub integration can't observe local work until a PR exists, so this is
     the one transition it can't drive. It's idempotent (only move if not already
     started). rig then **ensures In Review / Done idempotently** later (Steps
     5/7) — deferring to a live integration if it already moved them, driving them
     itself if nothing did (see the adaptive note in Step 5).
   - `tracker: github` → fetch the issue via `gh issue view`.
   - `tracker: none` → the argument/task description is the spec; if it's a
     one-liner, ask the user to expand the acceptance criteria.
   Restate the acceptance criteria to yourself before coding.
2. **Set up an isolated checkout via `/rig-worktree`** (don't inline `git worktree
   add` — the skill owns fetch-before-branch, env symlinks, and per-worktree
   install). Branch name:
   - `tracker: linear` and the payload carries a suggested per-user branch name
     (`gitBranchName`) → use it **verbatim** (it's what a tracker↔GitHub
     integration matches on for auto-state).
   - Otherwise build from `vcs.branchConvention`: `{user}` = git user, `{ticket}`
     = the ID (or a kebab slug of the task in ad-hoc mode), `{slug}` = short
     kebab of the title.
   Base from `vcs.baseRef`. `cd` into the printed worktree path (`$WT`); use it
   as `{worktree-path}` in the agent prompts below.

3. **Open the run state** with the spec from Step 1.1:
   ```bash
   <STATE> init rig-task <run-id> --json '{"spec":{"id":"…","title":"…","source":"linear|github|adhoc","acceptanceCriteria":["…"]},"base":"<vcs.baseRef or --base>","nextAction":"spec review"}'
   ```
   Then patch the checkout: `<STATE> patch <run-id> --step step-1 --json
   '{"branch":"…","worktree":"$WT"}'`. If the run already exists, you're
   resuming — `<STATE> show <run-id>` and continue from its phase.

   **Name the session** so a background-job list reads as the work, not the
   prompt (Claude-Code-only; no-ops elsewhere). Pass `--name` through to
   `/rig-worktree` — `"FEAT: <title> (<ticket>)"` for new behavior/bug fixes
   (conventional-commit `feat:`/`fix:`), `"CHORE: <title> (<ticket>)"` for
   maintenance (`chore:`/`refactor:`/`test:`/`docs:`). Add `--skip-if-prefix
   "EPIC:"` so an epic label set by `/rig-epic` wins when this runs as a child.

## Step 2 — Spec review

Launch the **architect** and **qa** agents in parallel (names via `agents.*`):

- **architect**: "Review this spec for implementability. Identify ambiguities,
  missing acceptance criteria, files that need to change, and a suggested
  implementation order. Spec:\n{title}\n\n{description}"
- **qa**: "Review this spec from a testing perspective. What test cases are
  needed? Are the acceptance criteria testable? What edge cases matter?
  Spec:\n{title}\n\n{description}"

**Patch:** `{"phase":"spec-review","decisions":["<what you clarified, one line each>"],"blockers":["<what a human must answer>"],"nextAction":"write failing tests"}`.
Blockers are what a resumed session needs to know it's parked on — clear them
(`"blockers":null`) once they're answered.

If either flags something that should be fixed before coding, clarify it — and,
in tracker mode, update the item's description with the clarification and tell
the user what changed. **With `--spec-cleared`** (a caller like `/rig-epic run`
already front-loaded and resolved the specs), skip this pause: keep the architect
+ qa notes to inform RED/GREEN, but do **not** stop for blockers — proceed
straight to Step 3.

## Step 3 — RED: tests first

Launch **qa** to write tests against the spec, *before* any implementation:

- **qa**: "Write tests for this item. Cover every acceptance criterion + the
  edge cases the architect flagged. Don't stub or comment out — the tests must
  compile and fail for the right reason (missing implementation), not a syntax
  error. Worktree: {worktree-path}. Match the project's test framework and
  colocation conventions.\n\nSpec:\n{title}\n\n{description}\n\nArchitect
  notes:\n{architect_output}"

Run the suite from the worktree: `cd "$WT" && <test.command>`.

**Patch:** `{"phase":"red","tests":{"command":"<test.command>","status":"red","fail":<n>,"failures":["<the assertion, not the stack trace>"]},"nextAction":"minimum implementation"}`.
The failing assertions are the spec Step 4 codes against, so they're worth the
bytes; the stack traces aren't.

**Verify red.** The new tests must fail with a message that reflects the missing
behavior (e.g. "expected X, got undefined" / "not-yet-implemented symbol"). A
new test that passes immediately was pinning existing behavior — send it back to
qa. A syntax error is a qa bug; fix it before moving on.

## Step 4 — GREEN: minimum implementation

Launch **coder** with the failing tests as the spec:

- **coder**: "Implement {item} to make the failing tests pass. Worktree:
  {worktree-path}. Explore the affected files first and prefer *extending or
  reusing* existing code over a parallel implementation. Make the minimum change
  — no features not required by a test.\n\nSpec:\n{title}\n{description}\n\n
  Architect notes:\n{architect_output}\n\nFailing tests:\n{test_output}"

**Patch once green:** `{"phase":"green","tests":{"status":"green","pass":<n>,"fail":0,"failures":null},"nextAction":"pre-PR self-review"}`.

Re-run the suite. If still red, send coder a fix iteration with the current
failures. **Max 3 iterations**; if still red after 3, stop and surface to the
user — the spec is likely wrong or there's an unstated constraint. If
implementing exposes a missing edge case, add that test first (loop back to RED
for it) rather than piling untested behavior into GREEN.

## Step 4.25 — REFACTOR (optional, only while green)

If the GREEN code has obvious duplication, awkward naming, or a helper that
wants extracting, spawn **coder** once more — "no new behavior, tests stay
green" — and re-run the suite. Skip if the code is already clean.

## Step 4.5 — Pre-PR self-review (gate before push)

The point: pre-empt the PR review bot — every finding caught here is one you
don't pay a round-trip on. Delegate so the gate lives in one place:

1. **Find.** Run `/rig-review {vcs.baseRef}` for this item (in
   `{worktree-path}`). It walks `review.patternsFile` against
   `git diff {vcs.baseRef}...HEAD` and returns a triaged P0–P3 list.
2. **Classify and fix:**
   - **No P0/P1** → proceed to Step 5 (P2/P3 are fine to ship; report them).
   - **Any P0/P1** → run `/rig-review fix --source local --base {vcs.baseRef}` in
     `{worktree-path}`. It spawns **coder** per finding, re-runs `/rig-review`,
     and loops to clean (max `review.maxRounds`). If still P0/P1 after the last
     round it returns `unresolved` — stop, surface to the user, don't push.
3. Report the final state before pushing, e.g. `tests: green, review: 0 P0/P1 +
   2 P2 (deferred)`.
4. **Patch:** `{"phase":"self-review","review":{"source":"local","round":<n>,"p0":0,"p1":0,"p2":<n>,"open":["<deferred P2, one line each>"]},"nextAction":"open the PR"}`.
   The `pr-open` phase is guarded on `tests.status: green` and zero P0/P1. A
   rejection here means the gate isn't met — go fix the code, not the patch.

## Step 5 — Push and open the PR

1. Commit all changes with a message referencing the item.
2. Push the branch.
3. Open a PR (`gh pr create`) targeting `vcs.defaultBranch` (or the base from
   Step 1 if a stacked base was used):
   - Title carries the item ID where a tracker is used (e.g. `feat(ABC-18):
     …`), so it's visible in the PR list, not just the body.
   - Body includes: a summary; the tracker link (`Fixes ABC-18` for Linear /
     `Closes #<n>` for GitHub — the keyword that auto-links and, with
     `tracker.githubIntegration`, auto-transitions state); a test plan; and an
     **`## Architecture`** section stating any new abstraction/package/
     dependency/migration or core-domain touch and **why existing code wasn't
     reused** (write "No architectural change." otherwise).
   - Write both to `style.guideFile` (default `.claude/STYLE.md`): commit subject
     in the imperative under ~72 characters, PR summary that answers *what
     changed and why* in its first sentence, a test plan another person could
     run. Don't narrate the diff — the diff is right there.
   - **Proof the draft body before creating the PR** — `/rig-proof find` on it
     (pipe the draft in on stdin) and apply what comes back. Every reviewer reads
     this body; rewriting it after it's posted notifies all of them again.
4. **Link the PR + move to In Review — adaptively** (works with *or* without a
   live tracker↔GitHub integration; treat `tracker.githubIntegration` as a hint,
   not a gate — it may claim `true` while nothing is actually connected):
   - **`tracker: linear`:**
     - **Link:** `get_issue`; if no attachment already references this PR URL (a
       live integration may have added one), `create_attachment` with the PR URL.
     - **In Review:** `get_issue`; if the item is **not** already In Review or
       beyond, move it there (`save_issue`). If the integration already advanced
       it, leave it — never clobber a further-along state.
   - **`tracker: github`: go through the tracker adapter** (resolver: `<TRACKER>`
     = `.rig/rig-tracker` if executable else `<RIG_DIR>/scripts/rig-tracker.sh`;
     see [`docs/tracker-adapter.md`](../../docs/tracker-adapter.md)):
     - **Link:** `<TRACKER> link-pr <issue#> <pr#>` — idempotently ensures the PR
       closes/links the issue (Step 3 may already have written the `Closes #<n>`;
       `link-pr` is a no-op then). This is what moves the board to **Done** on
       merge via the Project's built-in automation.
     - **In Review:** if `tracker.board` is configured,
       `<TRACKER> set-status <issue#> "<tracker.board.statusOptions.inReview>"` —
       no GitHub *event* moves a card to In Review, so the adapter does it. Skip
       if no board is configured.
   Ensure the item's labels match `tracker.labelMapFile` if that file exists.
5. Capture the PR number for Step 6, and **patch:**
   `{"phase":"pr-open","pr":{"number":<n>,"url":"…","state":"open"},"nextAction":"drive the review loop"}`.
   This is what a later `finish` resolves the PR from — no re-derivation from
   the branch.

**Merge behavior.** Without `--auto-merge`, **do NOT `gh pr merge`** — open the PR
and hand back (the human, `/rig-sprint`, or `/rig-epic` decides). **With
`--auto-merge`**, the self-review is clean, so enable `gh pr merge <N> --squash --auto`
now (always squash — see the `--auto-merge` flag doc above); **CI/required checks are
the merge gate** — the PR lands on its own when they pass. Either way, don't hand-add PR
labels if the project ships a PR-labeler workflow — it applies them.

---
## ── `finish` phase (Steps 6–7) ──

When resuming via `rig-task finish` (rather than a one-shot run), first
**re-establish context from the run state** — `<STATE> show <run-id>` (or
`<STATE> list --skill rig-task` if you're not sure of the id). It carries the
spec, the branch, the worktree, the PR, and what the self-review deferred, so a
compacted or brand-new session picks up where `start` left off. `cd` into
`worktree` and continue.

**If there's no state** — the run predates it, or `start` ran without the
script — fall back to the old path. Resolve the open PR and its worktree from
the target arg, the current branch, or the single open PR for this unit, then
`cd` into that worktree.

## Step 6 — Review-bot loop: watch (default) or drive (`--local`)

- **`review.bot: none`** → nothing to do; the Step 4.5 local gate was the whole
  review. Go to Step 7.
- **A cloud auto-fix workflow is enabled** (the project installed
  `ci/workflows/auto-review-fix.yml` and its enable marker) and `--local` was
  **not** passed → **watch**: do not fix or push. Poll the PR (~60s intervals,
  up to ~30 min) until the bot's review reaches a terminal state and report it.
  Running a local driver alongside a cloud one collides (both push, both
  re-trigger), so only watch here.
- **Otherwise (drive)** → delegate to **`/rig-review fix <PR>`**. It owns the bot
  protocol for `review.bot` (poll + classify the bot's review, fix via
  **coder**, commit, push, re-trigger via `review.botRetrigger`), up to
  `review.maxRounds`. Take its returned outcome as this step's result:
  - `clean` — reviewed with no actionable issues (merge gates take over).
  - `actionable` — still has feedback after the last round; left for a human.
  - `timeout` — bot didn't respond in time; left for a human.

**Patch:** `{"phase":"review-loop","review":{"source":"bot","round":<n>,"outcome":"clean|actionable|timeout"},"nextAction":"hand back"}`.

Only `clean` is merge-green; everything else stops for a human.

## Step 7 — Hand back

Print the final outcome line and the PR URL. The outcome line carries real
state, not reassurance — `tests: 412 pass, 0 fail · review: 0 P0/P1, 2 P2
deferred · PR #418 open`, never "everything looks good".

**Tracker state — adaptive** (don't
trust `tracker.githubIntegration`; check reality): if the PR has already **merged**
by hand-back (e.g. `--auto-merge` landed it), ensure the item is **Done**
(`get_issue`; move only if not already Done — defer if a live integration closed
it). If the PR is still open, leave it **In Review** — the merge event (a later
`finish` run, `/rig-epic`'s merge gate, or a live integration) moves it to Done.
Never clobber a further-along state.

**Close the run.** Patch `{"phase":"done","nextAction":null}` once the PR is
open and the review reached a terminal state (`done` is guarded on a
`pr.number`). Keep the state file — `/rig-doctor` reports stale runs, and the
journal (`<STATE> journal <run-id>`) is how a human reconstructs a run that went
wrong. Delete it with `<STATE> rm <run-id>` when the PR merges.

If invoked by `/rig-sprint`, **return the outcome string** so the caller can decide
whether to merge, and skip any local-QA offer (the caller makes it once).

## Error handling

- Item can't be loaded/fetched → tell the user immediately.
- Branch already exists → ask whether to continue on it or start fresh.
- Test command times out or crashes (not just failures) → report and stop
  before opening a PR.
- `gh pr create` fails because a PR already exists → capture that PR's number
  and skip to Step 6.

## Env files in worktrees

A worktree is a fresh checkout — gitignored env files aren't carried over, and a
missing secret often reads as a flaky/timeout test failure. Step 1's `/rig-worktree`
setup symlinks gitignored env files from the main repo into the worktree before
any command runs. To re-link by hand, re-run the worktree setup with `--reuse`.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.