Engineering Lab · Reference
Agent Coding Handbook
Claude Code and Codex, from first install to unattended runs. Written to be read once end to end, then opened at a single section on the day you need it.
- Version
- 1.0.0
- Updated
- 2026-09-10
- Sections
- 18
- Sources
- 24 primary
Where to start
- First dayI have never used either toolRead §1 to §3, then stop and do the first task. Come back for §5 once you have corrected the agent twice.
- New repositoryI am setting a project up properly§5 and §6 for the instruction files, §10 to §12 for settings, permissions, and hooks. Copy the starting configuration in §11.
- Something is wrongIt ignored my rule, or did something I did not expect§4 for what actually loaded, §10 for which file won, §11 for how the tool call was decided.
Contents · 18 sections
Part 1. Foundations
Part 2. Context and instructions
Part 3. Execution units
Part 4. Configuration
Part 5. Daily workflow
Part 6. Advanced practice
Part A. Appendix
Part 1
Foundations
What a coding agent actually does, how to install both tools, and how they map onto each other.
Section 1
FoundationBoth toolsA coding agent is a loop, not a chat box
Read this first. Everything else in the handbook configures one part of the loop described here.
A chat model answers once. A coding agent runs a loop: it reads your goal, gathers evidence from the real repository, decides on one action, executes it through a tool, observes the result, and decides again. It stops when the goal is met or when it needs you.
The model supplies reasoning. Everything around it — which files it can see, which commands it may run, what happens after each edit, what counts as done — is the harness. claude and codex are harnesses. Configuring them well is the whole job.
The agent loop, one turn at a time
Every task you give either tool walks this cycle. Failures do not end the run — they re-enter it at the point that produced them.
- gate → act · Gate fails — fix and re-run
- observe → context · Missing evidence — gather more
- approve → plan · Approval denied — re-plan
The six parts you configure
Every feature in the rest of this handbook belongs to exactly one of these. When something goes wrong, name the part first — it tells you which file to open.
| Part | Question it answers | Where you configure it | Handbook section |
|---|---|---|---|
| Instructions | What are the standing rules here? | CLAUDE.md, AGENTS.md, .claude/rules/ | §5, §6 |
| Context | What can the model actually see right now? | Retrieval, /context, compaction | §4 |
| Skills | How is this repeated job done properly? | SKILL.md files | §7 |
| Tools | What is the agent allowed to touch? | Built-in tools, MCP servers | §9 |
| Control | What is blocked, and what needs me? | Permissions, sandbox, hooks | §11, §12 |
| Verification | How do we know it worked? | Quality gates, review, evals | §14, §17 |
Section 2
FoundationBoth toolsInstall both tools and reach a first real task
Get from nothing to a verified, committed change in one sitting.
Install Claude Code
# macOS, Linux, WSL
curl -fsSL https://claude.ai/install.sh | bash
# Windows PowerShell
irm https://claude.ai/install.ps1 | iex
# Homebrew (does not auto-update; run 'brew upgrade claude-code')
brew install --cask claude-code
# WinGet (does not auto-update)
winget install Anthropic.ClaudeCodeNative installs update themselves in the background. On native Windows, install Git for Windows as well — without it Claude Code falls back to PowerShell for its shell tool instead of Bash.
Install Codex
# Install (and later, update — same command)
curl -fsSL https://chatgpt.com/codex/install.sh | sh
# First run signs you in with your ChatGPT account
codexYour first task, in the right order
- Generate the instruction file — Run
/initin either tool. Claude Code writesCLAUDE.md; Codex writesAGENTS.md. Both read the codebase first and propose build commands and conventions. - Read what it wrote — This file is loaded into every future session. Delete anything the agent can already derive from the code, and keep the things it cannot — pitfalls, rationale, conventions that differ from the tool defaults.
- Ask a read-only question first — "Explain how authentication flows through this repo, and cite the files." You are checking whether the agent can find things before you let it change things.
- Give it one bounded change — One file, one behaviour, with a test you can run. Ask for the plan before the edit.
- Verify yourself — Run the build and the tests in your own terminal, then read the diff. Never approve a diff you have not read.
- Write down what you corrected — The first correction you repeat twice belongs in
CLAUDE.mdorAGENTS.md. This is how the setup compounds.
Section 3
FoundationBoth toolsClaude Code and Codex, side by side
The same seven concepts exist in both tools under different filenames. Learn the mapping once.
Both tools are terminal-first agent harnesses with an IDE extension, a cloud runner, and a GitHub integration. They differ in file layout and in how permissions are expressed, not in the shape of the loop.
| Claude Code | Codex | |
|---|---|---|
| Standing instructions | CLAUDE.md (also ./.claude/CLAUDE.md, CLAUDE.local.md) | AGENTS.md (plus AGENTS.override.md) |
| Scoped instruction files | .claude/rules/*.md with paths: frontmatter | Nested AGENTS.md per directory |
| Configuration file | .claude/settings.json (JSON) | .codex/config.toml (TOML) |
| Reusable procedures | .claude/skills/<name>/SKILL.md, run as /name | .agents/skills/<name>/SKILL.md, run as $name |
| Delegated agents | .claude/agents/*.md subagents | Cloud tasks and codex exec runs |
| External tools | .mcp.json / claude mcp add | [mcp_servers.*] in config.toml / codex mcp add |
| Permission control | permissions.allow/ask/deny rules + sandbox | approval_policy + sandbox_mode + permission profiles |
| Lifecycle automation | Hooks on ~30 events in settings.json | No equivalent hook system — use CI and git hooks |
| Non-interactive run | claude -p "..." | codex exec "..." |
| Bootstrap command | /init | /init |
Running both tools on one repository
Claude Code reads CLAUDE.md, not AGENTS.md. Keep one source of truth by writing AGENTS.md and importing it, so a change in shared conventions never has to be made twice.
@AGENTS.md
## Claude Code only
Use plan mode for changes under `src/billing/`.
Run `pnpm test` before reporting a task complete.Part 2
Context and instructions
What the agent sees at the start of a session, what you can put there on purpose, and what survives a long run.
Section 4
Working knowledgeBoth toolsThe context window is a budget you spend
Know exactly what is loaded before you type, and what happens when the session runs long.
Every session starts with a fresh context window and immediately fills part of it before you say anything. Knowing the running order tells you why an instruction was ignored — usually it was never loaded at all.
| # | What loads | Visible to you? |
|---|---|---|
| 1 | System prompt — behaviour, tool use, response format | No |
| 2 | Auto memory — first 200 lines or 25 KB of MEMORY.md | No |
| 3 | Environment info — cwd, platform, shell, git branch and status | No |
| 4 | MCP tool names (schemas stay deferred until needed) | No |
| 5 | Skill descriptions — names and one-liners, not bodies | No |
| 6 | ~/.claude/CLAUDE.md — your personal instructions | Yes |
| 7 | Project CLAUDE.md and unscoped .claude/rules/*.md | Yes |
| 8 | Your prompt | Yes |
Run /context in a session to see the real numbers for the current run, including which memory files actually loaded. If a file you wrote is not in that list, the agent cannot see it — no amount of rewording will help.
What survives compaction
When the window fills, Claude Code summarizes the conversation and continues. Content loaded from disk is re-injected; content that only ever existed in the conversation is summarized away. This table is the single most useful thing to know about long sessions.
| Loaded as | After compaction |
|---|---|
| System prompt and output style | Still apply |
Project-root CLAUDE.md and unscoped rules | Re-injected from disk |
| Auto memory | Re-injected from disk |
| The plan written in plan mode | Re-injected from disk |
Rules with paths: frontmatter | Reloaded when a matching file is next read |
Nested CLAUDE.md in subdirectories | Reloaded when a file in that directory is next read |
| Files the agent read or edited | Up to five re-read, most recently modified first |
| Invoked skill bodies | Re-injected, capped at 5,000 tokens each and 25,000 total |
| Anything you only said in chat | Summarized — treat it as gone |
Section 5
Working knowledgeBoth toolsCLAUDE.md and AGENTS.md — the files that shape every session
Write instructions that are actually followed: correctly placed, specific, and short.
Where Claude Code looks
| Scope | Location | Use it for |
|---|---|---|
| Managed policy | macOS /Library/Application Support/ClaudeCode/CLAUDE.md · Linux and WSL /etc/claude-code/CLAUDE.md · Windows C:\Program Files\ClaudeCode\CLAUDE.md | Organization standards nobody can opt out of |
| User | ~/.claude/CLAUDE.md | Your preferences across every project |
| Project | ./CLAUDE.md or ./.claude/CLAUDE.md | Team conventions, committed to git |
| Local | ./CLAUDE.local.md (gitignore it) | Your sandbox URLs, personal test data |
Files in directories above your working directory load at launch, ordered from the filesystem root down, so the file closest to where you started is read last. Files in subdirectories load on demand when the agent reads a file there.
Where Codex looks
Codex builds an instruction chain at startup: global first, then project files from the git root down to your working directory. In each location it checks AGENTS.override.md before AGENTS.md. Files closer to your current directory appear later in the combined prompt and therefore override earlier guidance. The chain stops once it reaches project_doc_max_bytes — 32 KiB by default.
- ~/.codex/AGENTS.mdYour personal working agreements, every repo
- my-repo/
- └─ AGENTS.mdRepository expectations, committed
- └─ services/
- └─ payments/
- └─ AGENTS.override.mdWins over the root file for this subtree
codex --ask-for-approval never "Summarize the current instructions."How to write instructions that get followed
- Stay under 200 lines — Longer files consume context and measurably reduce adherence. If it is growing, that content belongs in a path-scoped rule or a skill.
- Be concrete enough to verify — "Use 2-space indentation" beats "format code properly". "Run
pnpm testbefore committing" beats "test your changes". - Write what cannot be derived — Skip the directory listing and the dependency list — the agent can read those. Keep the pitfalls, the rationale, and the conventions that differ from the tool defaults.
- Remove contradictions — When two files disagree, the agent picks one arbitrarily. Review the whole set periodically, including nested files.
- Add an entry the second time you repeat yourself — The first correction is a conversation. The second is a missing instruction.
Imports
CLAUDE.md can pull in other files with @path/to/file, resolved relative to the importing file, up to four hops deep. Imports are expanded at launch, so they organize content — they do not reduce what it costs. Wrap a path in backticks to mention it without importing it.
See @README for the project overview and @package.json for available commands.
# Additional instructions
- Git workflow: @docs/git-instructions.md
- Personal preferences shared across worktrees: @~/.claude/my-project-instructions.mdSection 6
Working knowledgeClaude CodeRules and memory — instructions that load only when relevant
Split a growing instruction file into path-scoped rules, and understand the notes the agent writes about you.
Path-scoped rules
.claude/rules/ holds one markdown file per topic, discovered recursively. A rule with no frontmatter loads at launch with the same priority as .claude/CLAUDE.md. A rule with paths: frontmatter loads only when the agent reads a matching file — which is how you keep backend conventions out of a frontend task.
---
paths:
- "src/api/**/*.ts"
- "src/**/*.{controller,service}.ts"
---
# API rules
- Every endpoint validates its input before touching a service.
- Errors use the shared error envelope, never a bare string.
- A new endpoint ships with an integration test in the same PR.paths:| Pattern | Matches |
|---|---|
**/*.ts | All TypeScript files in any directory |
src/**/* | Everything under src/ |
*.md | Markdown files in the project root only |
src/components/*.tsx | Components in one specific directory |
src/**/*.{ts,tsx} | Brace expansion — two patterns from one line |
Personal rules in ~/.claude/rules/ apply to every project and load before project rules, so project rules take priority. The directory supports symlinks, which is the clean way to share one rule set across several repositories.
Auto memory: the notes the agent keeps
Separately from anything you write, Claude Code saves short notes as it works — your preferences, corrections you gave it, and project facts it cannot derive from the code. They live in plain markdown you can read, edit, or delete.
~/.claude/projects/<project>/memory/- MEMORY.mdIndex — first 200 lines or 25 KB load every session
- user_role.mdOne memory, read on demand
- feedback_testing.mdOne memory, read on demand
| CLAUDE.md | Auto memory | |
|---|---|---|
| Who writes it | You | The agent |
| What it holds | Instructions and rules | Learnings, preferences, corrections |
| Scope | Project, user, or organization | Per repository, shared across worktrees, machine-local |
| Use it for | Standards, workflows, architecture | Things you would otherwise re-explain every session |
{
"autoMemoryEnabled": false
}Part 3
Execution units
Skills, subagents, MCP servers, and plugins — the four ways to give an agent more capability without a longer prompt.
Section 7
Working knowledgeBoth toolsSkills — a procedure the agent loads only when it needs it
Turn any workflow you have pasted into chat twice into a file the agent can run properly.
A skill is a folder with a SKILL.md in it. Unlike an instruction file, the body costs nothing until it is invoked — which is why a skill can be long and detailed where CLAUDE.md must be short.
| Tool | Scope | Path | Invoke with |
|---|---|---|---|
| Claude Code | Project | .claude/skills/<name>/SKILL.md | /name |
| Claude Code | Personal | ~/.claude/skills/<name>/SKILL.md | /name |
| Claude Code | Plugin | <plugin>/skills/<name>/SKILL.md | /name |
| Codex | Project | .agents/skills/<name>/SKILL.md | $name |
| Codex | Repo root | $REPO_ROOT/.agents/skills/ | $name |
| Codex | Personal | ~/.agents/skills/ | $name |
Anatomy of a skill
---
name: review-diff
description: Reviews uncommitted changes for correctness and risk. Use before opening a PR.
allowed-tools: Bash(git diff:*) Read Grep
disable-model-invocation: true
---
## Current changes
!`git diff HEAD`
## Instructions
1. Group the diff by intent, not by file.
2. For each group, state the behaviour change in one sentence.
3. Flag: missing error handling, untested branches, widened permissions,
and any change to a public contract.
4. End with a GO / NO-GO line and the single most important reason.The ` !git diff HEAD ` line is dynamic context injection: the command runs and its output is inserted before the model reads the skill, so the review is grounded in the real diff rather than in a description of one.
SKILL.md frontmatter| Field | What it does |
|---|---|
name | Identifier; defaults to the directory name |
description | When the agent should invoke this on its own — the single most important field |
allowed-tools | Pre-approve tools for this turn, skipping permission prompts |
disable-model-invocation | true means only you can run it — use for deploys, commits, anything irreversible |
user-invocable | false means only the agent runs it, never /name |
paths | Glob patterns limiting when the skill can activate |
context: fork | Run the skill in an isolated subagent instead of the main conversation |
Codex uses the same two required fields — name and description — and adds an optional agents/openai.yaml for display name, icon, implicit-invocation policy, and MCP tool dependencies. Bundle supporting material in scripts/, references/, and assets/ beside the SKILL.md in either tool.
| If the content is… | Put it in |
|---|---|
| A fact true in every session | CLAUDE.md / AGENTS.md |
| A convention for one part of the tree | .claude/rules/ with paths: |
| A multi-step procedure with its own checks | A skill |
| Something that must happen regardless of what the model decides | A hook (§12) |
Section 8
AdvancedClaude CodeSubagents — delegate with a clean context and a narrow remit
Use a second context window when the work is separable and the noise is expensive.
A subagent is a separate context window with its own system prompt, tool allowlist, and model. It receives a task, works independently, and returns a summary — the intermediate file reads never enter your main session. That is the point: you keep the conclusion, not the search.
Delegate when the answer requires reading across many files and you only need the finding, or when you want a genuinely independent opinion on work the main session just produced. Do not delegate a single-file lookup — the coordination costs more than the search.
---
name: backend-reviewer
description: Reviews backend changes for data-safety and API-compatibility risk. Use after a service or migration is edited.
tools: Read, Grep, Glob, Bash(./gradlew test:*)
model: sonnet
permissionMode: plan
memory: project
color: cyan
---
You review backend changes. You never edit files.
For every finding, give: the file and line, the concrete failure scenario
(inputs and state that produce the wrong result), and the smallest fix.
Rank findings by severity. If you find nothing, say so in one line —
do not invent findings to fill the report.| Field | Effect |
|---|---|
name, description | Required. The description is what triggers automatic delegation. |
tools / disallowedTools | Allowlist, or subtract from the inherited set. A reviewer with no write tools cannot edit. |
model | sonnet, opus, haiku, fable, a full ID, or inherit |
permissionMode | default, acceptEdits, auto, dontAsk, bypassPermissions, plan |
maxTurns | Hard stop after N agentic turns; the result is marked partial |
skills | Preload specific skill bodies into the subagent's context |
mcpServers | Which MCP servers this subagent may reach |
memory | user, project, or local — its own memory, separate from the main session |
isolation: worktree | Run in its own git worktree so parallel agents cannot collide |
effort | low … max, overriding the session default |
# 1. Automatic — the agent matches your task to the description
# 2. By name in a prompt
"Have backend-reviewer look at the migration"
# 3. Guaranteed, via @-mention typeahead
@"backend-reviewer (agent)" review src/db/migrations
# 4. Run the whole session as that agent
claude --agent backend-reviewerSection 9
AdvancedBoth toolsMCP and plugins — connecting the agent to everything else
Give the agent typed access to your database, tracker, and design tools without pasting credentials into a prompt.
The Model Context Protocol is an open standard for exposing tools and resources to any compatible agent. An MCP server advertises named capabilities with schemas; the agent discovers and calls them. Because the contract is typed and the credentials live in the server, the agent never sees the secret — it only sees the tool.
Claude Code
# stdio server, project scope so the team gets it via git
claude mcp add --transport stdio --scope project postgres -- npx -y @bytebase/dbhub
# Remote HTTP server with a header
claude mcp add --transport http github https://api.githubcopilot.com/mcp/ \
-H "Authorization: Bearer ${GITHUB_TOKEN}"
claude mcp list # what is configured
claude mcp get <name> # one server's details
claude mcp login <name> # OAuth flow
claude mcp remove <name>| Scope | File | Shared with the team? |
|---|---|---|
local (default) | ~/.claude.json | No — this project, just you |
project | .mcp.json at the project root | Yes, via git |
user | ~/.claude.json | No — all your projects |
{
"mcpServers": {
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/",
"headers": { "Authorization": "Bearer ${GITHUB_TOKEN}" }
},
"database": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@bytebase/dbhub"],
"env": { "DB_URL": "${DATABASE_URL:-postgresql://localhost/mydb}" }
}
}
}${VAR} and ${VAR:-default} are expanded from your environment, so the file itself stays safe to commit. Project-scoped servers require an explicit approval the first time an interactive session sees them.
Codex
[mcp_servers.context7]
command = "npx"
args = ["-y", "@upstash/context7-mcp"]
env_vars = ["LOCAL_TOKEN"]
[mcp_servers.figma]
url = "https://mcp.figma.com/mcp"
bearer_token_env_var = "FIGMA_OAUTH_TOKEN"
[mcp_servers.chrome_devtools]
url = "http://localhost:3000/mcp"
enabled_tools = ["open", "screenshot"]
default_tools_approval_mode = "prompt"
tool_timeout_sec = 45codex mcp add context7 -- npx -y @upstash/context7-mcp
codex mcp add example --url https://mcp.example.com --oauth-client-id my-client
codex mcp login example
codex mcp listPlugins
A plugin is an installable bundle — skills plus MCP servers plus, in Claude Code, subagents, commands, and hooks — distributed through a marketplace. Reach for a plugin when you want one install to deliver both the instructions and the connected service; reach for a bare skill when you only need instructions.
Part 4
Configuration
The files, the precedence between them, the permission model, and the hooks that enforce what prose cannot.
Section 10
Working knowledgeBoth toolsConfiguration files and which one wins
Know every file in play and the exact order they resolve in — most configuration bugs are precedence bugs.
The `.claude` directory
- your-project/
- └─ CLAUDE.mdInstructions read every session
- └─ CLAUDE.local.mdYour personal overrides — gitignore this
- └─ .mcp.jsonProject MCP servers, shared with the team
- └─ .worktreeincludeGitignored files to copy into new worktrees
- └─ .claude/
- └─ settings.jsonPermissions, hooks, model — committed
- └─ settings.local.jsonYour overrides for this project — gitignored
- └─ rules/Topic instructions, optionally path-gated
- └─ skills/Reusable procedures, one folder each
- └─ agents/Subagents with their own context window
- └─ commands/Single-file prompts — skills supersede these
- └─ workflows/Scripts that orchestrate many subagents
- └─ output-styles/Instruction sets that adjust how the agent writes
~/, applies to every project- ~/.claude/
- └─ CLAUDE.mdYour preferences everywhere
- └─ settings.jsonYour defaults for all projects
- └─ keybindings.jsonCustom keyboard shortcuts
- └─ themes/Custom colour themes
- └─ rules/ · skills/ · agents/ · workflows/Personal versions of each
- └─ projects/<project>/memory/Auto memory — the agent writes this
- ~/.claude.jsonApp state, UI preferences, local MCP servers
Settings precedence, highest first
| # | Layer | File | Who it is for |
|---|---|---|---|
| 1 | Managed | managed-settings.json, MDM, or the claude.ai console | Your organization — you cannot override it |
| 2 | Command line | claude --settings <file> | You, this session only |
| 3 | Project local | .claude/settings.local.json | You, this project |
| 4 | Shared project | .claude/settings.json | Everyone on the project, via git |
| 5 | User | ~/.claude/settings.json | You, every project |
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": ["Bash(pnpm run lint)", "Bash(pnpm run test:*)"],
"ask": ["Bash(git push:*)"],
"deny": ["Read(./.env)", "Read(./.env.*)", "Read(./secrets/**)"],
"additionalDirectories": ["../shared-types"],
"defaultMode": "default"
},
"env": { "NODE_ENV": "test" },
"autoMemoryEnabled": true
}Codex `config.toml`
| # | Source |
|---|---|
| 1 | CLI flags and --config overrides |
| 2 | Project config — .codex/config.toml, closest to the working directory wins |
| 3 | Profile files — ~/.codex/<profile>.config.toml |
| 4 | User config — ~/.codex/config.toml |
| 5 | Cloud-managed defaults |
| 6 | System config — /etc/codex/config.toml |
| 7 | Built-in defaults |
model = "gpt-5.6"
approval_policy = "on-request" # or "never"
sandbox_mode = "workspace-write"
default_permissions = ":workspace" # :read-only | :workspace | :danger-full-access
web_search = "cached" # cached | indexed | live | disabled
[windows]
sandbox = "elevated" # fall back to "unelevated" if needed
[permissions.review-only]
# a named profile you can switch intoSection 11
Working knowledgeBoth toolsPermissions and sandboxing
Decide exactly what runs without you, what stops for you, and what never runs at all.
Two independent layers protect you. Permissions decide which tool calls the harness will make; they cover every tool. Sandboxing is OS-level enforcement on shell commands and their child processes. Permissions can be talked around by a convincing prompt injection; a sandbox cannot. Use both.
How one tool call is decided (Claude Code)
Rules are evaluated deny, then ask, then allow. The first match in that order decides — specificity never reorders it.
- prompt → call · "Yes, don't ask again" writes a new allow rule
Rule syntax
Every rule is Tool or Tool(specifier). A bare tool name in deny removes the tool from the agent's context entirely, so it never even sees it. A scoped rule leaves the tool available and blocks only the matching calls.
| Rule | Matches |
|---|---|
Bash | Every shell command |
Bash(pnpm run build) | That exact command |
Bash(pnpm run test:*) | Any command starting with that prefix |
Read(./.env) | Reading .env in the current directory |
Read(./secrets/**) | Reading anything under secrets/ |
Edit(/src/**/*.ts) | Editing TypeScript under src/, anchored at the settings source |
Read(//Users/alice/keys/**) | An absolute path — note the double leading slash |
WebFetch(domain:example.com) | Fetches to that host |
WebFetch(domain:*.example.com) | Any subdomain, but not the apex |
mcp__github | Every tool from the github MCP server |
mcp__github__create_issue | One specific MCP tool |
Agent(Explore) | Use of the Explore subagent |
Rules match each subcommand of a compound command independently, so Bash(safe-cmd *) does not approve safe-cmd && rm -rf /. Deny and ask rules also reach inside subshells, command substitutions, and loop bodies. Environment-runner wrappers such as npx, docker exec, and devbox run are not stripped — write one rule per inner command rather than trusting Bash(devbox run *).
Permission modes
defaultMode| Mode | Behaviour |
|---|---|
default | Prompts on first use of each tool. Shown as Manual in the UI. |
acceptEdits | Auto-accepts file edits and common filesystem commands inside the working directories |
plan | Reads and explores, never edits source. The right mode for "tell me how you would do this". |
auto | Auto-approves with background safety checks that verify actions match your request |
dontAsk | Auto-denies anything not pre-approved. Good for unattended runs with a tight allowlist. |
bypassPermissions | Skips prompts. Containers and disposable VMs only. |
| Mode | Sandbox | Approvals | Behaviour |
|---|---|---|---|
| Ask for approval (default) | workspace-write | on-request | Reads and edits inside the workspace; asks before the network or anything outside it |
| Approve for me | workspace-write | reviewed automatically | Same boundary; requests for extra access are reviewed for you rather than by you |
| Full access | unrestricted | none | Any file, any command, network included. Materially raises the risk of loss and leaks. |
Changing who reviews a request never widens the sandbox — the two are separate dials. In the Codex CLI, /permissions manages both.
{
"permissions": {
"deny": [
"Read(./.env)", "Read(./.env.*)",
"Read(./**/credentials.json)", "Read(~/.ssh/**)",
"Bash(curl:*)", "Bash(git push --force:*)"
],
"ask": ["Bash(git push:*)", "Bash(gh pr merge:*)"],
"allow": [
"Bash(pnpm install)", "Bash(pnpm run:*)",
"Bash(git status)", "Bash(git diff:*)", "Bash(git log:*)"
],
"defaultMode": "default",
"disableBypassPermissionsMode": "disable"
}
}Section 12
AdvancedClaude CodeHooks — automation that does not depend on the model remembering
Attach deterministic shell commands to lifecycle events so critical checks always run.
A hook is a shell command bound to an event. It runs whether or not the model thought of it, which is precisely why it is the right home for formatting, secret scanning, and any rule you have written into CLAUDE.md twice and watched get skipped anyway.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit",
"hooks": [
{ "type": "command", "command": "pnpm exec prettier --write \"$CLAUDE_FILE_PATHS\"" }
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/scan-secrets.sh" }
]
}
]
}
}| Event | Fires when | Blocking |
|---|---|---|
SessionStart | A session begins, resumes, clears, compacts, or forks | Exit 2 prevents the session starting |
UserPromptSubmit | Before your prompt is processed | Exit 2 rejects the prompt |
PreToolUse | Before a tool runs — matcher is the tool name | Exit 2 blocks the call |
PostToolUse | After a tool succeeds | Exit 2 stops the agent from finishing |
PostToolUseFailure | After a tool fails | Exit 2 stops the agent from finishing |
Stop | The agent is about to finish responding | Exit 2 makes it keep working |
SubagentStart / SubagentStop | A subagent spawns or finishes | Stop can block with exit 2 |
PreCompact / PostCompact | Around context compaction | PreCompact can block |
FileChanged | A watched file changes — matcher is a filename pattern | Exit 2 prevents the change |
InstructionsLoaded | Instruction files load — invaluable for debugging | Informational |
Notification | A notification is sent, e.g. a permission prompt | Informational |
SessionEnd | The session terminates | Informational |
There are around thirty events in total, covering permission decisions, task lifecycle, model switches, worktrees, config changes, and MCP elicitation. The full list is in the hooks reference; the twelve above cover most real setups.
The exit-code contract
| Exit code | Meaning |
|---|---|
0 | Success. Valid JSON on stdout is honoured. |
2 | Block the action. stderr is shown to the agent as the reason. |
| anything else | Non-blocking error — the action still proceeds. |
#!/usr/bin/env bash
# PreToolUse hook: refuse commands that would print a secret file.
set -euo pipefail
payload="$(cat)" # hook input arrives on stdin as JSON
command="$(jq -r '.tool_input.command // ""' <<<"$payload")"
if grep -Eq '(^|[[:space:]])(cat|less|head|tail)[[:space:]]+.*\.env' <<<"$command"; then
echo "Refusing: this command would print .env to the transcript." >&2
exit 2
fi
exit 0Part 5
Daily workflow
The loop to run every day, the gates that decide when a stage is finished, and how to run the whole thing unattended.
Section 13
Working knowledgeBoth toolsThe development loop
One pipeline, eight stages, each with a deliverable that feeds the next.
The failure mode of agent-assisted work is not bad code — it is unbounded work. A stage that never declares itself finished quietly turns into a 40-file diff nobody can review. Give every stage an input, a deliverable, and a criterion that says when it is done.
The pipeline
Each stage produces the next stage's input. An unmet criterion stops progression rather than deferring the problem downstream.
- test → implement · Red — fix, then re-run (cap the retries)
- review → plan · Scope changed — re-plan, do not patch over it
| Stage | Input | Done when | When unmet |
|---|---|---|---|
| Frame | Ticket, bug report, idea | The goal, the constraints, and the definition of done are written down in one paragraph | Ask, do not assume. An unframed task produces confident work on the wrong problem. |
| Explore | The framed goal | The agent names the files it will change and cites why, without having changed anything | Widen the search or point it at an entry point yourself |
| Plan | Exploration findings | Ordered steps, each with the check that proves it | Steps that cannot be verified get split until they can |
| Implement | The approved plan | One coherent change, built, matching the plan | Scope drift returns to Plan rather than continuing |
| Test | The change | Suite green, and a new test covers the new behaviour | Re-enter Implement with the failure list; after three rounds, take over |
| Review | The full cumulative diff | No blocking findings; every finding has a file, a line, and a failure scenario | Fix, then re-review only the changed part |
| Commit | Reviewed change | Conventional commit on a feature branch, pushed | Convention or protected-branch violations are fixed before retry |
| Handoff | Session end | What is done, what is verified, what is unresolved, and the next safe command | An unverified item is stated as unverified — never as complete |
Plan before you let it write
Plan mode is the highest-leverage habit in the whole handbook. The agent reads and explores but cannot edit, so you get to inspect the intent before the diff exists — and a bad plan costs you thirty seconds instead of a revert.
# Claude Code — Shift+Tab cycles the permission mode, or:
claude --permission-mode plan
# Persist it as the default for a risky repository, in .claude/settings.json:
# { "permissions": { "defaultMode": "plan" } }
# Codex — review without touching the working tree:
codex reviewSection 14
Working knowledgeBoth toolsQuality gates and review
Define what counts as evidence, so "it works" stops being an opinion.
A plausible implementation is not a verified one. A gate is a check with a binary outcome that must pass before work advances — compile, test, lint, type-check, security scan, and a review pass that did not write the code.
| Gate | Enforce it as | Why there |
|---|---|---|
| Formatting | PostToolUse hook on Edit | Deterministic, instant, and never worth a token of the model's attention |
| Secret scanning | PreToolUse hook on Bash, plus a deny rule on .env | Must hold even when a prompt injection is trying to talk past it |
| Type-check and unit tests | A command in CLAUDE.md the agent is told to run | The agent needs the output to fix its own work |
| Integration tests | CI on the pull request | Too slow for the inner loop; too important to skip |
| Independent review | A reviewer subagent, then a human | The context that wrote the code is the worst context to judge it |
| Human approval | The permission prompt and the PR | Accountability does not delegate |
Ask for a review that is falsifiable
"Review this code" produces a list of style opinions. Demand a concrete failure scenario for every finding and the list gets short, specific, and worth reading.
Review the uncommitted diff for correctness only — not style.
For each finding give exactly:
1. file:line
2. the inputs or state that produce the wrong behaviour
3. the observable wrong result
4. the smallest fix
Rank by severity. If a finding is a guess, label it PLAUSIBLE, not CONFIRMED.
If you find nothing, say so in one line. Do not pad the list.Both tools ship review entry points: /code-review and the ultrareview and Code Review integrations in Claude Code, and codex review plus /review in Codex. Use them for the first pass, and read the diff yourself for the second — the review is a filter, not a substitute.
Section 15
AdvancedBoth toolsHeadless runs, CI, and scheduled work
Run the agent where nobody is watching, safely.
Both tools are composable Unix programs. Piping into them and running them from CI is where the time actually comes back — the interactive session is for work that needs judgement, and everything else should be a script.
# Claude Code — one-shot, prints and exits
claude -p "translate new strings into French and open a PR"
# Feed it a stream
tail -200 app.log | claude -p "summarize anomalies as a bullet list"
git diff main --name-only | claude -p "review these files for security issues"
# Codex — the same shape
codex exec "add a regression test for the null-tenant bug"
codex resume # pick up where a previous run stoppedRules for unattended runs
- Deny by default — Run in
dontAskmode with an explicit allowlist, or a Codex permission profile scoped to what the job needs. A run with nobody to answer a prompt should fail closed, not proceed. - Sandbox it — A container or an ephemeral VM, with no production credentials mounted. The blast radius should be the checkout, and nothing else.
- Bound the run —
maxTurnson subagents, a job timeout in CI, and a token budget. An agent in a loop is expensive long before it is dangerous. - Produce a reviewable artefact — A pull request, not a push to main. The output of an unattended run is a proposal.
- Keep the trace — Log which context loaded, which commands ran, and what failed. Without the trace an unattended failure is unexplainable.
| Need | Claude Code | Codex |
|---|---|---|
| Automate PR review and triage | GitHub Actions, GitLab CI/CD, Code Review | GitHub integration |
| Long tasks off your machine | Claude Code on the web, claude --cloud | Codex cloud, codex apply |
| Recurring schedule | Routines (cloud), desktop scheduled tasks, /loop | Automations |
| From team chat | Slack — mention @Claude | Slack integration |
| Custom orchestration in your own code | Agent SDK (TypeScript, Python) | Codex SDK |
Part 6
Advanced practice
Running many agents at once without collisions, and treating your own setup as something to measure and improve.
Section 16
AdvancedBoth toolsParallel agents and worktrees
Run several agents at once without them editing the same file underneath each other.
Parallelism pays when tasks are genuinely independent — three separate bugs, or one review split across dimensions. It costs when they are not: two agents in one working tree will overwrite each other, and merging their reasoning is slower than doing the work once.
The isolation primitive is the git worktree: a second checkout of the same repository on its own branch, in its own directory. Each agent gets a real filesystem it cannot share, and you merge through the normal review path.
git worktree add ../repo-auth-fix -b fix/auth-refresh
git worktree add ../repo-perf -b perf/query-plan
git worktree list
git worktree remove ../repo-auth-fix # when the branch has mergedClaude Code automates this. A subagent with isolation: worktree in its frontmatter runs in a temporary worktree that is cleaned up if it changed nothing. .worktreeinclude in the project root lists the gitignored files — .env.local, build caches — that must be copied into each new worktree for the project to actually run there.
| Mechanism | Use when | Isolation |
|---|---|---|
| Subagent | You need a finding, not a diff — search, review, research | Separate context, same working tree |
Subagent with isolation: worktree | The delegated task will edit files | Own branch and checkout |
| Agent view / background agents | Several full sessions you want to watch from one screen | Separate sessions |
| Agent teams and cross-session messaging | Long-running sessions that must coordinate | Separate sessions that can message each other |
| Dynamic workflows | A deterministic fan-out — N dimensions, each verified | Scripted orchestration of many subagents |
Codex approaches the same problem from the cloud side: start several cloud tasks in parallel and bring each result back into your checkout with codex apply. The isolation is the remote environment rather than a local worktree, and the merge point is still a reviewed diff.
Section 17
AdvancedBoth toolsMeasuring and improving your own setup
Treat your configuration as a system with a failure rate you can watch go down.
Prompts, instruction files, and tool definitions are code with no test suite by default. A change that feels like an improvement can quietly break a behaviour you relied on, and you will not find out until it costs you an afternoon.
A minimal eval loop
- Collect real failures — Every time you correct the agent, save the task and the wrong output. Ten of these is already a useful suite.
- Write the expected behaviour, not the expected text — "Refuses to widen the permission scope" is checkable. "Says the right thing" is not.
- Re-run the set after every configuration change — New rule, new skill, new model — the same ten tasks, and you compare.
- Grade deterministically where you can — Exit codes, file diffs, and "did it touch a forbidden path" beat a subjective judgement every time.
- Keep the trace, not just the verdict — Which context loaded, which tool ran, where it turned wrong. A failed eval with no trace tells you nothing actionable.
Observability
/context shows what is loaded right now. The InstructionsLoaded hook logs which instruction files loaded, when, and why. claude doctor prints the fully resolved configuration and flags rules it had to skip. Between them you can answer nearly every "why did it do that?" question without guessing.
Cost and context discipline
| Symptom | Usual cause | Fix |
|---|---|---|
| Sessions compact constantly | A large instruction file loading every time | Move content into path-scoped rules and skills |
| The agent re-reads the same files each turn | No plan, so no memory of what it already knows | Plan first; the plan survives compaction |
| Answers drift late in a session | Key facts were summarized away | Put them in CLAUDE.md so they are re-injected |
| A wide search burns the whole window | Exploration in the main context | Delegate it to a subagent and keep only the conclusion |
| Costs spike with no more output | An unbounded retry loop | maxTurns, timeouts, and a hard retry cap |
Part A
Appendix
The commands and vocabulary to keep open in a second tab.
Section A
FoundationBoth toolsCommand cheat sheet
Everything you reach for in a normal week, in one place.
| Command | What it does |
|---|---|
claude | Start an interactive session in the current directory |
claude -p "…" | One-shot run: print the answer and exit |
claude --resume | Resume a previous session in this directory |
claude --agent <name> | Run the whole session as a named subagent |
claude --permission-mode plan | Start in plan mode — explore without editing |
claude --add-dir <path> | Grant access to a directory outside the working tree |
claude --settings <file> | Load an extra settings file for this session only |
claude mcp add|list|get|remove | Manage MCP servers |
claude doctor | Print the resolved configuration and flag broken rules |
claude --teleport | Pull a web or mobile session into your terminal |
| Command | What it does |
|---|---|
/init | Generate or improve CLAUDE.md from the codebase |
/context | Show what is actually loaded, with token counts |
/memory | Browse and edit memory files; toggle auto memory |
/permissions | View and edit permission rules, with their source file |
/compact | Compact the conversation now, on your terms |
/clear | Start fresh — the right move between unrelated tasks |
/mcp | Inspect connected MCP servers and their tools |
/code-review | Review the current diff |
/doctor | Configuration checkup, including CLAUDE.md trim suggestions |
/cd <path> | Move the session to a different working directory |
Shift+Tab | Cycle the permission mode without leaving the session |
| Command | What it does |
|---|---|
codex | Start an interactive session |
codex exec "…" | Non-interactive run |
codex resume | Continue a previous session |
codex review | Review uncommitted changes without touching the tree |
codex apply | Bring a cloud task's changes into your checkout |
codex cloud | Manage cloud runs |
codex mcp add|list|login | Manage MCP servers |
codex --ask-for-approval never "…" | Run with approvals off — read-only checks only |
/init | Generate AGENTS.md |
/status | Session details |
/permissions | Configure sandbox and approval behaviour |
/model | Pick the model and reasoning effort |
/review | Review the current changes |
$skill-name | Invoke a skill |
Vocabulary
| Term | Meaning |
|---|---|
| Harness | Everything around the model: context, tools, permissions, workflow, verification, state |
| Context window | The finite budget of text the model reasons over in one turn |
| Compaction | Summarizing the conversation to make room; disk-loaded content is re-injected |
| Instruction file | CLAUDE.md / AGENTS.md — context the model reads, not a rule it obeys |
| Rule | A topic-scoped instruction file, optionally gated by file path |
| Skill | A procedure in a SKILL.md, loaded only when invoked |
| Subagent | A delegated task in its own context window, returning a summary |
| MCP | Model Context Protocol — the open standard for exposing tools to agents |
| Hook | A shell command bound to a lifecycle event; runs regardless of the model |
| Permission rule | Tool(specifier) in allow / ask / deny; enforced by the harness |
| Sandbox | OS-level restriction on shell commands and their children |
| Quality gate | A binary check that must pass before work advances |
| Eval | A repeatable scenario that measures whether the setup still behaves |
| Handoff | The transfer of verified state, evidence, and open risk at a boundary |
Section B
Source library
Every factual claim above traces to one of these. When a tool ships a change, re-check it here rather than trusting the handbook.
Anthropic
- Claude Code documentationoverview · install · surfaces
- Explore the .claude directoryfile layout · scopes
- How Claude remembers your projectCLAUDE.md · rules · auto memory
- Explore the context windowstartup order · compaction
- Settings files and precedencesettings.json · precedence
- Configure permissionsallow · ask · deny · rule syntax
- Configure the sandboxed Bash toolsandbox · network isolation
- Hooks referenceevents · exit codes · matchers
- Extend Claude with skillsSKILL.md · frontmatter · invocation
- Create custom subagentsdelegation · isolation · frontmatter
- Connect Claude Code to tools via MCPMCP · scopes · .mcp.json
- Run parallel sessions with worktreesworktrees · parallel agents
- Best practices for Claude Codeworkflow · prompting
- Agent SDK overviewSDK · custom agents
- Building effective agentspatterns · workflows · when not to
OpenAI
- Codex documentationoverview · surfaces
- Codex CLIinstall · commands · slash commands
- Codex configuration basicsconfig.toml · precedence
- AGENTS.mdAGENTS.md · instruction chain
- Codex permission modesapprovals · sandbox modes
- Build skills for CodexSKILL.md · .agents/skills
- Extend Codex with MCPmcp_servers · TOML
Model Context Protocol
- Model Context Protocol specificationprotocol · tools · resources
Git
- git worktreeworktrees · isolation
This handbook records how I run these tools day to day. Tool behaviour changes; the source library above is the authority, and this page is the summary I keep in sync with it.