Bare-bones template for running an agent inside a Docker container with the situational-awareness mitigations adapted from the persona drift / sycophancy harness setup.
- Per-session container isolation. Each
docker rungets a fresh filesystem and process namespace. - Three-mount pattern. Data read-only, run workspace writable, fresh
~/.claude/per session. - Two-layer blank-slate enforcement. Empty
/home/runner/.claude/mount +CLAUDE_CODE_DISABLE_AUTO_MEMORY=1env var. - Minimal demonstrator orchestrator. Verifies the container is wired up correctly. You replace this with your actual experimental code.
Helps with:
- Session isolation between runs — no state leakage between sessions
- Repeated runs without detection — each session boots looking like the first and only run
- Controlled tool access — define exactly what the agent can do
- Blank-slate enforcement via the two layers above
Doesn't help with:
- Within-session situational awareness — if your prompts or experimental framing have a "designed feel" inside the conversation, the agent can still notice. That's a prompt-design issue, not an infrastructure issue. Docker controls what surrounds the conversation, not what's inside it.
- Model priors about deployment environments — frontier models have intuitions about what real production looks like
Everything below assumes Docker Desktop is installed and its daemon is running. If you already have it, skip to Quick start.
macOS (Homebrew):
brew install --cask dockerWindows: download the installer from https://www.docker.com/products/docker-desktop/ and run it. Select the WSL 2 backend when prompted. You may also need to enable hardware virtualization in your BIOS.
Then open Docker Desktop and wait for the whale icon (menu bar on macOS, system tray on Windows) to show the daemon is up. Confirm with:
docker run --rm hello-worldIf that prints a greeting, you are ready. If it complains it cannot connect to the daemon, Docker Desktop is not running yet.
# 1) Build the image (from this directory)
docker build -t agent-harness-demo .
# 2) Set your Anthropic API key
export ANTHROPIC_API_KEY=sk-ant-...
# 3) Run a demo session
./run_session.shEach run creates a fresh directory under runs/run_<timestamp>/. The demonstrator orchestrator inside the container:
- Confirms the workspace mounts are accessible
- Makes a trivial API call to Claude Haiku
- Writes a
transcript.txtto the writable workspace
Output should land in something like runs/run_20260512_143022/transcript.txt.
| File | What it is |
|---|---|
Dockerfile |
Image definition. Adapt the base image and system deps to your task. |
requirements.txt |
Python deps inside the image. Add whatever your orchestrator needs. |
orchestrator/run_session.py |
Minimal demonstrator. Replace with your actual experimental orchestrator. |
run_session.sh |
Example wrapper showing the docker run invocation. Adapt paths and image name for your project. |
-
Swap the base image if you need R (
rocker/tidyverse), Node + Claude Code CLI (see the sycophancy paper's Dockerfile), or other runtimes. -
Replace
orchestrator/run_session.pywith your actual session-orchestration code. The demonstrator is intentionally trivial — it just proves the container is wired up correctly. Your orchestrator implements the conversation flow for your experiment. -
Adjust
run_session.shpaths to point at your data and run directories. -
If using Claude Code CLI inside the container, restrict tools explicitly on the
claudeinvocation. The sycophancy harness uses--tools Read,Write,Edit,Glob,Grep,Bash,Task— note thatWebSearchandWebFetchare simply left out, which is how web access is withheld from the agent.Older versions of this template referenced a
--no-memoryflag as a third blank-slate layer. That flag no longer exists in Claude Code. The two layers above (fresh.claudemount +CLAUDE_CODE_DISABLE_AUTO_MEMORY=1) are what actually enforce the blank slate. The closest current equivalent is--bare, which skips auto-memory andCLAUDE.mdauto-discovery — but--barereads auth strictly fromANTHROPIC_API_KEYor anapiKeyHelperand never from OAuth or the keychain, so it will break a subscription-based login. Don't reach for it unless you are already using an API key. -
Withholding the tool is not the same as blocking the network. The container in this template has full outbound access, and if the agent has
Bashit can still reach the internet by other means (curl,install.packages()hitting CRAN). If you need the run to be genuinely offline, add--network noneto thedocker runinrun_session.sh. Note that this also blocks the orchestrator's own API calls, so it only applies to harnesses where the model is reached some other way — otherwise use a firewall/proxy allowlist that permits only the API endpoint.
"Cannot connect to the Docker daemon" — Docker Desktop is not running. Open the app and wait for the whale icon.
"docker: command not found" — Docker Desktop is installed but not on your PATH. On macOS, reopen the terminal after installing. On Windows, use a terminal opened after installation.
Image not found — build it first: docker build -t agent-harness-demo . from the repo root. Rebuild whenever the Dockerfile changes.
Scripts fail with carriage-return errors (Windows) — the shell scripts were checked out with CRLF line endings. Fix with:
git config core.autocrlf input
git checkout -- .Environment variable not visible inside the container — variables must be exported in the shell before docker run, and passed through with -e. On Windows, user environment variables set through System Settings are only picked up by terminals opened afterwards.
Permission errors writing to the run directory — the container runs as the non-root runner user. Make sure the host directory you mount at /workspace/run is writable, or chown it to match.
The Dockerfile is ~30 lines and is the easy part. The harder half is the code that runs inside it — the script that talks to the API, implements your experimental protocol, and crucially delivers your experimental signal through in-task content rather than through metaprompts about being in a study. That's where the actual design lives, and that's project-specific.
The demonstrator orchestrator in this template is meant only to prove the container infrastructure works end-to-end. Don't ship anything based on it.