DeltaDictum current contract
DD v2 keeps project knowledge an agent cannot derive from the code, and delivers each piece at the action it
applies to. The design is the v2 spec (the owner’s design note, kept outside git); this page states what
the code does today. Where this document and src/ disagree, src/ is what DD does and this document is
corrected.
Memories
- A memory is one JSON file,
.dd/memories/<id>.json(schema 8,src/memory/schema.js), committed with the project. Its kind isdecision,trap,failed_approach,factorprocedure; its status iscandidate,active,contested,supersededorarchived. - A memory carries a title, a body,
not_derivable(why a capable agent reading the code would not work it out), triggers, an optional check, evidence and its origin (model_initiatedoruser_explicit).not_derivableis required for a memory to be live, except for memories converted from v1 (review.migrated) until a person supplies it in the review UI. - At most one live (
activeorcontested) memory per topic. A revision is a candidate thatreplacesa live memory; accepting it supersedes that memory in the same commit. - Writes go through one journaled, locked transaction (
src/store/transactions.js); an interrupted one is rolled forward before the next. Validation (schema, secrets, unsafe content, glob safety, sizes) runs before anything is written. .dd/local/is derived and git-ignored: the compiled index, session files, telemetry.deltadictum compilerebuilds the index; session start rebuilds a stale one.
Delivery
- A memory fires when one of its trigger clauses matches an event:
onnames the event kinds (prompt,read,edit,write,run,search), and paths (globs with*,**,?and{a,b}), symbols, commands or prompt keywords narrow it. Matching is rule matching over the compiled index (src/engine/match.cjs), not similarity search. - Delivery happens in the harness’s hooks: at the prompt and before each tool call. Session start injects
nothing into the agent’s context; in Claude Code it shows the person one line (the hook’s
systemMessage, which the model does not see) with the number of live memories and how to open the review UI, except after a compaction. There is no MCP server. A shell command that only reads or searches (cat,head,sed -n,rg,ls…) is a read or search event, in Claude Code as in Codex; one that may change something (a redirection, an in-placesed, any other program) is a run (src/hooks/v2/events.cjs). - Each memory is delivered at most once per epoch of a session, and once per subagent (
src/engine/ledger.cjs); a compaction or/clearstarts a new epoch. The ledger’s claim records the dimension that fired the memory (orcheck), the event kind and the time. A delivery is at most 10,000 characters (delivery_limitin.dd/config.json, 2,000 to 50,000, compiled into the index): what does not fit whole is named and arrives at its next event. Each delivery leaves a ledger line with its event, counts and size, never its content. Stale evidence and disputed memories are marked. - Delivered text is advisory project knowledge. It never outranks the host, the user or the current code. Every delivery and every check report ends with a line saying so to the agent: apply it unless the user explicitly asked for what it rules out, and then do what the user asked and say which memory it departs from. The ledger’s size counts the knowledge, not that line.
Checks
- A memory may carry a declarative check: a regular expression over a changed file (
if_content,unless_content) or over a command (if_command,unless_command). A check never executes code. - Its
violatesandpassesexamples must behave as stated, or the compiler leaves it out. Unsafe patterns are refused, and evaluation runs under a time limit. when: turn(the default for files) runs at Stop over the final state of the files the turn changed;when: editruns at the edit; command checks run at the command. Only a violation the change introduced is reported: one already in the last commit (turn checks) or in the file before the call (edit checks) is not. A violation is reported at most twice per file per epoch, and a check that fired on a file the agent then left unchanged is recorded as a suspected false alarm. A check judges the first 256 KB of a file, and a turn check only the files touched by the event kinds itsonnames. Check lines share the 10,000-character field with the delivery: a line that does not fit waits for the next call, and a check that throws at Stop never costs the turn its capture request.
Capture
- A project opts in with its
.dd/folder: a committed.dd/config.json, or its first memory. Without it, hooks record nothing and ask nothing (src/hooks/v2/hook.cjs).deltadictum initwrites the opt-in (.dd/config.jsonwith the project id,.dd/.gitignore,.dd/memories/) and compiles; the Codex installer writes the config too. A project still holding a v1 store is migrated, not initialised. - Hooks record a footprint per session: paths, redacted commands, search patterns, identifiers and outcomes. Prompt text is not kept, except a short preview of a prompt detected as a user correction. Past 4 MB (about 7,000 tool calls) a session’s footprint takes no more tool calls or corrections, only Stop’s own markers.
- At Stop, DD asks the agent for a memory only when the turn holds evidence (a failure followed by a fix, or a correction), once per evidence set.
- The agent saves knowledge by writing
.dd/inbox/<name>.json(thesaveskill). DD ingests it in the same turn: it validates the proposal, suggests triggers from the session when none are given, refuses generic advice, and answers with the result. A proposal becomes a candidate, or an active memory when auto-accept applies. A proposal on a topic whose candidate is still pending replaces it; the replaced candidate is archived, never deleted. Every ingestion leaves one telemetry line with its outcome and reasons (.dd/local/telemetry/inbox.jsonl), never the content. - Auto-accept (on by default;
.dd/config.json"auto_accept": false, or the review UI’s Settings pane, turns it off) activates a candidate only when DD verified its grounds itself (a repository file it hashed, or a correction it recorded that names the memory and follows something the agent did), every trigger is live, it has no check, and it neither duplicates nor revises a live memory. A reported success never counts. - The agent’s requests to archive or revise a memory wait for a person in the review UI. A proposal or
request may name a
batch; the review UI accepts or rejects a batch whole, each candidate under the same rules as alone (a refused one is reported, the rest proceed), carrying out its archive requests on live targets and leaving revise requests to the person.
Maintenance and review
- Maintenance runs detached from session start at most once a day (
deltadictum maintainby hand): session files are rolled up into.dd/local/telemetry.sqliteand pruned after seven days, unused memories are retired, and the similarity index is refreshed. - An active memory whose paths were touched in at least 20 sessions (
retire.after_sessions) without it firing is archived. A memory a person reviewed is never retired this way, nor one a pending candidate revises. Archiving keeps the file. deltadictum healthreports dead triggers, memories with no opportunity or none used despite it, hubs, triggers only on central paths, suspected false alarms and stale evidence; for each memory, its fires by the dimension that matched; the capture funnel (asks, corrections, proposals stored, activated or rejected, rejections by reason) and the last deliveries.recallandstatusread the store.deltadictum doctorreports whether DD is working in the project (Node version, the opt-in, a readable config, the compiled index and its freshness, session files recorded, the last maintenance run, and the counts that need a person: dead triggers, stale evidence, conflicts, candidates), as problems with a hint each or as notes, and exits 1 on a problem; a project that did not opt in is told so and nothing is created for it. Thereviewskill shows its summary before opening the UI.deltadictum uiserves the review UI for one project on a loopback port, until interrupted: queues (among them the memories DD accepted on its own, until a person marks each as reviewed), a memory with its breadth, evidence, use and similar memories, a dry run of its triggers over recorded sessions, and every edit and status change, each validated as ingestion validates and followed by a recompile. It answers only its own Host, only a browser holding its key, and takes changes only as JSON.
Harnesses
- Claude Code is supported in full. Codex receives the same delivery through its hooks: its shell tool is
classified as Claude Code’s Bash is,
apply_patchheaders name the edited files, and Stop answers with a block. OpenCode receives memories appended to the tool result, leaving the system prompt and its cache alone. Grok Build runs the same hooks with Claude Code’s payload shape, not yet checked against a recorded session. - A hook that fails ends silently: DD never blocks the host.
Migration and import
deltadictum migrate [--dry-run]converts a v1 store (.dd/atoms/and the rest) into v2 memories, keeping their ids, and queues every converted memory for a person to supplynot_derivable.deltadictum import [--from DIR] [--dry-run]turns the project’s.claude/rules/*.md(pathsin the frontmatter) and.cursor/rules/*.mdc(globs) into active memories (src/memory/import.js): oneprocedureper file underrules/<claude|cursor>/<file>, firing on reads, edits and writes of the paths that match a repository file, the file itself as hashed evidence and as a revisit condition, accepted by the person who ran the import. A bare pattern (*.ts) applies across folders (**/*.ts). A rule with no paths oralwaysApply: true, one whose paths match nothing, and one whose topic a memory from elsewhere holds are skipped and reported. A body over 1,500 characters is cut at a paragraph and points at its file (review.issues: imported_truncated). A second import updates the memories whose file changed and leaves the rest alone.
DD is a plugin for coding-agent harnesses, not a service.
architecture/plugin-constraints.md states the limits that follow.