Repo Relay is a short-lived process. A GitHub workflow run invokes it, it processes exactly one event, updates Discord, persists state, and exits. There is no long-running server. This page covers how it stays consistent across those one-shot runs.
Run lifecycle
- The CLI entry point reads the GitHub event from the runner environment.
- It maps the event name to an internal event type and runs a pre-filter to skip events that would be discarded anyway.
- It connects to Discord (with retry/backoff if the gateway session limit is hit), then validates that the bot has the required permissions in every configured channel.
- It dispatches the event to the matching handler, which reads and writes the state DB and edits or creates Discord messages.
- It disconnects, closing the database cleanly.
Message flow
The PR flow is the core. On PR open, a handler builds an embed and creates a thread attached to it, then records the PR-to-message and PR-to-thread mappings in SQLite. On later events, the handler looks up the stored mapping, edits the existing embed to reflect current status, and posts the update into the stored thread. Status used to rebuild an embed (CI state, review state, cached PR metadata) all comes from the database, so the embed stays accurate even though each run is independent.
State storage
State is a SQLite database (WAL mode for safe concurrent access) located at ~/.repo-relay/{repo-name}/state.db, or under STATE_DIR if set. It holds:
| Table | Contents |
|---|---|
| PR message mapping | PR number to Discord message and thread IDs |
| PR status | Copilot, agent-review, and CI status per PR |
| PR data | Cached PR metadata used to rebuild embeds |
| Event log | Event history for debugging |
The database holds only Discord message IDs and PR metadata — no secrets — which is what makes it safe to cache (see Runners and State Persistence). On startup it runs an integrity check, and on close it checkpoints the WAL, making it safe for cache-based persistence.
Stale message handling
If a tracked Discord message has been deleted, fetching it returns an “Unknown Message” error. Repo Relay catches this, clears the stale database entry, and creates a fresh embed and thread. If the state DB itself is missing (for example a cache miss on a GitHub-hosted runner), it falls back to searching the last 100 messages in the channel to recover the existing PR message.
Thread architecture
Each PR has exactly one embed message with one attached thread. Updates are posted into the thread rather than as replies to the embed. Threads auto-archive after 24 hours and are unarchived automatically when a new update arrives, which is why the bot needs the Manage Threads permission.
Review detection path
Because GitHub Apps using GITHUB_TOKEN cannot trigger review workflow events, review detection runs as a side effect of other events (the piggyback approach) and, optionally, a scheduled poll. See Review detection for the full explanation.
Related
- Event and Notification Model — what each event produces.
- Developer Guide — file layout and extension points.
- Source of truth: the repo-relay repository.