Research finds out what is already true, so the design stage does not have to guess and does not have to look things up itself. It answers five questions, for each criterion that needs one: has someone built this already; are there guides or recipes covering it; do reputable sources recommend a way to do it; is an assumption the task leans on still true; and it hands the answers to the design stage in a form design can act on without looking again.
Research does not decide. It reports what exists and what is true of it. Choosing between two usable options is design's work. Research does not read to the bottom of anything: a subject is never exhausted, so the criteria are what stop this stage, never the subject.
Every finding names where it came from and when it was looked at. A finding with no source is not a finding, and the model's own recall is never the answer, only a lead worth confirming with one search.
Every write below goes through research-actions.sh, or playbook-actions.sh for the playbook
load, or the task skill's decline-recipe for a declined recipe, or scope's set-tests for the
automated tests answer. All four are named in this skill's own grant, so they run without asking,
and so does the project skill's recipe-source lookup. Any other Bash command still asks for
approval. Dispatching an agent needs no approval either; it is also named in this skill's own
grant.
The dispatch message is the role, the run mode and the inputs. Name the role on the Agent
call. The message itself is one line per item: the run mode, interactive or autonomous, then
each input the step hands over. An input is a path, the words a search is given, or one word a
step names, a stage or a set id. Nothing else goes in. The role's rules and
its return shape live in its agent definition, which reaches it on every dispatch. So the
message restates neither, and two runs of one step hand the role the same words. Each dispatch
below names this shape and lists its own inputs.
Determine the run mode
Look for a stated run mode on the task active in this conversation. Found, and it says
autonomous or light: act autonomously through this whole invocation. Anything else,
including no active task: act interactively, the safe default. Decide this once, at the start.
A mode that names stages in brackets, such as autonomous (implement), covers this stage only
when the list names research; otherwise this stage is interactive.
Research never blocks on this choice the way scope does. The run mode changes what this skill
asks at three points below: a recipe fit of false or unsure, a missing process recipe, and a
test runner found for a task with no automated tests. The split step and the close differ too,
and each says so where it stands. Everything else runs the
same way in both modes.
Find the task
Research runs against a task that already has an approved scope contract. Resolve the active
project's own folder with the project skill first, then the task, <taskId> when given or
whichever task is already active in this conversation. Neither known: say so in one line and
name the task skill. Stop; there is nowhere to write.
Once found, the task's own folder is <projectPath>/tasks/<task-id>. Every call below takes
that folder.
Read what is already there
Run:
"${CLAUDE_PLUGIN_ROOT}"/skills/research/scripts/research-actions.sh read "<task_folder>"
This prints summary lines. contract: says present or absent and contract-file: names the
file. criteria: lists the ids, and criteria-by-designer: lists the ids no person ever
approved. worktree: names the task's own git worktree, where the code is read and the spike
runs. none means the code path, as for a task made before every task had one.
automated-tests: is the contract's answer to whether this task has automated tests: yes, no
or not-asked. recipes-declined: names each framework a person declined a recipe for, or
none. One search:
line names each research file already on disk with its finding count.
Read the criteria's text from the contract file.
read is the one call here that runs from anywhere. Every other call refuses at exit 79 when
the task builds in its worktree and this window is elsewhere. The refusal names the tree and the
route that works from here, either the EnterWorktree tool or the call started with
cd <worktree> &&. Take the route it names, the way /aida:next does, and run the call again.
contract: absent: say so in one line and name the scope skill. Stop.
criteria-by-designer: names any id: say so in one line, with the ids. Interactive: say that the
contract carries criteria a run wrote on the person's behalf. Name /aida:scope as the place to
approve or change them. Research does not block, so go on after saying it. Autonomous: say the
same line for the record and go on. The fact is already on disk, in each criterion's author
field, so write nothing new.
search: lines present: this is a resumed or repeated run. Read each named file before deciding
what is still missing, rather than starting over.
inputs: says what the task's inputs/ folder holds: absent, empty, or present with one
input: line per file, to three levels deep. That folder holds material captured before the task
existed. Read every input: path before any search runs. This material is input, never a finding.
It names things to search for. Record a claim from it only once a search confirms it with a
source. Two exceptions, both written by research itself on an earlier run: a page saved at
inputs/<slug>.md and a tree under inputs/prior-art/. Each is already a recorded finding's
source, so it needs no search to confirm it.
No search: line, and research.v5.md or a research.v5/ folder exists in the task folder.
This is a first run on a version 5 task, and those files are its research. Read them. Record each
finding that still holds through record below, one search per version 5 file. --search is
version-5-<file-slug>, the file's name slugged. --searched-for is the file's own title or
subject line. --source names the version 5 file. --criteria-served names the criterion the
finding serves now. Dispatch a search only for a criterion those files do not cover. The coverage
check then says what is left. Version 6 parses nothing back. So the stage that reads the old
research is the conversion, and there is no converter.
Start the stage
Run:
"${CLAUDE_PLUGIN_ROOT}"/skills/research/scripts/research-actions.sh start "<task_folder>"
This creates the task's research folder and prints the criterion ids again, for reference
while planning searches. It is safe to run more than once; it never overwrites anything.
subscriptions: names the catalog playbook sets this project subscribes to, or says none.
When it names any, dispatch playbook-loader once, with the message this file names: the role,
the run mode, the task folder and those ids. It writes records/playbooks-catalog.json. Then, in
every case, run:
"${CLAUDE_PLUGIN_ROOT}"/skills/playbooks/scripts/playbook-actions.sh load "<task_folder>"
It reads the person's file, the project's file and the loader's record, and writes
records/playbooks.json. Show its record:, rendered:, source: and plays: lines. Say an
unreachable set in one line, and go on; the next run tries again. The plays are the rules
every later role follows. This step loads them once, where the task's evidence starts, so design
and implementation read one record and never fetch. Research itself cites no play.
outward-search: skipped, light run means the task is light. Dispatch no outward-searcher. The
search inside this project and the catalog lookup still run. start logged the skip. A criterion
that only an outward search would serve gets one finding that says the search was skipped. Its
--source is COMPROMISES.md in the task folder.
Read the parent's research
A task made by a split carries parent in its task.json, the parent task's id. Read that
field before planning any search. Absent, or the file does not hold it: go to the next section.
Present: the parent's research folder is <projectPath>/tasks/<parent>/research/, a sibling of
this task's own folder. Read every <search>.json in it. Then walk this task's criteria. Skip a
criterion this task already holds a finding for. The search: lines from read name every file
already on disk, and "Read what is already there" says to read each one first. record only
appends, so a repeated run would otherwise record the parent's findings a second time. Record
each parent finding that answers a criterion here, through record below, one --search per
parent file, named parent-<the parent's search name>. --searched-for is that file's own
searchedFor. --text is the finding's own text. --source holds the finding's source and
its lookedAt date, so the finding still names where it came from and when it was looked at.
record stamps today, which is when this task wrote the finding down, and the source says when
the parent looked. Never claim a fresh look at something nobody looked at again.
--criteria-served names this task's own criterion ids, from the contract read above. A parent
criterion id means nothing here, because this task's contract minted its own.
Dispatch a search only for a criterion nothing in that folder covers. Research is the most expensive thing AIDA does, so a child that re-runs its parent's searches pays twice for one answer.
The parent's folder is gone or unreadable: plan every search as if this task had no parent, and say so in one line. A missing parent is not a failure.
This step asks nothing. It runs the same way in both run modes.
Decide which searches are needed
Walk the criteria list. For each criterion, or each small group of related criteria, work out which of the five questions above actually apply to it. Not every criterion needs every question. A criterion about a UI label rarely needs a library search; a criterion about parsing a file format often does.
Typical search subjects, named by what they read, not by a fixed roster:
- Prior art inside this project. Load the process recipe for this project's framework and
read where this project's own code lives, where its own configuration lives, and which paths
are out of bounds. The recipe derives those from the project's own manifest. Then search by
proximity of name over things already named: class and service names, file and directory
paths, the layer something sits in. Read each candidate's own docblock, the top of the file,
never the whole file. A file with no docblock is reported as having none, which is itself a
signal. Search the configuration store the same way where the framework has one, because an
existing view or content type is prior art that needs no code. A configuration file has no
docblock, so the recipe says what to read in its place. Core and contributed code are noise
here; the outside search covers those. The project's own task records are prior art as well.
The searcher also reads
<project>/tasks/, so "have we built this" is asked of this project's history. A hit names the task and what it changed. Prior art that lives only on another branch or an old commit is invisible to the searcher, which has no git. Extract those files withgit show <ref>:<file>into<task_folder>/inputs/prior-art/<branch>/, and name that folder as a line in the searcher's message. - Prior art outside this project. A library, a module, a package that already does this. Apply the three-part test to anything found: is it maintained, is it used, is it supported. A process recipe for this project's own framework may refine that test; when none exists, apply the plain three-part test and say the recipe is missing (see "A missing process recipe" below).
- Guides and recipes. Ask the navigator's identify mode what covers this criterion, and search any source this project configured itself. Name what is found and say which kind it is: a guide, a tooling recipe, or an agentic recipe. Keep the mark "another framework" where the agent put one. Do not open any of them. Identifying is the whole job and design is the reader. The identify report says which catalogs it searched and which it could not reach; a catalog it could not reach is not a catalog that held nothing, and the finding says so.
- What reputable sources recommend. A current, dated source, never the model's own recall stated as fact. A memory of "the right way to do this" is a lead: confirm it with one search, or record that nothing confirmed it. Ask also how those sources verify the result: the command they run, and what a pass prints. Record the command verbatim, with its source. Design turns it into the order's own proof.
- An assumption that needs checking. Named because a mechanism the task leans on might have changed. Checking it has three outcomes, not one: true, false, or could not be settled. All three finish the check and all three are worth recording; a false assumption is one of the most useful things research produces.
- A spike. For a design question no document answers, for example whether one approach
handles a case, and only when the searches above came back empty. A spike answers one question
and is never kept. It is not dispatched: this conversation writes and runs it. It runs in the
task's worktree, the
worktree:line above, and<codePath>below means that tree. Read<codePath>/.gitignorefirst: when no line names.aida-spike/, say so and stop, and write nothing. Otherwise write a small runnable experiment under<codePath>/.aida-spike/and run it. Record the answer as a finding:--sourceis that folder path, and--textholds what ran and what it printed. Delete the folder before the coverage check closes research. The check refuses (exit 6) while it exists, so nothing throwaway ships. Carry the idea forward, never the code: design authors it fresh.
When automated-tests: reads no, the search inside this project asks one more thing. Is there a
test runner that covers the changed code? Name it in the searcher's words, with its
configuration file as the source. Serve the finding to the criteria the changed code serves.
How many searches run is set by what these criteria actually need. A task with three criteria that all rest on the same library may need one search, not three. The search inside this project is the exception: run it on every task that changes code.
Dispatch one agent per search
Name the role on every dispatch. Three roles cover every dispatched subject above, and each one carries its own tool set and its own limits, applied by the runtime:
| Search subject | Role |
|---|---|
| Prior art inside this project | internal-searcher |
| Prior art outside this project | outward-searcher |
| Guides and recipes | catalog-identifier |
| What reputable sources recommend | outward-searcher |
| An assumption that needs checking | outward-searcher |
A dispatch that names no role runs as the general agent, with every tool and this session's own
model, and nothing the roles promise holds. internal-searcher has no web tools at all, which is
what makes "prior art in this project" a claim about this project rather than about the internet.
For each search decided above, dispatch its role with the message this file names. Its lines are
the role, the run mode, the words to search, and the bound. The internal-searcher message
carries the code path and the project folder in place of the bound. The code path is the task's
worktree, from the worktree: line. The catalog-identifier message carries the project folder
and the project's frameworks beside the bound. Without those two the agent cannot ask this
project's own folders, and an agentic recipe a project put in its own folder is never named.
The agent never sees this conversation and this conversation never sees what the agent read,
only what it reports back. That isolation is what keeps the cost bounded.
What is this step's job is what to do with a bad return. An agent that comes back with prose instead of findings with a source and a date has not done the job. Ask it again, or record what it did find and note the rest as not searched.
A fetch-failed finding is a search snippet, not the page. Record it as returned, with
fetch-failed in --text, so design knows the fact is a snippet. This conversation may then
fetch that one page itself, undispatched: one page, one question. The spike folder rules do not
apply. Save what came back at <task_folder>/inputs/<slug>.md. Record a second finding with
--source the address. Its --text holds what the page said, that this conversation fetched it,
and where it is saved.
Record each finding
For every finding an agent returns, record it under that search's own name:
"${CLAUDE_PLUGIN_ROOT}"/skills/research/scripts/research-actions.sh record "<task_folder>" \
--search <slug> --searched-for "<the words this search searched for>" \
--text "<what was found, or that nothing was>" \
--source "<where it came from>" [--criteria-served <id[,id...]>]
<slug> is lowercase letters, digits and single hyphens, and it names the file: a search called
prior-art-internal writes its findings to research/prior-art-internal.json, and record then
renders research/prior-art-internal.md from it. Nothing reads the rendered file back; it is for
the design stage to read. Call record once per finding; calling it again with the same
--search adds another finding to the same JSON file, and the rendered markdown with it, rather
than replacing it. A finding is named <search>#<n>, counted from 1 as the rendered file
numbers it. A finding whose text cites another finding names it that way, never by a number
counted from 0. No script can tell an older text that counted from 0 from one that counts
from 1, so a person rewrites such a text when one is found.
--searched-for holds the words this search searched for. Words, not a sentence about how the
search ran: "responsive images, image styles, picture element". Give the same value on every
record call for one search. A second value is refused, because the search is the unit. A search
that broadens takes a new --search name of its own.
Those words bound every finding in the file. A search that found nothing proves nothing outside the words it used. The design stage reads them to know what it must look up for itself, because design names modules and APIs that were not decisions yet when research ran.
--criteria-served takes the criterion ids from the contract read above, comma separated, for
example c1,c3. Attach every id this finding actually speaks to. Leave it out when a finding
speaks to none: an empty list is allowed, and it is itself checked below, not silently accepted.
A search that found nothing is still recorded, once. --text says so plainly, for example
"looked and found nothing: no maintained package covers this without pulling in a whole
framework", and --searched-for holds the words it used. It serves the criterion its search was
dispatched for, so --criteria-served names that id. Silence and a negative result look
identical from outside; only the recorded negative tells design it is safe to decide without
searching again.
Never write a finding from memory. If nothing was dispatched to check something, it is not recorded as found; it is either dispatched or left for the next pass.
Reading the catalog
Everything published in the catalog is read through the navigator: guides, tooling recipes, process recipes, agentic recipes. Research never fetches a catalog address itself and never reads a cached copy directly.
A process recipe is looked up, never searched. The project's own sources answer before the catalog, so ask them first, once per framework:
"${CLAUDE_PLUGIN_ROOT}"/skills/project/scripts/project-actions.sh recipe-source "<projectPath>" research <framework>
It prints one line, or nothing. RECIPE: <path> source=<folder>: take that path and skip the
navigator. RECIPE: catalog, with or without searched=<folders>, or no line: ask the
navigator's process-recipe lookup for this project's framework at the research stage. A folder
that holds nothing is not an answer, so a folder miss never skips the navigator. When
searched= is present, record those folders beside the navigator's answer. A later reader then
tells a folder miss from a project with no folder. The no-recipe path below is reached only
after the navigator answers that none exists. The navigator answers with whether
one is available and, when it is, a path to the body on disk. Read the body from that path. The
body is never streamed into the conversation, which is what keeps a recipe affordable.
Verdict words and a missing heading follow
${CLAUDE_PLUGIN_ROOT}/skills/tool/references/reading-a-recipe.md.
Judge the fit once, after the body is read and before the first dispatch. Does this method,
by its description, Goal and Preconditions, describe the work the criteria and non-goals name?
Record the verdict on the search inside this project, on any one record call, with --recipe-fit <true|false|unsure> --recipe-path <path> --recipe-reason "<one sentence>". On false or unsure,
interactive: say the verdict with the reason. Then ask whether to continue with the recipe, without
it (the fallback below), or stop. Autonomous: continue with the recipe, record the verdict as
judged, and say so. unsure is recorded as unsure, never rounded to true or false.
Three answers, not one. A recipe that does not exist for this framework, a listing that could not be reached, and a network that failed are three different things and only the first is a fact about the framework. Record which one happened, in those words. Treating the second or the third as "this framework has no recipe" writes a false finding that nothing later can tell from a true one.
A source this project configured itself is read through recipe-source. The navigator serves
the published catalog. A project pointing at its own folder is a different source, and the command
above is how research reads it.
What a found recipe means
A found guide or recipe's --text says which class it is, since design's next step depends on
it:
- A tooling recipe means the tool exists and can be installed.
- A process recipe means the framework's own procedure for this is already written down.
- An agentic recipe means the decision is already made, for a source this project has configured. Say so in the text. A source this project has not configured, the live-search case, is not automatically trusted: say in the text that the source is not one this project accepted, so design knows the opinion is not binding.
Prior art candidates, in order
For a candidate found inside this project, record it as ordered evidence, never as a verdict.
State how close it is (same name, same directory, same layer) in --text, and let design answer
reuse, extend, supersede, or decline, in that order. Research does not choose between candidates that all
pass; it hands them over ranked by closeness and lets design decide fit.
A missing process recipe
When the lookup answers that no process recipe covers this project's framework, record that the recipe is missing and fall back.
When the lookup could not run at all, record that instead, in those words, and fall back the same way. The fallback is the same; the finding is not.
The fallback depends on the search. For prior art outside this project, apply the plain three-part test: maintained, used, supported. For prior art inside this project, search from the project root and say in the finding that the bound on custom code was not enforced. Do not guess the framework's directory layout.
Interactive: skip the ask for a framework the recipes-declined: line of read names; a person
already answered. Otherwise name the framework and ask whether to write one before moving on. The
answers are three: write one now, not now, or not for this framework. The shape is in
${CLAUDE_PLUGIN_ROOT}/templates/process-recipe-research.md, which carries the sections the
catalog requires and what research asks at each one. Publishing it is the catalog's own
create-on-miss path. "Not for this framework" is recorded on the task:
"${CLAUDE_PLUGIN_ROOT}"/skills/task/scripts/task-actions.sh decline-recipe --project "<projectPath>" <task-id> <framework>
It writes recipesDeclined in task.json, which read prints as recipes-declined:. Design
reads the same field and skips its own ask. Implementation never asks; it records the missing
recipe and goes on.
Autonomous: record the missing recipe as a note in the finding's own text and continue. Do not invent a framework-specific rule in its place.
When the contract needs more
If a search shows a criterion is too vague to check against (research.md's open question: a criterion that gives no bound), do not invent a bound. Say so, name the scope skill, and move on to what can be checked.
Check the automated tests answer
Do this before the coverage check, so its commit carries any change. It applies only when
automated-tests: reads no and a finding names a test runner that covers the changed code.
Interactive: say the runner and its source in one line. Ask one question: keep "no automated
tests", or change it to yes. On a change, run:
"${CLAUDE_PLUGIN_ROOT}"/skills/scope/scripts/scope-actions.sh --run-mode interactive \
set-tests "<task_folder>" --automated yes
Autonomous: keep the answer, and say the runner in one line. A person decides at the next window.
Run the coverage check
Once every planned search has been dispatched and recorded, run:
"${CLAUDE_PLUGIN_ROOT}"/skills/research/scripts/research-actions.sh check "<task_folder>"
This reads every research file's JSON and writes its report to <task_folder>/records/research-check.json.
It prints status:, the report's line count and report: with the path. When the status is not
zero it adds one open: line with the uncovered ids and the counts. The report holds:
- a research file with a missing, empty or malformed required field;
- a
criteriaServedid that names no criterion in the contract; - every criterion the contract holds that no finding anywhere cites (
criteriaWithNoFinding); - every finding whose
criteriaServedis empty (findingsWithNoCriterion).
A task whose research folder does not exist yet is not an error: it is reported as research not
started, with every criterion uncovered, the same as an empty research folder that does exist.
Exit 0: nothing to do. The record is committed when the stage closes: a clean check commits the
task folder, and record commits nothing. Dispatch distiller once, with the message this file
names. Its lines are the role, the run mode, the task folder, the stage research, and the
paths of research/*.json and records/research-check.json. Never a summary of this
conversation. It
writes records/research-distill.json. Then run:
"${CLAUDE_PLUGIN_ROOT}"/skills/research/scripts/research-actions.sh distill "<task_folder>"
Light: dispatch no distiller, and run this call alone. It prints distill: deferred to the design close, because design's close distills all three stages.
It prints standsAlone: and one gap: line per gap, and exits 0 on either value. Show each
gap: line; acting on one is another record call. standsAlone: stale means the findings
changed after the distiller read them: dispatch it again, then run the same call again. Exit 2
means the sidecar was not written. Send the same agent one message: write the file and read it
back. An agent has reported a write it never made. Dispatch a fresh one only when exit 2 repeats.
Exit 4 means the sidecar was malformed. The script set it aside at the setAside: path it
printed. Dispatch a fresh distiller, with the rule it broke quoted from agents/distiller.md.
Then run the same call again. A second exit 4 stops for the person: show the stderr line and the
path set aside. A dispatch that ends before the role's first write is the third case. The scope
skill states the rule under "The distill check": once more with the same message, then once on
model: sonnet.
Then show what research found, before anything else. This is a presentation, not a question.
Research asks nothing here, and the person speaks up only when something looks missing. Read
each rendered research/<search>.md. Show, per search, one line per finding with its source.
Then pull three things out of those findings and name them on their own. The guides and recipes
the catalog identified, by name. Each prior art candidate, with how close it is, as recorded;
design decides reuse, extend, supersede or decline, not research. Each assumption from scope that a
finding showed false, in one sentence. A count and a next command are not a presentation. The
findings are what design acts on, so the person sees them here or not at all.
Exit 4: a research file itself is broken: not valid JSON, not an object, or a top-level field
such as searchedFor missing or malformed. No action wrote such a file and none repairs it,
drop included. Say which file and what is wrong with it, and stop; a person decides what it
was. A broken finding inside a readable file is exit 5, below.
Exit 3: the script could not run the check at all. Read its stderr and fix the named problem, then check again.
Exit 6: the coverage is clean but the spike folder named on the spike: line still exists.
Delete that folder, then check again. Nothing else is missing.
Exit 5: the file reads, but a finding or the coverage is wrong. For each id the open: line
names under criteria with no finding, dispatch another search for that criterion specifically.
Every finding names the criterion it serves, so repair each entry in the report's
findingsWithNoCriterion.
The report names each one as finding, <search>#<n>, counted from 1. A "looked and found
nothing" finding serves the criterion its search was dispatched for, so serve it:
"${CLAUDE_PLUGIN_ROOT}"/skills/research/scripts/research-actions.sh serve "<task_folder>" \
--finding <search>#<n> --criteria-served <id[,id...]>
A positive finding attached to nothing is work nobody asked for: serve it with the criterion it serves, or drop it:
"${CLAUDE_PLUGIN_ROOT}"/skills/research/scripts/research-actions.sh drop "<task_folder>" \
--finding <search>#<n>
serve rewrites that one finding's criteriaServed; drop removes that one finding, and
removes the file when no finding is left. A drop moves every later finding in that file down
by one, so the report's other numbers for that file are stale. Repair one entry, run check
again, then the next. drop prints one cites: line per finding whose text names the dropped
finding, or a later one it moved down. Rewrite each citing finding: drop it and record it
again with the right number, or with no citation. The script cannot tell what the text meant.
The report's danglingCitations lists each finding whose text cites a search with no file, or a
number past that file's findings. Rewrite it the same way. A citation that now lands on the
finding that moved into a dropped one's place passes the check. The cites: lines are the only
warning, so read them when drop prints them.
A finding citing an id the contract does not hold is listed in the report's
unknownCriteriaIds the same way. Serve it with the ids it does serve. A finding with a missing
or malformed field is listed by its finding too. Drop it with drop, then record it again
with every field. Calling record alone, with the same --search, appends a
second finding and leaves the first one as it was, so it repairs nothing.
Research is done only when this check reaches exit 0. Nothing is left deliberately open, because design refuses to start until the report holds exit 0.
Offer a split
Research asks the split question once, here, because closed research is the first real evidence of how many pieces the task holds. The recommendation is the value and a person decides; nothing in this section splits on its own.
After distill reports, dispatch split-advisor once, with the message this file names: the
role, the run mode, and the task folder. It writes records/research-split.json. Then run:
"${CLAUDE_PLUGIN_ROOT}"/skills/research/scripts/research-actions.sh split-read "<task_folder>"
It prints recommendation:, children:, one child: line per child with its criterion count,
and reason: with the first sentence. Show those lines.
Exit 2: the advisor wrote no sidecar. Send it one message to write and read back, then run
split-read again. Still exit 2:
say the advisor wrote no sidecar. Go on flat. Exit 4 means the sidecar was malformed. The script
set it aside at the setAside: path it printed. Dispatch a fresh split-advisor, with the rule
it broke quoted from agents/split-advisor.md. Then run the same call again. A second exit 4
stops for the person: show the stderr line and the path set aside.
recommendation: flat: say so in one line. Go on.
recommendation: split, interactive: read the sidecar's children. Show each child's id,
goal and criterion ids. Ask one question: split as recommended, change it, or keep it flat. On
yes, invoke the task skill through the Skill tool, once, with split and the parent's task id.
Pass the children, goals and criteria exactly as recommended. That action takes each criterion's
text, not its id; read the texts from the contract file named on the contract-file: line. Then
name /aida:scope <child-id> for each child. Stop. On a
change, take the person's children, goals and criteria. Run the same. On no, record nothing
more. Go on.
recommendation: split, autonomous: say in one line that the advisor recommended a split and
recorded it at the sidecar path. Go on flat. A person decides at the next window.
Interactive: stop here. Name the next command for the person, /aida:design <task-id>, and never
invoke it yourself. Autonomous: invoke aida:design through the Skill tool, once, with the task
id, and stop if it refuses. Invoke it only when the mode covers design too; otherwise end as
interactive does, naming the command. Each stage refuses to start without the previous stage's
record, so a stage cannot run out of order. That is why this chain is safe.
Research never blocks
Research decides nothing about the problem, so there is nothing for a person to approve. It never asks permission to look something up, and every finding carries its source, so a wrong finding is checkable afterward by anyone. The questions it asks are the two above, the automated tests answer, and the split. The split comes after its own work is done. Autonomous mode runs every step the same way, apart from those four and the close. At each of the first three, it takes the noted branch instead of asking.
What this skill never does
It never reads a guide or a recipe it finds. It names it and moves on; design reads it.
It never picks between two candidates or two sources that both pass their own test. It ranks and hands the ranking to design.
It never writes a finding with no source. A claim from memory is a lead for one search, never the answer research records.
It never treats an empty criteriaServed as an error to avoid. record accepts it, and it is
exactly what the coverage check looks for on the other side. The check names every empty one,
and research closes only once each one names a criterion.