OMH Deep Research — Multi-Phase Web Research with Citation Verification
When to Use
- The user asks for "deep research on", "a research report about",
"comprehensive research", "investigate X", "what's known about Y"
- omh-deep-interview encounters an unfamiliar domain and needs background
- omh-ralplan needs external context before it can plan responsibly
- The user's question requires synthesizing 3+ web sources into one
coherent answer, not a single search
When NOT to Use
- A confirmed report already exists at
.omh/research/{slug}-report.md
with status: confirmed for this topic — view it instead
- The question is answerable by a single web_search call
- The user wants real-time / current-events data only (this skill
emphasizes durable synthesis, not freshness)
- No
web toolset is available in this Hermes install (see Prerequisites)
Prerequisites
This skill fail-fasts if any of the following are missing:
Hermes discovery: hermes skills list | grep omh-deep-research should
return this skill once installed.
Installation (symlink for Hermes discovery)
mkdir -p ~/.hermes/skills/omh && ln -snf <repo>/plugins/omh/skills/omh-deep-research ~/.hermes/skills/omh/omh-deep-research
Parent dir creation MUST precede the symlink.
Architecture
Five invocation phases, exit-safe between any two:
| # |
Phase |
Reads |
Writes |
Subagents |
| 1 |
decompose |
user query |
{slug}-plan.md, state.phase=search |
none |
| 2 |
search |
plan, state, findings/ |
new findings file(s), state.completed_subtopics, phase |
1-3 [omh-role:researcher] parallel |
| 3 |
gap_check |
all findings |
optional _followup.md OR direct phase flip |
0 or 1 [omh-role:researcher] |
| 4 |
synthesize |
all findings (parent inlines) |
{slug}-report.md status: draft, phase=verify |
1 [omh-role:research-synthesist] |
| 5 |
verify |
report + findings (parent inlines) |
confirmed frontmatter / state mutate / blocked |
1 [omh-role:research-verifier] |
Per-instance state. Each research session lives at
.omh/state/research--{slug}.json (the engine slugifies instance_id
into the filename). Multiple topics can be in flight concurrently — the
slug minted in Phase 1 IS the instance_id, and every subsequent
omh_state(...) call passes instance_id="{slug}". Per-session
artifacts (plan, findings, report) are slug-keyed under
.omh/research/. The earlier research-{id}.json and
research-state.json (singleton) wordings from older specs are
superseded.
Parent owns the filesystem. All web tool use happens inside delegated
subagents. The parent reads findings files and inlines their contents
into synthesist's and verifier's context field. Subagents return
text only.
Roles are referenced, never inlined. Use [omh-role:NAME] markers.
The full role bodies live in plugins/omh/references/role-*.md.
Procedure
Phase 0: Check for Existing State and Sentinel
Before starting any new research session:
- Mint a candidate slug for the new request (see Phase 1 rule).
Call this
new_slug. We need it to disambiguate enumeration below.
- List existing research instances —
omh_state(action="list_instances", mode="research"). If the tool is
unavailable, glob .omh/state/research--*.json manually. Each entry
carries an instance_id (== slug) and active flag.
- For each active entry, run a cancel check first:
omh_state(action="cancel_check", mode="research", instance_id="{slug}").
If cancelled, log CANCELLED slug={slug} and clear that instance via
omh_state(action="clear", mode="research", instance_id="{slug}").
- Sentinel self-heal (recovery from crash between confirm and
clear). For each remaining active entry whose
.omh/research/{slug}-report.md has frontmatter status: confirmed,
the previous run crashed after writing the sentinel but before
clearing state. Treat as completed: log
REPORT_CONFIRMED_RECOVERED slug={slug}, clear via
omh_state(action="clear", mode="research", instance_id="{slug}").
- Topic-match resume — if any remaining active entry's
topic
matches the new request, jump directly to Phase 2/3/4/5 for THAT
slug (use its existing instance_id); do not mint new_slug.
- Already-confirmed for this topic — if a
{slug}-report.md
exists with status: confirmed matching the new topic, prompt:
refresh (mint a new slug and re-run) / view existing report / cancel.
- No conflict — proceed to Phase 1 with
new_slug. Concurrent
active research on different topics is permitted; do not block.
Phase 1: Decompose
- Cancel check:
omh_state(action="cancel_check", mode="research", instance_id="{slug}").
- Mint a slug — concrete rule:
slug = kebab(topic)[:40] + '-' + YYYYMMDD + '-' + random4
where random4 is 4 lowercase-hex chars.
kebab() = lowercase, replace runs of non-alphanumeric with -,
strip leading/trailing -, truncate to 40 chars.
- Decompose the user's topic into 3-5 subtopics. For each subtopic,
draft 2-3 candidate search queries.
- Write the plan atomically (tmp → fsync → rename) to
.omh/research/{slug}-plan.md with frontmatter:---
status: planning
topic: {original user topic}
slug: {slug}
subtopics:
- name: {subtopic 1 name}
queries: [{q1}, {q2}, {q3}]
- ...
---
- Initialize state via
omh_state(action="write", mode="research", instance_id="{slug}", data={...}):{
"phase": "search",
"slug": "{slug}",
"topic": "{topic}",
"subtopic_count": N,
"completed_subtopics": [],
"started_at": "{ISO-8601}",
"session_id": "{uuid4}",
"synthesis_attempts": 0
}
- Log
STARTED slug={slug} and PLAN_WRITTEN slug={slug} subtopics=N.
- Exit. Re-invocation will pick up at Phase 2 via the Phase 0 resume path.
Phase 2: Search (parallel batched, re-entrant)
This phase is re-entrant: it dispatches one batch of up to 3
researcher subagents per invocation, then exits. Re-invoke to dispatch
the next batch. Re-entry is driven by state.completed_subtopics.
- Cancel check:
omh_state(action="cancel_check", mode="research", instance_id="{slug}").
- Read state and the
{slug}-plan.md frontmatter.
- Compute
pending = [s for s in plan.subtopics if s.name not in state.completed_subtopics].
- Take the next
batch = pending[:3] (up to 3 in parallel).
- Dispatch ONE batch call with the
[omh-role:researcher] marker:delegate_task(tasks=[
{
"goal": "<self-contained: topic, subtopic name, exact queries to run, output template per [omh-role:researcher]>",
"context": "<plan excerpt for this subtopic; no other subagent's findings>",
},
...up to 3...
])
Each task's goal is fully self-contained — no inter-subagent
dependencies. The role marker [omh-role:researcher] MUST appear in
each goal so the subagent loads the role.
- (Strict write-order — NEVER reverse this order):
- Write all findings file(s) for this batch atomically
(tmp → fsync → rename). Each file lands at
.omh/research/{slug}-findings/{subtopic-slug}.md with
frontmatter capturing subtopic, source_urls, and credibility
tags pulled from the subagent's returned SOURCES block.
- Update
state.completed_subtopics (extend the list, persist via
omh_state(action="write", mode="research", instance_id="{slug}", data=...)).
- Exit.
- Phase transition. On the next invocation, Phase 0 routes back
here. If
pending becomes empty after the write, set
state.phase = "gap_check" BEFORE exiting (still after the
findings write — order: findings → completed_subtopics → phase flip).
- Log
BATCH_COMPLETE batch=N subtopics=[name1,name2,...] per batch.
Pitfalls specific to Phase 2:
- Dedup across subtopics. Two researchers may surface the same URL.
The synthesist (Phase 4) handles cross-subtopic dedup via global
Sources renumbering; Phase 2 does NOT need to dedup across files.
- Slug for findings filename. Use
kebab(subtopic.name)[:60]. If
two subtopics kebab to the same slug, append -2, -3, etc.
- Parent never calls
web_search or web_extract directly. All
web tool use is inside the delegated [omh-role:researcher]
subagents. The parent's job is dispatch and disk.
Phase 3: Gap Check (TWO branches only)
The parent skill never calls web_search or web_extract directly;
all web tool use happens inside delegated subagents.
Cancel check: omh_state(action="cancel_check", mode="research", instance_id="{slug}").
Read all .omh/research/{slug}-findings/*.md files. From each, extract
the GAPS: bullet list. Concatenate, then dedup lexically
(case-insensitive trim-compare; preserve first occurrence).
TWO branches only:
(a) 0 gaps — Set state.phase = "synthesize", log
GAP_CHECK_COMPLETE gaps=0, exit.
(b) ≥1 gap — Delegate ONE [omh-role:researcher] subagent.
Goal: synthetic "subtopic" named _followup, with the deduped gap
list as the queries. Parent writes the returned text to
.omh/research/{slug}-findings/_followup.md (atomic). Set
state.phase = "synthesize", log GAP_CHECK_COMPLETE gaps=N, exit.
No threshold tiers. No N-versus-M gap branching. No inline
web_search branch. Two branches only — that is the contract.
Phase 4: Synthesize (parent inlines findings; parent writes report)
Cancel check: omh_state(action="cancel_check", mode="research", instance_id="{slug}").
Parent INLINES findings. Read ALL files under
.omh/research/{slug}-findings/ (including _followup.md if
present). Concatenate their full contents into the delegation's
context field. The synthesist subagent has no filesystem access.
Budget escape (verified safe). If the concatenated payload
exceeds the orchestrator's tool-arg budget (≈40KB+ across 5+
findings files is a soft threshold), the parent MAY summarize
each findings file's SYNTHESIS section while preserving:
- The full SOURCES
[N] block verbatim (titles + URLs + tags + dates)
- All GAPS sections verbatim
- The
_followup block verbatim (it is usually the smallest and
most claim-dense)
Do NOT drop or paraphrase any URL, citation tag, or numeric claim.
Dogfooded 2026-04: a 5-subtopic + 1-followup run with summarized
SYNTHESIS bodies + verbatim source lists passed verification at
high confidence with all 28 globally-renumbered citations intact.
When in doubt, prefer full inline; summarize only when forced.
Dispatch ONE [omh-role:research-synthesist] task:
delegate_task(
goal="<self-contained: produce report per [omh-role:research-synthesist] template; topic={topic}; reference inlined plan + findings>",
context="<plan frontmatter + every findings file content, fully inlined>",
)
Retry context. If state.synthesis_attempts > 0, append the
prior verifier's REQUEST_CHANGES feedback (stored in
state.last_verifier_feedback) to the goal as:
Address these prior verifier findings:
{feedback}
Parent always overwrites .omh/research/{slug}-report.md with
the returned text. Frontmatter starts at status: draft. NO -v2
suffixing. Prior verdicts live only in state, not on disk.
C3 propagation. Parent does NOT edit the returned report. Any
(insufficient sources for this subtopic) strings remain verbatim.
Set state.phase = "verify", log REPORT_DRAFT, exit.
Phase 5: Verify (parent inlines; 3-strike gate; ordered confirm)
Cancel check: omh_state(action="cancel_check", mode="research", instance_id="{slug}").
Parent INLINES report + findings. Read {slug}-report.md AND
all {slug}-findings/*.md files. Concatenate BOTH into the
verifier delegation's context field. Verifier subagent has no
filesystem access.
Tools allowlist (A5). When dispatching, pass a tools allowlist
EXCLUDING write/edit/filesystem tools where Hermes supports
per-call tool scoping. If Hermes lacks per-call scoping, document
in Known Gaps and rely on the prose READ-ONLY contract in
role-research-verifier.md.
Dispatch ONE [omh-role:research-verifier] task. Parse the
returned VERDICT.
On VERDICT: PASS — STRICT ORDER (NEVER reverse):
- Write
{slug}-report.md with frontmatter status: confirmed (atomic; idempotent sentinel; THIS is the source-of-truth and must land FIRST).
- Append
REPORT_CONFIRMED slug={slug} to the event log.
- Clear state via
omh_state(action="clear", mode="research", instance_id="{slug}").
- Print summary to user; exit.
Phase 0 self-heals if a crash occurs between step 1 and step 3 (it
detects the confirmed sentinel and clears the orphan state).
On VERDICT: FAIL with state.synthesis_attempts < 3:
- Increment
state.synthesis_attempts.
- Store the verifier's REQUEST_CHANGES verdict (full body) in
state.last_verifier_feedback.
- Set
state.phase = "synthesize".
- Log
VERIFY_FAIL slug={slug} and SYNTHESIS_RETRY attempt={N}.
- Exit. Re-invocation re-runs Phase 4 with feedback context.
On VERDICT: FAIL with state.synthesis_attempts == 3:
- Set
state.phase = "blocked".
- Surface the verifier's gap list to the user.
- Log
VERIFY_FAIL slug={slug} and BLOCKED_RETRIES_EXHAUSTED slug={slug}.
- Exit. State is RETAINED so the user can inspect or escalate
(Phase 0 will not auto-restart a blocked session).
Logging
Append-only events to .omh/logs/research-{session_id}.log. Events are
decisions and phase transitions only — never findings content (matches
the omh-deep-interview convention).
Documented event vocabulary:
STARTED slug={slug}
PLAN_WRITTEN slug={slug} subtopics=N
BATCH_COMPLETE batch=N subtopics=[...]
GAP_CHECK_COMPLETE gaps=N
REPORT_DRAFT
VERIFY_PASS
VERIFY_FAIL
SYNTHESIS_RETRY attempt=N
BLOCKED_RETRIES_EXHAUSTED slug={slug}
REPORT_CONFIRMED slug={slug}
REPORT_CONFIRMED_RECOVERED slug={slug}
BLOCKED slug={slug}
CANCELLED
Sentinel
Downstream skills (omh-deep-interview, omh-ralplan, omh-autopilot) detect
a completed research session by:
.omh/research/{slug}-report.md with frontmatter `status: confirmed`
This file is the durable contract. State is ephemeral; the sentinel is
the source of truth.
Pitfalls
- Never call
web_search or web_extract from the parent. All web
tool use happens inside delegated [omh-role:researcher] subagents.
- Never inline role text. Use
[omh-role:NAME] markers; bodies live
in plugins/omh/references/role-*.md.
- Phase boundaries are commit points. Each phase MUST exit cleanly
after writing its outputs and updating state. Long-running phases
that span multiple delegations are not exit-safe.
- One active research session per project. Phase 0 enforces this.
Don't create parallel research states.
- Slug collisions are user-visible. The
random4 suffix keeps
same-topic same-day re-runs from clobbering each other.
Known Gaps
- Persistence to wiki / fact_store / memory is not yet integrated.
The sentinel report is the only durable interface in v1. (Q2)
- Per-call subagent tool scoping for
[omh-role:research-verifier]
may be unavailable depending on Hermes install; READ-ONLY contract is
enforced by prose in role-research-verifier.md in that case. (A5)
1---2name: omh-deep-research3description: parallel web research; subagents→synthesis→cite-verify4---56# OMH Deep Research — Multi-Phase Web Research with Citation Verification78## When to Use910- The user asks for "deep research on", "a research report about",11 "comprehensive research", "investigate X", "what's known about Y"12- omh-deep-interview encounters an unfamiliar domain and needs background13- omh-ralplan needs external context before it can plan responsibly14- The user's question requires synthesizing 3+ web sources into one15 coherent answer, not a single search1617## When NOT to Use1819- A confirmed report already exists at `.omh/research/{slug}-report.md`20 with `status: confirmed` for this topic — view it instead21- The question is answerable by a single web_search call22- The user wants real-time / current-events data only (this skill23 emphasizes durable synthesis, not freshness)24- No `web` toolset is available in this Hermes install (see Prerequisites)2526## Prerequisites2728This skill fail-fasts if any of the following are missing:2930- **`web` toolset** — provides `web_search` AND `web_extract`. If either31 is unavailable, print:32 ```33 omh-deep-research requires the `web` toolset (web_search + web_extract); aborting.34 ```35 and exit before doing any work.36- **`omh_state` tool** — preferred path. If absent, fall back to manual37 JSON read/write at `.omh/state/research--{slug}.json` (per-instance —38 `{slug}` is the kebab+date+random4 minted in Phase 1; multiple research39 topics can run concurrently). If neither is writable, fail-fast with a40 clear error.41- **Write access** to `.omh/` for state, plan, findings, report, log.4243Hermes discovery: `hermes skills list | grep omh-deep-research` should44return this skill once installed.4546### Installation (symlink for Hermes discovery)4748```49mkdir -p ~/.hermes/skills/omh && ln -snf <repo>/plugins/omh/skills/omh-deep-research ~/.hermes/skills/omh/omh-deep-research50```5152Parent dir creation MUST precede the symlink.5354## Architecture5556Five invocation phases, exit-safe between any two:5758| # | Phase | Reads | Writes | Subagents |59|---|-----------|----------------------------------------|--------------------------------------------------------------|-----------|60| 1 | decompose | user query | `{slug}-plan.md`, state.phase=search | none |61| 2 | search | plan, state, findings/ | new findings file(s), state.completed_subtopics, phase | 1-3 `[omh-role:researcher]` parallel |62| 3 | gap_check | all findings | optional `_followup.md` OR direct phase flip | 0 or 1 `[omh-role:researcher]` |63| 4 | synthesize| all findings (parent inlines) | `{slug}-report.md` `status: draft`, phase=verify | 1 `[omh-role:research-synthesist]` |64| 5 | verify | report + findings (parent inlines) | confirmed frontmatter / state mutate / blocked | 1 `[omh-role:research-verifier]` |6566**Per-instance state.** Each research session lives at67`.omh/state/research--{slug}.json` (the engine slugifies `instance_id`68into the filename). Multiple topics can be in flight concurrently — the69slug minted in Phase 1 IS the instance_id, and every subsequent70`omh_state(...)` call passes `instance_id="{slug}"`. Per-session71artifacts (plan, findings, report) are slug-keyed under72`.omh/research/`. The earlier `research-{id}.json` and73`research-state.json` (singleton) wordings from older specs are74superseded.7576**Parent owns the filesystem.** All web tool use happens inside delegated77subagents. The parent reads findings files and inlines their contents78into synthesist's and verifier's `context` field. Subagents return79text only.8081**Roles are referenced, never inlined.** Use `[omh-role:NAME]` markers.82The full role bodies live in `plugins/omh/references/role-*.md`.8384## Procedure8586### Phase 0: Check for Existing State and Sentinel8788Before starting any new research session:89901. **Mint a candidate slug** for the new request (see Phase 1 rule).91 Call this `new_slug`. We need it to disambiguate enumeration below.922. **List existing research instances** —93 `omh_state(action="list_instances", mode="research")`. If the tool is94 unavailable, glob `.omh/state/research--*.json` manually. Each entry95 carries an `instance_id` (== slug) and `active` flag.963. **For each active entry**, run a cancel check first:97 `omh_state(action="cancel_check", mode="research", instance_id="{slug}")`.98 If cancelled, log `CANCELLED slug={slug}` and clear that instance via99 `omh_state(action="clear", mode="research", instance_id="{slug}")`.1004. **Sentinel self-heal (recovery from crash between confirm and101 clear).** For each remaining active entry whose102 `.omh/research/{slug}-report.md` has frontmatter `status: confirmed`,103 the previous run crashed after writing the sentinel but before104 clearing state. Treat as completed: log105 `REPORT_CONFIRMED_RECOVERED slug={slug}`, clear via106 `omh_state(action="clear", mode="research", instance_id="{slug}")`.1075. **Topic-match resume** — if any remaining active entry's `topic`108 matches the new request, jump directly to Phase 2/3/4/5 for THAT109 slug (use its existing `instance_id`); do not mint `new_slug`.1106. **Already-confirmed for this topic** — if a `{slug}-report.md`111 exists with `status: confirmed` matching the new topic, prompt:112 refresh (mint a new slug and re-run) / view existing report / cancel.1137. **No conflict** — proceed to Phase 1 with `new_slug`. Concurrent114 active research on different topics is permitted; do not block.115116### Phase 1: Decompose1171181. Cancel check: `omh_state(action="cancel_check", mode="research", instance_id="{slug}")`.1192. **Mint a slug** — concrete rule:120 `slug = kebab(topic)[:40] + '-' + YYYYMMDD + '-' + random4`121 where `random4` is 4 lowercase-hex chars.122 `kebab()` = lowercase, replace runs of non-alphanumeric with `-`,123 strip leading/trailing `-`, truncate to 40 chars.1243. Decompose the user's topic into 3-5 subtopics. For each subtopic,125 draft 2-3 candidate search queries.1264. **Write the plan** atomically (tmp → fsync → rename) to127 `.omh/research/{slug}-plan.md` with frontmatter:128 ```129 ---130 status: planning131 topic: {original user topic}132 slug: {slug}133 subtopics:134 - name: {subtopic 1 name}135 queries: [{q1}, {q2}, {q3}]136 - ...137 ---138 ```1395. **Initialize state** via `omh_state(action="write", mode="research", instance_id="{slug}", data={...})`:140 ```141 {142 "phase": "search",143 "slug": "{slug}",144 "topic": "{topic}",145 "subtopic_count": N,146 "completed_subtopics": [],147 "started_at": "{ISO-8601}",148 "session_id": "{uuid4}",149 "synthesis_attempts": 0150 }151 ```1526. Log `STARTED slug={slug}` and `PLAN_WRITTEN slug={slug} subtopics=N`.1537. Exit. Re-invocation will pick up at Phase 2 via the Phase 0 resume path.154155### Phase 2: Search (parallel batched, re-entrant)156157This phase is **re-entrant**: it dispatches one batch of up to 3158researcher subagents per invocation, then exits. Re-invoke to dispatch159the next batch. Re-entry is driven by `state.completed_subtopics`.1601611. Cancel check: `omh_state(action="cancel_check", mode="research", instance_id="{slug}")`.1622. Read state and the `{slug}-plan.md` frontmatter.1633. Compute `pending = [s for s in plan.subtopics if s.name not in state.completed_subtopics]`.1644. Take the next `batch = pending[:3]` (up to 3 in parallel).1655. **Dispatch ONE batch call** with the `[omh-role:researcher]` marker:166 ```167 delegate_task(tasks=[168 {169 "goal": "<self-contained: topic, subtopic name, exact queries to run, output template per [omh-role:researcher]>",170 "context": "<plan excerpt for this subtopic; no other subagent's findings>",171 },172 ...up to 3...173 ])174 ```175 Each task's `goal` is fully self-contained — no inter-subagent176 dependencies. The role marker `[omh-role:researcher]` MUST appear in177 each goal so the subagent loads the role.1786. **(Strict write-order — NEVER reverse this order):**179 1. Write all findings file(s) for this batch atomically180 (tmp → fsync → rename). Each file lands at181 `.omh/research/{slug}-findings/{subtopic-slug}.md` with182 frontmatter capturing `subtopic`, `source_urls`, and credibility183 tags pulled from the subagent's returned SOURCES block.184 2. Update `state.completed_subtopics` (extend the list, persist via185 `omh_state(action="write", mode="research", instance_id="{slug}", data=...)`).186 3. Exit.1877. **Phase transition.** On the next invocation, Phase 0 routes back188 here. If `pending` becomes empty after the write, set189 `state.phase = "gap_check"` BEFORE exiting (still after the190 findings write — order: findings → completed_subtopics → phase flip).1918. Log `BATCH_COMPLETE batch=N subtopics=[name1,name2,...]` per batch.192193**Pitfalls specific to Phase 2:**194195- **Dedup across subtopics.** Two researchers may surface the same URL.196 The synthesist (Phase 4) handles cross-subtopic dedup via global197 Sources renumbering; Phase 2 does NOT need to dedup across files.198- **Slug for findings filename.** Use `kebab(subtopic.name)[:60]`. If199 two subtopics kebab to the same slug, append `-2`, `-3`, etc.200- **Parent never calls `web_search` or `web_extract` directly.** All201 web tool use is inside the delegated `[omh-role:researcher]`202 subagents. The parent's job is dispatch and disk.203204### Phase 3: Gap Check (TWO branches only)205206The parent skill never calls `web_search` or `web_extract` directly;207all web tool use happens inside delegated subagents.2082091. Cancel check: `omh_state(action="cancel_check", mode="research", instance_id="{slug}")`.2102. Read all `.omh/research/{slug}-findings/*.md` files. From each, extract211 the `GAPS:` bullet list. Concatenate, then **dedup lexically**212 (case-insensitive trim-compare; preserve first occurrence).2133. **TWO branches only:**214215 - **(a) 0 gaps** — Set `state.phase = "synthesize"`, log216 `GAP_CHECK_COMPLETE gaps=0`, exit.217218 - **(b) ≥1 gap** — Delegate ONE `[omh-role:researcher]` subagent.219 Goal: synthetic "subtopic" named `_followup`, with the deduped gap220 list as the queries. Parent writes the returned text to221 `.omh/research/{slug}-findings/_followup.md` (atomic). Set222 `state.phase = "synthesize"`, log `GAP_CHECK_COMPLETE gaps=N`, exit.223224 No threshold tiers. No N-versus-M gap branching. No inline225 web_search branch. Two branches only — that is the contract.226227### Phase 4: Synthesize (parent inlines findings; parent writes report)2282291. Cancel check: `omh_state(action="cancel_check", mode="research", instance_id="{slug}")`.2302. **Parent INLINES findings.** Read ALL files under231 `.omh/research/{slug}-findings/` (including `_followup.md` if232 present). Concatenate their full contents into the delegation's233 `context` field. The synthesist subagent has no filesystem access.234235 **Budget escape (verified safe).** If the concatenated payload236 exceeds the orchestrator's tool-arg budget (≈40KB+ across 5+237 findings files is a soft threshold), the parent MAY summarize238 each findings file's SYNTHESIS section while preserving:239 - The full SOURCES `[N]` block verbatim (titles + URLs + tags + dates)240 - All GAPS sections verbatim241 - The `_followup` block verbatim (it is usually the smallest and242 most claim-dense)243 Do NOT drop or paraphrase any URL, citation tag, or numeric claim.244 Dogfooded 2026-04: a 5-subtopic + 1-followup run with summarized245 SYNTHESIS bodies + verbatim source lists passed verification at246 high confidence with all 28 globally-renumbered citations intact.247 When in doubt, prefer full inline; summarize only when forced.2483. Dispatch ONE `[omh-role:research-synthesist]` task:249 ```250 delegate_task(251 goal="<self-contained: produce report per [omh-role:research-synthesist] template; topic={topic}; reference inlined plan + findings>",252 context="<plan frontmatter + every findings file content, fully inlined>",253 )254 ```2554. **Retry context.** If `state.synthesis_attempts > 0`, append the256 prior verifier's REQUEST_CHANGES feedback (stored in257 `state.last_verifier_feedback`) to the goal as:258 ```259 Address these prior verifier findings:260 {feedback}261 ```2625. **Parent always overwrites** `.omh/research/{slug}-report.md` with263 the returned text. Frontmatter starts at `status: draft`. NO `-v2`264 suffixing. Prior verdicts live only in state, not on disk.2656. **C3 propagation.** Parent does NOT edit the returned report. Any266 `(insufficient sources for this subtopic)` strings remain verbatim.2677. Set `state.phase = "verify"`, log `REPORT_DRAFT`, exit.268269### Phase 5: Verify (parent inlines; 3-strike gate; ordered confirm)2702711. Cancel check: `omh_state(action="cancel_check", mode="research", instance_id="{slug}")`.2722. **Parent INLINES report + findings.** Read `{slug}-report.md` AND273 all `{slug}-findings/*.md` files. Concatenate BOTH into the274 verifier delegation's `context` field. Verifier subagent has no275 filesystem access.2763. **Tools allowlist (A5).** When dispatching, pass a tools allowlist277 EXCLUDING write/edit/filesystem tools where Hermes supports278 per-call tool scoping. If Hermes lacks per-call scoping, document279 in Known Gaps and rely on the prose READ-ONLY contract in280 `role-research-verifier.md`.2814. Dispatch ONE `[omh-role:research-verifier]` task. Parse the282 returned VERDICT.2832845. **On VERDICT: PASS — STRICT ORDER (NEVER reverse):**285 1. Write `{slug}-report.md` with frontmatter `status: confirmed` (atomic; idempotent sentinel; THIS is the source-of-truth and must land FIRST).286 2. Append `REPORT_CONFIRMED slug={slug}` to the event log.287 3. Clear state via `omh_state(action="clear", mode="research", instance_id="{slug}")`.288 4. Print summary to user; exit.289290 Phase 0 self-heals if a crash occurs between step 1 and step 3 (it291 detects the confirmed sentinel and clears the orphan state).2922936. **On VERDICT: FAIL with `state.synthesis_attempts < 3`:**294 - Increment `state.synthesis_attempts`.295 - Store the verifier's REQUEST_CHANGES verdict (full body) in296 `state.last_verifier_feedback`.297 - Set `state.phase = "synthesize"`.298 - Log `VERIFY_FAIL slug={slug}` and `SYNTHESIS_RETRY attempt={N}`.299 - Exit. Re-invocation re-runs Phase 4 with feedback context.3003017. **On VERDICT: FAIL with `state.synthesis_attempts == 3`:**302 - Set `state.phase = "blocked"`.303 - Surface the verifier's gap list to the user.304 - Log `VERIFY_FAIL slug={slug}` and `BLOCKED_RETRIES_EXHAUSTED slug={slug}`.305 - Exit. State is RETAINED so the user can inspect or escalate306 (Phase 0 will not auto-restart a blocked session).307308## Logging309310Append-only events to `.omh/logs/research-{session_id}.log`. Events are311decisions and phase transitions only — never findings content (matches312the omh-deep-interview convention).313314Documented event vocabulary:315316- `STARTED slug={slug}`317- `PLAN_WRITTEN slug={slug} subtopics=N`318- `BATCH_COMPLETE batch=N subtopics=[...]`319- `GAP_CHECK_COMPLETE gaps=N`320- `REPORT_DRAFT`321- `VERIFY_PASS`322- `VERIFY_FAIL`323- `SYNTHESIS_RETRY attempt=N`324- `BLOCKED_RETRIES_EXHAUSTED slug={slug}`325- `REPORT_CONFIRMED slug={slug}`326- `REPORT_CONFIRMED_RECOVERED slug={slug}`327- `BLOCKED slug={slug}`328- `CANCELLED`329330## Sentinel331332Downstream skills (omh-deep-interview, omh-ralplan, omh-autopilot) detect333a completed research session by:334335```336.omh/research/{slug}-report.md with frontmatter `status: confirmed`337```338339This file is the durable contract. State is ephemeral; the sentinel is340the source of truth.341342## Pitfalls343344- **Never call `web_search` or `web_extract` from the parent.** All web345 tool use happens inside delegated `[omh-role:researcher]` subagents.346- **Never inline role text.** Use `[omh-role:NAME]` markers; bodies live347 in `plugins/omh/references/role-*.md`.348- **Phase boundaries are commit points.** Each phase MUST exit cleanly349 after writing its outputs and updating state. Long-running phases350 that span multiple delegations are not exit-safe.351- **One active research session per project.** Phase 0 enforces this.352 Don't create parallel research states.353- **Slug collisions are user-visible.** The `random4` suffix keeps354 same-topic same-day re-runs from clobbering each other.355356## Known Gaps357358- **Persistence to wiki / fact_store / memory** is not yet integrated.359 The sentinel report is the only durable interface in v1. (Q2)360- **Per-call subagent tool scoping for `[omh-role:research-verifier]`**361 may be unavailable depending on Hermes install; READ-ONLY contract is362 enforced by prose in `role-research-verifier.md` in that case. (A5)