Runcastle

Deep Research Report

MIT0 downloads

by runcastle

v1.0.1
Homepage ↗

Fans out a searcher per sub-question to fetch and note public web sources, then an Opus synthesizer writes a fully cited report, then a Codex fact-checker adversarially verifies every claim against the notes and loops until the report holds up.

Topology

Disclosures

Disclosures — declared side-effect surface

Everything below runs on your machine or inside the sandbox when you use this workflow. Mismatches between these declarations and the actual code block publishing.

Host hooks

Commands executed on YOUR host machine by Sandcastle lifecycle hooks.

  • cp .env.example .env

Sandbox hooks

Commands executed inside the sandbox container.

None declared.

Network access

Searchers fetch public web pages via curl.

Shell expansion

Prompt files contain !`command` blocks — the agent CLI executes these commands at prompt-load time. They are highlighted amber in the prompt files below.

Files

Diff vs the stock Sandcastle 0.12.0 template Dockerfile — green lines were added by the author, red lines were removed from stock.

+# Sandbox image for the Deep Research Report workflow.
+# Node 22 + git + curl (searchers fetch public web pages) + both agent CLIs —
+# Claude Code (searchers + synthesizer) and Codex (fact-checker) — running as a
+# non-root `agent` user.
FROM node:22-bookworm
-# System dependencies.
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
curl \
jq \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
-# Claude Code CLI (the agent runtime).
-RUN npm install -g @anthropic-ai/claude-code
+# Agent CLIs: Claude Code (searchers + synthesizer) and Codex (fact-checker).
+RUN npm install -g @anthropic-ai/claude-code @openai/codex
# Non-root agent user. `sandcastle docker build-image` aligns AGENT_UID/GID to
# the host user via --build-arg to avoid permission errors on bind mounts.
+# node:22-bookworm already ships a "node" user at UID/GID 1000, so we RENAME it
+# (the stock Sandcastle template pattern) — groupadd/useradd would collide with
+# the existing IDs on a default build.
ARG AGENT_UID=1000
ARG AGENT_GID=1000
-RUN groupadd --gid ${AGENT_GID} agent \
- && useradd --uid ${AGENT_UID} --gid ${AGENT_GID} --create-home --shell /bin/bash agent
+RUN groupmod -o -g ${AGENT_GID} node \
+ && usermod -o -u ${AGENT_UID} -g ${AGENT_GID} -d /home/agent -m -l agent node
-USER agent
-WORKDIR /workspace
+USER ${AGENT_UID}:${AGENT_GID}
+WORKDIR /home/agent
+
+# Sandcastle bind-mounts the worktree and sets the working directory at
+# container start; the container just needs to stay alive until then.
+ENTRYPOINT ["sleep", "infinity"]
Show full Dockerfile (highlighted)
# Sandbox image for the Deep Research Report workflow.
# Node 22 + git + curl (searchers fetch public web pages) + both agent CLIs —
# Claude Code (searchers + synthesizer) and Codex (fact-checker) — running as a
# non-root `agent` user.
FROM node:22-bookworm

RUN apt-get update && apt-get install -y --no-install-recommends \
      git \
      curl \
      jq \
      ca-certificates \
 && rm -rf /var/lib/apt/lists/*

# Agent CLIs: Claude Code (searchers + synthesizer) and Codex (fact-checker).
RUN npm install -g @anthropic-ai/claude-code @openai/codex

# Non-root agent user. `sandcastle docker build-image` aligns AGENT_UID/GID to
# the host user via --build-arg to avoid permission errors on bind mounts.
# node:22-bookworm already ships a "node" user at UID/GID 1000, so we RENAME it
# (the stock Sandcastle template pattern) — groupadd/useradd would collide with
# the existing IDs on a default build.
ARG AGENT_UID=1000
ARG AGENT_GID=1000
RUN groupmod -o -g ${AGENT_GID} node \
 && usermod -o -u ${AGENT_UID} -g ${AGENT_GID} -d /home/agent -m -l agent node

USER ${AGENT_UID}:${AGENT_GID}
WORKDIR /home/agent

# Sandcastle bind-mounts the worktree and sets the working directory at
# container start; the container just needs to stay alive until then.
ENTRYPOINT ["sleep", "infinity"]

README

Deep Research Report

Turn one big question into a cited, fact-checked report — without opening fifty browser tabs. This workflow fans out a searcher per sub-question, synthesizes the notes into a single report, then runs an adversarial fact-checker that verifies every claim against the evidence before you trust a word of it.

What it does

You give it a research question and a handful of sub-questions. For each sub-question, a Claude Code searcher fetches public web sources, extracts specific checkable claims, and records each one with the source URL and an exact supporting quote in research/notes-<i>.md. Anything it can't confirm is flagged [UNVERIFIED] rather than dropped.

A Claude Code synthesizer then reads every notes file and writes research/REPORT.md: an executive summary, findings organized by theme with inline citations, where sources agree and disagree, and an explicit confidence-and-gaps section. Finally a Codex fact-checker adversarially audits the report against the notes — softening overstated claims, flagging unsupported ones, and killing bogus citations — looping until the report holds up, then emitting <promise>VERIFIED</promise>.

How it works

main.ts fans out the searchers with Promise.all, each on its own research/q-<i> branch so the parallel runs never collide. Each searcher seeds itself with a !`curl` block in the prompt — this is why the manifest discloses network access and shell expansion. The synthesizer then runs once on HEAD, and the Codex fact-checker loops on its own output. The topology is a fan-out into a verify loop (searcher ×N → synthesizer → fact-checker↺).

Requirements

Set CLAUDE_CODE_OAUTH_TOKEN (run claude setup-token) and OPENAI_API_KEY in .sandcastle/.env. Edit RESEARCH_QUESTION and the SUBQUESTIONS list at the top of .sandcastle/main.ts. Build the image once with npx @ai-hero/sandcastle docker build-image, then run npx tsx .sandcastle/main.ts. Everything the searchers fetch is public web content; nothing is sent anywhere but the models.