Source of truth — markdown-файлы в каталоге .memory/ внутри репозитория проекта:
заметки и реестр сущностей. Лежат в git, дают читаемые диффы и резолвимые
merge-конфликты.
SQLite-индекс рядом — производный, в .gitignore, пересобирается из markdown
и исходного кода. В индексе нет ничего, что нельзя восстановить из source of truth.
Файл .memory/notes/<id>.md — frontmatter плюс тело.
| Поле | Тип | Описание |
|---|---|---|
id |
string | Короткий хэш — 5 символов, lowercase base36, без даты и slug-а; генерируется сервером при create_note, совпадает с именем файла |
summary |
string | Авторская строка-анонс |
status |
enum | current / outdated / draft |
created |
date | ISO-дата создания |
updated |
date | ISO-дата последнего изменения |
anchors |
list | Список якорей, см. ниже |
summary — пишется LLM при создании, отвечает на вопрос «что заставит будущего
агента открыть эту заметку целиком». Единственный текст заметки, попадающий
в компактную выдачу поиска; на нём держится решение «разворачивать или нет».
status:
current— актуальное знание, дефолтный фильтр поискаoutdated— больше не истина (заменено новым решением либо откатано); попадает в выдачу только с явнымinclude_archiveddraft— не подтверждено, под рассмотрение; вне обычного поиска, попадает в выдачу только с явнымinclude_drafts
Markdown с ролевой структурой секций — это часть контракта сервиса. Каждая
секция несёт знание одного рода, и её каноническое имя — навигационная единица:
будущий читатель (подагент или сессия) находит нужное по имени секции. Сервис
сами секции не парсит и не фильтрует (нет sections-параметра в
get_notes/search) — get_notes возвращает тело целиком; контракт нормирует
имена, а не индексацию.
Два шаблона с закрытым набором канонических имён. Любая секция без данных опускается — пустой заголовок не пишется.
- Обычная заметка (ADR):
## Контекст·## Решение·## Альтернативы·## Инварианты (Must Hold)·## Антипаттерны и Подводные камни (Must Not)·## Пробелы и ограничения. - Заметка-исследование:
## Контекст·## Подтверждено·## Гипотезы·## Инварианты (подтверждённые)·## Пробелы.
Шаблоны, критерии классификации секций и anti-swamp правило для ## Инварианты
(наличие секции — единственное основание для веса critical) — в
04-note-authoring.md.
Раздел Гипотезы — явная, видимая в тексте пометка непроверенного. Заметка
с гипотезами может быть current: верифицирован сам артефакт (гипотезы честно
размечены), а не каждое утверждение в нём.
Тело может содержать ссылки на другие заметки в синтаксисе [[id]]. Так заметка
ссылается на прежние решения вместо того, чтобы инлайнить их контекст: знание
набивается в цепочку, каждое звено компактно.
[[id]] — ссылка уровня текста, не якорь: по ней не ищут, в ранжировании она
не участвует. Индексатор извлекает такие ссылки при разборе тела; при чтении
заметки под ней отдаётся компактный блок упомянутых заметок (id + summary,
со статусом-пометкой для не-current), и читатель сам решает, дозагружать ли их. Ссылка на несуществующий id
помечается как stale. Синтаксис совместим с граф-представлением Obsidian.
Часть заметки, не отдельная сущность файла. Структура — { uri, weight }.
uri — формат <type>:<payload>:
| Тип | Формат | Резолвится через |
|---|---|---|
file |
file:<path> |
файловая система |
symbol |
symbol:<path>::<symbol> |
индекс символов |
entity |
entity:<Name> |
реестр сущностей |
env |
env:<VAR_NAME> |
парсинг .env* |
weight:
| Значение | Смысл |
|---|---|
critical |
Закреплён: заметка гарантированно попадает в выдачу по этому якорю — поверх лимита и IDF |
core |
Связь центральная — основная работа велась здесь |
supporting |
Существенная, но не центральная |
incidental |
Предметно затронут — нижний порог членства |
core…incidental — множители ранжирования. critical — не множитель,
а гарантия попадания в выдачу.
incidental — нижняя граница: файл, затронутый менее чем предметно (формально,
одна-две строки), якорем не оформляется вовсе. Реальные cross-effects описываются
прозой в секции тела «Подводные камни», а не слабым якорем.
При развёртывании заметки (get_notes) возвращается её карта якорей — все типы
(file/symbol/entity/env), сгруппированные по весу (critical/core сверху),
со stale-статусом. file/symbol — это навигация «куда смотреть в коде»; entity:/
env: показывают, к какой сущности и каким переменным окружения привязана заметка,
чтобы при развороте была видна и осевая структура якорей (а не только код). В
компактной выдаче поиска якоря не отдаются.
Реестр .memory/entities.md. Каждая запись:
| Поле | Тип | Описание |
|---|---|---|
name |
string | Имя, PascalCase, уникально |
description |
string | Краткое «что это и зачем» |
- Заметка имеет 1..N якорей. Якорь принадлежит ровно одной заметке.
- Якорь
entity:ссылается наnameиз реестра сущностей — связь обязана резолвиться (иначе заметка не создаётся). - Якоря
file/symbol/envссылаются на артефакты кода; их существование проверяется индексом и файловой системой, расхождение помечается какstale. Stale-якорь автоматически не удаляется — он чинится черезrename_anchor(при переименовании) либо убирается осознанно. Молчаливое удаление потеряло бы связь незаметно.anchor_statusпересчитывается дёшево при изменении кода. - Структурных связей между сущностями нет.
- Поискового якоря заметка→заметка не существует. Заметка
outdatedи сменившая еёcurrentсвязаны через общий якорь. - Связь заметка→заметка возможна только на уровне текста — ссылкой
[[id]]в теле (см. выше). Это не якорь и в поиске не участвует.
Существует только ради скорости поиска и верификации якорей. Состав:
- зеркало заметок (frontmatter, тело, хэш содержимого);
- развёрнутые якоря заметок;
- реестр сущностей;
- индекс символов кода (файл, имя, вид, диапазон строк) — заполняется tree-sitter;
- индекс файлов (хэши) для инкрементальной переиндексации;
- полнотекстовый индекс по
summaryи телу; - счётчики появления файлов в якорях — для IDF; учитываются только
fileиsymbol, якоряentity:не штрафуются (сущность по природе — хаб).
Любая таблица восстанавливается из markdown и исходного кода.