Repository navigation
sources: add github_repo_events — one record per issue/PR event, with the login that acted - #513
Open
polyglotAI-bot wants to merge 1 commit into
Open
polyglotAI-bot wants to merge 1 commit into
polyglotAI-bot wants to merge 1 commit into
Conversation
… the login that acted
The notifications source cannot say who did something: a notification is one
record per thread, with no actor and no event id, it merges every event
between two polls into one update, and its reason keeps its value after later
activity. So its actor guardrail can only judge who is involved in a thread,
and comment-less events (assignments, review requests) can only be attributed
by guessing from the thread's history.
github_repo_events reads GET /repos/{owner}/{repo}/issues/events and emits one
record per event, naming the one login that acted, so allow_actors judges who
did it. The doer is not the `actor` field for every type: an assigned event
puts the assignee in `actor` and the doer in `assigner`, and a mentioned event
records no author at all. Each supported type (review_requested,
review_request_removed, assigned, unassigned) therefore reads a fixed field
and never falls back to `actor`; an event without it is dropped.
- Opt-in (sync.github_repo_events), default events review_requested and
assigned, only events aimed at the authenticated account by default, never
the account's own actions; optional username for tokens without /user.
- Records hold only immutable event fields, so a re-read is byte-identical
and the inbox does not re-surface it; no issue title or body.
- Per-repo JSON cursor: the position is the newest event seen, held back to
the read's server time minus 10 minutes, so late-listed events are caught
and a quiet repo reads nothing twice. No backfill on the first run or for a
newly added repo; a failed repo keeps its position.
- Conditional requests: a 304 costs no rate limit; the ETag is only reused
after a poll that emitted nothing and for the position it produced.
Docs, example config, template source lists and web UI mappings updated.
Installer output is unchanged (the source is off by default).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
github_repo_events, an opt-in source that emits one record per issue/PR event and names the one login that acted: "alice requested a review from you on PR #7", "bob assigned you issue #9". Theallow_actorsguardrail on it then judges who did the thing, not who is involved in the thread.The
github(notifications) source cannot do this, and no enrichment can make it:reasonkeeps its value after later activity. So a maintainer's review request and a stranger's later comment on the same PR become one record, and no single actor describes it.This source reads
GET /repos/{owner}/{repo}/issues/eventsinstead: newest first, full history, PRs included, one entry per event.Who acted is not the
actorfieldVerified against live repositories:
actorholdsreview_requestedreview_requesterreview_request_removedreview_requester(the account that removed the request, not the original requester)assigned/unassignedassignermentionedSo each supported type reads a fixed field (
_EVENT_SPECS) and never falls back toactor. For the two review types, an event whoseactordiffers fromreview_requesteris dropped. Any event without its field is dropped, never credited to a guess.mentionedcannot be attributed, so it is not supported: @-mentions stay on the notification source, which carries the comment and its author.Behaviour
sync.github_repo_events, off by default):repos— required;events— default[review_requested, assigned], alsoreview_request_removed,unassigned;only_targeting_me— defaulttrue: only events whose target is the authenticated account;allow_actors/deny_actors;username— set when the token cannot read/user, e.g. a GitHub App token;schedule,batch_size,condense.only_targeting_me. A team request names the team, not its members, and a member cannot be identified.github_eventscovers those.actor,event(also asreason, for the existing GitHub renderer),target,target_type,number,subject_type, and a URL built from the repo and the number.Dateheader) minus 10 minutes, and it never moves backwards.repos) starts at its newest event. An empty repo starts at the server's time minus 10 minutes.If-None-Match. An unchanged first page answers304, which does not count against the rate limit (verified: a repeat poll used 0).githubsource also on: a review request can reach the inbox twice. The docs suggest one source per consumer, ordeny_reasons: ["review_requested", "assign"]ongithub.Scope
nerve/sources/github_repo_events.py.GitHubRepoEventsSyncConfiginnerve/config.py, registry wiring with theactorguardrail.docs/sources.md(new section, config block, and a pointer from the existing actor-guardrail section),config.example.yaml,docs/config.md, template source lists.nerve inithas nothing to write (the golden-output tests stay as they are)./repos/{o}/{r}/issues/comments.ghhelper across the GitHub sources: each source keeps its own, as they do today.Test plan
tests/test_github_repo_events_source.py— 45 tests, organized by area:assignedwhereactoris the assignee; a missing doer is dropped; a reviewactor/requester mismatch is dropped.username, repo dedupe.ghplumbing: 304 exit handling, theDateheader (including a zoneless one), kill on timeout and on cancellation.SourceRunner+ database, where a re-read event keeps its rowid (not re-surfaced).5792 passed, 39 skipped.npm run buildpasses.review_request_removedattribution (verified live, plus theactorcheck), ETag kept only after an empty poll, cursor normalization, a built URL, the server clock for an empty repo, a guard for non-string timestamps, kill on cancellation,username, docs corrections, and guidance for using it alongsidegithub.Dateheaders, and made the page-cap warning name the time range it skips.asyncio.shieldaround the finalproc.wait()was not added: the process is already killed, so a second cancellation there leaves nothing running.🤖 Generated with Claude Code