fix(queue): enforce queued ⇒ no run-ownership invariant (ORCH-126) #145

Merged
admin merged 8 commits from feature/ORCH-126-bug-queued-job-can-keep-stale- into main 2026-06-17 11:56:28 +03:00

8 Commits

Author SHA1 Message Date
deploy-finalizer
ab157324a7 deploy(ORCH-036): finalize SUCCESS for ORCH-126
All checks were successful
CI / test (push) Successful in 1m14s
CI / test (pull_request) Successful in 1m13s
2026-06-17 11:56:26 +03:00
staging-runner
aca0466162 staging(ORCH-115): staging gate SUCCESS for ORCH-126
All checks were successful
CI / test (push) Successful in 1m20s
CI / test (pull_request) Successful in 1m12s
2026-06-17 11:50:19 +03:00
test-runner
3b8aca03ee test(ORCH-116): test gate PASS for ORCH-126
Some checks failed
CI / test (push) Has been cancelled
CI / test (pull_request) Successful in 1m18s
2026-06-17 11:48:44 +03:00
c8632f4b48 reviewer(ET): auto-commit from reviewer run_id=776
All checks were successful
CI / test (push) Successful in 1m18s
CI / test (pull_request) Successful in 1m14s
2026-06-17 11:47:05 +03:00
d7e7a4d817 fix(queue): enforce queued ⇒ no run-ownership invariant (ORCH-126)
All checks were successful
CI / test (push) Successful in 1m14s
CI / test (pull_request) Successful in 1m15s
Queued analyst-jobs hung forever even with ORCH_SERIAL_GATE_ENABLED=false
(incident ORCH-124/125, job 2286: queued + run_id=759/760 + pid=35/42 +
started_at=NULL — physically impossible). No path returning a job to
'queued' reset its run-ownership (run_id/pid); after a container restart a
reused pid made pid_alive(stale)=True, so the job-reaper Tier-1 saw a phantom
'running' and at max_concurrency=1 wedged the claim of the whole shared queue.

Enforce the invariant `status='queued' ⇒ run_id IS NULL AND pid IS NULL AND
started_at IS NULL` on existing columns (no schema change):

- D1 forward-cleanup: requeue_running_jobs / mark_job('queued') /
  mark_job_transient / reap_running_job('queued') reset run_id=NULL, pid=NULL
  in the same UPDATE that clears started_at; atomic status-guards preserved.
- D2 clean claim: claim_next_job resets pid/run_id on the queued->running flip
  (defense-in-depth) so the row carries pid IS NULL until _spawn stamps it.
- D4 self-heal + observability: db.find_impossible_queued_jobs /
  sanitize_impossible_queued run at startup (main.lifespan) and on each reaper
  tick (JobReaper.sanitize_impossible_queued_once, never-raise); counter
  impossible_queued_total in the GET /queue reaper block. Kill-switch
  ORCH_IMPOSSIBLE_QUEUED_SANITIZE_ENABLED (default on; gates only the D4 sweep).
- D5: reaper Tier-1 unchanged — the fix restores its precondition (pid reflects
  THIS run). Marked invariants ORCH-065/113/114/099 preserved.

Tests: tests/test_orch126_queued_stale_run.py (TC-01 mandatory regression
red->green; TC-02..TC-10). Full pytest tests/ -q green (2189 passed).
Docs: internals.md (run-ownership invariant section), .env.example, CHANGELOG;
cross-cutting adr-0052.

Refs: ORCH-126
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 11:39:26 +03:00
3fb7bd6e4c architect(ET): auto-commit from architect run_id=774
All checks were successful
CI / test (push) Successful in 1m12s
2026-06-17 11:22:30 +03:00
453c5b7d04 analyst(ET): auto-commit from analyst run_id=773
All checks were successful
CI / test (push) Successful in 1m12s
2026-06-17 11:07:33 +03:00
a5f691fc96 docs: init ORCH-126 business request
All checks were successful
CI / test (push) Successful in 1m17s
2026-06-17 11:00:16 +03:00