· 12 min read

From Memory to PR Autonomy: What I Built on Claude Code


I use a coding agent all day. Every morning, it’s a stranger.

“Hi! I’m new here. What’s a cluster?” — when what I wanted was closer to “prod goes through Actions, I know.” Not a smarter model. The same model, with nowhere to put what it learns.

Where those notes actually live — a five-layer architecture, and a promotion loop that keeps it from turning into a graveyard of half-useful notes — is its own post. This is what happens once a note has somewhere reliable to go: how recall actually works moment to moment, and everything I built on top of it once that foundation held — task state across sessions, and eventually letting the agent review its own pull requests. No new tool anywhere in this. I just kept extending the one I had, one annoyance at a time.

Part 1: memory

You can’t recall what you never wrote down. The whole system has one shape, underneath everything else in this post: create the note, maintain it, recall it. Watch the middle step disappear and the last one grow, because getting the right note in front of the agent at the right moment — without anyone having to remember it exists — turned out to be the hard part.

Five moments to use what I wrote

The harness gets five separate chances per session to put a past lesson in front of me at the right time:

Hook Purpose
Session start — “set the desk up” What am I mid-way through? Load the index of everything I’ve learned.
Every prompt — “just-in-time recall” Match my words against past lessons, surface the 2–3 that apply.
Before a tool — “the bouncer” Guard the files that bite. The only one of the five that can say no.
After a tool — “react to reality” A new fact appeared — make it findable, keep state fresh.
After each reply — “harvest, no cleanup” Rebuild the index, file the transcript for later. I never tidy up; it just happens.

One session never has to remember to do any of this. The harness does, on every single prompt.

How the right two surface out of 153

Take a prompt like “the alert annotations aren’t showing up” — split it into words, score every note in the corpus against its description and tags, take the top two or three. No embeddings, deliberately: keyword scoring against a required tag is cheap, fast, and — crucially — I can read exactly why a note did or didn’t surface, which matters a great deal when it’s wrong. A note with no tags, or filed in the wrong place, is invisible. That took two rounds of getting it wrong to learn.

The numbers, after enough use to trust them: 1,465 lookups, 43 misses, a 3% miss rate, 2.6 notes surfaced on average per lookup.

The notes that get reused most aren’t facts

The busiest notes in the graph — the ones with the most cross-links to other notes — aren’t infrastructure facts. They’re habits: when to act versus when to check in first, go verify instead of asking, a green test suite isn’t proof of anything on its own. The system started as a place to remember cluster names and config quirks. What it actually accumulated was a working theory of how I want to be worked with.

From six files to 153 notes

Two months ago, this was six files. It didn’t grow smoothly — one note in April, eleven by May, forty by June, 153 by the time I measured it properly — and the shape of that growth is worth a closer look: 49 notes are general engineering practice, another 42 are specific to my current employer’s infrastructure but not tied to any one repository, and the remaining 62 belong to a single repository each — mostly live infrastructure state, Kubernetes resource manifests, Ansible playbooks and service configs, and Terraform modules. Add it up and roughly 60% of the corpus, 153 notes and 214 links between them, isn’t tied to any single project — which means it’s just as useful in whatever session you’re reading this in as it is in mine.

Once a note had somewhere reliable to live, and recall was reliable enough to trust, I had the ingredients for a bigger problem.

Part 2: task state

I work one pull request from two terminals sometimes, and each one is amnesiac about the other. 9am, terminal one: started the flaky-test fix, left it half-done. 2pm, terminal two: what was I doing this morning? Where did I stop? No shared memory between them.

The fix was almost embarrassingly direct: copy the memory pattern and point it at tasks instead of notes. One global database instead of scattered TODOs, dependency-aware instead of held in my head, recalled the same way — at session start, so a new terminal opens onto whatever’s already in flight; created without being asked, whenever a prompt looks complex enough to outlive the session it started in. The database itself is beads, a git-backed issue tracker built for exactly this — agent-supervised work that has to survive across sessions without losing the thread.

Solving memory once meant I’d already solved tasks. That’s the part worth sitting with — the lifecycle was never really about notes.

Part 3: PR autonomy

Same three-stage lifecycle, but now the middle stage — maintain — is almost the entire job. Open the PR is one step. Review, fix, answer humans and Copilot, keep CI green, stay ahead of master: that’s most of the actual work, and every finding in it used to wait for me. I was the bottleneck, by construction.

Two foundations from Parts 1 and 2 turn out to be exactly what this needed: what I know (conventions, past mistakes, now context for every review) and what I’m doing (task state that survives across sessions, so results carry back to me instead of evaporating).

Three independent reviews, one synthesis

Three independent review runs over the same diff feeding into a synthesiser, which dedupes and ranks the findings against the last push

I push a branch, a hook fires with no command from me, and three independent review runs go over the same diff with the same prompt. A synthesiser dedupes and ranks the results against the last push — what regressed since I last looked, what resolved. One review run misses things at random; three plus a synthesiser miss less, and it tells you what changed, not just what’s currently wrong.

The findings run in the background, so they have to reach me somehow. They’re written to the same task-tracking system from Part 2 — the same wire — and a hook surfaces the open item as I work, the same way memories surface, the same way tasks surface. Three different problems, the exact same mechanism carrying the answer back each time.

A clock, not a chat

A background loop diagram: my live session and the loop each competing for one open task, settled by a short-lived lease

Half of what happens to a PR isn’t something I do. A colleague reviews it at 4pm, a bot leaves eight comments, CI goes red at midnight — nothing built for session-scoped work catches any of that. So there’s a background loop: not a chat, a clock. It wakes on its own, reads git and GitHub and the task list, and acts. It survives a dead cloud-provider login by pinging me and resuming when I’m back.

Which creates a new problem: now two workers can want the same piece of work — my live session and the loop, both looking at one open task. A lease settles it — a short-lived claim, the same idea as a “checked out” sign on a shared document: whoever claims the task first holds it, and it automatically expires and frees up again if they stall. The other worker sees it’s taken and moves on. No double-work, no two agents independently fixing the same finding.

What counts as actually ready

A pull request isn’t done until a real checklist clears — behind master, unresolved review threads (human and bot both count), checks failing or simply not reported yet, open findings each fixed or dismissed with a stated reason, an unapplied Terraform plan (nonprod the loop may apply; production, always a person). Two of those checks are marked differently on purpose: review state unreadable and plan state unreadable are their own category, not folded into “clean.” If the API call to check review state fails, that’s not the same as a clean review — a timed-out check used to get treated as a pass, quietly, and that’s exactly backwards. Absence of evidence isn’t evidence of a clean state.

The judge, and the box it’s allowed to handle alone

A 2x2 matrix scoring changes on blast radius and revert difficulty — the judge’s job is to grow the box of changes it can safely handle alone

Not everything that could be automated should be, so there’s a judge sitting in front of all of it, scoring changes on two axes: blast radius (how much of the system a change could actually touch if it’s wrong) and how easily the change reverts. A missing null check, a typo, a bad import — small blast radius, trivially undone — gets handled without me. A shared helper, a schema change, anything close to production gets flagged for a look, regardless of how small the diff looks. The entire point of the judge is to grow the box of things it can safely handle alone, and it only grows that box as fast as its mistakes stay near zero.

And every autonomous change signs its own work. A commit message names the specific loop that made the change (not “an AI”), links back to the exact finding it addresses, and states its own safety claim in writing — revertible, re-reviewed next round. If something in that stream turns out to be a bad habit, one git command finds every commit that loop made and reverts them as a set.

While I’m away, nothing is allowed to happen quietly — a message actually reaches me: “needs you — 1 regressed, 1 resolved since last push, 1 high severity of 19 total still open,” or “investigating a couple of high-severity findings, will fix what’s safely reversible and send a follow-up.” Mostly it’s FYI. Sometimes it needs an actual call from me. Underneath every message, there’s a full audit trail of what ran and what it found.

Three ways to steer an agent, and they are not equally trustworthy

This is the part that ties the whole system together, and it’s the part I wish I’d understood from the start instead of backing into it three separate times.

Nudge Gate Loop
What it is Text slipped into a live session A check before a tool that can refuse A clock, outside any session
Needs Someone in a session, and the model to actually read it Nothing — it’s mechanical Nothing in a session
Breaks when Nobody’s there, state moved on, the model skims it Almost never The goal keeps moving, or it needs real judgment
Trust level Advisory Absolute Eventual

A forgotten rule doesn’t get fixed by a better-worded reminder — that’s a nudge problem masquerading as a wording problem. Anything that must never happen belongs in a gate. Anything that has to run whether or not I’m at the keyboard belongs in a loop. Memory, mostly, is a nudge with unusually good aim. The pre-tool file guard is a gate. The background PR loop is, obviously, a loop. All three were built to solve a specific, separate annoyance, and only in hindsight do they turn out to be three points on the same axis.

It dreams, but only when I say so

Every session gets filed as a transcript. Periodically — not on a schedule, at session start, as a nudge — the system offers to mine that backlog for patterns I should have written down and didn’t: a note I should have kept, or occasionally a whole reusable skill. As of writing, 88 sessions are queued for that pass, last run three days ago. I run it myself, when I decide to. I approve or reject every single suggestion it makes. Nothing gets written to memory automatically from this pass.

That restriction is deliberate, not incidental. An agent that writes its own beliefs unsupervised isn’t building memory — it’s running a rumor mill on itself, and every subsequent recall inherits whatever it made up.

Results and lessons

I try to measure the thing that’s measuring me, and the honest version of that number is uncomfortable in the right way: it acts on its own about 5% of the time; the other roughly 90% it shows me findings and waits. The goal is around 75% autonomous, not 100% — some fraction of work should always come back to a person, on purpose.

The rest of the scorecard: 22 fixes made entirely on its own, all time. 9% of those later reverted — flagged by the system itself, not caught by me after the fact. 390 times it surfaced something instead of acting on it. 926 passing tests across the harness that makes all of this possible.

That 9% revert rate is the number that actually governs the pace of everything else here. The box the judge is allowed to handle alone only grows as fast as that number stays near zero. Almost nobody publishes their own failure rate for a system like this; it’s the least comfortable number on the list and also the one that makes the rest of it credible.

What it actually comes down to

None of this needed a new tool — I kept extending the one I had. If your agent forgets the same lesson twice, that’s the thread to pull first: give it somewhere to write things down, then make recall automatic, before you reach for anything fancier. The gate and the loop only became necessary once the agent’s job grew past what a nudge could safely cover. Build in that order, and each piece earns its own existence before the next one shows up.