Files
rengine/tools/runner
Yogesh OjhaandClaude Opus 5 1807ec37b3 fix(orchestrator): a stage must always reach a terminal activity, and a resume must not re-run what finished
Nine bugs found by a cloud review scoped to the celery engine and the stage
orchestration (46 files, 7,790 lines).

Stuck scans:
- run_stage did load_resolved(), the Stage construction and should_run() OUTSIDE
  the try/except, after committing a RUNNING activity. A raise there — a stage
  config model that moved under a queued scan, a constructor failure — left the
  row RUNNING for ever, so aggregate_status kept returning RUNNING and finalize
  deferred; the link_error calls the same finalize, so it never rescued it. The
  setup now runs inside the same handler, and the post-run bookkeeping is
  wrapped too: recording a result must never be what fails a finished stage.
- _fail_stage crashed on a missing activity row (a cascade-deleted scan), and
  its own except-branch called finish() with the same None and raised again.

Stage failures that were not failures:
- the abort poll opened a session on a path stages hit in tight loops and let
  any database error propagate, so a pool blip failed a running stage. It now
  fails open and keeps the last answer, like the Redis guards.

Wrong accounts of a run:
- finalize picked the scan's error with `next(a for a in activities if a.error)`,
  ignoring status over an unordered fetch — so a FAILED scan reported "superseded
  by stage retry" or a PARTIAL warning instead of the failure. Only a FAILED row
  may explain a failure.
- _undispatched compared the activity ROW count to the stage count, so one
  retried stage (which leaves a superseded row plus a new one) cancelled out one
  genuinely undispatched stage and the scan reported COMPLETED with a stage
  missing. It compares stage names now.
- endpoint_probe counted static endpoints excluded by skip_static as
  budget-skipped, so the coverage warning claimed a cap that was never hit.

Work done twice, and work undone:
- reap_stalled resumed the whole level, re-running stages whose activity was
  already SUCCESS: a finished hour-long stage ran again because a level-mate
  stalled, and if the second run failed the whole scan failed. build_canvas
  takes the set of finished stages and omits them (an emptied level is dropped);
  the done=None path is byte-identical to before.
- run_scan read the scan without a lock and committed status=RUNNING plus a
  fresh celery_task_ids over whatever was there, so an API cancel landing in
  that window was silently overwritten and the ids it needed to revoke were
  lost. finalize already row-locks; run_scan now does too.

- _write_temp_file called os.close(fd) after os.fdopen had already closed it,
  masking the real write error with EBADF and, in the threaded stages, risking
  a close of another thread's reused descriptor.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SAeBy7TBUn3Fd1ckyQp9r6
2026-09-10 10:18:07 +05:30
..