Skip to main content

Module listen

Module listen 

Source
Expand description

Listening for what happens after a review.

Every other command does one pass and exits, so a review asked for again after spar posted one waited for a person to notice and run spar again. spar listen waits instead, and it waits for free: the only thing that calls an agent is a review, a second look, or a check-in, and none of those starts until GitHub says something happened.

A second look is asked for, never assumed: a review requested again or a mention starts one, and a push or a comment alone does not. The author may still be working, and a comment may be another reviewer’s review rather than an answer to this one. The look that runs reads every commit and reply since the review on record.

Two sources feed it, because neither is enough alone. GitHub’s notifications say why a thread moved, a review request, a mention, a comment, and they are polled with a conditional request that costs nothing against the rate limit when nothing changed. But a push raises no notification for a reviewer, so a slower sweep of the open pull requests compares each head with the commit spar last read. Notifications are never raised for the viewer’s own actions, which is also why spar’s own comments never wake it.

Events settle before anything runs: a review request followed by three comments in the same minute is one second look, not four. What runs is decided by a table that is a pure function of the event, a few facts about the item, and the [listen] settings, so it can be read and tested on its own.

Structs§

Event
One thing that happened to one item.
Facts
What plan needs to know about an item beyond what is pending. Read once per dispatch, never per tick.
ListenState
Everything the listener keeps between ticks and between runs, in .spar/state/listen.json.
Opts
How one spar listen was asked to run.
Pending
What has happened to one item since spar last acted on it. Persisted, so a restart in the middle of settling loses nothing: the notification is already past the watermark and will not come again.
Subject
Thread
One notification thread, as GET /repos/{owner}/{repo}/notifications returns it.
Watched
What the listener knows about one item it is watching.

Enums§

Action
What the dispatch table decided, before anything runs.
Trigger

Constants§

STATE_VERSION

Functions§

backoff_secs
A minute, doubling, to a quarter of an hour.
dispatch_backoff_secs
A quarter of an hour, doubling, to six hours.
due
Whether an item has settled: quiet for settle seconds, or pending for max_wait however noisy, or --once.
events_from_sweep
What a sweep of the open pull requests says happened.
events_from_threads
Turn a poll’s threads into events, skipping what was already seen, and advance the watermark past them.
merge
Fold one event into what is pending for its item.
next_due
When the earliest pending item this run acts on becomes due and is past any wait a failure put on it. Leaving the wait out woke the loop every second until it ended.
next_link
The URL of the next page, from a Link header.
number_from_url
The number at the end of an issue or pull request API URL.
parse_duration
30m, 2h, 1d, 1h30m.
plan
The dispatch table.
poll_interval
How long to wait before the next poll: the config, what GitHub asked for, and the floor, whichever is largest.
prune_seen
Forget threads not seen for a week.
rate_limit_wait
How long a rate limited answer says to wait, clamped to a minute and an hour, or None when the answer was not about the rate limit.
request_answered
Whether the review the viewer is asked for is already on record: the newest request that counts for the viewer, as review_pending counts one, was made no later than the posted review began at reviewed_at.
retry_wait
How long an item whose dispatch did not finish waits to run again. A head that moved under it is GitHub or the author rather than a failure, so the next look starts once the push settles. Anything else backs off.
review_pending
Whether a pull request’s reviewRequests ask the viewer now: by name, or through a team in_team says the viewer is in. A team nobody could check counts as asking, which is how every team request used to be read.
run
Listen until stopped, or once.
settle
A notification’s reason is the one its thread was first raised for and it never changes, so a thread that began as a review request says so on every later update, spar’s own review among them. What the pull request says now decides: when nobody is asking any more, or the request is older than the review on record and so was answered by it, the update is only something happening on a thread the viewer is part of. True when it was.
settle_mention
A mention is a reason a thread keeps too, so a thread that began with one says so on every later update. On a pull request spar reviewed, or an issue whose pull request it reviewed, only a mention made since that review asks for a second look; any other is only something happening on a thread the viewer is part of. On any other issue only a mention spar answer has not judged asks anything, which is what keeps spar’s own answer, moving the thread it was raised on, from asking for another. True when it was.