WHAT
Purpose and context
- Purpose: Plan feature/change before writing code. Surfaces impact on existing functionality, gets alignment before implementation.
- Trigger (when to use):
- New feature, endpoint, component, or behaviour requested
- Change touches shared/core code
- Scope unclear or multi-interpretable
- Non-goals: do not write code (STOP at the approval gate); do not generate a code-level execution plan (that is
/plan, runs AFTER the SPEC is approved); do not interview the user unless asked (/br interview).
Mental model
request → force-query wiki (Context) → unknowns → impact + side-effect → Approaches (2–3, chọn có lý do) → SPEC draft .md (FR/SC id, Assumptions (default)/[CẦN LÀM RÕ]) + companion .html (sơ đồ archify mỗi task + prose) → self-review → STOP cổng duyệt → /plan.
What this skill produces — and what it deliberately does NOT
/propose produces SPEC (the design document): the thing HUMANS read to click approve at the gate. It is NOT an execution plan.
The code-level plan (exact paths, function signatures, tests, step-by-step code) is the job of the skill /plan, which runs AFTER the SPEC is approved.
Reason for the split (compare obra/superpowers, spec:plan ratio ≈ 1:8 — 82–511 lines versus 673–2621 lines): if you cram code-level into the SPEC, the draft balloons to the point where the approver cannot read what they are approving, and the approval gate becomes useless. Two documents, two readers.
Input and output contract
| Field | Required? | Meaning | |
|---|---|---|---|
| In | feature/change request | yes | restate in one sentence at step 1 |
| In | wiki (concepts/, entities/, sources/adr/, decisions.md) |
yes | force-query before drafting |
| Out | llmwiki/wiki/sources/draft/DDMMYY-feature-name-module.md |
yes | frontmatter type: draft, with all R7 sections (Context, Global constraints, Non-goals, Approaches, Plan, FR, SC, Assumptions, Agent Task Assignment, Sequence diagram, Self-review) |
| Out | llmwiki/html/DDMMYY-feature-name-seq.html + spec/artifact archify per task |
yes | engine diagram + full prose per task, bidirectional links |
| Out | line llmwiki/wiki/index.md + llmwiki/wiki/log.md |
yes | |
| Out | task id T-YYMMDD-NN |
no (fail-open) | frontmatter task: |
| Out | draft output report | yes (unless 0 artifacts) | Delivery section |
Rules and capabilities
- RULE-01 (MUST): OKF v0.1 (R9): the draft starts with a YAML frontmatter block (
---) withtype: draft(+ optionaltitle/status/tags/timestamp/task); copysources/draft/_template.md. Keep**Status:** proposedin the body so R7 can gate it. - RULE-02 (MUST): Never begin implementation during this skill.
- RULE-03 (MUST): The proposal is a PAIR:
.md+.html(one diagram per task). Validator R7 blocks incomplete proposals at write and commit — fix before asking for approval. - RULE-04 (MUST): If impact list empty, state explicitly "No existing code affected."
- RULE-05 (MUST): If multiple approaches exist, present with tradeoffs — do not pick silently.
- Capabilities: read wiki + repo (look up facts); write draft + HTML page + diagram spec in wiki; render diagram via drawing engine; log unknowns and task id (fail-open); do not write product code.
Failure boundaries
- SPEC still has
[CẦN LÀM RÕ]unanswered → blocked at approval gate (R7-n). - Draft missing R7 sections (Context/Global constraints/placeholder…) → validator blocks at write and at commit; fix before requesting approval.
- Diagram render still red after 2 spec revision rounds → stop and ask user, do not repeat validate.
code-logger.py --taskerror/missing → print "skip", leave blanktask:, do not block (fail-open).- User has not approved → stop at step 9; do not dispatch without a PLAN.
HOW
Main workflow
| Step | Type | Inputs | Action | Outputs/exit | Failure/next |
|---|---|---|---|---|---|
| W01 | deterministic | wiki | Step 0 force-query wiki → ## Context |
context + cite | — |
| W02 | judgment | request | Step 0b find unknowns; classify fact (look up) / decision ((default) · find-out-later · [CẦN LÀM RÕ]) |
tiered list of unknowns | architecture-breaking tier → [CẦN LÀM RÕ] |
| W03 | judgment | request, repo | Steps 1–5: restate, impact, side-effect, minimal plan, success criteria | SPEC content | impact empty → write "No existing code affected." |
| W04 | effect | W01–W03 | Step 6: write draft .md with all R7 sections + task id + index/log |
draft on disk | R7 blocks → fix |
| W05 | effect | Plan | Step 7: companion HTML — archify spec per task, render one shell command, embed iframe + prose, shell docs-site-macos |
.html + artifact |
red after 2 rounds → stop and ask |
| W06 | judgment | draft | Step 8: self-review 3-grid, fix in place | ## Self-review |
— |
| W07 | effect | draft + html | Step 9: STOP, show draft + HTML URL, wait for approval | approved / redirected | not approved → cancelled/redirect |
| W08 | effect | SPEC approved | Step 10: handover /plan |
PLAN | — |
Step-by-step details (source of truth for W01–W08):
- Force-query wiki BEFORE drafting — query/read wiki (
concepts/,entities/,sources/adr/,decisions.md) to find related concepts/ADRs/decisions; summarize into## Contextof the draft + cite[[wikilink]]/path. Do NOT propose "blindly" (R7-f blocks drafts missing## Contextwith content). 0b. Find unknowns BEFORE drafting (map ≠ territory, GH#40 — seefdk/wiki/concepts/map-not-territory.md): the stronger the model, the more it fills ambiguity with confident answers. Before writing a line of SPEC, briefly list "to do this right, what do I NOT yet know?" — probe the 5 places maps tend to lie: (a) the requester's implicit intent, (b) tribal conventions absent from the wiki orCLAUDE.md, (c) the real shape of the data (schema, sample, edges), (d) the runtime environment (version, env, permissions), (e) things that have changed since the wiki was written. Each unknown goes into the correct layer of## Assumptionsbelow: fact → look up; low-risk decision →(default); needs rechecking →(default, find-out-later → unknown-ledger); architecture-breaking if wrong →[CẦN LÀM RÕ]. Having no unknowns is a sign you haven't looked, not a sign you understand. - Restate request in one sentence to confirm understanding.
- List every existing file, function, or module affected or must change.
- List every existing feature or behaviour that could break as side effect.
- Propose minimal implementation plan as numbered steps.
- State what success looks like (verifiable criteria).
- Create draft file at
llmwiki/wiki/sources/draft/DDMMYY-feature-name-module.md(e.g.260425-new-approval-button-fe.md) containing proposal output from steps 1–5. Draft MUST include (enforced by validator R7 — blocked at write-time and commit if missing):## Context— summary of related wiki queried in step 0 (concept/ADR/decision), cite[[wikilink]]/path (force-query grounding — R7-f blocks if missing/empty)## Global constraints— constraints covering every task, copy verbatim real values from wiki/ADR/policy (version floor, dependency limits, naming rules, mandatory gates before push…). Every task implicitly carries this section; executing agents only see their own task, so shared constraints must live in one place everyone can be injected with. R7-h blocks if missing/empty.## Non-goals— what is intentionally NOT done. Without this section, scope drifts during execution.## Approaches— 2–3 options substantively different + tradeoff for each + chosen option and why. Silent choices forbidden.## Plan— tasks as- [ ]checklist items## Requirements (FR)— stable ids for each functional requirement:**FR-001**: Hệ thống PHẢI …. This is the anchor for PLAN to trace back (R18 prevents PLAN from missing any id).## Success criteria (SC)— stable id for each criterion:**SC-001**: …. See the section "Criteria must measure the PERSON, not the MACHINE" below.## Assumptions— every field the user didn't say but the model filled in itself, each line tagged(default). See the section "Self-fill or ask" below.
Fact or decision — look it up first, ask later, self-fill the middle part
There is a question standing before all of "self-fill or ask": is this a FACT or a DECISION?
- Fact — has a correct answer, findable by exploring the environment: read files, run commands, grep, look at
git remote -v, readpackage.json. → LOOK IT UP, absolutely do not ask. Asking the user something a tool can answer wastes their turns, and makes them think you don't know how to read the environment. - Decision — has no correct answer, it's a choice. This is where it splits further into
(default)/[CẦN LÀM RÕ]below. Decisions belong to the user — but most low-risk decisions can be self-filled with a sensible default and then disclosed, only escalating to ask when risk is high.
Boundary: if you can find it out with one command, it's a fact — go find it. If two reasonable people could choose differently, it's a decision — handle it per the lower tier.
DESIGN choices the user didn't state = low-risk decision → fill from the base, don't ask. When the brief touches UI but the user didn't specify macrostructure, theme, type-pairing, or anchor color, DO NOT interview — fill a brief-appropriate default from the catalog [[design-foundation]] (source: skills/hallmark/references/macrostructures/ + references/genres/), tag (default), and write the chosen macrostructure + theme name into ## Assumptions. A glance from the user shows what the machine chose, change one line if they don't like it. Choosing the wrong macrostructure is quick to fix — it's not auth or money. This is the reference source for filling design unknowns, plug it into the right (default) system. (Brief carries creative-intent but no theme fits → hallmark automatically goes to the Custom branch; still 6 disciplines + slop-test.)
Self-fill or ask — default is SELF-FILL
The user has already described everything they know. Don't interview them about things they don't know — self-fill a sensible default, then DISCLOSE that you filled it:
-
(default)— the model chooses itself. Just keep going, don't ask. The reviewer glances at## Assumptionsand can immediately tell what's their words versus the machine's words, and fix whichever line they disagree with. -
(default, find-out-later → [[unknown-<slug>]] U-NN)— the MIDDLE tier: the model fills a default IMMEDIATELY so it doesn't block work, but record it as debt in the ledger instead of staying silent. Use when the default carries risk and we know it will need re-checking — even when lowering a[CẦN LÀM RÕ]down (user says "just fill it in, deal with it later"). Record the debt withpython3 harness/scripts/unknown-ledger.py --add …→ it printsU-NNto paste into the Assumptions line. This debt does NOT block the gate — it only shows up in/lintso it doesn't sink. See[[150726-unknown-ledger]]. -
[CẦN LÀM RÕ: <câu hỏi cụ thể>]— model REFUSES to guess, put it straight into that spot in the SPEC. Only use for the group where one wrong default means broken architecture or broken users, matching the carve-out ofCLAUDE.md:- authentication / authorization mechanism (who can do what)
- data storage: what is stored, where, for how long, who can read it
- money / payments / limits
- anything with legal or compliance consequences
- any trust boundary (external input, system boundary)
R7-n blocks the approval gate if the SPEC still has
[CẦN LÀM RÕ]unanswered. User answers → replace with the real value; user says "just guess" → lower it to(default)deliberately, with a recorded trace.
Get the colored button wrong and it's a thirty-second fix. Get the auth mechanism wrong and that (default) silently becomes an architecture decision, and the reviewer skims past it because it looks done. (default) alone is a speculation-legitimizing machine; it's only safe when paired with [CẦN LÀM RÕ].
Interview only runs when the user REQUESTS it (/br interview) — and when it runs, the cap is 5 questions, chosen by (Impact × Uncertainty).
The multiple-choice Interview ALWAYS has one final option: "fill in the default now, find out later (record unknown debt)". User isn't sure, doesn't want to stop to think, but also doesn't want the model to guess silently → picks this option: the model fills in a reasonable default right away (work continues) and records an unknown in the ledger to pay off later. This is the escape route for any question the user isn't ready to answer while still keeping the trace — no forcing an answer, no silent guessing.
Pay off the unknown debt — when real information arrives
A recorded unknown ((default, find-out-later)) is not meant to be forgotten. When real information arrives:
- Fill the correct value into the source SPEC/PLAN — replace the line
(default, find-out-later → …)with the real value. - Close the ledger entry:
python3 harness/scripts/unknown-ledger.py --resolve --file unknown-<nguồn>.md --id U-NN --value "<giá trị đúng>" --fixed "<sửa ở đâu>" --date <YYYY-MM-DD>. The ledger keeps both the filled-value and the correct-value (audit — six months later you know what the model guessed, whether it was right, where it was fixed). - If the wrong default HAS already produced wrong code (already executed on the guessed value) → open
/orca-issue(repro-first) or/raise-issue, link backU-NNin body.
Open debt appears in /lint (report, non-blocking) — unknown-ledger.py --list. Paying debt is an action intentional of the person/agent; the skill only provides the flow, it does not "figure it out" for you.
When asking — ask the right way:
- One question per turn. Wait for the answer before asking the next question. Asking in clusters makes people confused and answer superficially.
- Always include a recommended option, placed first, so the user can nod once ("agree" / "the first one") and be done instead of having to compose an answer themselves.
Criteria must measure the PERSON, not the MACHINE
SC-xxx must be measurable and not tied to technology — say what the user gets, not what the machine does:
- ✅ "User completes account creation in under 2 minutes" · "withstands 1000 concurrent users without degradation" · "reduces support tickets about X by 50%"
- ❌ "runs
pytestreturns exit 0" · "validator returns 2" — that measures the machine. A system can be all green and still be useless.
A check at the machine layer still records, but records it as evidence of SC-xxx, not a replacement for it.
## Agent Task Assignment— table| Task | Agent (CLI) | Lý do chọn | Status |, one row per task, no empty Agent cell, Status=pending. Pick agents by cost table; if all on one agent, say why.**Sequence diagram:**link to companion.html(must exist on disk)- Durable Task ID (Pillar 3 — best-effort, fail-open):
python3 harness/scripts/code-logger.py --task new title="<feature>"→ print inT-YYMMDD-NN; write to frontmattertask: T-YYMMDD-NN. Command fail-open — old install missing--taskor store error → print "skipped", just leavetask:blank, do NOT block propose. This is the id that gate/dispatch/verify use to advance the lifecycle + anchor to the immutable audit trail (events.jsonl chained). Add row tollmwiki/wiki/index.mdand append tollmwiki/wiki/log.md.
- Create the companion HTML page at
llmwiki/html/DDMMYY-feature-name-seq.html. For EACH task in Plan the page MUST contain both parts — a real diagram AND rich prose. A page that is only prose, or only a list of steps styled to look like a diagram, is INCOMPLETE (R7-c, from 100926: counts only the archify artifacts the page embeds, one per task):- (A) Sequence diagram per task — drawn by the engine, never hand-rolled. Write one archify
sequencespec per task (llmwiki/html/DDMMYY-feature-name-tN.sequence.json; participants = lifelines; message variant: indigo = existing, emerald = added/changed, amber = blocked/fail) and render through/diagram→ archify. Batch every task in ONE shell call so N diagrams cost one model turn:for s in llmwiki/html/DDMMYY-feature-name-t*.sequence.json; do o="${s%.sequence.json}.html"; node ~/.agents/skills/archify/bin/archify.mjs deliver sequence "$s" "$o" --json && node ~/.agents/skills/archify/bin/archify.mjs visual-check "$o" --json; done. At most 2 spec-fix rounds: still red after the second round → stop and ask the user, never loop on validate (measured 100926: 57 validate turns at ~430k context ≈ 44M cache-read tokens for one page). Leavemeta.visual_presetunset (install defaultmacosmatches the page shell) — never copysignal-flowfromarchify/examples/*.json; set another preset only when the user asks, and then declare<meta name="overstack-preset" content="<preset>">on the page (R20 blocks an off-theme embed otherwise). Embed each artifact with thedocs-site-macos§Embedding an external artifact recipe —<iframe class="archify-embed" src="DDMMYY-feature-name-tN.html">+ its auto-height script + a "Open diagram separately ↗" link (a fixed-height frame always clips: the viewer measures 905–1565px tall) — titled by its task + a badge naming the assigned agent. The old list template (diagram-box+.lifelinechips +.msgrows) is NOT a sequence diagram and no longer counts. - (B) Rich detail prose — alongside each diagram, a readable explanation in full, complete sentences (not terse diagram labels): what changes and why, the data/flow at runtime, the safe-vs-blocked branch, and the concrete risk. This is human-read documentation → honour the prose rule (CLAUDE.md 2026-06-27): never caveman or over-compressed here. The diagram is the skeleton; this prose is the body — both are required.
- Style: load
/docs-site-macos(Skill tool) and follow it for the page shell (header, prose cards, badges) — the liquid-glass look (light gradient field + refraction layers + glass tier-2 cards + Apple-tint badges) AND its §Navigation sidebar (one link per section/task,.nav-toggle/.nav-close, scroll-spy, theme switch). Imitating the look without loading the skill shipped an 11-section page with no menu (GH#155); R20 now blocks a*/html/*.htmlpage with more than 3 sections and no<nav>. Never hand-roll a dark/flat theme (lesson 250626 "why does it look so bad"). Do not clone the diagram part of an older*-seq.html— those are step lists, not diagrams (lesson 100926). - Authoring split — Claude thinks, a cheaper CLI renders: the
.mdis the SUBSTANCE (Claude's job) and must be render-complete — besides Plan + prose, include a## Render briefsection giving, per task, the diagram's ordered steps (each tagged legacy / add / block) and the full prose paragraph. The.htmlis then a mechanical render of that brief and, in orchestrated runs, MAY be dispatched to a cheaper CLI (OpenCodebig-pickle/agy/kiro, $0) — seeorca-workflow. Standalone/propose: Claude renders directly and## Render briefis optional. Because the render is free when delegated, prefer Fulldocs-site-macosrichness for the.htmlrather than a stripped page — Claude's token cost is the substance only and does not grow with HTML richness. - Link both ways:
.md↔.html
- (A) Sequence diagram per task — drawn by the engine, never hand-rolled. Write one archify
- Self-review — double-check with fresh eyes, fix in place. Write the result to
## Self-reviewof the draft (3-pass review):- Request coverage — each requirement in the request must map to exactly one task. Missing → add a task.
- Scan placeholders — none of the following may remain:
TBD,TODO, "handle errors appropriately", "handle edge cases", "similar to Task N". R7-g blocks. - Name-type consistency — the same thing must be called by the same name throughout the draft (function
clearLayers()in task 3 butclearFullLayers()in task 7 is a bug already born at writing time). If you find an error, fix it directly; no need for a second review round.
- STOP. No code. Show the draft content + the HTML preview URL. Wait for user to approve or redirect.
- After the user APPROVES — hand off to
/plan(Skill tool →plan) to expand this SPEC into anDDMMYY-<tên>-PLAN.mdexecutable. Do not dispatch tasks before a PLAN exists: cheap headless CLI agents inherit no context and cannot ask follow-up questions — they only have exactly what we feed them (lesson 250626: delivery ~1/5 when the brief is thin).
Branches
| ID | Kind | Guard | Behavior | Skip / failure | Rejoin |
|---|---|---|---|---|---|
| B01 | user_optional | user requests an interview (/br interview) |
ask at most 5 questions (Impact × Uncertainty), one question per turn, with a recommendation + a "fill in the default now, find out later" answer | not requested → auto-fill (default) |
W03 |
| B02 | conditional_required | the default carries risk, known to need re-checking later | (default, find-out-later → …) + unknown-ledger.py --add take U-NN |
— | W03 |
| B03 | recovery | real information is available for a recorded unknown | fill the correct value into the SPEC/PLAN + unknown-ledger.py --resolve …; a wrong default has already spawned code → /orca-issue or /raise-issue |
— | outside the main loop |
| B04 | capability_optional | run within the orchestrated flow | render .html from ## Render brief can be handed to a cheap CLI (see orca-workflow) |
standalone → Claude renders directly | W06 |
| B05 | conditional_required | the brief touches UI but the user hasn't specified macrostructure/theme | fill from [[design-foundation]], record the macrostructure name + theme in ## Assumptions |
— | W03 |
Validation and stopping
Validator R7 (branches R7-c/f/g/h/n), R18, R20 deterministically block at write and commit time; the 3-pass self-review is the review part. The bare-diagram spec revision loop is capped at 2. The skill always stops at W07 — there is no path to code.
Examples
- Positive: "add a quick approve button to the approval screen" → query the wiki for the related ADR, note the button color in Assumptions
(default), draft260425-new-approval-button-fe.md+260425-new-approval-button-seq.htmlhas an archify diagram for each task → clean self-review → STOP, show draft + URL, wait for approval. - Boundary/failure: request "let customers view payment history" but it is unclear who may read the data → SPEC notes
[CẦN LÀM RÕ: role nào được xem lịch sử thanh toán của người khác?]→ R7-n blocks the approval gate until the user answers.
Delivery — Output Report
After all main skill tasks complete, write a propose draft to the wiki.
Steps
1. Build the filename:
- Format:
DDMMYY-<ten>.md DDMMYY= today (e.g.,020626for 2 June 2026)<ten>= 2–4 kebab-case words summarising what was done (e.g.,landing-page-coteccons,brand-kit-fintech,ingest-auth-spec)
2. Write llmwiki/wiki/sources/draft/DDMMYY-<ten>.md:
---
type: draft
title: "DDMMYY-<ten>"
status: proposed
tags: [<skill-name>, output-report]
timestamp: YYYY-MM-DD
---
# DDMMYY-<ten>
**Type:** draft
**Status:** proposed
**Tags:** <skill-name>, output-report
**Proposed:** YYYY-MM-DD
## What
<One sentence — what this skill invocation produced or decided>
## Output
<Key artefacts, files created/modified, or decisions made>
## Files
| File | Action |
|------|--------|
| `path/to/file` | created / modified |
## Notes
- Invoked via: `/<skill-name>` skill
## Origin
- **Draft:** `wiki/sources/draft/DDMMYY-<ten>.md`
- **Commit:** _(filled by verify-before-commit)_
- **Date promoted:** _(filled by verify-before-commit)_
3. Update wiki index & log:
llmwiki/wiki/index.md— append one row:| [DDMMYY-<ten>](sources/draft/DDMMYY-<ten>.md) | draft | YYYY-MM-DD |llmwiki/wiki/log.md— append:## YYYY-MM-DD — <skill-name> — <ten>
Skip only when the skill produces zero artefacts and zero decisions (e.g., a pure display mode like
/caveman-stats).