PRD to Plan
Break a PRD into a phased implementation plan using vertical slices (tracer bullets). Output is a Markdown file in the project's plans directory.
Process
1. Resolve PRD source and GitHub availability
Determine where the PRD content comes from and whether this invocation will publish to GitHub.
Step 1a — Detect GitHub availability (cheap, run once):
git remote get-url origin— if no output or the URL is notgithub.com, GH integration is unavailable (pure local mode)- If a GH remote exists,
gh auth status— if not authenticated, GH integration is unavailable
Capture <org>/<repo> from the remote URL for later use.
Step 1b — Resolve PRD source by precedence:
- Explicit GH issue argument passed to the skill (e.g.
#123orhttps://github.com/<org>/<repo>/issues/123) - In-context GH issue URL matching the current repo's remote, detected by scanning the conversation
- Local PRD file with
<!-- gh-issue: N -->footer — if the user points to a local PRD file (or one is apparent from recent conversation, e.g.write-a-prdjust wrote it), read it. If it contains the footer, extractNand treat equivalent to an explicit GH argument. This is how thewrite-a-prd→prd-to-planhandoff works across conversations without the user copying the issue number manually. - In-context or local PRD content without footer (pure local mode)
If the user passed --no-github (or equivalent), skip GH entirely and operate in local-only mode regardless of context — including ignoring any footer marker in the local PRD file.
Step 1c — Handle each case:
- Explicit arg, GH available:
- Run
gh issue view <n> --json number,title,url,updatedAt(no body — avoids duplicating content if the PRD is already in conversation). This doubles as a pre-flight that the parent issue exists. - Show confirmation:
Linking plan to #<n> '<title>' — last updated <updatedAt>.Display the timestamp passively; do not prompt on it. - If the PRD is NOT already in conversation, fetch the full body:
gh issue view <n> --json body,title,number,url - If conversation references a different issue number for this repo, surface the mismatch before proceeding:
You passed #<n> but I noticed #<m> earlier. Continue with #<n>?
- Run
- Explicit arg, GH unavailable: fail loudly. Explain whether
ghis missing, not authenticated, or the repo is not GitHub. Do not silently fall back. - In-context issue detected, GH available:
- Single match:
Detected GH issue #<n> '<title>' in this conversation. Link the plan as a sub-issue? (yes / specify different / skip GH) - Multiple matches: list them, ask which (or
none) - On confirm, proceed as if the user had passed an explicit arg
- Single match:
- In-context issue detected, GH unavailable: skip detection silently; operate in local-only mode
- Local file footer detected, GH available: proceed as if the footer's issue number were passed explicitly (same as "Explicit arg, GH available" case). Notify the user:
Detected GH issue #<n> from PRD footer — linking plan as a sub-issue. Use --no-github to disable. - Local file footer detected, GH unavailable: fail loudly — the PRD claims to be published but GH can't be reached; do not silently drop the sub-issue relationship. User either needs to fix their
ghsetup or pass--no-githubto explicitly demote to local mode. - Nothing in context and no explicit arg: operate in local-only mode. If the PRD content is also not apparent in the conversation, ask the user to paste it or point you to the file.
Feature name (normalized). Throughout this skill, <feature name> means the source title with a leading PRD: prefix stripped (case-insensitive, surrounding whitespace trimmed). The source title is the GH issue title (GH mode) or the # PRD: <Feature Name> heading of the local PRD (pure-local mode) — write-a-prd prefixes both with PRD: for scannability. That prefix must NOT leak into the derived artifacts: the plan slug (Step 7b), the plan heading (Step 7d template), or the Plan: <feature name> sub-issue title (Step 8b) — otherwise you get Plan: PRD: … and a prd-…-plan.md slug. Use the raw title — prefix intact — only where the actual issue is cited verbatim: the Linking… / Detected… confirmation messages above and the ("Title") portion of the Source PRD: header.
2. Explore the codebase
If you have not already explored the codebase, do so to understand the current architecture, existing patterns, and integration layers.
3. Identify durable architectural decisions
Before slicing, identify high-level decisions that are unlikely to change throughout implementation:
- Route structures / URL patterns
- Database schema shape
- Key data models
- Authentication / authorization approach
- Third-party service boundaries
- i18n namespace(s) and reusable key references (see Step 3.5)
These go in the plan header so every phase can reference them.
3.5. Assess i18n impact
Decision gate:
- Skip this step if the PRD involves only refactoring, testing, configuration, or other changes with no user-facing strings.
- Skip this step if the project does not use i18n.
- Otherwise (PRD introduces or modifies user-facing strings AND project uses i18n) → STOP and read references/i18n-phase.md now. It contains the full procedure (discover setup → audit reusable keys → enumerate new keys → plan Translations phase → wire up later phases) plus the acceptance signal used by Step 6 validation. Do not proceed to Step 4 without reading it.
4. Draft vertical slices
Break the PRD into tracer bullet phases. Each phase is a thin vertical slice that cuts through ALL integration layers end-to-end, NOT a horizontal slice of one layer.
Phase sizing under autonomous execution
When the plan will run under run-plan, nobody stops to look at the app between phases — so "independently demoable" is not what a phase boundary buys there. It buys four things, none of which needs the phase to be demoable: review scope (fewer criteria per verdict), retry blast radius (less work discarded per corrective pass), commit granularity, and context isolation (each phase runs in a fresh agent window, so a smaller phase means less accumulated context in any one agent). Size phases with a two-part rule:
- Size control — cap the phase. Criteria count is the proxy available at planning time: treat a double-digit criteria count as a split signal. This is directional, not a hard limit — review cost scales with criteria per verdict, retries re-review the phase, and a fixed retry ceiling is generous on a 4-criterion phase yet tight on a 12-criterion one. A phase boundary the source PRD/issue or an ADR has already decided overrides the signal: record that decision in the plan's architectural decisions and keep the phase whole, even over ten criteria — a split that contradicts a settled decision is a finding, not a saving.
- Cut placement — every resulting phase must keep criteria a reviewer can fail. The review gate audits acceptance criteria, so criteria quality determines whether it can catch anything. A vertical slice is the usual way to get failable criteria ("a station with exactly one overlapping assignment shows the worker's name beneath the station number"); a horizontal layer tends to yield trivially-satisfiable ones ("the client exports
getStationAssignments"). But failable is the requirement, not vertical. The one named exception to slicing vertically: a purely mechanical phase (locale files, codegen) whose criteria are still falsifiable — "nine keys exist in en, es and hi at the stated positions", "no es value is English" — is safe because the review gate can still fail it. A criterion built on a shell check must state the directory it runs from: a repo-root-relative pathspec run from a subdirectory returns the same silence as a genuine zero-hit pass, turning the gate vacuous. A criterion that requires evidence to be recorded must name a location the reviewer can read (commit body, committed file, synced plan issue) — "in the progress note" names nothing durable, and the review gate will return NOT MET on it. A criterion only a human can satisfy begins withHuman verifies …— Step 6b's Criteria verifiability item checks all three.
Smaller phases are not free: each adds fixed overhead — a brief, a review spawn, a commit, a handoff. If no phase ever fails, splitting increases total cost; the win materialises when phases need corrective passes, and it grows with expected defect rate. Split most aggressively where the work is riskiest.
Out of scope for plan content
Do NOT include phases or steps for:
- Branch creation —
run-plancreates aplan/<slug>work branch automatically at start - Per-phase commits —
run-plancommits after each phase using thecommitskill's format - PR submission —
run-planopens the PR at end of run, linking the plan sub-issue and parent PRD - Merge / deployment ceremony — these belong to the team's release process, not the plan
Plans describe the work (what to build, how to verify it). The git/GH ceremony (branch, commits, PR) is handled uniformly by run-plan. Users may opt out of individual pieces via --no-branch or --no-pr; plans don't need to adapt to those overrides — they describe the work either way. If the PRD calls for post-merge verification or production observation, capture that as a phase describing what to observe and decide, not as commit/PR steps.
Cross-phase evolution
When a shared function, component, or data structure is introduced in one phase and modified in a later phase, document the evolution explicitly:
- In the earlier phase: describe what is built and note that later phases will extend it
- In the later phase: state clearly that it MODIFIES the earlier phase's implementation, and describe the before→after change (e.g., "Modify
resolveDefault()from Phase 2 to check the cache BEFORE falling back to the hardcoded default") - This prevents implementing agents from rebuilding from scratch or being confused about the function's current state
Forward-compatibility
For each phase, identify structural decisions that must anticipate later phases:
- Extension points that later phases will populate (e.g., "Response DTO includes an empty
metadatamap — Phase 4 will populate it with audit fields") - Abstraction points that later phases will override (e.g., "Use a
resolveEndpoint(key)function for path resolution so Phase 6 can swap in tenant-specific paths") - Document these as explicit notes in the phase's "What to build" section
5. Quiz the user
Present the proposed breakdown as a numbered list. For each phase show:
- Title: short descriptive name
- User stories covered: which user stories from the PRD this addresses (a PRD organized as numbered decisions instead of stories: cite the decision numbers, and PRD coverage maps decisions to phases)
Ask the user:
- Does the granularity feel right? (too coarse / too fine)
- Should any phases be merged or split further?
Iterate until the user approves the breakdown.
6. Verify the plan
Before writing the file, verify the plan against the actual codebase, not your memory of it. Reviewers need a document to read, so first render the full plan (Step 7d's template) to a scratch draft outside the plans directory — resolve a scratch location at runtime, checking in this order: a scratch directory the repo's own instructions mandate, then the host's scratch or temp directory, then a scratch/ sibling of the plans directory ( when the location is inside the repo, verify git ignores it with git check-ignore and add it to .git/info/exclude yourself when it doesn't — never assume coverage), never the plans directory itself, which receives only the approved artifact at Step 7 — and hand reviewers that path, telling them in the brief that the scratch location is deliberate and not a finding. Every fix in this step edits the draft; Step 7 copies the approved draft into place. This catches the #1 source of plan errors: working from stale memory instead of real files. The agent that drafted the plan reads it charitably — it knows what each phase meant — so wherever the host allows, this verification should be done by an independent reviewer with a fresh context window, not by the author re-reading its own work.
Step 6a — Choose the verification mode (capability gate):
- Preferred — independent reviewer. If the host can spawn a sub-agent with its own context window (the reference implementation is Claude Code's Task tool — e.g. an
Exploreagent for read-only review), delegate verification to one. Hand it the plan and the Step 6b checklist, but NOT your rationale for the plan — it must read the referenced files cold and judge each claim on the evidence. The reviewer reports findings; it does not edit. For a large plan you may fan out 2–3 reviewers (e.g. one for source/feasibility accuracy, one for cross-phase coherence + PRD coverage, one for i18n) — keep it small; most checks are cheap. You (the author) then apply the fixes. - Fallback — adversarial self-review. If the host cannot spawn sub-agents, run the Step 6b checklist yourself as an explicit red-team pass in a distinct turn: assume every phase is wrong until the actual files prove otherwise. This still carries author bias, so it is weaker than an independent reviewer — but far better than a charitable re-read.
In both modes, re-read every source file referenced in any phase's "What to build" section from disk now — not your earlier summary.
Step 6b — Verification checklist (the reviewer runs it; the author fixes what it surfaces):
- Source accuracy — File paths exist. Function signatures match what the plan describes. Provider/component nesting hierarchies are accurate. Interfaces and types match what the plan claims to extend.
- Architectural feasibility — For each phase, components can actually access the contexts/hooks/functions they're described as using. Check provider nesting, import paths, and module boundaries.
- Cross-phase coherence — For each phase that modifies something from an earlier phase: the before→after change is explicitly documented, earlier phases' tests won't break silently from later changes, and shared interfaces evolve consistently.
- Decision completeness — No "either/or", "or alternatively", "if > N lines", or other unresolved language anywhere. Each must resolve to a single prescriptive decision. Plans close decisions; they don't enumerate options.
- PRD coverage — Every user story from the PRD maps to at least one phase. Every testing decision from the PRD has a corresponding TDD slice.
- Criteria verifiability — Every acceptance criterion must be checkable by
run-plan's Review agent from the repository alone. Three sub-checks: (1) Executable: run every read-only shell command a criterion states, from the directory the criterion names, and report what it returns today against what the plan says it must return after the phase — a command that already passes proves nothing, one that cannot pass on a correct implementation will make the executing agent change working code, and one that is invalid on the host's shell (BSD vs GNUgrep/sed/regcomp—\sis not portable) is silence indistinguishable from a pass. (2) Evidence location: a criterion that requires recorded output names a location the reviewer can read — the phase's commit body, a committed file, or the synced plan issue — never "the progress note", "the summary", or the conversation: those are not durable and the reviewer cannot see them. (3) Actor: a criterion only a human can satisfy (an apply, a console check, a live-traffic observation) begins withHuman verifies …/Human confirms …, so the executing skill's human-gate rule identifies it mechanically; in a phase that carries a**Known risk**: human actionline, EVERY such criterion is labelled, not just one. - i18n completeness — If the PRD contains user-facing strings and the project uses i18n, verify the plan matches the acceptance signal in references/i18n-phase.md: Phase 1 is a Translations phase, architectural decisions list i18n namespaces and reusable keys, no later phase edits translation files, non-default locales have real translations. If any criterion fails, return to Step 3.5 and rework.
Step 6c — Confidence scoring (must be independent of the author):
Rate each phase 0–10 for how likely an AI coding agent is to implement it correctly from the plan alone. The score must come from whoever ran the verification, not from the plan's author — a number the author assigns its own plan carries no signal. In independent-reviewer mode, the reviewer returns the scores; in fallback mode, treat the score as a self-checklist prompt and lean on the Step 6d user approval as the real gate.
For any score below 9, the reviewer must also classify the gap: specification gap (fixable by adding detail to the plan) or irreducible risk (no plan detail can close it). Irreducible risk must be one of these named causes:
- The phase requires a human action a coding agent cannot perform, whether verification or setup (visual inspection, physical hardware, provisioning in a third-party dashboard).
- The phase depends on external-system behavior unknowable before implementation (undocumented API responses, production data shape).
- The phase's residual uncertainty lives in runtime behavior observable only by executing the change against real state (a migration against real data, behavior under concurrent load) — not in what the code should say.
The irreducible-risk classification is only available for a phase with zero outstanding Step 6b findings — specification vagueness generally surfaces as a 6b finding, so a clean checklist is the operational test for "nothing fixable remains" (the counterfactual score below backstops what 6b misses). The reviewer must also report the counterfactual score the phase would earn if the named cause were resolved: the exception applies only if that counterfactual is 9+ and the actual score is 5 or higher. Below 5, the irreducible-risk exception is unavailable — return to Step 5 and split or checkpoint the risky work into its own thin phase instead (this floor governs only the exception: a below-5 phase classified as a specification gap is fixed per the next paragraph and never split on its score alone); a phase that is already such a minimal risk-isolating slice with a human checkpoint in its acceptance criteria is exempt from this floor (otherwise splitting would loop forever). In fallback mode the classification, like the score, is the author's own claim; mark such rows as self-classified in the Step 6d table so the user's approval is informed.
Flag any phase below 9. Fix every Step 6b checklist issue before proceeding. For a flagged phase classified as a specification gap, add concrete implementation details (code snippets, API configurations, exact function signatures) until the score reaches 9+ or the remaining shortfall is entirely a named cause. After applying fixes to any flagged phase, re-run verification in the same mode as Step 6a; the scores and classifications presented in Step 6d must come from that re-run — in independent mode never from the author, in fallback mode from a fresh red-team pass rather than carried over from before the fixes. A re-run may be scoped to the phases the fixes touched (plus any phase whose cross-phase references changed), or to a named fix set or single finding when the fixes cut across phases — either way the brief states the scope; the Step 6d table then labels every score with the round that produced it, so an unchanged phase's older score is visibly older and never presented as current.
Convergence. Every round's reviewer brief — round 1 included — asks the reviewer to class each finding as design (any Step 6b item except Criteria verifiability) or criteria (Criteria verifiability), and to grade each design finding critical (the phase as written builds the wrong thing or cannot build), major (an executing agent would stall or guess), or minor (anything else). A round whose design findings include no critical and no major has converged, whatever the scores: fix its remaining findings, re-run Criteria verifiability alone on the changed criteria, and proceed to Step 6d — the table discloses the post-convergence fixes, and a phase still below 9 is presented with its score and the note "no major design findings outstanding"; the user decides whether to publish. Zero findings is not the bar: an adversarial reviewer returns something every round, and a fix round can introduce the next round's findings — all-minor is the exit, not silence. A round that does NOT converge routes through the normal loop: the author fixes its findings and a reviewer re-runs per Step 6c, whose scoping rule applies unchanged; convergence decides only when the design re-run is skipped, never who runs what. An author-adjusted score is never presented: the Step 6d table carries the last reviewer-assigned score for each phase, labelled with its round, however many author fixes landed after it. A finding that flags an existing criterion as defective is answered by rewriting or deleting that criterion, never by adding another beside it; a finding that a needed criterion is missing is a coverage gap and legitimately adds one. Criteria added during verification are how a phase crosses the split signal and restarts the loop — in production, two runs each spent their last two of six rounds this way. Never pad a phase with invented detail to inflate its score; fabricated snippets read as authoritative and are worse than a flagged phase.
A phase accepted under the exception must carry the risk into the plan file: add a **Known risk**: <named cause> line to that phase in Step 7d, and for a human-action cause add a matching acceptance criterion (e.g. - [ ] Human verifies <X>) — executing agents read the plan alone, so a risk that lives only in this conversation is a risk they never see.
Step 6d — Present verification results — Show the user a summary table with each phase's confidence score (0–10), any issues found, and the changes made to resolve them. If verification changed the structure the user approved at Step 5 (a phase merged or split), the table presents the new structure and states what changed and why — the Step 5 approval is not re-run; this approval covers it. For any phase presented below 9 under the irreducible-risk exception, the table must state the named cause (and mark it self-classified in fallback mode) so the user is accepting that risk explicitly, not overlooking it. Do NOT proceed to Step 7 until the user explicitly approves. If the user requests changes, revise, re-verify (same mode as above), and re-present. Repeat until approved.
7. Write the plan file
Prerequisite: Step 6 verification results MUST have been presented to the user and explicitly approved before writing. If Step 6 has not been completed and approved, go back and complete it now.
Step 7a — Determine the plans directory using this precedence:
- If
.agents/plans/exists, use it - Else if
.claude/plans/exists, use it - Else if
.agents/exists, create.agents/plans/and use it - Else if
.claude/exists, create.claude/plans/and use it - Otherwise, create
.plans/and use it
Step 7b — Derive the plan slug:
The slugify rule (shared with write-a-prd Step 6a):
- Lowercase
- Replace spaces with hyphens
- Strip any character that is not alphanumeric or a hyphen
- Collapse any run of consecutive hyphens to a single hyphen; trim leading/trailing hyphens
- If a local PRD file is in play (e.g.
.agents/plans/mui-v9-migration-prd.md), pair the slug by swapping the suffix:mui-v9-migration-plan.md - Else slugify the GH issue title — strip the leading
PRD:prefix first (the feature-name rule in Step 1c) — using the rule above: "PRD: MUI v9 Migration" → "MUI v9 Migration" →mui-v9-migration-plan.md - State the resulting filename in the Step 6d approval message; the user's approval covers it (no separate confirmation)
Step 7c — Re-invocation detection (run before writing):
Check for existing state:
- Target plan file exists at the computed path?
- When GH is in play: a sub-issue with title
Plan: <feature name>exists under the parent? Prefer reading the<!-- gh-sub-issue: N -->footer from an existing local file (more reliable than title matching). Otherwise querygh api /repos/<org>/<repo>/issues/<parent>/sub_issuesand match by title.
Branch based on state:
| Local file | Sub-issue | Prompt |
|---|---|---|
| No | No | Proceed normally |
| Yes | No | Plan file exists. Overwrite (regenerate) / re-publish existing file as sub-issue / cancel? |
| Yes | Yes | Plan file and sub-issue #N both exist. Regenerate file / update sub-issue body / cancel? |
| No | Yes | Sub-issue #N exists but no local file. Fetch its body as the local file / regenerate from scratch / cancel? |
If the user chose a publish-only or update-only path, skip the file write below and jump to Step 8 using the existing file. Otherwise proceed.
Step 7d — Write the plan using the template below.
The Source PRD: header has two forms — do not mix them:
- GH in play:
> Source PRD: #123 — https://github.com/<org>/<repo>/issues/123 ("Title") - Pure local:
> Source PRD: .agents/plans/<slug>-prd.md
When a GH issue is in play, omit any local PRD file reference — the GH issue is the canonical source, and the local PRD file is typically uncommitted (a local path would confuse anyone reading the plan from GitHub).
Source PRD:
- GH in play:
#123 — https://github.com/<org>/<repo>/issues/123 ("Title")- Pure local:
.agents/plans/<slug>-prd.md
Architectural decisions
Durable decisions that apply across all phases:
- Routes: ...
- Schema: ...
- Key models: ...
- i18n: Namespace(s)/scope(s) used, reusable key reference (from Step 3.5, omit if no user-facing strings or no i18n)
- (add/remove sections as appropriate)
Phase 1:
User stories:
Known risk: <named cause from Step 6c — only for a phase accepted below 9 under the irreducible-risk exception; omit this line otherwise>
What to build
A concise description of this vertical slice. Describe the end-to-end behavior, not layer-by-layer implementation.
Acceptance criteria
- Criterion 1
- Criterion 2
- Criterion 3
Phase 2:
User stories:
What to build
...
Acceptance criteria
- ...
8. Publish to GitHub (only if GH is in play)
Skip this step entirely if operating in local-only mode.
Step 8 — Determine the operation based on how Step 7c resolved:
- Create — no existing sub-issue (fresh first-time publish, or "re-publish existing file" from the re-invocation matrix). Follow Steps 8a–8e.
- Update — existing sub-issue should be updated in place (from the "update sub-issue body" branch of the re-invocation matrix). Skip 8b–8e and follow Step 8f only.
Step 8a — Confirmation gate:
- Create path:
Ready to create sub-issue under #<parent> from <path>. Review the file (and edit if needed), then reply 'create' to proceed, or 'cancel' to stop. - Update path:
Ready to update existing sub-issue #<n> from <path>. Review the file (and edit if needed), then reply 'update' to proceed, or 'cancel' to stop.
Wait for explicit confirmation. The user may edit the file before confirming.
Step 8b — Create the sub-issue:
First ensure the plan label exists, since gh issue create --label plan fails with HTTP 422 if it doesn't:
gh label create plan \
--color bfd4f2 \
--description "Phased implementation steps and technical rollout details" \
2>/dev/null || true
This is idempotent: gh label create errors when the label already exists, and 2>/dev/null || true swallows that so a pre-existing plan label is left untouched. Do NOT add --force — that would overwrite an existing label's color/description. If this command fails for a real reason (auth, network), the gh issue create below surfaces it. Then create the sub-issue:
gh issue create \
--title "Plan: <feature name>" \
--body-file <path> \
--label plan
Capture both the new issue number (for display) and the issue's integer database ID (for the attach call in Step 8c). The REST sub_issues endpoint expects the integer database ID, NOT the GraphQL node ID — passing the node ID returns HTTP 422 is not of type 'integer'. Fetch it with:
CHILD_DB_ID=$(gh api /repos/<org>/<repo>/issues/<child_number> -q .id)
Do NOT use gh issue view <n> --json id — that returns the GraphQL node ID (string starting with I_kw…), which the endpoint rejects.
Step 8c — Attach the sub-issue relationship:
gh 2.88.1 has no --parent flag and no sub-issue subcommand. Use the REST API. Note the -F (uppercase) flag — gh api sends -f values as strings, but sub_issue_id must be a number:
gh api --method POST /repos/<org>/<repo>/issues/<parent>/sub_issues \
-F sub_issue_id=<child_db_id>
Retry up to 3 times on failure with backoff: 250ms, 1s, 3s (most failures are transient rate-limit or eventual-consistency issues).
Step 8d — On success:
- Append
<!-- gh-sub-issue: <child_number> -->as a footer to the local plan file - Report the sub-issue URL to the user
Step 8e — Failure handling:
Create fails (Step 8b): the local file is intact. Report the error verbatim and instruct the user to re-run
/prd-to-plan #<parent>to retry — the re-invocation matrix in Step 7c will detect the existing file and offer the publish-only path.Attach fails after retries (Step 8c): the child issue exists but has no parent link (orphan). Do NOT auto-delete. Report the partial state with the exact fix command:
Plan written to <path>. Sub-issue #<n> created: <url> Failed to attach as sub-issue of #<parent>: <error> To fix the relationship manually: CHILD_DB_ID=$(gh api /repos/<org>/<repo>/issues/<n> -q .id) gh api --method POST /repos/<org>/<repo>/issues/<parent>/sub_issues \ -F sub_issue_id="$CHILD_DB_ID" To start over: gh issue delete <n>, then re-run /prd-to-plan #<parent>.
Step 8f — Update path (when the user chose "update sub-issue body" from Step 7c's re-invocation matrix):
Instead of create + attach, run:
gh issue edit <n> --body-file <path>
This replaces the body wholesale, preserving the issue number, comment history, and the sub-issue relationship. No changelog preamble is added — the local file is the source of truth.
The <!-- gh-sub-issue: <n> --> footer is already present in the file from the original create; do NOT re-append. On success, just report the sub-issue URL to the user.