diff --git a/.planning/phases/03-sandbox-aware-prompting/03-RESEARCH.md b/.planning/phases/03-sandbox-aware-prompting/03-RESEARCH.md
new file mode 100644
index 0000000..0b5b53c
--- /dev/null
+++ b/.planning/phases/03-sandbox-aware-prompting/03-RESEARCH.md
@@ -0,0 +1,280 @@
+# Phase 3: Sandbox-Aware Prompting - Research
+
+**Researched:** 2026-04-09
+**Domain:** Shell scripting (heredoc generation, file manipulation), Claude Code memory system
+**Confidence:** HIGH
+
+## Summary
+
+Phase 3 adds two files to `~/.claudebox/` on every claudebox launch: a fully-managed `SANDBOX.md` containing sandbox context, and a user-owned `CLAUDE.md` that imports it via Claude Code's `@` syntax. Since `~/.claudebox` is bind-mounted as `~/.claude` inside the sandbox (SAND-08, already implemented), Claude Code automatically loads both files at session start.
+
+The implementation is straightforward shell scripting -- a heredoc write for SANDBOX.md and a head/grep check for the CLAUDE.md import line. The main research concern was verifying that `@SANDBOX.md` relative imports work correctly from `~/.claude/CLAUDE.md`. Official documentation confirms relative paths resolve relative to the containing file, which is the behavior we need. There are known issues with tilde-expansion imports (`@~/.claude/foo.md`) but our case uses a simple filename in the same directory, which is the simplest and most reliable form.
+
+**Primary recommendation:** Implement as a single contiguous block in `claudebox.sh` between the existing `mkdir -p "$HOME/.claudebox"` (line 102) and the gitconfig generation (line 104). Use a heredoc for SANDBOX.md content and simple `head -1` / `grep` for the CLAUDE.md import check.
+
+
+
+## User Constraints (from CONTEXT.md)
+
+### Locked Decisions
+- **D-01:** Two-file approach. claudebox manages `~/.claudebox/SANDBOX.md` (sandbox context) and ensures `~/.claudebox/CLAUDE.md` has an `@SANDBOX.md` import at the top. Since `~/.claudebox` is bind-mounted as `~/.claude` inside the sandbox, Claude Code auto-loads both files at session start.
+- **D-02:** `SANDBOX.md` is fully owned by claudebox -- overwritten on every launch. User should not edit this file; changes are lost on next run.
+- **D-03:** `CLAUDE.md` is user-owned. claudebox only ensures the `@SANDBOX.md` import line exists at the top. If missing, it's re-added. All other content is untouched.
+- **D-04:** Friendly guide tone -- short prose paragraphs, not terse bullets. Sections: sandbox overview, installing tools (comma + nix shell with examples), default restrictions (phrased as "by default, not mounted" to avoid contradicting user customizations), git setup.
+- **D-05:** Default restrictions use "by default" phrasing: "By default, the following are not mounted into the sandbox: SSH keys, GPG/age keys, cloud credentials, Tailscale." Includes note: "If your setup has been customized, some of these may be available."
+- **D-06:** Git section notes identity is pre-configured (name/email) and suggests HTTPS for remotes by default. Mentions safe.directory is set.
+- **D-07:** Uses Claude Code's `@path` import syntax in CLAUDE.md. `@SANDBOX.md` at the first line. This is auto-expanded at session start -- no Read tool call needed.
+- **D-08:** On every launch, claudebox: (1) writes/overwrites `~/.claudebox/SANDBOX.md` with current content, (2) checks if `~/.claudebox/CLAUDE.md` exists -- creates it with just the import line if not, (3) if CLAUDE.md exists, checks first line for the `@SANDBOX.md` import -- prepends it if missing.
+
+### Claude's Discretion
+- Exact prose wording and section ordering in SANDBOX.md
+- How the first-line check works in shell (grep, head, etc.)
+- Whether to use a comment marker around the import line for robustness
+
+### Deferred Ideas (OUT OF SCOPE)
+None -- discussion stayed within phase scope
+
+
+
+
+
+## Phase Requirements
+
+| ID | Description | Research Support |
+|----|-------------|------------------|
+| AWARE-01 | Default `CLAUDE.md` is created in `~/.claudebox/` on first run if not present | D-08 defines exact behavior; shell pattern for conditional file creation is standard |
+| AWARE-02 | Injected CLAUDE.md tells Claude it's in a sandbox, how to use comma/nix for tools, and what's not available | D-04/D-05/D-06 define content structure; `@SANDBOX.md` import mechanism verified against official docs |
+
+
+
+## Standard Stack
+
+No new libraries or packages. This phase is pure shell scripting within the existing `claudebox.sh` and `flake.nix` structure.
+
+| Tool | Purpose | Already Available |
+|------|---------|-------------------|
+| `cat` (heredoc) | Write SANDBOX.md content | Yes (coreutils in runtimeInputs) |
+| `head` | Check first line of CLAUDE.md | Yes (coreutils) |
+| `grep` | Pattern match for import line | Available but not in runtimeInputs -- use shell builtins instead |
+| `sed` | Prepend line to file | Available but not in runtimeInputs -- use temp file + cat instead |
+
+**Key constraint:** The file manipulation runs on the HOST before `exec bwrap`, so host tools are available. No need to worry about sandbox PATH limitations for this code.
+
+## Architecture Patterns
+
+### Integration Point in claudebox.sh
+
+The new code inserts between line 102 (`mkdir -p "$HOME/.claudebox"`) and line 104 (gitconfig generation). This is the natural location because:
+1. `~/.claudebox` directory is guaranteed to exist
+2. File generation happens before the bwrap exec
+3. Groups all pre-launch setup together
+
+```
+# Existing line 102
+mkdir -p "$HOME/.claudebox"
+
+# NEW: SANDBOX.md generation (D-02)
+# NEW: CLAUDE.md import check (D-03, D-08)
+
+# Existing line 104+
+GIT_NAME=$(git config --global user.name ...)
+```
+
+### Pattern: Heredoc for SANDBOX.md
+
+Write SANDBOX.md using a heredoc. This keeps content inline in claudebox.sh (as noted in CONTEXT.md specifics section) with no extra files in the derivation.
+
+```bash
+# Write SANDBOX.md (overwritten every launch per D-02)
+cat > "$HOME/.claudebox/SANDBOX.md" << 'SANDBOXEOF'
+# Sandbox Environment
+
+You are running inside a bubblewrap (bwrap) sandbox...
+SANDBOXEOF
+```
+
+Use single-quoted heredoc delimiter (`'SANDBOXEOF'`) to prevent variable expansion -- the SANDBOX.md content is static text, no shell variables needed. [VERIFIED: standard bash heredoc behavior]
+
+### Pattern: CLAUDE.md Import Check
+
+```bash
+# Ensure CLAUDE.md has @SANDBOX.md import (D-03, D-08)
+CLAUDEMD="$HOME/.claudebox/CLAUDE.md"
+IMPORT_LINE="@SANDBOX.md"
+
+if [[ ! -f "$CLAUDEMD" ]]; then
+ # First run: create with just the import line
+ echo "$IMPORT_LINE" > "$CLAUDEMD"
+elif ! head -1 "$CLAUDEMD" | grep -qF "$IMPORT_LINE"; then
+ # Exists but missing import: prepend
+ tmp=$(mktemp)
+ { echo "$IMPORT_LINE"; cat "$CLAUDEMD"; } > "$tmp"
+ mv "$tmp" "$CLAUDEMD"
+fi
+```
+
+This pattern:
+- Creates the file if missing (AWARE-01)
+- Checks only the first line for the import (D-07, D-08)
+- Prepends without destroying existing content (D-03)
+- Uses `grep -qF` for fixed-string match (no regex needed)
+- Uses mktemp + mv for atomic write (established pattern in codebase)
+
+### Anti-Patterns to Avoid
+
+- **Inline sed -i for prepending:** Non-portable, and the script already uses the mktemp+mv pattern for gitconfig. Stay consistent.
+- **Writing SANDBOX.md content inside CLAUDE.md:** Defeats the purpose of the two-file approach. SANDBOX.md is overwritten every launch; CLAUDE.md is user-owned.
+- **Variable expansion in SANDBOX.md heredoc:** Would break if user's env has unexpected values. Use single-quoted delimiter.
+
+## Don't Hand-Roll
+
+| Problem | Don't Build | Use Instead | Why |
+|---------|-------------|-------------|-----|
+| Atomic file write | Custom lock files | `mktemp` + `mv` | Already the pattern in codebase (gitconfig), atomic on same filesystem |
+| First-line check | Complex parsing | `head -1 \| grep -qF` | One-liner, POSIX-compatible, no edge cases for this use |
+
+## Common Pitfalls
+
+### Pitfall 1: Heredoc Indentation
+**What goes wrong:** Using `<<-` with tabs for indentation but the content has mixed tabs/spaces, producing wrong output.
+**Why it happens:** `<<-` only strips leading tabs, not spaces.
+**How to avoid:** Use `<<` (no dash) with no indentation in the heredoc body. The SANDBOX.md content should be flush-left in the script.
+**Warning signs:** SANDBOX.md has unexpected leading whitespace.
+
+### Pitfall 2: Import Line Getting Duplicated
+**What goes wrong:** If the check logic has a bug, `@SANDBOX.md` could appear multiple times in CLAUDE.md.
+**Why it happens:** Checking for the line anywhere in the file instead of just line 1, or not checking at all.
+**How to avoid:** Always check `head -1` specifically. The import must be on line 1 for Claude Code to process it before other content.
+**Warning signs:** Multiple `@SANDBOX.md` lines in CLAUDE.md after several launches.
+
+### Pitfall 3: CLAUDE.md Import Approval Dialog
+**What goes wrong:** Claude Code shows an approval dialog for the `@SANDBOX.md` import, breaking the "zero friction" goal.
+**Why it happens:** Claude Code may prompt for approval when it encounters external imports for the first time.
+**How to avoid:** The official docs state this dialog appears for "external imports in a project" -- user-level `~/.claude/CLAUDE.md` imports may behave differently since they are user-owned. This needs testing. If the dialog appears, it's a one-time approval.
+**Warning signs:** User sees "approve imports" prompt on first claudebox session.
+**Risk level:** LOW -- even if it appears, it's one-time and self-explanatory.
+
+### Pitfall 4: Race Condition with Temp File
+**What goes wrong:** If claudebox is killed between writing the temp file and mv, a stale temp file is left behind.
+**Why it happens:** No cleanup trap for this specific temp file.
+**How to avoid:** The existing trap at line 109 cleans up `$GITCONFIG_TMP`. Either extend that trap or accept that orphan temp files in /tmp are harmless (cleaned on reboot).
+**Warning signs:** None in practice -- /tmp is ephemeral.
+
+## Code Examples
+
+### SANDBOX.md Content Structure
+
+Based on decisions D-04, D-05, D-06, the content should follow this structure. Exact prose is Claude's discretion.
+
+```markdown
+# Sandbox Environment
+
+You are running inside a bubblewrap (bwrap) sandbox managed by claudebox.
+Your filesystem is isolated -- only the current working directory and
+essential system paths are mounted.
+
+## Installing Tools
+
+You have two ways to install tools on the fly:
+
+**Comma (preferred for quick one-off commands):**
+`, ripgrep` runs ripgrep without permanent installation. Comma uses
+nix-index to find the right package automatically.
+
+**Nix shell (for persistent access within the session):**
+`nix shell nixpkgs#python3 -c python3 script.py` runs a command with
+a package available. To keep it in your PATH for the session:
+`nix shell nixpkgs#python3` then use `python3` normally.
+
+## Default Restrictions
+
+By default, the following are not mounted into the sandbox:
+- SSH keys (~/.ssh)
+- GPG and age keys (~/.gnupg, age key files)
+- Cloud credentials (~/.aws, ~/.config/gcloud)
+- Tailscale state
+
+If your setup has been customized, some of these may be available.
+
+## Git
+
+Your git identity (name and email) is pre-configured from the host.
+The `safe.directory` setting trusts the mounted working directory.
+For remote operations, prefer HTTPS URLs over SSH since SSH keys
+are not available by default.
+```
+
+### Shell Implementation
+
+```bash
+# === Sandbox-aware prompting (AWARE-01, AWARE-02) ===
+
+# Write SANDBOX.md -- fully managed, overwritten every launch (D-02)
+cat > "$HOME/.claudebox/SANDBOX.md" << 'SANDBOXEOF'
+[content here]
+SANDBOXEOF
+
+# Ensure CLAUDE.md has @SANDBOX.md import (D-03, D-08, AWARE-01)
+CLAUDEMD="$HOME/.claudebox/CLAUDE.md"
+if [[ ! -f "$CLAUDEMD" ]]; then
+ printf '%s\n' "@SANDBOX.md" > "$CLAUDEMD"
+elif [[ "$(head -1 "$CLAUDEMD")" != "@SANDBOX.md" ]]; then
+ tmp=$(mktemp)
+ { printf '%s\n' "@SANDBOX.md"; cat "$CLAUDEMD"; } > "$tmp"
+ mv "$tmp" "$CLAUDEMD"
+fi
+```
+
+Note: Using `[[ "$(head -1 ...)" != "@SANDBOX.md" ]]` is simpler and avoids needing grep. Exact string comparison on the first line. [VERIFIED: standard bash string comparison]
+
+## State of the Art
+
+| Old Approach | Current Approach | When Changed | Impact |
+|--------------|------------------|--------------|--------|
+| Monolithic CLAUDE.md | `@import` syntax for modular files | Claude Code ~1.0 (2025) | Allows splitting managed vs user-owned content cleanly |
+| `--append-system-prompt` | `~/.claude/CLAUDE.md` + `@imports` | Claude Code memory system | File-based approach persists across sessions without CLI flags |
+
+**Claude Code `@import` behavior (verified):**
+- Relative paths resolve relative to the containing file [CITED: code.claude.com/docs/en/memory]
+- Maximum import depth: 5 hops [CITED: code.claude.com/docs/en/memory]
+- User-level `~/.claude/CLAUDE.md` is loaded at session start for all projects [CITED: code.claude.com/docs/en/memory]
+- First 200 lines or 25KB of MEMORY.md is the auto-memory limit, but CLAUDE.md files load in full [CITED: code.claude.com/docs/en/memory]
+
+## Assumptions Log
+
+| # | Claim | Section | Risk if Wrong |
+|---|-------|---------|---------------|
+| A1 | `@SANDBOX.md` in `~/.claude/CLAUDE.md` resolves to `~/.claude/SANDBOX.md` without needing `./` prefix | Architecture Patterns | Import silently fails, Claude doesn't see sandbox context. Mitigation: test on first run. |
+| A2 | User-level `~/.claude/CLAUDE.md` imports don't trigger an approval dialog | Pitfall 3 | One-time dialog appears. Low impact -- user approves once. |
+
+## Open Questions
+
+1. **Does `@SANDBOX.md` (no path prefix) resolve correctly from `~/.claude/CLAUDE.md`?**
+ - What we know: Official docs say relative paths resolve relative to the containing file. The simplest form `@filename` should resolve to the same directory.
+ - What's unclear: Known bugs exist with tilde-expansion paths (`@~/.claude/foo.md`), but our case is simpler (same-directory, no tilde).
+ - Recommendation: Test during implementation. If it fails, try `@./SANDBOX.md` as fallback.
+
+2. **Comment marker for the import line?**
+ - What we know: D-03 says claudebox only touches the first line. A comment marker could help identify it as managed.
+ - What's unclear: Whether adding a comment on the same line as `@SANDBOX.md` breaks the import.
+ - Recommendation: Keep the import line bare (`@SANDBOX.md` only). Add a comment on line 2 if desired: ``.
+
+## Sources
+
+### Primary (HIGH confidence)
+- [Claude Code Memory Documentation](https://code.claude.com/docs/en/memory) -- `@import` syntax, resolution rules, file loading order, user-level CLAUDE.md behavior
+- `claudebox.sh` (current codebase) -- existing patterns, integration points, line numbers
+
+### Secondary (MEDIUM confidence)
+- [GitHub Issue #4754](https://github.com/anthropics/claude-code/issues/4754) -- relative path resolution bug (old version, different case than ours)
+- [GitHub Issue #8765](https://github.com/anthropics/claude-code/issues/8765) -- tilde expansion bug (different from our same-directory case)
+
+## Metadata
+
+**Confidence breakdown:**
+- Standard stack: HIGH -- no new dependencies, pure shell scripting
+- Architecture: HIGH -- integration point is clear, patterns established in codebase
+- Pitfalls: HIGH -- well-understood shell patterns, main risk is Claude Code import behavior (low consequence)
+
+**Research date:** 2026-04-09
+**Valid until:** 2026-05-09 (Claude Code import syntax is stable)