| name | forge-plan |
| description | Write implementation plans for clarified Forge issues. Use this skill whenever an issue needs a plan before coding begins — exploring the codebase, identifying affected files, and writing step-by-step implementation instructions into the issue's plan field. Triggers on: /forge-plan, planning issues, writing implementation plans, exploring codebase for an issue, preparing issues for development, moving issues from clarified to approved. Also use when the pipeline needs to advance an issue from clarified status. |
| user_invocable | true |
| arguments | documentId |
Forge Plan
This is the planning step in the issue pipeline: clarified → approved (it runs after triage and clarify). Its job is to turn a triaged, reproduced issue into a concrete implementation plan that a coding agent (or developer) can follow without re-exploring the codebase.
Planning is the highest-value step in the pipeline. A good plan saves the coding step from wasting tokens on exploration, wrong turns, and rework. A bad plan (or no plan) means the coding agent explores blindly, makes architectural mistakes, and produces code that needs heavy review.
Usage
/forge-plan <documentId>
Tools
forge_issues, forge_comments, plus codebase exploration tools (Read, Glob, Grep).
Two-Tier Planning
Not every issue needs deep codebase exploration. The planning depth should match the complexity:
Lightweight plan (xs/s/m): Use project knowledge (forge_knowledge) + issue description + targeted Glob to identify files and write the plan. Read at most 1-2 source files — only when you need to check an existing pattern or verify a component's current props/API. The coding agent will read the files during implementation anyway, so duplicate deep-reading wastes tokens.
Deep plan (l/xl): Full codebase exploration. Read all affected files, trace dependencies, verify patterns. l/xl issues involve architectural decisions where a wrong plan costs more than the exploration.
The tier is determined by the triage comment's complexity classification.
Workflow
Pull-model note: On large issues forge_step_start returns a lean manifest (bodyTruncated:true). Fetch plan/description/acceptanceCriteria as needed via forge_issues.get { documentId, fields: ['description', 'acceptanceCriteria'] } rather than assuming full body is present.
Step 1: Fetch Issue & Triage Context
Fetch the issue and its comments in parallel:
forge_issues → get → { documentId: "<id>" }
forge_comments → list → { filters: { issue: "<documentId>" } }
forge_config → get
Verify status is clarified. If the issue isn't clarified yet, stop and explain — planning before triage + clarify skips the completeness and reproduction checks and risks wasting exploration time on an incompletely-understood issue.
Find the triage comment (starts with **Triage**) and extract the complexity classification — it sets both planning depth and exit behavior. Then read the clarify findings (comment + step handoff — every issue now passes through clarify, so these are always present): the reproduction outcome, the environment tested, and a code-level root-cause hypothesis. Trust clarify's verified behavior over re-deriving the problem from the description — plan the fix for the confirmed root cause, not the reported symptom.
Checkout the latest baseBranch (from forge_config, see preamble for the detection rule) so exploration sees the current base-branch code.
Step 2: Understand the Issue
Read everything available:
- title, description — what needs to change
- acceptanceCriteria / aiAcceptanceCriteria — what "done" looks like
- suggestedSolution / aiSuggestedSolution — proposed approach (if any)
- Triage comment — complexity and category context
- attachments — screenshots, mockups, or files the user uploaded. If the issue has
attachments (array of {name, mime, url}), fetch and read each one using the Read tool (for images) or WebFetch (for other files). Screenshots often show the bug or desired UI — they are critical context that the description alone may not capture.
- relations — check the issue's
relations field for linked issues
Synthesize: what area of the system is affected, what the change should accomplish, and what constraints exist.
Handle Relations
If the issue has relations, fetch each related issue to understand the context:
forge_issues → get → { documentId: "<related docId>" }
How to use each relation type:
blocked_by / depends_on — Check the blocker's status. If it's not yet developed or beyond, flag it in the plan as a prerequisite. The plan should note which parts depend on the blocker's output (schema changes, new APIs, shared components). If the blocker is already completed, read its plan to understand what was built and build on top of it.
related_to — Read the related issue's plan (if it has one) or description. Identify overlapping files — if both issues touch the same files, the plan must account for potential merge conflicts. Note which files overlap and how changes should be coordinated.
duplicate_of — Shouldn't reach planning (triage sets needs_info). If it does, stop and post a comment pointing to the original.
caused_by / fixed_by — Read the linked issue for root cause context. Factor the underlying cause into the plan to avoid a surface-level fix.
Include a Relations section in the plan when relations affect implementation (overlapping files, dependencies, ordering constraints). Skip it when relations are purely informational.
Step 3: Build the File Map
Resolve the issue into concrete file paths using project knowledge — there is no local knowledge.json file. Query the knowledge store via forge_knowledge (list for the body-free index, get a slug for its body, search topically); for prior conventions/gotchas, also forge_memory.search.
Use it to:
- Resolve the affected area into exact file locations — infer paths from the project's own structure and any
paths-style knowledge entries (e.g. a frontend-feature path → the project's feature dir); don't assume a fixed layout
- Identify which domains / content types are involved
- Pull any recipe matching the issue type (new endpoint, new page, new tool) for its implementation-steps template
- Reference naming and state-management conventions
Then use a targeted Glob to confirm the files exist:
Glob: <source-root>/**/*<keyword>*
Step 4: Explore (Depth Depends on Tier)
For xs/s/m (lightweight):
- You now have the file list from project knowledge + Glob. That's usually enough.
- Only read a source file if you need to check: a component's current props/API, an existing pattern to reference, or whether a utility already exists.
- Limit to 1-2 file reads max. The coding agent will read everything during implementation.
For l/xl (deep):
- Read
references/exploration-guide.md for the full exploration approach.
- Read all affected files to understand current state.
- Follow the data flow — trace from API to UI or vice versa.
- Check for shared dependencies — Grep for imports of any types/utilities you plan to change.
- Read existing tests to understand testing patterns.
Consistency / systemic-pattern sweep (signal-gated)
Some issues describe a PATTERN, not a single spot — naming one location is an EXAMPLE, not the scope boundary. Signals: "sync" / "consistent" / "standardize" / "unify", "across" / "everywhere", "match the system/shared component", "e.g." / "such as", or replacing an ad-hoc thing with the shared one. Trigger ONLY on these signals — a simple, single-spot issue does NOT get swept (keeps lightweight plans lightweight). When triggered:
- Identify the pattern — the shared component/util to converge on and the ad-hoc anti-pattern it replaces.
- Inventory first, before any change step. Grep/ast-grep the whole relevant surface for every occurrence of the anti-pattern (and of the target, to copy correct usage). This inventory is the canonical checklist: every site must end up in Affected Files OR in a tracked follow-up — never silently dropped. (Agents default to fixing only the instance they were shown; the explicit inventory overrides that.)
- All-or-track. If the full set fits one reviewable change, plan all of it. If genuinely too large, plan the highest-value slice now and create a
draft follow-up issue (a real issue, not a prose mention) enumerating every deferred site, linked related_to. "Done" = re-running the inventory grep returns zero survivors, OR every survivor is in an open follow-up.
- Don't chase adjacent yaks. Sweep only the one pattern this issue is about. A different smell → its own
draft issue, never bolted onto this plan. If the issue only incidentally touches the ad-hoc pattern (not a standardization ask), keep cleanup a separate follow-up rather than expanding scope.
- If swept scope is materially larger than triage's complexity guess, say so in the plan comment so the coder/human can re-gauge; don't quietly shrink back to the named example.
Step 5: Write the Plan
Write the implementation plan following the format in references/plan-format.md. The plan goes into the issue's plan field:
forge_issues → update → { documentId: "<id>", data: { plan: "<markdown plan>" } }
For lightweight plans: Focus on the what — which files, what changes, what approach. The coding agent will figure out the how when it reads the code. Reference project-knowledge recipes when applicable.
For deep plans: Be concrete about both what and how — file paths, function names, pattern references. The coding agent should be able to follow the plan step-by-step without re-exploring.
No-code deliverables (decision / audit / spike)
Some issues ship no source code — their only deliverable is a decision (a port-or-drop matrix, an architecture audit, a spike conclusion). With nothing to commit, the issue would stall: code produces no branch, review has no diff, test/release have nothing to merge or close — a re-dispatch loop.
Make the decision a mergeable artifact instead. Plan an in-repo markdown document and treat it exactly like code:
- Classify an issue as a no-code deliverable only when its acceptanceCriteria/goal is purely a decision/audit/spike write-up with no source, UI, API, or schema change. When in doubt, plan it as normal code — this is a narrow class, not a catch-all for "has some docs."
- The deliverable is a doc at
docs/proposals/<topic>.md (short, kebab, topic-focused), added to the index in docs/proposals/README.md. Name the exact path in the plan and outline the required sections so the coding step writes substantive content, not a stub.
- This makes the change a docs-only diff (touches only docs/prose, no source paths — derived from the repo's own structure), which flows code → review → test → release like any other change. The coding/review/test steps key off that mechanical signal, so a real decision becomes a durable, discoverable artifact rather than a comment that vanishes from the codebase.
- Do NOT decompose a no-code deliverable (see Step 5.5). Recommended follow-ups (e.g. ports the decision endorses) spin off as standalone issues linked with a soft
related_to — never as decomposes children, since a pure decision has nothing to integrate.
Step 5.5: Decide whether to decompose (l/xl epics only)
For l/xl issues with >3 parallel workstreams that each ship independently, split the epic into a parent + children using kind='decomposes' dependency edges. Adding the first edge makes core (ISS-138) create + push a shared integration branch off <baseBranch>, park the parent at waiting, and stamp branchConfig so every child branches off and merges back into that branch (not base). The lifecycle hooks in pipeline/decomposition-subscribers.ts then automate cascade approve, the all-children-ready watcher, the parent's integration gate, and close cascade. Only the parent squash-merges the integration branch → base, once, after verifying the assembled epic.
When to decompose:
- Each child must be reviewable + testable independently.
- Cap at 6-8 children per epic — worker reliability degrades beyond that.
- The parent must have a meaningful integration-test step after all children land (otherwise just use
blocks dependencies — the watcher exists specifically to re-fire integration tests on the parent).
- Workstreams should not share critical code paths that will collide at PR-merge time.
When NOT to decompose:
- Single-file changes, refactors localized to one module, bug fixes.
- No-code deliverables (decision/audit/spike, docs-only — see above): never decompose, regardless of how many recommendations they contain. A pure decision has nothing to integrate; endorsed follow-ups become standalone
related_to issues.
- Items where one child's failure should not block siblings' release — the gate is atomic by design.
- Nested decomposition (epic → epic → story). Single-level only for v1.
How to decompose — once the decision is made, follow the mechanics in references/decompose-protocol.md: create children at draft, add decomposes (and any sibling blocks) dependency edges via forge_project_pm → set_dependency (verify each row landed — never claim an edge in prose without the call succeeding, the ISS-131 failure mode), write the parent plan as the per-child index plus a parent integration-step section, and do NOT create the integration branch, stamp branchConfig, or set the parent status yourself (core's decomposeParent does all three on the first edge). The reference also documents the integration-branch model (children branch off + merge back into the shared branch; the parent is the only one that squash-merges to base), the post-approval automation (cascade-approve, the decomposeChildrenPending gate, close cascade), and how to verify sibling-blocks edges took effect.
Step 6: Validate the Plan
Before posting, sanity-check:
- Does every file in "Affected Files" actually exist? (Glob to verify — you may already know from Step 3)
- Do the implementation steps cover all acceptance criteria?
- Are there any obvious risks or edge cases not addressed?
Skip checking test files for lightweight plans — the coding agent handles testing.
Step 7: Post Comment & Set Status
The exit behavior depends on complexity (from the triage comment, extracted in Step 1):
xs / s / m complexity:
forge_comments → create → { data: { body: "<plan comment>", issue: "<documentId>", author: "Alakazam" } }
forge_issues → update → { documentId: "<id>", data: { status: "approved" } }
Auto-approving xs/s/m plans keeps the pipeline fast. Status update is LAST — it triggers the coding step.
l / xl complexity:
forge_comments → create → { data: { body: "<plan comment>", issue: "<documentId>", author: "Alakazam" } }
forge_issues → update → { documentId: "<id>", data: { status: "waiting" } }
Set status to waiting — l/xl issues (and every decomposed epic) wait for a human to review the plan and manually approve. Status update is LAST.
If no triage comment found (manual invocation, not from pipeline):
- Default to auto-approve behavior (treat as
m)
Plan Comment Format
**Plan** — <one-line summary of the approach>
**Affected files:** <count> files in <package(s)>
**Status:** <Auto-approved / Awaiting human approval>
The full plan has been written to the issue's plan field.
Keep the comment short — the full plan lives in the plan field, not the comment. The comment is just a notification for humans and downstream skills.
Plan-specific output reminder
The plan goes to the API (forge_issues → update on the plan field), NOT to chat output. Don't print it twice. (See pipeline preamble for general output rules.)