MalloryEstate Briefing
M·054Design agreed. Not yet built.
← All briefings

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.

Dated 2026-07-11Programme Reliability & Ops

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_pending (D1, migration 023)
One row per ball-in-court thread, keyed {channel}:{conversation_id}:{thread_key}; tracks status (open/resolved/snoozed/dismissed), sla_due_at, and last_nudged_at.
ChannelAdapter interface
{id, poll, permalink, nativeUrl, isResolved} — Slack is the only v1 implementation; the poll cron iterates registered adapters.
Detector + SLA evaluator (pure functions)
Detector turns raw channel state into PendingItems with ball-in-court/resolution flags; evaluator computes per-item clock state, finds newly-crossed thresholds, coalesces, and enforces min-interval — no I/O in either.
POST /ingest/pending
Service-token-gated ingest seam for future non-pollable channels, built and tested in v1 despite being unused by Slack.
/nudge engine (reused)
Existing mode-gate, quiet-hours, dedupe, batch-flush, web+FCM delivery layer — Response SLAs adds a new response_sla nudge kind rather than a new delivery path.
WaitingCard.svelte / WaitingView.svelte
Feed card (ambient glance with a countdown bar) and a dedicated grouped view (Breached · Due soon · Later · End-of-day) with jump/snooze/dismiss actions.
Settings "Response SLAs" section
Global thresholds, per-method clock/SLA/windows, and an auto-populating per-channel posture + override list.

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

Email / WhatsApp / Discord / Asana / GitHub adapters (interface-ready, not built)MKCE 2nd-Google accountAuto-suggesting which channels to promote to ActiveSLA analytics / history dashboardsReplying from Mallory (v1 surfaces + nudges only, never sends)Natural-language snooze
spec · docs/superpowers/specs/2026-07-11-response-slas-design.md
plan · docs/superpowers/plans/2026-07-11-response-slas.md
Mallory · Estate briefing