speq-plan-pr · diff
git:20260728.761e80e to git:20260825.418db64
30 added, 30 removed. Audit A to A.
---
name: speq-plan-pr
description: "Headless, non-interactive version of /speq-plan for CI or agent-driven runs. Plans a feature without a live interview, commits the result to a feat/plan-name branch, and opens a draft PR. If a decision genuinely needs a human, it persists the partial plan and open questions and asks in a PR comment instead of blocking. Arg: plan name, feature intent text, PR number, or branch name."
model: sonnet
---
# Spec Planner, headless (Orchestrator)
- You are a thin orchestrator with no live user to interview. Your goal is:
- - Turn a feature intent (or an existing plan branch/PR) into a validated plan authored entirely by `planner-agent`, run in headless mode.
+ You are a thin orchestrator with no live user to interview. Your goal:
+ - Turn a feature intent, or an existing plan branch/PR, into a validated plan authored entirely by `planner-agent` in headless mode.
- Land that plan on a `feat/<plan-name>` branch and a draft PR, using `git-agent` for every git/`gh` action.
- Hand any irreducible decision to a human as a PR comment and stop there.
- You must follow this workflow:
- - Delegate all planning judgment to `planner-agent` and all git/GitHub actions to `git-agent`; your own work is resolving the input, writing the plan's status files, briefing those agents, and interpreting their returns.
+ Rules:
+ - Delegate all planning judgment to `planner-agent` and all git/GitHub actions to `git-agent`. Your own work: resolve the input, write the plan's status files, brief those agents, interpret their returns.
- Run the steps in order: resolve target → fetch async answers (resume only) → discovery → delegate planning → branch on the result → report.
- - Keep one `feat/<plan-name>` branch and one PR per plan; `git-agent`'s `create-pr` reuses whatever PR already exists.
+ - Keep one `feat/<plan-name>` branch and one PR per plan. `git-agent`'s `create-pr` reuses an existing PR.
## Required Skills (for the orchestrator)
Invoke before starting:
- - `/speq-cli` — spec discovery and search
- - `/speq-writing-guardrails` — Prose style for artifacts and GitHub text
+ - `/speq-cli`: spec discovery and search
+ - `/speq-writing-guardrails`: prose style for artifacts and GitHub text
`planner-agent` and `plan-reviewer` invoke their own required skills; `git-agent` invokes `/speq-git-operations`.
## Workflow
### 0. Load Project Hook (orchestrator)
Check for `.speq/plan-pr-hook.md` in the repo root.
- - **Present:** read it. Announce "Loaded project hook: .speq/plan-pr-hook.md". Its content is authoritative — it may add, change, or override any part of this skill's workflow below when the two conflict.
- - **Absent:** continue normally, no mention.
+ - **Present:** read it. Announce "Loaded project hook: .speq/plan-pr-hook.md". Its content is authoritative: it can add to, change, or override any part of this workflow. If the hook conflicts with this workflow, the hook wins.
+ - **Absent:** continue without mention.
### 1. Resolve Target
Resolve what to work on and land on the right branch:
```
Delegate to git-agent — operation: checkout
target: <the raw argument the caller passed>
```
- If `checkout` reports not-found, the argument is free-text feature intent for a brand-new plan. Derive `<plan-name>` the same way `speq-plan` does (verb table below) and create its branch:
+ If `checkout` reports not-found, the argument is free-text feature intent for a new plan. Derive `<plan-name>` per the verb table and create its branch:
```
Delegate to git-agent — operation: create-branch
branch: feat/<plan-name>
```
Then read from disk yourself: whether `specs/_plans/<plan-name>/` exists, and whether `specs/_plans/<plan-name>/open-questions.md` exists and is non-empty. Take the PR draft/ready state from the `checkout` return.
| Verb | When |
|------|------|
| `add` | New feature |
| `change` | Modify existing |
| `remove` | Deprecate/delete |
| `refactor` | Restructure, same behavior |
| `fix` | Bug or spec mismatch |
Pattern: `<verb>-<feature-scope>[-<qualifier>]`
#### PR-title derivation (shared with `speq-implement-pr`)
- Derive the PR title deterministically from `<plan-name>` as a conventional-commit feature title `<type>(<scope>): <slug>`:
+ Derive the PR title from `<plan-name>` as a conventional-commit title `<type>(<scope>): <slug>`:
- - **type** — map the verb: `add`/`change` → `feat`, `remove` → `chore`, `refactor` → `refactor`, `fix` → `fix`; fallback `chore` for an unparseable name.
- - **scope** — the `<feature-scope>` segment (the token after the verb).
- - **slug** — the humanized `<plan-name>` (hyphens → spaces).
+ - **type**: map the verb: `add`/`change` → `feat`, `remove` → `chore`, `refactor` → `refactor`, `fix` → `fix`; fallback `chore` for an unparseable name.
+ - **scope**: the `<feature-scope>` segment (the token after the verb).
+ - **slug**: the humanized `<plan-name>` (hyphens → spaces).
Example: `add-search-candle` ⇒ `feat(search): add search candle`. With no scope segment, emit `<type>: <slug>`.
### 2. Fetch Answers — resume only
If step 1 found an unresolved `open-questions.md`, pull the human's replies:
```
Delegate to git-agent — operation: read-comments
since: <timestamp of your last "flag open questions" commit>
```
- Forward the returned Q&A to `planner-agent` in step 4 — it stands in for the live interview `speq-plan` would otherwise run.
+ Forward the returned Q&A to `planner-agent` in step 4. It stands in for the live interview `speq-plan` would run.
### 3. Discovery (orchestrator)
- Gather just enough context to brief `planner-agent`, the same lightweight calls `speq-plan` makes:
+ Gather enough context to brief `planner-agent`, the same lightweight calls `speq-plan` makes:
```bash
speq domain list
speq feature list
speq search query "<relevant terms>"
```
### 4. Delegate to planner-agent
Same shape `speq-plan` uses, with headless framing added:
```
Delegate to planner-agent — Plan <plan-name> (headless)
## Plan Name
<plan-name>
## Interview Mode
headless
## User Intent
<feature intent text, or "see resume Q&A below">
## Clarifying Interview Results
<the free-text feature intent (new plan), or the Q&A text step 2 fetched (resume) — this stands in for a live interview>
## Existing Context
<output of relevant `speq search` / `speq feature get` calls>
## External Research
none — agent to research as needed
## Project Hook
<if active: note ".speq/plan-pr-hook.md — read it and apply it" — otherwise omit this section>
## Your Task
Produce spec deltas and plan.md per your normal workflow. You are in headless mode: follow your "Headless / Non-Interactive Mode" section — assume and document conventional decisions, escalate only irreducible ones via the OPEN QUESTIONS: sentinel. Tag deep-reasoning tasks with [expert].
Return the list of files created and the validation result, or an OPEN QUESTIONS: block if you had to stop.
```
### 5. Adversarial Plan Review
- Skip this step if step 4 returned `OPEN QUESTIONS:` — resolve that first (step 6 handles it). Otherwise, before landing the plan, spawn `plan-reviewer` to challenge it. Bounded to 2 rounds total, same shape `speq-plan` uses:
+ Skip this step if step 4 returned `OPEN QUESTIONS:`; step 6 handles that. Otherwise spawn `plan-reviewer` to challenge the plan. Maximum 2 rounds total, same shape `speq-plan` uses:
```
Delegate to plan-reviewer — Review <plan-name> (round 1)
## Plan Name
<plan-name>
## User Intent
<the feature intent text / resume Q&A used in step 4>
## Clarifying Interview Results
<same text passed to planner-agent in step 4 — headless mode has no live interview>
## Plan Artifacts
plan.md, decision-log.md, and every specs/_plans/<plan-name>/**/spec.md delta
## Project Hook
<if active: note ".speq/plan-pr-hook.md — read it and apply it" — otherwise omit this section>
```
It writes its findings to `specs/_plans/<plan-name>/review/round-1.md` and returns only `PLAN REVIEW round 1: BLOCKERS: <n>, ADVISORY: <n>, INTENT: <n> — <path>`. `INTENT` counts the BLOCKERs on the Intent Fidelity axis alone.
- **If `INTENT > 0`:** the reviewer's case is that the plan solves a different problem than the one asked for — fold the Intent-Fidelity BLOCKER text, read from the round file, into step 6's `OPEN QUESTIONS:` branch and stop. A plan that misreads the intent is exactly the irreducible decision headless mode escalates to a human.
+ **If `INTENT > 0`:** the plan solves a different problem than the one asked. Read the Intent-Fidelity BLOCKER text from the round file, fold it into step 6's `OPEN QUESTIONS:` branch, and stop.
- **If `INTENT == 0` and BLOCKER findings exist:** respawn `planner-agent` with the path to `review/round-1.md`, instructing it to read the BLOCKER findings from that file and execute each `Fix:` line (log each resolved blocker as a `[plan-review]`-prefixed `## Review Findings` entry in `decision-log.md`, re-validate), then respawn `plan-reviewer` for round 2 with the same path to confirm resolution. Do not loop a third time.
+ **If `INTENT == 0` and BLOCKER findings exist:** respawn `planner-agent` with the path to `review/round-1.md`. Instruct it to read the BLOCKER findings, execute each `Fix:` line, log each resolved blocker as a `[plan-review]`-prefixed `## Review Findings` entry in `decision-log.md`, and re-validate. Then respawn `plan-reviewer` for round 2 with the same path to confirm resolution. Do not run a third round.
- **If BLOCKERs remain after round 2:** treat this exactly like an `OPEN QUESTIONS:` return from `planner-agent` — read the unresolved BLOCKER findings from `review/round-2.md` and fold them into step 6's "`OPEN QUESTIONS:` returned" branch as the questions list.
+ **If BLOCKERs remain after round 2:** treat this like an `OPEN QUESTIONS:` return. Read the unresolved BLOCKER findings from `review/round-2.md` and fold them into step 6's `OPEN QUESTIONS:` branch as the questions list.
- **ADVISORY findings:** carry into step 7's PR body/report, read from the last round file when composing; never block or persist.
+ **ADVISORY findings:** carry into step 7's PR body/report, read from the last round file when composing. Never block or persist them.
### 6. Branch on the Result
- **Clean return** (no `OPEN QUESTIONS:` sentinel, and no unresolved BLOCKERs from step 5) — ship the plan as a draft:
+ **Clean return** (no `OPEN QUESTIONS:` sentinel, no unresolved BLOCKERs from step 5): ship the plan as a draft.
1. Confirm `speq plan validate <plan-name>` passes.
2. Commit the plan and open the draft PR with one composite call:
```
Delegate to git-agent — operation: ship-draft
paths: the plan directory
message: spec(plan): <plan-name>
title: <the derived <type>(<scope>): <slug>>
body: summarize the plan's Features table and task count, include the
plan.md ## Impact section verbatim as its own "## Impact" heading,
ending "Draft pending implementation — run /speq:implement-pr <plan-name> to implement and mark ready"
```
- 3. If this is a resume of a previously-blocked plan, clear the block yourself: delete `specs/_plans/<plan-name>/open-questions.md` and the `> **Status:** blocked …` banner line from `plan.md`, then:
+ 3. If this resumes a previously blocked plan, clear the block yourself: delete `specs/_plans/<plan-name>/open-questions.md` and the `> **Status:** blocked …` banner line from `plan.md`, then:
```
Delegate to git-agent — operation: commit
paths: the plan directory
message: spec(plan): resolve open questions for <plan-name>
Delegate to git-agent — operation: push
```
- The PR stays a draft — `speq-implement-pr` is the only skill that marks it ready.
- 4. If step 5's verdict reported a non-zero `ADVISORY` count, or `decision-log.md`'s Design Decisions section is non-empty (every headless "assume and document" entry is a candidate an architect may want to sanity-check), compose one comment covering both and post it — skip entirely if there is nothing to flag. Read the Advisory findings from the last round file, `specs/_plans/<plan-name>/review/round-<N>.md`. Compose the body per `speq-writing-guardrails`' PR-facing content rule:
+ The PR stays a draft. `speq-implement-pr` is the only skill that marks it ready.
+ 4. If step 5 reported a non-zero `ADVISORY` count, or `decision-log.md`'s Design Decisions section is non-empty, post one comment covering both. Skip if there is nothing to flag. Read the ADVISORY findings from the last round file, `specs/_plans/<plan-name>/review/round-<N>.md`. Compose the body per `/speq-writing-guardrails`' PR-facing content rule:
```
Delegate to git-agent — operation: comment-pr
body: the ADVISORY findings excerpted from the step 5 round file (if any)
and the Design Decisions entries from decision-log.md (if any)
```
- **`OPEN QUESTIONS:` returned (from step 4, or from step 5's round-1 Intent gate or unresolved round-2 BLOCKERs)** — persist the partial plan and ask the human async. Author the status files yourself, then delegate only git operations:
+ **`OPEN QUESTIONS:` returned** (from step 4, or from step 5's round-1 Intent gate or unresolved round-2 BLOCKERs): persist the partial plan and ask the human async. Author the status files yourself, then delegate only git operations.
1. Write `specs/_plans/<plan-name>/open-questions.md`:
```markdown
# Open Questions: <plan-name>
speq-plan-pr could not complete this plan without human input. What's done so far is committed on this branch. Reply inline on the PR, or resume with `/speq:plan <plan-name>` locally, or re-run `/speq:plan-pr <plan-name>` after commenting.
- [ ] <question 1, or a BLOCKER folded in from step 5 — round-1 Intent-Fidelity, or unresolved after round 2>
- [ ] <question 2>
```
- 2. Insert `> **Status:** blocked — see open-questions.md` as the first line under `plan.md`'s H1 (skip if already present).
- 3. Compose the questions checklist as the PR comment body, and append any ADVISORY findings excerpted from step 5's round file, if present — one comment, not two.
+ 2. Insert `> **Status:** blocked — see open-questions.md` as the first line under `plan.md`'s H1. Skip if already present.
+ 3. Compose the questions checklist as the PR comment body. Append any ADVISORY findings excerpted from step 5's round file. One comment, not two.
Then, with one composite call:
```
Delegate to git-agent — operation: flag-blocked
paths: the plan directory
message: spec(plan): flag open questions for <plan-name>
title: <the derived <type>(<scope>): <slug>>
body: <blocked-plan summary>, including plan.md's ## Impact section
verbatim as its own "## Impact" heading if populated — a blocked
plan may have partial Impact info; include it as-is, never
fabricate the rest
comment_body: <the questions checklist and any ADVISORY findings excerpted
from step 5's round file>
```
### 7. Report (orchestrator)
- Tell the caller whether the plan is ready or blocked, and the PR link either way. Print plan.md's `## Impact` section to the terminal. Mention any ADVISORY findings — read them from `specs/_plans/<plan-name>/review/round-<N>.md`, not from memory — and any Design Decisions entries surfaced. Both are also posted as a PR comment per step 6.
+ Tell the caller whether the plan is ready or blocked, with the PR link either way. Print plan.md's `## Impact` section to the terminal. Mention any ADVISORY findings, read from `specs/_plans/<plan-name>/review/round-<N>.md`, not from memory, and any Design Decisions entries surfaced. Both are also posted as a PR comment per step 6.
## Spec Hierarchy (reference)
```
specs/
├── <domain>/<feature>/spec.md # Permanent
├── _plans/<plan-name>/ # Active
│ └── open-questions.md # Present only while blocked
└── _recorded/<plan-name>/ # Archived
```
## Work Split (reference)
| Step | Performed by | Why |
|------|--------------|-----|
| Target resolution, discovery, status files, coordination | This skill (pins Sonnet) | Tool-call heavy, reasoning light |
| Spec delta authoring, ADR, task decomposition, assume-vs-escalate calls | `planner-agent` sub-agent | Reasoning-heavy; defects here compound through implementation |
- | Adversarial review, revision loop | `plan-reviewer` sub-agent | Catches intent drift, infeasibility, and ambiguity before implementation, not after |
+ | Adversarial review, revision loop | `plan-reviewer` sub-agent | Catches intent drift, infeasibility, and ambiguity before implementation |
| Branch, commit, push, PR create/comment | `git-agent` sub-agent | Generic git/GitHub operations; keeps git/gh detail out of the orchestrator |
## Anti-Patterns
| Pattern | Why Wrong |
|---------|-----------|
| Asking the user a live question | Headless — irreducible decisions go to the PR comment |
| Running git/gh directly | `git-agent` performs every git and GitHub operation |
| Marking the PR ready | `speq-implement-pr` owns `ready-pr`; plans stay draft |
| A third review round | Bounded to 2 — leftover BLOCKERs become open questions |