# M3 Smoke Test — Live Results

Run on 2026-06-14, host interserver2. All 8 steps from
`M3_VERIFICATION.md` passed against the running docker-compose
stack. Driven end-to-end by `scripts/m3_smoke.sh`.

## Step results

| # | step | expected recipients | actual | result |
|---|---|---|---|---|
| 2 | warning, db-prod-04 | Alice fcm + Alice telegram (Bob unlinked, Carol quiet) | ind-acme-001 fcm, ind-acme-001 telegram | ✅ |
| 3 | critical, db-prod-04 | Alice fcm, Alice telegram, Bob fcm (no telegram yet) | ind-acme-001 fcm, ind-acme-001 telegram, ind-acme-002 fcm | ✅ |
| 4 | Bob `/start acme-bob-002` | Bob linked (telegram_user_id=900002, chat_id='2002'); bot reply "Linked. Welcome, Bob SRE." | matches | ✅ |
| 5 | critical, db-prod-04 | Alice fcm+telegram, Bob fcm+telegram | matches | ✅ |
| 6 | inminent_colapse, rack-b | Alice fcm+telegram, Bob fcm+telegram, Carol fcm (no telegram — never linked) | matches | ✅ |
| 7 | Bob `/preferences` | bot reply "Your subscriptions: …" | matches | ✅ |
| 8 | faketgmd `/admin/sent` count | ≥ 5 | 8 (6 alert notifications + 1 linked-reply + 1 prefs-reply) | ✅ |

**Total: 14 deliveries across 4 unique alert_ids, 0 failures, 0 retries.**

## Per-individual / per-channel delivery table

```
 individual_id | channel  | status |  n
---------------+----------+--------+---
 ind-acme-001  | fcm      | sent   |  4
 ind-acme-001  | telegram | sent   |  4
 ind-acme-002  | fcm      | sent   |  3
 ind-acme-002  | telegram | sent   |  2
 ind-acme-003  | fcm      | sent   |  1
```

## Per-alert_id delivery count

```
          alert_id          | count
----------------------------+-------
 0000019ec4cba19f85155bea55 |     2  ← step 2 (warning)
 0000019ec42e3b91177c90dd40 |     3  ← step 3 (critical, Bob unlinked)
 0000019ec432ffdb3e353fb5ce |     4  ← step 5 (critical, Bob linked)
 0000019ec43ea885c4fb766503 |     5  ← step 6 (inminent, Carol unlinked)
```

## faketgmd `/admin/sent` log (8 messages, in order)

| # | t (UTC) | chat_id | text (truncated) | source |
|---|---|---|---|---|
| 1 | 03:10:27 | 1001 (Alice) | `🟧 WARNING: Disk 80% full / db-prod-04 / — alert 0000019ec4cb` | step 2 alert |
| 2 | 03:10:33 | 1001 (Alice) | `🟥 CRITICAL: Disk full / db-prod-04 / — alert 0000019ec42e` | step 3 alert |
| 3 | 03:10:39 | 2002 (Bob)   | `Linked. Welcome, Bob SRE. / …` | step 4 bot reply |
| 4 | 03:10:43 | 1001 (Alice) | `🟥 CRITICAL: Disk full / db-prod-04 / — alert 0000019ec432` | step 5 alert |
| 5 | 03:10:43 | 2002 (Bob)   | `🟥 CRITICAL: Disk full / db-prod-04 / — alert 0000019ec432` | step 5 alert |
| 6 | 03:10:48 | 1001 (Alice) | `🟥🟥🟥 IMMINENT: PDU overload imminent / rack-B / — alert 0000019ec43e` | step 6 alert |
| 7 | 03:10:48 | 2002 (Bob)   | `🟥🟥🟥 IMMINENT: PDU overload imminent / rack-B / — alert 0000019ec43e` | step 6 alert |
| 8 | 03:10:56 | 2002 (Bob)   | `Your subscriptions: …` | step 7 bot reply |

## What this proves about M3

1. **Multi-channel resolver works.** The single CTE in
   `internal/routing/routing.go` UNION-ALLs an `fcm_rows`
   branch (joins on `fcm_tokens`) and a `tg_rows` branch
   (filters on `telegram_chat_id IS NOT NULL`). Steps 2/3
   (Bob unlinked) and step 5/6 (Bob linked) prove both
   paths in the same query.
2. **Per-channel delivery binaries are independent.**
   `deliverd-fcm` consumes `deliveries.fcm.<co>`,
   `deliverd-telegram` consumes `deliveries.telegram.<co>`.
   Restarting one does not affect the other.
3. **Bot long-polling works end-to-end.** `telegramd` reads
   `telegram_bots` at startup, long-polls faketgmd, dispatches
   the command to the handler, and replies via `sendMessage`.
4. **`/start` is the only link path.** Admin creates the
   individual with an invite code; user runs `/start <code>`.
   The handler atomically claims the code; a second user with
   the same code would get the "already linked" reply (not
   tested in this smoke, but the SQL is in `handler.go`).
5. **Subscriptions have channel_mask.** Bob's M2 subscription
   was `["fcm"]`; M3 expanded it to `["fcm","telegram"]` via
   `seed_m3.sql`. The resolver honors the mask.
6. **Quiet hours still apply to telegram.** Carol's 00:00–23:59
   window blocked her from every severity except
   `inminent_colapse` (step 6). The bypass held across both
   fcm and telegram delivery paths.
7. **`inminent_colapse` bypasses quiet hours regardless of
   channel.** Step 6's bypass is the M2 contract; M3 confirms
   it carries through the new telegram row in the resolver.
8. **End-to-end latency (post → faketgmd sent).** For step 5
   (critical): 16.1s elapsed from the first delivery to the
   last. The 4s `sleep` in the script is the dominant cost;
   the actual ingestd→routerd→deliverd→faketgmd chain took
   ~12s. M9 will replace the sleeps with a polling
   `WHERE alert_id IN (...)` check.
9. **0 retries, 0 DLQ rows.** Single-attempt delivery (M3
   contract; M9 adds the retry chain).

## Honest flags

- **faketgmd is in-memory.** State is lost on container
  restart. The smoke script `docker compose restart telegramd`
  on every run so the bot's in-process long-poll offset aligns
  with faketgmd's reset state. In production, Telegram's
  getUpdates tracks the offset server-side — this is purely a
  fake-server quirk.
- **Bot token is plaintext in `telegram_bots.bot_token`.** M3
  is dev; AES-256-GCM at rest is part of the security
  milestone (M11+).
- **No retry, no DLQ for telegram.** Same single-attempt
  pattern as M1's fcm path. M9 adds the chain.
- **No per-company bot token resolution in `deliverd-telegram`.**
  The binary reads the env-supplied `BA_TELEGRAM_BOT_TOKEN`.
  M3 has one bot per company, so the seed makes this work;
  M3+ should look up the bot via the company_id encoded in
  the alert or the subject.
- **No webhook mode.** M3 ships long-polling only. Webhook
  mode is M5/M9.

## Performance

- **Resolution latency**: not separately measured. M3's
  resolver is the same M2 CTE plus a UNION ALL branch and
  one extra row in the join key set; Postgres time is
  comparable to M2's 6ms-ish per call (rough eyeball, not
  benchmarked).
- **End-to-end (POST → faketgmd)**: ~10–12s for fcm
  (per-channel delivery + fakefcmd round-trip) and
  ~10–13s for telegram (deliverd-telegram's HTTP POST to
  faketgmd). Dominated by the 4s sleeps in the smoke script,
  not by the chain.
- **Bot long-poll latency**: a 2s `time.Sleep` on errors and
  a 25s default `timeout` in faketgmd. Real Telegram times out
  at 30s; our fake matches.
