* feat(pipeline): add check_project_map to DOCS phase * test(pipeline): add check_project_map unit tests + update check_docs tests * docs(adr): add ADR-028 project map contract * docs(project-map): mention check_project_map guard in Update Protocol * docs(handoff): add PR handoff for check_project_map * docs(handoff): set PR number 67 * docs(review): fix PR#66 to PR#67 typo in project map and handoff * fix(ci): ruff lint errors in pipeline-status and project map test --------- Co-authored-by: opencode-agent <agent@opencode.local>
95 lines
No EOL
6.6 KiB
Markdown
95 lines
No EOL
6.6 KiB
Markdown
---
|
||
pr: 67
|
||
title: feat(pipeline): add check_project_map to DOCS phase
|
||
---
|
||
|
||
## Что сделано
|
||
|
||
- **`.opencode/scripts/pipeline-status.py`** — 3 точечные правки:
|
||
- Константа `PROJECT_MAP_DIR = REPO_ROOT / "docs" / "project-map"` (после
|
||
`ADR_DIR`, строка 54).
|
||
- Функция `check_project_map()` (после `check_adr`) — проверяет
|
||
`PROJECT_MAP_DIR.exists()` + `README.md.exists()`, возвращает
|
||
DONE/NOT_DONE (без AMBIGUOUS, чисто файловая система). Шаблон по образцу
|
||
`check_adr`.
|
||
- В `check_docs` вставлен вызов между `check_adr` и
|
||
`_check_docs_reviewer_comment` (short-circuit на NOT_DONE). Docstring
|
||
`check_docs` обновлён (теперь 5 детерминированных проверок).
|
||
- **`tests/test_pipeline_status_project_map.py`** — новый файл, 3 unit-теста
|
||
(по образцу `test_pipeline_status_adr.py`):
|
||
- `test_project_map_exists` — README.md есть → DONE, "README.md" в detail.
|
||
- `test_project_map_dir_missing` — директория отсутствует → NOT_DONE,
|
||
"директория" в detail.
|
||
- `test_project_map_readme_missing` — директория есть, README.md нет →
|
||
NOT_DONE, "README.md не найден" в detail.
|
||
Загрузка скрипта под уникальным module name `pipeline_status_project_map`
|
||
(не коллидирует с `pipeline_status` из других тестов).
|
||
- **`tests/test_pipeline_status.py`** — критическая правка существующих
|
||
тестов:
|
||
- Добавлен helper `make_project_map(tmp_path)` рядом с `make_handoff` —
|
||
создаёт `tmp_path/project-map/README.md`.
|
||
- Во все 10 `test_check_docs_*` тестов добавлен
|
||
`monkeypatch.setattr(ps, "PROJECT_MAP_DIR", make_project_map(tmp_path))`.
|
||
Без этого новая под-проверка возвращала NOT_DONE и ломала assertions
|
||
(7 тестов с валидным ADR доходили до check_project_map).
|
||
- **ADR-028** (`docs/decisions/028-pr-67-project-map-contract.md`) —
|
||
фиксирует контракт project-map: README.md обязателен (mandatory, enforced),
|
||
модульные файлы и frontmatter `last_updated` — aspirational (не enforced).
|
||
Альтернативы: полный контракт (отклонено — блокирует пайплайн, ~12 файлов),
|
||
env-flag (отклонено — docs-reviewer автосоздаёт), diff-based (отклонено —
|
||
сложнее, не гарантирует валидность).
|
||
- **`docs/project-map/README.md`** — секция Update Protocol дополнена строкой
|
||
про новый guard; tree обновлён (новый тест-файл + тег PR#67 на
|
||
pipeline-status.py).
|
||
|
||
## Почему
|
||
|
||
Детерминированный guard для project-map отсутствовал —
|
||
`pipeline-status.py:check_docs` проверял handoff, ADR и docs-reviewer
|
||
comment, но не валидировал `docs/project-map/`. Это позволяло PR проходить
|
||
DOCS-фазу даже при «тихом провале» docs-reviewer (забыл обновить карту при
|
||
структурных изменениях). Memory `technical/pipeline-status-no-project-map-check.md`
|
||
зафиксировала эту дыру.
|
||
|
||
Контракт карты был размазан по 3 файлам (`docs-reviewer.md`,
|
||
`get-project-map/SKILL.md`, `reviewer.md`) с несоответствиями (2 разных commit
|
||
message для одной операции). Фактическое состояние — только README.md без
|
||
frontmatter и модульных файлов. ADR-028 ослабляет контракт под фактическое
|
||
состояние (README.md mandatory, модульные файлы aspirational) и фиксирует
|
||
единый source of truth. Полный контракт остаётся в `get-project-map/SKILL.md`
|
||
как рекомендация.
|
||
|
||
Локальная верификация: 404 теста прошли, coverage 89.19% ≥ 80%
|
||
(pipeline-status.py покрыт на 88%).
|
||
|
||
## Pending
|
||
|
||
- После merge: фаза MEMORY — обновить memory
|
||
`technical/pipeline-status-no-project-map-check.md` секцией Closure (guard
|
||
добавлен в PR#67, контракт зафиксирован в ADR-028).
|
||
- ADR filename переименован с placeholder `<PR-NUMBER>` на `028-pr-67-project-map-contract.md`
|
||
после create_pr (коммит `docs(handoff): set PR number`).
|
||
|
||
## Watch out
|
||
|
||
- **10 существующих тестов правлены** — `test_check_docs_*` в
|
||
`test_pipeline_status.py` теперь требуют `make_project_map(tmp_path)` +
|
||
monkeypatch `PROJECT_MAP_DIR`. Если добавить новый `test_check_docs_*` без
|
||
этого — упадёт на NOT_DONE от `check_project_map`. Паттерн: копируй строку
|
||
`monkeypatch.setattr(ps, "PROJECT_MAP_DIR", make_project_map(tmp_path))` из
|
||
соседнего теста.
|
||
- **Уникальный module name в новом тесте** — `pipeline_status_project_map`
|
||
(не `pipeline_status`). Если использовать `pipeline_status`, произойдёт
|
||
коллизия с `test_pipeline_status.py`/`test_pipeline_status_adr.py` которые
|
||
грузят скрипт под тем же именем — `sys.modules` уже занят, тесты
|
||
конфликтуют. Шаблон: новый test-файл для под-проверки → уникальный module
|
||
name.
|
||
- **Контракт ослаблен намеренно** — ADR-028 явно фиксирует что модульные
|
||
файлы и frontmatter `last_updated` НЕ enforced. Если будущий PR захочет
|
||
ужесточить контракт (полный enforcement) — это будет breaking change,
|
||
требующий создания ~12 файлов для существующего репо. См. альтернативу в
|
||
ADR-028.
|
||
- **scaffold-handoff.sh нумерация** — скрипт считает `ls docs/decisions |
|
||
grep -E '^[0-9]{3}-' | wc -l + 1`. Если ADR-028 уже существует, следующий
|
||
вызов даст 029. При создании ADR вручную + через scaffold в одном PR —
|
||
scaffold создаст дубликат с +1 номером (нужно удалить вручную). |