Do it by hand once, then automate

I want to teach from the PoC I'm building, so I keep a daily log here: what I built, why, and how I used Claude. On day one I wrote the log with Claude by hand. Then I turned those prompts into a command, and its first real run wrote the notes for today.

What runs now

  • go run . log PROJECT in this site's generator. It reads the newest Claude Code session of that project and writes two files in notes/.
  • A .prompt.md transcript next to the note: prompts verbatim, tool output cut to 6 lines, names redacted. The site shows it in a terminal pane beside the note.
  • A draft note that claude -p writes from the transcript, in the voice of the newest note on the site.
  • --dry-run prints both files and writes nothing. The command never overwrites a file.

The numbers: 610 lines of Go, about 2 minutes per run, and the first draft took 1 min 43 s.

Five choices and why

A decision diary. Each note lists choices as "choice, why, what I didn't pick". It teaches the reasoning, which you can't see in the code. Not picked: a replay of every prompt, or a list of lessons without the why.

On my own site, without client names. The notes are mine and public, so they live here, not in the client's repo. Client, product and host names never appear. Not picked: a log folder inside the PoC repo.

The session file, not a copy of the terminal. Claude Code saves every session as JSONL. The command reads that file, so it gets every prompt word for word. Not picked: pasting the scrollback by hand.

Claude drafts, I edit. The command calls claude -p without tools and without saving a session. The newest note goes along as the example of the voice. Not picked: a template that fills in blanks, which can't explain a choice.

The names to hide live outside the repo. They sit in ~/.config/alen.andronewille.eu/redact.txt, one per line, because the list itself names the client. Your home folder, email addresses and common secret formats are always replaced. Not picked: a list in the site's repo.

How I used Claude

  • Directions before a format. I asked: "give me 3 directions and your suggestions for them. keep it compact, i have adhd." I picked the decision diary and moved the notes from the PoC repo to this site.
  • A hand pass first. Claude wrote the first note and its .prompt.md by hand, redacting names as it went. That pass set the format the command now follows.
  • Then the tool. I asked: "now write this into a tool so i can run that instead of going through these prompts with you for the next sessions." Claude read how Claude Code stores sessions and built the command.
  • A dry run on a real session. The first dry run leaked the product name into the file name. Claude now redacts the title before it makes the slug.
  • The first real run drifted. I had moved the hand-written note away first, so Claude had no voice example. The layout of the choices changed until I edited the note.

What I'd tell someone starting the same

  1. Do a chore by hand with Claude once, then ask for the tool. The manual pass is the spec.
  2. Dry-run on a real session before writing. That is where the leaked name showed up.
  3. Keep the list of names to hide out of the repo you publish from.

Next

  • Run go run . log after each session and edit the draft before publishing.
  • Make the command handle two sessions on one day.

all notes

session log, Claude Code, 6 prompts

session · claude code · 6 prompts

Generated from a Claude Code session. Prompts are verbatim; tool output is cut to 6 lines.
Names in [brackets] are redacted.
Prompt:
now for the sake of teaching; let's also keep a working log, including claude usage and how and why choices are made, something that reads easily. give me 3 directions and your suggestions for them. keep it compact, i have adhd.

final output should be an md file with a filename like 2026-11-09-some-title.md. I'm planning on publishing one each day i work on it.
Claude:
Pick one of these 3 directions for the daily log. I recommend 1.

1. Decision diary (recommended)
- Sections: what we built (3 lines), 3–5 decisions written as *choice → why → what we didn't pick*, then how Claude helped, what you corrected, and what's next.- Teaches: the reasoning. That's the part people can't see in the code.- My suggestion: limit it to 5 decisions per day. Today's would be:- Claim Service as the only writer of claim state;- VC 2.0 instead of the vc claim;- real mTLS instead of fake auth;- Dutch console text with English glossary terms;- SSH instead of HTTPS for GitLab.
2. Session replay
- Sections: your prompts in order. Each gets *what you asked → what Claude did → what came out → what surprised*.- Teaches: how to work with Claude step by step.- My suggestion: quote your own prompts verbatim, since that's the most instructive part. Today has 7 prompts, so it stays short.
3. Lessons learned
- Sections: 3 takeaways per day, each with a concrete moment from the session, plus a small stats block (time, files, tests).- Teaches: habits other people can reuse.- My suggestion: use today's real moments:- running the tests 8 and 15 times in a row exposed two race bugs;- the route conflict only crashed the real binary, because the tests never built the console's routes;- GitLab blocks private projects for this group, so you had to create the project yourself.
Practical: put the files in poc/log/, so they ship with the code. Today's file would be log/2026-11-09-poc-in-een-dag.md.

Answer with 1, 2 or 3, plus Dutch or English, and I'll write today's log.
Prompt:
1, english

don't put them in poc/log/. add them to ~/code/alen/alen.andronewille.eu/notes

On the note itself, read: """



notes/2026-11-09-what-a-restack-taught-me.md

# What a restack taught me

The body is plain markdown: paragraphs, lists, code, fenced code blocks, links, tables.

## A section heading ...

- The filename sets the date and the URL. 2026-11-09 is the date shown on the site and in the feed. what-a-restack-taught-me becomes /notes/what-a-restack-taught-me/. Use lowercase letters, digits and hyphens.- The first line is the title. It has to be # Title, and everything after it is the body.- Saving a note publishes it locally. With go run . serve running, save the file and reload the page. The note shows up on the home page (the newest three), on /notes/ (all of them, newest first) and in /notes/feed.xml.

"""
Tool call:
Bash(cd ~/code/alen/alen.andronewille.eu && ls notes/ 2>&1 | tail -10 && git status --short 2>&1 | head -5 && f=$(ls notes/*. …)
Tool output:
Exit code 1
fatal: not a git repository (or any of the parent directories): .git
(eval):1: no matches found: notes/*.md
Tool call:
Bash(ls -la ~/code/alen/alen.andronewille.eu/ ~/code/alen/alen.andronewille.eu/notes 2>&1 | head -30)
Tool output:
~/code/alen/alen.andronewille.eu/:
drwxr-xr-x@    - alen  9 Nov 15:57 fonts
drwxr-xr-x@    - alen  9 Nov 15:57 notes
drwxr-xr-x@    - alen  9 Nov 16:00 site
drwxr-xr-x@    - alen  9 Nov 15:58 templates
.rw-r--r--@    6 alen  9 Nov 15:59 .gitignore
… +10 lines
Tool call:
Bash(cd ~/code/alen/alen.andronewille.eu && cat CLAUDE.md README.md)
Tool output:
# alen.andronewille.eu

The dev site. A small Go generator (`main.go`, in the style of `mig` in `../guidelines`) turns
`projects.json` and `notes/*.md` into static pages with `templates/` and `style.css`.

## Commands
… +48 lines
Tool call:
Skill(mig)
Tool output:
Launching skill: mig
Tool call:
Bash(cd ~/code/alen/guidelines && cat README.md checks.md words.md | head -400)
Tool output:
# MIG: Moustache Interface Guideline

How I make things: my own products, my tools, and the work I do for clients. Eight principles,
one standard, every surface.

Version 2026.11
… +245 lines
Claude:
MIG rules for this note: no emoji or exclamation marks, headings that state the point, sentences under 25 words, exact numbers. Gathering exact figures before writing:
Tool call:
Bash(git ls-files | wc -l && git ls-files '*.go' | xargs cat | wc -l && cat internal/demo/web/* | wc -l && go test ./internal …)
Tool output:
34
    5028
     994
97 checks
Tool call:
Write(~/code/alen/alen.andronewille.eu/notes/2026-11-09-a-poc-in-one-session.md)
Tool output:
File created successfully at: ~/code/alen/alen.andronewille.eu/notes/2026-11-09-a-poc-in-one-session.md (file state is current in your context — no need to Read it back)
Claude:
Checking that the site builds with the note:
Tool call:
Bash(cd ~/code/alen/alen.andronewille.eu && go run . build 2>&1 | tail -5 && ls site/notes/ && grep -c "A PoC in one session" …)
Tool output:
Built 4 pages into site in 5 ms.
a-poc-in-one-session
feed.xml
index.html
site/index.html:1
site/notes/feed.xml:1
… +1 lines
Claude:
To read it: run go run . serve in ~/code/alen/alen.andronewille.eu and open /notes/a-poc-in-one-session/.

Today's log is ~/code/alen/alen.andronewille.eu/notes/2026-11-09-a-poc-in-one-session.md, written as a decision diary in English. The site builds and the note appears on the home page and in feed.xml.

What's in it:
1. What runs now: 4 bullets with exact numbers (9 scenarios, 97 checks, 34 files, 5 028 lines of Go).2. Five choices and why: each one is *choice → why → what I didn't pick*.3. How I used Claude: the one build prompt, the two race bugs found by repeated test runs, the bug only the real binary showed, and where you steered.4. What I'd tell someone starting the same: 3 points.5. Next: 2 items.
It follows MIG's writing rules: no emoji, no exclamation marks, headings that state the point.

I left out the client name and the internal GitLab URL, because this note goes public. The domain stays generic: "a notary service". Check that against the client agreement before you publish. If naming the client is fine, adding the name is a one-line change.
Prompt:
write the poc session log into an .prompt.md file.

alen.andronewille.eu now has a viewer for files ending in that extention, their contents will be shown on the notes page it references (via filename: {orig_filename}.prompt.md).
Tool call:
Bash(cd ~/code/alen/alen.andronewille.eu && grep -n -i "prompt" main.go | head -40 && cat CLAUDE.md README.md | grep -n -i -A …)
Tool output:
242:// session is the Claude Code session behind a note, read from its .prompt.md file.
250:		if t.Kind == "prompt" {
255:		return "1 prompt"
257:	return fmt.Sprintf("%d prompts", n)
260:// turn is one block of the session: a prompt, Claude's text, a tool call, its output, or
294:	return map[string]string{"prompt": ">", "say": "●", "tool": "●", "out": "└"}[t.Kind]
… +31 lines
Tool call:
Bash(cd ~/code/alen/alen.andronewille.eu && sed -n 240,345p main.go)
Tool output:
func (n note) Href() string { return "/notes/" + n.Slug + "/" }

// session is the Claude Code session behind a note, read from its .prompt.md file.
type session struct {
	Turns []turn
}
… +101 lines
Tool call:
Write(~/code/alen/alen.andronewille.eu/notes/2026-11-09-a-poc-in-one-session.prompt.md)
Tool output:
File created successfully at: ~/code/alen/alen.andronewille.eu/notes/2026-11-09-a-poc-in-one-session.prompt.md (file state is current in your context — no need to Read it back)
Claude:
Two fixes: the header says prompts are verbatim, so the slash commands need their full names. And ⏳ is a pictographic emoji, which MIG fails in published copy.
Tool call:
Bash(cd ~/code/alen/alen.andronewille.eu && python3 - <<'EOF' …)
Tool output:
notes/2026-11-09-a-poc-in-one-session.prompt.md:8:⏺ Bash(find [project]-docs -name "*.md" -o -name "*.feature" -o -name "*.mmd" | xargs wc -c | tail -1)
notes/2026-11-09-a-poc-in-one-session.prompt.md:9:  ⎿  111702 total
notes/2026-11-09-a-poc-in-one-session.prompt.md:11:⏺ I've read the domain, Notary and portal docs. Skimming the backlog for conflicts I need to pick a side on, then I'll build.
notes/2026-11-09-a-poc-in-one-session.prompt.md:13:⏺ Build plan, based on the docs:
notes/2026-11-09-a-poc-in-one-session.prompt.md:20:⏺ Write(internal/trace/trace.go)
notes/2026-11-09-a-poc-in-one-session.prompt.md:21:  ⎿  File created successfully
… +61 lines
Claude:
To see it: run go run . serve in ~/code/alen/alen.andronewille.eu and open /notes/a-poc-in-one-session/. The session shows in the pane beside the note.

The transcript is notes/2026-11-09-a-poc-in-one-session.prompt.md. The site builds, and the pane reports "10 prompts".

What's in it:
- Prompts: all 10, word for word, including the clarifying question you stopped.- Claude's side: shortened to the turning points: the test failures and their fixes, the route-conflict crash, the Chrome checks, the GitLab push error and the push that worked. The first line of the file says it's condensed.- Redacted: the client, the product name, the internal GitLab host, your GitLab username and local paths are shown as [brackets].
The file also passes MIG's no-emoji check. I removed the ⏳ from a quoted line. The ⏺ and ✗ markers are terminal symbols the viewer needs, so they stay.

Read the 4 Claude summary lines (11, 13, 72 and 165) before you publish. Those are the parts I shortened most.
Prompt:
now write this into a tool so i can run that instead of going through these prompts with you for the next sessions
Claude:
MIG applies to this tool, so I'm reading its CLI rules (dx.md, surfaces/terminal.md). Then I'll look at how Claude Code stores this session on disk.
Tool call:
Bash(cd ~/code/alen/guidelines && sed -n '1,400p' dx.md | head -220)
Tool output:
# DX: for developers and their agents

Signature layer. Covers CLIs, TUIs, services and APIs, frameworks, agent tooling, and code handed
over to clients. The developer is the person; their coding agent is the second reader.

## Install
… +148 lines
Tool call:
Bash(cat ~/code/alen/guidelines/surfaces/terminal.md | head -150; ls -la ~/.claude/projects/~-code-[work]-p …)
Tool output:
# Terminal: CLIs and TUIs

Only what differs from the topics. A CLI does one job and exits; a TUI is for work that needs a
screen.

## Commands behind every screen
… +99 lines
Tool call:
Bash(cd ~/.claude/projects/~-code-[work]-poc && f=59e16794-b4e4-49b5-a356-83fba0905ce6.jsonl; wc -l $f; pyt …)
Tool output:
1301 59e16794-b4e4-49b5-a356-83fba0905ce6.jsonl
Counter({'assistant': 255, 'attachment': 230, 'user': 142, 'api-request': 120, 'api-request-blob': 81, 'last-prompt': 79, 'mode': 78, 'permission-mode': 78, 'atis-latch': 78, 'ai-title': 77, 'file-history-delta': 34, 'system': 25, 'file-history-snapshot': 13, 'queue-operation': 8, 'api-request-shape': 3})
Counter({('attachment', None): 230, ('assistant', ('tool_use',)): 124, ('user', ('tool_result',)): 124, ('api-request', None): 120, ('assistant', ('thinking',)): 88, ('last-prompt', None): 79, ('mode', None): 78, ('permission-mode', None): 78, ('atis-latch', None): 78, ('ai-title', None): 77, ('assistant', ('text',)): 43, ('file-history-delta', None): 34, ('api-request-blob', ('text',)): 28, ('system', None): 25, ('api-request-blob', ('thinking', 'tool_use')): 15, ('api-request-blob', ('text', 'thinking', 'tool_use')): 15, ('file-history-snapshot', None): 13, ('user', 'str'): 13, ('api-request-blob', ('tool_result',)): 9, ('queue-operation', None): 8, ('api-request-blob', ('tool_use',)): 7, ('api-request-blob', ('text', 'tool_result')): 6, ('user', ('text',)): 5, ('api-request-shape', None): 3, ('api-request-blob', ('text', 'tool_addition')): 1})
{"parentUuid": "aa6f5f51-c04a-4efb-a76a-5cc2e1f4fd52", "isSidechain": false, "promptId": "cb534712-ff64-433c-a9d1-468bf35fd235", "type": "user", "uuid": "4fd42ab5-7dae-4bcb-9746-ed6487203fd3", "timestamp": "2026-11-09T13:01:13.136Z", "permissionMode": "auto", "origin": {"kind": "human"}, "promptSource": "typed", "turnOrigin": "human", "turnPosition": {"promptIndex": 1, "turnIndex": 1}, "userType": "external", "entrypoint": "cli", "cwd": "~/code/[work]/poc", "sessionId": "59e16794-b4e4-49b5-a356-83fba0905ce6", "version": "2.1.309", "gitBranch": "HEAD"}
'read `../[project]-docs`\n\nimplement a poc where i can trigger claims and show the end-to-end process for a number of use cases. (golang is the preference)'
Shell cwd was reset to ~/code/[work]/poc
Tool call:
Bash(cd ~/.claude/projects/~-code-[work]-poc && python3 - <<'EOF' …)
Tool output:
{'kind': 'human'} typed None False 'read `../[project]-docs`\n\nimplement a poc where i can trigger claims and show the end-to-end process for a number of use cases. (golang is the preference)'
{'kind': 'human'} None None False '<command-message>anthropic-skills:neerlandsch</command-message>\n<command-name>/anthropic-skills:neerlandsch</command-name>'
None None True False 'Base directory for this skill: ~/.claude/skills/synced/9b6270b7-5b63-479c-8ffb-e662b90b8454_99e85db8-39c4-42a4-90d4-58e239edee41/neerlandsch'
None None None False '[Request interrupted by user for tool use]'
{'kind': 'human'} typed None False 'clear'
{'kind': 'human'} None None False '<command-message>i-have-adhd:i-have-adhd</command-message>\n<command-name>/i-have-adhd:i-have-adhd</command-name>\n<command-args>please check docs at all depths a'
… +13 lines
Tool call:
Bash(cd ~/code/alen/alen.andronewille.eu && grep -n '^func \|os.Args\|case "' main.go && sed -n '1,60p' main.go && cat go.mod …)
Tool output:
66:func main() { os.Exit(run(os.Args[1:])) }
68:func run(args []string) int {
74:	case "-h", "--help", "help":
77:	case "--version", "version":
80:	case "build":
82:	case "serve":
… +95 lines
Tool call:
Bash(cd ~/code/alen/alen.andronewille.eu && sed -n 60,125p main.go && sed -n 210,240p main.go && ls *.go && which claude && c …)
Tool output:
// latest is how many notes the home page lists.
const latest = 3

var noteName = regexp.MustCompile(`^(\d{4}-\d{2}-\d{2})-([a-z0-9]+(?:-[a-z0-9]+)*)\.md$`)

func main() { os.Exit(run(os.Args[1:])) }
… +117 lines
Tool call:
Bash(ls ~/.claude/projects | grep -i andro; cd ~/.claude/projects/~-code-[work]-poc && python3 - <<'EOF' …)
Tool output:
~-code-Andronewille
~-code-Andronewille-averij
~-code-Andronewille-belastingdients
~-code-Andronewille-boekhouding
~-code-Andronewille-klep
~-code-Andronewille-loods
… +31 lines
Claude:
Writing the tool as a log command in the site's generator, so it lives next to the viewer that reads its output.
Tool call:
Write(~/code/alen/alen.andronewille.eu/log.go)
Tool output:
File created successfully at: ~/code/alen/alen.andronewille.eu/log.go (file state is current in your context — no need to Read it back)
Claude:
Simplifying two spots I wrote too loosely (command parsing and the config path), then wiring the command into main.go:
Tool call:
Bash(cd ~/code/alen/alen.andronewille.eu && python3 - <<'EOF' …)
Tool output:
ok
Shell cwd was reset to ~/code/[work]/poc
Claude:
Creating the redaction list outside the repo, with the names I redacted by hand today, then a dry run on this session:
Tool call:
Bash(mkdir -p ~/.config/alen.andronewille.eu && cat > ~/.config/alen.andronewille.eu/redact.txt <<'EOF' …)
Tool output:
Session: ~/.claude/projects/~-code-[work]-poc/59e16794-b4e4-49b5-a356-83fba0905ce6.jsonl
exit=0
    1030   58971 [tmp]
12
1:==> notes/2026-11-09-[project]-claims-poc.prompt.md
==> notes/2026-11-09-[project]-claims-poc.prompt.md
… +40 lines
Tool call:
Bash(cd ~/code/alen/alen.andronewille.eu && python3 - <<'EOF' …)
Tool output:
==> notes/2026-11-09-project-claims-poc.prompt.md
> read `../[project]-docs`
> /anthropic-skills:neerlandsch
> /i-have-adhd:i-have-adhd please check docs at all depths andapply rules in i have adhd
> /anthropic-skills:neerlandsch internal/demo/web/index.html
> do that please
… +8 lines
Claude:
All 12 prompts come out word for word. Now the full path, with Claude drafting the note (dry run, nothing written):
Tool call:
Bash(cd ~/code/alen/alen.andronewille.eu && time go run . log ~/code/[work]/poc --dry-run > [tmp] …)
Tool output:
Session: ~/.claude/projects/~-code-[work]-poc/59e16794-b4e4-49b5-a356-83fba0905ce6.jsonl
Writing the note with Claude; this takes about a minute.
go run . log ~/code/[work]/poc --dry-run >   2.03s user 1.35s system 3% cpu 1:43.44 total
exit=0
==> notes/2026-11-09-prompt-it-once-then-build-the-command.md
# Prompt it once, then build the command
… +44 lines
Claude:
It works end to end: Claude drafted a solid note in 1 min 43 s and nothing was written. Updating the wait message to match, documenting the command and checking the CLI rules MIG requires:
Tool call:
Bash(cd ~/code/alen/alen.andronewille.eu && python3 - <<'EOF' …)
Tool output:
site builds alen.andronewille.eu into static HTML pages.

Examples:
  go run . build                  Build into ./site
  go run . build --out /tmp/dev   Build into another folder
  go run . serve                  Serve on localhost:8080, or a free port, rebuilt on every change
… +32 lines
Claude:
To use it after your next session, from ~/code/alen/alen.andronewille.eu:

```sh
go run . log ~/code/[work]/poc
```

It writes two files in notes/: the .prompt.md transcript and a note Claude drafts as a decision diary. It takes about 2 minutes and never overwrites an existing file. Read both before you publish.

What I checked on today's session:
- A dry run (--dry-run, so nothing was written) found all 12 of your prompts, verbatim, including the skill commands.- The redaction worked: no client name, product name, GitLab host, username or /Users/ path came out.- Claude's note followed today's format (title, 5 choices, how I used Claude, next steps) and took 1 min 43 s.- --help works, a wrong project folder exits 1 with the fix in the message, and there are no colour codes in the output. The site still builds.
What changed in the site repo:
1. **log.go** (new): reads the session from ~/.claude/projects/, writes the transcript in the format your viewer reads (prompts verbatim, tool output cut to 6 lines), and redacts it. It then calls claude -p with no tools and no saved session, giving it your newest note as the style example.2. **main.go**: the log command and its help text. Use --dry-run to check first, or --transcript-only to write the note yourself.3. README.md and CLAUDE.md: how to run it.4. **~/.config/alen.andronewille.eu/redact.txt** (outside the repo): the names to hide, one per line as name => [replacement]. It now holds today's client, product, GitLab host, username and work path. Your home folder, email addresses and common secret formats are always replaced.
Limits:
- It takes only the newest session of a project. If you work in two sessions on one day, run it once per session file (go run . log path/to/session.jsonl).- The site folder isn't a git repo, so I didn't commit anything there.
Run go run . log ~/code/[work]/poc --dry-run once and read the note, to check the voice.
Prompt:
run the dry run for today's session
Tool call:
Bash(cd ~/code/alen/alen.andronewille.eu && out=[tmp] …)
Tool output:
Session: ~/.claude/projects/~-code-[work]-poc/59e16794-b4e4-49b5-a356-83fba0905ce6.jsonl
Writing the note with Claude; this takes about 2 minutes.
exit=0
==> notes/2026-11-09-ship-the-poc-then-automate-the-log.md
# Ship the PoC, then automate the log

… +50 lines
Claude:
The dry run finished. Read the full output (note and transcript) here: `[tmp]

What it would write:
- Note: notes/2026-11-09-ship-the-poc-then-automate-the-log.md, titled "Ship the PoC, then automate the log". It has the same sections as today's hand-written note, and now also covers building the log command.- Transcript: notes/2026-11-09-ship-the-poc-then-automate-the-log.prompt.md, 1 098 lines with all 13 prompts, including this one.- Redaction: the search for the client, product, GitLab host and username found nothing to redact. Its only match for /Users/ was the literal text "/Users/" inside one of my replies, not a real path.- Numbers: the ones in the note match the session (97 checks, 5 028 lines of Go, 1 min 43 s).
One thing to decide: notes/ already has today's hand-written note, 2026-11-09-a-poc-in-one-session.md. A real run adds a second note for 2026-11-09 next to it. Either:
1. keep the hand-written one and skip the real run today, or2. delete the hand-written note and its .prompt.md first, then run go run . log ~/code/[work]/poc (about 2 minutes).
Which one?
Prompt:
2, replace the hand-written one
Claude:
Moving the hand-written note and its transcript to the scratchpad as a backup (not deleting them), then the real run:
Tool call:
Bash(cd ~/code/alen/alen.andronewille.eu && bak=[tmp] …)

hire me

I work with a few people at a time. When there's room, I call the next one on my list.

Only I read this. I delete it once we've spoken.