e94f7a0ca6
- Add shared Arc<Semaphore> (1 permit) through main → AppState and jobs. All heavy operations (catalog refresh, rebuild, nightly recompute, factory reset) now serialise: a second trigger returns "already_running" instead of spawning a parallel task that fights over the SQLite WAL lock. - Scheduled nightly job acquires the semaphore too, so it waits rather than stomping on a manual rebuild triggered at startup. - Replace 90 × precompute_lightweight calls (90 separate transactions, horizon fetched 90 times) with precompute_next_90_nights: one bulk SELECT to find missing dates, horizon fetched once, all inserts in a single transaction. Eliminates the 1–5s per-INSERT lock waits seen when multiple jobs were competing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>