--- pr: 122 title: fix(pipeline-status): use statusCheckRollup instead of actions/runs CI filter --- ## Что сделано - **CI-проверка переведена с `gh api .../actions/runs` + `name=="CI"` фильтра на `gh pr view --json statusCheckRollup`** в `.opencode/scripts/pipeline-status.py`. `statusCheckRollup` агрегирует ВСЕ checks по head SHA PR (CI, CI (always), ADR check и т.д.) — не зависит от имени workflow. - **Убраны функции**: `_get_pr_head_branch`, `_query_ci_run`, `_classify_ci_status`, `_poll_until_done`, `_retry_no_runs`, `_extract_json_field_loose` (использовалась только в CI-фазе). - **Добавлены функции**: `_query_ci_rollup` (запрос rollup), `_classify_rollup` (классификация статусов/conclusions), `_classify_rollup_with_poll` (polling при IN_PROGRESS/QUEUED/PENDING), `_retry_no_checks` (ререй при пустом rollup). - **Тесты переписаны** (`tests/test_pipeline_status_ci.py`): 23 теста под statusCheckRollup, включая ключевой `test_ci_docs_only_pr` (CI (always) + ADR check без ci.yml — regression guard для PR#120 bug). Mock-фабрика `mock_run_cmd_seq` теперь принимает `pr_view_responses` вместо `api_responses`. - **Docstring модуля** обновлён: CI-фаза описана через statusCheckRollup. - **Handoff + ADR-054** созданы. Проверки: `ruff check` OK; `ruff format --check` OK (40 files); `pytest tests/test_pipeline_status_ci.py tests/test_pipeline_status.py` 107 passed; `check-permissions.py` OK. ## Почему Docs-only PR (PR#120) не запускают `ci.yml` (paths-ignore), только `always-ci.yml`. Старый код фильтровал workflow runs по `name=="CI"` → не находил `CI (always)` → возвращал AMBIGUOUS ("нет CI run на ветке ..."). Pipeline застревал на CI-фазе. `statusCheckRollup` агрегирует все checks по head SHA — не зависит от имени workflow, корректно обрабатывает docs-only PR. ## Pending — ## Watch out - `_extract_json_field_loose` убрана — использовалась ТОЛЬКО в CI-фазе. `extract_json_field` (без `_loose`) оставлена — используется в `check_merge` и `get_pr_title`. - `JSON_FIELD_RE` (модульная константа) не используется нигде — оставлена как есть (не входит в scope issue #121, была не используется и до PR). - `_classify_rollup` считает `SKIPPED` и `NEUTRAL` conclusions успешными (вместе с `SUCCESS`) — GitHub помечает skipped jobs как SKIPPED, это не failure. - `_classify_rollup` использует regex для извлечения `status`/`conclusion` из JSON (без `json` импорта) — стиль скрипта (см. `extract_json_field`). Если в JSON появятся поля с такими же именами вне `statusCheckRollup` — возможны ложные matchи; но `gh pr view --json statusCheckRollup` возвращает только rollup, так что на практике безопасно. - Polling сообщение изменилось: было `gh run view --branch {head_branch}`, стало `gh pr checks {pr_number}`.