ADR-0020: Overdue Task Escalation is Notify-Only — No Auto-Reassignment
Status
AcceptedTags
fieldforce, overdue, escalation, notificationsDecision
When a fieldforce task remains overdue beyond the org’sescalation_after_hours threshold, the system sends a push notification to the next-role-up (supervisor → manager → admin) and sets escalated_at on the task. The task’s assigned_to is unchanged. The manager decides whether to reassign, extend the deadline, or cancel the task manually.
Why
Auto-reassignment requires answers to questions the system cannot resolve: which specific user in the next role receives the task, what happens to the original assignee’s accountability, and how to handle tasks that are nearly complete when escalation fires. These decisions require business context only a human manager has. Rejected alternatives:- Auto-reassign to next role: Changes
assigned_to, creates an audit entry. “The manager” is not a single user in multi-manager orgs. Destructive if the original assignee was about to complete the task. Rejected. - Visibility-change escalation: Expand
AllowedUserIDsso escalation targets can see the task. Adds a new scoping concept with no existing precedent. Rejected.
How it works
NULL. Neither is included in API responses — they are internal job state.
Per-org timing is read from org_module_configs (ADR-0019) on each tick. Defaults: overdue_notify_after_hours = 0 (notify immediately when overdue), escalation_after_hours = 24.
Known limitations
- Single escalation:
escalated_atis set once. If the task remains unresolved after escalation, no further automated action occurs. The manager must intervene. - 1-minute delay: Notifications fire within 1 minute of the threshold crossing, not instantly. For
overdue_notify_after_hours = 0, the first notification may be up to 1 minute late. - Role resolution ambiguity: In orgs with multiple managers, all managers in the org receive the escalation notification. No single manager is designated.
Rules for agents
- The escalation job MUST NOT change
assigned_to,status, or any other task field — it only setsescalated_atand sends a notification - Tasks with
status IN ('approved', 'cancelled')MUST be skipped in both passes — do not notify on terminal tasks - Both
overdue_notified_atandescalated_atMUST be set atomically with the notification — if the notification fails, do not set the column (retry on next tick) - Do not expose
overdue_notified_atorescalated_atin API responses — these are internal job state columns - Never send a second overdue notification or second escalation for the same task — the NULL check is the idempotency guard