Preview slot utilization & contention dashboard (from semaphore events)
We have nine preview slots leased through the semaphore
(environment-config-lease), and we now reclaim them off the critical path
(3h lease TTL + the hourly GC sweep — see docs/preview-resource-gc.md, PR
#2009). What we don't have is any historical view of how the fleet is
actually used. Every question about capacity is answered by eyeballing
pnpm preview status at a single instant. We want a post-hoc dashboard.
Why#
- Is nine slots the right number, or are we chronically contended / chronically idle? Right now it's a vibe, not a measurement.
- When PRs wait for a slot, how long do they wait, and how often? (Contention.)
- How much of a slot's leased time is actually used (deploy/test renewals) vs. idle-holding? (Utilization vs. squatting.)
- Are orphaned/leaked leases and GC reclaims trending down after #2009? (The whole point of that work — we should be able to see it.)
- Cost proxy: slot-hours held, and how much the 3h TTL actually saved vs. 24h.
Data source#
The semaphore coordinator (apps/semaphore, D1-backed) already logs every
lease transition — acquired / renewed / evicted / expired / force-released —
as an event, keyed by slot slug + holder (pr-<n> / manual-<user> / gc).
That event log is the raw material; no new instrumentation on the preview
tooling side should be needed (confirm the events carry: slug, holder, kind,
timestamp, and leasedUntil).
Metrics to derive#
- Utilization: per-slot and fleet-wide % of wall-clock leased, and % of leased time with a renewal in the trailing window (used vs. idle-held).
- Contention: count + duration of "waiting for a slot" episodes (the deploy wait loop already logs these; may need to emit them as events too), and how often the fleet hit 0 available.
- Lease lifecycle: median/p95 lease duration, renewals per lease, holder breakdown (PR vs manual vs gc).
- Health: orphaned-at-detection count, GC reclaims/run, force-releases — trend over time (proves #2009 works and stays working).
Delivery options (decide during grooming)#
- PostHog (lowest effort): pipe semaphore lease-transition events to PostHog and build the dashboard there. Fits "post-hoc dashboard", no new UI to maintain, and we already use PostHog. Downside: another event sink to wire from the semaphore worker.
- In-OS-app page: a
/preview-fleetview in os.iterate.com querying the semaphore's event log via RPC. More control, more to build/own. - Extend semaphore.iterate.com: it already shows live leases; add historical/aggregate charts next to them. Keeps everything in one place.
Lean: start with (1) PostHog for the numbers, since the events already exist and the value is the measurement, not a bespoke UI.
Open questions#
- Do the semaphore events already persist long enough for post-hoc analysis, or do they need a retention bump / export?
- Are "waiting for a slot" episodes events yet, or only log lines? Contention is the most valuable metric and may need to be emitted explicitly.
Raised by Jonas 2026-07-15 as a follow-up to the preview-resource-GC work.