An object's state and queued work survive a crash, a deploy or a move to another worker, and pick up where they left off.
Trays are actors inside your object. Each tray has its own durable state and an inbox: awaiting a send returns once the message is in the store, and the tray handles its messages one at a time, from the head of its queue. A message is gone only when its handler returns and the removal is written, so if the worker dies, another worker reopens the tray and redelivers what was unfinished, with ctx.attempts marking the retry. No workflow engine and no replay log: the handler simply runs again. An object can hold many trays, one per queue or one per key.
The whole durable object, one tray and a route that admits work, with a Postgres store shared by every worker: