What If You Could Change the Agent Harness Mid-Session?
We ran 16 experiments across three investigation threads to find out how Claude Code handles agent context at runtime. The first results were wrong. Here is what we actually discovered.
We had a theory. If you ran xcaffold apply --blueprint=execute-backend --target=claude in the middle of a Claude Code session, the next Agent tool subagent you spawned would see the updated context — the new CLAUDE.md, the new rules, the domain-specific instructions.
It was a clean theory. And when we first tested it, it appeared to be wrong.
This is the story of how we ran sixteen experiments across three investigation threads, discovered a methodological flaw in our own tests, and emerged with something more useful than the theory we started with.
The Setup
xcaffold compiles .xcaf manifests into provider-native output directories. For Claude Code, that means writing to .claude/ — including CLAUDE.md, rules/, and agents/. Blueprints are named subset selectors: they tell the compiler which agents, rules, and contexts to include in a given compilation run.
A multi-stage agent workflow uses blueprints to switch context at stage transitions:
- Planning stage:
stage-planningblueprint — lean rules, minimal surface area - Execution stage:
execute-backendblueprint — domain agents, enforcement rules, task context - Review stage:
review-backendblueprint — quality-focused rules, no noise
The question we were actually asking: when the orchestrator runs xcaffold apply --blueprint=execute-backend --target=claude and writes new files to .claude/, do Agent tool subagents spawned after the switch see the updated context? Or does Claude Code lock context at session start and never re-read the files?
Experiments 1–7: Building a (Flawed) Picture
Our first four experiments were confirmatory. We verified that Claude Code resolves CLAUDE.md by walking up from the session's CWD. We confirmed that gitignored .claude/ directories in worktrees are still fully resolved. We confirmed that path-scoped rules activate lazily when matching files are read. We confirmed parallel sessions are isolated.
Experiment 5 was the first disappointment. The Agent tool's isolation: worktree setting creates a temporary worktree under .claude/worktrees/, but the subagent's loaded context still came from the parent session — the worktree directory had no .claude/CLAUDE.md of its own. Worktree isolation gives file isolation, not context isolation.
Then Experiments 6 and 7 seemed to seal the case against runtime switching.
Experiment 6: A background process changed CLAUDE.md on disk mid-session. Four seconds later, a subagent was spawned. Both the parent and the subagent reported the original context.
Experiment 7: We applied blueprint A, dispatched subagent 1. Applied blueprint B, dispatched subagent 2. Subagent 2 still reported the original context.
We wrote it down: rules are session-locked. We started designing an alternative architecture based on worktree-scoped CLI dispatch.
We were wrong.
The CLI Dispatch Detour (Experiments 8–13)
Before we discovered the flaw in experiments 6–7, we spent six experiments exploring an alternative: launching fresh claude --agent sessions from different working directories, each with its own .claude/ state.
This approach worked. Each fresh CLI session loaded the current disk state. Blueprint switching between sessions was reliable. We measured the cost: roughly 11K session-specific tokens plus 13K globally cached tokens, about $0.076 per session. We confirmed that --resume reused the session cache at near-zero cost. We confirmed that parallel worktree-scoped sessions had zero cross-contamination. We confirmed that xcaffold apply --dry-run provided full diff visibility before any switch.
The CLI dispatch architecture was viable but complex — it required JSON output parsing, session ID management, and worktree-scoped .claude/ directories per domain. We nearly committed to building it.
Then we re-read the documentation.
The Flaw in Our Methodology
Here is what we missed: in experiments 6 and 7, we asked subagents to self-report their context. We prompted them to tell us which CLAUDE.md they were reading.
But an Agent tool subagent does not inherit the parent session's conversation. It receives a fresh prompt constructed by the parent, and it loads its own CLAUDE.md independently from disk. In our tests, the parent's prompt to the subagent was shaped by the parent's own session-locked understanding of the context. The subagent's response reflected the parent's framing, not the subagent's actual loaded context.
We were testing the parent session's interpretation, not the subagent's independent context resolution. The results were measuring the wrong thing.
The Claude Code documentation was explicit about this. It states that subagents receive "every level of the memory hierarchy the main conversation loads" and that custom subagents "load their own CLAUDE.md file from disk." These two statements directly contradicted our experimental conclusions from experiments 6–7. That contradiction is what triggered the re-investigation.
The Correction: Behavioral Canaries
We redesigned the experiments around a different principle: don't ask the subagent what it knows — watch what it does.
We called this technique a behavioral canary. The CLAUDE.md content was written to produce observably different file outputs depending on which blueprint was active:
- Plan-mode CLAUDE.md: "Write docs to
docs/withplan-prefix. NEVER create code files." - Backend CLAUDE.md: "Write code to
src/api/withimpl-prefix. NEVER create docs."
The subagent could not "report" the correct context by inference. It had to actually read its CLAUDE.md and follow the instructions — and the evidence would be in the filesystem.
To ensure rigor, we ran these experiments via scripted claude -p (pipe mode) sessions launched from the test project directory, rather than interactive sessions. This eliminated any possibility of human interference influencing the subagent's behavior.
Experiment 14: The Moment It Clicked
The setup was a test project with the two canary contexts deployed via xcaffold blueprints.
- Applied
plan-modeblueprint →CLAUDE.mdinstructed: write docs - Started a session, spawned an Agent tool subagent: "Create a todo API"
- Subagent created
docs/plan-todo-api.mdwith a<!-- PLANNER-CONTEXT -->header ✓ - Applied
execute-backendblueprint →CLAUDE.mdon disk now instructed: write code - Resumed the same session (
--resume), spawned a NEW subagent: "Create a todo helper" - Subagent created
src/api/impl-todo-helpers.jswith a// BACKEND-CONTEXTheader ✓
The parent session's own system prompt was still plan-mode — that was session-locked. But the second subagent read the updated CLAUDE.md from disk and produced backend code.
The conclusion inverted: Agent tool subagents read CLAUDE.md and rules fresh from disk at spawn time, independent of the parent session's cached context.
Experiment 15: Mid-Session, No Resume
We needed to rule out the possibility that --resume was doing something special. So we tested an entirely fresh session with a compound prompt:
- Apply plan-mode blueprint
- Start fresh session with two tasks: first, run
xcaffold apply --blueprint=execute-backend --target=claude -yvia Bash; second, spawn a subagent
The session ran xcaffold apply — four files written to .claude/. Then it spawned the subagent. The subagent created src/api/impl-server.js and src/api/impl-todos.js, both with // BACKEND-CONTEXT headers.
The parent session could observe the disk change when it read the new CLAUDE.md — but its own system prompt remained the original plan-mode context from session start. What mattered was that the subagent, spawned after the switch, followed the updated instructions.
Mid-session blueprint switching works even in fresh sessions, with no --resume.
The subagent also followed rules deployed mid-session by xcaffold: camelCase naming, module.exports, correct directory structure. Rules, like CLAUDE.md, are read fresh from disk per subagent.
Experiment 16: The One Thing That IS Locked
We pushed further. What if xcaffold deployed a new agent definition mid-session?
- Apply plan-mode blueprint → no agents in
.claude/agents/ - Start session
- Run
xcaffold apply --blueprint=execute-backend --target=claudevia Bash → deploysbackend-worker.mdto.claude/agents/ - Verify the file exists on disk ✓
- Spawn a subagent with
subagent_type: 'backend-worker'
Result: Agent type 'backend-worker' not found.
Agent definitions are loaded into the Agent tool's type registry at session start. Files written to .claude/agents/ after the session begins are not recognized. This one is genuinely session-locked.
The Complete Picture
The session-lock boundary is more precise than we initially thought:
| Component | Parent Session | Agent Tool Subagent | Mid-Session Switch Works? |
|---|---|---|---|
| CLAUDE.md | Session-locked | Fresh from disk | YES |
| .claude/rules/ | Session-locked | Fresh from disk | YES |
| .claude/agents/ | Session-locked | Session-locked | NO |
| .claude/settings.json | Session-locked | Inherited from parent | N/A |
| Hooks (Pre/PostToolUse) | Live-reloaded | Agent-specific or inherited | YES |
The practical implication: xcaffold blueprint switching works natively with the Agent tool for the things that matter most — context and rules. The only constraint is that agent definitions must be pre-deployed at session start.
A note on fork subagents: Claude Code has an experimental fork mode (CLAUDE_CODE_FORK_SUBAGENT=1) where subagents share the parent's prompt cache and system prompt. Fork subagents would be session-locked. The fresh-from-disk behavior described here applies to regular Agent tool subagents only.
A note on depth: Agent tool subagents cannot spawn their own subagents — the tool is one level deep. The orchestrator dispatches workers, but workers cannot dispatch sub-workers.
The Architecture It Unlocks
The correction eliminates the need for worktree-scoped CLI dispatch as the primary context isolation mechanism. We separately verified that xcaffold's extends field composes correctly: an agents-only base blueprint produces no CLAUDE.md, and a child blueprint inheriting from it adds its own contexts and rules while preserving the base's agent definitions.
This gives us a two-layer blueprint strategy:
# Layer 1: Base blueprint — applied once at session start
# Registers all agents (session-locked from here)
kind: blueprint
version: "1.0"
name: feature-base
agents: [backend-worker, frontend-worker, reviewer]
settings: default
---
# Layer 2: Execution blueprint — applied per task
# extends inherits agents; switches context and rules
kind: blueprint
version: "1.0"
name: execute-backend
extends: feature-base
contexts: [backend-context]
rules: [backend-rules]
The dispatch protocol becomes:
ORCHESTRATOR SESSION (project root)
│
├─ Session start: xcaffold apply --blueprint=feature-base --target=claude -y
│ → All agents registered (session-locked from here)
│
├─ Per task:
│ ├── xcaffold apply --blueprint=execute-<domain> --target=claude -y
│ │ → CLAUDE.md switches to domain context
│ │ → Rules switch to domain rules
│ │ → Agents preserved via extends inheritance
│ │
│ └── Agent tool: spawn <domain-worker> subagent
│ → Subagent reads FRESH CLAUDE.md + rules from disk
│ → Domain behavior enforced by context, not prompt engineering
│
└─ Cleanup: delete ephemeral blueprint files if needed
Compare that to the worktree-scoped CLI dispatch approach we explored in experiments 8–13:
| Aspect | Worktree-Scoped CLI | Blueprint-Switch Agent Tool |
|---|---|---|
| Dispatch mechanism | claude --agent from worktree CWD | Native Agent tool (in-session) |
| Context isolation | Worktree .claude/ per domain | Blueprint switch at project root |
| Worktrees needed? | Yes, for both context and git | Only for git isolation |
| Session management | JSON parsing, session_id tracking | Native conversation return |
| Cost per task | ~$0.076 (fresh session each time) | Shared prompt cache |
The native approach is simpler and cheaper.
What This Changes for Worktrees
Worktrees are still useful — but for a different reason. When you need parallel branches, independent commits, or file-level isolation between two concurrent agents, worktrees provide clean separation. What they do not need to provide anymore is context isolation. Blueprint switching handles that natively, without spinning up separate CLI sessions.
The mental model is cleaner: xcaffold blueprints control what the agent knows; worktrees control where the agent writes.
The Lesson
Our initial experiments were wrong because we tested the wrong thing. We asked agents to self-report their context instead of observing their output. The self-reports were shaped by the parent session's framing, not by the subagent's actual loaded context.
Behavioral canaries are a useful technique when testing agent behavior. Design your CLAUDE.md content to produce outputs that cannot be faked by inference. Then watch the filesystem. Observable artifacts — files, API calls, structured output — are more reliable than self-reported state.
If you are building multi-stage agent workflows and wondering whether you need complex worktree orchestration to keep contexts clean: you probably do not. Apply a base blueprint at session start, extend it per task, and let your agent's fresh-from-disk reads do the work.
Try It with Your Stack
The experiments in this post used Claude Code, but xcaffold is provider-agnostic. The same blueprint-switching pattern works wherever your agent reads configuration from disk:
- Claude Code —
xcaffold apply --target=claudewrites to.claude/ - OpenAI Codex —
xcaffold apply --target=codexwrites to.codex/ - Cursor —
xcaffold apply --target=cursorwrites to.cursor/ - Copilot —
xcaffold apply --target=copilotwrites to.github/
The session-lock boundary will differ per provider — which files are cached at session start versus re-read on each tool call is an implementation detail of each agent runtime. But the core idea holds: if your agent re-reads its configuration files when spawning workers or switching tasks, blueprint switching gives you scoped context without complex isolation infrastructure.
We'd be curious to see what you find. If you run similar experiments on Codex, Copilot, Cursor, or another runtime, we want to hear about it — especially if the session-lock boundary lands differently.
Ready to try blueprint switching? Define your first base blueprint, add an execution blueprint that extends it, and test the switch with your own workflow.