Skip to main content

draille

tests PyPI

Plain-markdown durable memory for AI agents. Any runtime, local, git-versioned, hand-editable, zero lock-in.

Early stage — actively developed, the API and file formats may still move. Version numbers track features, not maturity.

Why « draille »

A draille is a transhumance trail in the mountains of southern France — a path not built, but worn into the land by herds walking it season after season. Nobody designed it; repeated passage made it, and it remembers the way for whoever comes next.

That is exactly what agent memory should be: not a database bolted on the side, but a trace left by work itself — records worn in by sessions, ranked by how often following them actually led somewhere (outcomes), readable by the next traveler (human or agent) with no tooling at all.

What it is

Stdlib-only Python tools, no dependencies:

Command Role
draille init scaffold memory/ (HANDOVER template, journal) and print the agent bootstrap block
draille record write a durable record (markdown + frontmatter, stable content-hash id); --supersedes <id> marks a prior record obsolete
draille prime rank all records (classification weight + outcome tally) into a budgeted digest for session start
draille outcome append "this record demonstrably helped/failed" to an append-only log, keyed by immutable id
draille search ranked full-text search over records (pure scan, no index) — or delegate to your own engine via DRAILLE_SEARCH_CMD (BYO backends)
draille expire retire a record: move it from memory/records to memory/expired — the only retrieval-blocking retraction path (never deletes)
draille handover show/set the CORE block of memory/HANDOVER.md (atomic, Letta-style core memory)
draille doctor health-check the store: corrupt records, orphan outcomes, dangling supersedes, unsafe scope homes, duplicate ids (exit 1 on any issue — CI-friendly)
draille status fast persistence + health check — is memory uncommitted (dirty) or corrupt? exit 1 if so (for hooks/gates: `draille status
draille migrate import legacy JSONL records into markdown

Superseding — stale memory that stops misleading. Outdated facts are the named failure of agent memory: "we use Postgres" lingers and misleads long after the project moved to SQLite. draille record … --supersedes <old-id> marks the old record obsolete; prime and search hide it by default (still on disk, still in git history — search --all brings it back). One frontmatter line, no graph, no TTL daemon: the markdown-shaped answer to temporal decay.

Expiring — the actual retraction path. Superseding marks a record stale but still findable (search --all); sometimes a record needs to stop being found at all — wrong from the start, or genuinely dead. draille expire <id> moves the file from memory/records/ to a sibling memory/expired/, never deletes it. search and prime only glob memory/records/*.md, so an expired record stops surfacing by construction — no filter to keep in sync in either scanner. Setting status: archived by hand in the frontmatter is not a retraction mechanism: no scanner reads or filters on it, so an "archived" record still ranks and still surfaces. expire is the one path that structurally works.

Task guard — pending tasks don't silently erode. LLM rewrites of the CORE block are lossy: a task nobody closed can just vanish on the next handover set. Tag it - [t] <task> and set stamps a - [t-xxxx] id on write; close it explicitly with closed: <reason> on the line. If a pending id disappears anyway, the write still happens (soft, by design — a blocking guard kills a headless agent) but it's logged to task-guard.jsonl and warned on stderr, and draille status surfaces the open-drop count.

Each is also a standalone script (draille/<name>.py) you can copy anywhere — no install needed.

Storage is just files in your repo:

memory/
  records/*.md        # one record per file — human-editable, git-diffable
  outcomes.jsonl      # append-only; git is the WORM/recovery layer

draille vs. Claude Code's native memory

Claude Code now ships its own memory: a per-project auto memory directory indexed by MEMORY.md, but it's per-user and per-tool — it lives outside the repo and only Claude Code reads it. draille is project-owned: records live in the repo, travel with git clone, and are legible to any runtime (Claude Code, Codex, Cursor, or a human with cat). The two compose fine — native memory for personal prefs and scratch state, draille for what the project has learned.

Non-goals

  • No per-turn auto-extraction. Frameworks like mem0/Letta watch every turn and write memory automatically. draille doesn't, on purpose: automatic extraction produces noisy memory, and the whole point here is a curated, human-editable, git-diffable trail. Capture is a deliberate act (record, or the session-end ritual), not a background process. If you want the agent to hold live working state mid-session, that's what handover set is for.
  • No embedded vector database. Semantic recall is a DRAILLE_SEARCH_CMD backend you bring, not infrastructure draille ships (BYO backends).
  • No hosted service / multi-user layer. draille is a local, per-repo store. For memory-as-a-service at scale, that's a different tool.

Install

pip install draille        # or: pipx install draille
# or: git clone and run the scripts directly — stdlib only, nothing to install

Quickstart

draille init              # scaffold memory/ + print the agent bootstrap block
draille record decision foundational "Use Postgres" --body "Because RLS."
draille prime             # ranked digest — paste/inject at session start
draille outcome <id> success --note "constrained the schema choice"
draille search postgres   # ranked hits across all records

Root resolution: $MEMORY_ROOT env var, else the git root of the cwd. Mono-project by default (memory/ at the repo root). Multi-scope routing via an optional memory/scopes.json ({"scope": "relative/home", "central": "."}) — homes must stay inside the root (absolute paths and .. are rejected). --dir is an explicit escape hatch that bypasses root and scope routing entirely; don't pass it untrusted input.

Agent bootstrap

Drop this in your AGENTS.md / CLAUDE.md (see AGENTS.md):

  1. Session start: run draille prime, read memory/HANDOVER.md.
  2. Session end, triage three tiers:
    • HOT → rewrite the CORE block of memory/HANDOVER.md (≤15 lines, merge — never stack),
    • DURABLEdraille record <type> <classification> "<title>" --body "…",
    • JOURNAL → append memory/journal/<YYYY-MM-DD>.md.
  3. Commit session-end: <date>. Never auto-push.

The full runtime-agnostic ritual — with the judgment criteria per tier and the persistence check (draille status) — lives in PROTOCOL.md; the block above is its condensed pointer.

The system is the ritual, not the tooling — the scripts just keep the trail walkable.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

draille-1.7.0.tar.gz (31.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

draille-1.7.0-py3-none-any.whl (37.4 kB view details)

Uploaded Python 3

File details

Details for the file draille-1.7.0.tar.gz.

File metadata

  • Download URL: draille-1.7.0.tar.gz
  • Upload date:
  • Size: 31.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for draille-1.7.0.tar.gz
Algorithm Hash digest
SHA256 0b7042577f5a448ee45a9787b3aa9830dd87813ff696224e61e48d24d149db10
MD5 f31a9de57d874ef34ad4ebecfe8e2711
BLAKE2b-256 8b037326a30878635e45cd25573e19793e49db1cc2412d96c88ce6083953033c

See more details on using hashes here.

File details

Details for the file draille-1.7.0-py3-none-any.whl.

File metadata

  • Download URL: draille-1.7.0-py3-none-any.whl
  • Upload date:
  • Size: 37.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for draille-1.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4cba5d0fa025aa65629135162b50e8a619f2181d039979de607093b957ee4fdc
MD5 766fa31e6b676d09aa116c90f4eefad2
BLAKE2b-256 8a23bf59d501f87169fad950ea86c3e01d200529a94caaf628959d13b6c6c7db

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

1.7.0 This release

2 files

1.6.1

2 files

1.5.0

2 files

1.4.0

2 files

1.3.0

2 files

1.2.0

2 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