82 lines
3.6 KiB
Markdown
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.
|