# Heartbeat Refactor Plan

## Current State

- 1981 lines, 30+ functions, 1 monolithic file
- 16 responsibility domains crammed into one file
- Fragile health gating (all bools in results dict)
- Missing sidecar engine metrics, Puck telemetry, dead letter checks

## Proposed Module Structure

```
scripts/heartbeat/
├── __init__.py              # Package marker
├── __main__.py              # Entry point: main(), _main()           (~120 lines)
├── config.py                # Constants, thresholds, fingerprints     (~50 lines)
├── state.py                 # Pending messages, stathera counts       (~70 lines)
├── telegram.py              # Send, delete, dead-man switch           (~110 lines)
├── alert_dedup.py           # Redis dedup                            (~45 lines)
├── formatter.py             # _format_message() + helpers             (~340 lines)
├── checks/
│   ├── __init__.py          # run_checks() orchestrator               (~100 lines)
│   ├── clickhouse.py        # CH connectivity + stats                 (~80 lines)
│   ├── services.py          # systemd status, OOM, overrides          (~140 lines)
│   ├── stathera.py          # Gaps, overlaps, fingerprints            (~220 lines)
│   ├── kintsugi.py          # NDJSON, repair summary, stuck           (~170 lines)
│   ├── freshness.py         # Per-symbol freshness + coverage         (~125 lines)
│   ├── resources.py         # CPU/RAM/load via Prometheus             (~110 lines)
│   ├── version.py           # Version drift, hotfix, .pth             (~180 lines)
│   ├── seeder.py            # Seeder state + journal                  (~110 lines)
│   └── sidecar.py           # NEW: engine metrics, Puck, WS health   (~150 lines)
```

## Bugs Found

1. **Emergency alert HTML/Markdown mismatch** (line 1889) — uses `\\n` and backticks but parse_mode is HTML
2. **GNU `date -d` not portable** (line 411) — fails on macOS
3. **Fragile health gating** (line 1907) — `all(v is True for bools)` with no explicit gate list
4. **Inverted `kintsugi_stuck` semantics** — True means healthy, backwards from all other keys
5. **Mixed types in results dict** — annotated `bool | str` but contains `int` and `list[dict]`

## Missing Checks (New for sidecar.py module)

- trades_received / bars_emitted from /health endpoint
- Forming bar staleness per symbol
- Puck gap detection vs fill success rate
- WS reconnection count
- Dead letter count + oldest file age
- Checkpoint age vs CH latest
- Rate limiter utilization
- CH delayed inserts / merge queue depth

## Message Readability Improvements

- 📡 for streaming (done), inline "normal range" annotations
- Emoji legend at bottom of unhealthy messages
- DEGRADED tier between HEALTHY and UNHEALTHY
- Explicit HEALTH_GATE_KEYS set instead of fragile all-bools
