Files
kandev/docs/specs/integrations/system-design/github-review-task-cleanup.md
T
2026-09-23 17:39:51 +01:00

82 lines
3.6 KiB
Markdown

---
status: draft
system: integrations
requirements:
- REQ-INTEGRATIONS-GITHUB-REVIEW-CLEANUP-001
created: 2026-09-23
owners:
- kandev
---
# GitHub review task cleanup system design
## Purpose and boundaries
`internal/github` owns review-watch deduplication, cleanup policy, and GitHub
feedback requests. The task repository owns `tasks.archived_at`. The cleanup
selector reads that state; it does not change task archive state. Shared
provider backoff remains a Platform contract.
## Requirement mapping
| Requirement | Design section |
| --- | --- |
| `REQ-INTEGRATIONS-GITHUB-REVIEW-CLEANUP-001` | [Eligibility](#eligibility); [Cleanup flow](#cleanup-flow); [Explicit actions](#explicit-actions) |
## Eligibility
Add selectors for **scheduled** review cleanup in `internal/github/store.go`.
Select a review-task record when its `task_id` is empty, its task row is
missing, or its task row exists with `archived_at IS NULL`. Exclude a record
only when a matching task row is archived. Use a left join or an equivalent
predicate so missing tasks and incomplete reservations remain visible.
Keep `ListReviewPRTasksByWatch` and `ListAllReviewPRTasks` as complete inventory
methods. Watch deletion, reset, and explicit cleanup use those inventories.
Scheduled per-watch cleanup and the scheduled global orphan sweep use the new
eligible selectors. The global sweep still skips enabled-watch records that
the per-watch pass handled in the same cycle. A disabled watch remains eligible
through the global sweep.
The dedup row stays in storage while the task is archived. Unarchive makes the
same row eligible again, so the watch cannot create a duplicate task for that
pull request. No schema migration or backfill is required.
## Cleanup flow
Before a scheduled Auto feedback request, check the conditions that retain a
task regardless of PR state: an enabled lifecycle prompt and a user-authored
message. If either is present, skip feedback and keep the task and dedup row.
If either local check fails, fail closed and keep both. Keep Always delete and
Never behavior unchanged. Incomplete reservations have no task to inspect and
retain their terminal-PR cleanup path.
Eligibility comes from the database before client resolution where practical.
An empty eligible set does not resolve a credential or call GitHub. The poller
must check eligibility again on each scheduled pass; archive and unarchive
take effect without a service restart. A task archived after selection may
finish an already-started request, but later passes exclude it.
## Explicit actions
The manual all-workspace and workspace cleanup endpoints continue to inspect
archived records under each watch's policy. Watch deletion and reset continue
to enumerate archived tasks and apply their existing destructive behavior.
Do not route these actions through a scheduled-only selector.
## Failure and recovery
A missing task row remains visible to the cleanup path that removes stale
dedup records. A task-not-found result from the session checker counts as no
user-authored message so cleanup can remove its stale dedup record. Other
task-retention check failures leave both task and record intact. A watch read
failure leaves its records untouched for that cycle.
Provider failures follow the [Platform backoff design](../../platform/system-design/pr-watch-and-bounded-storage.md#provider-failure-backoff-and-health).
## Verification
SQLite-backed service and poller tests cover enabled and disabled watches,
archived and active tasks in one batch, incomplete and missing-task records,
unarchive, Auto retention, and explicit actions. A counting GitHub client
asserts feedback call counts over repeated scheduled cycles.