architect(ET): auto-commit from architect run_id=317
All checks were successful
CI / test (push) Successful in 17s
All checks were successful
CI / test (push) Successful in 17s
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
- **Quality Gates** (`src/qg/checks.py`) — проверки выхода со стадии, реестр `QG_CHECKS`.
|
||||
- **Agent Launcher** (`src/agents/launcher.py`) — запуск Claude CLI агентов в изолированном git worktree, мониторинг, auto-advance.
|
||||
- **Queue** (`src/queue_worker.py`, ORCH-1) — персистентная очередь задач (SQLite `jobs`), atomic claim, max_concurrency, ретраи, restart-safe.
|
||||
- **Job-reaper** (`src/job_reaper.py`, ORCH-065 — [adr-0011](adr/adr-0011-job-reaper-lease-reclaim.md)) — фоновый daemon-поток (каркас `reconciler`), стартует/останавливается в `main.lifespan` (после `reconciler.start()` / перед `worker.stop()`). Детектирует «мёртвый» `running`-job **без рестарта** процесса (Tier-1 мёртвый `jobs.pid` после `reaper_dead_ticks` тиков; Tier-2 `agent_runs.exit_code` записан, а job ещё `running`; Tier-3 backstop `reaper_max_running_s`) и приводит строку к корректному статусу через те же контракты (`_try_advance_stage`/`_finalize_job`, gate-driven; exit≠0/неизвестно → `attempts<max`→`queued`, иначе `failed`+Telegram). Атомарный reap-claim (guard `status='running'`) совместим со стартовым `requeue_running_jobs`. Тот же поток периодически делает проактивный реклейм stale/dead merge-lease (см. ниже). never-raise; kill-switch `ORCH_REAPER_ENABLED`; снимок в `GET /queue` (блок `reaper`).
|
||||
- **Reconciler** (`src/reconciler.py`, ORCH-053 — реализовано, [adr-0007](adr/adr-0007-reconciler.md)) — фоновый daemon-поток (паттерн `queue_worker`), стартует/останавливается в `main.lifespan` (после `worker.start()` / перед `worker.stop()`). Реконсилирует рассинхрон «источник истины ≠ стадия задачи» при потерянном webhook. F-1 gate-side (продвигает застрявшую стадию по локальной БД через штатный `advance_stage(..., finished_agent=None)`), F-2 plane-side (опрос Plane API → `handle_*` из `plane.py`), F-3 (БД-fallback `sha→branch` в `handle_ci_status`). Источник истины — гейт/Plane, не событие; идемпотентность (active-job guard + atomic-claim + grace); kill-switch `ORCH_RECONCILE_ENABLED`. `analysis` F-1 не трогает (человеческий гейт). F-1 также пропускает escalated (retry≥лимита) и Blocked/Needs-Input задачи (ORCH-060). Наблюдаемость — блок `reconcile` в `GET /queue`.
|
||||
- **Project Registry** (`src/projects.py`, ORCH-6) — Plane project id → repo + prefix; фильтрация вебхуков по проекту.
|
||||
- **Plane Sync** (`src/plane_sync.py`) — синхронизация статусов/комментариев в Plane.
|
||||
@@ -190,6 +191,53 @@ never-raise на единицу работы; тишина при синхрон
|
||||
и реестры (`STAGE_TRANSITIONS`/`QG_CHECKS`) не меняются. Подробнее:
|
||||
[adr-0007](adr/adr-0007-reconciler.md), детально — `docs/work-items/ORCH-053/06-adr/ADR-001-stuck-task-reconciler.md`.
|
||||
|
||||
### Job-reaper + проактивный реклейм merge-lease (ORCH-065 — design)
|
||||
Финализация статуса job (`done`/`queued`/`failed`) выполняется ТОЛЬКО в
|
||||
`launcher._monitor_agent → _finalize_job` внутри живого процесса. Смерть
|
||||
monitor-потока/процесса между `proc.wait()` и `_finalize_job` (краш, OOM,
|
||||
self-restart во время deploy) оставляла строку `jobs` навсегда `running`; при
|
||||
`max_concurrency=1` одна зомби-строка блокирует claim всех job → встаёт конвейер
|
||||
ВСЕХ проектов (инциденты 07.06: jobs 236/239/242/254). `requeue_running_jobs()`
|
||||
спасал ТОЛЬКО на старте процесса. Симметрично залипал merge-lease (ORCH-043):
|
||||
реклейм был лениво-по-TTL и только при чужом `acquire`, liveness держателя по pid
|
||||
не проверялся. Это последняя ручная точка автономного self-deploy (блокер ORCH-54).
|
||||
ORCH-065 вводит фоновый watchdog, чтобы смерть процесса/потока на любой стадии НЕ
|
||||
оставляла навсегда захваченных ресурсов:
|
||||
- **Job-reaper** (`src/job_reaper.py`) — daemon-поток по образцу `reconciler`,
|
||||
работает **без рестарта**. Трёхуровневая 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,
|
||||
без двойной обработки).
|
||||
- **Проактивный реклейм stale/dead lease** (функции в `merge_gate.py`:
|
||||
`pid_alive`, `reclaim_stale_lease`) — на старте (рядом с `requeue_running_jobs`)
|
||||
и периодически из тика reaper: освобождает lease, чей держатель **мёртв** (pid
|
||||
не жив) ИЛИ **просрочен** (TTL `merge_lock_timeout_s`); живой держатель в
|
||||
пределах TTL — НЕ трогать (защита легитимного merge). holder-aware, never-raise,
|
||||
условность как ORCH-43 (`merge_gate_repos`/self-hosting).
|
||||
- **Идемпотентная финализация merge** — без новой merge-логики: re-drive через
|
||||
reaper→`queued`→переисполнение стадии / reconciler; дорогие шаги не повторяются
|
||||
(`branch_is_behind_main==False`); добавлен never-raise guard `pr_already_merged`
|
||||
(читает состояние PR) — уже слит = no-op.
|
||||
- **Схема БД:** единственное изменение — `jobs.pid INTEGER` через идемпотентный
|
||||
`_ensure_column` (live-safe). `STAGE_TRANSITIONS`, `QG_CHECKS`, `check_*`, БАГ-8,
|
||||
exit-коды хука, файл-схема lease — без изменений.
|
||||
- **Наблюдаемость:** блок `reaper` в `GET /queue` (enabled, interval, last_run_ts,
|
||||
reaped_total, last_reaped, lease_reclaimed_total); каждый reap/lease-reclaim →
|
||||
`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` → строго прежнее поведение.
|
||||
|
||||
Подробнее: [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`.
|
||||
|
||||
## Откаты
|
||||
- Reviewer REQUEST_CHANGES → откат на `development` + retry (`MAX_DEVELOPER_RETRIES = 3`).
|
||||
- Tester `check_tests_passed` FAIL → откат на `development` + retry.
|
||||
@@ -223,7 +271,7 @@ never-raise на единицу работы; тишина при синхрон
|
||||
- `events` — входящие вебхуки (дедуп)
|
||||
- `tasks` — задачи и их стадии
|
||||
- `agent_runs` — запуски агентов (run_id, usage, cost)
|
||||
- `jobs` — очередь задач (ORCH-1)
|
||||
- `jobs` — очередь задач (ORCH-1); колонка `pid` (ORCH-065) — pid агентского процесса для liveness-детекции зомби job-reaper'ом
|
||||
|
||||
## Изоляция (git worktree, ORCH-2)
|
||||
Каждая задача исполняется в отдельном git worktree, ветки не пересекаются. Репозитории проектов разделены под `/repos/<project>`.
|
||||
@@ -233,7 +281,7 @@ never-raise на единицу работы; тишина при синхрон
|
||||
|--------|------|----------|
|
||||
| GET | `/health` | health check |
|
||||
| GET | `/status` | активные задачи (stage != done) |
|
||||
| GET | `/queue` | очередь: counts + max_concurrency + resilience + reconcile (ORCH-053) + post_deploy (ORCH-021) + последние jobs |
|
||||
| GET | `/queue` | очередь: counts + max_concurrency + resilience + reconcile (ORCH-053) + reaper (ORCH-065) + post_deploy (ORCH-021) + последние jobs |
|
||||
| POST | `/webhook/plane` | Plane webhook |
|
||||
| POST | `/webhook/gitea` | Gitea webhook (push, PR, CI status) |
|
||||
|
||||
@@ -247,4 +295,4 @@ never-raise на единицу работы; тишина при синхрон
|
||||
Схема БД, потоки данных, resilience-слой, детали Dockerfile — [internals.md](internals.md).
|
||||
|
||||
---
|
||||
*Актуально на 2026-06-07. Обновлять при изменении src/stages.py, src/qg/checks.py, src/main.py. Статусы доработок: ORCH-036 (исполняемый самодеплой `deploy`, adr-0007) — реализовано; ORCH-043 (merge-gate, adr-0006) — design, ветка feature/ORCH-043; ORCH-053 (reconciler, adr-0007, src/reconciler.py) — реализовано; ORCH-060 (F-1 skip escalated/Blocked/Needs-Input, `docs/work-items/ORCH-060/06-adr/ADR-001`) — реализовано в ветке feature/ORCH-060 (Guard 1 `developer_retry_count>=MAX_DEVELOPER_RETRIES` + Guard 2 `plane_sync.fetch_issue_state` Blocked/Needs-Input, флаг `ORCH_RECONCILE_SKIP_BLOCKED_ENABLED`); ORCH-058 (провенанс staging-образа: check_staging_image_fresh + staging_check свежего образа + хук-guard, adr-0008) — реализовано в ветке feature/ORCH-058 (обновлять также при изменении src/image_freshness.py, scripts/orchestrator-deploy-hook.sh, Dockerfile); ORCH-061 (толерантность staging-вердикта к инфра-FAIL C9a/C9b, adr-0009, `docs/work-items/ORCH-061/06-adr/ADR-001`) — реализовано в ветке feature/ORCH-061 (обновлять также при изменении src/staging_verdict.py, scripts/staging_check.py, флаг staging_infra_tolerance_enabled); ORCH-021 (post-deploy наблюдение прода + реакция на деградацию, adr-0010, `docs/work-items/ORCH-021/06-adr/ADR-001`) — реализовано в ветке feature/ORCH-021-post-deploy-rollback (reserved-agent job `post-deploy-monitor`: арм в src/stage_engine.py блок `next_stage == "done"`, тик `run_post_deploy_monitor` + перехват в src/agents/launcher.py ДО _spawn; чистая логика src/post_deploy.py never-raise; флаги `post_deploy_*` в src/config.py; блок `post_deploy` в `/queue`; артефакт 16-post-deploy-log.md; self-hosting всегда ALERT_ONLY — тик не рестартит прод; обновлять также при изменении src/post_deploy.py / арм-блока / launcher-перехвата).*
|
||||
*Актуально на 2026-06-07. Обновлять при изменении src/stages.py, src/qg/checks.py, src/main.py. Статусы доработок: ORCH-036 (исполняемый самодеплой `deploy`, adr-0007) — реализовано; ORCH-043 (merge-gate, adr-0006) — design, ветка feature/ORCH-043; ORCH-053 (reconciler, adr-0007, src/reconciler.py) — реализовано; ORCH-060 (F-1 skip escalated/Blocked/Needs-Input, `docs/work-items/ORCH-060/06-adr/ADR-001`) — реализовано в ветке feature/ORCH-060 (Guard 1 `developer_retry_count>=MAX_DEVELOPER_RETRIES` + Guard 2 `plane_sync.fetch_issue_state` Blocked/Needs-Input, флаг `ORCH_RECONCILE_SKIP_BLOCKED_ENABLED`); ORCH-058 (провенанс staging-образа: check_staging_image_fresh + staging_check свежего образа + хук-guard, adr-0008) — реализовано в ветке feature/ORCH-058 (обновлять также при изменении src/image_freshness.py, scripts/orchestrator-deploy-hook.sh, Dockerfile); ORCH-061 (толерантность staging-вердикта к инфра-FAIL C9a/C9b, adr-0009, `docs/work-items/ORCH-061/06-adr/ADR-001`) — реализовано в ветке feature/ORCH-061 (обновлять также при изменении src/staging_verdict.py, scripts/staging_check.py, флаг staging_infra_tolerance_enabled); ORCH-021 (post-deploy наблюдение прода + реакция на деградацию, adr-0010, `docs/work-items/ORCH-021/06-adr/ADR-001`) — реализовано в ветке feature/ORCH-021-post-deploy-rollback (reserved-agent job `post-deploy-monitor`: арм в src/stage_engine.py блок `next_stage == "done"`, тик `run_post_deploy_monitor` + перехват в src/agents/launcher.py ДО _spawn; чистая логика src/post_deploy.py never-raise; флаги `post_deploy_*` в src/config.py; блок `post_deploy` в `/queue`; артефакт 16-post-deploy-log.md; self-hosting всегда ALERT_ONLY — тик не рестартит прод; обновлять также при изменении src/post_deploy.py / арм-блока / launcher-перехвата); ORCH-065 (job-reaper + проактивный реклейм merge-lease + идемпотентная финализация merge, adr-0011, `docs/work-items/ORCH-065/06-adr/ADR-001`) — design, ветка feature/ORCH-065 (новый daemon-поток src/job_reaper.py + старт/стоп в src/main.py lifespan; колонка `jobs.pid` через _ensure_column + проставление в src/agents/launcher.py `_spawn`; функции реклейма lease `pid_alive`/`reclaim_stale_lease` + guard `pr_already_merged` в src/merge_gate.py; флаги `reaper_*`/`lease_reclaim_*` в src/config.py; блок `reaper` в `/queue`; обновлять также при изменении этих мест).*
|
||||
|
||||
@@ -16,11 +16,12 @@ Per-work-item решения живут в `docs/work-items/<id>/06-adr/ADR-NNN-
|
||||
| adr-0008 | Провенанс staging-образа перед BUILD-ONCE retag | accepted | 2026-06-06 | ORCH-058 |
|
||||
| adr-0009 | Толерантность staging-вердикта к инфраструктурным FAIL | accepted | 2026-06-07 | ORCH-061 |
|
||||
| adr-0010 | Post-deploy мониторинг прода + реакция на деградацию | proposed | 2026-06-07 | ORCH-021 |
|
||||
| adr-0011 | Job-reaper + проактивный реклейм merge-lease | accepted | 2026-06-07 | ORCH-065 |
|
||||
|
||||
> ⚠️ Историческая коллизия: номер `0007` занят двумя файлами —
|
||||
> `adr-0007-reconciler.md` (ORCH-053) и `adr-0007-executable-self-deploy.md`
|
||||
> (ORCH-036). Оба accepted; для новых сквозных ADR использовать следующий
|
||||
> свободный номер (текущий максимум — `0010`).
|
||||
> свободный номер (текущий максимум — `0011`).
|
||||
|
||||
## Формат
|
||||
**Контекст → Решение → Альтернативы → Последствия → Связи.** Статус: proposed / accepted / superseded.
|
||||
|
||||
77
docs/architecture/adr/adr-0011-job-reaper-lease-reclaim.md
Normal file
77
docs/architecture/adr/adr-0011-job-reaper-lease-reclaim.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# adr-0011: Job-reaper + проактивный реклейм merge-lease
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Статус | accepted |
|
||||
| Дата | 2026-06-07 |
|
||||
| Источник | ORCH-065 (BUG P0, блокер ORCH-54) |
|
||||
| Детально | `docs/work-items/ORCH-065/06-adr/ADR-001-job-reaper-and-lease-reclaim.md` |
|
||||
|
||||
## Контекст
|
||||
|
||||
Единый инстанс с общей БД и очередью (`jobs`, `max_concurrency=1` для
|
||||
self-hosting). Финализация статуса job (`done`/`queued`/`failed`) происходит
|
||||
ТОЛЬКО в `launcher._monitor_agent → _finalize_job` внутри живого процесса. Смерть
|
||||
monitor-потока/процесса между `proc.wait()` и `_finalize_job` (краш, OOM,
|
||||
self-restart во время deploy) оставляет строку `jobs` навсегда `running`. При
|
||||
`max_concurrency=1` одна такая зомби-строка блокирует claim всех job →
|
||||
**встаёт конвейер всех проектов**. Единственная защита — `requeue_running_jobs()`
|
||||
— работает ТОЛЬКО на старте процесса. Симметрично: merge-lease (ORCH-043,
|
||||
файл `.merge-lease-<repo>.json`) реклеймится лишь лениво по TTL при чужом
|
||||
`acquire`; liveness держателя по pid не проверяется → залипший lease блокирует
|
||||
чужие merge. Это последняя ручная точка автономного self-deploy (блокер ORCH-54);
|
||||
доказанные инциденты 07.06 — jobs 236/239/242/254.
|
||||
|
||||
## Решение
|
||||
|
||||
1. **Job-reaper** — новый daemon-поток `src/job_reaper.py` (каркас `reconciler`:
|
||||
never-raise, `_stop`-Event, старт/стоп в `lifespan`, снимок в `/queue`,
|
||||
kill-switch). Работает **без рестарта** процесса. Liveness — трёхуровневая:
|
||||
Tier-1 мёртвый `jobs.pid` (новая колонка) после `reaper_dead_ticks` подряд
|
||||
тиков; Tier-2 `agent_runs.exit_code` записан, а job ещё `running`; Tier-3
|
||||
backstop по потолку `reaper_max_running_s`. Действие переиспользует контракты:
|
||||
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'` + `rowcount`, как `claim_next_job`) исключает
|
||||
двойную обработку (совместимость со стартовым `requeue_running_jobs`).
|
||||
2. **Проактивный реклейм stale/dead lease** — функции в `merge_gate.py`
|
||||
(`pid_alive`, `reclaim_stale_lease`), вызываемые на старте (рядом с
|
||||
`requeue_running_jobs`) и периодически из тика reaper. Освобождение, если
|
||||
держатель **мёртв** (pid не жив) ИЛИ **просрочен** (TTL); живой держатель в
|
||||
пределах TTL — НЕ трогать. holder-aware, never-raise, условность как ORCH-43.
|
||||
3. **Идемпотентная финализация merge** — без новой merge-логики: re-drive через
|
||||
reaper→`queued`→переисполнение стадии / reconciler; дорогие шаги не
|
||||
повторяются (`branch_is_behind_main==False`); добавлен детерминированный
|
||||
never-raise guard `pr_already_merged` (читает состояние PR), консультируемый
|
||||
перед повторным merge → уже слит = no-op.
|
||||
4. **Схема БД** — `jobs.pid INTEGER` через идемпотентный `_ensure_column`
|
||||
(паттерн live-safe миграции). Больше ничего не меняется.
|
||||
|
||||
Kill-switch'и (`ORCH_*`): `reaper_enabled`, `reaper_interval_s`,
|
||||
`reaper_dead_ticks`, `reaper_max_running_s`, `lease_reclaim_enabled`;
|
||||
переиспользуются `merge_lock_timeout_s`, `merge_gate_repos`. `false` → строго
|
||||
прежнее поведение.
|
||||
|
||||
## Альтернативы
|
||||
- Reaper внутри reconciler — отвергнуто (смешение stage- и jobs-уровней, общий
|
||||
kill-switch, хуже изоляция).
|
||||
- Только эвристика `agent_runs` без `jobs.pid` — отвергнуто как основной механизм
|
||||
(не ловит зомби, чей monitor умер до записи exit_code); оставлена как Tier-2/3.
|
||||
- БД-lock / внешний брокер очередей — вне объёма (single-node SQLite).
|
||||
- Форс `done` по факту exit0 — отвергнуто; выбран gate-driven advance.
|
||||
|
||||
## Последствия
|
||||
- (+) Зомби-job и залипший lease самовосстанавливаются без рестарта и без
|
||||
оператора; очередь общего инстанса не встаёт; снят технический блокер ORCH-54.
|
||||
- (+) Контракты неизменны (`STAGE_TRANSITIONS`, `QG_CHECKS`, `check_*`, БАГ-8,
|
||||
exit-коды хука); одна колонка через проверенный idempotent-паттерн.
|
||||
- (−) pid-liveness валиден в предположении одного pid-namespace (агент —
|
||||
дочерний процесс оркестратора); закрыто backstop'ом по времени и TTL.
|
||||
- (−) streak-счётчик in-memory (сброс на рестарте; рестарт покрыт
|
||||
`requeue_running_jobs`).
|
||||
|
||||
## Связи
|
||||
- Базируется: adr-0002 (очередь), adr-0006 (merge-gate), adr-0007 (reconciler /
|
||||
self-deploy).
|
||||
- Разблокирует: ORCH-54.
|
||||
@@ -326,6 +326,7 @@ webhook (plane/gitea) background thread (queue_worker)
|
||||
| `status` | `queued` → `running` → `done` \| `failed` |
|
||||
| `attempts` / `max_attempts` | счётчик попыток (инкремент при claim) / лимит ретраев (default 2) |
|
||||
| `run_id` | FK на `agent_runs.id` после старта |
|
||||
| `pid` | (ORCH-065) pid агентского процесса (`proc.pid` из `_spawn`); liveness-сигнал для job-reaper. Добавляется `_ensure_column` (idempotent) |
|
||||
| `task_content` | ТЗ, которое пишется в task-файл агента |
|
||||
| `error` | последняя ошибка |
|
||||
|
||||
@@ -343,6 +344,29 @@ status='queued'` и проверяет `rowcount`. При гонке двух т
|
||||
jobs со статусом `running` (воркер умёр на рестарте) → возвращаются в `queued`.
|
||||
Потом стартует воркер; на shutdown — `worker.stop()` (Event.set + join).
|
||||
|
||||
### Job-reaper (ORCH-065, рестарт НЕ требуется)
|
||||
|
||||
`requeue_running_jobs()` спасает ТОЛЬКО на старте процесса. Зомби-job, возникший
|
||||
**без** рестарта (умер monitor-поток/дочерний процесс, а сервис жив), оставался
|
||||
`running` навсегда и при `max_concurrency=1` блокировал всю очередь. Фоновый
|
||||
daemon-поток `src/job_reaper.py` (каркас `reconciler`) периодически
|
||||
(`reaper_interval_s`) сканирует `running`-jobs и реапит «мёртвые»:
|
||||
- **Tier-1** — `jobs.pid` мёртв (`os.kill(pid,0)`→`ProcessLookupError`) на
|
||||
протяжении `reaper_dead_ticks` подряд тиков (анти-ложноположительность);
|
||||
- **Tier-2** — у `agent_runs[run_id]` записан `exit_code`, а `jobs.status` ещё
|
||||
`running` (monitor умер между записью exit_code и `_finalize_job`);
|
||||
- **Tier-3** — backstop: job висит `running` дольше `reaper_max_running_s`.
|
||||
|
||||
Реап атомарен (`UPDATE jobs SET ... WHERE id=? AND status='running'` + `rowcount`,
|
||||
как `claim_next_job`) → совместим со стартовым `requeue_running_jobs` без двойной
|
||||
обработки. Действие переиспользует контракты: exit0 → gate-driven
|
||||
`_try_advance_stage`+`_finalize_job` (источник истины — QG); exit≠0/неизвестно →
|
||||
`attempts<max`→`queued`, иначе `failed`+Telegram. Тот же поток на старте и
|
||||
периодически делает проактивный реклейм stale/dead merge-lease (`merge_gate.py`:
|
||||
`pid_alive`/`reclaim_stale_lease`). never-raise; kill-switch `ORCH_REAPER_ENABLED`
|
||||
/ `ORCH_LEASE_RECLAIM_ENABLED`; снимок в `GET /queue` (блок `reaper`). Подробнее —
|
||||
adr-0011.
|
||||
|
||||
### Конфиг
|
||||
|
||||
- `ORCH_MAX_CONCURRENCY` (default 1) — лимит параллельных jobs.
|
||||
|
||||
Reference in New Issue
Block a user