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

5.6 KiB

Проект: /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-инструментов.
  • Не добавлять зависимости.