feat(pipeline-oracle): issue-number input, bootstrap NEXT, issues dashboard #80

Merged
slaid098 merged 4 commits from feat/pipeline-oracle/issue-input into main 2026-08-16 19:04:47 +03:00
Owner

Что сделано

  • Оракул pipeline-status принимает любой номер: PR или issue. Резолюция детерминированная: GET /pulls/{N} → 200 PR-путь; 404 → GET /issues/{N} → поиск открытых PR с Closes|Fixes|Resolves #N (ровно 1 → issue #N → PR #M, 0 → bootstrap, >1 → AMBIGUOUS со списком); ни PR ни issue → stderr «Не существует ни PR, ни issue с номером N», exit 1.
  • Bootstrap-вывод: 6 фаз ❌ + NEXT: dispatch subagent (subagent_type=general, template=A) for issue #N, exit 0.
  • NEXT_ACTIONS переписаны: явные плейсхолдеры типа номера (issue #N для bootstrap, PR #N для остальных), ISSUE-фаза при существующем PR — инструкция дописать Closes #<issue> в body (без template=A), CI-NEXT — subagent_type=general, template=D + логи через curl /actions/runs/{id}/jobs + /actions/jobs/{id}/logs, убрано упоминание _forgejo_run_view().
  • snake_case → kebab-case во всех строках NEXT/ошибок: merge-pr, pipeline-status.
  • Таймауты к единообразию: description tool'а «up to 7 min», wrapper timeout: 480000 (8 мин запас), текст ошибки без «60s».
  • .opencode/tools/pipeline-status.ts: pr_number опционален; вызов без аргумента → дашборд открытых issues (#N <title> — PR #M | —), заменивший таблицу открытых PR.
  • Docstring'и фаз перенумерованы на реальные 1..6.

Почему

  • Оракул падал на номере issue (PR #N не существует, exit 1), ломая bootstrap пайплайна — пайплайн стартует с номера issue, а PR ещё не создан.
  • Аудит зафиксировал сопутствующие дефекты: путаница плейсхолдеров N, CI-NEXT с невызываемой приватной функцией, snake_case имена инструментов (рецидив ADR-034), рассинхронизированные таймауты, недосягаемый режим дашборда через tool.

Watch out

  • Закрытые/слитые PR в резолюции не участвуют (только state=open; дополнительный фильтр merged на всякий случай).
  • Пагинация открытых PR: сканирование до пустой страницы (без эвристики по размеру батча — Forgejo может вернуть меньше 50 на неполной странице).
  • ISSUE-фаза без известного issue-номера (вход PR-номером) оставляет плейсхолдер <issue> в тексте NEXT.
  • Моки пагинации в тестах имитируют терминальную пустую страницу — это реальное поведение Forgejo API.

Pending

  • Синхронизация run-pipeline SKILL.md с новыми NEXT-строками — отдельным issue (зависимость по спеке #74).

Closes #74

## Что сделано - Оракул `pipeline-status` принимает любой номер: PR или issue. Резолюция детерминированная: `GET /pulls/{N}` → 200 PR-путь; 404 → `GET /issues/{N}` → поиск открытых PR с `Closes|Fixes|Resolves #N` (ровно 1 → `issue #N → PR #M`, 0 → bootstrap, >1 → AMBIGUOUS со списком); ни PR ни issue → stderr «Не существует ни PR, ни issue с номером N», exit 1. - Bootstrap-вывод: 6 фаз ❌ + `NEXT: dispatch subagent (subagent_type=general, template=A) for issue #N`, exit 0. - `NEXT_ACTIONS` переписаны: явные плейсхолдеры типа номера (`issue #N` для bootstrap, `PR #N` для остальных), ISSUE-фаза при существующем PR — инструкция дописать `Closes #<issue>` в body (без template=A), CI-NEXT — `subagent_type=general, template=D` + логи через curl `/actions/runs/{id}/jobs` + `/actions/jobs/{id}/logs`, убрано упоминание `_forgejo_run_view()`. - snake_case → kebab-case во всех строках NEXT/ошибок: `merge-pr`, `pipeline-status`. - Таймауты к единообразию: description tool'а «up to 7 min», wrapper `timeout: 480000` (8 мин запас), текст ошибки без «60s». - `.opencode/tools/pipeline-status.ts`: `pr_number` опционален; вызов без аргумента → дашборд открытых issues (`#N <title> — PR #M | —`), заменивший таблицу открытых PR. - Docstring'и фаз перенумерованы на реальные 1..6. ## Почему - Оракул падал на номере issue (`PR #N не существует`, exit 1), ломая bootstrap пайплайна — пайплайн стартует с номера issue, а PR ещё не создан. - Аудит зафиксировал сопутствующие дефекты: путаница плейсхолдеров N, CI-NEXT с невызываемой приватной функцией, snake_case имена инструментов (рецидив ADR-034), рассинхронизированные таймауты, недосягаемый режим дашборда через tool. ## Watch out - Закрытые/слитые PR в резолюции не участвуют (только `state=open`; дополнительный фильтр `merged` на всякий случай). - Пагинация открытых PR: сканирование до пустой страницы (без эвристики по размеру батча — Forgejo может вернуть меньше 50 на неполной странице). - ISSUE-фаза без известного issue-номера (вход PR-номером) оставляет плейсхолдер `<issue>` в тексте NEXT. - Моки пагинации в тестах имитируют терминальную пустую страницу — это реальное поведение Forgejo API. ## Pending - Синхронизация run-pipeline SKILL.md с новыми NEXT-строками — отдельным issue (зависимость по спеке #74). Closes #74
test(pipeline-oracle): update tool tests for no-arg and kebab-case
All checks were successful
CI (always) / bootstrap (pull_request) Successful in 4s
CI / bootstrap (pull_request) Successful in 6s
CI / typecheck (pull_request) Successful in 30s
CI / complexity (pull_request) Successful in 30s
CI / lint (pull_request) Successful in 30s
CI / test (3.13) (pull_request) Successful in 1m40s
bf24b70494
Author
Owner

Code Review Summary

Оракул pipeline-status теперь принимает номер issue (детерминированная резолюция PR/issue/bootstrap/ambiguous/ghost), поддерживает bootstrap-вывод и дашборд открытых issues без аргумента. Изменения хорошо структурированы, покрыты ~370 строками новых тестов (все ветки резолюции, пагинация, исключение merged-PR, дашборд, no-arg путь tool'а), CI зелёный. PR body полный (4 heading'а), Watch out документирует edge cases, Pending — отложенную синхронизацию run-pipeline SKILL.md.

Positives

  • Детерминированная резолюция номера: GET /pulls/{N} → 404 → GET /issues/{N} → поиск открытых PR по Closes/Fixes/Resolves — ровно 1 → issue-путь, 0 → bootstrap, >1 → AMBIGUOUS, ни одного → ghost exit 1. Все ветки покрыты тестами.
  • find_open_prs_for_issue сканирует до пустой страницы (без эвристики по размеру батча) — поведение Forgejo API учтено и задокументировано в Watch out.
  • NEXT_ACTIONS приведены к kebab-case (merge-pr, pipeline-status), CI-NEXT теперь ссылается на реальные API-эндпоинты (/actions/runs/{id}/jobs + /actions/jobs/{id}/logs) вместо невызываемой _forgejo_run_view().
  • Удалённые функции (pr_exists, list_open_pr_numbers, format_table, format_pr_row) не имеют оставшихся readers — rg по репо чист.
  • Tool wrapper: pr_number опционален, no-arg → дашборд; _ts_loader.mjs корректно маппит NaN → {}.
  • Таймауты синхронизированы: description «up to 7 min» ↔ CI_WAIT_TIMEOUT=420 ↔ wrapper timeout: 480000 (8 мин запас).
  • Ошибки осмысленные и на stderr с exit 1 (ghost/error), bootstrap и AMBIGUOUS — exit 0 с понятным выводом.

Suggestions (info, not blocking)

  • pipeline-status.py:352 [perf] _forgejo_issue_list — одностраничный запрос (limit=50 без пагинации), тогда как _forgejo_pr_dispatch list пагинирован. При >50 открытых issues дашборд обрежется. Для текущего репо (5 issues) неактуально, но стоит унифицировать.
  • pipeline-status.py:1138 [perf] format_issues_dashboard делает N+1 API-сканов: для каждого issue вызывается find_open_prs_for_issue (полный пейджинг всех открытых PR). При росте числа issues это O(issues × PR-страниц) запросов. Можно один раз собрать все открытые PR и матчить по issue.
  • pipeline-status.py:1075-1076 [style] CLOSURE_RE.search(body) избыточен: если refs непуст, search заведомо совпадёт; если пуст — in refs и так False. Достаточно if int(issue_number) in refs.
  • run-pipeline/SKILL.md:28 [doc] «блокирует до 5 мин» рассинхронизировано с новым «7 min» в tool description — отложенная синхронизация зафиксирована в Pending, ок.
  • pipeline-status.py [style] Файл 1185 строк — пре-существующий монолит, PR добавляет ~260 строк; декомпозиция (например, вынос резолюции в отдельный модуль) — на будущее, не блокирует.

Verdict: APPROVE

## Code Review Summary Оракул `pipeline-status` теперь принимает номер issue (детерминированная резолюция PR/issue/bootstrap/ambiguous/ghost), поддерживает bootstrap-вывод и дашборд открытых issues без аргумента. Изменения хорошо структурированы, покрыты ~370 строками новых тестов (все ветки резолюции, пагинация, исключение merged-PR, дашборд, no-arg путь tool'а), CI зелёный. PR body полный (4 heading'а), Watch out документирует edge cases, Pending — отложенную синхронизацию run-pipeline SKILL.md. ### Positives - Детерминированная резолюция номера: `GET /pulls/{N}` → 404 → `GET /issues/{N}` → поиск открытых PR по Closes/Fixes/Resolves — ровно 1 → issue-путь, 0 → bootstrap, >1 → AMBIGUOUS, ни одного → ghost exit 1. Все ветки покрыты тестами. - `find_open_prs_for_issue` сканирует до пустой страницы (без эвристики по размеру батча) — поведение Forgejo API учтено и задокументировано в Watch out. - NEXT_ACTIONS приведены к kebab-case (`merge-pr`, `pipeline-status`), CI-NEXT теперь ссылается на реальные API-эндпоинты (`/actions/runs/{id}/jobs` + `/actions/jobs/{id}/logs`) вместо невызываемой `_forgejo_run_view()`. - Удалённые функции (`pr_exists`, `list_open_pr_numbers`, `format_table`, `format_pr_row`) не имеют оставшихся readers — `rg` по репо чист. - Tool wrapper: `pr_number` опционален, no-arg → дашборд; `_ts_loader.mjs` корректно маппит NaN → `{}`. - Таймауты синхронизированы: description «up to 7 min» ↔ `CI_WAIT_TIMEOUT=420` ↔ wrapper `timeout: 480000` (8 мин запас). - Ошибки осмысленные и на stderr с exit 1 (ghost/error), bootstrap и AMBIGUOUS — exit 0 с понятным выводом. ### Suggestions (info, not blocking) - **pipeline-status.py:352** [perf] `_forgejo_issue_list` — одностраничный запрос (`limit=50` без пагинации), тогда как `_forgejo_pr_dispatch` list пагинирован. При >50 открытых issues дашборд обрежется. Для текущего репо (5 issues) неактуально, но стоит унифицировать. - **pipeline-status.py:1138** [perf] `format_issues_dashboard` делает N+1 API-сканов: для каждого issue вызывается `find_open_prs_for_issue` (полный пейджинг всех открытых PR). При росте числа issues это O(issues × PR-страниц) запросов. Можно один раз собрать все открытые PR и матчить по issue. - **pipeline-status.py:1075-1076** [style] `CLOSURE_RE.search(body)` избыточен: если `refs` непуст, search заведомо совпадёт; если пуст — `in refs` и так False. Достаточно `if int(issue_number) in refs`. - **run-pipeline/SKILL.md:28** [doc] «блокирует до 5 мин» рассинхронизировано с новым «7 min» в tool description — отложенная синхронизация зафиксирована в Pending, ок. - **pipeline-status.py** [style] Файл 1185 строк — пре-существующий монолит, PR добавляет ~260 строк; декомпозиция (например, вынос резолюции в отдельный модуль) — на будущее, не блокирует. ### Verdict: APPROVE
slaid098 deleted branch feat/pipeline-oracle/issue-input 2026-08-16 19:04:47 +03:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
slaid098/opencode-config!80
No description provided.