Stolperfalle.
A self-hosted memory for AI coding agents. When an agent hits a trap, the lesson is stored as a knowledge unit: what happened, why it matters, what to do next time. When another agent later hits a matching error, the unit is already in its context, before it starts guessing.
This lesson was written on 23 October, after a release died silently at a security audit. A week later the same audit failed again, on a vulnerable library pulled in by a dependency. This time the hook was ready.
- CI · security audit
joserfc 1.6.8 GHSA-jpxq-7h8p-2q76 fix 1.7.5 joserfc 1.6.8 GHSA-9pg5-8c7x-5hfx fix 1.7.6 Found 2 known vulnerabilities in 1 package Process completed with exit code 1.
- Stolperfalle hook · unit injected
“For a transitive advisory,
uv lock --upgrade-packagewith the package name is usually enough and needs no pyproject change.” - Agent · next command
$ uv lock --upgrade-package joserfc Updated joserfc v1.6.8 -> v1.7.7 352 passed
- CI · merge · store
Security audit passed · merged · released v0.2.43 confirm → confidence 0.64 · 2 confirmations
ku) is one stored lesson; its confidence rises each time an agent confirms it. The unit arrived with the error, and the agent’s next command was the one it named. Paid for once, used twice.Why.
AI coding agents forget. Every session starts from nothing, so the trap one session fell into is waiting, unmarked, for the next: the silent misconfiguration, the API that behaves differently from its docs, the tool that fails without an error. Run agents across many repositories and the same lesson gets learned again and again.
Stolperfalle is German for trip hazard. The tool marks the spot so the next agent steps around it.
Until July 2026 it was called Stolperstein. Stolpersteine are Gunter Demnig’s brass Holocaust memorial stones, set into German pavements. A dev tool has no business borrowing that name, so I renamed it.
The business case
Repeating the same mistake is silly.An agent without a memory repeats it in every session.
The trap in Plate I cost about an hour the first time. The second time it took under five minutes, from the failed check to a green one.
Each repeat costs a session’s time, its tokens and a developer’s attention, and it rarely announces itself. Stored once, a lesson reaches the next agent that hits the same error, in every repository on the install. Running it takes one container and under €30 a month.
The loop.
One store, three paths through it. Six MCP tools (the protocol coding agents use to call outside tools) over one SQLite file: query, propose, reflect, confirm, flag and status. Search runs on full text and local embeddings (search by meaning) together.
open the diagram to zoom →- Write
propose·reflectAn agent proposes what it learned. Orreflectdrafts candidates from a session summary, and the agent reviews them before proposing. - ReadClaude Code hooksWhen a prompt or a failed shell command carries a real error signal (a traceback, a non-zero exit code, an HTTP status), the hook calls
queryand injects the best-matching unit. Plain conversation doesn’t trigger it. - Quality
confirm·flagAgents confirm advice that worked and flag advice that was wrong, stale or duplicated.
What leaves the server.
The first question to ask about a tool that reads error output. The answer, operation by operation:
| Operation | Leaves your server? |
|---|---|
queryproposeconfirmflagstatus | No. SQLite on your server. Embeddings are computed locally (all-MiniLM-L6-v2) unless you point it at an embeddings API. |
| Hooks | No. Error text goes from the developer’s machine to your server, nowhere else. |
reflect | Only with a key. The session summary goes to Anthropic (claude-sonnet-5-5) if an API key is set. Without one a local heuristic runs and nothing leaves. |
Around the data.
- IsolationEvery unit has an owner org, and
queryonly returns units from trusted orgs. Today that is a read filter that trusts all by default; write-side permissions are planned. The clean boundary today is one install per team or client. - ProvenanceEach install signs its units with its own Ed25519 signing key (a
did:key, a public-key identity), stored outside the database. A database leak does not let anyone forge units. - Running itOne container, about 1 GB with the embedding model, and one SQLite volume. Migrations snapshot the database before any breaking change.
reflectcosts about one US cent a call: under €30 a month even at 100 calls a day, zero without a key.
The numbers.
The store audit, October 2026.
I had four Claude reviewer agents audit 599 of the store’s roughly 640 non-archived knowledge units, each taking a slice with an index of the whole store. The reviews, the fact-checks and the rewrites were model work. A human decided what to apply.
| Outcome | Units | Share of 599 |
|---|---|---|
| Kept as-is | 527 | |
| Duplicates merged | 32 | |
| Rewrittenof 26 attempted | 22 | |
| Corrected after a fact‑checkof 10 suspected | 9 | |
| Archived as stale | 3 | |
| Flagged, left in place | 6 |
88% kept is a model’s judgment, not ground truth. The six left in place: four rewrites that couldn’t be applied, because propose rejected each fix as a duplicate of the unit it was fixing, one suspected error that turned out to be right, and one duplicate pair kept on purpose because the two complement each other. The audit also found twelve near-identical units about zsh versus bash, proposed weeks apart. Paraphrases slip past the duplicate check.
Choosing the model behind reflect.
reflect ran on gpt-4o-mini, which OpenAI shuts down on 14 January 2027. Before replacing it I built 12 test cases from real units in the store, each a session summary with the learnings a careful human would extract and the noise that must not be extracted. A separate Claude agent graded six anonymous outputs per case (the seventh configuration failed every call), labels shuffled per case. Each row is a model and its reasoning setting.
| Configuration | Overall, 1–5 | Cost / call | Bad candidates | Learnings found / 13 | Median latency |
|---|---|---|---|---|---|
gpt-4o-miniprevious | $0.0002 | 10 | 10 | 3.3 s | |
gpt-6-lunaprevious payload | every call HTTP 400 | ||||
gpt-6-lunareasoning low | $0.0003 | 5 | 12 | 8.6 s | |
claude-haiku-5-5effort low | $0.0005 | 3 | 13 | 4.7 s | |
claude-haiku-5-5effort medium | $0.0006 | 2 | 13 | 5.1 s | |
claude-sonnet-5-5effort lowin production | $0.0095 | 1 | 13 | 6.6 s | |
claude-opus-5-5effort low | $0.0180 | 1 | 13 | 7.7 s | |
A model-name swap would have failed silently.
OpenAI’s named replacement for gpt-4o-mini is a reasoning model and rejects temperature with an HTTP 400. reflect caught every LLM error, logged a warning and fell back to its heuristic, so production would have kept “working” with much worse candidates and nothing watching that warning. Now a 400, 401, 403 or 404 from the provider raises. Only timeouts, server errors (5xx), refusals and unparseable output fall back, they log at error level, and status counts them by reason.
Production now runs claude-sonnet-5-5 at low effort. The cheapest model was the worst value: terse, generic candidates that dropped product names and versions. Sonnet and Haiku found the same 13 learnings; Sonnet had fewer bad candidates, and the cost difference is small in absolute terms, so I picked Sonnet. Opus did not beat Sonnet, at twice the price. The whole comparison cost about $0.36.
Limits, stated in the eval itself: 12 cases, one run per configuration, one grader. The grader was a Claude model, so the gap between the Claude and OpenAI rows may carry some self-preference. A second, non-Anthropic grader was planned and did not run. Read neighboring rows as indicative, not precise. The full write-up is in the repo.
Known gaps.
As of October 2026, and listed in the repo’s own overview, because a memory tool that hides its holes is not one you should trust with your errors.
- No redaction on write.Agents can and did write client names and internal hostnames into knowledge units. The audit found them and they were rewritten by hand. Until redaction exists, keep one install per client.
- No hard delete.Flagging a unit archives it but keeps the row. Erasure, say a GDPR Art. 17 request, needs an operator to delete it from SQLite.
- Confidence is mostly unrated.94% of units sit at the default confidence with zero confirmations, because agents rarely call
confirm. Treat confidence as weak until it is derived from usage. - No edit path.A correction is a new unit that supersedes the old one, and a near-identical fix can be rejected as a duplicate of the thing it fixes.
- No measured productivity effect yet.The pilot below would measure it.
- One LLM provider.Extraction in
reflectruns on the Anthropic SDK. Switching providers is a code change today.
The pilot that would measure it.
Four weeks, one team, one install. Before and after, on the errors the hooks detect:
- How often the same error signature recurs across sessions.
- How often an injected unit is followed by a successful next command.
- How many units get confirmed or flagged, which shows whether the quality signal starts working.
The open standard.
Stolperfalle is not a fork. Its knowledge units follow Mozilla AI’s open cq schema, a shared format for what agents learn, and stay strictly valid on the wire. The fields I need beyond it ride in an extensions slot, under stolperfalle:* keys.
That slot didn’t exist. I proposed my extensions upstream in discussion #286, scoped it in issue #406 as one generic slot instead of a looser schema, and it merged as PR #453 on 23 June 2026. Any implementation can use it.
Upstream declined to promote severity, kind and contributing orgs into the core schema: importance should emerge from usage, not self-declaration. The October audit points the same way: declared severity skews high, confidence never moves off its default, and duplicates are paraphrases. I posted those findings back to the discussion.
Leaving is cheap. MIT license. One SQLite file. Units serialize to the upstream schema, so they can move to any other cq implementation. The LLM is optional: remove the key and everything except reflect’s draft quality keeps working.
Where it stands
The idea is small: what one session learns shouldn’t die with it. The gaps above are the work still on the bench.
Stolperfalle is the first piece of a machine-readable layer for what an organization knows. It runs in production with roughly 640 live units, and the code, the overview and the eval are all public. If your teams run coding agents across many repositories, tell me what they keep relearning.
The model swap that would have failed without a sound →the build log, on casey.berlinSourcesThe codeOVERVIEW.mdThe evalNext caseKlosterdorf, in German →