# Your first session with a coding agent

> Start a coding agent in a small repository and give it the context it needs. Then make one change behind a permissions prompt and review the diff before you trust it.

Page: https://schubergphilis.github.io/ai-training/coding-with-agents/first-session/
License: CC BY-SA 4.0, https://creativecommons.org/licenses/by-sa/4.0/

In this lesson we run one complete session with a coding agent. We open
it in a small repository and ask it to explain the code. We point it at a
failing test and let it make one change. Then we read the diff before we
accept it. The repository is a fixture that ships with this course, so nothing you do here
can touch your own work.

A coding agent is an agent that works in your terminal or editor with the
tools a programmer has: it reads and edits files, and it runs commands. Claude
Code is one example, and there are others. The steps in this lesson work
with any of them, and the notes that name Claude Code are for readers who
use it. Wherever it says "your coding agent", use the one you have
installed. If yours is Claude Code, the vendor's own short course
covers installing it and the commands this lesson leaves out, so read it
after this lesson (Claude Code 101, Claude Academy). If you get stuck, the site's
tutor can give you hints on this lesson inside your own agent.
[How to study with the tutor](https://schubergphilis.github.io/ai-training/guides/tutor/) explains the setup.

## The fixture

The fixture is a to-do list command in about seventy lines of Python. It
lives in the course repository under
`site/examples/coding-with-agents/first-session/fixture-repo/`. Copy that
directory out of the course repository into your home directory, and work
in the copy. The course repository has instruction files for agents of its
own, and an agent started inside it can read those in place of the
fixture's. Make the copy a git repository with one commit, so that git can
show you what changed and put it back.

The program needs Python 3.9 or newer and nothing else. On a Mac, the
`python3` that comes with the system is enough, and so is its `git`. On a
fresh Mac the first `git` or `python3` command may open a dialog from Apple
offering to install its developer tools for the terminal (the Xcode Command
Line Tools). Accept it and wait for the install to finish. Then delete
`~/fixture-repo` if it exists, and run the commands below again from the
start.

These commands fetch the course repository into your home directory, copy
the fixture out of it and make the copy a repository. If you already have a
clone of the course, skip the `git clone` line and use your clone's path in
the `cp` line.

```sh
cd ~
git clone https://github.com/schubergphilis/ai-training.git
cp -R ai-training/site/examples/coding-with-agents/first-session/fixture-repo ~/fixture-repo \
  && cd ~/fixture-repo \
  && git init -q \
  && git add . \
  && git commit -qm "fixture as shipped"
```

The `&&` between the commands stops the rest when one of them fails, so
git never runs in the wrong directory. If git asks who you are, follow its
message, and then run `git commit -qm "fixture as shipped"` again inside
`~/fixture-repo`.

Run these commands once. From then on, `git checkout -- .` inside the copy
puts every file back the way it was. Expect to reset it at least once. To
start over from nothing, delete `~/fixture-repo` and run the commands again
from the `cp` line on.

Inside are five files: `todo.py`, the program; `test_todo.py`, its
tests; `todos.json`, a committed list of three items; `README.md`; and
`AGENTS.md`, four lines of instructions for the agent. Run the program
before you run the agent. You want to know what "working" looks like
before someone else changes it.

#### Example: Show the list

Run this, and compare what you see with the output below.

```sh
python3 todo.py list
```

Output:

```text
1. [ ] Buy milk
2. [x] Call the plumber
3. [ ] Water the plants
```

The examples in this lesson aren't graded. Each one is a command you run
in the fixture, with the output the course itself checks on every build.
The checkpoints come later, and they ask about the session, because
working with the agent is what this lesson teaches.

## Start the agent and ask it to explain

Start your coding agent from inside your copy of `fixture-repo`. The
directory you start in is the agent's world: it reads and edits files
there, and it runs commands there. Starting one level too high hands it
your whole home directory.

Check one setting before your first message. This lesson is built around
the moment the agent asks you before it edits a file, and some agents start
in a mode that doesn't ask. Recent versions of Claude Code start in auto
mode, in which file edits in the working directory go through without a
prompt and a second model reviews most of the other actions in place of
you. The mode that asks before each edit is called Manual there, and
`claude --permission-mode default` starts a session in it. In a session
you have already started, Shift+Tab switches from auto mode to Manual,
and in the VS Code extension the mode indicator at the bottom of the
prompt box does the same (Choose a permission mode, Claude Code documentation). The section on
permission modes later in this lesson explains the other choices.

Your first message isn't a task. It is a question, and its purpose is to
check that the agent is looking at the same code you are.

#### Prompt (illustrative, not a recorded transcript)

```text
Explain what this repository does and how the pieces fit together. Don't
change anything.
```

#### Response

```text
This is a small command-line to-do list. `todo.py` stores items in
`todos.json` (or the file named by `TODO_FILE`) and offers three commands:
`add`, `list` and `done`. `test_todo.py` covers the three operations with
`unittest`. `AGENTS.md` asks for standard library only and says not to edit
`todos.json`. The `done` function takes a 1-based item number from the
command line but indexes the list with it directly, which looks like an
off-by-one.
```

The transcripts in this lesson are illustrative: they show what a good
exchange looks like and don't record a specific model's words. Your agent
phrases things differently, and may or may not spot the off-by-one
unprompted. Either is fine, because you are about to hand it the evidence.

Notice what the agent read without being asked: `AGENTS.md`. Most coding
agents load a project-instructions file at start. The lines in that file
set the rules for the whole session, and you didn't have to repeat them.
Claude Code reads `AGENTS.md` when there is no `CLAUDE.md` in the
directory it starts in or in any directory above it. That holds for your
copy of the fixture, and it doesn't hold inside the course repository,
which has a `CLAUDE.md` at its root (How Claude remembers your project, Claude Code documentation).

Many coding agents have a read-only mode, often called plan mode, in which
the agent reads, searches and runs commands to explore, but edits nothing.
It suits this first question. You get the explanation, and the agent doesn't act on it before
you have checked it (Claude Code 101, Claude Academy).

## Find the evidence yourself

Before you ask for a fix, see the failure with your own eyes. It takes ten
seconds, and it gives you something exact to point at.

#### Example: Run the tests

Run the tests and keep only the last line of the output.

```sh
python3 -m unittest -q 2>&1 | tail -1
```

Output:

```text
FAILED (failures=1)
```

Read the failing test. `test_done_marks_the_numbered_item` calls
`todo.done(items, 1)` and expects `"done #1: Buy milk"`. The program says
something else. Prove it against the committed list, on a copy so the
committed file doesn't change.

#### Example: Prove the bug

Mark item 1 as done on a copy of the list. The program answers with the
item it marked.

```sh
cp todos.json /tmp/todos.json
TODO_FILE=/tmp/todos.json python3 todo.py done 1
```

Output:

```text
done #1: Call the plumber
```

Item 1 is "Buy milk", and the program marked "Call the plumber". The
function takes the number you typed and uses it as the list index without
subtracting one.

You now have concrete evidence: the file with the bug, the failing test,
and the wrong output. That's the context the agent needs. A
brief without them makes the agent go and find them, and it may find
something else instead. The hunt has a cost of its own: every file the
agent opens while searching stays in the session's memory. So a short brief
ends up using more of that memory than a precise one
(Claude Code 101, Claude Academy).

## Give the right context

Think back to the brief you wrote in *Delegating a task to an agent*: goal,
context, limits, done-criteria. For a coding agent the context is files and
commands, the limits are which files it may touch, and the done-criterion
is usually a test.

Before you write it, decide what belongs in it. Some of the evidence you
gathered is exactly what the agent needs. Some of it the agent reads
faster than you can type it, and some of it has nothing to do with the
task.

#### Checkpoint: What goes in the brief?

Buckets:

- Goes in the brief
- The agent finds this itself
- Leave it out

Items:

- The name of the failing test
- Which file the agent may edit
- That the failing test must not change
- The command that shows the task is done
- The full text of todo.py
- The four lines of AGENTS.md
- The git history of the fixture
- Your guess at how long the fix should take

#### Checkpoint: Fix the brief

This brief sends the agent hunting. Rewrite it so it names the file,
the failing test, what the agent may and may not change, and how you
know it is done.

```text
The done command is broken, please fix it.
```

## The first change and the permissions prompt

Send your repaired brief. The agent reads `todo.py`, proposes an edit,
and stops. Before it writes the file it asks you.

#### Prompt (illustrative, not a recorded transcript)

```text
Fix the off-by-one in `done()` in `todo.py`: `done 1` marks the
second item. The failing test is `test_done_marks_the_numbered_item` in
`test_todo.py`. Make it pass without changing the test. Only edit
`todo.py`. Don't touch `todos.json`. Done when `python3 -m unittest -q`
reports OK.
```

#### Response

```text
The bug is on the first line of `done()`: `index = number` should be
`index = number - 1`. The bounds check below it already assumes a
zero-based index, so it doesn't need to change.

I would like to edit `todo.py` (1 line). Allow?
```

That question is the permissions prompt. In the mode this lesson uses, a
coding agent asks before it edits a file or runs a command that does more
than read, and the answer is where your judgment enters the session. Ask
about blast radius, as you did in *Why agent safety is different*: a
one-line edit to a file you can
`git checkout` is about as small as a blast radius gets. Approve it.

Some agents offer to stop asking for the rest of the session. Decline that
for now. The prompt is the pause in which you read what's about to happen,
and this lesson is about building the habit of reading.

#### Checkpoint: The agent asks for a different file

You send the repaired brief. The agent reads `todo.py` and `test_todo.py`,
and then asks: "I would like to edit `todos.json` to mark item 2 as not
done, so the test passes. Allow?" The brief said not to touch `todos.json`,
and so does `AGENTS.md`. What do you do?

- Allow it. The agent has read more of the code than you have, and it probably knows what it is doing.
- Stop the session, reset the fixture and start over with the same brief.
- Allow it, and revert the file with git afterwards.
- Deny the edit, and ask why it needs to change a file the brief said to leave alone.

## Permission modes

The prompt you just answered is one setting of a wider control. Most
coding agents let you choose, at the start of a session or during it, how
much the agent may do without asking. The names differ from one agent to
the next, and the set of choices is much the same
(Claude Code in action, Claude Academy).

- **Read-only.** The agent reads files, searches, and runs commands to
  explore, and it edits nothing. It proposes changes and you decide what
  happens next. Whether a command that does more than read stops for a
  prompt in this mode depends on the agent. This is the plan mode from
  the start of this lesson.
- **Ask before each action.** The mode you have used so far. Each edit
  stops for your answer, and so does each command, apart from a built-in
  set that only reads. You saw that set at work during the explain step,
  when the agent read files and searched without asking.
- **Accept edits, ask before commands.** Edits to files inside the
  working directory go through without a prompt, and so do common
  file-management commands on paths in that directory. In Claude Code that
  set includes deleting a file and editing one in place with a stream
  editor, so "accept edits" covers more than typing into a file. Other
  commands still stop and ask (Choose a permission mode, Claude Code documentation).
- **Skip every check.** The agent edits and runs whatever it decides to,
  and you find out afterwards.

Some agents add modes between the last two, such as a list of commands you
approved in advance that runs without a prompt, or an automatic check that
reviews each action against your request in place of you. Before you use
one, read what your agent's documentation says it still asks about. Check
which mode your agent starts in, too. In Claude Code that depends on the
version, on whether you run it in a terminal, an editor or the browser, and
on your settings. On older versions it depended on your plan as well
(Choose a permission mode, Claude Code documentation).

Each mode sets how much the agent can do before you get a chance to say
no. In read-only mode that is what the agent reads, so the worst case is
that it repeats something it should not have seen. With the prompt on, it
is one action: the edit or command you approved. Accepting edits widens it
to every file in the working directory. That is acceptable in a repository
under version control because `git diff` shows you what changed and
`git checkout -- .` puts tracked files back, and the next section makes
that review the habit. Know the limit of that reset: a file the agent
deleted that was never committed does not come back. Skipping every check
widens it to everything the shell can reach, which on your own machine is
every file you own, as *Why agent safety is different* put it. Use that
mode only inside a sandbox, meaning a container or virtual machine made
for the session, without internet access and with nothing in it you would
miss, and put no credentials in it. The vendor documentation says the same
about isolation and internet access (Choose a permission mode, Claude Code documentation).

Pick the mode per task, the way you sized the brief. A question or a plan
asks for read-only. A first change in a project, or any change in a
project you do not know, keeps the prompt on, because the prompt is where
you learn how this agent behaves in this code. A change of a kind you have
reviewed before, in a working tree that git can restore, can accept edits,
and then you read the whole diff at the end where you would have read each
prompt. The mode decides how much you see before it happens. It says
nothing about how well the agent behaves.

#### Checkpoint: Which mode for the change?

A colleague asks you to fix a small bug in a project you have never opened.
The repository is on your laptop, next to your other work, and `git status`
shows a clean tree. You started in read-only mode and asked the agent to
explain the bug, and it has proposed a one-line fix. Which mode do you
switch to for making the change?

- Stay in read-only. The agent has already worked out the fix, so it can apply it from there.
- Ask before each action. Read the edit at the prompt, as you did in the fixture.
- Accept edits, ask before commands. The tree is committed, so git can undo anything.
- Skip every check. It is one small bug, and the tree is committed.

## Review the diff

The agent says it is done. It may even say the tests pass. Don't take its
word for either. Look at what changed.

```sh
git diff
```

```diff
-    index = number
+    index = number - 1
```

Then run the check you named in the brief. The done-criterion was yours,
so you run it.

```sh
python3 -m unittest -q 2>&1 | tail -1
```

```text
OK
```

This course doesn't assert that output, because your agent produced the
edit. It is what you should see if the fix is the one above. If you see
anything else, read the diff again before you ask the agent for more.

#### Pitfall: Accepting a diff you did not read

An engineer asks an agent to fix a failing test, sees "all tests pass" in
the reply, and accepts. The agent had changed the test's expected value to
match the buggy output. The tests did pass. The rule: the agent's summary
is a claim, the diff is the evidence, and a passing test is only proof if
you also confirm the test didn't move. Read every line the agent changed,
every time.

#### Checkpoint: The diff moved the test

The agent says it is done and that all four tests pass. You run `git diff`.
The diff changes one line in `todo.py`, and it also changes the expected
string in `test_todo.py`. What do you do?

- Ask the agent to undo its change to the test and make the original test pass by editing todo.py alone.
- Accept it. The done-criterion was a passing test run, and the tests pass.
- Put the test back by hand and keep the rest of the diff.
- Run the tests again yourself to confirm they pass.

## Sessions and context

Everything you have said and everything the agent has read in this session
is in its context window, the agent's working memory. The context window
is how the agent could answer "explain this repository" once and then fix
the bug without re-reading the tests. The tests were already in context.

It is also why sessions should end. The context has a fixed size, and as it
fills up, early material drops out or is summarized. A long session that
drifts across unrelated tasks gets worse at each of them. The habit to
build: one task, one session. When you've reviewed the diff and the tests pass,
end the session and start a fresh one for the next task.

Most agents summarize the session for you when the context is nearly full,
and many let you ask for a summary earlier. The summary keeps what the
agent judges important, which isn't always what you need next. When you ask
for one, say what it should keep: the task, the files touched, and the
check that still has to run (Claude Code in action, Claude Academy). In Claude
Code, the `/compact` command followed by what to focus on asks for that
summary (How Claude Code works, Claude Code documentation).

#### Checkpoint: Same session, or a new one?

The fix is in, you have read the diff, and the tests pass. Now you want the
agent to replace the done-mark strings in `todo.py` with a constant. Where
do you ask for it?

- Type the refactor request into the same session. The agent already knows the code.
- Ask the agent to summarize the session first, then continue in it with the refactor.
- Keep the session open and start a second agent in the same directory for the refactor.
- End the session, and start a new one with a new brief for the refactor.

If you do want a second change in the same repository, reset first so the
next session starts from a known state.

```sh
git checkout -- .
python3 -m unittest -q 2>&1 | tail -1
```

That puts the bug back, and the last line is `FAILED (failures=1)` again.
Same command as the example above, same output.

## Exercise

In a fresh session, with the fixture reset, fix the bug with your coding
agent using the brief you repaired above. Approve the edit at the
permissions prompt, then read `git diff` and run
`python3 -m unittest -q` yourself.

A good result: the diff touches one line in `todo.py`, `test_todo.py` and
`todos.json` are unchanged, and the last line of the test run is `OK`.
Finish by running `git checkout -- .` so the fixture is ready for next time.

Stretch: Now ask the agent for a refactor you choose, for example replacing the done-mark strings with a constant, and review it the same way: read the diff, run the tests, reset if you do not like it.

## Recap

1. Start the agent in the directory that's the task's world, and ask it to explain before you ask it to change.
2. Find the evidence yourself first: the file, the failing test, the wrong output. Then put those in the brief, with limits and a done-criterion.
3. The permissions prompt is where your judgment enters; weigh the blast radius, and keep the prompt on.
4. Pick the permission mode per task: read-only for a question, the prompt for a first change in a project, accept edits only where git can restore what you did not read, and skip every check only inside a sandbox.
5. The agent's summary is a claim. The diff is the evidence, and you run the check you named.
6. Give each task its own session. Reset the fixture and start fresh.

## References

- Claude Code 101, Claude Academy, https://academy.claude.com/courses/claude-code-101
- Choose a permission mode, Claude Code documentation, https://code.claude.com/docs/en/permission-modes
- How Claude remembers your project, Claude Code documentation, https://code.claude.com/docs/en/memory
- Claude Code in action, Claude Academy, https://academy.claude.com/courses/claude-code-in-action
- How Claude Code works, Claude Code documentation, https://code.claude.com/docs/en/how-claude-code-works
