unikit-plan · v7.6 · 2026-10-28 · sha256 6e8a9f0c9294a71c
unikit-plan v7.6A
Immutable. This exact content never changes and is served at /api/v1/blob/6e8a9f0c9294a71c.
---
name: unikit-plan
description: >-
Create an implementation plan for a feature — a dependency-ordered, actionable task
list for the project. Has four modes: fast (a quick single-pass plan), full (a richer
plan that can also create a git branch), ultra (a multi-file bundle — a manifest plus
one deeply specified file per phase — for execution by a smaller model), and add
(extend an existing plan with more tasks). Pick the mode from the user's wording: "full
plan" runs full; "quick plan" or "fast plan" runs fast; "ultra plan", "ultraplan" or
"ultra-plan" runs ultra; a plain "create a plan" with no qualifier defaults to fast;
"add to the plan" or "extend the plan" runs add. Use whenever the user wants to plan a
feature or task, e.g. "create a plan", "create a full plan", "create a quick plan",
"run an ultra plan for the inventory system", "plan this feature", "add this to the
plan", "extend the plan", "add a phase to the plan". Ultra runs only when the user
names it — never because the feature looks large.
argument-hint: "[fast | full | ultra | add | --list] [--base <branch>] <feature description in free form>"
allowed-tools:
- Read
- Write
- Edit
- Glob
- Grep
- Bash(ls *)
- Bash(find *)
- Bash(wc *)
- Bash(git *)
- Bash(shasum *)
- Bash(sha256sum *)
- Bash(date *)
- Bash(node *)
- Agent
- Skill
- AskUserQuestion
disable-model-invocation: false
user-invocable: true
metadata:
author: unikit
version: "7.6"
category: planning
---
# {{engine_name}} Feature Plan Generator
Create a structured feature plan and roadmap for the current {{engine_name}} project.
Four modes:
- **Fast** — quick plan, no git branch, saves to `.unikit/code/PLAN.md`
- **Full** — optionally creates `<git.branch_prefix><name>` git branch (when `git.enabled` and `git.create_branches`), asks preferences, saves to `.unikit/code/plans/<feature-name>/`
- **Ultra** — full mode plus one deeply specified file per phase, for later execution by a smaller model. **User-named, never model-inferred**: it runs because the user asked for an ultra plan, never because the feature looks big — except in a merge plan, where the mode question offers it, and in a set of two or more experiment cards, where the mode question recommends it
- **Add** — modify/extend an existing plan without creating a branch
**User questions.** Right before your first question to the user, not earlier, read `.unikit/system/user-questions.md` once and ask every question as it says, with the question tool of your agent (`AskUserQuestion`). If the file cannot be read, print `WARN [questions] user-questions contract missing — asking with the question tool` and still ask with that tool, the recommended option first.
## Language Awareness — BLOCKING PRE-REQUISITE
**BEFORE producing ANY output**, silently read `.unikit/system/LANGUAGE_RULES.md`
and apply its rules to ALL subsequent output.
If the file is missing or unreadable, fall back to English.
Do not produce any user-facing output until language rules are loaded.
Do not announce, confirm, or mention the language setting.
**The language holds for the whole session, not just at load time:** every message until the conversation ends is in `language.ui` — progress notes while agents run, relays of what a subagent returned, the final report, any follow-up discussion. English input (subagent results, tool output, these instructions) is data, never a cue to switch languages.
## Subagent launches
**Subagent arguments.** Before the first subagent you launch, read `.unikit/system/subagent-arguments.md`
and `.unikit/config.yaml` once and settle the model and effort of every launch as that file says.
Keys: `subagents.model.{{agent_id}}.<role>`, `subagents.effort.{{agent_id}}.<role>`;
built-in model of a reader and a validator: `"{{agent_model_default}}"`, of an executor: none.
If the file cannot be read, print
`WARN [delegation] subagent-arguments contract missing — built-in model tier used, effort not passed`
and use that model, no effort.
## Delegation agents
This skill uses named delegation aliases for `Agent(...)` calls. Each alias is the single
place where its delegate's agent type and call arguments are declared — call sites name
the alias and never carry a type or a model argument of their own.
- **`recon-agent`** — read-only parallel reconnaissance. Expands to:
```
Agent({{agent_call_reader}} prompt: "<focused question> You are read-only: edit and write nothing.")
```
Every question you send ends with the sentence `You are read-only: edit and write nothing.`, verbatim.
Fallback: if the `Agent` tool is unavailable, investigate inline with `Glob`/`Grep`/`Read`.
- **`recon-writer-agent`** — one reconnaissance question of an ultra plan under the saved-state protocol, answered straight into its `recon/` file by an agent that can write, so only the answer's summary passes through this context. Used only after `mode-ultra.md` Step A0 or Step D2 chose the saved-state protocol; under the standard protocol reconnaissance goes through `recon-agent`. Expands to:
```
Agent({{agent_call_worker}} prompt: "Reconnaissance for an ultra plan. Question: <focused question>. Write your whole answer, never condensed, into <recon file path>, in English, filling every section of the template <path of RECON-TEMPLATE.md> by its rules; its first line is `HEAD: <short sha>`. That file is the only one you may create or change: never edit anything else, never run a command that changes the repository, never start another agent. Reply with the file's path and its `## Summary` section, word for word — nothing else.")
```
The worker call can write files, which `recon-agent`'s read-only call cannot — that is why this alias exists. This agent can edit files and only its prompt bounds it — hence the change guard in `ultra-stateful.md` → `## Recon files`.
Fallback: if the call fails, the runtime has no agent tool, or the agent returns without its file, ask that question through `recon-agent` — it only reads and cannot save a file — then, the moment the answer returns and before anything else, create `recon/` if it is missing and write the answer into the file yourself, whole, in the form of `RECON-TEMPLATE.md`; take `pending` off only once the file is on disk (`ultra-stateful.md` → `## Recon files` → *Without a writing agent*).
## Input
`$ARGUMENTS` — optional keyword `full`, `fast`, `ultra`, or `add`, optional `--base <branch>` flag, followed by free-form description in any language. The mode may also be named inside that free-form text rather than as a leading token.
**Parsing rules:**
1. Extract `--base <branch>` if present anywhere in arguments → store as `base_branch`, remove from text
2. If `--list` is present → list mode, show all plans and STOP
3. If the first word (after flag removal) is `full` → full mode, remaining text is the feature description
4. If the first word is `fast` → fast mode, remaining text is the feature description
5. If the first word is `ultra`, **or** the text asks for an ultra plan in any phrasing or language — "ultra plan", "ultraplan", "ultra-plan", "ультраплан", "make an ultra plan for the inventory" → ultra mode; strip the ultra wording and the verb that carried it, the remainder is the feature description
5a. If the text left after an optional leading `ultra` is exactly the name of a folder under `.unikit/code/plans/` that holds `.planning/STATE.md` and no manifest `.unikit/code/plans/<name>/PLAN.md`, this is a **continuation**: load `{{skills_dir}}/{{self_name}}/references/mode-ultra.md` and `{{skills_dir}}/{{self_name}}/references/ultra-stateful.md`, and follow `## Resume` there; Steps 0.1-4 do not run again.
6. If the first word is `add` → add mode, remaining text is what to add/change in the existing plan
7. Otherwise → ask interactively, entire text is the description
Ultra is **user-named, never model-inferred**. Rule 5 recognises the request wherever it sits in the sentence, but it must be a request: ultra is never offered in Step 0.2 (except in a merge plan, where the mode question offers it, and in a set of two or more experiment cards, where the mode question recommends it) and never chosen because the feature looks large, spans many files, or seems hard — size is not a request. Wording that only asks for care — "a deep plan", "plan this thoroughly", "a detailed plan" — is **not** ultra; fall through to rule 7 and ask.
**A merge request** — the user asks to merge a named branch into the current one (not a rebase, a cherry-pick or a merge by pull request number): load `{{skills_dir}}/{{self_name}}/references/merge-plan.md` and follow it. It owns the trigger, the analysis, the mode question — `fast`, `full` or `ultra` (`add` does not apply to a merge) — and the shape of the plan; a branch that is not named is asked for there. An ordinary plan does not read that file.
**A Balance Checkpoints input** — the argument names a `CHECKPOINTS.md` file, a folder or slug under `.unikit/gamedesign/balance/`, or the request is calibration or measurement work on a balance document (`/unikit-gd-balance` prints this command): load `{{skills_dir}}/{{self_name}}/references/balance-plan.md` and follow it. It owns finding the current point, the snapshot, the horizon and the shape of the plan. An ordinary plan does not read that file. Missing or unreadable → `WARN [balance] balance-plan reference missing — planning as an ordinary plan; run unikit-ai update`, and no `Hand-back:` task is created in such a plan.
**An experiment input** — the argument names an experiment (`EXP-<nnn>`), a path to an `experiment.md` card, an experiment folder under `.unikit/gamedesign/experiments/`, or a set of them (`/unikit-gd-experiment` prints this command): load `{{skills_dir}}/{{self_name}}/references/experiment-plan.md` and follow it. It owns reading and checking the cards, the snapshot, the mode question, the settings, the branch and the base, the folder name and the shape of the plan. The word "experiment" in free text is not a selector. An ordinary plan does not read that file. Missing or unreadable → `WARN [experiment] experiment-plan reference missing — planning as an ordinary plan; run unikit-ai update`. A request that names an experiment by its description — "the latest experiment", "the experiment about onboarding" — while `.unikit/gamedesign/experiments/` holds at least one `experiment.md` is resolved first through `{{skills_dir}}/unikit-gd-experiment/references/lookup.md`, read only for such a request: the card the user confirms becomes the selector and this branch goes on as for `EXP-<nnn>`. That file missing or unreadable → `WARN [experiment] lookup reference missing — asking for a selector`, and the user is asked for an `EXP-<nnn>`.
**An experiment request** — the text asks the planner for an experiment plan, in any phrasing or language: "experiment plan", "experimental plan", "plan an experiment", "plan the experimental branch", "экспериментальный план". It may stand beside any mode word. The wording and the verb that carried it are stripped; what is left is the **source** — a description, a path, a game-design research named by path or in words ("the latest game-design research"), cards, or nothing (then the conversation itself). Load `{{skills_dir}}/{{self_name}}/references/experiment-plan.md` and `{{skills_dir}}/{{self_name}}/references/experiment-source.md`: the second owns every source that is not a card. Like ultra, the request is never inferred — a feature that is about experiments (an A/B switch, an experiment framework) and the bare word in a description are an ordinary plan. Cards named in the same request keep the route of **An experiment input**. `add` mode ignores the wording. Missing or unreadable `experiment-source.md` → `WARN [experiment] experiment-source reference missing — planning an ordinary plan from the description; run unikit-ai update`.
`--base <branch>` — the branch to create the feature branch from (full mode only). `--base` flag overrides `git.base_branch` from config. Priority: `--base` flag > `git.base_branch` from `.unikit/config.yaml` > fallback `main`.
## Workflow
### Step 0: Parse Mode & Select Mode
Initialize flags: `research_pre_linked = false`, `research_linked = false`, `design_linked = false`.
**If mode is `--list`** → load `{{skills_dir}}/{{self_name}}/references/mode-list.md` and follow it (it STOPs; Steps 0.1–7 do not run).
**If mode is `add`** → run **Step 0.5 (Bootstrap Context)**, then load `{{skills_dir}}/{{self_name}}/references/mode-add.md` and follow it (it STOPs; never creates a branch).
### Step 0.1: Resolve Git State
Do **not** auto-run `git init`. Read the git keys from `.unikit/config.yaml`:
- `git.enabled: false` → no branch commands; a full-mode plan goes to `.unikit/code/plans/<slug>/`
- `git.base_branch` → the target branch for diffs and merge guidance (default: the detected branch or `main`)
- `git.create_branches: false` → full mode still writes the rich plan, on the current branch
- `git.pull_requests.checkpoints` — read once by the mode file's `#### PR checkpoints`; `git.pull_requests.max_level` is never read by the planner.
`git.enabled: true` outside a git work tree → warn that git-aware actions are unavailable until the repository is initialized, and continue as with `git.enabled: false`.
### Step 0.2: Resolve Feature Description
If the user provided a feature description → use it and skip this step.
If the description is empty (user only typed a mode keyword like `full` or `fast`, or no arguments at all):
1. **Check session context** — look in the current conversation history for results of `/unikit-explore`. If found, use the exploration topic and findings as the feature description and context.
2. **Check recent researches** — if no session context, read `.unikit/code/researches/INDEX.md` (if it exists). The index is sorted newest-first. Take the first entry whose `Lifecycle` is `active` — a record carrying no `Lifecycle` line counts as `active` — and ask:
```
The question tool of your agent (`AskUserQuestion`): Found recent research: "<Title>" (<Created>)
Use as basis for planning?
Options:
1. Yes — use this research
2. No — I'll describe the feature myself
```
Based on choice:
- Yes → use research title/summary as description, mark `research_pre_linked = true` (skip research matching in Step 2)
- No → proceed to ask user for description (step 3 below)
`<Created>` is the displayed field — never substitute `Updated` for it. A record with no `Created` is shown as `"<Title>" (date unknown)`, never with empty brackets.
3. **No context available** — if neither session context nor researches exist, ask the user for a description:
```
The question tool of your agent (`AskUserQuestion`): Describe the feature you want to plan.
```
**If no mode keyword** was found by the Step 0 parsing rules, ask one question, adding the description question when the description is still missing:
```
The question tool of your agent (`AskUserQuestion`):
1. Describe the feature you want to plan. ← only when the description is still missing
2. Which planning mode?
a. Full (recommended) — creates git branch, codebase reconnaissance, full plan
b. Fast — quick plan without a branch
```
Ultra is deliberately absent from this question — see the parsing rules in Step 0 — except in a merge plan, where the mode question offers it, and in a set of two or more experiment cards, where the mode question recommends it.
For a merge request the mode question is the one in `merge-plan.md`; `ultra` appears there as the third option.
For an experiment input the mode question is the one in `experiment-plan.md`: `fast`, `full` and `ultra` are all shown, and `ultra` is recommended for a set of two or more cards — the user's own word still decides.
For an experiment request the source is resolved by `{{skills_dir}}/{{self_name}}/references/experiment-source.md` → `## The source`, which replaces the description part of this step — the session context, the researches index and the description question; it never falls back to the code researches index above. The mode question stays, as for any plan.
### Step 0.5: Bootstrap Context (MANDATORY — all modes except List)
Silently load the project knowledge base before any exploration — do not narrate the loading.
#### Required reads (always, every time, in parallel)
1. **`.unikit/DESCRIPTION.md`** — project description, tech stack, constraints
2. **`.unikit/ARCHITECTURE.md`** — architecture decisions, folder structure, module rules, dependency directions. **Architecture topics:** only the root here; the topic files its `## Topics` table lists load per phase in Step 5 (**Rule refresh per phase**) and, for the recon tasks below, by their focus.
3. **Read `.unikit/memory/code/RULES_INDEX.md`**. Load rules:
- **RULES.md**: ALWAYS read `.unikit/RULES.md` first (highest priority). **Rule topics:** only the root here — its topic files load per phase in Step 5 (**Rule refresh per phase**), or earlier when the Step 4 exploration needs one.
- **Core**: read the Core table. For EACH row where Required By = `all` or contains `{{self_name}}` — read that file from `.unikit/memory/code/core/` using the Read tool. Do NOT skip any matching row. Always re-read at skill start, never rely on prior conversation cache
- **Stack**: load dynamically when the current task or context matches "Load When" column, or when a need arises during work
4. **`.unikit/skill-context/{{self_name}}/SKILL.md`** — project-specific skill overrides (if exists)
5. **Read `{{skills_dir}}/{{self_name}}/references/ENGINE_RULES.md`** — engine planning vocabulary: kind → concept (§1), language & layout (§2), when to write `Editor:` (§3), engine planning pitfalls (§4), out of scope (§5), direct-edit feasibility (§6). Set `engine_rules_loaded = true`.
**If the file is absent** — a **normal path**, not an error (an engine whose planning vocabulary has not shipped yet): set `engine_rules_loaded = false`. Every step that writes an `Editor:` field, the `Editor tasks` setting or a confirmation line reads that flag (Step 5, Step 6, `mode-full.md`, `mode-fast.md`, `mode-add.md`). `add` mode runs this step too.
6. **Read `.unikit/system/engine-bridges/BRIDGES.md`, and for each bridge whose block names a rules tree its `.unikit/system/engine-bridges/<bridge>/INDEX.md` — the base section only**: the stamp under the title plus every section **except** the `## Check` table — the exceptions that shape planning: where a commit boundary falls, what may never run in parallel, how the work splits into phases. Set `bridge_rules_loaded = true`.
**Do not read the `## Check` table** — it is the executors', grepped per task by area.
**If the file is absent** — a normal path: set `bridge_rules_loaded = false`, print exactly one line, and continue with the same rights:
```
Bridge rules: <bridge> has no INDEX.md — no known exceptions for it, rights unchanged
```
Absence never switches a task to `⏸️ MANUAL`, never suppresses an `Editor:` field, and never disables an engine bridge (`.unikit/system/dev-principles.md` → **A9 · no rules ≠ no rights**).
7. **`.unikit/config.yaml` → `testing.plan.checkpoints`** — the test-run placement policy, resolved per mode by `mode-full.md` / `mode-fast.md` / `mode-ultra.md`; here it is only read. File or key absent → the mode file's default, silently.
#### Patches (learning from past fixes)
If `.unikit/code/patches/` exists:
- Use `Glob` to find all `*.md` files
- Read each patch to learn from past fixes
- Account for known pitfalls when designing the plan — tasks should avoid patterns that caused bugs
#### Design context (game-design module — optional)
`.unikit/gamedesign/GD-IDS.yaml` exists → it MUST be `version: 2`. On `version: 1` do NOT read it: emit `ERROR [design] GD-IDS.yaml is version 1 (pre-v2 layout); design grounding unavailable until the workspace is upgraded via /unikit-gd-spec`, set `design_linked = false`, and plan code-side only. On `version: 2` set `design_linked = true` — Step 4.5 reads the design once the feature scope is clear; nothing is read here. Absent → `design_linked = false`, and every design step is skipped.
Planning only *reads* design; it never writes to `.unikit/gamedesign/` — design changes go through the `/unikit-gd-*` skills.
Remember loaded rule file paths — pass them to Explore tasks in Step 4.
<!-- unikit:extension-point:bootstrap -->
### Step 1: Determine Feature Name and Folder
1. Read the description and understand the feature intent.
2. **Invent a short English name** for the feature — maximum 3-4 words, lowercase, hyphenated.
Examples: `mini-games-editor`, `customer-dialogue`, `item-appraisal-system`, `wallet-ui`.
**Fast mode** → skip steps 3-5 below. The plan goes to `.unikit/code/PLAN.md` (flat file, no folder).
**Full and ultra modes** → continue:
3. **Get today's date** (`YYYY-MM-DD`) — the value of the manifest's `Created:` and `Updated:` fields (Plan Manifest Template in `{{skills_dir}}/{{self_name}}/references/TASK-FORMAT.md`).
4. The folder name **is** the feature name from step 2 — `<feature-name>`, no date and no separator prefix (e.g. `item-appraisal-system`).
5. **Collision check — a slug that already exists never resolves itself silently.** Scan `.unikit/code/plans/` and `.unikit/code/archive/plans/` for a folder matching the new name in **any** of the three formats that coexist on disk: exact `<name>`, a folder ending in `_<name>` (the `YYYY-MM-DD_` era), and a folder ending in `-<name>` whose name starts with three digits (the older `DDD-` era).
- No match → create `plans/<feature-name>/` and continue.
- A match in `.unikit/code/plans/` that has no manifest `.unikit/code/plans/<name>/PLAN.md` → a plan that was not finished:
```
The question tool of your agent (`AskUserQuestion`): A plan named "<name>" was not finished (.unikit/code/plans/<name>/ has no manifest).
Options:
1. Continue it
2. Start over
3. Choose another name
```
- "Continue it" is offered only when the folder holds `.planning/STATE.md`; without it (an interrupted standard protocol) the option is absent.
- "Continue it" → load `{{skills_dir}}/{{self_name}}/references/mode-ultra.md` and `{{skills_dir}}/{{self_name}}/references/ultra-stateful.md`, and follow `## Resume` there.
- "Start over" → `node {{skills_dir}}/{{self_name}}/scripts/plan-bundle.mjs discard .unikit/code/plans/<name>`, print `INFO [plan] <name>: unfinished plan removed — starting over`, create the folder again and continue. A non-zero exit → stop with its `ERROR` line (code 3: the folder holds a finished plan). `node` cannot run → print the folder's path, ask the user to remove it, and stop.
- "Choose another name" → the folder stays; repeat this check on the new slug.
- A match in `.unikit/code/plans/` → ask, and do not decide it yourself:
```
The question tool of your agent (`AskUserQuestion`): A plan named "<name>" already exists (<matched folder>).
Options:
1. Refine the existing plan — hand over to add mode
2. Choose another name — I'll enter a different slug
```
- "Refine the existing plan" → hand control to the `add` body (`{{skills_dir}}/{{self_name}}/references/mode-add.md`) on the matched folder and print `INFO [plan] <name> exists — switching to add mode`.
- "Choose another name" → take the user's slug and repeat this check on it. On success print `INFO [plan] creating <new-name>`.
- A match **only in `.unikit/code/archive/plans/`** → the name belongs to an archived plan. An archived plan is not refined in place, and a second folder of that name would collide when this one is archived:
```
The question tool of your agent (`AskUserQuestion`): A plan named "<name>" is archived (<matched folder>).
Options:
1. Choose another name — I'll enter a different slug
2. Cancel planning
```
- "Choose another name" → as above. "Cancel planning" → **STOP**.
**Appending an automatic suffix (`-2`, `-v2`, a date) is forbidden.**
### Step 1.5: Load the Mode Body
Load only the selected mode's body — never all of them at once:
- **Full mode** → load `{{skills_dir}}/{{self_name}}/references/mode-full.md`, run its
additional steps (git branch, recon, preferences), then continue to the Shared Steps below.
- **Fast mode** → load `{{skills_dir}}/{{self_name}}/references/mode-fast.md`, run its
preferences step, then continue to the Shared Steps below.
- **Ultra mode** → load `{{skills_dir}}/{{self_name}}/references/mode-ultra.md`, run its
additional steps A0-C (writing protocol, git branch, recon, preferences) — Step A0 first and on its own, before any agent — then continue to the Shared Steps
below. Steps D-H of that body run later — they refine Step 5 and Step 6 of the shared
workflow, so do **not** run them here.
---
## Shared Steps (all planning modes)
### Step 2: Check for Related Researches
**An experiment plan** runs no topic matching in this step (points 1-6): a research reaches it only through an `Experiment:` line (`{{skills_dir}}/{{self_name}}/references/experiment-plan.md` → `## The snapshot comes first`), and point 7 reads what that finds.
**The research-link contract — read at the first link, and only then.** The moment this step links its first research — through the `research_pre_linked` shortcut below or through point 7 — read `.unikit/system/research-link.md`. For each research linked here, compute its `Summary SHA256` over the `## Active Summary` just read, by `## Computing the hash`, and keep the digest for Step 5 (`## Writing an entry`).
**If `.unikit/system/research-link.md` is missing or unreadable, do not block:** record no `Summary SHA256` and check none — print `WARN [research-drift]: research-link contract missing — drift detection off for this run; run unikit-ai update` once, and continue.
**If `research_pre_linked = true`** (the user already confirmed a research in Step 0.2) → read it as point 7 below does, mark `research_linked = true`, store its path for `## Based on`, and skip to Step 3.
Before exploring code, check if `/unikit-explore` has produced relevant researches.
1. Read `.unikit/code/researches/INDEX.md`
- If the file doesn't exist — skip this step entirely, proceed to Step 3.
2. Read `workflow.research_relevance_days` from `.unikit/config.yaml` (default: `7`).
3. **Filter** entries by three criteria:
- `Updated` is within `research_relevance_days` from today. The age key is `Updated`, never `Created`.
- `Status` is `completed` (skip `in-progress` and `needs-follow-up`). The field name and its three values are **fixed**.
- `Lifecycle` is not `superseded`. A record carrying no `Lifecycle` line counts as `active`.
**Log the drop** — one line, **always**, including when nothing was dropped:
```
INFO [research] index: <N> entries, <K> shown (<a> older than <days>d, <b> not completed, <c> superseded)
```
A record with no `Updated` is **not** guessed at from another field. It is excluded, and it is named:
```
WARN [research] <folder>: index row has no Updated — excluded; run /unikit-explore to redraw the index
```
4. **Match**: compare each surviving entry's `Summary` against the feature description. Select entries that are contextually relevant to the feature being planned.
5. If **0 relevant** researches found — proceed to Step 3 silently. Mark `research_linked = false`.
6. If **1 or more relevant** researches found — ask the user:
```
The question tool of your agent (`AskUserQuestion`): Found related researches:
1. <Title> (<Date>) — <Summary>
2. <Title> (<Date>) — <Summary>
Options:
1. Use all listed researches
2. Let me pick which ones (specify numbers)
3. Skip all — plan from scratch
```
Based on choice:
- Use all → load all listed researches as planning context
- Let me pick → ask which ones: the user writes their numbers (`1, 3`), and the recommended set is the first option; load only the selected
- Skip all → proceed without research context, mark `research_linked = false`
Highlight the most relevant entries in the question text (e.g., "Recommended: #1, #3").
7. For each selected research:
- Read its `RESEARCH.md` — `## Active Summary` as the declared input, `## Findings` and the adaptive artifacts for the rationale; read `CONTRACTS.md` when it exists, and `SOURCE.md` for the dialogue
- Use as planning context and as **starting point** for Phase B deep-dive — reduces scope of Explore tasks in Step 4
- Mark `research_linked = true` and store the research path for `## Based on`
### Step 3: Analyze Requirements
Before exploring code, analyze the feature description for completeness.
**If requirements are clear** — proceed to Step 4.
**If requirements are ambiguous or incomplete** — ask clarifying questions, each with its likely answers as the options:
```
The question tool of your agent (`AskUserQuestion`):
1. [Specific question about feature scope]
2. [Question about implementation approach]
3. [Question about edge cases]
```
Wait for answers before proceeding. Do not plan based on assumptions when the description is ambiguous — ask.
### Step 4: Explore the Codebase & Technical Design
This is the most critical step. The goal is to produce a **deep technical understanding** sufficient
for writing actionable tasks with meaningful WHY context and for generating a `## Technical Context` that reflects the actual codebase state at planning time.
You loaded the project rules in Step 0.5 (Bootstrap). Now use that knowledge to write precise prompts for Explore tasks and to synthesize their results against project conventions.
**Ultra mode, saved-state protocol:** reconnaissance goes through `recon-writer-agent` — each answer lands in its own `recon/<topic>.md`, whole and never condensed (written by the agent itself, or by this session before anything else when the runtime has no writing agent), and later steps work from the file's `## Summary` (`ultra-stateful.md` → `## Recon files`).
#### Phase A: Exploration (Explore tasks)
Launch 2-4 Explore tasks in parallel, each with a **specific focus**. Each task MUST receive references to project documentation files so it operates with project knowledge.
Every task gets the same prompt shape; only the focus and the rule paths change:
```
recon-agent(prompt:
"Before analysis, read these project docs:
- .unikit/ARCHITECTURE.md
- [architecture topic paths relevant to <focus> — from the root's `## Topics`]
- [core rule paths relevant to <focus> — from the RULES_INDEX.md Core table]
- [stack rule paths, when <focus> involves a specific framework]
Then: <focus>. Thoroughness: medium.")
```
Foci: (1) **architecture & affected modules** — the files and modules related to [feature domain], the directory structure, the entry points and how the modules interact; (2) **existing patterns** — similar functionality already implemented, and its patterns (services, controllers, models, DI bindings); (3) **dependencies & integration points**, when needed — everything that imports or uses [module/service], its integration points and side effects. In full mode, name the files and classes Step B found (`mode-full.md`).
After the tasks return, synthesize: files to create/modify, patterns to follow, dependencies, risks.
Code analysis in this skill is read-only: never `/unikit-execute`, never a code-writing delegate.
#### Phase B: Technical Deep-Dive (Explore agent)
**Always runs** — produces the plan's `## Technical Context` content based on the current codebase state.
When `research_linked = true`: use the research's `## Active Summary` — and `CONTRACTS.md` when it exists — as a **starting point** for the deep-dive. They provide initial constraints, interfaces, and patterns; Phase B verifies them against the actual code and updates/extends as needed. This ensures the plan's own context is fresh and accurate even if the codebase changed since the research was conducted.
When `research_linked = false`: perform full technical analysis from scratch.
Launch an Explore task for detailed technical analysis using findings from Phase A. Include doc references (the ARCHITECTURE.md root, the topic files matched to the phases found in Phase A, and all core/stack rules from Bootstrap). The task should:
1. Read source code of existing similar features found in Phase A
2. Extract interface signatures, constructor dependencies, DI bindings from installers
3. Identify patterns the new feature must follow (naming, structure, registration)
4. Find constraints — what is MUST vs FORBIDDEN based on existing code
Return format: structured report matching the `## Technical Context` section of the Plan Manifest Template in `{{skills_dir}}/{{self_name}}/references/TASK-FORMAT.md`. Do not guess — base on actual code read. Thoroughness: very thorough.
**Fallback:** If Agent tool is unavailable, perform analysis inline using Read.
Synthesize the task's findings with Bootstrap rules to produce the plan's `## Technical Context` content.
#### Ultra depth gate (ultra mode only)
In ultra, reconnaissance is **not finished** until the plan has code-level evidence for
**every** phase:
- relevant existing paths and symbols
- callers/consumers and side effects
- exact integration and configuration points
- existing tests, fixtures, commands, logging, migration, and documentation patterns
It feeds each phase file's `## Current-Code Evidence` table and its `### Implementation Steps`; thin reconnaissance is rejected by `{{skills_dir}}/{{self_name}}/references/ULTRA-PLAN-FORMAT.md` → `## Required Detail Gate`.
**Do not paste entire source files into phase plans.** Cite only the evidence that makes the
implementation steps deterministic.
When evidence for a phase cannot be gathered, the decision goes into the manifest's
`## Open Questions` as a blocking question — it is never hidden behind a vague step.
#### Phase C: Additional context
**OPTIONAL (recommended):** Read `.unikit/ROADMAP.md` if it exists:
- Use it to link this plan to a specific milestone (when applicable)
- This reduces ambiguity in `/unikit-implement` milestone completion and `/unikit-verify` roadmap gates
### Step 4.5: Resolve Design Context (game-design module)
**Only when `design_linked = true`:** load `{{skills_dir}}/{{self_name}}/references/design-context.md` and follow it. It reads the shared `design-read` contract, resolves the door (a system, a flow or a content type), produces the `## Design` snapshot plus the optional `## Flow Context` / `## Content Context`, and returns here for Step 5. When `design_linked = false` the body is never loaded. Read only — never write to `.unikit/gamedesign/`.
An experiment plan skips this step (`references/experiment-plan.md`) even when `design_linked = true`: its cards are the requirements, and a prototype never feeds `implemented_version`.
### Step 4.6: Read the Catalog Negatively (only when the plan carries editor work)
**Gate.** Runs only when Step 4 established that the feature touches the editor's **serialized state** *and* `.unikit/system/engine-bridges/BRIDGES.md` lists at least one bridge. A pure-code plan skips this step and makes no call at all.
Whatever discovery this skill's frontmatter carries is read-only and is the whole of the planner's contact with the bridges. Per bridge listed, in the package order `BRIDGES.md` states (a plan never names a bridge, so the user's order does not matter here):
- a bridge of kind mcp: the discovery names the frontmatter grants, once;
- a bridge of kind cli: the whole live listing, once, by the `catalog list` row of its block - one read-only call that gives the name, group and description of every command. The call fails (no editor answered) → this bridge is **not read**; print once `INFO [plan] <bridge>: the editor did not answer; this bridge is not read` and go on.
From the catalogs read, answer exactly two questions about their **union**:
1. **Which kinds of editor work have no route here at all** — so the plan does not schedule an intent this project cannot carry out. The six-word `kind` vocabulary is in `.unikit/system/dev-principles.md` → A8.
2. **Which evidence classes are reachable** — so no acceptance criterion is written against evidence nobody can produce. The claim-class → evidence-class lattice is A2 of the same file.
That is the entire question — **not** which tool does it, **not** how it is called, **not** a strategy — and neither the question nor its answer is written into the plan.
**A bridge that was not read makes the union incomplete.** Then "no route" is not a finding (unknown is not no — A9): print each such answer as a note — `NOTE [plan] <kind or class> is not reachable in the bridges read; <bridge> was not read` — and change nothing in the plan. Only when every listed bridge was read do the outcomes below apply.
**What the outcome may change, and what it may not:**
- a kind of work with no route in any bridge → do not schedule it as bridge work: express the change in a form that has a route, or keep the task and name the missing capability in its `WHY:`. **Do not pre-write `⏸️ MANUAL` into the task** — it is a runtime verdict (A9);
- an evidence class that no bridge reaches → rewrite the acceptance criterion against a class that is, or say plainly in `## Overview` that it cannot be closed here. Never silently downgrade it to the cheapest observation available.
**The planner never:** calls anything that changes state, reads the `## Check` table, reads the notes of a bridge (`.unikit/bridge-notes/`), or opens Context7.
**No bridge listed, or discovery yields nothing → skip.** Plan against the base principles, unchanged.
### Step 5: Create the Plan
- **Fast and full** — use the canonical templates from
`{{skills_dir}}/{{self_name}}/references/TASK-FORMAT.md`.
- **Ultra** — the canonical source of templates and integrity checks is
`{{skills_dir}}/{{self_name}}/references/ULTRA-PLAN-FORMAT.md` and nothing else.
`TASK-FORMAT.md` describes the single-file format and applies only where the bundle
specification explicitly points back at it.
**Plan file path:**
- **Fast mode** → `.unikit/code/PLAN.md` (single flat file)
- **Full mode** → `.unikit/code/plans/<feature-name>/PLAN.md` (single manifest in a folder)
- **Ultra mode** → `.unikit/code/plans/<feature-name>/PLAN.md` (manifest) + `phase-NN-<slug>.md` — written straight into that folder by either writing protocol (`mode-ultra.md` Step F)
In ultra, Step 5 is carried out by `mode-ultra.md` Steps D-G — the section list below still
applies to the manifest, minus the task-level subsections of `## Technical Context`.
#### Plan Sections (all planning modes)
0. **Header timestamps** — `Created:` and `Updated:` directly under the H1, both today's date from Step 1 (`Bash(date *)`). Their shape and the rule for moving `Updated:`: `{{skills_dir}}/{{self_name}}/references/TASK-FORMAT.md` → *Plan Manifest Template*; ultra writes the same two lines under its own H1. Full and ultra also write `Planned at: <short sha>` (`git rev-parse --short HEAD`) under `Updated:` — only in a git work tree with at least one commit; it never moves (`TASK-FORMAT.md` → *Plan Manifest Template*).
1. **`## Overview`** — 3-5 sentences: WHAT is being built, WHY it's needed, WHAT GOAL it serves.
2. **`## Based on`** — if `research_linked = true`, one entry per linked research in the form `.unikit/system/research-link.md` → `## The entry`, carrying the `Summary SHA256` computed back in Step 2 (a research whose digest could not be computed there is written without that line — `## Writing an entry`). The contract was already read in Step 2; it is not re-read here. After all research entries, add "see the `## Technical Context` section below".
If no research: "see the `## Technical Context` section below".
**`## Design`**, **`## Flow Context`**, **`## Content Context`** (game-design module) — the snapshots Step 4.5 prepared, placed in this order directly after `## Based on`, each omitted when it was not produced (always, for a pure-code plan). What each one carries — the `SYS-id`, its version and the cited Acceptance Criteria; the `FLOW-id`, its wiring mode and `GOAL` steps; the `CT-id`, its `scale` and `CT.fields` schema — is `design-context.md`'s. `## Design` feeds `/unikit-verify`'s `implemented_version` writeback; `## Flow Context` and `## Content Context` have none.
**`## Balance Context`** — only for a plan built from a Balance Checkpoints file (`references/balance-plan.md`): the snapshot of the point, placed after `## Based on` and after any of the three snapshots above, directly before `## Settings`; omitted in every other plan. What it carries is `TASK-FORMAT.md`'s (`### Balance checkpoint grammar`).
**`## Experiment Context`** — only for an experiment plan: the snapshot of the cards (and of the design researches that name them) when it was built from experiment cards (`references/experiment-plan.md`), or a brief of the source when it was built from another one (`references/experiment-source.md`); and the lines `Branch:` and `Base:`, placed in the slot of `## Balance Context` (a plan has one of them), directly before `## Settings`; omitted in every other plan. What it carries is `TASK-FORMAT.md`'s (`### Experiment context section`).
3. **`## Settings`** — User preferences (`/unikit-implement` reads this):
- `Testing: yes/no` — whether tests are written at all: tests for logic and system values (`dev-principles.md` item 5)
- `Test checkpoints: task | phase | plan` — where the test-checkpoint tasks stand; resolved in the mode file, omitted when `Testing: no`, `task` only in ultra.
- `Docs: yes/no` — whether to show documentation checkpoint (invokes `/unikit-docs`)
- `Editor tasks: engine-bridge | manual | direct` — read by `/unikit-implement`: how tasks carrying an `Editor:` line are carried out. Resolved in `mode-full.md` / `mode-fast.md`. Default when the line is not asked: `engine-bridge` if `BRIDGES.md` lists a bridge, otherwise `manual`. **Omit this line entirely when `engine_rules_loaded = false`**.
- `PR checkpoints: yes` — only when this plan carries at least one PR checkpoint task (`pr_checkpoints = true` in the mode file and two or more modules); otherwise the line is absent.
4. **`## Roadmap Linkage`** (optional, only if `.unikit/ROADMAP.md` exists):
- If linked: `Milestone: "<name>"` and `Rationale: "<why>"`
- If skipped: `Milestone: "none"` and `Rationale: "Skipped by user"`
5. **`## Checklist`** — phases with tasks. Every task MUST include description, `WHY:` line, `Files:` line — except a checkpoint, `Measure:` or `Hand-back:` task, which carries its own line in that position (`TASK-FORMAT.md`).
The WHY line answers: "what breaks or is missing if we skip this task?"
**When to write `Editor:`** — the criterion is neutral: the change touches the **serialized state of the editor**, not source text. Editing a plain text or config file stays in `Files:`. The concrete signals for the active engine are listed in `{{skills_dir}}/{{self_name}}/references/ENGINE_RULES.md` §3 — read them from there, **do not restate them here**: they are engine facts (a scene, a prefab, a blueprint are not the same concept across engines), and a second inline copy diverges from §3 on its first edit.
Form: `Editor: [kind] <container> → <target> : <action>`, one line per target, placed after `Files:` (grammar and the 6 kinds: `references/TASK-FORMAT.md` → `### Editor task grammar`). Pure code tasks omit the field. When `engine_rules_loaded = false` the field is **not generated at all**.
**Test-checkpoint task.** A run point is a **separate** checklist task carrying the line `Test checkpoint: <coverage>` in the position `Files:` occupies. The rest — no `Files:`, where a checkpoint is worth placing, a phase left without one, the closing `Test checkpoint: plan` under `Testing: yes`, no list of suites, no run commands elsewhere — is `{{skills_dir}}/{{self_name}}/references/TASK-FORMAT.md` → `### Test checkpoint task grammar`; **do not restate it here**.
**Modules (full and ultra).** Group the phases into modules by the criteria of `{{skills_dir}}/{{self_name}}/references/TASK-FORMAT.md` → `### Modules section` — always, whether or not PR checkpoints are on. Write `## Modules` directly after `## Roadmap Linkage` — above `## Checklist` in full, before `## Architecture and Decisions` in ultra (the ultra manifest template) — when there are two or more modules, or one longer than four phases; a module longer than four phases carries `why long:`. Do not restate the criteria here.
**PR checkpoint task.** When `pr_checkpoints = true` and there are two or more modules, the last task of every module but the last is a PR checkpoint task carrying `PR checkpoint: <module name> → <base>` in the position `Files:` occupies; under `Testing: yes` and `Test checkpoints: phase | task` the module's test-checkpoint task stands right before it. Grammar, labels and placement: `TASK-FORMAT.md` → `### PR checkpoint task grammar`.
**Rule refresh per phase.** Before you draft the tasks of each phase — in ultra, before you write each phase file (`mode-ultra.md` Step F) — match the phase's name and the tasks you are about to write against the `Load when` column of the `## Topics` tables in `.unikit/RULES.md` and in `.unikit/ARCHITECTURE.md` (read at Step 0.5; a root without that table has no topics) and the Stack table of `.unikit/memory/code/RULES_INDEX.md`. Read only the topic files and stack rules not loaded yet — keep their paths in `loaded_rules`, the same delta `/unikit-implement` Step 3.0 computes; when unsure, load. A listed topic file that is missing → `WARN [rules] topic file missing: .unikit/rules/<slug>.md` (an architecture topic: `WARN [architecture] topic file missing: .unikit/architecture/<slug>.md`), continue. Nothing is re-read between the tasks of one phase.
6. **`## Commit Plan`** — when 5+ tasks, checkpoints every 3-5 tasks, each mirrored by a decorative `<!-- Commit checkpoint: tasks X-Y -->` marker in `## Checklist`; `/unikit-implement` does not parse it (`TASK-FORMAT.md`). A range never crosses a module boundary.
7. **`## Engine Bridge Findings`** — emitted **empty** (heading and header row) whenever the plan carries at least one `Editor:` task, omitted otherwise; filled by the executor. Contract: `TASK-FORMAT.md` → `### MCP findings section`.
8. **`## Rule Candidates`** — emitted **always, and empty** (heading and header row). Contract: `TASK-FORMAT.md` → `### Rule candidates section`.
9. **`## Test Runs`** — emitted **empty under `Testing: yes`** (the heading alone), omitted under `Testing: no`. Contract: `TASK-FORMAT.md` → `### Test runs section`.
9a. **`## Measurements`** — emitted **empty** (heading and header row) whenever the plan carries at least one `Measure:` task, omitted otherwise; filled by the executor. It stands directly after `## Test Runs`, or — under `Testing: no`, when there is none — directly after `## Rule Candidates`, or, when that is absent too, above `## Dependency Graph`; always above `## Technical Context`. Contract: `TASK-FORMAT.md` → `### Balance checkpoint grammar`.
10. **`## Dependency Graph`** — phase dependencies in ASCII.
**Guard B — a phase carrying an `Editor:` task is serialized alone in its execution layer.**
The unit of parallelism downstream is the **phase**. `/unikit-implement` and `unikit-implement-coordinator` build the phase graph from the `**Dependencies:**` lines, compute execution layers (Layer 0 = phases with no dependencies; Layer N = phases whose dependencies all sit in layers 0..N-1) and run **every phase of one layer concurrently**, while the tasks *inside* a phase always run in order.
So this is a rule about **layers**, never about tasks: two `Editor:` tasks in one phase are already sequential, and splitting them into two phases to "separate" them creates exactly the collision it was meant to prevent.
**How to write it.** When a phase carries at least one `Editor:` line, write the dependency lines so that the phase is the only member of its layer:
- the editor phase **depends on every phase that must precede it**, so nothing from the earlier layers lands beside it;
- **every remaining phase depends on the editor phase**, directly or transitively, so nothing from the later ones does either.
Then check the graph you actually wrote, not the intent: walk the layers the way the coordinator does and confirm that every layer holding an editor phase has exactly one member. If serialization makes the plan awkward, move the editor work into a phase of its own rather than relaxing the rule.
**Module barrier.** A module boundary is a layer barrier — the closing phase of a module depends on every other phase of it, and every phase of the next module depends on that closing phase (`TASK-FORMAT.md` → `### Modules section`). Write it into the `**Dependencies:**` lines together with Guard B and check the layers once for both.
11. **`## Total Estimated Effort`** — sum of all phases.
12. **`## Technical Context`** — always included. Nine subsections (`CONTEXT`, `CONSTRAINTS`, `INTERFACES`, `KEY PATTERNS`, `DEPENDENCY GRAPH`, `FILES`, `EDITOR TARGETS`, `DI BINDINGS`, `OUT OF SCOPE`); `EDITOR TARGETS` is omitted entirely when the plan carries no `Editor:` task. In **ultra** the manifest keeps only the cross-phase part and the rest goes into the phase files — the distribution rule is `{{skills_dir}}/{{self_name}}/references/ULTRA-PLAN-FORMAT.md`. Content comes from Step 4 Phase B, synthesized with the Bootstrap rules: base it on the actual code, do not invent. Template: `{{skills_dir}}/{{self_name}}/references/TASK-FORMAT.md`.
**Self-check:** every interface the tasks name is in `### INTERFACES` with its full {{engine_code_language}} signature, every `Editor:` target has its `### EDITOR TARGETS` row (Kind / Container / Target / Change), and the DI bindings follow `references/ENGINE_RULES.md` §2. In **fast and full** the check runs inside the one plan file; in **ultra** both subsections live in the phase file of the task that owns them, and the check runs between the manifest checklist and that file — never pull them back into the manifest.
13. **`## Open Questions`** (optional) — uncertainties the planning pass could not close, one line each; the last section of the manifest (`TASK-FORMAT.md`). `unikit-plan-polisher` writes its leftovers here; omit the section when there are none.
### Step 6: Confirm with User
Show the user:
1. The plan path — `.unikit/code/PLAN.md` (fast), or `.unikit/code/plans/<feature-name>/PLAN.md` (full, ultra), plus the research reference if linked
2. Full and ultra: the git branch name when `branch_created = true`, otherwise the current branch name
3. A brief summary of the phases and the total estimated effort
4. The modules — `N modules, M PR checkpoints` (or `one module`) — one line
5. When `engine_rules_loaded = false` — the line `Engine rules: ENGINE_RULES.md not found, Editor: fields skipped`
6. The reminder: "To start implementation, run: `/unikit-implement`"
7. The other next steps, printed as a short list and not asked as a question: `/unikit-plan add <what to change>` changes this plan, `/unikit-improve` refines it before the work starts
**Merge plan:** the closing message is the one `{{skills_dir}}/{{self_name}}/references/merge-plan.md` describes — it names the command that runs the plan.
**Experiment plan:** the closing message is the one `{{skills_dir}}/{{self_name}}/references/experiment-plan.md` describes — it names the command that runs the plan, with the plan folder named.
**Ultra mode:** the items above plus `mode-ultra.md` Step H (phase-file count, task count, integrity result, and the not-implementation-ready line when blocking open questions exist). Under the saved-state protocol its `.planning/` folder is already gone (`plan-bundle.mjs finalize`).
### Step 7: Context Cleanup
Suggest the user to free up context space if needed: `/clear` (full reset) or `/compact` (compress history).
## Task Description Requirements
Beyond Step 5 item 5 and `references/TASK-FORMAT.md`: name a dependency on another task's output when the phase order does not make it obvious; a simple task (rename, delete, move) may carry its paths in the description instead of a `Files:` line. A task needs a concrete deliverable, and its `WHY:` must not restate it.
## Important Rules
1. **NO report tasks** — don't create summary/report tasks at the end of a plan
2. **Right granularity** — not too big (overwhelming), not too small (noise). A task should be completable in one focused session
3. **Dependencies matter** — order tasks so they can be done sequentially without blockers
4. **Respect module boundaries** — follow the dependency rules of the `.unikit/ARCHITECTURE.md` root and of the topic loaded for the phase; never invent a direction it does not state
5. **A plan is intent, not inventory — no tool name ever reaches it.** Names live in the live catalog and in the `evidence` column of a findings row, and nowhere else. No bridge id reaches the plan either: the `bridge` cell of a findings row is written by an executor. The planner never lifts an obligation on the executor's behalf — no pre-declared gate, no "this server cannot do X", no `⏸️ MANUAL` written in advance