Re-notify on still-active action-required signals at a configurable cadence #67

Open
opened 2026-05-23 20:55:29 +00:00 by coilysiren · 0 comments
Owner

Originally filed by @coilysiren on 2026-05-01T17:41:25Z - https://github.com/coilysiren/repo-recall/issues/41

Problem

Today the push dispatcher fires exactly once per transition into action-required. See State::diff_and_record_signals and the dispatcher comment in docs/push-notifications.md. A signal id (<repo_id>:<signal>) is recorded in seen_signals on first notify, and only pruned when it disappears from the action-required set. A repo that stays broken for hours or days notifies once. If the toast is missed (phone off, DND, screen-locked-and-cleared), there is no re-notify until the signal clears and re-breaks.

What this issue asks for

Make re-notification opt-in and time-based, deduped per signal id, with a configurable interval.

Behavior:

  • New signal (not in seen_signals): notify immediately, record first_seen + last_notified = now. (Unchanged.)
  • Existing signal still in the action-required set: re-notify only if now - last_notified >= REPO_RECALL_RENOTIFY_SECS. On send, bump last_notified.
  • Signal disappears from action-required: prune from seen_signals. (Unchanged.)
  • REPO_RECALL_RENOTIFY_SECS = 0 disables re-notification (current behavior, kept as the default to avoid surprising existing installs).

Schema change

Add last_notified INTEGER NOT NULL to seen_signals in src/state.rs. Backfill is irrelevant since the state DB is the durable one but signal rows are cheap to recreate; on first boot after upgrade, treat missing column as a fresh schema (drop+recreate is fine for seen_signals specifically since the worst case is one extra notify per currently-broken repo).

API change

Split diff_and_record_signals into two responsibilities:

  1. diff_signals(current_ids) -> { new: Vec<String>, due_for_renotify: Vec<String> } - returns ids to actually push.
  2. record_notified(ids) - bumps last_notified for the ids that successfully dispatched.

Pruning of disappeared ids stays in the diff step.

Config

Var Default Meaning
REPO_RECALL_RENOTIFY_SECS 0 0 = never re-notify (today's behavior). Otherwise minimum seconds between re-notifies for the same signal id.

Document in AGENTS.md env table and in docs/push-notifications.md under "Push dispatcher."

Test plan

  • Unit test in state.rs: insert a signal, diff again with same ids and last_notified = now-10s and renotify=60 -> not due. Diff again with last_notified = now-120s -> due. Diff with id removed -> pruned.
  • Smoke test in tests/smoke.rs: drive the dispatcher path with a fake subscription endpoint (existing pattern) and assert the renotify cadence honors the env var.

Out of scope

  • Per-signal-type cadences (e.g. CI every 30m, dirty tree every 4h). One global knob first, split later if anyone actually wants it.
  • Quiet hours / DND windows.
  • Backoff (renotify-then-doubling). Linear cadence is enough.
_Originally filed by @coilysiren on 2026-05-01T17:41:25Z - [https://github.com/coilysiren/repo-recall/issues/41](https://github.com/coilysiren/repo-recall/issues/41)_ ## Problem Today the push dispatcher fires exactly once per transition into action-required. See [`State::diff_and_record_signals`](src/state.rs) and the dispatcher comment in [`docs/push-notifications.md`](docs/push-notifications.md). A signal id (`<repo_id>:<signal>`) is recorded in `seen_signals` on first notify, and only pruned when it disappears from the action-required set. A repo that stays broken for hours or days notifies once. If the toast is missed (phone off, DND, screen-locked-and-cleared), there is no re-notify until the signal clears and re-breaks. ## What this issue asks for Make re-notification opt-in and time-based, deduped per signal id, with a configurable interval. Behavior: - New signal (not in `seen_signals`): notify immediately, record `first_seen` + `last_notified = now`. (Unchanged.) - Existing signal still in the action-required set: re-notify only if `now - last_notified >= REPO_RECALL_RENOTIFY_SECS`. On send, bump `last_notified`. - Signal disappears from action-required: prune from `seen_signals`. (Unchanged.) - `REPO_RECALL_RENOTIFY_SECS = 0` disables re-notification (current behavior, kept as the default to avoid surprising existing installs). ## Schema change Add `last_notified INTEGER NOT NULL` to `seen_signals` in [`src/state.rs`](src/state.rs). Backfill is irrelevant since the state DB is the durable one but signal rows are cheap to recreate; on first boot after upgrade, treat missing column as a fresh schema (drop+recreate is fine for `seen_signals` specifically since the worst case is one extra notify per currently-broken repo). ## API change Split `diff_and_record_signals` into two responsibilities: 1. `diff_signals(current_ids) -> { new: Vec<String>, due_for_renotify: Vec<String> }` - returns ids to actually push. 2. `record_notified(ids)` - bumps `last_notified` for the ids that successfully dispatched. Pruning of disappeared ids stays in the diff step. ## Config | Var | Default | Meaning | |-----|---------|---------| | `REPO_RECALL_RENOTIFY_SECS` | `0` | `0` = never re-notify (today's behavior). Otherwise minimum seconds between re-notifies for the same signal id. | Document in [`AGENTS.md`](AGENTS.md) env table and in [`docs/push-notifications.md`](docs/push-notifications.md) under \"Push dispatcher.\" ## Test plan - Unit test in `state.rs`: insert a signal, diff again with same ids and `last_notified = now-10s` and renotify=60 -> not due. Diff again with `last_notified = now-120s` -> due. Diff with id removed -> pruned. - Smoke test in `tests/smoke.rs`: drive the dispatcher path with a fake subscription endpoint (existing pattern) and assert the renotify cadence honors the env var. ## Out of scope - Per-signal-type cadences (e.g. CI every 30m, dirty tree every 4h). One global knob first, split later if anyone actually wants it. - Quiet hours / DND windows. - Backoff (renotify-then-doubling). Linear cadence is enough.
coilysiren added
P4
and removed
P3
labels 2026-05-31 07:01:09 +00:00
Sign in to join this conversation.
No labels
P0
P1
P2
P3
P4
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
coilyco-flight-deck/repo-recall#67
No description provided.