Skip to content

When waiting begins

 2 min A task’s starvation age starts when it becomes ready. That timestamp must be stored before an allocator can observe the transition.
Published

Jul 22, 2026

A task created last week but blocked until now has not spent a week waiting for an allocator. A task ready since last week has. Fairness depends on dating the interval in the right state.

Spice’s readiness code stores ready_at for later transitions into READY. The timestamp belongs with the mutation that exposes the work. Writing it in a second command would leave a window where Serve could observe a newly ready task with its old age.

Dependency edits that leave readiness unchanged retain the existing origin. An unrelated modification must not reset a task’s wait. When completion of one task exposes dependents, the code prepares their transition stamps before the final blocker completes, rather than requiring a fallible write after that irreversible completion.

Those are two ordered writes, not one transaction. If the final completion fails, the dependents remain blocked with prepared timestamps. Retrying through the same completion path replaces those stamps before trying again. Completing the blocker outside that path can leave the earlier origin in place: the resolver trusts ready_at; it does not reconstruct when the blocker actually finished.

Initial readiness also has existing evidence: the native entry timestamp and any wait or scheduled horizon. The resolver uses the latest applicable origin when there is no explicit later-transition timestamp. It does not substitute the current process’s startup time.

That keeps queue age stable across Serve restarts. A malformed timestamp or a ready row with no usable origin is an error; silently using “now” would make old waiting work look newly eligible.

The wider allocator has other ranking inputs. This note concerns only its clock: before using age to prevent starvation, establish which state is aging and make the transition into that state durable.