Files
kandev/docs/decisions/2026-08-31-task-owned-pr-watch-identity.md
2026-09-22 10:31:53 +01:00

2.7 KiB

ADR-2026-08-31-task-owned-pr-watch-identity: Keep PR watches task owned

Status: accepted Date: 2026-08-31 Area: backend, GitHub, workflow

Context

Agent sessions are attempts within a task, not independent ownership domains for a pull request. A session-scoped PR-watch key lets resumed or concurrent sessions create duplicate watches for the same task branch. Each duplicate polls GitHub and emits equivalent events, which creates unnecessary task-status projection contention.

Review work can outlive the session that initially discovered the pull request, so deleting watches solely because their provenance session has completed would stop required monitoring.

Decision

github_pr_watches is task-owned. A searching watch is canonical by (task_id, repository_id, branch) and a discovered watch is canonical by (task_id, repository_id, pr_number). session_id remains optional provenance only and must not participate in canonical identity, polling fan-out, or task-status publication.

The active-watch query admits watches for existing, non-archived tasks, including Review tasks whose originating session is complete, and excludes watches without a valid task/repository relationship. Reconciliation resolves one desired branch per task/repository and performs an atomic transition only when it differs from the canonical searching watch. Polling and publication coalesce state by task/repository/PR and observable PR state.

Upgrade cleanup is a one-time, idempotent, transactional migration. It requires a database snapshot before destructive work, retains a deterministic canonical row, prefers discovered rows, preserves the newest status and feedback watermarks, and removes only duplicate or orphaned watch rows.

Consequences

  • Multiple historical or concurrent sessions no longer multiply GitHub requests, feedback events, or status-summary work.
  • A watch is retained based on task lifecycle instead of session completion, preserving Review monitoring.
  • Session-level callers must be migrated to task/repository identity and cannot rely on a separate watch per session.
  • Legacy duplicate cleanup has a documented backup and rollback requirement.

Alternatives Considered

  • Keep session-scoped watches and deduplicate only at polling time. Rejected because duplicate persistent rows still create reconciliation writes and every future consumer must reproduce deduplication.
  • Delete every completed-session watch. Rejected because a Review task can legitimately require monitoring after its original session finishes.
  • Use only (task_id, repository_id) as the key. Rejected because multi-branch tasks require independent searching watches until a PR is discovered.