HoneyBun · Operations · Verified 2026-07-27
Eighteen automated flows reach an operator, and 628 places in the worker code can page
us. Every email goes out through Resend, every text goes out through GHL.
This page is the map and the standing procedure, traced from the worker source rather
than from the registry, because messaging-registry.js lists 13 flows while
63 functions actually send.
No message matches that filter.
Everything above goes to an operator. This half goes to Andrew. It is not a list of messages, it is one pipeline that 628 call sites across 118 worker files feed into. What varies is the severity you pass, and severity alone decides which channels fire.
| Severity | SMS | Push | Board task | Auto-remediate | |
|---|---|---|---|---|---|
| Critical168 call sites | Yes | Yes | Yes | Yes | Yes |
| Warning342 sites, plus 59 that omit it | Yes | No | Yes | Yes | Yes |
| Info and error13 info, 7 error | Yes | No | Yes | Yes | No |
SMS is critical only, by decision. Set 2026-07-24 (board
58d59209) after the Channel 1 routing fix turned the fanout on for the first
time and every severity, including info, started texting. A phone that buzzes for
information stops meaning anything when it buzzes for an outage.
error is not a routed severity. The dispatcher branches on
critical and warning only, so anything else falls to the info branch. That costs it the
immediate auto-remediation dispatch, which fires for warning and critical alone. Seven
call sites pass it on purpose. Thirteen more get it by accident, see the open items below.
Five guards, evaluated in this order. Every one of them exists because of a specific incident, and every suppression still writes a dead-letter record to KV so nothing vanishes silently.
ALERT_MUTEDead-letter only, no external calls at all. The break-glass for an alert storm.A fallback ladder, not a fanout. The first channel that succeeds wins and the rest never run, which is why an SMS can arrive for a warning if email is also broken.
[email protected] to [email protected]. Three consecutive binding failures also write a degradation breadcrumb to KV.Alert call sites per file, top ten of 118.
api/hb-deploy.js63api/hb-clients.js35api/hb-verticals.js31api/hb-provisioning-worker-v1.js21api/hb-session.js20api/gbp/oauth.js18api/crm/invite.js16monitoring/hb-territory-reconciliation.js16api/scheduled/resume_health.js15api/dcc-drift/index.js15| Piece | File | What it owns |
|---|---|---|
| Primary dispatcher | lib/alert-dispatch.js | Severity routing, dedupe, board task, auto-remediation. |
| Hardened wrapper | lib/send-alert.js | Guard stack and the five-channel fallback ladder. |
| SMS recipient | lib/alert-contact.js | Resolves the GHL contact by phone and caches it for 6 hours. |
| Promotion gate | lib/alert-promotion.js | Optional. Holds the board task until an alert recurs. Off by default, fails open. |
Read this before touching anything that sends. Most of these rules exist because something already went wrong once.
crm/invite.js._sendLeadSMS. There is no third option and no reason to
add one.messaging-registry.js, and note that the
registry's wired_in_v1 field means "the membership API accepts writes",
not "this message sends". It is not a health signal.Never guard a cross-module sender with typeof fn === 'function'
On an undeclared name that is silently always false. It never throws, never logs an
error, and the message simply never sends. That is exactly how the operator welcome
went dark for months while logging "not available in this context" on every provision.
Never let a send sit behind a swallowed error
_sendPurchaseConfirmation threw on its first line and the call site caught
it with .catch(). Every paying operator got no confirmation email and no
text, and nothing anywhere reported it.
Drip emails carry no SMS, and that is by design The drip system sends email only. Operators get a text at signup because the invite path sends one separately. Any "add a text to a drip" is new plumbing, not a config change.
SMS to Andrew is critical only Set deliberately after an alert flood. Do not restore warning or info texts without a new decision. A noisy source trips the shared alert breaker and blinds the platform.
| Channel | Provider | Entry point | Notes |
|---|---|---|---|
| Email to operators | Resend | api.resend.com/emails | 41 send sites. Every email, no exceptions. |
| SMS to operators | GHL | _sendLeadSMS(env, phone, body) | Resolves the contact by phone at send time. |
| Email to us | Resend | [email protected] | Every severity. Lands in [email protected]. |
| SMS to us | GHL | sendResilientAlertSms | Critical severity only. |
| Push | Web Push | operatorPush / _alertPush | Operator PWA for leads, internal PWA for alerts. |
beee375b. Needs a decision on volume before building.messaging-registry.js entry that claims it fires at provision-complete.
Board 3ea88a9d. Keep the function, the team resend path still uses it.hb-deploy.js defaults its alerts to error.
Its local sendAlert wrapper signs as severity = 'error' while
every other path defaults to warning. Thirteen of its 63 call sites omit
severity, so they route as info and skip auto-remediation. Nothing is lost, but nothing
is auto-fixed either.