"Your AI keeps proposing the database your team rejected in March"
In March the team spent two weeks evaluating a document store for the product search index. It fell over on multi-region writes and the idea was dropped, in a chat thread that scrolled away the same week. In July someone asks an agent to speed up search. It proposes the same document store. Clean reasoning, plausible migration plan, no idea the team already went down that road.
The agent isn't being dumb. Nothing it can read remembers March. Think about where rejections live today. ADRs record what you picked and mostly skip what you didn't. Post-mortems cover outages, not evaluations, and chat scrolls away. So every fresh session re-derives the obvious-looking answer, and a human has to remember March each time. That works until the human who remembers is on vacation. Or gone.
A rejected path only stays rejected if it's written down with the reason. Otherwise it isn't a decision, it's folklore, and folklore doesn't survive contact with an agent that wasn't there.
What fixed it for us was treating dead ends as first-class records. We build kgai, and in it rejected approaches stay in the graph with the reason they failed, right next to what shipped. Recall is scoped to the area the agent is about to touch and returns the decisions in force there, each carrying what was rejected and why. So before proposing a storage change, the agent reads that the document store fell over on multi-region writes, and moves on. Nothing is overwritten. If the constraint genuinely lifts, say the store fixes multi-region writes, you don't delete the rejection. You record a new decision that supersedes it, with the new reason, and the whole chain stays queryable.
That last part matters more than it looks. A deleted rejection and a superseded one look identical today and completely different in a year, when someone asks why you migrated. Dead ends are documentation makes that case in full, and the day a new dev asked why we don't use the obvious search engine is what it looks like when the record exists.
Capture is automatic in Claude Code sessions through a skill and hooks, the store is local-first with opt-in sync over an S3 bucket you own, and it's MIT.
Two commands install it, and rejections start getting recorded along with everything else. If you want to see how it sits next to other memory tools, the comparison is below.
Try it on your repo. Two commands, MIT, nothing leaves your machine until you configure a bucket.
See how kgai compares to other tools on the comparison page, or browse the other notes.