How Agent Work Survives the Real World
Once trycompai/crm has decided what evidence deserves a write, it faces a less glamorous but equally important question: will the work survive a slow provider, a restarted process, or two workers reaching the queue at once? This part of the design follows an AgentTask from due time through leasing, execution, recovery, and retirement. The goal is to distinguish work that is waiting, work that is slow, and work that has genuinely ended.
A task has a lifecycle, not a spinner
An AgentTask begins as durable intent. It names a subject, identifies the kind of work, explains why the work exists, carries any structured payload, and records a due time. Priority determines which due work deserves attention first. A research budget limits metered vendor calls. None of those fields says that the work has started. They describe what should happen when a worker becomes available.
When the due time arrives, a dispatcher claims the row. The claim sets a lease, records the start if there is not one already, and increases the attempt count. The lease is a temporary statement: one worker owns this attempt until a deadline or completion changes the situation. A second worker must not take the same live row.
The dispatcher uses a single database update with FOR UPDATE SKIP LOCKED. The phrase describes a useful concurrency rule. The database locks the rows selected for one claim. Another dispatcher skips those locked rows and selects different work. The queue can therefore support concurrent dispatchers without relying on a process-local list of claimed IDs.
The attempt count makes the lifecycle finite. If a worker loses its lease, the task can become eligible again while attempts remain. If the task reaches the attempt ceiling without a completion signal, the system retires it with an outcome. Retirement is not success, but it is a truthful terminal state. A representative can tell that the system tried and stopped rather than waiting for a status that will never arrive.
The due time and lease also prevent a scheduled recheck from looking like a failure. A task booked for a future date is waiting by design. A due task with no current lease is ready to claim. A leased task is in flight. A task with a finish time is complete or has been retired. These distinctions let the database describe the work without asking the interface to infer it from elapsed time alone.
Slow is not the same as failed
Background systems often make a dangerous simplification: if an operation misses its deadline, mark it failed and retry. That can duplicate work when the original operation continues and succeeds after the deadline. Comp AI CRM treats a deadline as a point for reconciliation, not automatic proof of failure.
For direct work, the dispatcher keeps the operation in flight when it outruns its item deadline. If the late operation eventually succeeds, its result settles the task once. If it eventually rejects, the late failure is reconciled. The queue does not launch a second attempt merely because the first result was slow to arrive.
Research sessions have a similar boundary during startup.