1b775e42f0
The two were ~90% identical (lock/skip, job_runs create, subprocess exec, timeout/kill, finalize) and had already drifted once during the use_lock change. Extract _execute(capture=bool) plus _record_run/_finalize_run helpers; run_job and run_job_capture become thin wrappers. run_job_capture also gains the use_lock parameter for free. Behavior is unchanged. Covered by tests/test_pipeline_runner.py, now including run_job_capture output capture + persistence and its skipped-lock case (42 tests green). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>