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
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.
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.
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.
On a successful list, call JobStore::enqueue and persist the new ETag and last_polled_at.
Handle a forge error for one repo without aborting the tick for the others.
Log per tick: repos considered, repos polled, issues enqueued. This is the
loop an operator watches to decide whether the thing is working.
Honour SIGTERM promptly — finish the repo in flight, do not start another.
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.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Part of #1. Depends on: PgStore JobStore impl, Gitea client
Goal
Give
tireless-worker pollan actual loop. Today it logs one line and waits forSIGTERM.
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 comparingintervals by hand.
Files
crates/tireless-worker/src/main.rs— wire the poll role to a loopcrates/tireless-worker/src/poll.rs— new; the loop itselfcrates/tireless-core/src/config.rs— a quiet-window predicate, if it iscleaner to test there than in the worker
Steps
pollmodule with a loop that ticks, selects due repos, and sleepsuntil the next due time rather than spinning on a fixed short interval.
Poll::clampto each repo's requested interval, and add jitter up topoll.jitter_seconds, derived from the repo id so it is stable per reporather than re-randomised every tick.
HH:MMinlocal time;
Config::validatealready guarantees both ends are present orneither.
JobStore::enqueueand persist the new ETag andlast_polled_at.loop an operator watches to decide whether the thing is working.
Acceptance
cargo test --workspacecargo clippy --all-targets --all-features -- -D warningsmin_interval_secondsispolled at the floor, not at the requested rate.
between repos.
outside it, including a window that wraps midnight.
Out of scope
Make the clock injectable instead if the tests need it.