fix(reaper): Tier-2 finalization grace + claim-before-act (no dup advance)
Tier-2 reaped a LIVE, still-finalizing monitor: _monitor_agent writes agent_runs.exit_code FIRST, then does git push / PR / Plane comments before _finalize_job, and the agent pid is already dead in that window — so the old "exit_code recorded -> reap now" had no grace and could race a healthy job. Worse, _reap_known_outcome ran the advance (advance_stage -> enqueue_job) BEFORE the atomic claim, so a reaper that lost the race had already enqueued the next stage (dup advance / dup enqueue), violating ADR-001 Р-1. Fix: - Tier-2 grace: reap only once agent_runs.exit_code has been recorded for >= reaper_finalize_grace_s (new setting, default 300s; > max finalization window). A live finalizing monitor is never reaped (FR-1.3/AC-3). New finished_age_s column computed in get_running_jobs. - claim-before-act for exit0: evaluate the canonical QG READ-ONLY (the reconciler pattern) to choose the terminal status, then atomically claim 'done' FIRST; only the claim winner runs the advance. A loser performs no side effects -> no dup advance / dup enqueue. Docs (golden source) updated in the same change: ADR-001, global adr-0011, README, internals, .env.example, CHANGELOG (also fixes the P3 broken adr-0011 link). New tests cover the grace window, lost-claim no-side-effects, and the already-advanced idempotent path. Refs: ORCH-065 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -207,14 +207,19 @@ ORCH-065 вводит фоновый watchdog, чтобы смерть проц
|
||||
работает **без рестарта**. Трёхуровневая liveness: Tier-1 мёртвый `jobs.pid`
|
||||
(новая колонка) после `reaper_dead_ticks` подряд тиков (анти-ложноположительность
|
||||
— живой долгий агент не реапится); Tier-2 `agent_runs.exit_code` записан, а job
|
||||
ещё `running` (monitor умер между записью exit_code и финализацией); Tier-3
|
||||
backstop по потолку `reaper_max_running_s` (> max agent_timeout+grace). Действие
|
||||
переиспользует контракты: exit0 → **gate-driven idempotent advance**
|
||||
(`_try_advance_stage`+`_finalize_job`, источник истины — канонический QG, не
|
||||
факт «exit0»; нет дубль-перехода); exit≠0/неизвестно → `attempts<max`→`queued`,
|
||||
иначе `failed`+Telegram. Атомарный reap-claim (`UPDATE ... WHERE id=? AND
|
||||
status='running'`) совместим со стартовым `requeue_running_jobs` (restart-safe,
|
||||
без двойной обработки).
|
||||
ещё `running` — но это окно неоднозначно (живой monitor пишет exit_code ПЕРВЫМ,
|
||||
затем git push/PR/Plane-комментарии), поэтому Tier-2 реапит только после
|
||||
finalization-grace `reaper_finalize_grace_s` (живой финализирующий monitor НЕ
|
||||
реапится); Tier-3 backstop по потолку `reaper_max_running_s` (> max
|
||||
agent_timeout+grace). Действие переиспользует контракты по принципу
|
||||
**claim-before-act**: для exit0 канонический QG оценивается read-only ПЕРЕД
|
||||
атомарным claim, затем claim `done` ПЕРВЫМ и только победитель claim делает
|
||||
`_try_advance_stage` (advance+enqueue) — проигравший claim (поздний monitor /
|
||||
стартовый requeue) не выполняет побочных эффектов (нет дубль-advance/-enqueue);
|
||||
источник истины — канонический QG, не факт «exit0»; гейт красный или exit≠0/
|
||||
неизвестно → `attempts<max`→`queued`, иначе `failed`+Telegram. Атомарный
|
||||
reap-claim (`UPDATE ... WHERE id=? AND status='running'`) совместим со стартовым
|
||||
`requeue_running_jobs` (restart-safe, без двойной обработки).
|
||||
- **Проактивный реклейм stale/dead lease** (функции в `merge_gate.py`:
|
||||
`pid_alive`, `reclaim_stale_lease`) — на старте (рядом с `requeue_running_jobs`)
|
||||
и периодически из тика reaper: освобождает lease, чей держатель **мёртв** (pid
|
||||
@@ -238,7 +243,8 @@ ORCH-065 вводит фоновый watchdog, чтобы смерть проц
|
||||
`logger.warning`; reap→`failed` и lease-reclaim → Telegram.
|
||||
- **Kill-switch'и:** `ORCH_REAPER_ENABLED`, `ORCH_REAPER_INTERVAL_S`,
|
||||
`ORCH_REAPER_DEAD_TICKS`, `ORCH_REAPER_MAX_RUNNING_S`,
|
||||
`ORCH_LEASE_RECLAIM_ENABLED`; `false` → строго прежнее поведение.
|
||||
`ORCH_REAPER_FINALIZE_GRACE_S`, `ORCH_LEASE_RECLAIM_ENABLED`; `false` → строго
|
||||
прежнее поведение.
|
||||
|
||||
Подробнее: [adr-0011](adr/adr-0011-job-reaper-lease-reclaim.md), детально —
|
||||
`docs/work-items/ORCH-065/06-adr/ADR-001-job-reaper-and-lease-reclaim.md`.
|
||||
|
||||
Reference in New Issue
Block a user