engmem v0.1 · полный разбор

Анатомия engmem

Память для ИИ-агента: решения, грабли и договорённости не теряются между задачами. И, в отличие от других таких памятей, эта умеет показать, помогает ли она.

Страница выведена из спецификации: при расхождении права ENGMEM-SPEC.md и тесты.

Часть 1 · Что это такое

Память, которая умеет себя измерить

Engmem — память для ИИ-агента: то, что выяснено в одной задаче, доступно ему в следующей. Хранилище, шаблоны записи, поиск и установщик — это она и есть.

Необычно в ней другое: она измеряет собственную пользу. Пре-регистрация, журнал переиспользования с дословными цитатами и телеметрия — надстройка, которая отвечает на вопрос, который памяти обычно не задают: меняет ли поднятый из прошлого документ решение в новой задаче. Ответ не должен поддаваться подгонке задним числом — поэтому измерение встроено в сам порядок работы, а не приделано к нему отчётом в конце.

Продукт стоит на трёх опорах, и порядок здесь содержательный:

  1. Единица памяти — инженерная история, и хранилище лежит вне всех репозиториев. Одна история тянется через несколько репозиториев и несколько сессий: решение принято в одном сервисе, грабли обнаружены в соседнем, договорённость — в третьем. Память, привязанная к репозиторию, режет такую историю по его границе; память, копящая отдельные факты, теряет обстоятельства, в которых факт был верен. Из этого следует всё остальное.
  2. Причинное измерение переиспользования — по документу, а не по ощущению. Как это устроено — четыре такта ниже.
  3. Ограниченный канал выдачи, который можно записать. У каждого вызова известная цена и оставленный след. В этом назначение поиска, а не в полноте выдачи.

Цикл целиком: четыре такта

Порядок обязателен для свидетельства, а не для работы. Пропущенный или переставленный такт не ломает ни задачу, ни разбор: разбор пройдёт по тому, что записано. Он лишь перестаёт быть доказательством — наивный план, написанный после чтения документов, уже ничего не доказывает.

  1. Пре-регистрация. Отдельный под-агент, которому дана только формулировка задачи, пишет наивный план до всякого поиска, и план уходит в черновик документа. Порядок здесь и есть измерение: план, написанный после чтения документов, свидетельством уже не будет.
  2. Работа. Поиск отдаёт адреса разделов, агент читает найденное, задача делается обычным образом. В середине engmem не участвует вовсе.
  3. Строка журнала с дословной цитатой. На каждый повлиявший документ — строка: что процитировано, что в итоге изменилось, какого рода влияние: reuse — пошли за документом, anti-reuse — сознательно против него, harmful — документ увёл не туда. Строка без цитаты недействительна; если не пригодилось ничего — пишется ровно Prior docs used: none.
  4. Разбор (Gate 1). Строки со всего хранилища сводятся в одну таблицу и читаются по правилу, записанному заранее.

Что именно записано заранее — в ENGMEM-SPEC.md §11. Основная контрольная точка: хотя бы одно дальнее событие переиспользования на всём наборе из 4–5 межрепозиторных историй — одно на набор, а не на каждую историю. «Дальнее» определено механически, а не на глаз: цитируемый документ считается ближним, если делит с цитирующим репозиторий или тег либо связан с ним ребром related; иначе — дальним. Ближний автор, скорее всего, вспомнил бы и сам — кредит за него слою поиска не полагается.

Механическую часть каждой строки решает один общий модуль, src/engmem/gate1.py: целостность цитаты, дистанцию и устарелость; классификацию он не решает, а читает — её ставит автор. tools/verify_citations.py и tools/gate1_report.py читают один и тот же вердикт, а не проверяют независимо — расходятся они только кодом выхода: verify_citations.py падает на непроверяемой цитате, gate1_report.py не падает никогда и сводит все четыре оси в таблицу для человека. Пустая колонка — единственное, чего машина решить не может: изменила ли цитата решение. В счёт идут только строки, классифицированные reuse: anti-reuse и harmful печатаются в таблице, но из подсчёта исключаются — как и строка без цитаты, и строка из черновика. Туда же уходят строки из историй про сам engmem — в выдаче это ось dogfooding: разработка инструмента не может служить доказательством его пользы.

| story | cited | quote | integrity | classification | distance | staleness | changed a decision? |
|---|---|---|---|---|---|---|---|
| 2000002-widget-cache | 1000001-response-cache | rejected the separate table because the response already carries freshness data | verified | reuse | distant | cited_active |  |

rows: 1
citation integrity: 1 verified, 0 no quote, 0 cited doc not in store, 0 quote not found
classification: 1 reuse, 0 anti-reuse, 0 harmful, 0 classification missing, 0 classification not recognized
rows excluded from the count: 0
excluded from the count, by axis (a row may match more than one -- see the total above for the true count, not the sum of these): 0 dogfooding (story about this repository), 0 citing document not active (draft/superseded)
1 valid (the last column above is theirs to fill), of which 1 distant, 0 adjacent, 0 undecidable
of the 1 distant, 0 cite a superseded document (flagged, not excluded -- see the staleness column)
ENGMEM-SPEC.md section 11's primary endpoint counts the distant figure above.
documents reporting no reuse: 1
The last column is yours where the tool left it blank.

[... continues with the audit coverage block -- see contracts/gate1.md ...]

Полный прогон печатает под этим ещё один блок — контроль полноты выборки, а не второй гейт. Инструмент сам называет две его оговорки. --session нарочно не проверяется, поэтому строка про «осиротевшие» записи считает и опечатки, и загрязнение из чужого хранилища, а не только настоящие пробелы. А telemetry.jsonl ведётся только дозаписью, без ротации, так что строки до Gate 1 и тестовые неотличимы от экспериментальных в пределах любого окна. Остальное — в docs/design/contracts/gate1.md, раздел «The audit coverage block».

Подделка ловится там же: если поправить цитату так, что она разойдётся с источником, проверка называет файл, строку и саму цитату и выходит с кодом 1.

2000002-widget-cache.md:23: quote not found in 1000001-response-cache — "rejected the separate table because it would have been slower"
verify_citations: 1 unverifiable citation(s), 0 citing a superseded document, 0 with a conflicting Reuse Log

Толкование результата тоже зафиксировано до данных. Если основная точка не взята, а вторичные взяты, вывод звучит так: хранилище как сжатый контекст себя оправдало, измерительная надстройка — нет, и надстройку сворачивают. Промах — это результат, а не повод смягчить критерий и попробовать ещё раз.

Чем это не является. Не векторная база: ни эмбеддингов, ни индексов, ни ранжирования по смыслу. Не ещё один memory bank при репозитории: цикл «захватить — сжать — подставить» здесь общее место, а не продукт. Не поисковик, соревнующийся с grep: свободный grep агента по хранилищу нельзя ни ограничить по объёму, ни занести в телеметрию — измеримым канал делает именно это, а не качество выдачи.

Часть 2 · Проблема

Агент не помнит вчерашний день

Вы решаете задачу вместе с ИИ-агентом. Обсуждаете варианты, отбрасываете тупиковые, находите подводный камень, о который спотыкались бы ещё неделю. Задача закрыта, ветка влита.

Через два месяца вы возвращаетесь в ту же подсистему. Агент начинает с нуля. Он не знает, что вариант с отдельной таблицей вы уже рассматривали и отвергли — и предлагает его снова. Не знает, что повторный вызов внутри одного запроса удваивает задержку — и пишет код, который на это наступает. Не знает про договорённость с соседней командой, которой нет ни в одном тикете.

Знание было. Оно исчезло в трёх местах: в закрытом окне чата, в вашей голове (где стёрлось) и в комментариях к пул-реквесту, которые никто не перечитывает.

Важно, чего проблема не касается. Речь не про то, «как устроен код» — это видно из самого кода, и дублировать его в заметках вредно: копия устареет первой. Теряется другое: отвергнутые варианты и причины отказа, грабли, бизнес-ограничения, устные договорённости. Того, чего в коде нет и не будет.

Почему обычные способы не срабатывают

Корень у всех четырёх один — цена записи платится сейчас, а польза приходит потом и не гарантирована. Человек в этой сделке всегда проигрывает будущему себе.

СпособПочему не работает
Писать в Confluence / NotionДорого писать, ещё дороже искать: страница не всплывает сама в момент, когда нужна.
Комментарии в коде и в PRОтвечают «что делает эта строка», но не «почему мы отказались от другого решения». К моменту нужды PR давно закрыт.
ADR (записи об архитектурных решениях)Работают, но пишутся вручную и только для крупных решений. Мелкие грабли — а именно они съедают часы — туда не попадают.
Память самого агентаЖивёт в рамках одной сессии или одного продукта, не переносится между инструментами и не поддаётся проверке: нельзя открыть файл и прочитать, что там записано.

Часть 3 · Решение

Пишет агент, ищет машина, решает человек

Engmem превращает каждую закрытую задачу в один markdown-файл и автоматически поднимает нужные файлы в начале следующей.

Сделка переворачивается: писать почти бесплатно, потому что документ составляет агент из диффа и переписки — от вас нужен один ответ «y». А польза приходит сама: агенту запрещено предлагать план, пока он не поискал в прошлом.

Три части, которые делают это возможным:

  • Хранилище — папка markdown-файлов под git. Никакой базы, сервера и облака. Файл можно открыть, прочитать, поправить руками, посмотреть историю через git log.
  • Поиск — команда engmem search. Детерминированный, без ML: одинаковый запрос всегда даёт одинаковый ответ, объём выдачи ограничен сверху, и каждый вызов оставляет строку телеметрии с точной ценой в байтах. Перечитывает шапки всех документов при каждом вызове.
  • Три команды-шаблона для агента: /engmem в начале задачи, /engmem.save в конце, /engmem.save.quick — короткий документ без раунда проверки, когда нет сил её делать. Это markdown-инструкции, а не код — поэтому работают в любом агенте.
/engmem работа /engmem.save store + 1 поднимает прошлое фиксирует новое каждый документ делает следующий старт умнее
Метрика успеха проекта — повторно использованные знания, а не количество написанных документов.

Маховик выше — это цикл для одного документа. Если отступить на шаг, хранилище — граф, который становится всё связнее. В нём два вида связей: те, что вписал автор, и те, что не писал никто. Вторые канал выдачи проводит сам — по сущностям, совпавшим у двух документов. Правило разбора считает такие пары дальними: ни общего репозитория, ни общего тега, ни ребра related — и только на них слой поиска вправе претендовать.

Часть 4 · Путь одной задачи

Что происходит от постановки до сохранения

Роль в цикле: здесь память наполняется — документ попадает в хранилище, и вместе с ним у измерения появляется предмет. Шаги 01 и 07 и есть измерительные такты (пре-регистрация и строка журнала с цитатой); всё между ними — обычная работа.

Печатать вам не придётся ничего: агент проходит путь от наивного плана до готового документа, а вы подтверждаете шапку и отвечаете, отменяет ли эта работа прошлое решение. Полный save просит ещё сверить цитаты — цену этого шага страница называет прямо на 07. Ниже — по шагам: что делает инструмент, что физически попадает в контекст модели и что остаётся после. Задача: «Добавить кеширование ответов для нового эндпоинта»; похожая работа велась три месяца назад, но вы этого уже не помните. Значения сняты с прогона на тестовом хранилище.

00 01 02 03 04 05 06 07 08 09 docs: 7 → 8 самый большой шаг: в контекст входят разделы найденных документов
Что накопилось у задачи к каждому шагу — с нуля на 00 до сохранённого документа на 08. Ровные участки на 05–06 — шаги, где engmem не добавляет в контекст ничего нового; 09 держит новый уровень, но сам ничего не прибавляет.

Часть 5 · Техническая механика

Три пути через код

Роль в цикле: это код самой памяти — через который запрос превращается в ограниченную выдачу и в строку телеметрии, и которым цикл ставится на машину. Ниже — что вызывается, в каком порядке и на каких развилках.

Входа три, ядро одно — расходится лишь то, кто на другом конце читает stdout. Ниже все три функция за функцией, в порядке реального вызова: engmem search, пара install / uninstall и engmem mcp для Claude Desktop. Порядок здесь не формальность: в T4 разобран баг, который виден только из очерёдности трёх вызовов и которого список из десяти функций не показал бы вовсе.

Путь 1 — engmem search: от команды до вывода

Тот же поиск из шага 03, изнутри; значения сняты с прогона ResponseCacheController ETAG revalidation по хранилищу из 8 файлов. Развилок три, и ни одна не имеет права отменить уже готовый ответ.

_cmd_search resolve_store load_store search render_* render_scoreboard log_search cli.py store не найдено? spine.py scoring.py output.py всегда — hit или miss telemetry.py sessions/ нет → fail, код выхода 2 draft — скрыт из выдачи; superseded — редирект на преемника запись не удалась → заметка в stdout, не сбой
render_scoreboard печатается после render_* на обеих ветках — hit или miss не влияет на то, покажется ли он. Обрыв записи телеметрии не отменяет уже напечатанный результат: log_search никогда не бросает исключение.

Путь 2 — engmem install / uninstall: четыре режима агента

Единственный путь, который трогает файлы за пределами хранилища. Развилка одна на обе команды: --agent выбирает один из четырёх режимов, у каждого свой каталог и свой формат следа.

cmd_install / cmd_uninstall --local / copilot-ide без .git → код 2, ничего не тронуто agent=claude agent=copilot-ide agent=copilot-cli agent=claude-desktop ~/.claude/commands + CLAUDE.md, сентинел-правило .github/prompts/*.prompt.md + copilot-instructions.md ~/.copilot/skills/*/SKILL.md правило не пишется claude_desktop_config.json слияние mcpServers.engmem
Uninstall проходит той же развилкой и тем же стражем каталога, но снимает ровно то, что установил install — вплоть до узкого совпадения сентинела, а не широкого маркера, которым install решает, добавлять ли правило.

Путь 3 — сессия MCP: engmem mcp, вход для Claude Desktop

Тот же поиск, что в пути 1, но по протоколу, где stdout — не текст для человека, а кадры JSON-RPC.

невалидный JSON → PARSE_ERROR serve(): читает строку stdin json.loads(line) _dispatch(msg) _write(stdout), если есть ответ initialize notifications/ initialized tools/list tools/call prompts/list prompts/get _run_search_for_tool → load_store → search → render_* → render_scoreboard на этом пути «fail()» под запретом — stdout несёт только протокол, ни одной лишней строки
Шесть обработчиков из семи расходятся из одной точки разбора — седьмой, ping, не нарисован; tools/call для engmem_search сходится назад — на то же ядро из трёх функций, которым пользуется engmem search на пути 1.

Часть 6 · Установка и удаление

Две команды поставить, одна убрать

Роль в цикле: установка — то, чем цикл переносится на новую машину. Она кладёт шаблоны, которые печатают пре-регистрацию и журнал переиспользования, и правило, обязывающее агента пройти измеряемый канал до того, как он предложит план.

Первая команда ставит сам пакет, вторая подключает его к вашему агенту:

uv tool install --editable .     # или: pipx install .
engmem install --agent claude    # или copilot-ide / copilot-cli

--editable означает, что после git pull новый код подхватится сам, без переустановки — для инструмента, который сейчас активно правится, это важнее «красивой» установки.

Режим установки диктует агент-потребитель: у каждого свой каталог и свой формат файла.

РежимКуда кладётКогда выбирать
claude~/.claude/commands/engmem.mdClaude Code. Имя файла становится именем команды — отсюда /engmem. С флагом --local — в текущий репозиторий (нужен уже существующий .git в этой папке, иначе выход с кодом 2).
copilot-ide.github/prompts/engmem.prompt.mdПлагин Copilot в VS Code, Visual Studio, JetBrains. Он распознаёт только двойное расширение .prompt.md, обычный .md молча игнорирует. Тоже пишет в текущий репозиторий — нужен .git здесь, иначе выход с кодом 2.
copilot-cli~/.copilot/skills/engmem/SKILL.mdТерминальный copilot. Он не читает .github/prompts/ вообще, только навыки. Точки в именах запрещены, поэтому команды здесь /engmem-save, а не /engmem.save.

Что install создаёт на самом деле

Четыре следа в трёх местах — знать их полезно, потому что именно они потом удаляются:

  • хранилище (~/Developer/engmem по умолчанию): папка sessions/ и git init, если репозитория ещё нет. Существующее не трогается;
  • три файла шаблонов в каталоге агента из таблицы выше;
  • штамп версии первой строкой после YAML-шапки каждого шаблона — по нему видно, какая версия установлена;
  • триггер-правило в CLAUDE.md или .github/copilot-instructions.md — на самом деле две строки: сентинел-комментарий <!-- engmem-trigger-rule --> и сама строка правила сразу под ним: Before proposing a plan, run `engmem search "<key terms for the task>"`. Сентинел здесь не для красоты — именно по нему uninstall потом находит и убирает ровно эти две строки и ничего больше. Для copilot-cli этот шаг пропускается — глобального файла инструкций у него нет, навыки вызываются вручную.

Повторный запуск — это upgrade: хранилище не пересоздаётся, шаблоны перезаписываются свежей версией, правило не дублируется (проверка идёт по маркеру engmem search, так что даже переформулированное вами правило распознаётся).

Удаление

Зеркальная команда с тем же --agent:

engmem uninstall --agent claude

Она удаляет ровно те файлы, что ставила, и убирает ровно те строки триггер-правила, которые сама написала — сентинел-комментарий и строку правила сразу под ним (а для установок постарше, без сентинела, — саму строку правила, побайтово). Совпадение узкое и точное — почему не широкое, разобрано в пути 2. Реальный вывод:

engmem uninstalled: 3 template file(s) removed, 2 trigger rule line(s) removed, agent=claude
store left untouched: /Users/…/Developer/engmem  (your documents — remove it yourself if you want)

Запускать повторно безопасно — второй раз вернёт нули.

Чего она принципиально не делает:

  • не трогает хранилище. Это ваши документы, а не файлы инструмента: они остаются читаемым markdown в git и без engmem. Команда лишь печатает путь, чтобы вы решили сами;
  • не удаляет чужое. Каталог навыка Copilot сносится, только если после удаления SKILL.md он пуст; посторонние строки в CLAUDE.md сохраняются, сам файл не удаляется, даже если опустел;
  • не удаляет сам пакет — команда engmem остаётся в PATH.

Поэтому полное удаление — три шага, и только первый обязателен для того, чтобы агент перестал звать несуществующую команду:

engmem uninstall --agent claude   # шаблоны и триггер-правило
uv tool uninstall engmem          # сам пакет и команда в PATH
rm -rf ~/Developer/engmem         # ваши документы — только если точно не нужны

Часть 7 · Границы

Почему эта память такая скупая

Engmem — это память; скупа она намеренно, потому что главный вопрос о ней пока открыт: поднятые в новую задачу прошлые документы измеримо меняют её результат, и выгода превышает цену записи. Пока это не проверено, каждая лишняя функция — вложение в недоказанное. Ответ даёт разбор через 3–4 недели работы, по контрольным точкам, записанным до данных (ENGMEM-SPEC.md §11). Отсюда все ограничения — условия чистоты опыта.

ОграничениеЧто защищает
Одна зависимость, стандартный CLI, никаких фич «на вырост»Минимум вложений до вердикта: не подтвердится — выбросить дёшево.
Детерминированный поиск, эталонные тесты как истинаВоспроизводимость датчика: канал, дающий на один запрос разные ответы, нельзя ни сравнить с базовым уровнем, ни осмысленно записать. «Вчера находило, сегодня нет» невозможно по построению.
Инструмент не делает сетевых вызововСвойство самого инструмента, но не всей системы: содержимое документов уходит в модель на каждой задаче — это вопрос к согласованию, а не решённый.
Приборы измерения нельзя упрощатьЖурнал переиспользования, pre-reg, телеметрия, скорборд. Выключить их — провести опыт без данных.

До разбора заморожены: сложные формулы ранжирования, векторный и смысловой поиск, индексы любого вида, автоматическая пометка устаревшего, веб-интерфейс, командный режим.

Где смотреть код: пять модулей в src/engmem/, шаблоны в src/engmem/templates/, контракты поведения в docs/design/contracts/, эталонные тесты в tests/test_scoring.py и tests/test_cli.py.

engmem v0.1 · full walkthrough

The Anatomy of engmem

Memory for an AI coding agent: decisions, landmines and agreements stop getting lost between tasks. And unlike other such memories, this one can show whether it helps.

This page is derived from the specification: where they disagree, ENGMEM-SPEC.md and the tests are right.

Part 1 · What it is

Memory that can measure itself

Engmem is memory for an AI coding agent: what you worked out in one task is available to it in the next. The store, the capture templates, the search and the installer are that memory.

What is unusual is the rest: it measures its own worth. Pre-registration, a Reuse Log of verbatim quotes, and telemetry are the layer that answers the question memory is rarely asked — does a document pulled out of the past change a decision in a new task? The answer must not be adjustable after the fact, so the measurement is built into the order of the work, not bolted on as a report at the end.

The product stands on three legs, and the order is the argument:

  1. The unit of memory is an engineering story, and the store lives outside every repository. One story spans several repositories and several sessions: the decision is made in one service, the landmine is found in the next, the agreement is recorded in a third. Memory tied to a repository cuts such a story along the repository boundary; memory that hoards isolated facts loses the circumstances in which the fact was true. Everything else follows from this.
  2. Causal reuse measurement — per document, not per feeling. How that works is the four beats below.
  3. A bounded retrieval channel that can be logged. Every call has a known cost and leaves a trace. That is what search is for — not the completeness of its results.

The loop end to end: four beats

The order is required for the evidence, not for the work. A skipped or reordered beat breaks neither the task nor the review — the review runs on whatever was recorded. It simply stops proving anything: a naive plan written after reading the documents proves nothing.

  1. Pre-registration. A separate sub-agent, given nothing but the task statement, writes the naive plan before any search runs, and the plan goes into the draft document. The order is the measurement: a plan written after the documents are read is no longer evidence.
  2. The work. Search hands back section addresses, the agent reads what it found, the task proceeds as it normally would. In the middle, engmem takes no part at all.
  3. A Reuse Log row with a verbatim quote. One row per influencing document: what was quoted, what changed as a result, and what kind of influence it was: reuse — the work followed the document, anti-reuse — it deliberately went against it, harmful — the document led it astray. A row without a quote is invalid; if nothing was of use, the line written is exactly Prior docs used: none.
  4. The review (Gate 1). Rows from the whole store are collected into one table and read against a rule written down in advance.

What was written down in advance lives in ENGMEM-SPEC.md §11. The primary endpoint: at least one distant reuse event across a set of 4–5 cross-repository stories — one across the set, not one per story. "Distant" is defined mechanically rather than felt: a cited document is adjacent if it shares a repository or a tag with the citing one, or is reachable from it through one related edge; otherwise it is distant. An adjacent document is one the author would plausibly have remembered anyway — the retrieval layer gets no credit for it.

One shared module, src/engmem/gate1.py, decides the mechanical part of every row — citation integrity, distance and staleness; classification it does not decide but reads, since the author sets it. tools/verify_citations.py and tools/gate1_report.py read that same verdict rather than checking independently; they differ only in what happens with it — verify_citations.py fails the run on an unverifiable quote, gate1_report.py never fails and renders all four axes into a table for the human. Its empty column is the one thing a machine cannot decide: whether the quote changed a decision. Only rows classified reuse reach the count: anti-reuse and harmful are printed in the table but excluded from it, as are a row without a quote and a row from a draft. So are rows from stories about engmem itself — the dogfooding axis in the output: building the tool cannot be evidence that the tool helps.

| story | cited | quote | integrity | classification | distance | staleness | changed a decision? |
|---|---|---|---|---|---|---|---|
| 2000002-widget-cache | 1000001-response-cache | rejected the separate table because the response already carries freshness data | verified | reuse | distant | cited_active |  |

rows: 1
citation integrity: 1 verified, 0 no quote, 0 cited doc not in store, 0 quote not found
classification: 1 reuse, 0 anti-reuse, 0 harmful, 0 classification missing, 0 classification not recognized
rows excluded from the count: 0
excluded from the count, by axis (a row may match more than one -- see the total above for the true count, not the sum of these): 0 dogfooding (story about this repository), 0 citing document not active (draft/superseded)
1 valid (the last column above is theirs to fill), of which 1 distant, 0 adjacent, 0 undecidable
of the 1 distant, 0 cite a superseded document (flagged, not excluded -- see the staleness column)
ENGMEM-SPEC.md section 11's primary endpoint counts the distant figure above.
documents reporting no reuse: 1
The last column is yours where the tool left it blank.

[... continues with the audit coverage block -- see contracts/gate1.md ...]

The full run prints a second block beneath this one — sample-completeness audit coverage, not another gate. The tool states two standing caveats of its own. --session is deliberately unvalidated, so the orphan-row figure also counts typos and cross-store contamination, not only genuine gaps. And telemetry.jsonl is append-only with no rotation, so pre-Gate-1 and testing rows are indistinguishable from experimental ones within whatever window is in force. See docs/design/contracts/gate1.md, "The audit coverage block," for the rest.

Fabrication is caught in the same place: edit a quote so it drifts from its source, and the check names the file, the line and the quote itself, and exits 1.

2000002-widget-cache.md:23: quote not found in 1000001-response-cache — "rejected the separate table because it would have been slower"
verify_citations: 1 unverifiable citation(s), 0 citing a superseded document, 0 with a conflicting Reuse Log

The reading of the result is fixed in advance too. If the primary endpoint is missed while the secondaries hold, the verdict reads: the store as compressed context earned its keep, the measurement superstructure did not — and the superstructure gets wound down. A miss is a result, not grounds for softening the criterion and trying again.

What this is not. Not a vector database: no embeddings, no indexes, no ranking by meaning. Not another repository-side memory bank: the capture→compress→inject loop is common ground here, not the product. Not a search engine competing with grep: an agent's free-form grep over the store can neither be capped in volume nor written to telemetry — and that, rather than result quality, is what makes a channel measurable.

Part 2 · The problem

Your agent doesn't remember yesterday

You solve a task with an AI agent. You weigh options, discard the dead ends, uncover a pitfall that would have cost you another week. The task closes, the branch merges.

Two months later you're back in the same subsystem. The agent starts from zero. It doesn't know you already considered a separate table for cache metadata and rejected it — so it proposes exactly that again. It doesn't know a repeated lookup inside one request doubles the latency — so it writes code that walks straight into it. It doesn't know about the agreement with the neighbouring team that lives in no ticket at all.

The knowledge existed. It vanished in three places: a closed chat window, your own memory (where it faded), and pull-request comments nobody ever re-reads.

What the problem is not about. Not "how the code works" — that's visible in the code, and copying it into notes is actively harmful: the copy goes stale first. What gets lost is different: rejected options and the reasons for rejecting them, pitfalls, business constraints, verbal agreements. The things that aren't in the code and never will be.

Why the usual answers fail

All four share one root cause: the cost of writing is paid now, the benefit arrives later and isn't guaranteed. In that trade, the present self always beats the future one.

ApproachWhy it doesn't hold
Write it in Confluence / NotionExpensive to write, more expensive to find: the page never surfaces itself at the moment it's needed.
Comments in code and PRsThey answer "what does this line do", never "why did we reject the alternative". By the time you need it, the PR is long closed.
ADRs (architecture decision records)They work, but they're written by hand and only for big decisions. The small traps — the ones that actually eat hours — never make it in.
The agent's own memoryScoped to one session or one vendor, doesn't travel between tools, and can't be inspected: you can't open a file and read what it holds.

Part 3 · The solution

The agent writes, the machine searches, the human decides

Engmem turns every closed task into one markdown file, and automatically surfaces the right files at the start of the next one.

That inverts the trade: writing is nearly free, because the agent composes the document from the diff and the conversation — all you supply is a single "y". And the benefit arrives on its own: the agent is forbidden to propose a plan before it has searched the past.

Three parts make that possible:

  • The store — a folder of markdown files under git. No database, no server, no cloud. You can open a file, read it, fix it by hand, inspect its history with git log.
  • The search — the engmem search command. Deterministic, no ML: the same query always yields the same answer, the result has a fixed ceiling, and every call leaves a telemetry line carrying its exact cost in bytes. It re-reads every document's header on every call.
  • Three template commands for the agent: /engmem at the start of a task, /engmem.save at the end, /engmem.save.quick for a short document with no review round, when there's no attention left for one. They're markdown instructions rather than code — which is why they work in any agent.
/engmem the work /engmem.save store + 1 surfaces the past captures the new every document makes the next start smarter
The project's measure of success is knowledge actually reused — not the number of documents written.

The flywheel above traces the loop for one document. Zoom out, and the store is a graph that keeps getting more connected. Two kinds of edge sit side by side in it: the ones an author wrote, and the ones nobody wrote. The retrieval channel carries the second kind on its own, through entities two documents happen to share. The review rule counts such a pair as distant — no shared repository, no shared tag, no related edge — and those are the only ones the retrieval layer may claim credit for.

Part 4 · One task, end to end

From the ask to the saved document

Its role in the loop: this is how the memory fills up — a document reaches the store, and with it the measurement acquires something to measure. Steps 01 and 07 are the measuring beats themselves (pre-registration and the quoted Reuse Log row); everything between them is ordinary work.

You type nothing: the agent walks the path from the naive plan to the finished document, and you confirm the header and answer whether this work supersedes an earlier decision. A full save also asks you to check the quotes — step 07 names the cost of that plainly. Below, step by step: what the tool does, what physically lands in the model's context, and what remains afterwards. The task: "Add response caching for a new endpoint"; similar work happened three months ago, but you no longer remember it. Every value was captured from a live run against a test store.

00 01 02 03 04 05 06 07 08 09 docs: 7 → 8 the biggest single step: sections of the found documents enter the context
What the task has accumulated by each step — from nothing at 00 to a saved document at 08. The flat stretch at 05–06 marks steps where engmem adds nothing new to the context; 09 holds the new level but doesn't add to it either.

Part 5 · The machinery

Three paths through the code

Its role in the loop: this is the memory's own code — what turns a query into a bounded result and a telemetry line, and what puts the loop on a machine. Below: what gets called, in what order, and at which branch points.

Three entry points, one core — what diverges is only who reads stdout at the other end. Below are all three, function by function, in the order they actually run: engmem search, the install / uninstall pair, and engmem mcp for Claude Desktop. Order here is not a formality: T4 dissects a bug visible only from the sequence of three calls, one a list of ten functions would never have shown.

Path 1 — engmem search: from the command to the output

The same search from step 03, from the inside; the values come from running ResponseCacheController ETAG revalidation against a store of 8 files. There are three branch points, and none of them may discard an answer that's already built.

_cmd_search resolve_store load_store search render_* render_scoreboard log_search cli.py store missing? spine.py scoring.py output.py always — hit or miss telemetry.py sessions/ missing → fail, exit code 2 draft — hidden from results; superseded — redirected to successor write fails → stdout note, not a crash
render_scoreboard prints after render_* on both branches — a hit or a miss doesn't change whether it appears. A telemetry write failure never unprints the result already shown: log_search never raises.

Path 2 — engmem install / uninstall: four agent modes

The only path that touches files outside the store. Both commands share one fork: --agent picks one of four modes, each with its own directory and its own trace shape.

cmd_install / cmd_uninstall --local / copilot-ide, no .git → exit 2, nothing touched agent=claude agent=copilot-ide agent=copilot-cli agent=claude-desktop ~/.claude/commands + CLAUDE.md, sentinel rule .github/prompts/*.prompt.md + copilot-instructions.md ~/.copilot/skills/*/SKILL.md no trigger rule written claude_desktop_config.json merges mcpServers.engmem
Uninstall walks the same fork and the same directory guard, but removes exactly what install wrote — down to the narrow sentinel match, not the broad marker install uses to decide whether to add the rule.

Path 3 — an MCP session: engmem mcp, the Claude Desktop entry point

The same search as path 1, but over a protocol where stdout isn't text for a human — it's JSON-RPC frames.

invalid JSON → PARSE_ERROR serve(): reads a stdin line json.loads(line) _dispatch(msg) _write(stdout), if there's a reply initialize notifications/ initialized tools/list tools/call prompts/list prompts/get _run_search_for_tool → load_store → search → render_* → render_scoreboard «fail()» is banned on this path — stdout carries protocol only, never one extra line
Six of the seven handlers fan out from one dispatch point — the seventh, ping, is not drawn; tools/call for engmem_search folds back onto the same three-function core that engmem search uses on path 1.

Part 6 · Install and removal

Two commands to set up, one to undo

Its role in the loop: installation is how the loop travels to a new machine. It lays down the templates that print the pre-registration and the Reuse Log, and the rule that obliges the agent to go through the measured channel before it proposes a plan.

The first command installs the package itself; the second wires it into your agent:

uv tool install --editable .     # or: pipx install .
engmem install --agent claude    # or copilot-ide / copilot-cli

--editable means a git pull picks up new code without reinstalling — for a tool under active repair that matters more than a tidy install.

The mode is dictated by the consuming agent: each has its own directory and its own file format.

ModeWrites toWhen to pick it
claude~/.claude/commands/engmem.mdClaude Code. The file name becomes the command name — hence /engmem. With --local, into the current repository instead (needs an existing .git there already, or it exits 2).
copilot-ide.github/prompts/engmem.prompt.mdThe Copilot plugin in VS Code, Visual Studio, JetBrains. It only recognizes the double extension .prompt.md; a plain .md is silently ignored. Also writes into the current repository — needs a .git here, or it exits 2.
copilot-cli~/.copilot/skills/engmem/SKILL.mdThe terminal copilot. It doesn't read .github/prompts/ at all, only skills. Dots are illegal in skill names, so the commands there are /engmem-save, not /engmem.save.

What install actually creates

Four traces across three places — worth knowing, because these are exactly what gets removed later:

  • the store (~/Developer/engmem by default): a sessions/ folder and git init if it isn't a repository yet. An existing one is left alone;
  • three template files in the agent directory from the table above;
  • a version stamp on the first line after each template's YAML front matter — that's how you see which version is installed;
  • the trigger rule in CLAUDE.md or .github/copilot-instructions.md — actually two lines: a <!-- engmem-trigger-rule --> sentinel comment, then the rule itself right below it: Before proposing a plan, run `engmem search "<key terms for the task>"`. The sentinel is not decoration — it is exactly what uninstall later matches to remove precisely those two lines and nothing else. copilot-cli skips this step — it has no global instructions file, and skills are invoked by hand.

A repeat run is an upgrade: the store isn't recreated, templates are overwritten with the fresh version, and the rule isn't duplicated (detection goes by the engmem search marker, so even a rule you reworded is recognized).

Removal

The mirror command, with the same --agent:

engmem uninstall --agent claude

It deletes exactly the files it installed, and removes exactly the trigger-rule lines it wrote itself — the sentinel comment plus the rule line right after it (or, for older installs with no sentinel, the bare rule line, matched byte-for-byte). The match is narrow and exact — why it is not broad is worked through in path 2. The real output:

engmem uninstalled: 3 template file(s) removed, 2 trigger rule line(s) removed, agent=claude
store left untouched: /Users/…/Developer/engmem  (your documents — remove it yourself if you want)

Safe to run twice — the second run reports zeros.

What it deliberately does not do:

  • never touches the store. Those are your documents, not the tool's files: they stay readable markdown under git without engmem. The command only prints the path, so the decision stays yours;
  • never removes what isn't its own. A Copilot skill directory is pruned only if it's empty after its SKILL.md goes; unrelated lines in CLAUDE.md survive, and the file itself is never deleted even if it ends up empty;
  • doesn't remove the package — the engmem command stays on your PATH.

So a full removal is three steps, and only the first is required to stop agents calling a command that no longer exists:

engmem uninstall --agent claude   # templates and the trigger rule
uv tool uninstall engmem          # the package and the PATH command
rm -rf ~/Developer/engmem         # your documents — only if you're sure

Part 7 · Limits

Why this memory is so frugal

Engmem is memory; v0.1 is frugal on purpose, because the central question about it is still open: prior documents pulled into a new task measurably change its outcome, and the benefit exceeds the cost of writing them. Until that is tested, every extra feature is an investment in something unproven. The answer comes from a review after 3–4 weeks of real work, against endpoints written down before the data (ENGMEM-SPEC.md §11). Hence every constraint: they are the conditions for a clean experiment.

ConstraintWhat it protects
Two dependencies, a standard-library CLI, no "just in case" featuresMinimum investment before the verdict: if it doesn't hold up, throwing it away is cheap.
Deterministic search, golden tests as the source of truthA reproducible sensor: a channel that answers one query differently on different days can neither be compared against a baseline nor meaningfully logged. "It found it yesterday, not today" is impossible by construction.
The tool makes no network callsTrue of the tool, not of the system: document contents reach the model on every task — that's a sign-off question, not a settled one.
The measuring instruments may not be simplifiedReuse Log, pre-reg, telemetry, scoreboard. Switching them off means running the experiment with no data.

Frozen until the review: elaborate ranking formulas, vector and semantic search, indexes of any kind, automatic staleness marking, a web interface, team mode.

Where to read the code: five modules in src/engmem/, templates in src/engmem/templates/, behaviour contracts in docs/design/contracts/, golden tests in tests/test_scoring.py and tests/test_cli.py.