Skip to content
Back to skills

Worktree Pipeline Skill

ASecurity

Tracker-ticket-to-merged-PR pipeline via git worktrees — sync, plan, adaptive review, /run-plan, code review, PR merge. Triggers: run-worktree-pipeline, worktree pipeline, ticket to PR pipeline, tracker ticket pipeline.

  • 6 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 5, 2026
documentationgoshellbashsqlvuenodecode-reviewgitdatabasefrontend

Works with

  • claude code
  • terminal
  • cli
  • mcp

Security analysis

A100/100

Scanned October 5, 2026

npx -y skills add darellchua2/civiltekk-skills --skill worktree-pipeline-skill --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Worktree Pipeline Skill?

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

Security grade badge for Worktree Pipeline Skill
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/darellchua2-worktree-pipeline-skill-civiltekk-skills/badge)](https://www.skillsdirectory.com/skills/darellchua2-worktree-pipeline-skill-civiltekk-skills)

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: worktree-pipeline-skill
description: >-
  Tracker-ticket-to-merged-PR pipeline via git worktrees — sync, plan,
  adaptive review, /run-plan, code review, PR merge. Triggers:
  run-worktree-pipeline, worktree pipeline, ticket to PR pipeline,
  tracker ticket pipeline.
license: Apache-2.0
compatibility: opencode
metadata:
  pattern: hub-and-spoke
category: Git/Workflow
---

## What I do

I run the **full ticket-to-merged-PR pipeline**, one ticket at a time, each in
its own **git worktree** so the main working tree stays free. I am the
orchestrator: heavy knowledge lives in the skills/subagents I drive
(`ticketing-skill` for new tickets, `plan-execution-inline-skill` by
default (`plan-execution-skill` --gate on explicit subagent opt-in) for
execution, skill `civiltekk-pr-workflow-skill` create route by default
(`pr-workflow-subagent` on the opt-in subagent arm) for the PR) — I own
sequencing, PLAN authoring, worktree lifecycle, and re-validation.

Usage: `/run-worktree-pipeline [--dry-run] [base-branch] <ticket-refs...>`

## Step 1 — Parse arguments

- Leading `--`-flags are stripped before the first-token test (`--dry-run`
  is the only flag).
- First token is a **base-branch** iff it fails the ticket regex
  `^(#\d+|[\w.-]+/[\w.-]+#\d+|[A-Z][A-Z0-9]+-\d+|[\w.-]+/[A-Z][A-Z0-9]+-\d+)$`
  **and is not purely numeric**. Full first-token taxonomy, in test order:
  `--`flags → ticket forms (`#N` GitHub · `owner/repo#N` cross-repo GitHub ·
  bare numeric `N` (auto-`#N`) · `KEY` tracker · `repo/KEY` cross-repo
  tracker) →
  base-branch fallthrough. Every accepted token shape is listed here — a
  variant that matches none of them is the base-branch, never silently
  dropped.
- Bare numerics (`351`) auto-normalize to GitHub issue refs (`#351`).
- **`repo/KEY` cross-repo refs** (`canvastekk-workflow-engine/DA-2952`) name
  a foreign repo: resolve it to the sibling checkout `../<repo>` relative to
  the main checkout; it must exist and be a git repo — missing → one batched
  user ask for the local path, still unresolved → abort with a clear error.
  Bare `KEY`/`#N`/`owner/repo#N` behave exactly as before (session repo or
  named GitHub repo). Everything repo-scoped downstream — base branch, its
  validation, the worktree root, and the gh context — resolves **per
  ticket's repo** (Steps 2–4 run `git -C <repo>` and `gh ... -R
  <owner/name>` for foreign repos; the session repo is unchanged).
- Zero ticket refs → print usage and stop.
- The base-branch sets **both** where feat branches are cut from AND the PR
  target. Default (omitted): repo default branch via
  `git symbolic-ref --short refs/remotes/origin/HEAD` (yields
  `origin/<base>`; strip the prefix; fallback `main`).
- **Validate the base** after resolving it (per repo for `repo/KEY`
  tickets): `git ls-remote --exit-code --heads origin <base>`; non-zero exit
  → abort with a clear error naming the attempted base (fail-fast — never
  reach Step 2 with a typo'd base).
- **`--dry-run`**: print the resolved base (per repo), ticket execution
  order, per-ticket predictions — merged / held-on-`blocked-by:` /
  held-on-open-PR-overlap — which PRs will get background merge watchers,
  and the would-be `feat/<KEY>` branch + worktree names (per repo), then
  stop before Step 2. Read-only: no writes, no branch/worktree/remote
  mutations.
- **Arm selection (default inline — capability binding per AGENTS.md
  §Portability contract)**: every invocation runs the **inline arm** —
  fully in-session, zero subagents — unless the **subagent arm is opted
  in**, which requires ALL three: (1) the user explicitly requested
  subagent orchestration, (2) the harness is OpenCode, (3) the subagent
  deps resolve (preflight below). Any unmet condition → run inline with a
  prominent note (requested-but-unavailable → say so and proceed inline;
  never abort on a subagent-arm miss once the inline deps resolve).
  - OpenCode: subagent tool + deployed agents — opt-in only
  - Claude Code: subagents not wired for this skill — inline always
  - Other/none: inline always
- **Dependency preflight (per-skill installs, resolved per arm)**: the
  inline arm (the default) hard-requires skill
  `plan-execution-inline-skill` (Step 8) + skill `code-review-inline-skill`
  (Step 9 — the wrapper resolves the deployed `agents/code-review-subagent.md`
  checklist itself) + skill `civiltekk-pr-workflow-skill` (Step 10, create
  route), resolved by the skill loader. The
  opt-in subagent arm hard-requires skill `plan-execution-skill` --gate
  (Step 8) and agents `code-review-subagent` (Step 9) +
  `pr-workflow-subagent` (Step 10). Any missing dep for the arm actually
  running → abort (`failed`) with the install hint
  `npx github:darellchua2/civiltekk-opencode-claude-skills add <name>`
  (sole exception: the opted-in subagent arm with unresolvable deps falls
  back inline per the arm-selection rule). Soft deps
  degrade with a note: `ticketing-skill` (only for new-work tickets,
  Step 3), `architecture-review-subagent` / `uiux-reviewer-subagent` /
  `requirements-specialist-subagent` (Step 7 skip-with-note rule). The
  inline arm routes architecture review to skill `architecture-review-skill`
  (reviewer-baseline-skill first) instead of the deployed agent file.
- **Execution model (pipelined)**: ticket order = authoring order, but only
  **one implementation runs at a time**. The next ticket's implementation
  starts once the active ticket has **created its PR (Step 10a)** — not once
  it merges — AND this ticket's own blockers (below) have merged; unblocked
  tickets never wait on CI. A held ticket (blocked-by, 6f, or 10a overlap)
  **releases the implementation lane** — the next implementable ticket
  starts immediately. Each PR ships with a **background merge
  watcher** (Step 10b), so any number of PRs may be awaiting merge
  concurrently while the next implementation proceeds.
- **`blocked-by:` hold, not skip**: if a ticket's body contains
  `blocked-by: <ref>` naming a ticket that is not yet merged (an open PR
  counts as unmerged), **hold** it — report as held, keep it in run order.
  Evaluation point: the Step 3 body fetch (dry-run predicts it earlier); a
  blocked-by hold parks with whatever state exists — typically none, before
  branch/worktree/PLAN. **Auto-resume** when the blocker's merge
  notification arrives: re-enter at the first unexecuted step — rebase
  `feat/<KEY>` onto the updated base **only if the branch already exists**
  (push `--force-with-lease` after a resume rebase). Contrast: 6f/10a
  overlap holds park AFTER PLAN authoring, so their resume continues at
  Step 7 / 10a. Tickets still held when nothing else is runnable are
  reported deferred at run end, not failed. (No tracker link traversal —
  body text only.)

## Steps 2-10 — per ticket (in order)

2. **Sync + branch** (all git/gh run in the ticket's repo — `git -C <repo>`
   and `gh ... -R <owner/name>` for foreign repos; session repo unchanged):
   `git fetch origin <base>`. **Merged-ticket check**:
   `gh pr list --state merged --head feat/<KEY>` non-empty → the ticket is
   already merged; report the skip with a note and advance to the next
   ticket. Otherwise cut `git branch feat/<KEY> origin/<base>`. If the
   branch or worktree already exists (mid-pipeline failure leftovers OR a
   held ticket resuming), report state and ask: prune / resume / refuse —
   never clobber silently; **resume** is the held path (Step 1): rebase
   `feat/<KEY>` onto the updated `origin/<base>` (push `--force-with-lease`
   after the rebase) and re-enter at the first unexecuted step.
3. **Ticket fetch/create**: existing ref → fetch its description (`gh issue
   view [-R <owner/name>]`; tracker tickets per `ticketing-skill`
   §MCP Availability Guard — degrade with a clear report when the tracker is
   unavailable). New work → create the
   ticket first via `ticketing-skill` (`/create-ticket`), then
   continue.
4. **Worktree**: locate the **main** checkout via
   `git worktree list --porcelain | sed -n 's/^worktree //p' | head -1`
   (NOT `$(git rev-parse --show-toplevel)` — that nests when invoked from a
   worktree). Create `git worktree add <root>/<KEY> feat/<KEY>` — **always,
   even when the ticket is in this repo**. `<root>` is
   `$WORKTREE_PIPELINE_ROOT` when set (applies to ALL tickets — foreign
   repos included, a documented asymmetry), else
   `<ticket-repo>/../worktrees/` — derived from the ticket's repo, so
   `repo/KEY` tickets get a worktree root beside their own checkout.
   Pre-flight `git worktree list` for stale `<KEY>` entries.
   **CodeGraph index (conditional)**: iff the ticket repo's checkout has
   `.codegraph`, run `git -C <root>/<KEY> check-ignore -q .codegraph/` first — exit 0
   (ignored on the ticket branch) → run `npx @colbymchenry/codegraph init -i`
   **inside the new worktree** (before Step 5; 5–60s, index gitignored);
   exit 1 → skip init entirely with a one-line note (".codegraph/ not
   ignored in target repo — skipping init to keep commits clean") and
   continue on the rg/grep fallback (any other `check-ignore` exit →
   treat as the same soft-skip path); CLI absent or init failure → one-line
   soft-skip note and continue on rg/grep. No `.codegraph/` in the ticket
   repo's checkout → skip silently. Never write ignore entries (tracked
   `.gitignore` edits stage into per-phase commits; per-worktree
   `info/exclude` is not honored by linked worktrees). Never symlink the
   main checkout's `.codegraph/` into the worktree — the index reflects the
   main checkout's branch state and paths (sharing undocumented).
   **Ticket start transition (worktree exists = work began)**: once the
   worktree is created, tracker tickets get the `ticketing-skill` §Start
   transition (check-first idempotent; GitHub issues = no-op with a note —
   no status field, `Closes #N` covers close-on-merge). The Atlassian §MCP
   Availability Guard applies — JIRA unavailable → report the transition
   skipped, never block the run. A ticket that later fails or stays held
   legitimately remains In Progress (`ticketing-skill` §Start honest-state
   rule). `--dry-run` and `/worktree-pipeline-preview` stay read-only: they
   stop before any mutation and never transition.
5. **Re-validate**: cross-check the ticket description once more against the
   latest `origin/<base>` content **in the worktree**; if stale, update the
   ticket and note deltas before proceeding.
6. **PLAN authoring** (self-contained — this skill owns it; see §PLAN
   Authoring): adopt/generate the ticket-scoped PLAN in the worktree, run the
   atomicity self-check, commit and push it on `feat/<KEY>`.
7. **Plan review (§Adaptive Review)**: you triage before delegating — from
   the ticket, the PLAN's Dependency & Consumer Map, and the touched paths,
   select reviewers, then issue **parallel Task calls** for the selected
   ones only (a selected reviewer absent from this session's agent list →
   skip it with a note; per-skill installs may not carry every reviewer):
   - `architecture-review-subagent` iff the Consumer Map has **cross-module
     nodes** (a consumer beyond the node itself). Default (inline arm): run
     the review in-session via skill `architecture-review-skill`
     (reviewer-baseline-skill first) — the Task-call route below is the
     opt-in subagent arm.
   - `uiux-reviewer-subagent` iff **frontend signal** (tsx/jsx/vue/svelte/css
     files, components/pages/app paths, UI keywords in the diff).
   No proactive requirements review — requirements coverage is
   reviewer-owned: each selected reviewer verifies the PLAN against the
   ticket's stated requirements and emits **Requirements Gaps** for
   anything missing or ambiguous (never a silent assumption). A thin-map
   backend ticket may select zero reviewers — Step 9 code review
   (unconditional) backstops.
   **Requirements Gaps relay**: any reviewer (here or Step 9) returning a
   non-empty `Requirements Gaps` array → relay it to
   `requirements-specialist-subagent` **Mode R** and apply the answers to
   the PLAN before proceeding (max 2 relay rounds — agent contract bound;
   agent absent → surface the gaps to the user directly and proceed on
   their answers).
   Triage assumptions (stated, not hidden): a thin Consumer Map may skip
   architecture review, so author the map honestly at Step 6.
   `coverage-subagent` is NOT part of plan review — it is a coverage
   *reporting* agent, so reviewing a pre-implementation PLAN is a stage
   mismatch (nothing measurable exists yet). Apply findings to the
   PLAN; re-review only when findings were structural. Zero selected
   reviewers → skip delegation entirely.
8. **Execute**: run `plan-execution-inline-skill` with
   `PLANS/PLAN-${KEY}.md` **inside the worktree** — always pass the explicit
   PLAN path, never rely on branch-name auto-detect (opt-in subagent arm:
   `plan-execution-skill` --gate with the same explicit path). Plan review
   happened upstream in Step 7 — the executor must not re-review. Gate
   sequence, tier selection (light default per phase; full per
   `verification-loop-skill` §Tiered gating), pass semantics, and memo
   format come from `verification-loop-skill` §The gate contract (this
   skill defines none of them); the executor commits + pushes per phase and
   writes the gate memo, and the run's last gate — the **ticket exit
   gate** — is full.
9. **Code review** (default — inline arm: invoke `code-review-inline-skill`; it owns
   baseline-first, checklist resolution, and the review loop; the remainder
   of this section describes the opt-in subagent arm): `code-review-subagent` has `edit: deny` (bash is allowlisted to read-only git, and its cwd is the session checkout, not the worktree) — **you compute
   the diff** (`git diff origin/<base>...feat/<KEY>` and `--stat`) and embed
   it (file list + hunks) in the Task prompt. Fix findings: severity ≥
   Major mandatory; Minor by judgment. **Re-gate after review fixes**: fix
   commits land after the exit gate, so before pushing a fix commit re-run
   the **full** gate once on the fixed tree and append its `tier=full` memo
   line — the final pushed SHA must carry a green `tier=full` memo (a
   review-fix push without one breaks Step 10's citation). Relay any
   non-empty `Requirements Gaps` array per Step 7's relay rule before fixing.
   **LEARNINGS capture is yours, not the reviewer's**: reviewers have no
   write access — they return LEARNINGS candidates as report content (a
   `LEARNINGS candidates:` block). LEARNINGS writes stay working-tree only
   through the run — Step 8 phase commits never stage them (canonical
   rule: `continuous-learning-skill` step 6). For each candidate, write
   `LEARNINGS/<category>/<slug>.md` in the worktree (skip if the file
   already exists; suffix `-2` on a genuine distinct-entry collision) and
   append its `_index.md` entry. At end of ticket — after the bounded
   review loop, before Step 10a — land **one dedicated `chore(learnings)`
   commit** sweeping every LEARNINGS write of the run (phase-time captures
   plus review candidates: all bodies + `_index.md`), never folded into a
   review-fix commit. In repos that ignore `LEARNINGS/**/*.md`, that same
   commit also appends each new body's `!LEARNINGS/<category>/<slug>.md`
   negation to `.gitignore` — the add otherwise errors on / silently drops
   the ignored body file. Any PLAN re-ticks / gate-memo appends from review
   fixes fold into this same commit — its tree is docs-only and its memo
   names the gated implementation SHA Step 10a cites; anything code-shaped
   riding it is a fix commit and takes the re-gate rule. Refresh any
   tracked `_index.md` entry restating the learnings-timing rule (e.g. the
   #445 single-writer row) in this same commit.
   **Bounded loop: max 2
   fix-and-re-review iterations** — exhaustion → halt per §Failure Policy.
10. **PR + merge watching** — split: 10a foreground, 10b background.
    **10a — PR creation (foreground).** Default (inline arm): invoke skill
    `civiltekk-pr-workflow-skill` (create route) — it owns framework
    detection, the gate-memo check, the PR body, and the semver label; the
    pipeline pins below (target `<base>`, the `tier=full` memo citation,
    `Closes <TICKET_ID>`) still apply, and the merge watcher is 10b's
    background shell (never a subagent); the remainder of this section
    describes the subagent arm. First the **authoritative overlap
    re-check** (the §6f early leg is advisory only): `comm -12` of
    `git -C <ticket-repo> diff --name-only origin/<base>...feat/<KEY> | sort`
    against each earlier
    in-run ticket's still-open PR diff (per repo, likewise sorted) —
    non-empty → hold ticket
    N pre-PR (worktree kept); auto-resume on that PR's merge notification:
    rebase, re-run the full gate (the SHA changes), push
    `--force-with-lease`, then create the PR. A PR
    that would show merge conflicts because an earlier in-run PR merged
    inside the 6e→10a window (stale base) classifies the same way —
    overlap-hold, never a failed ticket. Clear → `pr-workflow-subagent`
    creates the PR **target
    `<base>`** — the Task prompt MUST state gates are green by citing the
    final `GATE <short-sha> tier=full` memo line for the pushed SHA from the PLAN
    trace block (that citation IS the pipeline-mode gate memo per
    `verification-loop-skill` §Gate memo; a `tier=light` line is phase
     evidence and never satisfies this citation) and instruct it to skip its
    steps 2 / 2.5 / 3 / 4: run-plan verified the gate per phase, docstrings
    were filled before the gate, coverage badges
    stay out (README must not change after Step 9 review — CI carries the
    coverage signal), and the PLAN is ticked and committed; the CI gate
    below is the merge decision. The Task
    prompt MUST instruct it to include `Closes <TICKET_ID>`
    in the PR body (keep the `#` — `Closes #366`, not `Closes 366`; must
    predate the merge). On PR creation the orchestrator **advances to the
    next implementable ticket** — it does NOT wait for CI (Step 1 execution
    model).
    **10b — merge watcher (background).** Spawn a background shell (harness
    binding below) that: `timeout 1800 gh pr checks -R <owner/name> <num>
    --watch` (GNU
    coreutils; macOS: `gtimeout`) — 30-minute timeout; merge only when green
    with `gh pr merge -R <owner/name> <num> --squash` — the `feat/<KEY>` head is
    short-lived, so squash is the classifier verdict
    (`civiltekk-pr-workflow-skill` merge route, `references/merge.md`
    §Phase 1 head-class rule); capture the merge
    SHA via `gh pr view -R <owner/name> <num> --json mergeCommit`; report the outcome to the
    main session (merge SHA on success; the failing check names on red).
    **Red-verdict guard:** a red verdict matches only `statusCheckRollup`
    conclusions `FAILURE` / `TIMED_OUT` / `CANCELLED` (plus the
    pending-at-timeout rule below) — `SKIPPED` and `NEUTRAL` are never red
    (GitHub marks not-applicable jobs `SKIPPED`, so docs-only PRs carry them
    in the rollup and stay green). A green watch (exit 0) proceeds to merge
    even when the rollup contains `SKIPPED` entries — exit 0 remains the
    primary green signal. When the watch exits non-zero with checks
    reported, classify before declaring red:

    ```bash
    jq '[.statusCheckRollup[] | select(.conclusion == "FAILURE" or .conclusion == "TIMED_OUT" or .conclusion == "CANCELLED")] | length'
    ```

    Nonzero count → red; zero → not red — report for manual verdict rather
    than auto-merge (the green watch, not the count, authorizes merging).
    The `-R <owner/name>` flags are mandatory — the watcher runs
    unattended, so prose scoping rules elsewhere never reach it (session
    repo ≠ ticket repo for `repo/KEY` tickets).
    Zero configured checks (exits non-zero with "no checks reported") →
    merge directly with a "no CI configured" note. Red or
    pending-at-timeout → report and stop. The watcher performs **gh-side
    operations only** — watch → merge → SHA capture → report; it performs
    **no local git mutations** (worktree/ref mutations are the main
    session's, below, keeping them serialized away from concurrent
    `git worktree add` calls).
    **Notification handling** — main session, at step/ticket boundaries
    only (never mid-Task: a Step 8 run-plan or Step 9 review may run many
    minutes), in arrival order, each notification exactly once. On a merge
    notification: report the merge SHA, then run the cleanup the watcher
    must not — `git -C <ticket-repo> worktree remove <root>/<KEY>`, purge
    the worktree from the opencode registry (below), delete the remote
    branch, and `git -C <ticket-repo> fetch` in the ticket
    repo's main checkout (**fetch-only** — never
    `pull` in the user's main worktree; uncommitted state may conflict);
    **opencode registry purge** — OpenCode keeps a `worktree` row plus a
    phantom `project` row per worktree directory in
    `~/.local/share/opencode/opencode.db` and never garbage-collects them;
    any dangling path makes `opencode reload` fail whole-command with
    `ENOENT ... FileSystem.realPath <dir>` (seen 2026-09-26 with 56 stale
    rows from prior pipelines), so run this in the same cleanup step:

    ```bash
    node -e 'const{DatabaseSync}=require("node:sqlite");const p=process.argv[1];const db=new DatabaseSync(process.env.HOME+"/.local/share/opencode/opencode.db");db.prepare("DELETE FROM worktree WHERE directory=?").run(p);db.prepare("DELETE FROM project WHERE worktree=?").run(p)' "<root>/<KEY>"
    ```

    Requires Node ≥23.4 (`node:sqlite` built in); older Node fallback:
    `sqlite3 ~/.local/share/opencode/opencode.db "DELETE FROM worktree WHERE directory='<root>/<KEY>';DELETE FROM project WHERE worktree='<root>/<KEY>'"`.
    Tracker tickets: ensure exactly one `ticketing-skill` §Close transition to
    Done — check the ticket status first, transition only if still open. On
    a red notification: the fix is queued for the next boundary (immediate
    if idle), bounded at **2 fix-and-re-watch rounds per ticket**; red-fix
    pushes ride Step 9's re-gate rule — run the **full** gate once on the
    fixed tree and append its green `tier=full` memo for the new final SHA
    before re-watch (the 10a citation names the final pushed SHA).
    Harness binding (§Portability contract) for the background mechanism:
    - OpenCode: background shell (`background: true`) with completion
      notification.
    - Claude Code: background Bash (run_in_background).
    - Other/none: foreground `timeout 1800 gh pr checks <num> --watch`
      before advancing (the pre-#560 behavior).
    Requires bash (git-bash/WSL on Windows).

## PLAN Authoring (Step 6 detail)

All commands run **in the worktree** (`worktrees/<KEY>`), on `feat/<KEY>`.
`$TICKET_ID` is the normalized ref (`#123` or `PROJ-123`); `$KEY` is its
alphanumeric form (`123` or `PROJ-123`).

### 6a. Adopt or rename an existing PLAN draft

Before generating from scratch, check whether an existing draft should be
adopted (avoids duplicate plans, preserves git history). Canonical filename:
`PLANS/PLAN-${KEY}.md` — Step 8 invokes this exact path. Drafts named
`PLAN-GIT-<issue-number>.md` or other variants are `git mv`'d to the
canonical form on adoption.

1. **Search candidates in `PLANS/` only** (never repo root — a root
   `PLAN.md` may belong to unrelated active work):
   `ls PLANS/PLAN.md PLANS/PLAN-DRAFT-*.md PLANS/TODO-*.md 2>/dev/null`
   Also prior-iteration canonical names (`PLANS/PLAN-GIT-*.md` etc.).
2. **Already adopted?** Canonical name exists → skip to 6d.
3. **Single candidate → auto-adopt** via `git mv` (preserves history):
   `git mv "PLANS/PLAN-DRAFT-<slug>.md" "PLANS/PLAN-${KEY}.md"`.
   Before auto-adopting a generic `PLANS/PLAN.md`, verify its `**Issue:**`
   header matches this ticket; mismatch → non-candidate + warn.
4. **Multiple candidates → prompt the user** which to adopt, via the `question`
   tool with this payload shape (instantiate options from the actual drafts —
   best three matches plus the decline option, keeping within the 2-4 option
   cap; keep payloads small per deployed `AGENTS.md` §Question Tool Payloads):

   ```json
   {
     "questions": [
       {
         "question": "Multiple PLAN drafts match this ticket. Which should be adopted as PLANS/PLAN-<KEY>.md?",
         "header": "PLAN draft adoption",
         "multiple": false,
         "options": [
           {
             "label": "Adopt <draft-name>",
             "description": "git mv the draft to the canonical PLANS/PLAN-<KEY>.md form and continue with it."
           },
           {
             "label": "Keep drafts in place",
             "description": "Adopt nothing now; generate a fresh PLAN from the ticket and leave the drafts for manual cleanup."
           }
         ]
       }
     ]
   }
   ```
5. **Non-adopted candidates → left in place with a warning** (user cleans up).
6. **No candidate / no `PLANS/` dir** → `mkdir -p PLANS`, continue to 6b.

> Note: 6a searches relative to the worktree cwd — drafts must be
> **committed to `<base>`** to be adoptable here; uncommitted main-worktree
> drafts are invisible by design.

### 6b. BRD/SRS draft linking

Document-ladder order: **BRD first, then SRS**. For each:

```bash
ls docs/brd/BRD-draft-*.md 2>/dev/null   # then docs/srs/SRS-draft-*.md
```

If drafts found, ask the user (via `question` — harness binding, §Portability contract: OpenCode `question` · Claude Code `AskUserQuestion` · Other/none — plain-reply ask, skip linking if unanswered) whether to link one, using this
payload shape (instantiate `<BRD|SRS>`, `<key>`, and the draft name per ladder
order):

```json
{
  "questions": [
    {
      "question": "Found <BRD|SRS> draft(s). Link one to this ticket's PLAN?",
      "header": "Draft linking",
      "multiple": false,
      "options": [
        {
          "label": "Link <draft-name>",
          "description": "Rename the draft to the <BRD|SRS>-<key> form, repoint its **PLAN**: header, and record the path for 6c header injection."
        },
        {
          "label": "Skip — no link",
          "description": "Leave drafts in place; continue with an empty doc path (backward-compatible)."
        }
      ]
    }
  ]
}
```

On link:
- Rename: `git mv docs/brd/BRD-draft-{slug}.md docs/brd/BRD-{key}.md`
  (plain `mv` + `git add` if untracked); same for SRS.
- Update the doc header `**PLAN**:` placeholder to `PLANS/PLAN-{key}.md`.
- Record `BRD_PATH` / `SRS_PATH` for header injection in 6c.
- Declined/absent → empty path (skip — backward-compatible).

### 6c. Generate the PLAN

Write `PLANS/PLAN-${KEY}.md` using this template:

```markdown
# PLAN: <title>

**Branch**: feat/<KEY>
**Issue**: <ticket URL>          ← + `**BRD**: <path>` / `**SRS**: <path>` lines when linked
**Base**: <base>

## Acceptance Criteria
- [ ] <checkable criteria from the ticket>

## Dependency & Consumer Map

_Before writing steps, list each touched file/module and who consumes it. Use `codegraph_callers` (code) or `tofu graph` + grep (IaC)._

| Node (file/module) | Depends on (must precede) | Consumers (who depends on this) | Change risk |
|---------------------|---------------------------|---------------------------------|-------------|
| `path/to/file`      | —                         | caller-A, module-B              | low/med/high |

## Implementation Phases

_Every step MUST be atomic and carry rationale. Reject any step missing a "Why"._

### Canonical step format
- [ ] **N.M** <single atomic action — verb + target + outcome>
    — **Why:** <what this unblocks / why it must precede others>
    — **Done when:** <objective, checkable completion signal>
    — **Consumers affected:** <who depends on this; none if N/A>

### Phase 1: <name>
- [ ] **1.1** <atomic action> (per canonical format)
…

## Technical Notes
<from ticket>

## Dependencies
<external dependencies / blocked-by tickets>

## Risks & Mitigation
<risks + mitigations>
```

**Step authoring rules** (enforced by 6d):
- **Atomic**: one reversible concern per step; two concerns → split.
- **Rationale mandatory**: every step has **Why**; a step without it is malformed.
- **Completion signal**: objective **Done when**, never subjective "done".
- **Consumers explicit**: blast radius visible to reviewers; "none" if isolated.

### 6d. Atomicity self-check (commit gate)

1. Read the PLAN back from disk.
2. For every `- [ ] **N.M**` / `- [x] **N.M**` step, confirm the three
   rationale lines follow it: `— **Why:**`, `— **Done when:**`,
   `— **Consumers affected:**`.
3. **Any step missing any field → do NOT commit.** Surface malformed steps
   (line number + text), fix, re-check. Gate must pass with zero malformed
   steps.
4. Also verify: Dependency & Consumer Map section exists; phase ordering
   matches the map's constraints; every Acceptance Criterion is addressed
   by ≥1 implementation step (catches silently forgotten requirements at
   authoring time — belt for thin-map tickets that select zero reviewers).

### 6e. Commit and push the PLAN

```bash
git add "PLANS/PLAN-${KEY}.md" docs/brd/ docs/srs/ 2>/dev/null
git commit -m "docs(plan): add PLAN-${KEY}.md for ${TICKET_ID}"
git push -u origin "feat/${KEY}"
```

`/run-plan` commits implementation phases, not an untracked PLAN — an
untracked PLAN file would be lost on worktree removal, which is why this
step pushes it.

> Skipped by design in pipeline context: initial ticket progress comment
> (execution follows immediately; ticket updates flow through Step 5
> re-validation and pr-workflow) and the branch-workflow setup signal
> (pipeline runs assume an established repo; run `/create-ticket` standalone
> if you want that signal).

### 6f. Overlap hold gate (between 6e and Step 7)

**Early leg (advisory)**: while any earlier in-run ticket still has an open
PR, intersect that PR's branch diff with THIS ticket's PLAN **Dependency &
Consumer Map touch-set** (materialize the map's first-column file paths,
normalize, `sort` — `comm -12` needs sorted input; the map 6d just
validated — at this boundary the branch diff contains only the PLAN commit,
so a `comm -12` on branch diffs would be vacuous here). Non-empty
intersection → **hold** ticket N: keep the worktree, report held, and
auto-resume when that PR's merge notification arrives — rebase `feat/<KEY>`
onto the updated base (push `--force-with-lease` after the rebase) and
continue at Step 7, re-running the **full** gate iff the rebase touched
implementation commits (at this boundary the tree is PLAN-only — Step 8's
exit gate re-gates the real tree). Advisory default: an empty or missing
Consumer Map skips the early leg — worst case is a late hold at the 10a
authoritative check, never a wrong merge.

## Failure Policy

- **Ticket-failure triggers** (per ticket — nothing aborts the remaining
  run): the executor's `[goal:blocked]` terminal marker on the active
  ticket (Step 8), review-fix exhaustion after 2 iterations (Step 9), PR
  creation failure (Step 10a), or watcher exhaustion (Step 10b) — a
  pending-at-timeout watch fails the ticket immediately (pending CI has no
  concluded failure to fix; keep the worktree, report under Issues), while
  a concluded-red watch enters the 2 fix-and-re-watch rounds and fails the
  ticket once exhausted. CI red is
  **not** a run-level abort: a failed watcher fails that ticket
  only.
- **Keep the scene**: the failed ticket's worktree + `feat/<KEY>` branch
  stay in place for inspection (Step 2's prune/resume/refuse ask handles
  clean reruns); a red watcher never cleans up.
- **Tickets in flight when a ticket fails**: independent tickets proceed;
  dependent (held) tickets stay held and are reported **deferred** at run
  end. A `[goal:blocked]` on the active ticket pauses the implementation
  lane — background watchers keep running and notifications keep draining
  at boundaries.
- **Final report waits** for outstanding watchers (each bounded by the
  30-minute cap plus up to 2 red-fix rounds) before the Return Contract.
- **Return Contract semantics**: `partial` for any halt after a ticket has
  started OR any failed-red ticket; `success` allows deferred-held tickets
  (listed under Issues); `failed` is reserved for pre-execution failures
  (invalid base branch, zero tickets resolved, missing hard dependency from
  Step 1's preflight, unresolvable foreign repo).

## Guarantees

- One active implementation at a time; any number of background merge
  watchers. A ticket's worktree lives until its PR resolves — merge →
  cleaned up by the main session's notification handler; red → kept for
  fixes. Every merge is green-only; the watcher performs no local git
  mutations.
- Every ticket re-validated against latest `origin/<base>` before execution.
- The main working tree is never checked out on a feat branch.
- Every PLAN passes the atomicity self-check before commit.
- No standalone tick/progress commits at any step — PLAN updates ride the
  phase's atomic commit (Step 8) or fold into the review-fix commit
  (Step 9); the squash merge keeps PLAN noise out of release notes.
- Delegation is hub-and-spoke from the primary session (build agent allows
  `task: {"*": allow}`); delegates whose cwd is the session checkout (not the
  worktree) receive precomputed diffs.
- Each worktree gets a CodeGraph index when the main checkout has one
  (skipped with a note when the index would be unignored, the CLI is
  absent, or init fails).

## Return Contract

**Status:** success | partial | failed — deferred-held tickets alone do not
downgrade to `partial`; any failed-red ticket does
**Output:** per ticket — PR URL + merge SHA (watcher-reported) + final state
(merged / failed-red / deferred-held); one line each
**Summary:** 2-3 sentences max
**Issues:** blockers, held/deferred tickets, red-watcher outcomes, or "None"

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…