103 lines
5.1 KiB
YAML
103 lines
5.1 KiB
YAML
work_item: ORCH-126
|
||
stage: analysis
|
||
author_agent: analyst
|
||
status: ready-for-review
|
||
created_at: 2026-06-17
|
||
model_used: claude-opus-4-8
|
||
title: "ORCH-126 — гигиена run-ownership queued-job + диагностика невозможных состояний"
|
||
framework: pytest
|
||
track: bug
|
||
scope: >
|
||
Покрывает: сброс run_id/pid на всех путях возврата job в queued (restart/retry/transient/reap),
|
||
чистый claim без stale pid, устойчивость окна _spawn, детект/санацию невозможных queued-состояний,
|
||
корректность Tier-1 reaper-liveness. Вне покрытия: семантика serial-gate (ORCH-088/124) и dep-gate
|
||
(ORCH-026), переписывание reaper/transition-lease, изменение схемы БД.
|
||
notes: >
|
||
TC-01 — ОБЯЗАТЕЛЬНЫЙ регресс-тест бага: красный на коде ДО фикса, зелёный ПОСЛЕ (BR-5 / AC-8).
|
||
Тесты используют изолированную временную SQLite-БД (db.init_db во временной директории), без сети,
|
||
без Claude CLI, без реального Popen (spawn мокается/подменяется). Полный регресс pytest tests/ -q
|
||
должен оставаться зелёным (AC-7).
|
||
|
||
tests:
|
||
- id: TC-01
|
||
type: integration
|
||
description: >
|
||
РЕГРЕСС (red→green): job в running со stale run_id+pid (started_at set) проходит
|
||
requeue_running_jobs() (имитация рестарта); ассерт — строка queued с run_id IS NULL,
|
||
pid IS NULL, started_at IS NULL. До фикса pid/run_id остаются (красный), после — чисто.
|
||
module: tests/test_orch126_queued_stale_run.py
|
||
expected: PASS
|
||
|
||
- id: TC-02
|
||
type: unit
|
||
description: >
|
||
mark_job(job_id, 'queued') сбрасывает run_id и pid (а не только started_at/finished_at).
|
||
module: tests/test_orch126_queued_stale_run.py
|
||
expected: PASS
|
||
|
||
- id: TC-03
|
||
type: unit
|
||
description: >
|
||
mark_job_transient(...) на job со stale run_id/pid возвращает чистый queued (run_id/pid NULL),
|
||
сохраняя инкремент transient_attempts и backoff available_at.
|
||
module: tests/test_orch126_queued_stale_run.py
|
||
expected: PASS
|
||
|
||
- id: TC-04
|
||
type: unit
|
||
description: >
|
||
reap_running_job(job_id, 'queued', ...) сбрасывает run_id/pid; атомарный guard
|
||
WHERE status='running' сохранён (повторный вызов на уже-queued строке -> rowcount 0).
|
||
module: tests/test_orch126_queued_stale_run.py
|
||
expected: PASS
|
||
|
||
- id: TC-05
|
||
type: integration
|
||
description: >
|
||
claim_next_job() флипает queued->running и не оставляет stale pid: сразу после claim
|
||
jobs.pid IS NULL (до _spawn-стампа). Подтверждает чистый старт (AC-3).
|
||
module: tests/test_orch126_queued_stale_run.py
|
||
expected: PASS
|
||
|
||
- id: TC-06
|
||
type: integration
|
||
description: >
|
||
При выключенном serial-gate (ORCH_SERIAL_GATE_ENABLED=false) валидный queued ORCH-job
|
||
выбирается claim_next_job при running=0 и не зависает (AC-2).
|
||
module: tests/test_orch126_queued_stale_run.py
|
||
expected: PASS
|
||
|
||
- id: TC-07
|
||
type: integration
|
||
description: >
|
||
Reaper-тик на свежеклеймленном running с pid IS NULL не реапит строку (Tier-1 пропускает
|
||
pid IS NULL), легитимный старт сохраняется (AC-4).
|
||
module: tests/test_orch126_queued_stale_run.py
|
||
expected: PASS
|
||
|
||
- id: TC-08
|
||
type: integration
|
||
description: >
|
||
Детект/санация невозможного состояния: предзаписанная строка queued с непустым pid/run_id/
|
||
started_at приводится к чистому queued (авто-санация при старте/реапе) И/ИЛИ фиксируется
|
||
счётчиком в снимке очереди; операция идемпотентна и never-raise (AC-5).
|
||
module: tests/test_orch126_queued_stale_run.py
|
||
expected: PASS
|
||
|
||
- id: TC-09
|
||
type: integration
|
||
description: >
|
||
Окно _spawn: при провале launch (launch_job/_spawn бросает до стампа pid) обработчик
|
||
_drain_once возвращает job в queued чистым (pid/run_id NULL); повторный claim стартует
|
||
штатно (AC-6).
|
||
module: tests/test_orch126_queued_stale_run.py
|
||
expected: PASS
|
||
|
||
- id: TC-10
|
||
type: unit
|
||
description: >
|
||
Анти-регресс: для здорового job (никогда не стартовал) поведение mark_job/claim байт-в-байт;
|
||
терминальные исходы (done/failed/cancelled) и стамп finished_at не затронуты сбросом ownership.
|
||
module: tests/test_orch126_queued_stale_run.py
|
||
expected: PASS
|