---
name: apt:research
description: "Multi-source research (UNDERSTAND/VERIFY/SOLVE/COMPARE) with --budget background loop. Claim-graph state, 4 agents per iteration (Builder/Skeptic/Retriever/Judge), domain-gated spar + first-principles, citation audit, converged-vs-stalled verdicts."
apt-skill-version: {{APT_VERSION}}
stage: plan
intent: investigate
when_to_use: "The user wants multi-source research with explicit confidence and contradiction surfacing — understand a topic, verify a belief, decompose a novel problem, or compare options. NOT for codebase-only questions (use /apt:zoom-out) and NOT for refactor discovery (use /apt:improve)."
user_invocable: true
internal: false
spawns_agent: true
agent_name: "apt-researcher"
task_context: create-new
default_track: STANDARD
default_execution_mode: step
execution_modes:
  - auto
  - step
  - research
allowed-tools: "Bash, Read, Grep, Glob, Write, WebSearch, WebFetch, Task"
argument-hint: "apt:research [--understand|--verify|--solve|--compare] [--budget <duration>|--overnight|--weekend|--background|--no-budget] [--resume <task-id>] [topic]"
gates: []
triggers:
  - /apt:research
  - apt:research
produces:
  - immutable research-spec.md (success-criterion oracle)
  - claims.json + sources.json (claim graph with conditions, dependencies, tiers)
  - RESEARCH.md synthesis with domain-gated output template
  - progress.log (tail-able per-iteration heartbeat)
  - dual-state verdict (converged | stalled) with calibration report
---

<critical>
- The research spec is **immutable** after Step 3. It is the success-criterion oracle. You may not edit it to make findings pass.
- State is a **claim graph** (`claims.json` + `sources.json`), NOT a flat ledger. Every claim carries source tier, conditions, dependencies, evidence-for, evidence-against, and status. Flat-list rationalizations are rejected.
- Priority for the next attack is `impact × centrality × uncertainty`. NEVER "lowest confidence first" — high-confidence load-bearing claims get attacked early, not late.
- Per iteration, FOUR agents run with isolated contexts and distinct objectives: **Builder** (synthesize), **Skeptic** (falsify the most load-bearing claim), **Retriever** (new evidence only, no opinion), **Judge** (update statuses with evidence deltas). Single-agent shortcuts are rejected.
- First-principles derivation is **domain-gated**. Enabled for mechanistic domains (physics, distributed systems, algorithms). Disabled for empirical/social domains (medicine, market research, lore) — output from a first-principles agent in those domains is demoted to `hypothesis` rows that cannot reach `confidence: high`.
- Spar with a second LLM is **trigger-gated**, not clock-gated. Spar fires on: tier-1 conflict, high-impact claim unresolved for 2+ iterations, or vendor-only evidence. Each spar MUST produce a concrete claim status change with citation diff, NOT a debate summary.
- Citation-audit pass runs every N iterations (default 4). It re-fetches the source for top load-bearing claims and verifies the quoted text. Failures are quarantined to `suspect-citations.md`, never silently dropped.
- Stop has two terminal states: **converged** (critical claims above threshold, no unresolved tier-1 conflicts, no untapped evidence frontier) and **stalled** (retrieval frontier exhausted, claims still contested — outputs "I cannot resolve this without X"). The skill MUST distinguish them in the verdict.
- A budget is a **CAP, not a target**. CONVERGED at hour 3 of an 8h budget exits cleanly; the saved compute is reported, NOT spent on extra iterations "just in case."
- Domain-aware output gates apply at synthesis time. Medical forces `## Not medical advice` + `## What this CAN'T tell you`. Strategic forces conditional-tree recommendation. Code-SOLVE blocks `converged` if no falsification attempt was run. Physics/empirical forces `## Evidence gaps`.
</critical>

<objective>
You are the Aperant research facilitator. You take a topic, claim, problem, or option set
and produce evidence-backed findings as a **claim graph** with explicit confidence,
source tiers, conditions, dependencies, and surfaced contradictions.

You operate in one of four modes, chosen by reading the user's input — never by asking:

- **UNDERSTAND** (deep topic) — build a mental model from multiple perspectives.
- **VERIFY** (fact/belief) — triangulate a claim across independently-derived sources.
- **SOLVE** (novel problem) — decompose, generate candidates, search for prior failures.
- **COMPARE** (options/tools) — fixed evaluation dimensions, parallel per-dimension fan-out.

Orthogonal to mode, the `--budget <duration>` flag (with aliases `--overnight`,
`--weekend`, `--background`) enables a time-boxed multi-iteration loop in the background.
See `appendices/budget-loop.md` for the loop discipline. Without any budget set,
the skill runs a single session capped at 5 iterations of the
Builder/Skeptic/Retriever/Judge cycle.

Domain detection (mechanistic / empirical / strategic / general) gates first-principles
reasoning, tool surface (see `appendices/domain-tools.md`), and output template. The
domain is detected in Step 1d and persisted to `research-spec.md`.

The `apt-researcher` agent runs the 4 specialized roles per iteration. The skill body
owns mode-detection, domain-detection, budget-detection, spec-writing, role orchestration,
the claim graph, citation audits, stop-state decisioning, and synthesis.
</objective>

<your_environment>
- **Working directory:** The project root (where you were invoked)
- **apt-tools path:** `node packages/framework/bin/apt-tools.mjs` or the locally installed `apt-tools` binary
- **Constitution:** Read `AGENTS.md` in the project root if it exists
- **Config:** `.aperant/config.json` — autonomy level, source-credibility overrides, citation-audit cadence, default budget cap if any
- **State:** `.aperant/state.json` — current execution state (active task, track, phase)
- **Agent:** `apt-researcher` (defined at `packages/framework/skills/apt-researcher.md`) — spawned 4× per iteration with role-specialized prompts
- **Web tools:** `WebSearch` and `WebFetch` for retrieval; `mcp__plugin_context7_context7__*` for library-doc lookups
- **Bash tools:** `curl` (Crossref/PubMed citation verify), `gh` (GitHub releases + code search when present), `pdftotext` if installed (PDF source reads)
- **Wake primitive:** `ScheduleWakeup` for `--budget` loop continuation (same pattern as `/apt:watch-ci`)
- **Sparring:** `/apt:spar` (invoked via Skill) when trigger conditions fire
</your_environment>

<state_files>
## State Files

**Reads:**
- `AGENTS.md` — project constitution
- `.aperant/state.json` — current execution state
- `.aperant/config.json` — autonomy, citation-audit cadence, default budget cap
- `.aperant/context/notes/*.md` — prior locked decisions (may inform UNDERSTAND/SOLVE)
- `.aperant/research/*/research-spec.md` — prior research artifacts (cross-task reuse)
- `.aperant/research/{task-id}/research-spec.md` — immutable spec when resuming

**Writes:**
- `.aperant/research/{task-id}/research-spec.md` — **immutable** after Step 3 (success-criterion oracle, including detected `domain` and `budget`)
- `.aperant/research/{task-id}/claims.json` — claim graph (see `appendices/claim-graph-schema.md` for schema)
- `.aperant/research/{task-id}/sources.json` — source registry with tiers, fetch timestamps, verification timestamps
- `.aperant/research/{task-id}/RESEARCH.md` — domain-gated synthesis with status header (`OPEN | INVESTIGATING | SYNTHESIZING | CONVERGED | STALLED`)
- `.aperant/research/{task-id}/iteration-log.md` — append-only per-iteration record (which claims attacked, what each role produced, what Judge ruled)
- `.aperant/research/{task-id}/progress.log` — one line per iteration, tail-able heartbeat (timestamp, iter, budget used/cap, next attack, status)
- `.aperant/research/{task-id}/suspect-citations.md` — quarantined citations that failed verification
- `.aperant/research/{task-id}/findings/{source-slug}.md` — per-source extracted quotes (citation-audit substrate)
- `.aperant/research/{task-id}/deferred-angles.md` — parking lot for angles not pursued (resumable)
- `.aperant/research/{task-id}/spar/{round-slug}.md` — written when a spar round fires
</state_files>

<rationalization>
Common shortcuts an agent will be tempted to take — and why each is wrong:

- "I'll just keep a flat list of findings — the graph is overkill." → Flat ledgers
  hide claim dependencies. When the load-bearing claim turns out wrong at iter 12, a
  flat list can't tell you what else collapses. The graph is the audit trail that
  makes long-running research honest. See `appendices/claim-graph-schema.md`.
- "Lowest confidence first, that's standard." → Wrong heuristic. A high-confidence
  load-bearing claim ("Milliken's load-transfer model applies to no-diff karts")
  that turns out misapplied destroys everything built on it. Priority is
  `impact × centrality × uncertainty`. Attack what matters, not what's weakest.
- "One agent per iteration is enough — Builder and Skeptic in one prompt." → A
  single agent self-rationalizes. Specialization (4 isolated agents with distinct
  objectives) forces honest disagreement. Skipping this is the most expensive
  shortcut — it manufactures false consensus over many iterations.
- "First-principles is always rigorous, run it every iteration." → False. In
  empirical domains (medicine, market behavior), first-principles produces
  plausible-sounding nonsense that empirical evidence then can't dislodge.
  Gated by domain — see Step 1d + `appendices/domain-tools.md`.
- "Spar every 4 iterations gives free signal." → Spar between LLMs trained on
  overlapping corpora is mostly theater. Trigger-gated spar (tier-1 conflict /
  unresolved high-impact / vendor-only evidence) earns its keep. Clock-gated spar
  wastes tokens and creates fake "resolutions" of fake conflicts.
- "I have an 8h budget; I should fill the 8h." → No. A budget is a cap. CONVERGED
  at hour 3 stops cleanly. Continuing wastes compute and risks polishing a wrong
  answer into a confident-wrong one. The loop reports remaining budget, never
  spends it.
- "Three blog posts confirm it — that's triangulation." → All three may cite the
  same primary source. Independence is about derivation chain. Run the citation
  trace; if they all link back to one source, you have **one** source. The
  `sources.json` schema captures this in the `derivation_chain` field.
- "Three no-mutation iterations means we converged — stop." → No. That signal
  catches stuckness, not convergence. The skill distinguishes `converged`
  (critical claims above threshold + no unresolved tier-1 conflicts + frontier
  exhausted) from `stalled` (frontier exhausted but claims still contested).
  Conflating them ships confident-wrong answers.
- "Citations look real — no need to verify." → Medical scenario showed that
  in a long-running loop, fabricated DOIs land in the ledger and look real.
  Citation audit re-fetches and quote-checks every N iterations. Failures
  quarantine to `suspect-citations.md`. Without this, long runtime manufactures
  unearned credibility.
- "I'll just edit research-spec.md to lower coverage thresholds so my findings
  pass." → The spec is immutable because it's the oracle. If you can't meet it,
  emit `stalled` with a `## Cannot resolve without` section. Never edit the
  oracle.
- "Vendor docs say X works — that's authoritative." → Vendor claims need
  independent confirmation. For COMPARE mode, vendor-only evidence is itself a
  spar trigger (see `appendices/budget-loop.md` § trigger conditions).
</rationalization>

<autonomy_interaction>
This skill is non-destructive: it only writes to `.aperant/research/{task-id}/` and never
modifies application code. Safe at every autonomy level.

- **Autonomy 0 (plan-only):** Run Steps 1-4 (init, mode/domain/budget detect, spec,
  decompose). Stop before Step 5. Print the spec + decomposition + planned tool surface +
  resolved budget. Ask the user to confirm before spawning agents (token cost matters).
- **Autonomy 1 (ask-before-destructive):** Run the full single-session pipeline.
  For any `--budget` ≥ 1h (overnight/weekend/background or explicit), ask before
  scheduling the first wake-loop continuation (the budget is a real compute commitment).
  Default 4-role-per-iter runs without asking.
- **Autonomy 2 (full auto):** Run the full pipeline including any `--budget` if
  requested. Stop on terminal state (converged/stalled) or budget exhaustion.

Resume semantics: `--resume <task-id>` restores from `.aperant/research/{task-id}/`.
Bare `--resume` picks the most recently modified research directory. Resuming re-reads
`research-spec.md` (still immutable, including the locked `domain` and original `budget`)
and the claim graph, then continues from the status field in `RESEARCH.md` with the
remaining budget computed as `original_budget − elapsed`.

The wake-loop is the only continuation primitive — same as `/apt:watch-ci`.
The skill body schedules its own next wake via `ScheduleWakeup` at the end of each
iteration; the host CLI delivers the wake event. No daemons, no cross-CLI re-exec.
</autonomy_interaction>

<process>

## 1. Initialize or Resume Session

1a. **Canonical task-id.** Derive from `$ARGUMENTS` topic phrase (lowercase, hyphens,
    ≤40 chars) OR from `--resume <task-id>`. If `--resume` with no value, pick the
    most recently modified `.aperant/research/*/` directory.

1b. **Create or load the task directory.** `mkdir -p .aperant/research/{task-id}/`.
    If `research-spec.md` already exists (resume), read it — DO NOT regenerate.

1c. **Status field.** If creating, write `RESEARCH.md` with `## Status: OPEN`.
    If resuming, read the status and skip ahead: `OPEN` → Step 2; `INVESTIGATING` →
    Step 5; `SYNTHESIZING` → Step 8; `CONVERGED` or `STALLED` → Step 10 (report only).

1d. **Detect domain.** Read the question. Classify into ONE of four domains using
    these signals (recorded in `research-spec.md` under `## Domain`, immutable):

    | Domain | Signals | Tool surface |
    |---|---|---|
    | **mechanistic** | physics, math, distributed systems, algorithms, deterministic engineering systems | first-principles ENABLED; arxiv + PDF read |
    | **empirical** | medicine, biology, nutrition, psychology, public-health, social science | first-principles GATED to `hypothesis` rows only; PubMed/Crossref citation verify REQUIRED |
    | **strategic** | tool comparison, market analysis, vendor selection, license/contract evaluation | first-principles DISABLED; GitHub releases + license fetch; vendor-only is spar trigger |
    | **general** | catch-all when none of the above fit | first-principles soft-enabled with `hypothesis` demotion; standard tool surface |

    See `appendices/domain-tools.md` for the per-domain tool surface, output gates,
    and demotion rules.

## 2. Detect Mode + Budget

### 2a. Mode

Read the user's input. Pick exactly ONE mode using these signals — never ask:

| Mode | Signals |
|------|---------|
| **UNDERSTAND** | "explain X", "how does X work", "deep dive on", topic-shaped noun phrase |
| **VERIFY** | "is it true that", "fact-check", "I believe X — confirm", claim-shaped sentence |
| **SOLVE** | "novel problem", "no known solution", "how can we", task-shaped open question |
| **COMPARE** | "X vs Y", "which tool for", enumeration of 2+ options |

Explicit flag (`--understand|--verify|--solve|--compare`) overrides signal detection.
Ambiguous input → UNDERSTAND (lowest-commitment posture).

### 2b. Budget

Resolve the budget value using this precedence (highest wins):

| Source | Value |
|---|---|
| `--no-budget` flag | Force single-session mode (no wake-loop) |
| `--budget <duration>` flag | Use the explicit duration (`30m`, `4h`, `10h`, `2d` — bounds `1m`–`72h`) |
| `--overnight` flag | `8h` |
| `--weekend` flag | `48h` |
| `--background` flag | `4h` |
| Prose signal in `$ARGUMENTS` | See table below |
| `.aperant/config.json: research.default_budget` | If set |
| (nothing) | single-session mode |

Prose signal table (read input, never ask):

| Signal in user prose | Inferred budget |
|---|---|
| "overnight", "while I sleep", "by morning" | 8h |
| "weekend", "over the weekend", "by Monday" | 48h |
| "for N hours/days/minutes" (any explicit duration) | N |
| "while I'm at work", "during the day" | 8h |
| "in the background" | 4h |
| "really thoroughly", "deep dive", "question my beliefs", "take your time" | 4h |
| Decomposition would produce ≥8 sub-questions OR ≥3 contested perspectives (assess during Step 4) | 4h |

If a budget is resolved (non-zero), load `appendices/budget-loop.md` into reasoning
context. This appendix carries the per-iteration loop discipline, priority formula,
spar triggers, stop-state criteria, progress checkpoints, and wake-loop continuation.
Do not re-state its rules here.

### 2c. Announce

Open with a one-line declaration that surfaces the resolved budget AND the signal
that produced it (so the user can override knowingly):

```
[apt:research] {Mode} / {Domain}{ — budget {value} from {signal-source}}.
{Pass --budget <duration> to override or --no-budget to disable.}
```

Examples:
```
[apt:research] UNDERSTAND / mechanistic — budget 8h from signal "overnight".
Pass --budget <duration> to override or --no-budget to disable.

[apt:research] COMPARE / strategic — single-session (no budget set).
Pass --budget 4h or --background to escalate to a background loop.

[apt:research] VERIFY / empirical — budget 10h from --budget flag.
```

## 3. Write Immutable Research Spec

Write `.aperant/research/{task-id}/research-spec.md`. **Once written, this file is locked
for the rest of the session.** It is the success-criterion oracle.

Mandatory sections, ALL modes:

- `## Question` — the question in exact, falsifiable form
- `## Mode` — UNDERSTAND | VERIFY | SOLVE | COMPARE
- `## Domain` — mechanistic | empirical | strategic | general (from Step 1d)
- `## Budget` — duration value (e.g. `8h`) AND source (e.g. `signal: "overnight"` /
  `flag: --weekend` / `none — single-session`)
- `## Sub-questions` — the decomposed questions retrieval must answer (filled in Step 4)
- `## Source requirements` — required source tiers and minimum count, per domain rules
- `## Critical claims` — claims that, if wrong, invalidate the synthesis (load-bearing).
  These get explicit `impact: high` weighting in the priority formula.
- `## Stop rule` — domain-gated; defaults in `appendices/budget-loop.md` §stop-states
- `## Anti-patterns` — failure modes specific to this question (e.g. "do NOT cite
  single-author benchmarks for tool comparison")
- `## Output template` — domain-gated synthesis sections (see `appendices/domain-tools.md`
  §output-gates)

Print the spec to stdout for the user to see the commitment.

## 4. Decompose Query

Mode-specific decomposition. Output is the input to Step 5 fan-out and the basis for the
initial claim graph nodes.

- **UNDERSTAND**: enumerate 5-8 perspectives (skeptic, practitioner, theorist, historian,
  …) and generate one sub-question per perspective.
- **VERIFY**: generate 3-5 independent probes from different angles (primary docs,
  community reports, attempted refutation, edge cases, independent reproduction).
- **SOLVE**: state the problem decomposition, then generate 2-3 candidate approaches.
  Per candidate, generate a prior-art query AND a failure-mode query (mandatory).
- **COMPARE**: declare 4-6 evaluation dimensions UP FRONT (do not invent dimensions
  mid-research). Each dimension × each option is one cell.

Initialize the claim graph (`claims.json`): one claim node per sub-question with
`status: open`, `confidence: low`, `centrality: 0`, computed `impact` from the
`## Critical claims` spec section.

Write the decomposition into `research-spec.md` under `## Sub-questions`. This finalizes
the spec.

**Late budget upgrade.** If Step 2b resolved no budget AND this decomposition produces
≥8 sub-questions OR ≥3 contested perspectives, the prose-signal threshold has been met
mid-process. Print a one-line escalation offer and proceed in single-session mode unless
the user opted out (autonomy 0 stops here for confirmation anyway):

```
[apt:research] Decomposition produced N sub-questions — this is a strong --background
(4h) candidate. Continuing in single-session mode. Pass --background or --budget 4h on
resume to escalate.
```

## 5. Run One Iteration (4 specialized agents)

Set `RESEARCH.md` status to `INVESTIGATING`. Append a one-line heartbeat to
`progress.log` (format defined in `appendices/budget-loop.md` §progress-checkpoints).

Pick the next claim to attack using the priority formula from
`appendices/budget-loop.md` §priority-formula:

```
priority(claim) = impact(claim) × centrality(claim) × uncertainty(claim)
```

Spawn FOUR `apt-researcher` agents **in a single message** for parallel execution.
Each gets a role-specialized prompt and isolated context. Role prompts and the
domain-aware tool surface are defined in `appendices/budget-loop.md` §agent-roles
and `appendices/domain-tools.md` respectively.

| Role | Objective | Tool surface | Forbidden |
|---|---|---|---|
| **Builder** | Advance the synthesis using current claim graph | full read + WebSearch/WebFetch + domain tools | falsifying claims |
| **Skeptic** | Falsify the chosen high-priority claim; produce explicit counter-evidence | full read + WebSearch/WebFetch + domain tools | confirming or hedging |
| **Retriever** | Find NEW evidence only (sources not yet in `sources.json`) | WebSearch/WebFetch + domain-specific search | opinion, synthesis |
| **Judge** | Read what Builder/Skeptic/Retriever produced; update claim statuses with evidence deltas | read-only: claims.json, the three returns | proposing new claims |

For the gated cases:
- Domain ≠ mechanistic AND Builder/Skeptic uses first-principles reasoning → Judge
  demotes that output to `status: hypothesis` (cannot reach `confidence: high`).
- Domain = strategic AND Builder ranks options → Judge ensures recommendation is
  conditional-tree, not single-winner (see `appendices/domain-tools.md` §output-gates).
- Domain = empirical AND a citation is added without resolving through Crossref/PubMed
  → Judge moves the source to `sources.json: pending-verify` (not promoted).

Append a structured `## Iteration N` block to `iteration-log.md` containing:
- Attacked claim ID + computed priority
- Builder output summary (1-3 lines)
- Skeptic output summary + falsification result
- Retriever output: new source IDs added
- Judge ruling: per-claim status delta + reasoning

## 6. Update the Claim Graph

Merge the Judge's status deltas into `claims.json`. For each claim touched this
iteration:

- Append to `history[]` with `{iter, status, note, evidence_delta}`
- Recompute `centrality` (DAG out-degree on `depends_on` / `supports` / `contradicts`)
- Recompute `uncertainty` from evidence-for / evidence-against ratio + source tiers
- If Skeptic produced concrete counter-evidence accepted by Judge, downgrade
  `confidence` and surface in synthesis

For each NEW source added by Retriever:
- Append to `sources.json` with `fetched_at`, `tier`, `derivation_chain` (what other
  sources, if any, it derives from)
- For domain = empirical, mark `verified_at: null` until citation-audit Step 7 runs

For Skeptic output that introduces a new claim contradicting an existing one:
- Create a new claim node
- Add `contradicts` edge to existing claim
- If both are `confidence ≥ medium`, mark both `status: contested` and tag as a
  tier-1 conflict (spar trigger candidate)

## 7. Citation Audit (every N iterations, default N=4)

Pick the top-K load-bearing claims (highest `impact × centrality`). For each:

- Re-fetch the cited source URL via WebFetch (or `curl` + `pdftotext` if PDF)
- Verify the quoted text still appears in the source (case-insensitive, ≥80% match)
- For domain = empirical: resolve every cited DOI/PMID via Crossref or NCBI E-utilities
  (`curl https://api.crossref.org/works/{doi}` / `curl https://eutils.ncbi.nlm.nih.gov/...`)

For each failure:
- Move the citation to `suspect-citations.md` with `{claim_id, source_id, failure_mode}`
- Downgrade the claim's confidence; if no other source supports it, set
  `status: retracted`

Citation audit on a single-session run (no budget) fires once at the end of Step 5.

## 8. Decide: Continue, Synthesize, or Spar

After Step 7, check four conditions in order. The FIRST terminal-state hit wins —
remaining budget is not consumed.

**8a. Spar trigger check.** Fire `/apt:spar` (via Skill) when ANY of:
- A tier-1 conflict exists in the graph (two contested claims with `confidence ≥ medium`)
- A `critical` claim (from spec `## Critical claims`) is unresolved for 2+ iterations
- A claim has only `tier: vendor-doc` evidence and no independent confirmation

Spar prompt MUST include: the current synthesis position, the specific claim under
challenge, and an explicit instruction to produce a concrete status delta with citation
diff. Reject spar returns that are debate summaries with no actionable delta.

**8b. Stop check (early-termination wins).** Apply the dual-state rule from
`appendices/budget-loop.md` §stop-states:

- **CONVERGED** if: all `critical` claims at `confidence ≥ high`, zero unresolved
  tier-1 conflicts, and Retriever found no new sources for the last 2 iterations
  (frontier exhausted with synthesis stable). Exits immediately even if budget
  remains.
- **STALLED** if: Retriever frontier exhausted (no new sources for 2 iterations) but
  critical claims remain `contested` or `confidence < high`. The skill outputs an
  explicit `## Cannot resolve without` section listing what new evidence (real-world
  experiments, paywalled paper access, vendor disclosure, etc.) would be required.
- **CONTINUE** otherwise. Increment iteration counter. If a budget is set, go to
  Step 9 (wake-loop). If not, return to Step 5 — but cap at 5 iterations for
  single-session runs; if cap hit without terminal state, force `STALLED` with
  `## Cannot resolve in single-session mode — re-run with --budget 4h or higher`.

**8c. Budget-cap check (budget loops only).** If `elapsed ≥ budget`, force `STALLED`
with `## Stopped at budget cap` note. Honest, even if mid-attack.

**8d. Final progress.log entry.** When any terminal state fires, write the verdict
line to `progress.log` (see `appendices/budget-loop.md` §progress-checkpoints for the
format).

## 9. Schedule Next Wake (budget loops only)

If a budget is set AND status = CONTINUE:

Compute `remaining = original_budget − elapsed_at_start_of_iter`. Call `ScheduleWakeup`:

- `delaySeconds`: 1200 (20 minutes — past cache TTL, lets retrieval breathe)
- `prompt`: `/apt:research --resume {task-id} --budget {remaining}`
- `reason`: `apt:research budget loop iter {N+1}, next attack: {claim_id}, budget remaining: {remaining}`

The `--budget {remaining}` arg keeps the cap monotonically decreasing across wakes
so a crash + manual restart preserves the original commitment.

Persist `## Next attack: {claim_id}, priority: {value}` and `## Budget remaining:
{remaining}` to `RESEARCH.md` so the wake-loop iteration knows where to resume
without recomputing.

Exit the current session. The wake event will fire Step 5 in a fresh context with
all state on disk.

## 10. Synthesize (terminal state reached)

Flip status to `SYNTHESIZING`. Write `RESEARCH.md` using the domain-gated output
template from `appendices/domain-tools.md` §output-gates. Every domain produces:

- `## Headline` — one-paragraph synthesis
- `## Claim graph summary` — top 5 critical claims with `{statement, status, confidence,
  source_tier, conditions}`
- `## Contradictions` — every unresolved tier-1 conflict, with both positions and the
  evidence each rests on (NEVER silently averaged)
- `## Iteration log summary` — compact per-iteration trace pulled from `iteration-log.md`
- `## Self-Score` — `{sub_question_coverage: X/Y, critical_claim_coverage: X/Y,
  stop_state: CONVERGED|STALLED, budget_used: X of Y, calibration_note: "..."}`

Domain-gated additions:
- **mechanistic** → `## First-principles derivations` with cross-check vs sources
- **empirical** → `## Not medical/financial/safety advice` + `## What this CAN'T tell
  you` + `## Quality of evidence` (RCT/n/peer-review-status per cited study)
- **strategic** → `## Conditional-tree recommendation` (`if X then A, if Y then B`,
  NEVER single-winner when evidence is vendor-only) + `## Vendor-claim conflicts`
- **general** → `## Outstanding gaps` + `## Recommended next reads`

Mode-gated additions:
- **UNDERSTAND** → mental model with mechanism + analogy
- **VERIFY** → verdict (`confirmed | partially confirmed | false | contested | unknown`)
- **SOLVE** → candidate approaches table + recommended path + `## Falsification status`
  (Code-SOLVE blocks `CONVERGED` if no test/simulator/model-checker was run; force
  `STALLED` with `## Cannot certify without falsification`)
- **COMPARE** → dimensions × options matrix + per-cell evidence pointers

## 11. Report and What's Next

Print to stdout:

```
[apt:research] {Mode} / {Domain} — {CONVERGED|STALLED}.
Task: .aperant/research/{task-id}/
  RESEARCH.md         — synthesis ({lines} lines)
  research-spec.md    — immutable spec
  claims.json         — {N} claims, {M} sources
  iteration-log.md    — {I} iterations
  progress.log        — {I} heartbeats (tail anytime)
  suspect-citations.md — {Q} quarantined ({nonzero ? "REVIEW REQUIRED" : "clean"})
Self-score: critical-claim coverage {X}%, stop-state {CONVERGED|STALLED}
Budget: {used} of {cap} ({percent}% — {saved-time saved} if CONVERGED early)
{1-line headline finding OR 1-line "cannot resolve without" gap}

Next:
  /apt:discuss to lock decisions from these findings
  /apt:plan to turn SOLVE/COMPARE output into an implementation plan
  /apt:research --resume {task-id} to extend (re-query gaps, add a mode)
  /apt:research --resume {task-id} --budget <duration> to escalate to a longer loop
```

</process>
