50 sessions · 1.1 GB of logs · ~1,300 of my messages · 445 failed shell commands
I wrote the rule down. It broke again 44 minutes later.
I ran Matt Pocock's /retro skill over my last 50 Claude Code sessions. It reads the session logs and looks for where the agent's environment failed it: navigation, missing checks, expensive tool calls, instructions that change nothing. Not where the code is broken. The top finding was me, writing "broken" about 15 times in three weeks, mostly about the same thing.
The 44 minutes

I build three sites with Claude Code (nautivia.eu, nautivia.app and croatianferries.com) and a push to main is the deploy. I don't test locally; I test live, on my phone. Again and again I'd open the desktop and something had broken, or open the small iPhone and a card was clipped.
On 3 October I wrote "you are not validating changes on all screen sizes!" and the agent saved a tidy memory note, with a Why and a How to apply: before saying done, read every phone-size screenshot and assert nothing is clipped. That was 21:44. At 22:28 I wrote "broken on desktop". At 22:30, "this is broken, map must fit above the fold". At 06:11 the next morning, another.
What /retro does
It is a skill in mattpocock/skills (skills/engineering/retro), paired with his writing-for-agents style guide. You run it on a session, or on the last N. It sorts candidates into a few buckets: navigation (how long did it take to find the right file), automated checks, coding standards for a reviewer agent, steering files that are too big, tool economy, instructions that are no-ops, and information the agent could not reach. One rule does most of the work: a mechanical mistake gets a deterministic check, not another rule. Prose rules are for judgement calls.
For 50 sessions I had it script the extraction instead of reading 1.1 GB: every one of my messages, every failed tool call grouped by cause, and every tool result over 40 KB.
Why the note lost
An instruction in CLAUDE.md or in memory is one line among hundreds the model weighs, mid-task, against the task itself and everything else in the window. Sometimes it wins. A hook weighs nothing; it either lets the command through or it doesn't. Addy Osmani put it in one line in Agent Harness Engineering: "Hooks are what separate 'I told the agent to do X' from 'the system enforces X.'"
So instead of a better note, each repo now has a pre-push hook. It builds the site, serves the build locally, opens a few key pages at 360, 375, 820, 1366 and 1920 px, and fails on two things: the page scrolls sideways, or a link or button sits off the screen edge. Push is the deploy, so a failing hook is a deploy that doesn't happen. On the hub it takes about 14 seconds.
// the probe, run in each page at each size
if (document.documentElement.scrollWidth > innerWidth + 1) bad.push('page scrolls sideways');
for (const el of document.querySelectorAll('a, button, input, select')) {
const r = el.getBoundingClientRect();
if ((r.right > innerWidth + 1 || r.left < -1) && !insideAScroller(el)) bad.push('off-screen');
}
Two traps while building it. A page missing from the build returned a 200 directory listing and passed, so a path with no index.html now fails. And body { overflow-x: hidden } makes every element look like it is inside a scroll container, so the check would never fire; only containers below body count now. Before trusting it I pointed it at a page with a 900 px link: 6 problems, red.
What else it found

I ran it on a client project the same morning. The details are theirs; the lessons repeated:
- The rule was written twice and still lost. "Use browser X" was in
CLAUDE.mdand in memory. One session made 553 calls to the other browser and 59 to the right one. A hook now blocks the other browser's tools in that repo, with a message saying which one to use. - The memory index was over its size limit. It loads up to a cap, and the file was past it, so the last lines never loaded. The newest lessons were the first ones to disappear. The fix was shortening every index line to a pointer and keeping the detail in each note's own file.
- An alert nobody reads. A nightly consistency check had failed 5 times. Under it, a nightly job had not completed for 13 days because three empty rows crashed it. Fixed and back-filled.
- 452 lint errors, no pre-commit hook. 442 were auto-fixable. Now 0, and a hook lints staged files.
And on my side:
- 22 tests failing on main. Nobody knew, because nothing ran them. They are listed in a known-failures file so the hook can run the other 788; a line gets deleted when a test is fixed, never added to get a push through.
- The build dirtied a tracked file every run. It fetched the live queue reading over a committed daily snapshot, so every build left the tree dirty, and a dirty tree blocked
git pull --rebaseabout 20 times across the sessions (not all of them this file). Until now the only defence was a memory note about rebasing. The fix was the build writing its fetch to a gitignored folder. - A skill's examples left out a line a hook requires. 28 blocked browser scripts, because the hook demands a profile line and the skill's own example didn't have one.
- 1,525 full-size screenshot reads. Now capped at 1,200 px and cropped before reading.
- macOS shell papercuts. No
timeout(10 failures), zsh aborting on empty globs (32),sleepblocked by a hook (21). One line in the global steering file.
What I would tell myself three weeks ago
Every finding above had been a note I wrote and believed. The note felt like the fix because writing it was the moment the frustration ended. The pattern I'm keeping: when the same complaint shows up a third time, it is not an instruction problem. Find the command where it would have been caught and put the check there.
Run it on your own sessions
Install from github.com/mattpocock/skills and run /retro last 50 sessions. Then do the boring part: build the checks it names, and prove each one goes red.