# Timeout is a state, not a silence

> worklists.dev — 2026-08-20
> Written for ED-3 — the in-house supply-chain platform team — the platform engineer wiring SLA handling and deciding what a missed window looks like on the record.

One thesis: **a timeout is a state the record enters, not a silence, because on an append-only
ledger a state change with no actor must be explicit.** A deadline that passes without an act
leaves nothing for `trace` to find. So the breach is written — by an alarm, never by a person — and
the two neighboring verbs, `block` and `escalate`, each belong to a specific party for the same
reason.

## The silence problem

A receiving task for a pallet of lettuce has a window: dock 12, before 08:00. At 08:00 nothing has
happened. In a system with a status column, the task still says `pending`, and a dashboard
somewhere paints it red by comparing `now()` to `due_at` at render time. The record itself holds
no fact about the breach. Replay the ledger next month and the breach is not in it; it was an
artifact of when someone looked.

A fold over acts has to do better than that, because the fold *is* the record. If the only acts
are `dispatch` and `assign`, the task folds to `pending` forever, and *this window was missed* is a
fact that was never stated.

## Timeout is an act with no performer

`timeout` takes the task and the deadline it breached, and appends an act that folds to
`timed-out`. Three things about it are deliberate:

**It has no performer.** The act is authored by the alarm. Writing a person into `who` would put a
fabricated actor on an evidence ledger — someone who did nothing, recorded as having done
something. The actor field says what it is: a server-side clock that ran against the worklist.

**It is not terminal.** The state machine's own words for it are *awaiting escalation*. A timed-out
task is still open; it has entered a state that says a window passed, and what happens next is a
separate act by a separate party.

**It records a breach; it never causes one.** An alarm that fires before the deadline is refused
with `timeout-not-due`, which is retryable because time fixes it. Writing the breach anyway would
put a fact on the ledger that is not true. The early alarm is one of the package's poison fixtures.

It escalates on **non-action, never non-delivery**. Whether a notification reached a device is
unobservable from the server; whether an act was appended is not. So the clock runs against the
worklist, and a dead network is not a breach until the window is.

`timeout` is one of the five verbs that answer in the package `worklists.dev` today, with `query`,
`trace`, `claim` and `complete`.

## Block belongs to its author

Before the window closed, the receiver might have raised a block: *the pallet arrived with the
wrong label*, *the dock was occupied*, *I was handed a short load*. `block` takes the task and a
fixed-enum cause, and appends an act that is timestamped, located and **authored by the
performer**.

That authorship is the whole mechanism. A block is *that wasn't me / here's what I was handed*, and
it runs against the gradient of every existing workplace system, where the person closest to the
problem has the least ability to put it on the record. So the block is author-owned, private by
default, and never editable by a supervisor.

The package goes further than read-only, because read-only is a rule someone regresses. **A
non-author never receives the block at all.** Not a redacted version, not a `withheld(...)` marker
— `withheld` says *there is a value you are not licensed for*, which discloses that a block exists,
and for this mechanism the existence of the block is part of what is private. The test asserts it
the only way it can: a non-author's `trace` of a blocked task is deep-equal, member for member, to
the trace of the same task with the block acts removed. The task still folds to `blocked`. The block
is not a way to disappear; it is a way to be on the record without being exposed by it.

The cause is a fixed enum per deployment, never free text in the releasable form. `unblock`
releases it and retains the block record.

## Escalate is a separate party's act

A task that has timed out, or that a performer cannot finish, needs someone with authority to do
something. `escalate` is that request, and it is addressed to a **role**, never a person — the
performer escalates to `shift-lead`, and who fills that role is resolved at invocation. Letting a
performer pick a person would mean rendering a list of people, which is the roster this door does
not have.

Worklists holds no authority. `escalate` compiles to the identity rail's ceremony; the grant or the
refusal is never Worklists'. An approval is a task, so the request lands in the role's worklist
with the same shape as everything else there, and it settles with the same evidence shape.

## The record, read back

`trace` on the lettuce pallet's receiving task, read by an auditor: dispatched from PO 2026-0007;
assigned to dock 12; timed out at 08:00 by the alarm against the 08:00 window; escalated to
`shift-lead`; settled at 08:41 with an event hash. Read by the receiver: the same, plus the block
they raised at 07:52 with the cause *short load*. Read by anyone else: no block, and no sign of one.

Every state change is an act with an honest author — a person, an alarm, or a ceremony. Get
started by folding a record with an early alarm and a due one. The refusal on the first is the
point.

---
Get started: https://worklists.dev/get-access/ · All posts: https://worklists.dev/blog/ · Machine face: https://worklists.dev/llms.txt
