[

PROJECT MEMORY

]

Project memory that keeps the thread. And a gate that decides what it may keep.

ShelraCode keeps what it learns about a project in plain files under .shelra/memory/. Before each request it loads the entries that matter; after real work, one bounded reflection proposes new ones, and a deterministic gate decides. The gate refuses secrets and prompt-injection phrasing, and never lets an inference replace a rule you stated.

What it keeps

Durable facts about the project: how it builds and tests, decisions and the reasons behind them, procedures that worked, failures and their fixes, and the rules you state.

  • Rules you state (“always …”, “never …”, “prefer …”) are saved as you said them, without a model call, and marked as yours.
  • Facts learned from work come from a reflection after a turn that changed files, worked through a failure or investigated at length. What a turn that ended unverified teaches is kept too, marked unverified, and never outranks a confirmed fact.
  • Procedures that proved themselves in two or more turns become project skills in .agents/skills/<name>/SKILL.md, with their origin written in the file.

Entries are Markdown files you can read and edit, listed in an index (MEMORY.md), and history.jsonl records every change as JSON Lines. Memory that applies to all your projects lives in ~/.shelra/memory and is loaded beside the project's.

What it refuses to keep

A memory fails at the write, not the read: once something wrong is stored, every later session trusts it. So every write, automatic or requested by the model, passes one gate: a plain function that gives a reason for each decision.

  • Secrets. Text shaped like a credential is rejected.
  • Prompt injection. Phrasing that tries to steer the agent, such as “ignore previous instructions”, “bypass the checks” or “pretend to be…”, is rejected.
  • Replacing what you said. Whatever the model writes is recorded as an inference, and an inference never replaces a rule you stated.
  • Repeats. An exact repeat is skipped, and a close rewrite of an entry either updates it or is dropped: it never becomes a second copy.
  • Sprawl. Entries that are too short or too long are rejected, each kind of entry is capped, and the index stops at 200 lines.

Each entry records where it came from (you, a command the host saw run, or the model's inference) and how sure it is, and retrieval weighs it accordingly.

How it finds what matters

Before every request, and before every sub-agent's brief, retrieval ranks the entries against the task: the words and file paths they share, weighted by trust, by recency, and by whether the files an entry depends on changed since it was last confirmed. Up to four entries are loaded in full, within 3,000 characters; the next twelve are listed by title and the rest counted, and memory_list shows them all.

Retrieval is lexical: no embeddings, no vector index to keep in sync, nothing sent to another service. The trade-off is in the limits below.

How it learns

After a turn that did real work, a bounded reflection (30 seconds and 1,500 output tokens per attempt, at most three attempts) reads a digest of the turn: the request, the files changed, the last commands and how they ended, the final report and the entries that already exist. It may propose up to five durable facts; the gate decides which survive.

You can also manage it directly: the agent has memory_list, memory_read, memory_write and memory_delete, and every write goes through the same gate.

The evidence

A benchmark suite asks the one question that matters: does the memory change what a later session achieves? In phase A the agent implements a function whose tests depend on an undocumented code-generation step. Phase B changes the schema and must regenerate, twice, from phase A's finished workspace: once with the memory phase A wrote, once with it wiped. The check reads a hash of the schema inside the generated module, so only a real regeneration passes.

shelra-memory · qwen/qwen3-coder-30b-a3b-instruct
RunB · with memoryB · memory wipedA · learnDate
#12✗ failed✗ failed✓ passed2026-09-17
#13✗ failed✓ passed✓ passed2026-09-17
#14✓ passed✗ failed✓ passed2026-09-17
#15✗ failed✗ failed✓ passed2026-09-17
#16✗ failed✗ failed✓ passed2026-09-17
#17✓ passed✗ failed✓ passed2026-09-17
#18✗ failed✗ failed✓ passed2026-09-17
#19✓ passed✗ failed✓ passed2026-09-17
#20✓ passed✗ failed✓ passed2026-09-17
Passed4 of 91 of 99 of 9

Completed runs on one model, 9 of them: a small sample. Not in the table: #27 (interrupted) and #29 (deepseek/deepseek-v4-flash-0731:free, failed). The harness log counts runs #12 and #13 as invalid for this question (a reflection that returned nothing; a workspace copy that skipped .shelra) and traces the failures of #15 and #16 to tool and harness defects, all since fixed. Runs from before 2026-09-22 record commits from before the repository's history was rewritten, so they cannot be replayed from public commits. Every run is in the history.

Limits

  • Retrieval is lexical: a request that shares no words with an entry will not load its body, although its title is still listed.
  • Reflection is only as good as the model running the turn; the gate limits the damage of a poor extraction but cannot make a good one.
  • Promoted skills are written into your repository under .agents/skills: review them like any other committed file.
  • Entries do not link to one another; what an entry replaces and the files it depends on are its only references.
  • Deleting is not gated: the agent's memory_delete can remove any entry, yours included; history.jsonl keeps the record.

The full design, with the research behind each decision: docs/design/shelra-memory-engine.md.

Your backlog won't clear itself.

Give ShelraCode the task. Get it back verified.

The ShelraCode start screen: the project it opened and what it already knows, 3 memories, 1 skill and AGENTS.md.