Reliability & Ops
Response SLAs
Ball-in-court monitoring with countdown-to-SLA nudges — Mallory tracks what's waiting on you and coalesces the pressure into one nudge.
Situation
Mallory needs to watch the async channels the operator lives in (Slack first; email/WhatsApp/Discord/Asana/GitHub later) and track messages awaiting a reply. Each channel carries a configurable SLA; as items near or pass it, Mallory sends a single coalesced nudge for the whole set — never constant individual pings — and celebrates at zero.
This formalises the ad-hoc runtime slack-email-monitor (ball-in-court → batch nudges) into a configurable, visualised, SLA-driven system that lives entirely edge-side — built in the Worker + web + D1, deliberately clear of the concurrent VM→OpenRouter migration and the connector admin console.
Key decisions
Detection: "direct + active threads" heuristic, free / no LLM
DMs, @-mentions, and threads already participated in where the newest message isn't mine; certain channels can opt into LLM triage for precision, others roll into an end-of-day sweep.
Per-method SLA clock: business or wall
business ticks only inside configured response windows; wall is raw duration muted by quiet hours — both DST-safe via Europe/London.
Coalesced + min-interval nudge rhythm
A frequent cron evaluates deadlines and, only when something newly crosses a warning/breach threshold, emits one roll-up of the whole outstanding set, respecting a minimum gap between nudges.
Three channel postures: Active / End-of-day / Ignore
DMs default to Active, channels default to End-of-day; nothing nags until a channel is deliberately promoted.
Edge-native with an ingest port (Approach 1)
Slack is polled directly from the Worker; a generic POST /ingest/pending seam lets future non-pollable channels push items in without rework.
v1 scope: Slack only, adapter interface built for later channels
The ChannelAdapter interface and ingest port are built and tested in v1 even though only Slack uses them.
Architecture
Two ~15-minute crons plus an EOD cron read/write one D1 table (channel_pending); a pure detector + evaluator carry all the logic; delivery reuses the existing /nudge engine.
{channel}:{conversation_id}:{thread_key}; tracks status (open/resolved/snoozed/dismissed), sla_due_at, and last_nudged_at.{id, poll, permalink, nativeUrl, isResolved} — Slack is the only v1 implementation; the poll cron iterates registered adapters.response_sla nudge kind rather than a new delivery path.Risks & guardrails
Business-clock edge cases
A Friday-18:00 message with a 90-min SLA must resume ticking at the next window open (Monday), not appear falsely overdue over the weekend — explicitly called out and tested.
Coalescing correctness is the whole point
Min-interval + newly-crossed detection must prevent repeat pings for the same outstanding item; the evaluator is the real gate, the nudge engine's dedupe ledger is only a safety net.
Double-nudging during cutover
At deploy, the operator must flip off the runtime slack-email-monitor's Slack leg (Gmail leg stays) to avoid two systems nudging on the same items.
Visual design not yet signed off
Live pixel mockups of the feed card + countdown bar are called out as a polish step still needed before web components are finalised.
Delivery
Phase A — data model + pure core
Migration 023, Env secrets, ChannelPendingRow type, config parse-with-defaults, clock math, detector, evaluator — all unit-tested before any wiring.
Phase B — Slack adapter + ingestion + crons + nudge wiring
Poll/permalink/nativeUrl/isResolved, the ingest route, the poll/evaluate/EOD crons, and the response_sla nudge kind.
Phase C — web surfaces + config UI
WaitingCard, WaitingView, and the Settings Response SLAs config section.
Cutover
Operator provisions SLACK_USER_TOKEN + INGEST_PENDING_CN secrets, applies migration 023 remotely (held), flips off the runtime Slack leg, then deploys.
Out of scope
docs/superpowers/specs/2026-07-11-response-slas-design.mdplan ·
docs/superpowers/plans/2026-07-11-response-slas.md