# End-to-end tracing

> Follow one working request across every boundary before asking the agent to change it, then verify the same path after the edit

A static page has one boundary: the file you edited is the file that ships. Get
it wrong and the page is wrong in front of you. A form post crosses five, and
each one is a place where an edit can be correct in its own file and wrong for
the app.

**Trace** is borrowed from operations, where a request's path through a system
is a trace and each hop in it is a span --- OpenTelemetry's vocabulary, and week
10's subject. Nothing here is instrumented: this is the same idea done by hand,
by reading, before a change rather than after an incident.

## Trace before changing

Ask the agent to follow one path from browser input through the route, handler,
database and response, naming the file at every boundary. In the guestbook from
the [lecture demo](https://lecture-demos.fly.dev/), that answer is five lines
long:

```text
src/pages/index.astro      <form method="post" action="/api/messages">
src/pages/api/messages.ts  export const POST  →  form.get("body")
src/lib/db.ts              addMessage(body)   →  INSERT into messages
src/lib/schema.ts          body: text().notNull()
                           redirect("/", 303) → listMessages() → <li>
```

At each boundary there is one question --- **what crosses it, and in what
shape?** Between the form and the route it is a string called `body`, because
that is the input's `name` attribute and nothing else in the markup reaches the
server. Between the handler and the database it is a row with a `body` column.
Between the response and the browser it is a `303` and a second request.

Asking for this costs one cheap turn and is checkable on the spot: if a file it
names doesn't exist, you have learned something important before it wrote a
patch.

## Change one path

The reason to spend that turn is that full-stack mistakes are quiet. Consider a
plausible, entirely reasonable edit --- a nicer label on the guestbook form:

```diff
- <input id="body" name="body" required maxlength="500" />
+ <input id="body" name="message" required maxlength="500" />
```

The markup is fine. The handler, untouched, is also fine. But it still reads
`form.get("body")`, now gets an empty string, and its `if (body)` guard skips
the insert --- so the POST returns its `303`, the browser follows it, the page
renders, and the message is gone. The type checker and runtime report no error;
both files are locally correct, but the insert is skipped and the message is
lost.

This is the characteristic full-stack failure, and it is why the trace comes
first. A change is scoped by the boundaries it crosses: renaming that field is a
two-file change, and knowing that is the difference between a rename and a bug.

## Verify every boundary

Verify where the change landed, not just at the end of it. For the rename above
that means the form's `name`, the handler's `form.get`, the row in SQLite and
the rendered `<li>` --- four checks, each of which fails differently.

The lecture demo keeps this pattern as tests. `spec/guestbook.test.ts` is the
guestbook's trace written down, one assertion per boundary:

```text
✓ accepts a message and redirects back to the page
✓ persists the message: a fresh page load includes it
✓ broadcasts new messages over the SSE stream
```

It drives the running app over HTTP, so it is checking the thing that ships
rather than a function in isolation. A trace you write down once becomes
[backpressure](https://comp.anu.edu.au/courses/comp4020-agentic-coding-studio/topics/backpressure/): a later rename fails in the test suite.

## When tracing is the wrong move

Reading a whole system before touching any of it is how a week disappears, and
an agent will produce that reading for you at length. The trace is bounded by
the change you intend --- adding a field to the guestbook form never needs
anyone to open `src/lib/events.ts`, because nothing in that file is on the path.

So the honest signal is this: if you cannot say which boundaries a change
crosses, that is not a reason to read more code. It is a
[plan that isn't ready](https://comp.anu.edu.au/courses/comp4020-agentic-coding-studio/topics/spec-driven-development/).
