Laundry Queue

A hundred-plus-resident building using this queue at once isn't something anyone can click through by hand to check — so instead of manually testing a handful of cases, this runs the real queue code through a large number of randomly generated, reproducible days and reports what happened.

Simulated building, not real usage. Every number below comes from 50 randomly generated Saturdays in a made-up 100-resident building, run through the real queue code with a virtual clock (ADR 0007, ADR 0010), not measurements of a real laundry room. Re-run it yourself with pnpm sim; the assumptions it ran with are listed below.

Watch one simulated day

Picks the seed closest to each policy's own average above — a typical day, not the most dramatic one — and plays it once, start to finish, on the same board layout the real queue uses.

08:00

Washer 1 StateFree Next— Time left—
Washer 2 StateFree Next— Time left—
Washer 3 StateFree Next— Time left—
Washer 4 StateFree Next— Time left—
Washer 5 StateFree Next— Time left—
Washer 6 StateFree Next— Time left—
Washer 7 StateFree Next— Time left—
Washer 8 StateFree Next— Time left—
Dryer 1 StateFree Next— Time left—
Dryer 2 StateFree Next— Time left—

What the numbers add up to

Same data as the day you just watched (or any of the other 49 seeds run under each policy) — summarised across all of them below.

Join-to-start wait (median)

Join-to-start wait (p90)

Join-to-start wait (max)

People moved back or ejected

Machine idle while someone waited behind an absent front

Extra wait a late person caused others

Extra wait a late person paid themselves

Assumptions this run made

These are guesses, not data from a real building — changing them changes every number above.

What these numbers actually show

The idle-machine result above is a real, assumption-independent check that the code does what the rules say: wait-forever alone produces minutes of a machine sitting idle while someone waited behind an absent front; eject and move-back-one both produce zero. That's not sensitive to how aggressive the lateness assumptions are — it's the direct consequence of those two policies actively resolving a stuck front-of-queue reservation, and wait-forever never doing anything about it.

move-back-one's p90 and max wait times sit far above its own median, while eject's don't — a long tail, not just a higher average. The reason is mechanical: nothing in the rule limits how many times in a row one reservation can be swapped backward. A more realistic lateness assumption would shorten that tail, but wouldn't remove the mechanism that produces it — the rule has no safeguard against a long run of bad luck, independent of how likely that run is.

eject's apparently low "extra wait paid by self" isn't because ejecting is cheap — it's a measurement gap. An ejected reservation is simply gone from this simulation's data the moment it happens, so no further cost is ever counted against it. The real cost (losing your place entirely, rejoining from the back) is exactly what ADR 0005 already argued against in words; this page's numbers don't measure that cost's size, since nothing here follows a reservation past the point it's deleted.

None of this settles, or is being used here to argue, which policy is fairest. That argument is ADR 0005's and README's, made on grounds this simulation can't touch — what cost is acceptable to impose on someone for stepping away, not how many minutes it costs on average.

Limitations of this simulation

A direction this surfaced, not a decision to build it

The long-tail finding suggests a real question for a future iteration of this app: should the queue cap how many consecutive times one reservation can be swapped backward before some other mechanism takes over — for example, routing it into the existing cross-machine "earlier machine" offer instead of an indefinite in-place swap? This page doesn't answer that; it's recorded as a possible second-phase direction in plan.md, not a scoped feature.

Full detail behind this reading: ADR 0011.