Claude Code Session Preflight
Table of content
by Ray Svitla
most Claude Code sessions go wrong before the first prompt.
the package is a week stale. the model fell back to whatever the global config said. the working tree has uncommitted changes from yesterday. a .claude/settings.local.json from a sandbox repo still has an open allow rule. an MCP server you forgot about is wired to a third-party system. the agent runs, returns “done”, and the diff lands somewhere you didn’t intend.
a serious agent session deserves a preflight. five minutes of read-only checks beats an hour of cleanup.
below are the ten checks I run before any Claude Code session that’s going to touch real code, real branches, or real MCP-connected systems. unless explicitly noted, every command is read-only.
the 10-point preflight
| # | check | the question it answers |
|---|---|---|
| 1 | version | is the CLI at a known good version? |
| 2 | model + effort | what model and effort level will actually run? |
| 3 | repo state | is the working tree clean and on the right branch? |
| 4 | permissions | what is allow/deny/ask resolving to here? |
| 5 | MCP servers | which servers will be live, with what scope? |
| 6 | context files | which CLAUDE.md / AGENTS.md / skills will load? |
| 7 | sources | where is the agent allowed to fetch from? |
| 8 | output path | where will the output land — branch, file, PR? |
| 9 | rollback plan | how do you undo this in one command? |
| 10 | stop conditions | when do you kill the run? |
if you can answer all ten in under five minutes, you’re ready. if you can’t, you don’t have a preflight problem — you have an unknown-stack problem, and the agent will surface it the loud way.
1. version
# read-only
claude --version
which claude
npm view @anthropic-ai/claude-code version
@anthropic-ai/claude-code ships fast. don’t memorize a number; run the three commands and compare. drift of more than a couple of point releases is a smell — old isn’t broken, but old means you might be reading docs and release notes that describe behavior you don’t have.
what to check:
- local CLI version vs npm
latest. one or two minor versions of drift is normal; weeks behind is suspicious. - the binary your shell actually runs. homebrew, npm-global, asdf, and a stale shim can all disagree.
- the auto-update channel. in
~/.claude/settings.json,autoUpdatesChannelcontrols whether you ride the stable or latest line. pick one and know which.
failure mode: stale package. you read a release note about a fix and assume you have it. you don’t.
2. model and effort
inside a Claude Code session, run /status. it shows you which settings sources are live and which model and effort the session resolved to.
settings precedence in Claude Code, highest to lowest:
- managed (org policy)
- command-line args
.claude/settings.local.json.claude/settings.json~/.claude/settings.json
a model field in your project local file silently overrides your user file. an effortLevel set high globally will burn budget on a one-line fix you wanted Haiku for. the fields that matter:
| field | purpose |
|---|---|
model | default model id |
effortLevel | persists effort across runs |
availableModels | locks the menu |
alwaysThinkingEnabled | extended thinking default |
failure mode: you think you’re on Sonnet at medium. you’re actually on Opus at the highest tier because a teammate committed .claude/settings.json last sprint.
3. repo state
# read-only
git status -sb
git log --oneline -5
git stash list
git diff --stat HEAD
four lines. they answer: is the tree clean, am I on the branch I think I’m on, are there stashes I forgot, and is there uncommitted work the agent will trample.
then create a session branch so the run is isolated:
# creates a new branch (reversible)
git switch -c session/$(date +%Y-%m-%d)-short-task-name
cheap, reversible, and the branch name tells you in two weeks what the session was for.
failure mode: right repo, wrong branch, dirty tree. the agent’s edits land on top of work you forgot about, the diff looks plausible, and you commit it.
4. permissions
permissions in Claude Code are JSON rules in three buckets — allow, deny, ask — evaluated deny-first, then ask, then allow. arrays merge across scopes, so a project file extends, not replaces, your user file.
read the live state:
# read-only
cat .claude/settings.json 2>/dev/null
cat .claude/settings.local.json 2>/dev/null
cat ~/.claude/settings.json
the rule format is Tool or Tool(specifier). patterns that should never be permanent in allow:
Bash(allow-all on shell)Bash(rm *),Bash(git push *)WebFetchwith no domain restrictionEdit(./**)in a repo that has secrets
a sane denylist for any project that has secrets:
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Bash(curl * | sh)",
"Bash(rm -rf *)"
],
"ask": [
"Bash(git push *)",
"Bash(npm publish *)"
]
}
}
ask is the underused middle. it lets the agent propose, you approve, no full allow-all required.
failure mode: dangerous permissions inherited. a sandbox repo’s allow-all bash rule lives in ~/.claude/settings.json and follows you into a production repo.
5. MCP servers
# read-only
claude mcp list
claude mcp get <name>
inside a session, /mcp shows live status, including OAuth state for remote servers.
three scopes:
| scope | loads in | shared | stored in |
|---|---|---|---|
| local (default) | current project | no | ~/.claude.json |
| project | current project | yes (committed) | .mcp.json at repo root |
| user | all your projects | no | ~/.claude.json |
verify before the session:
- which servers will start when the session opens?
- any of them remote? if yes, what auth, what scope?
- any of them write to systems you can’t easily roll back (payments, ticketing, chat, prod databases)?
- any project-scoped servers from
.mcp.jsonyou haven’t approved? Claude Code prompts on first use;claude mcp reset-project-choicesresets approvals if you need to re-decide.
useful rule: if an MCP server can spend money, send messages, or delete records, it doesn’t belong in a routine coding session. give it its own session with its own settings file.
failure mode: forgotten MCP. you wired a server six weeks ago, never removed it, and an agent run pulls a wall of irrelevant context — or worse, fires off a side effect you didn’t ask for.
6. context files
the agent reads more than your prompt. it reads context files. before a serious session, know what’s loaded:
~/.claude/CLAUDE.md— your user-level instructions<repo>/CLAUDE.md— project-level instructions<repo>/AGENTS.md— the vendor-neutral equivalent.claude/skills/**/*— skills with frontmatter triggers.claude/hooks/and thehooksblock in settings — pre/post-tool actions- subdirectory
CLAUDE.mdfiles for narrower scopes
quick listing:
# read-only
ls -la .claude/ 2>/dev/null
find . -maxdepth 3 \( -name 'CLAUDE.md' -o -name 'AGENTS.md' \) 2>/dev/null
read the project root file. CLAUDE.md and AGENTS.md drift faster than code — instructions that referenced src/legacy/ still sit there a year after src/legacy/ was deleted. stale instructions are not neutral; they teach the agent to do the wrong thing with confidence.
failure mode: missing context files, or worse, present-but-wrong. the agent follows the file. the file is lying.
related: AGENTS.md vs CLAUDE.md vs INSTRUCTIONS.md , AGENTS.md .
7. sources
what is the agent allowed to read from?
- web — does the session need
WebFetch, and to which domains?WebFetch(domain:docs.anthropic.com)is precise; bareWebFetchis open internet. - MCP servers — see above. each one is a source.
- local files —
Read(./**)is broad; narrowerRead(./src/**)is safer if you have asecrets/dir. - env vars — anything in
envin your settings file is in the agent’s context window.
if the task only needs the local repo, deny the rest for the session. it shrinks both the attack surface and the hallucination surface.
failure mode: open-internet fetch in a session that should have been local-only. the agent reads a forum post that contradicts your codebase and “fixes” working code.
8. output path
before you start, write down where the output is going:
- which branch — and is it the session branch from step 3?
- which file paths — and is the agent allowed to write to them?
- which PR target — main? a long-lived feature branch? draft or ready?
- which external destinations — issue tracker, chat, deploy?
if any of those is “I’ll figure it out,” you don’t have an output path; you have an open-ended search. open-ended searches are for exploration sessions, not for serious ones.
failure mode: agent reports success, output is in a different branch, an unexpected file, or a draft PR you never opened.
9. rollback plan
write the rollback before the run, not after.
| if the session… | rollback |
|---|---|
| made commits on a session branch | git switch main && git branch -D session/... |
| made unstaged edits | git restore . (after confirming nothing else is there) |
| made a stash | git stash drop (the specific one, not clear) |
| pushed to a remote | revert commit + force-push only if you own the branch and confirmed |
| sent an external action via MCP | manual reversal in that system; agents don’t auto-undo external state |
the cheapest rollback is “throw away the branch.” that’s why step 3’s session branch matters. anything that escaped the branch — pushed commits, MCP-sent messages, deployed artifacts — is no longer a one-command undo.
failure mode: no rollback. you didn’t branch, you didn’t note the pre-run commit, the agent rewrote three files, and now you’re reading diffs to figure out what was yours.
10. stop conditions
decide in advance what makes you kill the run:
- a tool call fails twice in a row with the same error
- the agent edits a file that wasn’t in the planned output path
- token usage hits a budget you set (a number, not “feels expensive”)
- elapsed time hits a wall-clock limit
- an MCP server starts retrying or hangs
- the agent reports “done” but a verification check fails
a stop condition you didn’t write down is not a stop condition. it’s a vibe.
write them in the prompt or in a scratch note next to the terminal. the point isn’t bureaucracy; it’s that you, not the agent, decide when this is over.
the preflight log
paste this into a scratch file at the start of the session. fill it in as you walk the ten checks. when the run is over, keep the filled log in the repo or a side note — five lines of markdown per session beats trying to remember next week.
session: 2026-mm-dd-<short-task>
head before: <commit sha>
1. version : claude --version →
2. model/effort : /status →
3. branch/status : git status -sb →
4. permissions : sources + key rules →
5. MCP servers : claude mcp list →
6. context files : CLAUDE.md / AGENTS.md / skills →
7. output path : branch + paths + PR →
8. rollback cmd :
9. stop conditions:
head after : <commit sha>
notes : tokens, elapsed, any tool calls outside scope
if you want the read-only checks in one keystroke, alias the three that are pure shell:
# ~/.zshrc or ~/.bashrc — read-only, no side effects
alias ccpf='claude --version; git status -sb; claude mcp list'
the other six need /status, JSON inspection, or your own judgment. those don’t alias honestly. metadata-only is enough — you don’t need transcripts, you need pointers.
when to skip preflight
short list:
- a throwaway scratch repo with no secrets and a clean tree
- a read-only question that won’t trigger any write tools
- a session in a sandboxed environment you already audited today
everything else gets the ten checks. a serious agent session is a deploy. nobody deploys on vibes.
related
→ AGENTS.md vs CLAUDE.md vs INSTRUCTIONS.md — which instruction file the agent actually reads → AGENTS.md — what to put in the file the agent reads first → best MCP servers for Claude Code — what’s worth wiring, what isn’t → agent failure modes — the wider catalog this preflight prevents → Boris Cherny on Claude Code — the harness perspective → agent verification loop — the post-run companion to this preflight