Files
memo/docs/orders/08-fix-collections.md

79 lines
5.6 KiB
Markdown

Проект: /root/WORK/memo (Kotlin/JVM). Заказ на ИСПРАВЛЕНИЕ дефекта определения коллекций.
## Доказанный дефект
На корпусе /root/WORK/memo-e2e (3 папки с заметками: `infra`, `work/jira`, `life/books`,
всего 12 .md) команда
memo index /root/WORK/memo-e2e
создаёт ШЕСТЬ баз индекса вместо трёх:
/root/WORK/memo-e2e/.memo/index.db
/root/WORK/memo-e2e/infra/.memo/index.db
/root/WORK/memo-e2e/life/.memo/index.db
/root/WORK/memo-e2e/life/books/.memo/index.db
/root/WORK/memo-e2e/work/.memo/index.db
/root/WORK/memo-e2e/work/jira/.memo/index.db
и «итого: 24 обновлено в 6 коллекциях» — одни и те же заметки проиндексированы по 2-3 раза.
Причина: каталог считается коллекцией, если .md-файлы есть где-то на глубине до 2 ВНУТРИ него.
## Правило, которое надо реализовать
Коллекция — это каталог, в котором есть хотя бы один файл `*.md` **непосредственно в нём самом**
(не в подкаталогах). Всё остальное коллекцией не является.
Правки только в memo-core/src/main/kotlin/memo/core/Collections.kt (и, при необходимости,
в местах его вызова в memo-cli и memo-mcp — но менять надо только вызовы, не логику):
1. `fun findCollections(root: java.io.File): List<java.io.File>` — рекурсивный обход root,
возвращает ВСЕ каталоги (включая сам root, если подходит), в которых есть `*.md` напрямую.
Скрытые каталоги (имя начинается с '.') пропускать, каталог `.memo` не обходить.
Порядок — по пути, детерминированный.
2. `fun resolveCollection(path: java.io.File): java.io.File` — если path каталог, вернуть ближайший
каталог, содержащий .md напрямую: сам path, иначе подняться вверх по родителям до первого такого
(но не выше, чем файловая система); если не найден — вернуть сам path.
Если path — файл, вернуть его родительский каталог (file.parentFile).
3. `fun isCollection(dir: java.io.File): Boolean` — вспомогательная, реализует правило из абзаца выше
(ровно один уровень, без рекурсии).
## Тесты: добавить в memo-core/src/test/kotlin/memo/core/ новый файл CollectionsTest.kt
Ровно 4 теста, имена ровно такие:
1. `findCollectionsReturnsOnlyDirsWithDirectMarkdown` — временный корень с такой структурой:
`root/a.md`, `root/sub/b.md`, `root/sub/deep/c.md`, `root/empty/` (без .md), `root/nested/only/deep/d.md`
→ ровно 4 коллекции: root, root/sub, root/sub/deep, root/nested/only/deep. Каталогов `empty`, `nested`,
`nested/only` в результате быть НЕ должно.
2. `parentDirWithoutDirectMarkdownIsNotCollection` — структура `root/top/inner/x.md` →
findCollections даёт ровно один элемент — `root/top/inner`.
3. `resolveCollectionRaisesToNearestWithMarkdown` — для файла `root/top/inner/x.md` resolveCollection
возвращает `root/top/inner`; для каталога `root/top` (внутри .md нет, есть только в inner) —
тоже `root/top/inner`.
4. `hiddenDirsAreSkipped` — структура `root/.hidden/a.md`, `root/vis/b.md` → ровно одна коллекция `root/vis`.
Плюс: если в корне лежит `root/.memo/index.db`, это ничего не меняет и не ломает результат.
## Обязательная сквозная проверка (сделать самому, приложить вывод)
cd /root/WORK/memo
./gradlew :memo-cli:installDist -q
rm -rf /root/WORK/memo-e2e/infra/.memo /root/WORK/memo-e2e/work/jira/.memo /root/WORK/memo-e2e/life/books/.memo \
/root/WORK/memo-e2e/.memo /root/WORK/memo-e2e/work/.memo /root/WORK/memo-e2e/life/.memo
memo-cli/build/install/memo/bin/memo index /root/WORK/memo-e2e
find /root/WORK/memo-e2e -name index.db | sort
Ожидается РОВНО три пути:
/root/WORK/memo-e2e/infra/.memo/index.db
/root/WORK/memo-e2e/life/books/.memo/index.db
/root/WORK/memo-e2e/work/jira/.memo/index.db
и в выводе index — «итого: 12 обновлено в 3 коллекциях». Второй прогон index — «итого: 0 обновлено».
После: ./gradlew test --rerun-tasks — ВСЕ тесты проекта зелёные (включая те 5+memo-cli+memo-mcp).
Коммит: git add -A && git commit -m "core: коллекция — каталог с markdown напрямую; исправлен тройной обход"
СТРОГИЕ ЗАПРЕТЫ:
- Не выводить план текстом; сразу правь файлы.
- Не менять логику Searcher, Indexer, Chunker, Embedder, Db.
- Не менять контракт CLI (имена команд и флагов) и MCP-инструментов.
- Не добавлять зависимости.