DEV Community

Denys Severyn
Denys Severyn

Posted on

19 review comments, one mistake, zero memory: giving Claude Code a guilty conscience

Claude Code writes code that works. Builds, tests pass, CI green. Then a human reads it.

19 review comments, one mistake, zero memory: giving Claude Code a guilty conscience

  • IList<T> on a Kafka message. Compiles, serializes, consumer can't deserialize an interface. Kafka has no opinion on your abstractions. List<T>.
  • Variable named result. In a codebase where even the loop variables have job titles.
  • Handler changed, validator next to it not. Contract test still green because it never covered the new field.
  • Enum entry for the old name, two directories away from the diff.

None of it in CLAUDE.md. All of it known to every human on the team, including the one who joined last week. That's the gap: tacit knowledge. The reviewer has it, the model doesn't, and nothing moves it across except the reviewer, again, by hand, with a slightly shorter comment each time.

Each comment gets fixed. Next PR, same class, different file. Fixing the comment removes today's symptom. Capturing why is the only part that changes the next PR.

What I tried first

lessons.md, hand-written, imported from CLAUDE.md. Rule for Claude: reviewer catches something, add a line.

Three months in: 99 lines, 312 captures logged, top line hit 19 times. Not 19 reviewers. One class, "after a rename, search the whole repo, not your diff", caught 19 times, each with a new wrinkle, by reviewers of steadily declining patience. Second line at 10. Third at 8. The file had become a leaderboard.

Claude had every line in context every session. Didn't matter. 60k tokens into a change, the line is somewhere behind three file reads and a test run. Memory wasn't the problem. Timing was. Nobody re-reads the safety briefing mid-flight.

Then four bash hooks and jq. Every script: command -v jq || exit 0. No jq → silently dead, no signal. Found out weeks later. The system had achieved perfect silence, which is the one thing it was built to prevent.

tacit

github.com/N1XUS/tacit. Claude Code plugin. Stores papercuts: small, repeatable, they add up. Three parts.

1. Capture the why, not the what

Hook fires after gh pr view --comments / gh api …/comments. Points at a skill: triage each comment, fix the valid ones, then answer one question per fix:

Why did I write it that way? What class of mistake is this an instance of?

"Forgot to remove the enum" is not a root cause. "Scoped the removal to files I was already editing" is. The first one gets you a sticky note. The second one gets you a grep.

2. Two tiers, split is load-bearing

Store Holds Test
~/.claude/papercuts.md the CLASS, generalised "Holds in a random Python repo?"
<repo>/.claude/papercuts.md how it bites HERE "True only in this repo?"

Global: "Message types cross a serialization boundary: concrete collections, never interfaces."
Local: "Kafka payloads: List<T> not IList<T>, grep -rn 'IList' src/Messages/."

One file can't hold both "every line generic" and "on second hit, rewrite as a concrete command". Concrete commands are stack-specific. So two files. Took me embarrassingly long to stop fighting that.

Scoped variant: .claude/papercuts/sql.md with paths: frontmatter, injected only when a matching file is written. Same schema as native .claude/rules/, so promotion is a file move.

3. Inject at the moment, not at the start

Event When Does
SessionStart startup, resume, compact rule + both tiers
PostToolUse(Bash) fetched review comments capture skill
PreToolUse(Bash) git push, gh pr create checklist before shipping
`PreToolUse(Write\ Edit)` 1st edit, every 15th
UserPromptSubmit 1st prompt, every 10th reminder: reviews / corrections → which skill

Every trigger a fact: command ran, file about to be written. Not every edit: text repeated 200×/session is wallpaper, and the model learns to skip it exactly as fast as you learned to skip cookie banners.

Counters that get audited

Every global line ends [hits: N, last: YYYY-MM]. Same class caught again → increment, sharpen wording, no near-duplicate. hits >= 2 → lint fails: rewrite as a check, put the command in the local store, mark escalated.

Hits are self-reported by a model. Asking a model to count its own mistakes is asking a cat to log its own vase incidents. Cheapest way to satisfy "increment, don't duplicate" is to duplicate anyway and report a clean conscience. So every capture appends a line to papercuts-log.jsonl, and tacit audit diffs log against counters. Logged > claimed: near-duplicates added quietly. Claimed > logged: capture without log line. Evidence, not a hunch.

Hard cap 30 per tier. Line 31 is where checklists go to become decoration. An unread checklist is worse than none: looks like coverage.

Two invariants

  1. Never emit permissionDecision. "allow" skips the user's confirmation. A context-only hook emitting it silently auto-approves git push. Asserted in tests.
  2. Never throw, never exit non-zero. Hook that breaks a tool call is worse than no hook. Every failure → silence. tacit doctor exists because silent failure in a system whose job is to not stay silent is fatal.

Built and deleted the same day

Correction detection: "you did it wrong, here's how". No finite grammar. Tried:

  • Regex. Bounded, Unicode-safe. English only.
  • Haiku via API. Thinking off, effort low, 4 output tokens, Y/N. ~$0.00001/prompt. Needs ANTHROPIC_API_KEY in hook env. Subscription sessions don't have one.
  • Detached claude -p --model haiku. Session auth, works on subscription. 3.6s spin-up per prompt. A whole second Claude, booted from cold, to say Y or N. Hiring a consultant to nod. Also can't auth inside the desktop app.

Shipped: one-line reminder, 1st prompt then every 10th, names which skill handles reviews / PR comments / corrections. Main model judges. It already holds the prompt, it's the smartest thing in the room, and it works for free. Any language. ~60 tokens per 10 prompts. Re-arms after compaction. The dumbest option, after a full day of clever ones.

All three classifiers removed from the tree. Not flagged off. Removed. Dead code behind an opt-in is dead code with a nicer funeral.

What the host does natively now

Checked against Claude Code 2.1.278. Auto memory records corrections (type: feedback), per repo, on by default. Project-root CLAUDE.md survives compaction. Path-scoped rules load on read/write, summarised away on compaction.

Nothing native: root-cause capture, recurrence counters, counter audit, generic/local split, checklist at write or push time. Those are why this exists. Day the host ships them, I delete it, gladly. I have other papercuts.

Install

claude plugin marketplace add N1XUS/tacit
claude plugin install tacit@tacit
Enter fullscreen mode Exit fullscreen mode

Store files: plain markdown, yours, outside the plugin dir. Disable the plugin, papercuts stay. Like real ones.

tacit list · tacit audit · tacit doctor · tacit config local <path>

Top comments (0)