7.1 KiB
Проект: /root/WORK/memo (Kotlin/JVM). Заказ на исправление ТРЁХ доказанных дефектов демона memo-watch.
Доказанные дефекты (воспроизведено на живом корпусе)
Корпус /root/WORK/memo-e2e-watch2 — копия эталонного (infra, life/books, work/jira, 12 .md).
Все .memo предварительно удалены. Запуск:
MEMO_MODEL_DIR=/root/WORK/memo/models/siglip2
memo-watch/build/install/memo-watch/bin/memo-watch /root/WORK/memo-e2e-watch2
- Создаёт 6 баз вместо 3. Вывод
наблюдаю:перечисляет корень,infra,life,work,life/books,work/jiraи всем заводит.memo/index.db. Причина — своя локальная копияdiscoverCollections()вWatchMain.kt(walkTopDown + depth 2 «есть .md где-то внутри»); вmemo-coreэту же ошибку уже вылечили (findCollections= только каталог с *.md НАПРЯМУЮ), но watcher остался на старой копии. - Не индексирует при старте. Сразу после запуска (без правок файлов) в базах 0 файлов и 0 чанков;
наполняется только та коллекция, в которой потом что-то изменилось. Проверка:
правка одного файла в
infra→ вinfra/.memoпоявились файлов 4, чанков 12, аlife/booksиwork/jiraостались с нулями. Никакого первичного прохода при старте нет. - Держит все базы открытыми постоянно.
WatchMainоткрываетDbиEmbedderна каждую коллекцию и не закрывает до SIGINT; тот же.dbв это время открывают CLI и MCP. Это надо проверить на отсутствие «database is locked» (одновременные watcher + поиск).
Что сделать
Правки ТОЛЬКО в модуле memo-watch (src/main и src/test). memo-core не менять.
- Убрать копию логики. В
WatchMain.ktудалить локальнуюdiscoverCollectionsи функцию определения корня по.memo; использовать из ядра:memo.core.findCollections(base)иmemo.core.resolveCollection(raw). Поведение: передан один каталог-коллекция → он один; передан корень → все коллекции внутри. - Первичный проход при старте. После
watcher.start()(или до него) выполнить для каждой коллекцииindexer.indexTree(coll)один раз, чтобы индекс был полным сразу, без ожидания событий. Вывести в stdout по строке:индексирую: <путь> -> обновлено <N>. Затем уже строкинаблюдаю:. - Устойчивость к параллельному доступу. Включить в
Dbуже есть WAL — не менять. Вместо этого добавить вWatchMainобработку ошибок записи так, чтобыdatabase is lockedне убивал демон: при ошибке печатать в stderr и повторять попытку один раз через 1 секунду (достаточно локальной обёртки вокругindexer.indexTree). Не менять код ядра.
Тесты: дополнить memo-watch/src/test/kotlin/memo/watch/WatcherTest.kt и добавить новый файл
Новый файл memo-watch/src/test/kotlin/memo/watch/WatchMainLogicTest.kt, ровно 2 теста:
collectionsFoundByCoreRule— дерево:root/a.md,root/sub/b.md,root/nested/only/deep/d.md,root/empty/→findCollections(root)даёт ровно три каталога (root, root/sub, root/nested/only/deep), и среди них НЕТroot/nestedиroot/nested/only.startupIndexPassFillsEveryCollection— временный корень с двумя коллекциями (root/c1/x.md,root/c2/y.md), у каждой создаётсяDb+.memo, вызываетсяIndexer.indexTree(coll)по разу (как это делает первичный проход), после чего в обеих базахSELECT COUNT(*) FROM chunks> 0 иSELECT COUNT(*) FROM files== 1. Модель брать изMEMO_MODEL_DIR(как в CoreSmokeTest).
Обязательная сквозная проверка (приложить вывод)
cd /root/WORK/memo
./gradlew :memo-watch:installDist -q
rm -rf /tmp/mw && cp -r /root/WORK/memo-e2e /tmp/mw
find /tmp/mw -name .memo -type d -prune -exec rm -rf {} + 2>/dev/null
# 1) старт: 3 базы, все наполнены сразу
MEMO_MODEL_DIR=/root/WORK/memo/models/siglip2 \
memo-watch/build/install/memo-watch/bin/memo-watch /tmp/mw > /tmp/mw.log 2>&1 &
WPID=$!
sleep 40
echo "--- базы:"; find /tmp/mw -name index.db | sort
echo "--- чанки сразу после старта:"
python3 - <<'EOF'
import sqlite3, glob
for db in sorted(glob.glob("/tmp/mw/**/.memo/index.db", recursive=True)):
c = sqlite3.connect("file:"+db+"?mode=ro", uri=True)
print(db.replace("/tmp/mw",""), "файлов", c.execute("SELECT COUNT(*) FROM files").fetchone()[0],
"чанков", c.execute("SELECT COUNT(*) FROM chunks").fetchone()[0])
c.close()
EOF
# 2) параллельно с работающим демоном ищем тем же ядром — не должно быть locked
memo-cli/build/install/memo/bin/memo search /tmp/mw/life/books "кто ведёт рассказ в романе" --k 3 --json | head -c 400
echo
kill $WPID 2>/dev/null; wait $WPID 2>/dev/null
cat /tmp/mw.log
Ожидается: РОВНО три пути index.db; у каждой коллекции ненулевые файлы и чанки СРАЗУ после старта;
поиск при работающем демоне отвечает без ошибки database is locked; в логе — строки
индексирую: ... -> обновлено N и наблюдаю: ... (по три каждого вида).
После: ./gradlew test --rerun-tasks — все тесты проекта зелёные. Коммит: git add -A && git commit -m "watch: первичный проход при старте, коллекции по правилу ядра"
СТРОГИЕ ЗАПРЕТЫ:
- Не выводить план текстом; сразу правь файлы.
- Не менять memo-core, memo-cli, memo-mcp.
- Не менять смысл существующих тестов WatcherTest.
- Не добавлять зависимости.