Engmem — память для ИИ-агента: то, что выяснено в одной задаче, доступно ему в следующей. Хранилище, шаблоны записи, поиск и установщик — это она и есть.
Необычно в ней другое: она измеряет собственную пользу. Пре-регистрация, журнал переиспользования с дословными цитатами и телеметрия — надстройка, которая отвечает на вопрос, который памяти обычно не задают: меняет ли поднятый из прошлого документ решение в новой задаче. Ответ не должен поддаваться подгонке задним числом — поэтому измерение встроено в сам порядок работы, а не приделано к нему отчётом в конце.
Продукт стоит на трёх опорах, и порядок здесь содержательный:
- Единица памяти — инженерная история, и хранилище лежит вне всех репозиториев. Одна история тянется через несколько репозиториев и несколько сессий: решение принято в одном сервисе, грабли обнаружены в соседнем, договорённость — в третьем. Память, привязанная к репозиторию, режет такую историю по его границе; память, копящая отдельные факты, теряет обстоятельства, в которых факт был верен. Из этого следует всё остальное.
- Причинное измерение переиспользования — по документу, а не по ощущению. Как это устроено — четыре такта ниже.
- Ограниченный канал выдачи, который можно записать. У каждого вызова известная цена и оставленный след. В этом назначение поиска, а не в полноте выдачи.
Цикл целиком: четыре такта
Порядок обязателен для свидетельства, а не для работы. Пропущенный или переставленный такт не ломает ни задачу, ни разбор: разбор пройдёт по тому, что записано. Он лишь перестаёт быть доказательством — наивный план, написанный после чтения документов, уже ничего не доказывает.
- Пре-регистрация. Отдельный под-агент, которому дана только формулировка задачи, пишет наивный план до всякого поиска, и план уходит в черновик документа. Порядок здесь и есть измерение: план, написанный после чтения документов, свидетельством уже не будет.
- Работа. Поиск отдаёт адреса разделов, агент читает найденное, задача делается обычным образом. В середине engmem не участвует вовсе.
- Строка журнала с дословной цитатой. На каждый повлиявший документ — строка: что процитировано, что в итоге изменилось, какого рода влияние:
reuse — пошли за документом, anti-reuse — сознательно против него, harmful — документ увёл не туда. Строка без цитаты недействительна; если не пригодилось ничего — пишется ровно Prior docs used: none.
- Разбор (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
Толкование результата тоже зафиксировано до данных. Если основная точка не взята, а вторичные взяты, вывод звучит так: хранилище как сжатый контекст себя оправдало, измерительная надстройка — нет, и надстройку сворачивают. Промах — это результат, а не повод смягчить критерий и попробовать ещё раз.