{{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 (whengit.enabledandgit.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
- Add — modify/extend an existing plan without creating a branch
Output artifacts by mode:
Fast mode → single flat file .unikit/code/PLAN.md:
.unikit/code/PLAN.md— the single manifest: overview, settings, checklist with WHY context per task, effort estimates, file paths, commit plan, dependency graph, and the## Technical Contextsection (constraints, interfaces, key patterns, files, editor targets, DI bindings) based on the codebase state at planning time.- Temporary plan for quick work —
/unikit-implementmay offer deletion after completion.
Full mode → folder .unikit/code/plans/<feature-name>/:
.unikit/code/plans/<folder>/PLAN.md— the single manifest: overview, settings, checklist with WHY context per task, effort estimates, file paths, commit plan, dependency graph, and the## Technical Contextsection (constraints, interfaces, key patterns, files, editor targets, DI bindings) based on the codebase state at planning time.
Ultra mode → the same folder, additively:
.unikit/code/plans/<folder>/PLAN.md— the same manifest, carrying the mode marker plus## Phase Indexand## Cross-Phase Dependencies, with## Technical Contextreduced to its cross-phase part.phase-NN-<slug>.md— one file per phase, holding the task-scoped detail. The canonical shape of both is{{skills_dir}}/{{self_name}}/references/ULTRA-PLAN-FORMAT.md.
When a research is linked (from /unikit-explore), the plan references it via ## Based on using the Research Reference Format below. The research's ## Active Summary is used as input for generating the plan's own ## Technical Context, not as a replacement — the plan's section reflects the actual codebase state at planning time and supersedes the research summary.
Research Reference Format
Standard block for ## Based on when linking to a research. Each entry records the SHA256 of the region between the ## Active Summary markers of the research's RESEARCH.md, as it was at linking time. /unikit-improve, /unikit-implement and /unikit-verify recompute it to detect that the research actually changed — a content signal, not a clock comparison. The plan does not copy the summary: the plan's own ## Technical Context is already a snapshot, and it is a better one because it was checked against the current code.
### <slug>
- **Summary SHA256**: <64 hex chars>
- `RESEARCH.md` — the manifest; its `## Active Summary` is the declared input
- `CONTRACTS.md` — interfaces, patterns, files, DI bindings (include only if the file exists)
- `SOURCE.md` — original exploration dialogue (include only if the file exists)
Full paths are resolved from .unikit/code/researches/<slug>/. Example:
### customer-items-on-scene
- **Summary SHA256**: 9f2c1d4e7a05b83c6e1f0a94d27b5c38ea6417d9b0c25f83a1e46d7c92b0f5a1
- `RESEARCH.md` — the manifest; its `## Active Summary` is the declared input
- `CONTRACTS.md` — interfaces, patterns, files, DI bindings
- `SOURCE.md` — original exploration dialogue
The heading is the folder name, whatever that name is. Folders created before dateless naming keep the form YYYY-MM-DD_name — the on-disk migration merges a folder's contents but never renames it — so an entry reading ### 2026-03-15_customer-items-on-scene is exactly as valid as the example above.
An entry carrying - **Brief SHA256**: … predates the manifest: it hashed RESEARCH_BRIEF.md, the retired brief field, and it is never recomputed against the summary — the three readers report drift unknown and /unikit-improve Step 5.5 replaces it on an accepted re-link.
What is hashed: the bytes between the ## Active Summary markers of RESEARCH.md, and nothing else.
The rule is unchanged — hash the requirements, never the log. What changed is where the requirements live. A manifest mixes declared requirements with an append-only session log, so it is hashed section by section, behind start/end markers. The earlier revision of this section rejected markers on the grounds that the file split already was one; the file split has been retired, because the split is what created the obligation to keep two documents in sync, and that cost was paid on every save.
| Region / file | Role | Hashed |
|---|---|---|
RESEARCH.md → between the ## Active Summary markers |
the planner's declared input — constraints, requirements, decisions | yes |
RESEARCH.md → ## Findings, ## Sessions, the header |
evidence and log; Updated: moves on every session |
no — appending a session must not report drift |
SOURCE.md, CONTRACTS.md, ADR, C4, the dependency graph |
log and rationale | no — this skill reads them for context, and that is not a reason to hash them: any appended clarification would fire drift with the requirements unchanged |
Computing the hash. Normalize, then hash — never hash the raw bytes:
- Extract the text between
<!-- unikit:active-summary:start -->and<!-- unikit:active-summary:end -->, excluding the marker lines themselves. Both markers are matched as whole lines. If either is missing, or either occurs more than once, the region is undefined: omit theSummary SHA256line and printWARN [research] <folder>: Active Summary markers missing or duplicated — drift detection disabled for this link. - Strip a leading UTF-8 BOM if present.
- LF line endings — strip every carriage return (
CR, byte0x0D). - Trim trailing spaces from every line.
- Exactly one final newline.
- Preserve line order and leading whitespace. This is a prohibition, not a transformation: the summary carries fenced code blocks and indented list structure, and any well-meaning re-indentation breaks every hash that was ever recorded.
Feed the normalized text through stdin, never a temp file: … | shasum -a 256 | awk '{print $1}', falling back to sha256sum when shasum is unavailable.
Rule 0 is carried out on text this skill has already read, not by a separate shell command. It adds no grant: allowed-tools is unchanged by the move from a file to a region.
HTML comments inside the region are kept in the hashed text; only the two marker lines are excluded, by rule 0. There is no pasted copy of the summary anywhere in the plan, so there is nothing to align the digest with. The manifest's template comments are stable text: the template ships via unikit-ai update, existing manifests are project files and are never re-delivered, so a template edit cannot retroactively flip an already-recorded hash.
Rejected alternative: git hash-object would reuse the existing Bash(git *) grant instead of adding two, and .unikit/ is not gitignored so the manifest is normally tracked. It is SHA-1 with a blob header — the field says SHA256 — and it would make the check depend on git while this skill explicitly supports git.enabled: false.
When no hash tool is available. If neither shasum nor sha256sum runs, omit the Summary SHA256 line entirely and print one line to the user:
WARN [research] no SHA256 tool available — drift detection disabled for this link
Do not write a placeholder and do not substitute a timestamp: an absent field is honester than a field that looks like a hash and is not one.
The same applies when the object itself is absent. A linked research with no RESEARCH.md — omit the line and print WARN [research] <folder>: no RESEARCH.md. A RESEARCH.md whose ## Active Summary markers are missing or duplicated — omit the line and print the rule-0 warning above. None of these branches blocks plan creation: drift detection is a convenience, not a gate.
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.
Subagent Delegation — BLOCKING PRE-REQUISITE
When the workflow reaches a step that requires a subagent (Agent), the assistant MUST automatically spawn the
subagent if agent execution is supported by the current environment and not prohibited by higher-priority
instructions.
Only if agent execution is unavailable or blocked, the assistant MUST ask the user before proceeding with any alternative.
Delegation agents
This skill uses a named delegation alias for Agent(...) calls. The alias is the single
place where the delegate's model is declared — call sites name the alias and never carry a
model argument of their own.
recon-agent— read-only parallel reconnaissance. Expands to:Agent(subagent_type: Explore, model: sonnet, prompt: "<focused question>")sonnetis a tier alias, never a version — the one model value that may be written into UniKit. A versioned model id goes stale silently and must never replace it.Fallback: if the
Agenttool is unavailable, investigate inline withGlob/Grep/Read.
recon-agent— read-only parallel reconnaissance. Expands to:Agent(subagent_type: Explore, prompt: "<focused question>")No model is named: this runtime either has no dispatch-time model argument or offers only versioned model ids, and a versioned id goes stale silently. The runtime's own configured default applies.
Fallback: if the
Agenttool is unavailable, investigate inline withGlob/Grep/Read.
develop-agent— not used by this skill. It belongs to the code-writing skills (/unikit-implement,/unikit-fix,/unikit-verify); planning reads and analyses code, it does not write it. Recorded here so the alias named in "Code Analysis & Delegation Rules" can be looked up in the one place aliases are documented.
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 — the user is talking, not typing a CLI.
Parsing rules:
- Extract
--base <branch>if present anywhere in arguments → store asbase_branch, remove from text - If
--listis present → list mode, show all plans and STOP - If the first word (after flag removal) is
full→ full mode, remaining text is the feature description - If the first word is
fast→ fast mode, remaining text is the feature description - 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 - If the first word is
add→ add mode, remaining text is what to add/change in the existing plan - 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 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, because an unwanted bundle leaves the user a folder of phase files they never asked for, while a missed one costs them one word.
--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
/unikit-plan full Item appraisal system → mode: full, base: HEAD, description: "Item appraisal system"
/unikit-plan full --base master Item appraisal system → mode: full, base: master, description: "Item appraisal system"
/unikit-plan fast Item appraisal system → mode: fast, description: "Item appraisal system"
/unikit-plan ultra Item appraisal system → mode: ultra, base: HEAD, description: "Item appraisal system"
/unikit-plan add Add error handling phase → mode: add, description: "Add error handling phase"
/unikit-plan Item appraisal system → mode: ?, ask user
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.
Resolve the current git mode from .unikit/config.yaml:
git.enabled: true→ git-aware workflow is allowedgit.enabled: false→ no-git workflow onlygit.base_branch→ target branch for diffs/merge guidance (default: detected branch ormain)git.create_branches: true→ full mode may create a branchgit.create_branches: false→ full mode still creates a rich plan, but stays on the current branch
If git.enabled = false:
- Skip all branch commands
- Save full-mode plans under
.unikit/code/plans/<slug>/(slug-based fallback) - Treat the "create feature branch" step as unavailable
If git.enabled = true but the repository is not actually inside a git work tree:
- Warn the user that git-aware actions are unavailable until the repository is initialized
- Fall back to the same no-git behavior as above
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):
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.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 whoseLifecycleisactive— a record carrying noLifecycleline counts asactive, because records written before the field existed do not carry it — and ask:AskUserQuestion: Found recent research: "<Title>" (<Created>) Use as basis for planning? Options: 1. Yes — use this research 2. No — I'll describe the feature myselfBased 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, andUpdatedis never substituted for it:Createdexists for display and for breaking ties,Updateddrives every filter and all sorting. A record with noCreatedis shown as"<Title>" (date unknown)— never with empty brackets, because an empty bracket is indistinguishable from normal and hides the gap in the dialogue the same way an unlogged filter hides it in Step 2.- Yes → use research title/summary as description, mark
No context available — if neither session context nor researches exist, ask the user for a description:
AskUserQuestion: Describe the feature you want to plan.
If no mode keyword was found by the Step 0 parsing rules:
If the description was already resolved above → ask only about the mode:
AskUserQuestion: Which planning mode?
Options:
1. Full (recommended) — creates git branch, codebase reconnaissance, full plan
2. Fast — quick plan without a branch
If the description is ALSO still missing (no session context, no researches chosen) → combine into a single question:
AskUserQuestion:
1. Describe the feature you want to plan.
2. Which planning mode?
a. Full (recommended) — creates git branch, codebase reconnaissance, full plan
b. Fast — quick plan without a branch
Based on choice:
- Full → full mode (the Full-mode additional steps load in Step 1.5)
- Fast → fast mode (the Fast-mode additional step loads in Step 1.5)
Ultra is deliberately absent from this question — see the parsing rules in Step 0.
Step 0.5: Bootstrap Context (MANDATORY — all modes except List)
Before any exploration or planning — silently load the project knowledge base. Do NOT narrate the loading process to the user. Runs in every mode named by this step's heading.
Required reads (always, every time, in parallel)
.unikit/DESCRIPTION.md— project description, tech stack, constraints.unikit/ARCHITECTURE.md— architecture decisions, folder structure, module rules, dependency directionsRead
.unikit/memory/code/RULES_INDEX.md. Load rules:- RULES.md: ALWAYS read
.unikit/RULES.mdfirst (highest priority) - Core: read the Core table. For EACH row where Required By =
allor 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
- RULES.md: ALWAYS read
.unikit/skill-context/{{self_name}}/SKILL.md— project-specific skill overrides (if exists)Read
{{skills_dir}}/{{self_name}}/references/ENGINE_RULES.md— engine planning vocabulary: kind → concept (§1), language & layout (§2), when to writeEditor:(§3), engine planning pitfalls (§4), out of scope (§5), direct-edit feasibility (§6). Setengine_rules_loaded = true.If the file is absent — this is a normal path, not an error (an engine whose planning vocabulary has not shipped yet). Set
engine_rules_loaded = falseand:- Do not generate the
Editor:field in any task. - Do not write the
Editor tasksline into## Settingsand do not ask the editor-mode question (Step 5,mode-full.md/mode-fast.md). - Report it at the confirmation step — the line
Engine rules: ENGINE_RULES.md not found, Editor: fields skippedgoes into all three confirmation points: Step 6 Fast mode, Step 6 Full mode, andAdd Step 3: Confirminmode-add.md(theaddmode never reaches Step 6).
Step 0.5 runs in every mode except List — so
addmode loads the vocabulary too and may appendEditor:lines to an existing plan on the same terms asfull/fast.- Do not generate the
Read
.unikit/system/engine-mcp/INDEX.md— the base section only. The delivery stamp plus every section except the## Checktable: access, the live failure classes, shape and cost, what is irreversible here, the lane, and what to do when the file is silent. Those are the exceptions that shape planning — an irreversible write decides where a commit boundary falls, the lane decides what may never run in parallel, shape and cost decide how the work splits into phases. Setmcp_index_loaded = true.Do not read the
## Checktable. It is keyed by area for the executors, which grep their own task's area plus the cross-cutting ones on every editor task. A plan that carries checks forward has started making the executor's decisions with month-old information.If the file is absent — a normal path, not an error (a server that ships no rules tree, or no engine MCP at all). Set
mcp_index_loaded = false, print exactly one line, and continue with the same rights:MCP rules: no INDEX.md — no known exceptions for this server, rights unchangedAbsence never switches a task to
⏸️ MANUAL, never suppresses anEditor:field, and never disables the engine MCP (.unikit/system/dev-principles.md→ A9 · no rules ≠ no rights).
Patches (learning from past fixes)
If .unikit/code/patches/ exists:
- Use
Globto find all*.mdfiles - 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)
Check whether .unikit/gamedesign/GD-IDS.yaml exists.
- Exists → this project carries a game-design workspace. Schema guard (clean
break — no automatic migration): the registry MUST be
version: 2. On a pre-v2version: 1registry, do NOT read it — emit a loudERROR [design] GD-IDS.yaml is version 1 (pre-v2 layout); design grounding unavailable until the workspace is upgraded via /unikit-gd-specand setdesign_linked = false(the plan continues purely code-side, never silently misreading the old layout). With a validversion: 2, setdesign_linked = trueand note it for Step 4.5, which reads the relevant system design (and any flow that exercises it, and any content type that feeds it) and produces the plan's## Design+ optional## Flow Context/## Content Contextsnapshots. Do NOT read the design docs here — Step 4.5 owns that, after the feature scope is clear. - Absent → set
design_linked = falseand skip every design step. The plan is purely code-side, exactly as before — projects without a design module are unaffected.
One-way boundary: planning reads design (GD-IDS.yaml, systems/*.md,
flows/*.md, and the read-only ## System Map [gen] / ## Flow Map [gen] in
GAME.md); it never writes or edits any .unikit/gamedesign/ artifact. Design
changes flow only through the /unikit-gd-* skills.
Remember loaded rule file paths — pass them to Explore tasks in Step 4.
Step 1: Determine Feature Name and Folder
- Read the description and understand the feature intent.
- 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:
- Get today's date in
YYYY-MM-DDformat — it no longer goes into the folder name; it is the value of the manifest'sCreated:andUpdated:fields (see the Plan Manifest Template in{{skills_dir}}/{{self_name}}/references/TASK-FORMAT.md). - The folder name is the feature name from step 2 —
<feature-name>, no date and no separator prefix (e.g.item-appraisal-system).
# Example
ls .unikit/code/plans/
# 2026-03-08_mini-games-editor/ ← earlier format, left exactly as it is
# customer-types/
# → next: <new-feature>/
Collision check — a slug that already exists never resolves itself silently. Scan
.unikit/code/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>(theYYYY-MM-DD_era), and a folder ending in-<name>whose name starts with three digits (the olderDDD-era).- No match → create
plans/<feature-name>/and continue. - A match → ask, and do not decide it yourself:
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
addbody ({{skills_dir}}/{{self_name}}/references/mode-add.md) on the matched folder and printINFO [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>.
Appending an automatic suffix (
-2,-v2, a date) is forbidden. The date used to be a separator as well as a sort key: two runs at the same feature produced two distinct names on their own. Without it there is one name, and a silently suffixed second folder is how the branch resolver starts finding the wrong plan again — the resolver matches the branch name, and the branch name has no suffix.- No match → create
Step 1.5: Load the Mode Body
The shared preamble (Steps 0–1) is done. Load the selected mode's reference body on demand — do not keep all five mode bodies in context 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 A-C (git branch, recon, preferences), then continue to the Shared Steps below. Steps D-H of that body run later — they replace Step 5 and Step 6 of the shared workflow, so do not run them here.
(--list and add modes already dispatched in Step 0 to their own bodies — mode-list.md / mode-add.md — and STOP; they never reach here.)
Shared Steps (all planning modes)
Step 2: Check for Related Researches
If research_pre_linked = true (user already confirmed a research in Step 0.2) → read that research's RESEARCH.md — ## Active Summary as the declared input, ## Findings and the adaptive artifacts for the rationale — plus CONTRACTS.md and SOURCE.md when they exist, mark research_linked = true, store research path for ## Based on, and skip to Step 3.
Before exploring code, check if /unikit-explore has produced relevant researches.
Read
.unikit/code/researches/INDEX.md- If the file doesn't exist — skip this step entirely, proceed to Step 3.
Read
workflow.research_relevance_daysfrom.unikit/config.yaml(default:7).Filter entries by three criteria:
Updatedis withinresearch_relevance_daysfrom today. The age key isUpdated, neverCreated: with a continuation cycle, freshness means "when this was last confirmed", not "when the folder was opened".Statusiscompleted(skipin-progressandneeds-follow-up). The field name and its three values are fixed — renaming either makes this filter match nothing and report "no researches found" instead of an error, which is a failure nobody can see.Lifecycleis notsuperseded. A record carrying noLifecycleline counts asactive.
Log the drop. After filtering, print exactly one line — always, including when nothing was dropped, because a line that appears only on a drop is a line nobody learns to expect:
INFO [research] index: <N> entries, <K> shown (<a> older than <days>d, <b> not completed, <c> superseded)A record with no
Updatedis 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 indexThe repair exists and is named in the line: any save re-renders the index whole.
Match: compare each surviving entry's
Summaryagainst the feature description. Select entries that are contextually relevant to the feature being planned.If 0 relevant researches found — proceed to Step 3 silently. Mark
research_linked = false.If 1 or more relevant researches found — ask the user:
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 → wait for user to specify numbers, load only 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").
- For each selected research:
- Read its
RESEARCH.md—## Active Summaryas the declared input,## Findingsand the adaptive artifacts for the rationale; readCONTRACTS.mdwhen it exists, andSOURCE.mdfor 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 = trueand store research path for## Based on(uses Research Reference Format) - The plan's
## Technical Contextis still generated in Step 5 — the research's## Active Summaryis used as input, not replacement (the section reflects the actual codebase state at planning time)
- Read its
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:
Before planning, a few things need clarification:
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.
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.
Doc references to include in every Explore task prompt:
.unikit/ARCHITECTURE.md— always (module boundaries, dependency rules)- Core rule files loaded in Bootstrap — pass the paths from RULES_INDEX.md relevant to the task's focus (e.g., design principles for architecture analysis, folder structure for file path verification)
- Stack rule files loaded in Bootstrap — pass if the task's focus involves that framework
Task 1 — Architecture & affected modules:
recon-agent(prompt:
"Before analysis, read these project docs:
- .unikit/ARCHITECTURE.md
- [core rule paths relevant to architecture — from RULES_INDEX.md Core table]
Then: find files and modules related to [feature domain]. Map the directory structure,
key entry points, and how modules interact. Thoroughness: medium.")
Task 2 — Existing patterns & conventions:
recon-agent(prompt:
"Before analysis, read these project docs:
- .unikit/ARCHITECTURE.md
- [core rule paths relevant to patterns — from RULES_INDEX.md Core table]
- [stack rule paths if task's focus involves a specific framework]
Then: find examples of similar functionality already implemented in the project.
Show patterns for [relevant patterns: services, controllers, models, DI bindings, etc.].
Thoroughness: medium.")
Task 3 — Dependencies & integration points (if needed):
recon-agent(prompt:
"Before analysis, read these project docs:
- .unikit/ARCHITECTURE.md
- [core rule paths relevant to dependencies — from RULES_INDEX.md Core table]
Then: find all files that import/use [module/service]. Identify integration points
and potential side effects of changes. Thoroughness: medium.")
Fallback: If Agent tool is unavailable, investigate directly using Glob/Grep/Read — search for relevant files, read key source code, and synthesize findings inline.
Rules:
- Fast mode: launch all 2-4 tasks from scratch.
- Full mode: Step B already identified key files, directories, and patterns. Use those findings to make Phase A prompts specific — include concrete file paths, class names, and module names discovered in Step B. This avoids re-discovery and focuses Phase A on deeper analysis of known areas rather than broad scanning.
Example: instead of "find files related to [feature]" → "Read
<content-root>/.../IFeatureService.<ext>and<content-root>/.../FeatureController.<ext>found in recon. Analyze their interfaces, DI bindings, and integration points." (<content-root>and<ext>resolve fromreferences/ENGINE_RULES.md§2) - After tasks return, synthesize: files to create/modify, patterns to follow, dependencies, risks.
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 (ARCHITECTURE.md + all core/stack rules from Bootstrap). The task should:
- Read source code of existing similar features found in Phase A
- Extract interface signatures, constructor dependencies, DI bindings from installers
- Identify patterns the new feature must follow (naming, structure, registration)
- 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
This is the material the phase file's ## Current-Code Evidence table and its
### Implementation Steps are written from. Thin reconnaissance does not survive the
detail floor — it 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. A file pasted whole goes stale on the first edit made against it, and it reads as more authoritative than a path-and-symbol citation while being less true.
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
Project docs (DESCRIPTION.md, ARCHITECTURE.md, RULES.md, core/stack rules, patches, skill-context) were already loaded in Step 0.5 (Bootstrap). This phase handles only remaining optional reads.
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-implementmilestone completion and/unikit-verifyroadmap gates
Step 4.5: Resolve Design Context (game-design module)
Runs only when design_linked = true (the gate is resolved inline in Step 0.5). When it is
true, load {{skills_dir}}/{{self_name}}/references/design-context.md and follow it on demand —
do not keep the design-context body in context for pure-code plans. That body reads the shared
design-read contract (.unikit/system/gamedesign/design-read.md), applies Flow-First
Resolution (intent decides the door — resolve a system, a flow, or a content type first,
ambiguous → ask), and produces the plan's ## Design (+ optional ## Flow Context /
## Content Context) snapshot, then returns here for Step 5. When design_linked = false, skip this step entirely (the design-context body is never
loaded).
This step embodies the one-way boundary: it only reads design artifacts — never write to
.unikit/gamedesign/.
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 an engine MCP is configured (MCP server {{engine_mcp_tool}} present in {{settings_file}}). A pure-code plan skips this step and makes no call at all.
Whatever engine-MCP grants this skill's frontmatter carries are read-only discovery and nothing else — that is the whole of the planner's contact with the engine MCP. Use them once, to answer exactly two questions:
- 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
kindvocabulary is in.unikit/system/dev-principles.md→ A8. - 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. A question of this shape keeps its answer for months ("is there a test run at all"); a question about a name loses it in days — which is why neither the question nor its answer is written into the plan.
What the outcome may change, and what it may not:
- a kind of work with no route → do not schedule it as engine-MCP 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⏸️ MANUALinto the task. That is a runtime verdict, reached by trying and producing the evidence of absence (A9); a planner that writes it in advance has lifted the executor's obligation to try; - an evidence class that is not reachable → rewrite the acceptance criterion against a class that is, or say plainly in
## Overviewthat 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 .unikit/MCP-RECHECK-NOTES.md, or opens Context7. The reference is the executors' resource — /unikit-implement and /unikit-fix reach for it on two triggers, and neither of them is "planning".
No engine MCP configured, or discovery yields nothing → skip. Plan against the base principles, unchanged. The absence of an answer is not a restriction (A9).
Step 5: Create the Plan
- Fast and full — use the canonical templates fro
…(truncated)