When the agent repeats a mistake you already fixed
it is not repeating a mistake — from its side the problem is new
The agent proposes the approach you tried in March and backed out of. Or it hits the same error again — ModuleNotFoundError: No module named 'yaml' — and starts guessing, in a repository where that exact error was solved four times already.
It is not being stubborn. From its side the problem is new: the session that solved it ended, and nothing read the transcript back. The word "repeats" describes your experience, not the agent's.
Which walls a machine actually hits again
deja friction counts errors that appear in three or more separate sessions and names the agents that hit them:
$ deja friction
what this machine keeps tripping over — 500 sessions read
16 sessions command not found: timeout
claude, opencode · last Aug 23
11 sessions command not found: python
antigravity, claude, codex, opencode · last Jul 22
10 sessions ModuleNotFoundError: No module named 'yaml'
claude, opencode · last Aug 20
Two things worth noticing in that output. The counts are sessions, not occurrences — sixteen separate times somebody started fresh and met the same wall. And the harness column is usually more than one, which is why a memory scoped to a single agent does not close it.
The fix that stayed fixed
Knowing you hit something sixteen times is only half of it. The other half is what ended it:
$ deja fix "ModuleNotFoundError: No module named 'yaml'"
The answer is a command that was actually run on this machine after that error, in sessions where the error did not come back afterwards. That last clause is the whole design: a command that was run and then followed by the same failure again is not a fix, and pairing an error with whatever came next without checking would produce exactly that kind of confident wrong answer. When nothing qualifies, the command says so rather than guessing:
$ deja fix "command not found: timeout" deja: no session on this machine ran a command after that error
Doing it without being asked
Neither command helps if you have to remember to run it, and the moment you would remember is after you have already lost the twenty minutes. So the same lookup runs from hooks, where the harness supports them:
- Before a command runs — the invocation this project actually uses, with the flags it was run with, instead of a plausible guess.
- After one fails — what followed that same error here before. This is the pair an agent never thinks to ask for, because from inside the session there is nothing to ask about.
- Before a file is edited — what earlier sessions decided about that file, including a decision that was tried and reverted.
deja install --auto wires whichever of those the harness exposes; the agents guide lists which supports what.
What about a rules file
CLAUDE.md, AGENTS.md and their equivalents are the right place for a rule you already know you need — the build command, the conventions, the directory nobody may touch. They do not solve this one for two reasons. Nobody writes down the wall they have not hit twice yet, and the file is read in full on every session, so it cannot hold the long tail of things that each mattered once. History is the long tail.
Marking what did not work
Sometimes the repeated mistake is one you already investigated and rejected. deja promote <id> --state rejected --note "why" marks that session, and every later hit for it shows that it was tried and rejected, with the reason. Nothing is deleted — --state accepted takes the mark back — and the note travels to the other machines you sync with.
Why agents forget in the first place · What compaction drops · Getting started