Skip to main content

epitaph

Every agent memory accumulates successes and preferences. tombstone accumulates refusals: it records rejected patches, rolled-back approaches, and abandoned paths as structured tombstones inside your repo — so the next agent checks before walking the same dead end. "This approach was buried here, in this module, on August 12."

Status: v0.1 (deterministic core). Distribution name is epitaph (decided 2026-09-05; the working name tombstone collided with Android crash dumps / Cassandra). Import module and CLI are epitaph too; records are still "tombstones" living in .tombstones/.

The problem: failure knowledge evaporates three times

  1. git history — Revert "Add Redis lock" records what was reverted but destroys why, under what conditions, and what the alternative was.
  2. Review — PR closes and objection threads bury rejection reasons in unsearchable form.
  3. Sessions — the moment an agent declares "I'll try a different approach" lives only in the transcript, and dies with the session.

The result is the worst item on any inter-session yield audit: re-discovering the same failure. Human teams solved this with brains and folklore ("we tried that in August, it went badly"). In the agent era, that brain is not stored anywhere.

tombstone is the repo-scoped attempt ledger: one JSON file per rejected attempt, committed to the repo, queryable by machine before the retry happens.

Quickstart

# install (from a checkout; PyPI release planned)
pip install .

cd path/to/your/repo

epitaph init
epitaph add \
  --attempt "Redis-based distributed lock to serialize session writes" \
  --reason "Race window was not closed; retry storm under load" \
  --scope src/session/lock.py src/session/manager.py \
  --evidence "PR #412" "revert 9f2e1a" \
  --retry-when "once a fencing token sits in front of the lock"

epitaph list
epitaph check "redis lock for sessions"   # what an agent runs BEFORE retrying
epitaph approve ts-20260812-a3f2          # one human line: candidate -> approved

Automatic detection

detect scans git log for revert commits (subject starting with Revert " and/or a body containing This reverts commit <sha>) and drafts candidate-confidence tombstones linking the evidence:

epitaph detect
# created ts-20260902-1b7e (candidate)
# 1 revert commit(s) scanned, 1 created, 0 already recorded

Or install a post-commit hook that runs detect automatically (it can never fail your commit):

epitaph install-hook

detect is incremental and idempotent: it remembers the last scanned commit in .tombstones/.cursor (so the post-commit hook only pays for new history), and the tombstone id is derived from the revert sha, so even a forced full rescan (tombstone detect --full) never duplicates. If a history rewrite strands the cursor, detect falls back to a full scan automatically.

Record schema (one JSON file per tombstone in .tombstones/)

{
  "id": "ts-20260812-a3f2",
  "attempt": "Redis-based distributed lock to serialize session writes",
  "scope": ["src/session/lock.py", "src/session/manager.py"],
  "rejected_at": "2026-08-12",
  "rejected_by": "human-review",
  "reason": "Race window was not actually closed; 3 tests went flaky in CI.",
  "evidence": ["PR #412", "revert 9f2e1a"],
  "retry_when": "Revisit once a fencing token sits in front of the lock.",
  "status": "active",
  "confidence": "approved"
}
  • id — ts-YYYYMMDD-<4 hex>
  • rejected_by — human-review | ci | agent-gaveup
  • status — active | stale (target code gone) | overturned (a retry succeeded — kept on purpose; honest failure data includes its own refutations)
  • confidence — approved (human-confirmed) | candidate (not yet approved, shown with lower confidence on query)

One file per tombstone minimizes git merge conflicts and makes partial adoption easy. .tombstones/ is meant to be committed — team sharing is the core value. Working solo? Add it to your personal gitignore.

See examples/ for full records.

MCP server (Claude Code and any MCP client)

Zero-dependency stdio MCP server (JSON-RPC 2.0, no mcp package needed):

python -m epitaph.mcp          # repo resolved from cwd, walking up
python -m epitaph.mcp --repo /path/to/repo

Two tools:

  • check_nogo(attempt?, files?) — match an intended approach / target files against the ledger.
  • recent_tombstones(scope?, limit?) — browse the ledger, newest first.

Claude Code config (.mcp.json in the project root, or ~/.claude.json):

{
  "mcpServers": {
    "epitaph": {
      "command": "python3",
      "args": ["-m", "epitaph.mcp"],
      "env": { "EPITAPH_REPO": "/absolute/path/to/your/repo" }
    }
  }
}

Or with the CLI: claude mcp add epitaph -- python3 -m epitaph.mcp (run from the repo root; the server resolves the repo from its working directory if EPITAPH_REPO is unset).

The server is a plain stdio JSON-RPC process — any MCP-capable client works. Cursor (.cursor/mcp.json):

{
  "mcpServers": {
    "epitaph": {
      "command": "python3",
      "args": ["-m", "epitaph.mcp"],
      "env": { "EPITAPH_REPO": "/absolute/path/to/your/repo" }
    }
  }
}

Codex (~/.codex/config.toml):

[mcp_servers.epitaph]
command = "python3"
args = ["-m", "epitaph.mcp"]
env = { EPITAPH_REPO = "/absolute/path/to/your/repo" }

Clients without MCP support can use the CLI instead — tombstone check "..." --file path produces the same match report through the same renderer.

Recommended one-line rule for AGENTS.md / CLAUDE.md — inject it with tombstone snippets (creates AGENTS.md if absent, appends to CLAUDE.md only when it already exists, idempotent):

Before implementing an approach, call check_nogo with your planned approach and target
files. On a match, read `reason` and `retry_when`: either address `retry_when` or pick
a different path. Tombstones are records of past rejections, not bans.

CLI reference

Command Purpose
tombstone init Create .tombstones/ in the target repo (--snippets also injects the rule below)
tombstone snippets Inject the recommended check_nogo rule into AGENTS.md (and CLAUDE.md if present) — idempotent
tombstone add --attempt T --reason R [--scope P...] [--evidence R...] [--rejected-by WHO] [--retry-when W] [--date YYYY-MM-DD] [--confidence C] [--status S] Record a rejection (defaults to candidate)
tombstone approve <id> Promote a tombstone to approved (one human line)
tombstone overturn <id> --reason R A retry succeeded — keep the refutation on record
tombstone list [--status S] [--scope P] List tombstones, newest first
tombstone show <id> Print one tombstone in full
tombstone check [TEXT] [--file P]... Query by attempt text and/or files before retrying
tombstone detect [--full] Scan git history for reverts, draft candidate tombstones (incremental via .cursor)
tombstone install-hook Install a post-commit hook that runs detect

Global: --repo PATH (default: cwd, walking up), --version.

check uses deterministic normalized substring and token-overlap matching (0.5 containment for multi-token queries, exact token for single tokens) against attempt + reason — scope paths are full of common words and only match via --file — and also matches queried files against scope anchors in both directions. Every hit prints its confidence and why it matched; on a repo without a ledger it answers softly (nothing recorded here yet) instead of erroring, matching the MCP behavior agents see.

Workflow: detect -> draft -> approve -> query

Stage Owner Notes
Detect deterministic rules (no LLM) git revert commits today; never-merged PR closes, session give-up transitions, and CI-failure branch abandonment are on the roadmap
Draft optional LLM (v0.2) v0.1 works fully without drafting
Approve one human line tombstone approve ts-.... An agent may never judge on its own — a tombstone is testimony, not a verdict
Query MCP tools + CLI check_nogo / recent_tombstones / tombstone check

Principles

  • Fully local by default — tombstones live only in your repo and on your machine. Zero network calls at runtime.
  • Tombstones do not assert — they record "this was rejected on this date for this reason"; they never claim "this is impossible". retry_when (the refutation condition) is expected on every record.
  • Candidates allowed — unapproved candidate records are kept and shown with lower confidence; approval friction is one line.
  • Deterministic core (v0.1) — detection and matching are plain rules; LLM drafting is a v0.2 option, never a dependency.
  • Human approval required — only tombstone approve (a human action) reaches approved confidence.

Differentiation

Adjacent tool How tombstone differs
deadends.dev Global, error-signature-centric ("don't sudo pip when you hit CUDA OOM") — not repo-scoped, community-curated, manually reported. tombstone: repo-scoped, attempt-level (the approach, not the error message), auto-detected, human-approved, expiring. Not competitors — two layers: global error signatures live there, project-context rejections live here. v1.x plans canon-ID cross-links so the layers interoperate.
Mem0 / Letta / CLAUDE.md memory Positive memory of successes and preferences. Negative memory needs its own data structure and its own query pattern (pre-flight lookup in the planning stage, not mid-chat recall).
ADR (Architecture Decision Record) Manual documents of human design decisions. tombstone records rejected attempts, with automatic detection and machine querying built in.
.cursorrules negative rules Context-free commands ("don't do X"). A tombstone is a record with evidence, conditions, and an expiry path.
yield-audit M9 (repeated inter-session knowledge cost) Measurement (the size of the repeated tax) ↔ tombstone (removal of its single most expensive line item). They sell each other.

Privacy

Tombstone records can contain sensitive reasons. Everything stays local: no telemetry, no network calls, no cloud sync. The records are plain JSON files inside your repo — audit them with git grep before sharing, the same way you audit any other committed file.

Limitations (v0.1)

  • Revert detection is heuristic (subject/body patterns); it drafts candidates, humans confirm.
  • Matching is lexical — synonyms and paraphrases may need the v0.2 embedding option.
  • No stale audit yet (v0.3: symbol-graph check that the anchored code still exists).
  • detect defaults rejected_by to human-review because git alone cannot tell who drove the revert; correct it during review.

License

Apache-2.0. See LICENSE.

Metadata

Release files for epitaph 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for epitaph 0.1.0
File Size Uploaded
epitaph-0.1.0.tar.gz 36.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for epitaph 0.1.0
File Interpreter ABI Platform
epitaph-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 64.6 kB

Release files / epitaph-0.1.0.tar.gz

Download URL epitaph-0.1.0.tar.gz
Size 36.8 kB
Tags Source
SHA-256 checksum
How to use checksums
b676e54d1950c1955c0be7082d5e6492b3320488121499b5e986c5e695779e67
BLAKE2b-256 checksum
How to use checksums
5d100f1b05966c51851e9cdb5076e95aa4233f9933949bb4d3a249bcd6166262
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 5, 2026.

Transparency log

Release files / epitaph-0.1.0-py3-none-any.whl

Download URL epitaph-0.1.0-py3-none-any.whl
Size 27.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
eed5a4157ce9f05869d57d3a0ad8a24f52590bae8d16b06a4f66169a310f2e88
BLAKE2b-256 checksum
How to use checksums
a9160c18c133c1252b8928b284b66dd77356cf9c61a00cfe6ad5886d9b536368
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 5, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.0

2 release files

0.2.0

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page