Elaborate an issue
Take a high-level GitHub issue (typically the output of draft, propose, or audit) and expand it into a deeply detailed engineering plan grounded in the actual repository state — the kind of issue body a senior engineer can pick up and execute without further clarification: a Description with concrete "already exists / this issue adds" boundaries, a Motivation, an optional User Stories section, Affected Areas, Acceptance Criteria, Non-Goals, and References.
elaborate comes two ways. Both render the same issue body.
The skill: /planwerk:elaborate
Use this when a human is at the keyboard. It is the better choice by default, because elaboration turns on decisions the issue never made, and the skill asks you about them instead of guessing.
/planwerk:elaborate owner/repo#123Run it from inside a checkout of the issue's repository. The skill reads the issue and its Meta/Sub-Issue neighborhood, walks the repository before it asks you anything, and then surfaces only the decisions that would otherwise become guesses — each grounded in the concrete path:line that raised it. A question it can answer by reading the code, it does not ask.
It then writes the plan, scores its own draft for executability, refines until the score clears 8, and asks whether to replace the issue body or post a comment. Nothing is written to GitHub until you say so. A question you decline to answer is recorded in the issue under Non-Goals or as an explicit assumption, never resolved silently.
Install it first: see Use the issue skills.
The command: planwerk-agent elaborate
Use this for unattended runs — CI, scripts, batch elaboration — where there is nobody to ask. It clones the repository itself, so it needs no checkout.
# Render the elaborated body to stdout
planwerk-agent elaborate https://github.com/owner/repo/issues/123
# Short form
planwerk-agent elaborate owner/repo#123
# JSON for automation
planwerk-agent elaborate --format json owner/repo#123
# Replace the issue body with the elaborated body
planwerk-agent elaborate --update-issue owner/repo#123
# Or post the elaboration as a new comment instead
planwerk-agent elaborate --post-comment owner/repo#123--update-issue and --post-comment are mutually exclusive — pick the one that matches your team's workflow (overwrite the source issue vs. preserve history and append a follow-up comment). See the CLI reference for every flag.
Where the skill asks you, the command records the ambiguity in Non-Goals and plans the smallest change that satisfies the issue. That is the trade: the command never blocks, and never gets an answer it could not derive.
How the command works
- Issue Input: The tool receives a GitHub issue reference (URL or
owner/repo#number). - Fetch Issue: Title, body, URL, and state are fetched via
gh issue view. - Fetch Relations: When the issue is a Sub Issue of a Meta Issue, the Meta Issue and the other Sub Issues are fetched via the GitHub GraphQL API (best-effort — a repo without sub-issue links, a missing token scope, or an older GitHub Enterprise Server degrades to "no relations" without failing the run). Each sibling and child Sub Issue carries its native
blockedBy/blockingdependency edges; a deployment that rejects those fields is queried again without them, so the relations still load, without edges. See Sub Issues are elaborated against their Meta Issue below. - Cache Check: The default-branch HEAD SHA is resolved via
gh api graphql. The cache key combines repo + HEAD + issue number + a fingerprint of the issue body — plus, when the issue is a Sub Issue, a fingerprint of the Meta Issue and sibling Sub Issues — so the cache invalidates automatically when the repo, the issue, the Meta Issue, or any sibling is edited. The fingerprint also covers each Sub Issue's linked pull requests and dependency edges, so adding or removing an edge, or closing its endpoint, re-elaborates. - Clone: On a cache miss, the repository is cloned locally.
- Pattern Load: The same pattern catalog used by
review/audit/proposeis loaded, filtered by detected technologies. The elaboration session receives the catalog's index and reads a pattern's file from a directory the run writes when it needs one. - Claude Elaboration: Claude is instructed to walk the repo first, identify what already exists vs. what the issue adds, and emit a detailed plan in six core sections (Description with concrete "already exists / this story adds" boundaries, Motivation, Affected Areas, Acceptance Criteria, Non-Goals, References), plus an optional User Stories section between Motivation and Affected Areas that groups the acceptance criteria under
As a {role}, I want {want}, so that {so_that}stories. User Stories are proportional — emitted only when the issue serves a distinct persona and omitted entirely for purely mechanical or infrastructure work (dependency bumps, formatter sweeps, CI fixes), never padded with a synthetic "As a developer" story. For a Sub Issue, the Meta Issue and sibling Sub Issues from step 3 are injected so the elaboration covers only this issue's slice and defers adjacent parts to the sibling that owns them. - Structuring: A second Claude call converts the elaboration into a strict JSON schema so the final body renders consistently.
- Output: The elaborated body is rendered as Markdown (default) or JSON. With
--update-issue, the issue body is overwritten; with--post-comment, the elaboration is posted as a new comment.
Sub Issues are elaborated against their Meta Issue
When the issue is a Sub Issue created by meta (or linked through GitHub's native sub-issue relationship), elaborate reads the Meta Issue and the other Sub Issues alongside it and injects them into the prompt as a Meta / Sub-Issue Context section. The elaboration is then told to:
- plan only this Sub Issue's slice of the larger effort and honor the Meta Issue's framing rather than re-deciding it;
- avoid duplicating work a sibling Sub Issue owns; and
- when this Sub Issue intentionally implements only part of a shared task because the remaining part lands in another Sub Issue, scope it to its part and cross-reference the sibling that carries the rest (e.g. "the remaining X is handled by #K"), recording the deferral under Non-Goals.
A closed sibling is treated as already-implemented context to build on; an open one as work that may land in parallel. This is automatic — there is no flag — and best-effort: an issue that is not a Sub Issue, or a repo where the relationship cannot be read, elaborates exactly as before.
Each sibling block also carries the sibling's native GitHub dependency edges as attributes of its opening tag: blocked-by names the issues that deliver before it and blocks the issues that wait on it, each followed by its state, with (this issue) marking the Sub Issue being elaborated. The edges sit on the tag rather than in the block because an issue body can copy any line of its block but cannot open the tag. The edges decide the order the Sub Issues deliver in, and the elaboration is told never to infer that order from issue prose, including Blocked by text in a body. A sibling that blocks this issue delivers first: once it is closed, its merged pull request is delivered state to build on. An open sibling this issue blocks delivers later: its scope is off-limits, and nothing it adds exists yet. A closed sibling this issue blocks already landed out of order and is read like any closed sibling. A neighborhood without edges, or a deployment that does not expose them, renders exactly as before. The /planwerk:elaborate skill reads the same edges from its neighborhood query and follows the same rule.
Counterpart work is scoped out, not deferred
The repository walk is where you first see which interfaces a plan actually moves, so it is where a counterpart in another repository first becomes obvious — often after the draft was written. When .planwerk/related-repos.md names a repository whose condition the plan meets and no counterpart issue is linked yet, the skill offers to file one at draft depth, then records it under Non-Goals as owner/repo#N.
That is not a delivery split. A pull request cannot span repositories, so the work was never part of this one, and the plan's own single-delivery check treats it as scoping. Deferring work in this repository to a follow-up issue is still a plan failure.
Running on a counterpart works the other way round: its blocker is where the contract it consumes is settled, so the skill reads that issue before planning.
The issue body keeps its header
An elaboration replaces the whole issue body, so it carries the source issue's **Category**: … | **Scope**: … header line through and corrects the Scope when the plan changed the size. An issue that never had that line renders without one. Both the skill and the command do this, and a Go test (TestBuildIssueBody_MatchesSharedFormat) fails when the two paths disagree about the format.
Large plans stay whole: a budget, and a continuation comment
An elaborated body has a budget of 40,000 characters, roughly 10,000 tokens. The body is injected whole into every planning, implementation, and verification prompt that reads the issue, so the budget is what keeps those prompts affordable. The command writes to it: the elaboration prompt names the moves that bring a draft under it without dropping a decision, a criterion, or a citation — state each fact once in the section that owns it, cite path:line instead of quoting code, one sentence per rejected alternative — and with --review the refine loop treats an over-budget body as a gap to close; without it, the command logs a warning and writes the body as it is. The skill treats the number as the body's limit instead, and never shortens a finished draft to reach it: it counts the draft once, at write-back, and a plan past 40,000 characters is written whole as the body plus continuation comments (design decision 93).
GitHub caps a body at 65,536 characters, and a plan that still exceeds the cap is not truncated. --update-issue writes it as the body plus one or more continuation comments: the body keeps the header, the leading sections, and the footer, and ends with a <!-- planwerk-agent:continued 1/N --> marker and a pointer naming the sections that follow; each continuation comment opens with <!-- planwerk-agent:continuation k/N -->. The cut falls on a section boundary where one is available, and never inside a fenced code block. Every command that reads an issue body (implement, elaborate, prompt) finds the parts by those markers and merges them back into one document before it reads a section, and the skills do the same. A part the body announces and no comment carries aborts the run rather than planning against a truncated issue. A rewrite reuses the existing continuation comments in place and deletes the ones a shorter body no longer needs, so the thread never carries a stale part. --post-comment posts an oversized elaboration as a run of comments the same way, except on an issue whose body is itself continued: there the run is refused, because its parts carry the same markers as the body's and every later read would merge them into the body; use --update-issue. The convention the skills follow is specified in plugins/planwerk/shared/issue-format.md, where a plan is split at the body's 40,000-character limit rather than at the cap; a draft-depth or survey body is split only at the cap.
Score the draft before output (--review)
--review adds a reviewer pass between elaboration and output. A reviewer scores the draft from 0 to 10 for executability — a 10 is a plan a zero-context implementer executes without asking a single question. While the score stays below the bar, the refine loop revises the draft to close the reviewer's gaps and iterates until the score clears the bar or --max-review-iterations is exhausted (default 3).
The skill always runs this pass; on the command it is opt-in.
The final score is surfaced in the output as Executability score: N/10, so a near-miss is visible rather than hidden behind a binary pass/fail. When the loop runs out of iterations below the bar, the surviving gaps and a "what a 10/10 plan would look like" target are rendered alongside the score under Reviewer Notes (unresolved) — address them before implementing.