auto-sync: 2026-06-04 01:40:01
This commit is contained in:
93
tasks/orchestrator/DEV_TASK_CI_FAIL_RETRY.md
Normal file
93
tasks/orchestrator/DEV_TASK_CI_FAIL_RETRY.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# DEV TASK — БАГ 7: красный CI на development должен авто-возвращать задачу developer'у (retry, как review)
|
||||
|
||||
**Проект:** orchestrator | **Сервер:** slin@82.22.50.71 | **Репо:** /home/slin/repos/orchestrator | **Контейнер:** orchestrator (8500)
|
||||
**Ветка:** `fix/ci-fail-retry-developer` из свежего main (`git checkout main && git pull && git checkout -b fix/ci-fail-retry-developer`). main = `7922f6b` (PR #17).
|
||||
|
||||
⚠️ **ГРАБЛЯ push (ORCH-7):** после push `git log origin/main..origin/fix/ci-fail-retry-developer` ДОЛЖЕН показать коммит ДО отчёта «PR готов». Токен Gitea: `docker exec orchestrator printenv ORCH_GITEA_TOKEN`.
|
||||
|
||||
## Контекст / дыра
|
||||
После бага 6 (PR #17) CI стал авторитетным гейтом стадии development. Но при **красном CI** наш код (gitea.py `handle_ci_status`, ветка `elif state == "failure" and current_stage == "development":`) делает ТОЛЬКО `notify_qg_failure` — уведомляет и встаёт. **Никто не перезапускает developer'а** → конвейер виснет, требует ручного вмешательства.
|
||||
|
||||
Для review такая ветка УЖЕ есть (request_changes → relaunch developer, max 3x). Нужно сделать **симметрично** для CI-failure.
|
||||
|
||||
## Образец (УЖЕ в коде — копируй его логику) — `handle_pr`, ветка REQUEST_CHANGES, ~стр.289-314:
|
||||
```python
|
||||
elif review_state == "REQUEST_CHANGES" and current_stage == "review":
|
||||
conn = get_db()
|
||||
retry_count = conn.execute(
|
||||
"SELECT COUNT(*) as cnt FROM agent_runs WHERE task_id = ? AND agent = 'developer'",
|
||||
(task_id,),
|
||||
).fetchone()["cnt"]
|
||||
conn.close()
|
||||
if retry_count < MAX_DEV_RETRIES:
|
||||
update_task_stage(task_id, "development")
|
||||
notify_stage_change(task_id, current_stage, "development")
|
||||
try:
|
||||
task_desc = (... attempt {retry_count + 1}/{MAX_DEV_RETRIES} ...)
|
||||
job_id = enqueue_job("developer", repo_name, task_desc, task_id=task_id)
|
||||
logger.info(...)
|
||||
except Exception as e:
|
||||
notify_error(task_id, f"Failed to relaunch developer: {e}")
|
||||
else:
|
||||
notify_error(task_id, f"Max developer retries ({MAX_DEV_RETRIES}) reached, escalating")
|
||||
logger.error(...)
|
||||
```
|
||||
`MAX_DEV_RETRIES = 3` (gitea.py:33). Импорты `enqueue_job`, `update_task_stage`, `notify_stage_change`, `notify_error`, `notify_qg_failure`, `get_db` УЖЕ есть в файле — не дублируй.
|
||||
|
||||
## Фикс — переписать CI-failure ветку в `handle_ci_status` (~стр.219-222)
|
||||
БЫЛО:
|
||||
```python
|
||||
elif state == "failure" and current_stage == "development":
|
||||
# CI is now the authoritative gate for development -> review.
|
||||
# A failing CI means the QG did not pass; notify (do not silently advance).
|
||||
notify_qg_failure(task_id, current_stage, "check_ci_green", f"Gitea CI failed on branch '{branch}'")
|
||||
```
|
||||
СТАЛО (уведомить О ПРОВАЛЕ + авто-retry developer'а с тем же лимитом):
|
||||
```python
|
||||
elif state == "failure" and current_stage == "development":
|
||||
# CI is the authoritative gate for development -> review.
|
||||
# On red CI: notify, then bounce the task back to the developer (capped retries),
|
||||
# symmetric to the review REQUEST_CHANGES path.
|
||||
notify_qg_failure(task_id, current_stage, "check_ci_green", f"Gitea CI failed on branch '{branch}'")
|
||||
conn = get_db()
|
||||
retry_count = conn.execute(
|
||||
"SELECT COUNT(*) as cnt FROM agent_runs WHERE task_id = ? AND agent = 'developer'",
|
||||
(task_id,),
|
||||
).fetchone()["cnt"]
|
||||
conn.close()
|
||||
if retry_count < MAX_DEV_RETRIES:
|
||||
# task already on 'development' — no stage change needed, just relaunch developer
|
||||
try:
|
||||
task_desc = (
|
||||
f"Work item: {work_item_id}\nRepo: {repo_name}\nBranch: {branch}\n"
|
||||
f"Stage: development\nNote: CI failed, fix and re-push (attempt {retry_count + 1}/{MAX_DEV_RETRIES})"
|
||||
)
|
||||
job_id = enqueue_job("developer", repo_name, task_desc, task_id=task_id)
|
||||
logger.info(f"Task {task_id}: CI failed, enqueued developer (attempt {retry_count + 1}, job_id={job_id})")
|
||||
except Exception as e:
|
||||
notify_error(task_id, f"Failed to relaunch developer after CI failure: {e}")
|
||||
else:
|
||||
notify_error(task_id, f"Max developer retries ({MAX_DEV_RETRIES}) reached after CI failure, escalating")
|
||||
logger.error(f"Task {task_id}: max retries reached after CI failure, needs manual intervention")
|
||||
```
|
||||
**Ключевые отличия от review-ветки (учти!):**
|
||||
- Задача УЖЕ в `development` → НЕ зови `update_task_stage`/`notify_stage_change` (в review был переход review→development, здесь перехода нет).
|
||||
- branch берётся из переменной `branch` (не `head_branch`).
|
||||
- Сохрани `notify_qg_failure` ПЕРЕД retry (чтобы Слава видел и провал, и факт ретрая).
|
||||
|
||||
## Ограничения
|
||||
- 🚫 НЕ трогай: review-retry ветку (образец оставь как есть), check_ci_green success-ветку, HMAC/project-filter, stages.py, qg/checks.py, stage_engine, nginx, openclaw.json, .env, deploy, очередь (enqueue_job не меняй), PLANE_STATES, conftest.py, status-only, gitea_public_url.
|
||||
- 🚫 retry_count считается по ВСЕМ agent_runs developer'а задачи — это ОБЩИЙ лимит на developer (review-retry + CI-retry вместе ≤ MAX_DEV_RETRIES). Так и надо — общий потолок на доработки developer'а. НЕ заводи отдельный счётчик.
|
||||
- Conventional Commits, один коммит: `fix(ci): bounce task back to developer on red CI (capped retries)`.
|
||||
|
||||
## Тесты (контейнер)
|
||||
`IMG=$(docker inspect orchestrator --format '{{.Config.Image}}'); docker run --rm -v /home/slin/repos/orchestrator:/code -w /code --entrypoint python3 $IMG -m pytest tests/ -q`
|
||||
- Добавь/обнови в tests/test_webhooks.py (рядом с `test_gitea_ci_failure_on_development_notifies_qg_failure`):
|
||||
- CI failure на development при retry_count < 3 → enqueue_job("developer",...) ВЫЗВАН + notify_qg_failure вызван, stage остаётся "development".
|
||||
- CI failure при retry_count >= 3 → enqueue_job НЕ вызван, notify_error («Max developer retries... after CI failure») вызван.
|
||||
- (мокай get_db/agent_runs count и enqueue_job/notify_*). Эти тесты — pure-logic, НЕ через webhook-POST (чтобы не упереться в 401 HMAC-барьер baseline-9).
|
||||
- Baseline после PR #17: **215 passed + 10 failed** (9 baseline HMAC/401 + 1 webhook-POST тест на том же 401 — off-limits, не «чинить»). Итог passed ≥215 (+ твои новые pure-logic тесты). НЕ урони passed; новые webhook-тесты делай pure-logic (вызов handle_ci_status напрямую с моками), НЕ через TestClient POST.
|
||||
|
||||
## Отчёт
|
||||
- НЕ деплоить, НЕ мержить (мерж + боевой прогон делает ассистент: задеплою и дёрну CI-failure на ET-011 → должно вернуть developer'а, он починит 10× E402 в src/api/main.py и перезапушит → зелёный CI → development→review автономно).
|
||||
- В отчёте: точный старый/новый код CI-failure ветки, новые тесты с пояснением, полный вывод pytest (счётчик). Маленькая правка — не раздувай. Один исполнитель.
|
||||
Reference in New Issue
Block a user