# Observations & Punch List

## Open

- [ ] Pace formula is duplicated — `_compute_pace()` in `server.py` and the equivalent inline calculation in `simulate_display.py` (lines 282-289) share the same logic but live separately. A shared `companion/pace.py` module would keep them in sync.
- [ ] `codex_notify.py` silently drops the message if Codex sends plain-text stdin instead of JSON — the `json.JSONDecodeError` path sets `payload = {}` and the `message` field is never forwarded. Should fall back to treating raw stdin as the message.
- [ ] `_underpace_since` and `_underpace_last_fired` are protected by `_underpace_lock` inside `_check_underpace`, but `get_pace_payload` reads them under the same lock in a separate acquisition — two separate lock grabs rather than one consistent snapshot.

## Notes

- `looks_like_auto_approval` only guards `Notification` events; `PermissionRequest` noise is now suppressed at the ntfy level (`reason != "permission_request"`), so the two mechanisms are complementary rather than redundant.
- The idle-trigger condition (pct<25 on 7d window, ≥2d remaining, 30min cooldown) is untested in production — the 25% threshold and 2-day floor may need tuning once real low-usage weeks are observed.
