Harness
Concepts

What the loop is allowed to remember, who approved it, and why the attribution matters more than the storage.

View raw Markdown · llms.txt · For agents

Memory here is not a vector database and not a chat history. It is a small set of approved project decisions that a human (or, within declared bounds, the retro) promoted, recalled into a prompt when they are relevant, and attributed so they can be revoked.

memory:
  enabled: true
  backend: file
  maxRecall: 5
  preferOverDocBridge: true
  recurrence: { minSightings: 2, maxPerRun: 3 }
  autoPromote: { enabled: false }

From a lesson to a memory

  1. loop retro reads the event log and proposes learnings — the same lesson seen again and again.
  2. A learning proposed recurrence.minSightings times stops being an anecdote and is surfaced as ready to promote, with the exact command, so the human act is one keystroke instead of an analysis.
  3. promoteLearnings writes it as a memory record with an actor: human, or loop-auto when memory.autoPromote.enabled is on.

The actor is the part that matters. A record nobody can tell apart from a human decision is a record nobody can safely revoke — which is why automatic promotion is never recorded as human (ADR-0019, amendment of 2026-09-19), and why loop learning promoted and loop learning reject --ids … --by human both exist.

Recall

Before a contract is frozen or a brief is written, planMemoryContext selects what to include and reports what it cost and saved: hits, the Doc Bridge references before and after, the approximate characters saved, and a digest of the selection. That digest is frozen with the contract — memory changing is one of the three things that invalidate a cached contract, because a contract frozen under different decisions is a different contract.

preferOverDocBridge decides the tie: an approved decision beats re-deriving the same fact from documentation, which is both cheaper and more likely to be current.

The honest limitation

The file backend's recall is a substring scan with a fixed relevance over the deserialised records. So maxRecall: 5 means "the first five records containing the token", not "the five most relevant". For a few dozen decisions that is fine and the ordering barely matters; for a few thousand it would not be, and a ranking that does not exist should not be implied.

An SQLite/FTS5 backend — same AgentMemoryAdapter interface, same record format, same attestation, no new dependency, since node:sqlite ships with the Node 22 the package already requires — was designed and deliberately left out of this release.

On this page