v2.2 to v2.3

468 added, 349 removed. Audit A to A.

---
name: unikit-explore
description: >-
Enter explore mode for technical work — a thinking partner for {{engine_name}} code,
architecture, and implementation decisions before you write any code. Use it to investigate
a technical solution, design the architecture of a feature, choose between frameworks or
libraries, compare implementation approaches, analyze how existing code or a system works,
or deeply root-cause a complex bug without fixing it yet. The ultra mode adds adaptive
artifacts (a C4 view, ADRs, a dependency graph) to the research folder — run it on "ultra
research", "ultra explore" or "ultraresearch"; otherwise run the default. Trigger on
"let's explore this technical solution", "how should we architect this feature", "which
framework should we use", "run an ultra research on the save system", "investigate this
error deeply". Research and analysis only — it never writes code. This is the CODE /
engineering explorer — for GAME-DESIGN, GDD, mechanics, or balance research (no code)
use /unikit-gd-explore.
argument-hint: "ultra | [topic, system name, or question]"
allowed-tools:
- Read
- Glob
- Grep
- Write
- Edit
- Bash(ls *)
- Bash(find *)
- Bash(wc *)
- Bash(mkdir *)
- Bash(date *)
- Agent
- AskUserQuestion
- WebSearch
- WebFetch
user-invocable: true
metadata:
author: unikit
- version: "2.2"
+ version: "2.3"
category: research
---
# UniKit Explore — Thinking Partner for {{engine_name}} Projects
Enter explore mode. Think deeply. Visualize freely. Follow the conversation wherever it goes.
**IMPORTANT: Explore mode is for thinking, not implementing.** You may read files, search code, and investigate the codebase, but you must NEVER implement features or modify project code. If the user asks to implement something, remind them to exit explore mode first (e.g., start with `/unikit-plan`).
- **This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore.
-
## 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.
<!-- unikit:agents codex -->
## 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.
<!-- unikit:end -->
---
## Delegation agents
This skill uses named delegation aliases for `Agent(...)` calls. Each alias is the single
place where its delegate's model is declared — call sites name the alias and never carry a
model argument of their own.
<!-- unikit:agents claude -->
- **`recon-agent`** — read-only parallel reconnaissance. Expands to:
```
Agent(subagent_type: Explore, model: sonnet, prompt: "<focused question>")
```
`sonnet` is 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 `Agent` tool is unavailable, investigate inline with `Glob`/`Grep`/`Read`.
<!-- unikit:end -->
<!-- unikit:agents !claude -->
- **`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 `Agent` tool is unavailable, investigate inline with `Glob`/`Grep`/`Read`.
<!-- unikit:end -->
<!-- unikit:agents claude -->
- - **`check-agent`** — fresh-context, read-only findings validator (`+check`). Expands to:
+ - **`check-agent`** — fresh-context, read-only coherence pass. Expands to:
```
- Agent(subagent_type: Explore, model: sonnet, prompt: "<the criteria from references/coherence-gate.md>")
+ Agent(subagent_type: Explore, model: sonnet, prompt: "Read <path of references/coherence-gate.md> and run the pass it specifies over <durable file paths> — pass <n> of 2, adjudicated so far: <ledger>. Return the report that file specifies. You are read-only.")
```
- `Explore` is read-only **by construction** — its tool set excludes `Edit`/`Write`, so the
- read-only contract is guaranteed by the dispatch, not merely requested in the prompt.
+ The path travels, never the text. `Explore` is read-only by construction.
`sonnet` is 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 `Agent` tool is unavailable, run the pass yourself, inline — see
- `references/coherence-gate.md`
- (`WARN [coherence] fresh-context pass unavailable — running inline`). The gate is never
- skipped or delayed.
+ Fallback: if the `Agent` tool is unavailable, read `references/coherence-gate.md` yourself
+ and run the pass inline (`WARN [coherence] fresh-context pass unavailable — running inline`).
+ The gate is never skipped or delayed.
<!-- unikit:end -->
<!-- unikit:agents !claude -->
- - **`check-agent`** — fresh-context, read-only findings validator (`+check`). Expands to:
+ - **`check-agent`** — fresh-context, read-only coherence pass. Expands to:
```
- Agent(subagent_type: Explore, prompt: "<the criteria from references/coherence-gate.md>")
+ Agent(subagent_type: Explore, prompt: "Read <path of references/coherence-gate.md> and run the pass it specifies over <durable file paths> — pass <n> of 2, adjudicated so far: <ledger>. Return the report that file specifies. You are read-only: edit and write nothing.")
```
- This runtime may offer no read-only-by-construction agent type, so the read-only
- contract rides on the prompt rather than on the dispatch: state it explicitly in the
- criteria you send from `references/coherence-gate.md`.
+ The path travels, never the text. The read-only contract rides on the last sentence of the prompt above, which is never dropped.
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 `Agent` tool is unavailable, run the pass yourself, inline — see
- `references/coherence-gate.md`
- (`WARN [coherence] fresh-context pass unavailable — running inline`). The gate is never
- skipped or delayed.
+ Fallback: if the `Agent` tool is unavailable, read `references/coherence-gate.md` yourself
+ and run the pass inline (`WARN [coherence] fresh-context pass unavailable — running inline`).
+ The gate is never skipped or delayed.
<!-- unikit:end -->
## Artifact Ownership
- Primary ownership: `.unikit/code/researches/` directory only
- All other context artifacts (`DESCRIPTION.md`, `ARCHITECTURE.md`, `ROADMAP.md`, plans, rules) are **read-only**
- If a discovery should affect another artifact, capture it in research now and route follow-up to the owner skill later
### Insight routing table
During exploration you'll discover different types of insights. All of them go into `RESEARCH.md`,
but tag them mentally so that `Next step:` carries a concrete follow-up action:
| Insight type | Follow-up skill |
|--------------|-----------------|
| New requirement / feature idea | `/unikit-plan` |
| Architecture decision | `/unikit-architecture` |
| Project convention / coding rule | `/unikit-rules` or `/unikit-memory` |
| Strategic direction / milestone | `/unikit-roadmap` |
| Assumption invalidated | Relevant owner skill |
| Bug / broken behavior found | `/unikit-fix` |
| Game-design idea / GDD gap | `/unikit-gd-brainstorm`, `/unikit-gd-explore`, `/unikit-gd-spec`, or `/unikit-gd-system` |
- When writing the `Next step:` field of `## Active Summary`, use this table to generate a
- specific follow-up instead of a generic "update other files". Example:
- - "Architecture decision: use Addressables pooling → run `/unikit-architecture` to formalize"
- - "New convention: all factories return UniTask → run `/unikit-rules` to codify"
+ The `Next step:` field of `## Active Summary` names a concrete follow-up from this table, never a generic "update other files".
---
## The Stance
- - **Curious, not prescriptive** — Ask questions that emerge naturally, don't follow a script
- - **Open threads, not interrogations** — Surface multiple interesting directions and let the user follow what resonates
- - **Visual** — Use ASCII diagrams liberally when they'd help clarify thinking
- - **Adaptive** — Follow interesting threads, pivot when new information emerges
- - **Patient** — Don't rush to conclusions, let the shape of the problem emerge
- - **Grounded** — Explore the actual codebase when relevant, don't just theorize
+ A stance, not a workflow: no fixed steps, no required sequence, no mandatory outputs — you are a thinking partner helping the user explore.
+ - **Curious, not prescriptive** — ask the questions that emerge naturally; follow no script and do not ask the same questions every time
+ - **Open threads, not interrogations** — surface several directions and let the user follow what resonates; pivot when new information emerges, and follow a tangent when it is valuable
+ - **Visual** — use ASCII diagrams liberally when they help clarify thinking; a good diagram is worth many paragraphs
+ - **Patient** — let the shape of the problem emerge; do not rush and do not force structure — you are not obliged to reach a conclusion, or to be brief
+ - **Grounded** — explore the actual codebase when relevant, rather than theorize; question assumptions, the user's and your own; when something is unclear, dig deeper instead of faking understanding
+ - **Don't implement** — never write feature code. Saving research files is fine, writing application code is not
+ - **Don't auto-save** — offer to save the research, don't just do it. The thing that may not happen unasked is the research being saved: manifest, artifacts, registry, gate, confirmation. Writing the dialogue log while you talk — the folder that holds it included — is not that, and asks nothing
+
---
## What You Might Do
Depending on what the user brings, you might:
- **Explore the problem space**
- - Ask clarifying questions that emerge from what they said
- - Challenge assumptions
- - Reframe the problem
- - Find analogies from game development
-
- **Investigate the codebase**
- - Map existing architecture relevant to the discussion
- - Find integration points and Zenject bindings
- - Identify patterns already in use (DI containers, signals, factories)
- - Surface hidden complexity and coupling
- - Trace data flow through systems
+ - **Explore the problem space** — clarifying questions, challenged assumptions, a reframed problem, analogies from game development
+ - **Investigate the codebase** — the architecture in play, integration points and DI bindings, patterns already in use, hidden coupling, data flow
+ - **Compare options** — approaches side by side, comparison tables, tradeoffs (performance, complexity, extensibility), a recommendation if asked
+ - **Surface risks and unknowns** — what could go wrong, gaps in understanding, spikes worth running, performance concerns
Use the `recon-agent` alias for parallel codebase investigation. When the exploration topic touches multiple systems or modules, launch 1-5 of them to gather context faster:
```
recon-agent(prompt:
"In [project root], find files and modules related to [topic keywords].
Report: key directories, relevant files, existing patterns, integration points.
Thoroughness: quick|medium. Be concise — return a structured summary, not file contents.")
```
- **Fallback:** If Agent tool is unavailable, use Glob/Grep/Read directly.
-
- **Compare options**
- - Brainstorm multiple approaches
- - Build comparison tables
- - Sketch tradeoffs (performance, complexity, extensibility)
- - Recommend a path (if asked)
-
**Visualize**
```
+------------------------------------------+
| Use ASCII diagrams liberally |
+------------------------------------------+
| |
| +----------+ +----------+ |
| | System A |------->| System B | |
| | (State) | | (View) | |
| +----------+ +----------+ |
| | |
| v |
| +----------+ |
| | Zenject | |
| | Binding | |
| +----------+ |
| |
| System diagrams, state machines, |
| data flows, DI graphs, component |
| hierarchies, scene compositions |
+------------------------------------------+
```
- **Surface risks and unknowns**
- - Identify what could go wrong
- - Find gaps in understanding
- - Suggest spikes or investigations
- - Flag performance concerns
-
---
## Step 0: Bootstrap Context (MANDATORY)
Before responding to the user — before any exploration, questions, or analysis — you MUST load the project context. This is not optional. Do it silently (don't narrate the loading process to the user), but do it completely.
- This step has **no exceptions**. It used to have exactly one — `init` rebuilt the researches
- index and needed no project context — and that mode no longer exists: the registry is
- re-rendered as part of every save. The absence is recorded here rather than left silent,
- because a MANDATORY step that once had a hole reads like an oversight when the hole simply
- closes.
-
### Required reads (always, every time)
Read ALL of these files in parallel before doing anything else:
1. **`.unikit/DESCRIPTION.md`** — project description, tech stack, constraints
2. **`.unikit/ARCHITECTURE.md`** — architecture decisions, folder structure, module rules
3. **Read `.unikit/memory/code/RULES_INDEX.md`**. Load rules:
- **RULES.md**: ALWAYS read `.unikit/RULES.md` first (highest priority)
- **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)
### Stack documentation enrichment (context7)
- When the exploration topic involves a library or framework from the project's tech stack,
- assess whether the loaded Stack rules provide enough information:
-
- 1. **Stack rule loaded and covers the topic** → proceed with available knowledge, no external lookup needed.
-
- 2. **Stack rule loaded but doesn't cover the specific question** (user asks about
- API methods, configuration options, or features not described in the rule) →
- keep the rule for project conventions, additionally query context7 for the
- specific API documentation gap.
-
- 3. **No matching stack rule exists** but the topic involves a library/framework
- from the project's tech stack (check `DESCRIPTION.md`) → query context7 as the
- primary documentation source for that library.
-
- **context7 query flow:**
- 1. `mcp__context7__resolve-library-id` — find the library ID.
- 2. `mcp__context7__query-docs` — focused query matching the specific aspect the user
- is exploring (not a broad "everything about X" — target the concrete question).
-
- **Fallback:** If context7 tools are unavailable or the lookup fails — proceed with
- your own training knowledge, but explicitly tell the user that the information was not
- verified against current documentation and may be outdated.
-
- **Priority:** Stack rules (project conventions — "how we use it here") always take
- precedence over context7 / web results (API reference — "what the library offers").
- When both are loaded, use stack rules for coding patterns and project-specific decisions,
- and external docs for API details and feature capabilities.
-
- **Mid-exploration enrichment:** If the topic shifts during exploration and a new library
- becomes relevant — apply the same logic at that point. Don't wait until the next full
- context bootstrap.
+ When the topic involves a library or framework from the project's tech stack (`DESCRIPTION.md`) and the loaded Stack rules do not cover the question — no matching rule, or a rule that is silent on the API asked about — query context7 for that gap: `mcp__context7__resolve-library-id`, then one focused `mcp__context7__query-docs` on the concrete question, never "everything about X". Stack rules keep priority for project conventions ("how we use it here"); context7 answers API details ("what the library offers"). Tools unavailable or the lookup failed → use your own knowledge and tell the user explicitly that it was not verified against current documentation and may be outdated. When the topic shifts to a new library mid-exploration, apply this again at that point.
### Optional reads (check if relevant)
- - `.unikit/ROADMAP.md` — strategic milestones (if any). If ROADMAP.md contains a `## References` section with linked documents, and the exploration topic relates to a specific milestone — read the reference documents associated with that milestone (listed in the `Milestones` column of the References table). This provides the original requirements/design context behind the milestone without asking the user for additional input.
+ - `.unikit/ROADMAP.md` — strategic milestones (if any). If ROADMAP.md contains a `## References` section with linked documents, and the exploration topic relates to a specific milestone — read the reference documents associated with that milestone (listed in the `Milestones` column of the References table).
- `.unikit/code/researches/` — prior researches (check for related topics)
- `.unikit/code/plans/` — active feature plans (if any)
- - **Game-design grounding** (only if a design workspace exists) — check `.unikit/gamedesign/GD-IDS.yaml`. **Schema guard:** it MUST be `version: 2` — on a pre-v2 `version: 1` registry, skip design grounding and emit a loud `WARN [design] GD-IDS.yaml is version 1 (pre-v2 layout); design grounding skipped` rather than silently misreading the old layout. When valid, **load the shared READ contract** `.unikit/system/gamedesign/design-read.md` and apply it: its **read surfaces** (read the registry — system `doc_status`/`version`/`depends_on`, flow `mode`/`goals`/`depends_on`, content `scale`/`belongs_to`/`fields` — and follow each entry's `source` path to `.unikit/gamedesign/systems/*.md` / `.unikit/gamedesign/flows/*.md` / `.unikit/gamedesign/content-types/*.md`; the human-readable views are GAME.md's `## System Map [gen]` / `## Flow Map [gen]` / `## Content Map [gen]`), its **Flow-First Resolution** (*intent decides the door*), and its **one-way boundary**. **First-class flow input:** when the exploration topic names or implies a **flow** (a player-facing sequence, onboarding / progression / pacing, a `FLOW-<slug>` id), ground **first-class on that flow** — not merely as a reactive add-on to a system; a system-shaped topic grounds on the system; ambiguous → ask (per design-read). An empty `flows: []` (or no `flows` key yet) → skip the flow read silently, never an error. **First-class content input:** when the topic names or implies a **content type** (a catalog, "the item types", a `CT-<slug>` id), ground **first-class on that content type** via the content door of the design-read ladder — its `CT.fields` schema, `scale`, and `belongs_to`; an empty `content_types: []` (or no `content_types` key) → skip the content read silently, never an error. **One-way boundary: explore may *read* design for grounding; it never writes or edits design — that flows through the `/unikit-gd-*` skills.**
-
- ### Why this matters
-
- Without this context you'll give generic {{engine_name}} advice instead of advice grounded in this project's actual architecture, conventions, and patterns. The quality difference is enormous — this is the foundation that makes explore mode valuable.
+ - **Game-design grounding** (only if a design workspace exists) — `.unikit/gamedesign/GD-IDS.yaml` MUST be `version: 2`; on `version: 1` skip design grounding with a loud `ERROR [design] GD-IDS.yaml is version 1 (pre-v2 layout); design grounding skipped`. When valid, load the shared READ contract `.unikit/system/gamedesign/design-read.md` and apply it — its read surfaces, its Flow-First Resolution (*intent decides the door*), its one-way boundary. **First-class flow input:** a topic that names or implies a flow — a player-facing sequence, onboarding, pacing, a `FLOW-<slug>` id — grounds first on that flow; the systems it depends on are the secondary read; ambiguous → ask. **First-class content input:** a topic that names or implies a content type — a catalog, "the item types", a `CT-<slug>` id — grounds on it: its `CT.fields`, `scale`, `belongs_to`. An empty `flows: []` or `content_types: []` (or no such key) → skip that read silently. Explore reads design and never writes it — that flows through the `/unikit-gd-*` skills.
### Input handling
The argument after `/unikit-explore` can be:
- - **The slug of an existing research folder** in `.unikit/code/researches/` — this is an
- **entry into the continuation cycle**, not a new topic. Read the manifest and continue that
- research (see [Continuing a research](#continuing-a-research)).
+ - **The slug of an existing research folder** in `.unikit/code/researches/` — an **entry into the continuation cycle**, not a new topic: load `{{skills_dir}}/{{self_name}}/references/continuing.md` and follow it.
- **An ultra request** — the leading `ultra` token, or the same request in the user's own wording ("ultra research", "ultraresearch", "ultra explore", "ультраисследование", "run an ultra research on the save system") — switches on adaptive research artifacts. Load `{{skills_dir}}/{{self_name}}/references/ULTRA-RESEARCH-FORMAT.md` and follow it when saving. Everything before saving — the stance, the exploration itself — is unchanged. Ultra is **user-named, never model-inferred**: it is never chosen because the topic is large or difficult, and wording that only asks for care ("research this deeply", "a thorough investigation") is not an ultra request — explore normally.
- - A vague idea: "object pooling system"
- - A specific problem: "the save system is getting unwieldy"
- - A system name: to explore its architecture
- - A flow or player sequence: "the first-session flow", "the onboarding", a `FLOW-<slug>` id — a **first-class flow input** that grounds on the dynamics axis (per the design-read Flow-First Resolution)
- - A content type or catalog: "the item types", "the loot catalog", a `CT-<slug>` id — a **first-class content input** that grounds on the catalog axis (per the design-read Flow-First Resolution)
- - A comparison: "UniTask vs coroutines for this"
- - A question: "how does the DI container handle scene transitions?"
- - Nothing: just enter explore mode
-
- If the argument matches the name of an existing folder in `.unikit/code/researches/`, treat it
- as a continuation rather than a new subject, and follow
- [Continuing a research](#continuing-a-research).
+ - **Anything else** — a vague idea, a specific problem, a system name, a comparison, a question, or nothing at all — starts an ordinary exploration. A flow (`FLOW-<slug>`, "the onboarding") or a content type (`CT-<slug>`, "the item types") grounds on the design axes (see *Optional reads*).
On an ultra request, strip the ultra wording and the verb that carried it, treat the rest as the topic and explore normally; the mode only changes what is written at save time. An ultra request with no topic falls into the ordinary no-topic branch — ask for the topic, then work in ultra. If `references/ULTRA-RESEARCH-FORMAT.md` cannot be read, **degrade to a standard research** and print one line `WARN [ultra] reference missing — saving a standard research`: the exploration has already happened, and losing it over a missing reference file is not an acceptable trade.
+ **A standard research does not read that file.**
+
### Exploration mode detection
Determine the exploration mode based on user input:
- **File-based exploration**: The user's request contains explicit references to files or folders with documentation to research (e.g., "Сделай исследование на основе документов `docs/feature.md`", "Research these files: `path/to/spec.md`"). In this mode, the primary source of truth is the referenced documents.
- **Prompt-based exploration**: The user's request is a topic, question, idea, or problem statement WITHOUT references to specific documentation files. Examples: "explore object pooling", "how should we refactor the save system?", "compare UniTask vs Coroutines". The primary source is the interactive dialogue with the user.
Remember this mode — it determines whether `SOURCE.md` is generated when saving (see [SOURCE.md for prompt-based explorations](#sourcemd-for-prompt-based-explorations)).
+ It also decides when the log starts being written: in a **prompt-based** exploration [pinning](#pinning-the-log-is-written-as-you-talk) is in force from the first exchange — the one carrying the original request; a **file-based** exploration opens no folder ahead of the save.
+
+ **If the mode changes mid-way** — the user opened with a topic and later handed you files — pinning **continues**, and at save time `SOURCE.md` is generated under the prompt-based rule.
+
### When a plan exists
If the user mentions a plan or you detect one is relevant:
1. Read the existing plan from `.unikit/code/plans/`
2. Reference it naturally in conversation
3. Offer to capture insights in a research when decisions are made
---
+ ## Pinning: the log is written as you talk
+
+ In a prompt-based exploration the dialogue log is not assembled at the end — it is **pinned**
+ to disk while the conversation is still going, and appended to from there on. Pinning and
+ saving are two different operations, and only the second one saves the research.
+
+ **When the first write happens is your judgement, with a floor.** Pin as soon as any one of
+ these has happened — the list is closed:
+
+ - a requirement or a constraint was stated;
+ - a decision was made;
+ - the user corrected you;
+ - a comparison, a table or a diagram was produced that the conversation then leans on.
+
+ Any one of them is enough, and nothing outside the list counts: "this feels like it is going
+ somewhere" is not a signal. The judgement asked of you here is the coarse one — *is this a
+ research yet?* — and you already make it today, one section down, where you decide the
+ conversation has crystallized. It is **not** the judgement of which words matter; that one you
+ are never allowed to make, and the rules for the log say why.
+
+ **The floor.** The first write happens no later than the moment you would offer to save (see [Saving Research Results](#saving-research-results)).
+
+ **What goes into the first write:** the whole conversation from its first turn, verbatim, by
+ the rules in [SOURCE.md for prompt-based explorations](#sourcemd-for-prompt-based-explorations)
+ — including the turns that came before the signal.
+
+ **After that, append.** Each of your replies ends by appending that exchange's user turns to
+ the end of the file — one `Edit`, and no judgement involved.
+
+ **Where.** `.unikit/code/researches/<slug>/SOURCE.md`, with `<slug>` generated by the ordinary rule (see [Naming convention](#naming-convention)); a topic that settles into another name is renamed at save time (*How to save*, step 1).
+
+ **What pinning is not.** The first write:
+
+ - does **not** create `RESEARCH.md` or any adaptive artifact;
+ - does **not** re-render the registry;
+ - does **not** run the coherence gate or the `## Integrity` checks;
+ - does **not** ask for confirmation to save the research — that question stays where it is;
+ - does **not** mean the research is finished, and you may not cite the file's existence as a
+ sign that it is.
+
+ **A session that never crystallizes leaves nothing behind, and that is the correct outcome.**
+
+ **Failures.**
+
+ - The `Edit` did not go through — no permission, no disk. Print one line and **carry on with
+ the conversation**:
+
+ ```
+ WARN [pin] could not append to SOURCE.md — pinning is off for the rest of this session
+ ```
+
+ This is also the one state in which saving has to pick the log back up, see
+ `references/continuing.md`.
+
+ - `.unikit/code/researches/` does not exist: create it (`mkdir -p`) and continue, silently.
+
+ **On a continuation** pinning writes into the same file, into that session's `## Session <YYYY-MM-DD HH:MM>` block — appended, never rewritten.
+
+ ---
+
## Saving Research Results
When the conversation crystallizes — insights have emerged, decisions were made, or the user wants to preserve context — offer to save the research.
### Research directory structure
All researches live in `.unikit/code/researches/`. Each research gets its own folder:
```text
.unikit/code/researches/<slug>/
├── RESEARCH.md ← the manifest
├── SOURCE.md ← dialogue log (prompt-based explorations only)
- └── [adaptive artifacts] ← ultra only
- ├── CONTRACTS.md
- ├── C4-CONTEXT.md
- ├── C4-CONTAINER.md
- ├── C4-COMPONENT-<scope>.md
- ├── DEPENDENCY-GRAPH.md
- └── ADR-NNNN-<slug>.md
+ └── [adaptive artifacts] ← ultra only (`references/ULTRA-RESEARCH-FORMAT.md`)
```
### Naming convention
- **Name**: the folder is named `<slug>` — 4-5 words max, kebab-case, derived from the
research topic. **No date.**
- - The date lives in the `Created:` and `Updated:` fields of the manifest header, which is
- also where every age filter and all sorting read it from.
- - The reason the date left the name is structural, not cosmetic: a dated folder makes the
- continuation cycle impossible to express. `<date>-<slug>` cannot be *continued* tomorrow
- without the name becoming a lie, so the format would quietly push every follow-up into a
- second folder — which is exactly the duplication this format exists to remove.
+ - The date lives in the `Created:` and `Updated:` fields of the manifest header.
+ <!-- Why the folder name carries no date: docs/skills/unikit-explore-save-rationale.md in the UniKit repository. -->
+
### How to save
Ask:
```
Save this research to .unikit/code/researches/?
Research name: <generated-folder-name>
Options:
1. 💾 Yes — save research
2. 🚫 No
```
Based on choice:
- Yes → save the research to `.unikit/code/researches/<slug>/`, then re-render `researches/INDEX.md`
- No → skip saving → **STOP**
If the user agrees:
1. Determine the folder name: generate `<slug>` from the research topic (4-5 words,
kebab-case, no date).
- **If that slug already names a folder in `.unikit/code/researches/`**, do not resolve it
- silently. Ask:
+ **If the log was pinned**, the folder already exists under the slug the topic carried at that
+ moment. Recompute the slug from the topic as it has now settled, and when the two differ,
+ rename the folder — **before** the manifest is written. A saved research is never renamed.
+ **If that slug already names a folder in `.unikit/code/researches/`** — any folder other
+ than this exploration's own pinned one — do not resolve it silently. Ask:
+
```
AskUserQuestion: A research named <slug> already exists. What should happen?
Options:
1. Continue the existing research — append a session to it
2. Use another name — you type the slug
```
- - *Continue* → enter [Continuing a research](#continuing-a-research); print
+ - *Continue* → load `{{skills_dir}}/{{self_name}}/references/continuing.md` and follow it; print
`WARN [research] <slug> exists — continuing it`.
- *Another name* → save under the slug the user gave; print
`WARN [research] <slug> exists — saving under <new-slug>`.
Both lines are printed **after** the answer, never instead of the question. An automatic
- suffix (`-2`) is **forbidden**: the date used to be a separator as well as a sort key, and
- a silently appended suffix is what makes addressing-by-meaning start finding the wrong
- folder again. Refusing outright is not an option either — a continuation verb exists here,
- and offering it costs less than a refusal.
+ suffix (`-2`) is **forbidden**. Refusing outright is not an option either.
2. Create the research directory:
```
mkdir -p .unikit/code/researches/<slug>
```
- The order of writing is owned by `{{skills_dir}}/{{self_name}}/references/ULTRA-RESEARCH-FORMAT.md` → `## Write order`, and it is not restated here. **In a standard research, items 1 and 5 — the adaptive artifacts and the Integrity checks — simply do not apply**; the rest of the order holds unchanged, including that the registry is re-rendered before the gate runs.
+ **Write in this order.** A standard research skips items 1 and 5; ultra runs all six.
- **In ultra mode** the artifacts are written first and `RESEARCH.md` second. Writing the index of artifacts before the artifacts would point its links at files that do not exist yet.
+ 1. The adaptive artifacts.
+ 2. `RESEARCH.md` — with the `## Artifact Index` pointing at files already written.
+ 3. `SOURCE.md` (prompt-based explorations only) — pinned as the conversation went on, and so
+ already on disk before the save begins. What happens at this point is the append of
+ whatever the log is still missing, never the writing of the file.
+ 4. `researches/INDEX.md` — **re-rendered whole** from the contents of the folders (Step 4).
+ 5. The Integrity checks (`references/ULTRA-RESEARCH-FORMAT.md` → `## Integrity`).
+ 6. The coherence gate.
+ Never write the index of artifacts before the artifacts, and never run the gate before the
+ registry is re-rendered.
+
3. Write `RESEARCH.md` — the manifest. One file carries the whole research: the machine-read header, the planner's input between the `## Active Summary` markers, the evidence in `## Findings`, and the append-only `## Sessions` log.
- **In ultra mode**, the very first line of the file is the research mode marker `<!-- unikit:research-mode:ultra -->`, and a `## Artifact Index` section follows immediately after `## Table of Contents`. Both are specified in `references/ULTRA-RESEARCH-FORMAT.md`; neither is restated here.
+ **In ultra mode**, the very first line of the file is the research mode marker `<!-- unikit:research-mode:ultra -->`, and a `## Artifact Index` section follows immediately after `## Table of Contents`. Both are specified in `references/ULTRA-RESEARCH-FORMAT.md`.
Take `Created:` and `Updated:` from `date`, never from memory. Both feed the registry and `Updated:` is the sort key every reader uses, so a remembered date fails **silently** — it surfaces to the user as "no researches found", not as an error.
```markdown
# <Research Title>
Created: YYYY-MM-DD HH:MM
Updated: YYYY-MM-DD HH:MM
Status: completed | in-progress | needs-follow-up
Lifecycle: active | paused | superseded
Research: <slug>
Target: SYS-<slug> | FLOW-<slug> | CONTENT-<slug> (optional)
Kind: feature | improvement (optional)
Supersedes: <slug> (optional)
## Table of Contents
- [Artifact Index](#artifact-index) — ultra mode only; omit this line in a standard research
- [Active Summary](#active-summary)
- [Findings](#findings)
- [Sub-section 1](#sub-section-1)
- [Sub-section 2](#sub-section-2)
- [Sessions](#sessions)
- [References](#references)
## Active Summary
<!-- unikit:active-summary:start -->
Topic: <1-2 sentences: what was explored>
Goal: <what this research is for — the decision it has to enable>
Scope: <in / out — the out-half carries the stop condition>
Constraints: <`C-<n>` — what the subject imposes>
- Requirements: <`REQ-<n>` — established by evidence>
- Decisions: <`DEC-<n>` — taken, each with its reason in one line>
+ Requirements: <`REQ-<n>` — "<the user's own words>" (`SOURCE.md:<from>-<to>`): <gloss>.
+ A requirement not sourced from the user carries `inferred` in place of a
+ quotation; one that departs from what they said carries `diverges`.>
+ Decisions: <`DEC-<n>` — taken, each with its reason in one line. A requirement marked
+ `diverges` is invalid without a `DEC-` here naming why the user's own proposal
+ was not taken.>
Risks: <`RISK-<n>` — material, not hypothetical>
Open questions: <`OQ-<n>` — unresolved; say which ones block>
Success signals: <how we will know the work landed>
Next step: <the one action that follows>
<!-- unikit:active-summary:end -->
## Findings
<Everything presented to the user during the exploration. This is the EVIDENCE and the
reasoning, never the requirement. Include ALL:
- ASCII diagrams and visualizations
- Detailed descriptions of systems and components
- Code examples and patterns found
- Comparison tables
- Architecture analysis
- Data flow descriptions
- Risk analysis and performance considerations>
## Sessions
<!-- unikit:sessions:start -->
### <YYYY-MM-DD HH:MM> — <session title>
- **What changed**: <one line per changed item, as `<ID> <name>: <old> → <new>`, with the
literal values and the tokens that carry them elsewhere. This line is the input to the
coherence gate's value sweep on the next save — prose here is paid for later in gate passes>
- **Key notes**: <what was learned>
- **Gate**: <`passed (N passes)` | `stopped at budget: <k> unresolved — OQ-…`>
+ - **Readback**: <`<N> shown · <M> corrected · <K> demoted to OQ`, or
+ `not needed (every requirement was stated)`>
- **Links (paths)**: <files read or written>
<!-- unikit:sessions:end -->
## References
<Relevant files, documentation, external resources.
If context7 or web search was used during exploration, include the library names
and specific topics that were queried — this helps reproduce or update the research later.
Example: "R3 (context7: Observable.CombineLatest usage patterns)", "DOTween (web: sequence API)">
```
Filling the `What changed` line — one item per line, values and their carriers named:
```markdown
- **What changed**: `DEC-51` direction_count: base 3 → 5 (carriers: `direction_count`, "base 3")
`DEC-49` card catalogue: 12 → 13 (carriers: `DEC-49`, "13", "thirteen")
```
+ **The `Readback` field is never empty.** Where there was nothing to show it reads
+ `not needed (every requirement was stated)`. `K > 0` names the unconfirmed remainder — those
+ requirements are already sitting in `Open questions:`, and the field only makes them countable.
+ The three numbers are counters and never a second home for the requirements themselves, and they
+ are not obliged to add up: `M + K` can be less than `N`, because the rest were confirmed as they
+ stood.
+
**The two state axes are separate.** `Status` is completeness, and its three values never
change — the `/unikit-plan` registry filter greps this field by name and answers "no
researches" rather than an error when it is renamed. `Lifecycle` is currency. Neither is
derivable from the other.
**Table of Contents is mandatory.** Place it immediately after the header block and before
`## Artifact Index` / `## Active Summary`. It must reflect the document's actual sections and
- sub-sections — not a copy of the template above. Research documents get long, and the TOC is
- how both humans and agents reach the section they need.
+ sub-sections — not a copy of the template above.
**`## Active Summary` is the planner's input and the only hashed region.** Write it to be read
cold, by someone who never saw the conversation. `## Findings` holds the reasoning that
produced it; the summary holds the conclusion. A fact belongs to exactly one of the two — the
- other refers to it by ID, never by retelling it.
+ other refers to it by ID, never by retelling it. A requirement pointing at a `## Findings`
+ sub-heading for its layout is that same reference by ID in another form — ordinary forward
+ resolvability, not a new mechanism.
- **Language Awareness for `RESEARCH.md`**: the manifest follows the same language rules as
- every other artifact. When the configured language is not English, translate section headings
- and prose into the target language. Field **names** in the header (`Created:`, `Status:`,
- `Lifecycle:`, …), the marker comments, ID prefixes, code identifiers, code blocks and file
- paths stay in English — they are read by machines.
+ **A requirement the user stated carries their own words and an anchor into the log.** The line
+ has three parts, and they do not substitute for one another:
- **Where the previous sections went.** The retired three-file format put seven owning sections
- in its result document. They are gone *as owners* and their content is redistributed; recorded
- here so the next edit does not restore them "for completeness":
+ ```
+ `REQ-<n>` — "<the user's own words>" (`SOURCE.md:<from>-<to>`): <your gloss>
+ ```
- | Previous section | New owner |
- |------------------|-----------|
- | `## Topic` | `## Active Summary` → `Topic:` |
- | `## Context` | `## Active Summary` → `Goal:` / `Scope:` |
- | `## Exploration` | `## Findings` |
- | `## Conclusions` | `## Findings` for the reasoning; `## Active Summary` → `Requirements:` / `Risks:` for the requirement |
- | `## Decisions` | `## Active Summary` → `Decisions:` (`DEC-<n>`) |
- | `## Open Questions` | `## Active Summary` → `Open questions:` (`OQ-<n>`) |
- | `## Next Steps` | `## Active Summary` → `Next step:` |
- | `## References` | stays its own section, at the end |
+ | Part | Role | Required |
+ |------|------|----------|
+ | the quotation | **normative** — it *is* the requirement, and it doubles as the anchor | whenever the source is something the user said |
+ | `` `SOURCE.md:<from>-<to>` `` | a hint for the eye, not a contract | written alongside the quotation; never checked |
+ | the gloss | your own formulation, in the project's terms | always; **subordinate** to the quotation, never a substitute for it |
- `## References` stays standalone rather than folding into `## Findings` on purpose: `## Findings`
- is the section that grows without bound, and a reference list buried inside it stops being
- findable.
+ 1. **Quote the words that distinguish, not only the noun.**
+ 2. **What gets checked is that the phrase is present, not that the line range exists** — a grep
+ over `SOURCE.md`, done by you at the moment the requirement is written. A quotation that
+ resolves to nothing blocks the save until it is repaired: the coherence gate does not read
+ `SOURCE.md`, so nobody downstream will catch it.
+ 3. **The anchor is the quotation, and the line number is a hint.** Numbers that have drifted are
+ not a finding — repair them next time you touch the requirement.
+ 4. **A requirement with no user source carries no anchor.** That is not an omission but a
+ different class, and it is marked as one below.
+ 5. A requirement may rest on up to three places in the log, joined by `·`. More than three is a
+ sign that it is not one requirement; split it.
- ### SOURCE.md for prompt-based explorations
+ The order is not a convenience: the anchor points at whatever was the source of the wording at
+ the moment of writing, so the log has to be verbatim **before** an anchor is put onto it. An
+ anchor onto a digest is the same defect wearing a reference.
- If the exploration was **prompt-based** (see [Exploration mode detection](#exploration-mode-detection)), generate an additional artifact `SOURCE.md` in the same research directory. This file captures the full dialogue context so that the exploration can be reproduced or continued later without losing any context.
+ **Every requirement carries where it came from.** The marker is written inline in the line, in
+ backticks, and there are exactly three of them:
- `SOURCE.md` is a **log, not a derived representation of the research.** Two consequences,
- both deliberate: it is **not** part of the hashed region (only `## Active Summary` inside
- `RESEARCH.md` is), and it is **not** in the coherence gate's durable scope. A log is allowed
- to be redundant with the manifest — that is what a log is for, and admitting it to the gate
- would hand back exactly the job of reconciling two differently-written texts.
+ | Marker | Means | Rights |
+ |--------|-------|--------|
+ | `stated` | the source is something the user said; the line carries the quotation and the anchor above | trusted; not put back to them |
+ | `inferred` | your own conclusion — from the code, from documentation, from a defect you found | **carries none of the user's authority**; shown in the readback before it becomes the planner's input |
+ | `diverges` | the requirement departs from what the user actually said | **invalid without a paired `DEC-`** naming why their own proposal was not taken; shown in the readback |
- **When to generate**: Only for prompt-based explorations (user gave a topic/question/idea without referencing specific documentation files). Do NOT generate for file-based explorations (user referenced specific files/folders as input documentation).
+ 1. **The marker is an attribute of the line, not a prefix.** The identifier vocabulary stays
+ closed at six (`### Identifiers`).
+ 2. **`diverges` without its `DEC-` is a defect.**
+ 3. **An unmarked requirement is read as `stated`**. An unmarked line owes a quotation and an
+ anchor, so the omission surfaces as a missing anchor instead of passing in silence.
+ 4. `stated` and `inferred` mirror `extracted from SOURCE.md` and `generated` in `gd-provenance`.
+ Two cases worth settling in advance:
+
+ - **Partly the user's, partly yours** — they named the goal and you worked out the mechanism.
+ Their half stays `stated` with the quotation on it, and the worked-out half becomes its own
+ `REQ-` marked `inferred`. There is no mixed marker.
+ - **They agreed to something you proposed** ("yes, let's do that"). That is `stated`, and what
+ you quote is their agreement *together with* the proposal it accepted. Quote the "yes" on its
+ own and the anchor resolves to a word that decides nothing.
+
+ **A requirement about structure is tested for two readings before it is written down.** The
+ trigger is a closed list: a requirement about structure, layout, order or quantity. Not the
+ ones that feel suspicious, and not all of them — in a summary of twenty this catches three to
+ five.
+
+ The operation is to **write out both readings**, two lines, and look at what you got:
+
+ ```
+ REQ-6 "the payload is shown as five sections"
+ A) five sibling frames; no "payload" container at all
+ B) one "payload" frame, five subsections inside it
+ both buildable, the code differs → FAIL
+ ```
+
+ If the second reading **does not write** — you try and there is nothing to put on the B line —
+ the test has passed.
+
+ On a failure, resolve from cheapest to dearest:
+
+ 1. **Put the distinguishing word from the source into the requirement.**
+ 2. **The source does not distinguish → separate the readings with a diagram.** The diagram lives
+ in `## Findings`, which already holds the ASCII drawings, and the requirement carries a
+ reference to its sub-heading:
+ `` `REQ-<n>` — "<quote>" (`SOURCE.md:…`): <gloss> (layout: `## Findings` → "<sub-heading>") ``.
+ The diagram is **normative, not an illustration**: where it and the gloss disagree, the
+ diagram is the one that is right. And where the layout cannot be drawn in ASCII at all —
+ three-dimensional, animated, data-dependent — this rung is skipped rather than forced, and
+ the requirement goes to the next one.
+ 3. **Still two readings after that → it is not a requirement but an `OQ-<n>`**, and it goes to
+ the readback below.
+
+ Three notes on the trigger. More than two readings fails the same way; the readback then
+ offers up to three readings as options and, beyond three, prints them without a question. A structural requirement the user never dictated (`inferred`)
+ is tested all the same. And where you cannot tell whether a requirement is structural, treat it
+ as structural: a false positive costs two written lines, a miss costs a replanned phase.
+
+ This is a discipline of writing, not a sixth criterion of the gate. The gate's scope is closed
+ at five, and one of its passes costs minutes where this costs a line.
+
+ **Language:** translate section headings and prose of `RESEARCH.md` and `SOURCE.md` into the configured language; header field names (`Created:`, `Status:`, `Lifecycle:`, …), marker comments, ID prefixes, code identifiers, code blocks and file paths stay in English — machines read them.
+
+ <!-- Where the sections of the retired three-file format went: docs/skills/unikit-explore-save-rationale.md in the UniKit repository. -->
+
+ ### Identifiers
+
+ IDs are optional. Add one only when something else references it — another artifact or a
+ handoff. Do not add IDs to make a short note look formal.
+
+ The vocabulary is closed. Six prefixes, and adding a seventh is a decision, not a
+ convenience:
+
+ | Prefix | Means | Lives in |
+ |--------|-------|----------|
+ | `C-<n>` | a constraint the subject imposes | `RESEARCH.md` → `## Active Summary` → `Constraints:` |
+ | `REQ-<n>` | a requirement established by evidence | `## Active Summary` → `Requirements:` |
+ | `DEC-<n>` | a decision taken | `## Active Summary` → `Decisions:` |
+ | `RISK-<n>` | a material risk | `## Active Summary` → `Risks:` |
+ | `OQ-<n>` | an open question | `## Active Summary` → `Open questions:` |
+ | `ADR-<nnnn>` | a decision heavy enough to need its own file | its own file; the ID **is** the filename |
+
+ **An ID that affects the plan's requirements must exist in `## Active Summary` of `RESEARCH.md`.**
+ IDs that live only in `## Findings` trace the reasoning rather than state a requirement, and
+ that is allowed.
+
+ **One value is stated in exactly one owning section.** A number, a threshold, a set of
+ parameters, an enumeration, a path, a signature, the membership of a list — written once, and
+ referenced by ID everywhere else. `## Findings` holds the evidence and the reasoning;
+ `## Active Summary` holds the requirement; an artifact holds the rationale.
+
+ **Characterizing a referenced item in your own words is not a duplicate.** A table cell, a
+ diagram label, a consequence line and an ADR `## Context` are read where they stand, by
+ someone who has not opened the owning section, and they are obliged to remain readable there.
+ The obligation is to not repeat the **value** — `overwrites the zone size (RISK-1)` is
+ correct; `overwrites the zone size to 356.4 × 356.4 (RISK-1)` is a second copy of a number
+ that now has to be kept true by hand.
+
+ This is what makes the coherence gate decidable: whether a value appears in a second place is
+ grepped, whereas whether two sentences are "the same fact" is a judgement that lands
+ differently every time it is made.
+
+ **A value that lives in an artifact carries a revision marker in `## Active Summary`.** The
+ summary line carries the marker instead of the value:
+
+ ```
+ Decisions: `DEC-9` — MoverConfig holds the reference, not a copy (rev.2 · parameters in `ADR-0003`)
+ ```
+
+ Change a value in the artifact and raise `rev.<n>` in the same save. The raised markers are
+ also the input to the next save's value sweep: they name exactly which decisions have carriers
+ worth grepping.
+
+ **An ID is stable and is never reused.** A withdrawn question keeps its number out of
+ circulation; the next one takes the following number.
+
+ **A superseded item keeps its ID.** Mark it superseded, name what replaced it, and leave it
+ in place. `ADR-<nnnn>` additionally carries `Supersedes:` / `Status: superseded` per the ADR
+ format of `references/ULTRA-RESEARCH-FORMAT.md`.
+
+ Numbering is per research folder and starts at 1 — `ADR-` at `0001`, zero-padded to four.
+
+ ### SOURCE.md for prompt-based explorations
+
+ If the exploration was **prompt-based** (see [Exploration mode detection](#exploration-mode-detection)), generate an additional artifact `SOURCE.md` in the same research directory.
+
+ `SOURCE.md` is a **log, not a derived representation of the research.** Two consequences, both
+ deliberate: it is **not** part of the hashed region (only `## Active Summary` inside
+ `RESEARCH.md` is), and it is **not** in the coherence gate's durable scope. A log is allowed to
+ be redundant with the manifest.
+
**Template**:
```markdown
# Exploration Request
## Original Request
<The exact text of the user's initial request/prompt that started this exploration>
## Questions & Answers
+ <!-- Every answer below is a blockquote carrying the user's own words. -->
+
### Q1: <Exact question text as it was asked>
- **Answer**: <The answer that was given — by user or discovered during exploration>
+ Offered:
+ A) <the option exactly as it was put to the user>
+ B) <...>
+ **Answer**:
+ > <The user's own words, verbatim. A blockquote is the only admissible form of an
+ > answer the user gave. When the answer was not the user's but was discovered during
+ > exploration, say so on the line after the quote block.>
### Q2: <Exact question text>
- **Answer**: <The answer>
+ **Answer**:
+ > <verbatim>
<!-- Continue for all questions asked during the exploration -->
## Additional Clarifications
- <All additional details, corrections, and clarifications the user provided during the exploration that were not direct answers to questions. If none — write "None">
+ <All additional details, corrections, and clarifications the user provided, each as a
+ blockquote in the user's own words. If none — write "None">
## Conclusion
- <The final result of the exploration: what was decided, what approach was chosen, what understanding was reached. This should be a concise summary of the outcome, not a copy of RESEARCH.md>
+ <The final result of the exploration: what was decided, what approach was chosen, what
+ understanding was reached. This is the one section written in your own words.>
```
+ **An answer is a quotation, never a digest.** The blockquote is the only admissible form of an
+ answer the user gave.
+
+ **A choice with no recorded menu is not an answer.** When the answer picked among options you
+ put to the user, write those options down next to it under `Offered:`. Without them a reply
+ like "option 1" or "let's go with B" resolves to nothing, and the only surviving way to learn
+ what was agreed is the document derived from this log — which leaves the derived document
+ certifying itself.
+
+ The readback menus of Step 3.5 are menus in exactly this sense.
+
+ **Mark your own cuts, and only your own.** `[…]` means *you* left something out of the
+ quotation. A bare `…` means the user spoke that way — trailed off, paused, thought better of a
+ sentence. Two different things get two different marks.
+
+ **Never cut inside a noun phrase that names a structure, an order or a quantity.** What tells
+ two such requirements apart is rarely the noun — it is the modifiers standing next to it. "A
+ separate block of the same kind" and "a block" carry the same noun and describe different
+ designs, and the whole of the difference sits in the words a cut removes first, because they
+ are the ones that read as decoration.
+
+ **When in doubt, do not cut.** The cost of an extra quoted line is measured and small; the
+ cost of a cut modifier is a phase planned against a requirement nobody stated.
+
+ The one admissible exception is a secret: a credential, a token or a key quoted by accident is
+ cut, marked `[…]`, and the reason is written on its own line under the quote.
+
**On a continuation, append — never rewrite.** A new session adds a fresh block
`## Session <YYYY-MM-DD HH:MM>` at the end of the file, carrying that session's questions,
- answers and clarifications. Everything already in the file stays exactly as it is. The point
- of a dialogue log is that it records what was actually said at the time, and an edited log
- records only what the last session believed.
+ answers and clarifications. Everything already in the file stays exactly as it is.
- **Language Awareness**: Follow the same language rules as other artifacts. Translate section headings and prose into the configured language. Keep code identifiers and file paths in English.
+ ### Step 3.5: Readback
- **Important**: Capture the actual dialogue content faithfully. The value of this artifact is in preserving the exact questions, answers, and clarifications — not in summarizing or rephrasing them.
+ Before the registry is re-rendered and before the gate runs, put back to the user the
+ requirements they have not actually seen.
+ **What goes in — three classes, and only these:**
+
+ | Class | Where it comes from |
+ |-------|---------------------|
+ | `inferred` | the provenance marker |
+ | `diverges` | the provenance marker |
+ | failed the two-readings test | the third rung of the resolution ladder |
+
+ A requirement marked `stated` that passed the test is **not shown**. Out of twenty-two
+ requirements it typically prints three.
+
+ **One question per requirement, at most four per `AskUserQuestion` call** — `k` requirements take ⌈k/4⌉ calls, in ID order.
+
+ 1. **Print the grounds first, then ask.** Before each call, print the grounds of every requirement that call carries, as plain markdown in a block of their own — the question mechanism carries the options and nothing else:
+
+ ```
+ Before saving — <N> requirements out of <M>. The rest go as they are.
+
+ <ID> <CLASS>
+ I wrote: "<your formulation>"
+ You said: "<the quotation>" <anchor>
+ Grounds: <the code, the log line or the document it rests on>
+ Other path: <the alternative> — cost: <what it would change>
+ ```
+
+ `You said:` is printed for `diverges` and for two readings — there it is the quotation the readings interpret. For two readings, `Other path:` gives way to the readings themselves — `A) …`, `B) …` (and `C) …`), each with what it would build.
+ 2. **Ask.** One question per requirement, its options set by the class:
+
+ | Class | Options |
+ |-------|---------|
+ | `diverges` | `Mine (DEC-<n>)` · `Yours` · `Open question` |
+ | `inferred` | `Requirement` · `Finding only` · `Open question` |
+ | two readings | `A` · `B` · `Open question` — three readings: `A` · `B` · `C` · `Open question`; more than three: the readings are printed with the grounds, no question is asked, and the item stays the `OQ-<n>` it already is |
+
+ The free answer ("Other") is added by the tool itself — never list it as an option.
+ 3. **"Recommended" only on a two-readings question**, and only on the reading your code or log evidence supports — that evidence is its `Grounds:` line. The `diverges` and `inferred` menus stay neutral: no option is marked.
+ 4. **No `AskUserQuestion` → the same questions as numbered text**, one block per requirement, answered by number (`REQ-8 — 1, OQ-5 — B`). Print them, then **end your turn and wait for the answer** — do not go on with the save:
+
+ ```
+ REQ-8 inferred
+ 1) Requirement 2) Finding only 3) Open question
+ ```
+
+ **What the answers do:**
+
+ - `Mine (DEC-<n>)` → the requirement keeps its marker and its `DEC-`; nothing is rewritten.
+ - `Yours` → rewrite the requirement to the user's original words, mark it `stated`, and keep its anchor on the original quotation — it is in the log already. Its paired `DEC-<n>` is marked superseded by this requirement.
+ - `Requirement` → the `inferred` marker stays and nothing is rewritten.
+ - `Finding only` → the line leaves `Requirements:` and its content moves to `## Findings`. Its `REQ-<n>` is **not reused**; the next requirement takes the following number.
+ - `A` / `B` / `C` → the item is already an `OQ-<n>` (the third rung of the resolution ladder); the chosen reading becomes a `REQ-` with the next number, marked `stated` and anchored on the logged answer, and the `OQ-<n>` keeps its number and is marked superseded by that `REQ-<m>`.
+ - `Open question` → the requirement leaves `Requirements:` and becomes an `OQ-<n>` with the next number; a `diverges` item's paired `DEC-<n>` is marked superseded. An item that already is an `OQ-<n>` (two readings) stays as it is — no second number.
+ - **"Other" — a correction** → `Edit` the `RESEARCH.md` already on disk: rewrite the requirement, change its marker to `stated`, and anchor it to the user's words from this very exchange — they are in the log already, pinned as they were said. A correction that turns out to be a new requirement opens a new `REQ-` with the next number, marked `stated`.
+ - **"Other" — a counter-question** → answer it, then put the same menu for that requirement again.
+
+ **Order of writes.** Append the readback exchange — the printed grounds, the question, `Offered:` and the answer — to `SOURCE.md` **before** the `Edit` it causes: the quotation a correction or a chosen reading is anchored on must already be in the log when the requirement is written, because that phrase is grepped at that moment.
+
+ **Counting for the `Readback` field of `## Sessions`.** `Yours`, `Finding only`, `Open question` on an item that was not already an `OQ-<n>`, `A` / `B` / `C` and an "Other" correction count as `corrected`; `Mine` and `Requirement` do not. `<K> demoted to OQ` counts only what **Nobody answered** demoted — never an item that already was an `OQ-<n>`.
+
+ **A text-tier question left unanswered** — the next message is about something else — applies **Nobody answered** and continues the save from Step 4: `RESEARCH.md` is already on disk and must not stay unchecked by the registry and the gate.
+
+ **Log every readback question in `SOURCE.md`** (prompt-based explorations; a file-based one has no log) like any other menu: the question, `Offered:` with the option labels — for `A` / `B` / `C` each label together with the text of its reading, since a letter alone resolves to nothing — and the chosen label verbatim; a free answer is a blockquote, as every answer is.
+
+ **Nobody answered** — the user left, or interrupted. The save is **not** cancelled. Every
+ requirement of the three classes that went unconfirmed is demoted to an `OQ-<n>` and leaves
+ `Requirements:`, and one line is printed:
+
+ ```
+ WARN [readback] <k> requirements went unconfirmed — demoted to OQ
+ ```
+
+ An item that already is an `OQ-<n>` (two readings) stays as it is and is not counted in `<k>`.
+
+ **Nothing to show** — every requirement is `stated` and every test passed. Skip the step in
+ silence: no block, no line.
+
### Step 4: Re-render the Researches Index
`.unikit/code/researches/INDEX.md` is a **generated** registry. It is not edited and it is not
- appended to: on every save it is re-rendered whole from the folders on disk, so it cannot fall
- behind them.
+ appended to: on every save it is re-rendered whole from the folders on disk.
1. List the subfolders of `.unikit/code/researches/`.
2. For each, read its `RESEARCH.md` and take: the **first line** (the mode marker — present
means `ultra`, absent means `standard`), the H1 title, the header fields `Created` /
`Updated` / `Status` / `Lifecycle` / `Target`, and the `Topic:` line from
`## Active Summary` — that line **is** the `Summary`.
- 3. A folder with no readable `RESEARCH.md` is skipped, and the skip is **printed**:
+ 3. A folder with no readable `RESEARCH.md` is skipped, and the skip is **printed**. Which line
+ gets printed depends on what the folder holds.
+ A folder carrying a pinned `SOURCE.md` and no manifest is an **unfinished exploration**, not
+ a broken one — a conversation that crystallized and ended before it was saved. It stays out
+ of the registry all the same, and it is announced by a line that names the way back into it:
+
```
+ NOTE [research] <folder> — unfinished exploration: SOURCE.md is there, the manifest is not.
+ Continue it with: /unikit-explore <folder>
+ ```
+
+ `NOTE`, not `WARN`: the work resumes through `references/continuing.md`.
+
+ Every other folder without a manifest keeps the line it has always had:
+
+ ```
WARN [research] skipped <folder> — no readable RESEARCH.md
```
- Skipping in silence is forbidden. An unreadable manifest and an honestly empty registry
- look identical from the outside, so an unannounced skip reports "no researches" for a
- research that is sitting right there on disk.
+ Skipping in silence is forbidden. What skips a folder is the missing manifest, and nothing else.
4. Overwrite the file whole. The header is always:
```markdown
# Researches Index
> Generated by /unikit-explore on every save. Do not edit manually — edits are overwritten.
```
5. Each record:
```markdown
---
### <Research Title>
- **Created**: YYYY-MM-DD HH:MM
- **Updated**: YYYY-MM-DD HH:MM
- **Status**: completed | in-progress | needs-follow-up
- **Lifecycle**: active | paused | superseded
- **Mode**: standard | ultra
- **Summary**: <1-2 sentences, from `Topic:`>
- **Path**: `<slug>/`
- **Target**: SYS-<slug> (optional, omit the line when absent)
```
6. Sort by `Updated` descending; ties broken by `Created` descending; then by folder name.
`Updated` is the key on purpose — in a research with a continuation cycle, freshness means
"when this was last confirmed", not "when the folder was opened".
- 7. After the re-render, print the reconciliation summary — the same three lines the retired
- `init` command printed:
+ 7. After the re-render, print the reconciliation summary:
```
researches/INDEX.md re-rendered:
- Kept: N
- Added: N (list names)
- Removed: N (list names)
```
- **Reconciliation is no longer a separate verb.** There used to be an `init` argument whose
- whole job was to rebuild this file from disk; it is gone, and its work is now part of every
- save — the same pattern as `syncRulesState` Phase 1 and the `## System Map [gen]` re-render in
- game-design. It stopped being a separate command, but it did **not** stop being **visible**,
- which is what step 7 is for.
-
- The cost is worth naming: the registry can no longer be repaired by a dedicated command. In
- exchange it can no longer fall behind, because it is rebuilt from the folders — the actual
- source of truth — before the coherence gate reads the same files from disk.
+ <!-- Why reconciliation stopped being a separate command: docs/skills/unikit-explore-save-rationale.md in the UniKit repository. -->
### Research Coherence Gate
After **all** writing is done — the artifacts, `RESEARCH.md`, `SOURCE.md` and the re-rendered
- `researches/INDEX.md` — and **before** confirming the save to the user, read
- `{{skills_dir}}/{{self_name}}/references/coherence-gate.md` and run the gate it specifies as a `check-agent` dispatch.
- The read is conditional: this is the only moment the file is needed, so it is not loaded at
- the start of an exploration.
+ `researches/INDEX.md` — and **before** confirming the save to the user, hand the path
+ `{{skills_dir}}/{{self_name}}/references/coherence-gate.md` to a `check-agent` dispatch, and
+ run the gate it specifies through that dispatch. The agent reads the file; this session does
+ not, because this is the peak of the session and only the report is needed here.
- Its position is fixed: **after Step 4, before Step 5.** The order matters in both directions.
- The gate re-reads the durable files from disk, so running it before the write has nothing to
- read; and confirming before it runs tells the user the research is safe while it may still be
- incoherent.
+ Act on the report's `Next:` line, and on nothing the report does not carry:
- In ultra the gate runs **after** the bundle integrity checks, not instead of them.
+ - `pass` → write the `Gate:` value it names, record the remainder it lists, go to Step 5;
+ - `repair` → apply the listed repairs as one batch, keeping to the repair rules the report
+ quotes, then dispatch pass 2 with the findings adjudicated so far;
+ - `ask` → put the unresolved findings, the count per pass and the four options to the user,
+ and on a save write the `OQ-<n>` entries and the `Gate:` value it names;
+ - `hold` → do not confirm; tell the user which durable file could not be read.
- If `references/coherence-gate.md` cannot be read, print one line
- `WARN [coherence] reference missing — saving without the coherence pass` and continue — the
- same trade as the ultra reference above: the research has already been done and written, and
- losing it over a missing reference file is not acceptable.
+ Only when the dispatch cannot run does this session read the file itself (the `check-agent`
+ fallback).
+ If `references/coherence-gate.md` does not exist, or the pass reports that it cannot read it,
+ print one line `WARN [coherence] reference missing — saving without the coherence pass` and
+ continue.
+
### Step 5: Confirm the save
- Only once the gate has **finished** — passed, or stopped at its budget with the user's answer
- in hand — tell the user what was written: the folder, the manifest, any adaptive artifacts,
- and the registry line. Until then the save is not confirmed — a gate that runs after the
- confirmation is a gate that reports on a decision already announced.
+ Only once the gate has **finished** — passed, or stopped at its budget with the user's answer in
+ hand — tell the user what was written: the folder, the manifest, any adaptive artifacts, and the
+ registry line. Until then the save is not confirmed.
A save the user authorised over surviving findings is confirmed like any other; its session
entry then reads `stopped at budget: <k> unresolved`, not `passed`. That record is where the
override lives — not a withheld confirmation.
### Important rules for saving
- - **Don't auto-save** — Always offer and let the user decide
+ - **Don't auto-save** — Always offer and let the user decide. What the ban covers is saving the **research**: the manifest, the adaptive artifacts, the re-rendered registry, the gate and the confirmation. Pinning falls outside it — appending a turn to `SOURCE.md` is not a save, and neither is creating `<slug>/` to hold it
- **Generate the name** from the research context — don't ask the user to name it
- - **One manifest** — `RESEARCH.md` carries the research. There is no second canonical file and
- no brief: `## Active Summary` *is* the machine input, and `## Findings` holds the evidence
- that produced it
- - **`SOURCE.md` only for prompt-based explorations** (see [SOURCE.md for prompt-based explorations](#sourcemd-for-prompt-based-explorations)), appended to on a continuation, never rewritten
- - **The registry is re-rendered whole on every save** — never edited, never appended to; this
- is how other skills discover researches, and how it stays in step with the folders
- - **Run the coherence gate** — it is part of saving, not an option. It runs *after* the user has agreed to save, so it neither replaces the question nor weakens `Don't auto-save`
- The user may edit the suggested name before you save
---
- ## Continuing a research
-
- A research is not finished when it is saved — it is **continued**. This is the reason the
- folder name carries no date.
-
- **Entry.** Either the argument named an existing folder in `.unikit/code/researches/`, or the
- user chose *Continue the existing research* in the collision dialogue during a save.
-
- **Procedure.**
-
- 1. Read the folder's `RESEARCH.md` in full — the header, `## Active Summary`, `## Findings`
- and every past `## Sessions` entry. In ultra, read the files in `## Artifact Index` too.
- 2. Explore further, exactly as in a fresh session.
- 3. When saving:
- - **Append** a new entry to `## Sessions`, immediately **before** the closing marker.
- Past entries are reproduced verbatim — the section is append-only.
- - Move `Updated:` to now (from `date`). `Created:` never changes.
- - **Revise `## Active Summary` in place.** New IDs continue the existing numbering; an ID
- is never reused; a superseded item **keeps its number**, is marked superseded, and names
- what replaced it. Revising the summary is the point of a continuation — a session that
- only appends to `## Findings` has recorded evidence without ever updating the conclusion.
- - Reconsider `Status` and `Lifecycle` **explicitly**, and say what they became. Neither
- carries over by default; a research that has quietly stayed `in-progress` across four
- sessions is telling the registry something nobody decided.
- - Append the session's dialogue to `SOURCE.md` if the exploration is prompt-based.
- - Re-render the registry and run the coherence gate, exactly as on a first save.
- 4. **Say it out loud when the folder has outgrown its question.** From the fourth session, or
- past fifteen artifacts, print one line and continue — a note, never a gate:
-
- ```
- NOTE: 4th session, 17 artifacts — a research that keeps growing past its original question
- is cheaper to close and restart with `Supersedes:` than to extend.
- ```
-
- The threshold counts **sessions**, not artifacts: repeated passes are what multiply the
- carriers of one value. A large folder written in one sitting is not the problem this note
- is about.
-
- **Superseding a whole research.** When a new research replaces an old one rather than
- continuing it, the new one carries `Supersedes: <slug>` in its header and the old one is set
- to `Lifecycle: superseded`. The superseded folder is **not deleted** — it is the trace of the
- reasoning, and removing it makes the replacement look unmotivated.
-
- ---
-
-
- ## What You Don't Have To Do
+ ## Superseding a research
- - Follow a script
- - Ask the same questions every time
- - Produce a specific artifact
- - Reach a conclusion
- - Stay on topic if a tangent is valuable
- - Be brief (this is thinking time)
+ When a new research replaces an old one rather than continuing it, the new one carries
+ `Supersedes: <slug>` in its header and the old one is set to `Lifecycle: superseded`. The
+ superseded folder is **not deleted** — it is the trace of the reasoning, and removing it makes
+ the replacement look unmotivated.
---
## Handling Different Entry Points
- **User brings a vague idea:**
- ```
- User: I'm thinking about adding an object pooling system
-
- You: Object pooling is a broad space in {{engine_name}}. Let me think about this...
-
- POOLING APPROACHES IN {{engine_name}}
-
- Simple Managed Advanced
- | | |
- v v v
- +--------+ +---------+ +----------+
- |Queue<T>| |Pool with| |Addressable|
- | Manual | |Auto-grow| | + Pool |
- | return | |+ Limits | | Async Load|
- +--------+ +---------+ +----------+
- | | |
- trivial moderate complex
-
- What are you pooling? Projectiles? UI elements? Enemies?
- ```
-
- **User brings a specific problem:**
+ **User brings a specific problem** — the shape every entry follows: read the code, draw what is there, and ask the one question that decides where to go next:
```
User: The inventory system is a mess, everything is coupled
You: [reads codebase]
+----------------------------------------------+
| CURRENT INVENTORY FLOW |
+----------------------------------------------+
|
+-------------+-------------+
v v v
+---------+ +---------+ +---------+
| Item | | Slot | | UI |
| Manager | | System | | Display |
+----+----+ +----+----+ +----+----+
| | |
+------+------+------+------+
| |
v v
+---------+ +---------+
| Save | | Zenject |
| System | | Binding |
+---------+ +---------+
I see tight coupling in three places. Which one hurts most?
```
- **User wants to compare options:**
- ```
- User: UniTask vs Coroutines for our async loading?
-
- You: Context matters here. Let me check what you're already using...
-
- [reads codebase, finds patterns]
-
- +----------------------------------------------+
- | ASYNC PATTERNS IN YOUR PROJECT |
- +----------------------------------------------+
-
- Coroutines UniTask
- Cancellation manual CancellationToken
- Error handling silent fail try/catch
- Return values callback await result
- Testability hard easy
- Zenject DI awkward natural
- Performance GC alloc zero-alloc
-
- You already use UniTask in 3 systems. Consistency
- alone makes the case. But there's more...
- ```
-
- **User is stuck mid-implementation:**
- ```
- User: /unikit-explore save-load-system
- The serialization is more complex than expected
-
- You: [reads plan from .unikit/code/plans/]
-
- You're on task 4: "Implement save serialization"
-
- Let me trace what's involved...
-
- [draws diagram, explores options, suggests paths]
+ The other entries take the same shape:
- Want to capture this as a research for reference?
- ```
+ - **A vague idea** ("adding an object pooling system") — map the space in one diagram, simple to advanced, then ask what narrows it ("What are you pooling?")
+ - **A comparison** ("two async approaches for our loading") — check what the project already uses before comparing, and ground the table in that usage
+ - **Stuck mid-implementation** (`/unikit-explore save-load-system`) — read the active plan in `.unikit/code/plans/`, find the task, trace what it involves, and offer to capture the result as a research
---
## Ending Discovery
There's no required ending. Discovery might:
- **Flow into action**: "Ready to plan? Run `/unikit-plan`"
- **Result in saved research**: "Saved to `.unikit/code/researches/inventory-decoupling/`"
- **Just provide clarity**: User has what they need, moves on
- **Continue later**: "We can pick this up anytime"
When it feels like things are crystallizing, you might summarize:
```
## What We Figured Out
**The problem**: [crystallized understanding]
**The approach**: [if one emerged]
**Open questions**: [if any remain]
**Next steps** (if ready):
- Save research: I'll create a research record
- Create a plan: /unikit-plan [fast|full|ultra] <description>
- Keep exploring: just keep talking
```
But this summary is optional. Sometimes the thinking IS the value.
-
- ---
-
- ## Guardrails
-
- - **Don't implement** — Never write feature code. Saving research files is fine, writing application code is not
- - **Don't fake understanding** — If something is unclear, dig deeper
- - **Don't rush** — Discovery is thinking time, not task time
- - **Don't force structure** — Let patterns emerge naturally
- - **Don't auto-save** — Offer to save research, don't just do it
- - **Do visualize** — A good diagram is worth many paragraphs
- - **Do explore the codebase** — Ground discussions in reality
- - **Do question assumptions** — Including the user's and your own