fix(github): stop PR polling for archived tasks (#643)

* fix(github): stop PR polling for archived tasks

The PR watcher kept making GitHub API calls for tasks the user had
archived, wasting rate limit and surfacing stale checks. Hide archived
and orphaned watches from the poller via an INNER JOIN on tasks, and
proactively prune watches when task.updated (archived_at set) or
task.deleted events fire. Closes #989.

* fix(github): address review comments on PR watch pruning

- Detect archived_at from a non-empty string value instead of key
  presence, so a future null/zero archived_at in the task event payload
  doesn't silently prune watches on non-archive updates.
- Track task-event subscriptions on the Service and release them in the
  provider cleanup so handlers don't accumulate if the service is torn
  down and re-created while the event bus persists.
- Propagate RowsAffected errors from DeletePRWatchesByTaskID instead of
  silently discarding them.
This commit is contained in:
José Almeida
2026-04-19 21:49:05 +01:00
committed by GitHub
parent 4b55a69145
commit 375472cc02
6 changed files with 326 additions and 3 deletions
+23 -2
View File
@@ -151,10 +151,17 @@ func (s *Store) GetPRWatchByTask(ctx context.Context, taskID string) (*PRWatch,
return &w, err
}
// ListActivePRWatches returns all active PR watches.
// ListActivePRWatches returns all active PR watches whose task is not archived.
// Watches for archived tasks (and orphaned watches whose task row was hard-deleted)
// are excluded so the poller stops making GitHub API calls for them. An INNER JOIN
// on `tasks` is used so orphans are dropped automatically.
func (s *Store) ListActivePRWatches(ctx context.Context) ([]*PRWatch, error) {
var watches []*PRWatch
err := s.ro.SelectContext(ctx, &watches, `SELECT * FROM github_pr_watches ORDER BY created_at`)
err := s.ro.SelectContext(ctx, &watches, `
SELECT w.* FROM github_pr_watches w
INNER JOIN tasks t ON t.id = w.task_id
WHERE t.archived_at IS NULL
ORDER BY w.created_at`)
return watches, err
}
@@ -173,6 +180,20 @@ func (s *Store) DeletePRWatch(ctx context.Context, id string) error {
return err
}
// DeletePRWatchesByTaskID deletes all PR watches for a task. Returns the number
// of rows removed so callers can log meaningful diagnostics.
func (s *Store) DeletePRWatchesByTaskID(ctx context.Context, taskID string) (int64, error) {
res, err := s.db.ExecContext(ctx, `DELETE FROM github_pr_watches WHERE task_id = ?`, taskID)
if err != nil {
return 0, err
}
n, err := res.RowsAffected()
if err != nil {
return 0, err
}
return n, nil
}
// UpdatePRWatchPRNumber updates a PR watch's PR number after discovery.
func (s *Store) UpdatePRWatchPRNumber(ctx context.Context, id string, prNumber int) error {
_, err := s.db.ExecContext(ctx,