Research Session Loop Convention

Companion to docs/orchestrator-convention.md (Pattern A), docs/alignment-page-convention.md, docs/alignment-yaml-routing-contract.md, and docs/interview-convention.md.

This document defines how Pattern A framework-decomposition research orchestrators chunk multi-step research across fresh-context sessions without the implementation exec loop (tasks/roadmap.md + tasks/todo.md + /exec).

Why this exists

Research orchestrators (customer-discovery, competitive-analysis, positioning, journey-map) historically queued each framework as a step in tasks/todo.md and let /exec drive execution. That works, but it couples research chunking to the implementation exec loop, which carries machinery that is dead weight for research:

None of that maps to "research topic 3 of 6." A research chunk needs only: topic, scope, status, output path. The research-only execution profile does not help — it means "research then implement in the main agent," not "research and stop."

The real problem this convention solves is context-window bloat: running an interview, framework selection, multiple framework research passes, and synthesis in one agent session (or even a few) accumulates context until quality degrades. The fix is to make each heavy research phase its own fresh-context session.

Two loops, separated

Implementation Exec Loop Research Session Loop
Driver/exec reads tasks/todo.md, runs one step, stopsThe orchestrator re-invokes itself; each invocation runs one phase, stops
State storetasks/roadmap.md + tasks/todo.md checkboxesSelected-set run manifest + canonical-intermediate file existence
Unit of workone code step (or one phase)one heavy research phase (interview, one framework, synthesis)
"Done" meanstests green / acceptance criteria metcanonical artifact written + alignment page confirmed
Used bytype: execution skills, Pattern B playsPattern A research orchestrators
Routingrecommends the executorrecommends its own re-invocation

The implementation exec loop's artifacts (tasks/roadmap.md, tasks/todo.md) belong to type: execution work. Pattern A research keeps all of its state in the run manifest and the research artifacts themselves (see "State store" below).

The unifying rule

Every invocation of a self-advancing research orchestrator is:

(consume any pasted YAML from the previous gate)   ← light: read + write a small file, archive
   → (do exactly ONE heavy phase)                  ← the only thing that justifies a fresh session
      → (emit the next gate: alignment page or preliminary handoff)
         → STOP

The YAML-in and gate-out are cheap bookends; the heavy phase in the middle is the only thing that bloats context, so that is the unit isolated per session. A phase whose context cost is negligible (e.g. recording an approved selection) is not given its own session — it folds into the head of the next heavy session. Do not spend a fresh-context round-trip on an almost-empty session.

Stage taxonomy (shared meta-pattern)

This Research Session Loop and the Design Tree Loop (docs/design-tree-loop-convention.md) are two instances of one meta-pattern: interrogation → research → ⟨transform⟩ → ⟨gate⟩ → implement. The universal stages are interrogation, research, and implement; the two middle stages vary by phase. The Research phase loop's instance is interrogation → research → plan → review → implement(docs), which maps onto this document's session ladder as:

Stage word Research Session Loop reality
interrogationstate G — the looping stage-zero interrogation page + confidence gate
researchstates F + C — the deep interview and running each selected framework inline
planstate E — the framework multi-select page (deciding which frameworks; the multi-select approval is the per-set scope plan)
revieweach framework's findings review page + the synthesis review page — interleaved per gate, not one terminal review
implement(docs)state A — writing the canonical research/{orchestrator}.md

The Design Tree Loop's instance instead uses interrogation → research → design → plan → implement(scoped) with one binding alignment gate per skill-run-on-a-branch. The stage words are shared vocabulary so the meta-pattern is legible across both loops; this document remains the normative authority for Pattern A research orchestrators (the design doc references this mapping but does not redefine it). See docs/design-tree-loop-convention.md §0.

The session ladder

Because every session starts cold, mode detection must resolve purely from pasted-YAML + filesystem state. On each invocation, the orchestrator resolves the first matching state:

# Detected state Heavy phase this session Emits
0Pasted compiled YAML presentBranch on approval_status: ready-for-agent-review → apply approval for the gate it answers, then fall through to the next pending phase below. not-approvedamend the named page (this is a refinement session).amended page, or proceeds ↓
ACanonical research/{orchestrator}.md existsdone; emit next-skill route
BAll selected framework intermediates exist, no canonicalsynthesizecanonical-approval (review) page
CSelection recorded, ≥1 framework pendingrun next pending framework — load its subskill inline and enter at its research stage (the multi-select approval already satisfied its scope gate)that framework's review page
DMulti-select page in review, selection not yet recorded— (waiting)points user to the page
EPreliminary interview handoff exists, no multi-select pagebuild framework multi-select review pagemulti-select page
FInterrogation completion handoff exists, no preliminary interview handoff (deep) — or it doubles as the handoff (scoped)run the deep interview pass (deep orchestrators); scoped orchestrators fold this into state Epreliminary interview handoff, or proceeds ↓
GNothing yet (genuinely cold, no on-disk artifacts) for an interrogation-page participantrun one stage-zero interrogation round (the looping interrogation page)an interrogation/{orchestrator}-r{N}-{branch}.html round page; on confidence-gate pass, the interrogation completion handoff

Resolution order is YAML first, then most-progressed state backward (A→G). "Pending framework" = a selected framework whose canonical intermediate file does not yet exist. State G applies only to skills participating in the interrogation-page archetype (docs/interrogation-page-convention.md); non-participating orchestrators have no state G and cold-start at state F (deep) or E (scoped) as before.

Stage-zero interrogation loop (state G)

For interrogation-page participants, a genuinely cold start resolves to state G: elicitation runs as the looping HTML interrogation page before the framework/scope page. State G is itself a self-advancing sub-loop — each round is one heavy session:

state G round = 1
build interrogation/{orchestrator}-r{round}-{branch}.html   (assumptions manifest if round==1, else follow-ups)
STOP → user answers → compiled answers YAML
next session (state 0): write research/_working/interrogation-{orchestrator}-r{round}.yaml,
    run the CONFIDENCE GATE:
        NOT covered → round += 1; build the next round; STOP
        covered/waived → write the interrogation completion handoff; fall through to state F/E

A round's confidence gate is the loop exit: the orchestrator cannot advance to state F/E until at least one interrogation round is completed and every interview area is covered or explicitly waived at the coverage checkpoint. The round page carries data-interrogation-gate="coverage-checkpoint" on the exit round; flagging a gap raises the round number and continues the loop.

Loop continuation reuses the self-advancing exception (docs/alignment-yaml-routing-contract.md): each round ends the terminal message with ## Next Work only, telling the user to clear context and paste the compiled YAML into a fresh session. The round page's compiled YAML carries a top-level command plus agent_routing.command naming the same parent orchestrator (never a child framework path command) with gate_type: interrogation-round. Detection resolves purely from pasted-YAML + filesystem: the next session reads the pasted round YAML and the on-disk round sidecars to decide whether to emit the next round or write the completion handoff.

Framework approval granularity

When the loop runs a framework inline (state C), the multi-select approval — recorded in the run manifest — is that framework's Stage-1 scope approval. The framework therefore enters at its research stage (Stage 2 of the staged research workflow): it performs the research and builds a single findings/artifact review page, instead of re-running its own scope gate first. The result is one approval gate per framework inside the loop — the multi-select page approves scope for the whole selected set, and each framework then produces exactly one findings page. Standalone (non-loop) invocation of a framework subskill is unchanged: invoked on its own, the subskill runs its full two-stage workflow, including its own scope gate.

Worked example: /customer-discovery

Session Entry state Does Stops with
1F (cold)deep interviewwrites research/{slug}/_working/preliminary-customer-discovery-interview.md
2Ereads handoff → builds framework multi-select pagemulti-select review page; user reviews, compiles YAML
30 (approved multi-select) → Crecords selection (light) → runs framework 1framework-1 review page
40 (approved fw-1) → Cwrites research/{slug}/customer-discovery-{fw1}.md (light) → runs framework 2framework-2 review page
… per framework …
N0 (approved fw-k) → Bwrites last intermediate (light); all done → synthesizesynthesis review page
N+10 (approved synthesis) → Awrites canonical research/{slug}/customer-discovery.md, archives run manifest, updates .progress.yaml, emits routedone

Session 3's "record the approved selection" is light, so it folds into the head of the run-framework-1 session rather than being its own session.

State store

Self-advancing orchestrators use two minimal pieces of state, and deliberately keep HTML out of the machine path:

  1. Selected-set run manifest — written when the multi-select YAML is approved. Records which frameworks were selected and their intermediate paths. Recommended location:

Shape: ```yaml orchestrator: customer-discovery slug: skills-showcase # omit in flat mode selected_frameworks:

  1. Progress = canonical-intermediate file existence. A selected framework is done when its canonical intermediate (research/{orchestrator}-{framework}.md) exists, and pending otherwise. This is unambiguous because the staged research workflow writes the working packet to _working/ first and the canonical intermediate only on approval — so the canonical file's presence is exactly "approved and done." pending = selected − existing-intermediates.

.progress.yaml stays coarse. Do not push per-framework chunk status into research/.progress.yaml. Its pipeline_stage field continues to record only which orchestrator stage last touched a product path (e.g. pipeline_stage: customer-discovery); afps-status and other readers keep treating it as a pointer, not the source of truth. The run manifest is the chunk-level state; the manifest is archived at synthesis.

Why not the HTML alignment page as the store. The alignment page is an ephemeral, per-topic, confirmed-and-frozen approval record with no progress model, and HTML is a poor agent-writable state surface (it would have to be parsed back each session). The page's job is approval + human-visible progress, not machine state. See "Alignment page role" below.

File and naming conventions

Artifact Flat path Product-path
Interrogation round pageinterrogation/{orchestrator}-r{N}-{branch}.htmlinterrogation/{orchestrator}-r{N}-{slug}.html
Interrogation round answer sidecarresearch/_working/interrogation-{orchestrator}-r{N}.yamlresearch/{slug}/_working/interrogation-{orchestrator}-r{N}.yaml
Interrogation completion handoffresearch/_working/interrogation-{orchestrator}-handoff.mdresearch/{slug}/_working/interrogation-{orchestrator}-handoff.md
Preliminary interview handoffresearch/_working/preliminary-{orchestrator}-interview.mdresearch/{slug}/_working/preliminary-{orchestrator}-interview.md
Selected-set run manifestresearch/_working/{orchestrator}-run.yamlresearch/{slug}/_working/{orchestrator}-run.yaml
Framework working packet (staged)research/_working/preliminary-{framework}-research.mdresearch/{slug}/_working/preliminary-{framework}-research.md
Framework canonical intermediateresearch/{orchestrator}-{framework}.mdresearch/{slug}/{orchestrator}-{framework}.md
Synthesized canonicalresearch/{orchestrator}.mdresearch/{slug}/{orchestrator}.md

The preliminary interview handoff and the run manifest live under _working/ and inherit the existing throwaway-packet treatment. The interview handoff must be a complete context transfer — the next fresh session builds the framework selection page reading only this file, so it must carry: detected mode (e.g. pre-product vs product-exists), context summary, recommended framework subset with rationale, and any user answers that shape the selection.

Archive timing

Archive a source only when its consumer's output is committed, never when the next page is merely built:

Alignment page role

The alignment page does exactly what it already does well, and nothing more:

Routing: self-re-invocation

A self-advancing research orchestrator continues its loop by routing to its own re-invocation. After consuming approved YAML and writing the artifact for a gate, the confirmed-page handoff and the terminal message name the next command — the same orchestrator — and instruct the user to clear context and re-invoke:

✔ Framework 1 (W3 Hypothesis) confirmed and written.
## Next Work
Start a fresh context and let the parent run the next pending framework. Progress: 2 of 4 frameworks complete.

## Recommended Next Command
/customer-discovery

This is the defined mechanism for advancing the loop, and docs/alignment-yaml-routing-contract.md § Approved Artifact State recognizes a skill's own re-invocation as a valid post-approval route. The cross-skill downstream route is emitted only after final synthesis.

Post-canonical amendment routing

After final synthesis has been approved and the canonical research artifact has been written, bounded low/medium corrections may route to research-amend instead of restarting the whole Pattern A loop. Use this only for narrow amendments to existing canonical research, such as one missed competitor, one corrected source fact, or a small evidence update whose blast radius is known. High-impact or systemic drift still routes to the affected framework, synthesis, or full Pattern A rerun; examples include changed ICP/category strategy, a competitor set that changes positioning, broad source staleness, or conflicts spanning multiple frameworks.

Do not recommend research-amend while any Pattern A alignment page is still in review. Review-pending pages continue only through compiled YAML pasted into a fresh session; downstream or amendment commands become valid only after approved artifacts are written, or from separate research-health/status scans.

Self-routing continuation payload

Pattern A review pages should make the bottom compiled YAML self-routing data by beginning with # Invoke with: <parent-command> followed by a top-level command field and an agent_routing mapping. The invocation comment gives the user and fresh LLM agent an immediate visual cue, the root command gives parsers the exact parent invocation to run with the pasted YAML, and agent_routing gives enough context to route back to the parent orchestrator when the user pastes the YAML. The parent orchestrator still owns interpretation, state resolution, artifact writing, archiving, and inline framework loading.

Example for an inline framework findings gate:

# Invoke with: $competitive-analysis research/afps-tracker
command: "$competitive-analysis research/afps-tracker"
agent_routing:
  workflow: pattern-a-research-loop
  parent_skill: competitive-analysis
  command: "$competitive-analysis research/afps-tracker"
  product_path: research/afps-tracker
  gate_owner: parent-orchestrator
  gate_type: framework-findings
  framework_slug: swot
  framework_mode: inline-subskill
  run_manifest: research/afps-tracker/_working/competitive-analysis-run.yaml
  next_resolution: parent-resolves-from-yaml-and-filesystem

For non-framework gates, use the same mapping and set gate_type to the active gate (framework-selection, shortcut-selection, or synthesis); omit framework_slug and framework_mode when no framework is active. In flat mode, omit product_path or set it to an empty value. The top-level command and agent_routing.command must be the same parent-orchestrator command, including the same product/research path argument when present. It must never be a path-shaped child framework command.

agent_routing is routing metadata, not execution authority. The receiving parent still validates approval_status, identifies which gate the YAML answers, derives progress from the run manifest plus canonical-intermediate file existence, writes or amends the appropriate artifact, archives consumed sources, and decides whether to load a framework subskill inline. Do not put framework-owned commands, downstream-skill commands, $exec, or /exec in this mapping.

Terminal handoff format

Every terminal message for a Pattern A loop stop ends with the applicable sections below:

  1. ## Next Work — always present. For pending review approval gates, this is the final section and tells the user to review the page, compile responses, clear context, and paste the compiled YAML into a fresh session. For post-write handoffs, name the immediate next work state: run the next pending framework, build synthesis, or move to the first downstream skill after approved synthesis.
  2. ## Recommended Next Command — use only after approved YAML has been consumed and the approved artifact has been written or updated. Name the same parent orchestrator command while frameworks or synthesis remain; after final synthesis, name the first downstream command selected by the orchestrator's routing tree.

Do not place any other section after the applicable final section. If no command is appropriate after final synthesis, write No follow-up command recommended under ## Recommended Next Command.

For the last framework write, recalculate pending frameworks from the run manifest plus canonical-intermediate existence before writing the handoff. If no frameworks remain and the synthesized canonical artifact is missing, ## Next Work must say the next run builds the unified synthesis review page, and ## Recommended Next Command must name the parent synthesis route, for example /customer-discovery --synthesize or /customer-discovery --synthesize research/afps-tracker.

Framework subskills do not own user-facing routing. When a framework is run inline, its final handoff is still the parent orchestrator's handoff and must use the same two-section format with the parent command only.

Approval-boundary labels

Use two distinct labels so review pages do not hide the required continuation command:

While any review approval gate is pending, do not route to downstream or cross-skill work. Framework subskills remain downstream-route-free in loop mode: they do not emit Recommended next skill, path-shaped child framework commands, $exec, /exec, or downstream commands. The parent orchestrator is the only user-facing continuation command during framework loops.

Does the skill need to be re-invoked? Can the agent auto-call it?

Heavy vs. light phases

Isolate heavy phases (each consumes meaningful context): the deep interview, each framework's research pass, synthesis. Fold light operations into an adjacent session's head/tail:

If a run is small enough that a phase is trivially cheap (e.g. a 2-framework run), fold rather than spend a round-trip on a near-empty session. The unifying rule gives this judgment automatically: one heavy phase per session, light bookends.

Refinement and synthesis

Revision hygiene

When user feedback asks to remove, replace, or reduce emphasis on research content, revise the active working packet, alignment page, intermediate, or synthesis toward the requested final state. Keep corrected or rejected claims out of canonical findings, recommendations, framework outputs, synthesis narratives, and future-session instructions. If provenance matters, preserve it in a concise revision note or archive record rather than reintroducing it into the forward-facing research story.

Relationship to the other conventions

Migration status

This convention is normative for all Pattern A research orchestrators. The rollout is complete: all four reference orchestrators implement the session ladder (states 0–F), keep their state in the run manifest plus canonical-intermediate existence, and advance by self-re-invocation. New Pattern A research orchestrators follow this convention from creation.

Orchestrator Version Cold entry Run manifest
customer-discoveryv1.13state F (deep interview — context_intake: deep)research/{slug}/_working/customer-discovery-run.yaml
competitive-analysisv0.25state E (context_intake: scoped — no deep interview)research/{slug}/_working/competitive-analysis-run.yaml
positioningv0.23state E (context_intake: scoped — no deep interview)research/{slug}/_working/positioning-run.yaml
journey-mapv0.23state E (context_intake: scoped — no deep interview)research/{slug}/_working/journey-map-run.yaml

customer-discovery is the deep-intake reference (state F → E → C… → B → A); the three scoped-intake orchestrators have no deep-interview phase and resolve a cold start directly to state E. The adjacent afps-status reader (base, v0.5) is loop-aware: it reports "k of N frameworks complete" from the run manifest and routes a mid-run loop back to its orchestrator.

Rollout checklist (the steps each migration followed)

Each of customer-discovery, competitive-analysis, positioning, journey-map was migrated by:

  1. Replace the "write framework steps to tasks/todo.md; /exec drives" execution model with the session ladder (states 0–F).
  2. Replace the "Parent does not execute frameworks" constraint with "Parent self-advances one phase per invocation and follows the next pending framework subskill inline." The migrated SKILL.md should describe self-re-invocation positively (name the orchestrator's own command); it does not need to enumerate what not to route to.
  3. Add the deep-interview → preliminary-handoff → stop step (state F) and the build-multi-select-from-handoff step (state E).
  4. Add the selected-set run manifest write on multi-select approval (state 0/C head) and the file-existence pending-detection (state C).
  5. Update synthesis (state B) to archive the run manifest and emit the next-skill route on canonical write.
  6. Set terminal handoff routing to self-re-invocation: end every loop stop with ## Next Work plus the appropriate parent-owned command section.
  7. Bump version: per docs/skill-versioning.md (decimal bump — behavioral change), archive the prior SKILL.md, and add a CHANGELOG.md entry. Apply the same to each framework subskill when its parent-owned handoff contract changes; subskills remain single-framework staged-research skills and do not become user-facing commands.

Adjacent updates