This has happened to me more than once: one evening the team goes through several variants of a block, and the next morning someone asks why the heading got shorter. The chat has dozens of messages, all anyone remembers is something like "the background was light, I think", and the exact decision dissolves somewhere between the edits.
That's where requests.log comes in handy. It's a short external trace of the work that you can open in a plain editor and read top to bottom. I value it not for the number of lines, but for the way it lets you connect a request to what actually got kept.

Why you need an external trace
Over the years I've gotten used to keeping four things separate: the request, the decision, the chat, and the saved version of the file. They answer different questions, though during a single session they easily blur into one fuzzy story.
| Source | What you can learn from it | What it doesn't prove |
|---|---|---|
requests.log | What was asked for and when the entry appeared | That the change was made and accepted |
| Decision note | Which option was chosen and who marked it | What the saved file looks like now |
| Chat history | Discussion context and replies | What made it into the current version of the project |
| Version control | Which files and lines were saved | Why that particular option was chosen |
The log works like a pointer. It takes you back to the right moment and the exact wording, but it won't restore the code itself - for that, check the files and the project's version history.
There's an important boundary here, and I've seen plenty of wrong conclusions drawn right at it. The UserPromptSubmit hook event receives the text in the prompt field before Claude Code processes the request. A line like that confirms the text was sent, but the outcome of the task is still unknown at that point.
The diagram shows a distinction that matters when you're reading the log. The top chain shows how the work happened; the bottom one shows what stays in the log. The automatic line appears before the outcome; the decision note appears only after the review. Neither one replaces the other.
What a useful entry is made of
Let me show this with three versions of an entry. I've run into each of them in real projects.
doneA line like that is almost useless: it's unclear who did what, when, and which task this "done" even refers to.
User: Make the card heading shorter...
Claude: Okay, let me look at the component first...
User: And reduce the padding too...
Claude: Fixed...And here there's already too much for a quick search. The full stream belongs in the chat history, but a separate log quickly turns into an awkward copy of it. I've tried it this way, then spent my own time scrolling.
2026-09-08T14:32:11+03:00 | prompt_submitted | actor=user | ref=card-header | text="Shorten the card heading to a single line"
2026-09-08T14:41:06+03:00 | decision_recorded | actor=user | ref=card-header | text="Accepted the short heading, size 24 px"The third version is my favorite: it keeps a short fact, a timestamp with a time zone, an author, and a ref tag that links the lines together. The field names here were made up for this practice project. This is our file's own format, not a mandatory schema from Claude Code. In this example the ref tags were added by hand to the related lines. A hook doesn't guess which block a request belongs to: the base UserPromptSubmit event carries no such meaningful tag. If your log has no linking field, you can still search it by the exact phrase.
What was known at the moment of the entry
The prompt_submitted line says exactly one thing: this request arrived in the session, and the hook recorded it. Usually that's your text, but not always. UserPromptSubmit also fires on the next round of /loop, on a background subagent's report, and on a message from another session, so actor=user in our format is an assumption of this training example. It doesn't follow that Claude Code changed the page, that the review passed, or that anyone liked the result.
The outcome is worth recording only once it's actually known. In our example that's a separate note added after the page was reviewed. If your format has result or error fields, fill them with observed facts, not a guess about how the task will turn out.
Where to mark the decision that was made
A decision appears only once someone has explicitly recorded it. In the example above, the user added the line after reviewing the page. The author is named in actor, and ref=card-header keeps the link to the request.
The note can be manual, and that's fine. The automatic entry saves the input, and a person confirms what the result means. A "done" line with no author and no link isn't something to trust.
Reading the request log
Let's go through a small five-line log. This is exactly the kind of short fragment I usually start with when I need to reconstruct how a decision was made.
2026-09-08T14:32:11+03:00 | prompt_submitted | actor=user | ref=card-header | text="Shorten the card heading to a single line"
2026-09-08T14:36:20+03:00 | prompt_submitted | actor=user | ref=card-header | text="Show two options: 24 px and 28 px"
2026-09-08T14:41:06+03:00 | decision_recorded | actor=user | ref=card-header | text="Accepted the short heading, size 24 px"
2026-09-08T15:03:44+03:00 | prompt_submitted | actor=user | ref=card-shadow | text="Make the card shadow softer"
2026-09-08T15:08:17+03:00 | prompt_submitted | actor=user | ref=card-header | text="Try a darker color for the heading"Imagine you need to find the last accepted decision about the heading format. Start by searching for card-header in your editor. You get four lines. The last one holds a new request about the color, not a decision. So the conclusion rests on the line at 14:41:06: the short heading, size 24 px, was accepted.
My working order for reading a log is simple:
- First I put the question into words: am I looking for a request, a decision, or an observed error.
- Then I find the shared tag, the component name, or the exact phrase.
- I keep only entries of the right type and read them in time order.
- I name the lines that back up the conclusion. I honestly mark everything else as unknown.
You can do this kind of reading without even running Claude Code. It's a useful habit: check the conclusion against the file yourself first, and only then hand the same work off to the agent.
When there's no decision in the log
Now let's remove the line at 14:41:06. What's left is a request to show two sizes and a later request to change the color. Which size did they go with? The log doesn't say.
In cases like this it's very tempting to pick 24 px, because it was the first option offered. Don't. The request shows an intent to check options, not the outcome of that choice. There's only one honest conclusion here: the log has no recorded decision about the size.
The fork doesn't depend on the order of the requests, only on the type of entry you find. There's an explicit decision note - the conclusion can be confirmed. Only requests are left - you record it as unknown and don't fill the gap with a guess. That restraint saves a lot of time in the end.
When Claude Code goes through a log, I ask it to name the exact entries the conclusion rests on. If there's no confirming line, that goes into the list of unknowns, with no guess at a successful outcome.
What to leave out of the log
I'm not trying to turn a useful log into a dumping ground for everything. What goes into an entry should be chosen for the question you'll have to answer later. For finding decisions, a timestamp, an event type, an author, a linking tag, and a short piece of text are usually enough.
- Don't copy keys, tokens, the contents of
.env, or personal data. - Don't carry over an external service's full response when you only need a status or an operation id.
- Keep the full prompt only where it's genuinely needed. In this article it's full only because the example is short and made up.
- Check the file size and the value of old entries from time to time. There's no single retention period that fits every project.
Truncating a line to a hundred characters improves readability, but it doesn't strip secrets automatically. A short token fits comfortably into the first hundred characters (seen it happen more than once). Decide first what data is allowed to be recorded at all. That's the rule to start with, not the line length.
Going through the log together with Claude Code
Claude Code is good at putting together a timeline and quickly finding related lines. But it has to actually read the file first: having requests.log in the project doesn't by itself mean its contents are already in context.
Give the agent a specific task:
Read requests.log. Find the last explicitly accepted decision
about the card heading size (card-header). Cite the entries you're relying on.
If the log only has a request, say so.
List separately what remains unknown.A good answer separates facts from the conclusion: it quotes the time, the event type and the text of the decision, then lists the open questions with no confirmation attached. Check those references against the source lines. The mistake is especially noticeable when the agent calls the last request an "accepted option".
For this sample log, the answer would go like this: the decision about the size is confirmed by the entry at 2026-09-08T14:41:06+03:00; a request about a darker color appeared later, but there's no known decision about the color. That's the kind of answer I consider reliable: it doesn't promise more than what's in the file.
- A hook saves the request before it's processed, so that entry doesn't confirm the outcome.
- A separate note links the accepted decision to the request by time or by tag.
- The search starts with a specific question and ends with a reference to the source lines.
- If there's no confirmation, the log leaves a gap. There's no need to fill it in by guessing.
That's how requests.log stays a small working trace: it helps you find the right point in a discussion, but it doesn't pretend to be a backup of the project or proof that a task is done. When a log is kept honestly, you actually want to come back to it.
GO TO FULL VERSION