Benchmarks execute as a BullMQ job in the queue worker, but
/api/benchmark/status and the 409 double-run guards read the web
process's in-memory BenchmarkService, which never sees the run. Status
always read idle, and a second POST /api/benchmark/run was accepted
mid-flight and queued behind the first.
Status now comes from the queue, the one place both processes can see.
The worker publishes each stage on its job via job.updateProgress(),
and resolveBenchmarkStatus() reports the active job's stage, "starting"
for a job still queued, or the local status for a sync=true run. The
status endpoint and all three run guards read it. If the queue can't be
read they fall back to the local status, as before.
Closes#1326