Executing a follow-up, end to end
A row falls due, the drain takes it — two per tick on the phone channel — and runs it through the same gates as any outbound action on that channel. One of three outcomes is written back: placed, awaiting approval, or refused with a reason. More than twenty-four hours late and it is closed as missed: instead of rung.
The thirteen stages#
- TRIGGER — the row's due time passes. Nothing external happens; time is the trigger, which is what makes this flow different from every other one in this section.
- USER / EXTERNAL EVENT — none at the start. The customer has done nothing; they are simply waiting for something they were promised.
- AUTH / WORKSPACE RESOLUTION — the row belongs to exactly one workspace, and the drain acts within it. There is no cross-workspace queue and no privileged operator pass through this path.
- INGEST / REQUEST —
runner.chase_phonepicks up duephonerows on the engine's tick, two per tick, so a batch of call-backs is worked through over several passes rather than seizing every line at once. - CANONICAL RECORD — the row resolves to its Person and to a reachable identity: a number for a call, a mailbox or handle for a message. A row whose person has no usable identity cannot be executed.
- CLASSIFICATION / RESEARCH / REASONING — the channel decides the action.
taskrows are never picked up at all; nothing drains them, at any tick. - KNOWLEDGE + MEMORY + RULES — what the action will say is grounded the same way any outbound message is. On a call,
safe_salesstill refuses a commercial term Knowledge does not support, follow-up or not. - AUTONOMY / APPROVAL — the channel's autonomy rule decides whether the action may leave. A held attempt is recorded as
awaiting approval <id>and joins Needs You, ranked with everything else waiting on a person. - ACTION / PROVIDER — a permitted phone row goes through
voice_engine.place_call, with the same gates as any outbound call: suppression and do-not-contact, line readiness, and what the workspace's allowances permit. A line that is not ready is retried in an hour. - RESULT — one of three strings against the row:
call placed <id>,awaiting approval <id>, orrefused: <reason>. A placed call becomes an ordinary call with its own transcript, summary and cost. - RELATIONSHIP / TIMELINE / MEMORY — the attempt appears on the person's timeline, and a successful action stops the commitment being owed. Anything durable learned on the resulting call can be remembered against that contact.
- AUDIT / USAGE / NEEDS YOU — the attempt is recorded with its outcome and its reason, usage is metered against the workspace, and anything held sits in front of a person while the clock keeps running on it.
- NEXT — the row is completed, retried, or — past twenty-four hours late on the phone channel — closed as
missed:and never rung, because a very late call-back is worse than none.
Stage by stage#
| Stage | What you see | What changes | What can fail |
|---|---|---|---|
| Due | The row goes overdue | Nothing yet | Nothing — a minute between due and picked up is the tick, not a fault |
| Pick-up | Two rows moving per tick | The rows being attempted | A long queue means later rows wait, and ageing rows can pass the cut-off |
| Identity | Nothing | Nothing | A person with no reachable identity on that channel |
| Autonomy | An item in Needs You | The row shows awaiting approval | Nobody works the queue, and the held row ages towards missed: |
| Gates | A refusal with its reason | The refusal on the row | Suppression, an unready line, or an exhausted allowance |
| Action | A call in progress | A call record with its own id | The call itself fails; that is the call's own failure path, not the follow-up's |
| Outcome | One of three strings | The row's recorded result | Reading awaiting approval as refused, and fixing the wrong thing |
| Lateness | A closed row reading missed: | The row closes | Nothing was rung, deliberately |
Reading the outcome correctly#
call placed <id>- The commitment to ring was kept. Follow the id for what was said; the follow-up's part is done.
awaiting approval <id>- Nothing has been dialled. A person must decide, and the row keeps ageing while they do.
refused: <reason>- A gate decided. Some reasons clear themselves — an allowance resets, a line recovers — and some are the correct permanent answer, such as a do-not-contact entry.
missed:- The twenty-four-hour cut-off. Nobody abandoned this; the clock ran out, and the row still says what was promised.
What this flow will not do#
- It will not ring somebody who has asked not to be rung. A scheduled action is still an action, and suppression applies unchanged.
- It will not execute a
taskrow. Nothing drains that channel, so the wait is the whole answer. - It will not retry forever. A specific case — an unready line — is retried in an hour. Everything else is recorded and left for a person.
- It will not ring a very late row. Past twenty-four hours the call is not made at all, which is the deliberate outcome rather than a failure to reach the customer.
Questions#
Why is a follow-up subject to the same gates as an ordinary call?
Because it is an ordinary call. Placing it through voice_engine.place_call rather than through a private path is what guarantees a scheduled action cannot bypass suppression, allowances or the line's health.
Can the drain be made faster than two rows per tick?
The rate is a property of the drain rather than a per-workspace setting, and it exists to stop a queue of due rows seizing every line at once. A queue consistently outrunning it is a signal about how many call-backs are being promised.
What happens to the row while an approval is waiting?
It stays open, still owed, and still ageing against its due time — including against the twenty-four-hour phone cut-off. Approvals are not a pause on the clock.