Files

94 lines
7.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Проект: /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
1. **Создаёт 6 баз вместо 3.** Вывод `наблюдаю:` перечисляет корень, `infra`, `life`, `work`,
`life/books`, `work/jira` и всем заводит `.memo/index.db`. Причина — своя локальная копия
`discoverCollections()` в `WatchMain.kt` (walkTopDown + depth 2 «есть .md где-то внутри»);
в `memo-core` эту же ошибку уже вылечили (`findCollections` = только каталог с *.md НАПРЯМУЮ),
но watcher остался на старой копии.
2. **Не индексирует при старте.** Сразу после запуска (без правок файлов) в базах 0 файлов и 0 чанков;
наполняется только та коллекция, в которой потом что-то изменилось. Проверка:
правка одного файла в `infra` → в `infra/.memo` появились файлов 4, чанков 12, а `life/books`
и `work/jira` остались с нулями. Никакого первичного прохода при старте нет.
3. **Держит все базы открытыми постоянно.** `WatchMain` открывает `Db` и `Embedder` на каждую
коллекцию и не закрывает до SIGINT; тот же `.db` в это время открывают CLI и MCP.
Это надо проверить на отсутствие «database is locked» (одновременные watcher + поиск).
## Что сделать
Правки ТОЛЬКО в модуле `memo-watch` (`src/main` и `src/test`). `memo-core` не менять.
1. **Убрать копию логики.** В `WatchMain.kt` удалить локальную `discoverCollections` и функцию
определения корня по `.memo`; использовать из ядра:
`memo.core.findCollections(base)` и `memo.core.resolveCollection(raw)`.
Поведение: передан один каталог-коллекция → он один; передан корень → все коллекции внутри.
2. **Первичный проход при старте.** После `watcher.start()` (или до него) выполнить для каждой
коллекции `indexer.indexTree(coll)` один раз, чтобы индекс был полным сразу, без ожидания событий.
Вывести в stdout по строке: `индексирую: <путь> -> обновлено <N>`. Затем уже строки `наблюдаю:`.
3. **Устойчивость к параллельному доступу.** Включить в `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 теста:
1. `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`.
2. `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).
## Обязательная сквозная проверка (приложить вывод)
```bash
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.
- Не добавлять зависимости.