Poll loop: interval floor, per-repo jitter, quiet window #5

Open
opened 2026-08-07 12:39:16 +00:00 by grenade · 0 comments
Owner

Part of #1. Depends on: PgStore JobStore impl, Gitea client

Goal

Give tireless-worker poll an actual loop. Today it logs one line and waits for
SIGTERM.

For each enabled tracked repo whose interval has elapsed, list opted-in issues,
enqueue what is new, and store the returned ETag. The loop must respect the
interval floor, apply per-repo jitter so N repos do not fire together, and skip
everything during a configured quiet window.

Config::poll.clamp() already applies the floor; use it rather than comparing
intervals by hand.

Files

  • crates/tireless-worker/src/main.rs — wire the poll role to a loop
  • crates/tireless-worker/src/poll.rs — new; the loop itself
  • crates/tireless-core/src/config.rs — a quiet-window predicate, if it is
    cleaner to test there than in the worker

Steps

  1. Add a poll module with a loop that ticks, selects due repos, and sleeps
    until the next due time rather than spinning on a fixed short interval.
  2. Apply Poll::clamp to each repo's requested interval, and add jitter up to
    poll.jitter_seconds, derived from the repo id so it is stable per repo
    rather than re-randomised every tick.
  3. Implement the quiet window: while inside it, poll nothing. Parse HH:MM in
    local time; Config::validate already guarantees both ends are present or
    neither.
  4. On a successful list, call JobStore::enqueue and persist the new ETag and
    last_polled_at.
  5. Handle a forge error for one repo without aborting the tick for the others.
  6. Log per tick: repos considered, repos polled, issues enqueued. This is the
    loop an operator watches to decide whether the thing is working.
  7. Honour SIGTERM promptly — finish the repo in flight, do not start another.

Acceptance

  • cargo test --workspace
  • cargo clippy --all-targets --all-features -- -D warnings
  • A test shows a repo requesting an interval below min_interval_seconds is
    polled at the floor, not at the requested rate.
  • A test shows jitter is stable for a given repo across ticks and differs
    between repos.
  • A test shows the quiet-window predicate is true inside the window and false
    outside it, including a window that wraps midnight.

Out of scope

  • Claiming, running agents, or writing anything to the forge. Stage 2.
  • Reconciling stale labels. Stage 2.
  • The runner role — this issue is the poller only.
  • Do not add a "poll now" trigger or an interval below the floor for testing.
    Make the clock injectable instead if the tests need it.
Part of #1. Depends on: PgStore JobStore impl, Gitea client ## Goal Give `tireless-worker poll` an actual loop. Today it logs one line and waits for SIGTERM. For each enabled tracked repo whose interval has elapsed, list opted-in issues, enqueue what is new, and store the returned ETag. The loop must respect the interval floor, apply per-repo jitter so N repos do not fire together, and skip everything during a configured quiet window. `Config::poll.clamp()` already applies the floor; use it rather than comparing intervals by hand. ## Files - `crates/tireless-worker/src/main.rs` — wire the poll role to a loop - `crates/tireless-worker/src/poll.rs` — new; the loop itself - `crates/tireless-core/src/config.rs` — a quiet-window predicate, if it is cleaner to test there than in the worker ## Steps 1. Add a `poll` module with a loop that ticks, selects due repos, and sleeps until the next due time rather than spinning on a fixed short interval. 2. Apply `Poll::clamp` to each repo's requested interval, and add jitter up to `poll.jitter_seconds`, derived from the repo id so it is stable per repo rather than re-randomised every tick. 3. Implement the quiet window: while inside it, poll nothing. Parse `HH:MM` in local time; `Config::validate` already guarantees both ends are present or neither. 4. On a successful list, call `JobStore::enqueue` and persist the new ETag and `last_polled_at`. 5. Handle a forge error for one repo without aborting the tick for the others. 6. Log per tick: repos considered, repos polled, issues enqueued. This is the loop an operator watches to decide whether the thing is working. 7. Honour SIGTERM promptly — finish the repo in flight, do not start another. ## Acceptance - `cargo test --workspace` - `cargo clippy --all-targets --all-features -- -D warnings` - A test shows a repo requesting an interval below `min_interval_seconds` is polled at the floor, not at the requested rate. - A test shows jitter is stable for a given repo across ticks and differs between repos. - A test shows the quiet-window predicate is true inside the window and false outside it, including a window that wraps midnight. ## Out of scope - Claiming, running agents, or writing anything to the forge. Stage 2. - Reconciling stale labels. Stage 2. - The runner role — this issue is the poller only. - Do not add a "poll now" trigger or an interval below the floor for testing. Make the clock injectable instead if the tests need it.
grenade added the tireless/implement label 2026-08-07 12:40:54 +00:00
Sign in to join this conversation.