# ADR-028: Project map contract (mandatory README.md, aspirational modules) ## Статус Accepted (2026-07-25) ## Контекст Контракт `docs/project-map/` в репо `slaid098/opencode-config` размазан по 3 файлам с внутренними несоответствиями: - `.opencode/agents/docs-reviewer.md:158-159,191` — обязывает docs-reviewer обновлять карту при структурных изменениях и предлагает один commit message. - `.opencode/skills/get-project-map/SKILL.md:25-27` — описывает структуру карты (README.md + модульные файлы per module) и frontmatter `last_updated`. - `.opencode/agents/reviewer.md:199-205` — требует от code-reviewer проверять актуальность карты и предлагает *другой* commit message для той же операции (несоответствие с docs-reviewer.md). Фактическое состояние карты: только `README.md` (~140 строк, ASCII-дерево структуры репо) без frontmatter и без модульных файлов. Полный контракт (модульные файлы per module) никогда не соблюдался — aspirational документ, не enforcement. `pipeline-status.py:check_docs` (DOCS phase) валидировал handoff (4 секции), ADR (filename pattern) и docs-reviewer comment (heading `## Docs Review Summary`), но **не валидировал `docs/project-map/`**. Это позволяло PR проходить DOCS-фазу даже при «тихом провале» docs-reviewer (забыл обновить карту при структурных изменениях) — детерминированный guard отсутствовал. Зафиксировано в memory `technical/pipeline-status-no-project-map-check.md`. ## Решение Минимальный guard в `pipeline-status.py` — новая функция `check_project_map()` проверяет существование `docs/project-map/README.md` (через `PROJECT_MAP_DIR.exists()` + `README.md.exists()`). Встроена в `check_docs` между `check_adr` и `_check_docs_reviewer_comment` (short-circuit на NOT_DONE). Mandatory для pipeline-driven репо: docs-reviewer автосоздаёт initial README.md при отсутствии каталога (`docs-reviewer.md:82-84`), поэтому guard не блокирует первый PR и не требует ручной bootstrap-операции. Контракт ослаблен под фактическое состояние: - **README.md обязателен** — единственное, что проверяется детерминированно. Возвращает DONE с размером файла в detail, либо NOT_DONE с указанием что docs-reviewer должен создать (с ссылками на строки docs-reviewer.md). - **Модульные файлы per module и frontmatter `last_updated`** — aspirational, НЕ enforced. Полный контракт остаётся в `get-project-map/SKILL.md` как рекомендация для docs-reviewer, но не блокирует пайплайн. Возвращает только DONE/NOT_DONE (без AMBIGUOUS) — проверка файловой системы детерминирована, нет внешних API вызовов. ## Альтернативы - **Полный контракт (модульные файлы per module)** — отклонено: потребовало бы создания ~12 новых файлов (по одному на модуль) для существующего репо, блокируя пайплайн до их создания. Фактическое состояние никогда не соответствовало этому контракту — enforcement сломал бы все открытые PR. - **Опциональность через env-flag `OPENCODE_REQUIRE_PROJECT_MAP`** — отклонено: docs-reviewer уже автосоздаёт initial README.md при отсутствии каталога (`docs-reviewer.md:82-84`), feature-flag избыточен — guard никогда не блокирует корректно настроенный pipeline-driven репо. Env-flag добавил бы конфигурационную поверхность без выгоды. - **Diff-based проверка (карта обновлена в PR через `gh pr view --json files`)** — отклонено: сложнее в реализации (парсинг diff, фильтр по путям), не гарантирует валидность содержимого (файл может быть изменён, но остаться пустым/устаревшим). Существование README.md — более сильный guard (файл присутствует), diff-based — слабее (файл изменён, но может быть невалиден).