WHAT
Purpose and context
- Purpose: Turn an approved SPEC into text that a good engineer who knows nothing about this codebase, and whose taste is questionable still executes correctly. In overstack, that "engineer" is usually a cheap CLI running headless (
opencode/agy/kiro): it inherits no context from the main session, cannot ask back, and when it hits ambiguity it will guess and stay silent. Measured lesson 250626: thin brief → delivery ~1/5. - Trigger (when to use):
- SPEC (
/propose) has been approved by the user at the gate, and the task is about to be dispatched to an agent. - Work spans multiple steps, multiple files, or is split across ≥2 agents running in parallel.
- SPEC (
- Non-goals: NOT used when there is no approved SPEC (use
/proposefirst), and not used for a one-line fix. Not used to design or to ask for requirements (that is/propose).
Mental model
Whatever is not in the PLAN, the agent does not have. That is the entire principle of this skill.
SPEC đã duyệt (FR-xxx, Global constraints) → scope check → File structure → task tracer bullet (hoặc expand → migrate → contract) → mỗi task: Thoả + Files + Interfaces + Depends + Verify + Steps TDD → self-review → DDMMYY-<tên>-PLAN.md → orca orchestration task-create/dispatch --inject nguyên văn.
Input and output contract
| Field | Required? | Meaning | |
|---|---|---|---|
| In | Approved SPEC llmwiki/wiki/sources/draft/DDMMYY-<tên>.md |
yes | take as-is ## Context, ## Global constraints, ## Plan, ## Agent Task Assignment |
| Out | llmwiki/wiki/sources/draft/DDMMYY-<tên>-PLAN.md |
yes | exactly in the PLAN format: frontmatter + ## Origin + Global constraints verbatim + File structure + the ### Task N |
| Out | lines wiki/index.md + append wiki/log.md |
yes | per step 7 |
| Out | report SPEC breakage | when the reverse Gate triggers | collect once, each spot with the SPEC sentence that caused it |
Rules and capabilities
- RULE-01 (MUST): PLAN does not need file
.html— it is what the machine reads. HTML is attached to the SPEC (what a human views at approval time), generated by/proposeR7 waives the diagram check for the branch-PLAN.md. - RULE-02 (MUST): File paths always exact, with line ranges when editing an existing file.
- RULE-03 (MUST): A step that changes code must have complete code — not just prose description.
- RULE-04 (MUST): Commands to run exactly, with expected output (the agent needs to know what a correct fail looks like and what a pass looks like).
- RULE-05 (MUST): DRY, YAGNI, TDD, commit often.
- RULE-06 (MUST): In the existing codebase: follow the existing pattern. Do not opportunistically refactor things outside the task.
- Capabilities: read SPEC + codebase to identify real paths/signatures; write PLAN + index + log files in the wiki; do not dispatch before self-review is complete.
Failure boundaries
- No approved SPEC yet → cancelled, move to
/propose. - Reverse gate (request cannot be reduced to a task, contradiction, infeasible option, must fabricate functions/types/files) → blocked: STOP, do not write a half-baked PLAN, collect all breakages and report them at once, return to
/proposefor re-approval. - Placeholder remains or
FR-xxxis not accepted by any task → failed (R7/R18 block), fix in place. - One-line fix → do not use the skill.
HOW
Main workflow
| Step | Type | Inputs | Action | Outputs/exit | Failure/next |
|---|---|---|---|---|---|
| W01 | deterministic | SPEC | Read the approved SPEC, take the sections as-is | SPEC content | no approved SPEC → stop, /propose |
| W02 | judgment | SPEC | Scope check | one or more PLANs | multiple subsystems → split |
| W03 | judgment | SPEC + codebase | Draw ## File structure before splitting into tasks |
list of files + responsibilities | file/API does not exist → B01 |
| W04 | judgment | File structure | Split into tracer bullet tasks (or wide refactor) | list of tasks | WIDE REFACTOR → B02 |
| W05 | judgment | task | Write each task using the template (enough Files + Interfaces + Steps) | the ### Task N |
must fabricate functions/types → B01 |
| W06 | judgment | draft PLAN | Self-review 3 nets, fix in place | clean PLAN | — |
| W07 | effect | PLAN | Write file + index + log | DDMMYY-<tên>-PLAN.md |
R7/R18 block → fix, rewrite |
| W08 | effect | PLAN | Handoff: task-create + dispatch --inject verbatim with Global constraints |
tasks dispatched | — |
Details of each step (source of truth for W01–W08):
-
Read the approved SPEC —
llmwiki/wiki/sources/draft/DDMMYY-<tên>.md. Take as-is:## Context,## Global constraints,## Plan(the lines- [ ]),## Agent Task Assignment. -
Scope check — A SPEC embracing multiple independent subsystems → split into multiple PLANs, each PLAN on its own yielding runnable and testable software. Don't cram.
-
Draw
## File structurebefore splitting tasks — list every file to be created/modified and each file's responsibility (one file, one responsibility; files that change together stay together). The decomposition decision is locked here, not during coding. -
Split tasks into TRACER BULLETS — each task is a VERTICAL slice: a thin slice cutting through all layers (data → logic → UI/CLI), runnable on its own and self-proving, leaving an independently testable deliverable. Not a "horizontal slice" like "do the entire data layer" then the next task "do the entire logic layer" — horizontal slices can't run on their own. Setup, config, scaffolding, docs → fold into the task that needs them; split only when a reviewer can reject this task while still approving that one.
The named exception — WIDE REFACTOR (expand → migrate → contract). A mechanical change whose blast radius spans the whole codebase — renaming a column, changing the type of a shared symbol — cannot keep any vertical slice green: one edit turns thousands of call-sites red at once. Don't force it into the tracer bullet mold. Sequence:
- expand — add the NEW form alongside the old form, delete nothing yet. One task. Nothing breaks because the old form still exists.
- batch migrate — move call-sites to the new form, batching by blast radius (per-package, per-directory). One task per batch, blocked by the expand task. CI is green for each batch because the old form is still alive.
- contract — delete the old form when no caller remains. One task, blocked by every migrate batch.
- When even individual batches can't stay green on their own: put them on a shared integration branch, all blocked by a final "integrate-and-verify" task — green is only promised there.
-
Write each task using the template below (Files + Interfaces + Steps all required).
-
Self-review (the 3-point checklist, the 'Self-review' item below), fix in place.
-
Write the file
llmwiki/wiki/sources/draft/DDMMYY-<tên>-PLAN.md, add a line tollmwiki/wiki/index.md, appendllmwiki/wiki/log.md. -
Handoff: each
### Task Nis aorca orchestration task-create;dispatch --injectpumping verbatim that task's brief plus## Global constraints. Do not summarize — summarizing is where context drops.
Branches
| ID | Kind | Guard | Behavior | Skip / failure | Rejoin |
|---|---|---|---|---|---|
| B01 | recovery | Reverse gate: a STOP condition occurs in the Reverse gate item | STOP, collect ALL failures into ONE report, return to /propose to fix SPEC + re-approve |
— | W01 (after SPEC re-approval) |
| B02 | conditional_required | mechanical change with blast radius across the codebase | WIDE REFACTOR: expand task → migration batches (blocked by expand) → contract (blocked by all batches); batch does not go green on its own → integration branch + integrate-and-verify task | — | W05 |
| B03 | conditional_required | SPEC covers multiple independent subsystems | split into multiple PLANs, each PLAN can run on its own + be tested | — | W03 for each PLAN |
Validation and stopping
Deterministic: R7 blocks placeholders (write-time + commit), R18 blocks FR-xxx no task receives, R9/R2 block missing frontmatter/## Origin; line **Verify:** every task is an rc 0 command. Needs review: 3-net Self-review (SPEC coverage, placeholders, type/name consistency). Stop after W08, or right at B01.
Examples
- Positive: SPEC
DDMMYY-export-csv.mdapproved has FR-001..FR-003 → PLAN 3 tracer bullet tasks, each task has**Thoả:**,**Files:**exact paths,**Interfaces:**Consumes/Produces, the Verify line ispytest tests/test_export.py -q, Step 1–5 TDD → writeDDMMYY-export-csv-PLAN.md+ index + log → dispatch each task verbatim with Global constraints. - Boundary/failure: when declaring the File structure, you newly discover that the API
## Approacheschosen does not exist, and FR-002 contradicts Global constraints → STOP, do not write a half-baked PLAN, report both places once along with the SPEC sentence that caused it, return to/proposere-approve.
Reference — PLAN template
Mandatory header (frontmatter and ## Origin MUST NOT be omitted — the file is in wiki/sources/draft/ so R9 blocks if frontmatter is missing, R2 blocks if missing ## Origin):
---
type: draft
title: <tên>-PLAN
status: proposed
timestamp: YYYY-MM-DD
task: T-YYMMDD-NN # cùng task-id với SPEC
---
# <Tên> — PLAN thi hành
**Goal:** <một câu: cái này xây ra cái gì>
**Architecture:** <2-3 câu: cách tiếp cận>
**Tech stack:** <ngôn ngữ, lib, test runner, version>
**SPEC nguồn:** `wiki/sources/draft/DDMMYY-<tên>.md` (đã duyệt <ngày>)
## Origin
- **SPEC:** `wiki/sources/draft/DDMMYY-<tên>.md`
- **Commit:** _(verify-before-commit điền)_
## Global constraints
<chép NGUYÊN VĂN từ SPEC — sàn version, giới hạn dependency, luật đặt tên, gate trước push.
Mỗi task ngầm mang theo section này.>
## File structure
- Tạo `path/chính/xác.py` — <trách nhiệm duy nhất của file>
- Sửa `path/có/sẵn.py` — <đổi gì>
Each task:
### Task N: <tên>
**Thoả:** FR-001, FR-003
**Files:**
- Tạo: `đường/dẫn/chính/xác.py`
- Sửa: `đường/dẫn/có/sẵn.py:123-145`
- Test: `tests/đường/dẫn/test_x.py`
**Interfaces:**
- Consumes: <dùng gì từ task trước — chữ ký chính xác>
- Produces: <task sau dựa vào cái gì — tên hàm, kiểu tham số, kiểu trả về>
**Depends:** Task 1, Task 3 <!-- task nào phải DONE trước; không có → ghi `—`. `/orca-graph build` đọc dòng này để dựng đồ thị phụ thuộc; thiếu thì nó SUY từ Consumes/Produces và gắn nhãn gợi-ý -->
**Verify:** `pytest tests/x.py -q` <!-- lệnh rc 0 = task xong thật; thiếu → node chỉ đạt done_unverified -->
- [ ] **Step 1: viết test fail**
```python
def test_hanh_vi_cu_the():
assert ham(dau_vao) == ket_qua_mong_doi
```
- [ ] **Step 2: chạy cho THẤY nó fail**
Chạy: `pytest tests/đường/dẫn/test_x.py::test_hanh_vi_cu_the -v`
Mong đợi: FAIL — `NameError: name 'ham' is not defined`
- [ ] **Step 3: code tối thiểu cho pass**
```python
def ham(dau_vao):
return ket_qua_mong_doi
```
- [ ] **Step 4: chạy lại — PASS**
Chạy: `pytest tests/đường/dẫn/test_x.py::test_hanh_vi_cu_the -v`
Mong đợi: PASS
- [ ] **Step 5: commit**
```bash
git add tests/đường/dẫn/test_x.py src/đường/dẫn/x.py
git commit -m "feat: <việc cụ thể>"
```
Why **Thoả:** is mandatory: SPEC assigns stable ids FR-001, FR-002… for each requirement. Each task declares which ids it carries → the sentence "covers the whole SPEC" in Self-review stops being done by eye, it becomes greppable data, and R18 blocks if there is any FR-xxx that no task receives (it prints exactly the orphaned id). A requirement drifting away between two tasks is the most silent error of every plan — it only shows up when the agent delivers short, or worse, when the user uses it.
Why Interfaces is mandatory when there are ≥2 agents: the executing agent only sees its own task. Without declaring the signature, two parallel agents name functions differently and the join breaks — this is an error certain to happen, not a risk.
Reference — Placeholder ban — this is a BROKEN plan, not an unfinished plan
R7 blocks at write-time and commit. Never write:
TBD,TODO, "fill in later", "details later"- "appropriate error handling" / "add validation" / "handle edge cases" — state what to do, not "make it appropriate"
- "write tests for the above" without actual test code
- "same as Task N" — copy the code out. An agent may read tasks out of order, and each agent is only fed its own task.
- A step describes what to do without just how to do it (a step that changes code is required to have code)
- Mentions a function/type/method that is not defined in any task
Reference — Self-review
Once written, look it over with fresh eyes — fix in place, no second round needed:
- SPEC coverage — scan each requirement in the SPEC, point out which task implements it. Missing → add a task.
- Placeholder scan — look for exactly the patterns in the section above.
- Type/name consistency — do the function names, signatures, field names used in a later task match what is defined in an earlier task?
clearLayers()in Task 3 whileclearFullLayers()in Task 7 is a bug.
Reference — Reverse gate — /plan is allowed to REJECT the SPEC
Writing the PLAN is exactly how you discover a wrong SPEC: by the time you have to state real file paths and real function signatures, an impossible design shows itself. If the approval gate already let it through and it only shows up now, that means the user approved something that cannot be built — the gate has failed.
STOP, do not keep writing a half-finished PLAN, when you hit any of the following:
- A requirement in the SPEC cannot be traced to any task that can do it.
- Two items in the SPEC contradict each other, or contradict
## Global constraints. - The option chosen in
## Approachesturns out to be impossible (file does not exist, API does not exist, a constraint blocks it). - You have to invent a function/type/file that the SPEC never mentions, just to make the plan "work on paper".
Handling: gather ALL the breakages into ONE report (do not interrupt the user one at a time), each with the exact sentence in the SPEC that causes it, then go back to /propose fix the SPEC and re-approve it. A PLAN written on top of a broken SPEC is worse than no PLAN: it injects the wrong thing into an agent with a confident face, and a cheap agent will execute it verbatim.