Files
kandev/docs/plans/stable-pr-changes-refresh/plan.md
T
2026-08-23 17:34:52 +00:00

57 lines
4.1 KiB
Markdown

---
spec: docs/specs/tasks/requirements/multi-branch.md
created: 2026-08-12
status: complete
---
# Implementation Plan: Stable PR Changes Refresh
## Overview
The PR Changes row flickers because `useActiveTaskPRsWithFiles` includes `last_synced_at` in its request key and prunes the prior key before the replacement request resolves. Preserve the last resolved files for the same logical workspace/task/PR while refreshing, then replace them atomically when the current response lands. Scope changes and removed PRs must continue to clear stale data.
## Frontend
### PR file refresh cache
- Update `apps/web/hooks/domains/github/use-active-task-pr-files.ts` so request invalidation and display identity are separate: `last_synced_at` still triggers one new request, while the last resolved files remain readable under the current PR until that request settles.
- Preserve the existing workspace response guard and garbage collection for removed PRs. Do not retain files across workspace changes, task changes, PR removal, or a different logical PR.
- Keep `apps/web/components/task/changes-panel-body.tsx` and its desktop/mobile composition unchanged. Stabilizing hook output prevents `hasPRFiles` from oscillating, so the existing `PRFilesSection` stays mounted and preserves user collapse state.
## Tests
- **What:** After an initial PR file response resolves, advancing `last_synced_at` starts one refresh and leaves the prior files observable until the replacement resolves.
- **File:** `apps/web/hooks/domains/github/use-pr-workspace-scope.test.ts`
- **How:** Render the hook with a deferred second WebSocket response; assert the old file remains during the pending interval and is atomically replaced afterward.
- **What:** Workspace/task/PR scope changes and PR removal never reuse retained files.
- **File:** `apps/web/hooks/domains/github/use-pr-workspace-scope.test.ts`
- **How:** Extend the existing deferred-response coverage with same-workspace task/PR removal cases as needed by the chosen cache shape.
- **What:** A failed background refresh retains resolved files, while an initial failure remains empty.
- **File:** `apps/web/hooks/domains/github/use-pr-workspace-scope.test.ts`
- **How:** Reject a controlled replacement request after the initial response resolves and assert the stable list remains observable.
## E2E Tests
No new Playwright scenario is planned. This repair changes only hook-level cache normalization and deliberately leaves layout, touch behavior, scrolling, navigation, and viewport composition unchanged. The existing `apps/web/e2e/tests/task/mobile-changes-panel.spec.ts` covers the mobile PR Changes surface; the deferred refresh race is deterministic and more reliably proven at hook level than through the polling fixture.
## Verification Results
- `cd apps && pnpm install --frozen-lockfile` passed and installed the fresh-worktree dependencies.
- RED: the deferred timestamp-refresh test failed because `filesByPRKey` became empty before the replacement response resolved.
- RED: the controlled refresh-rejection test failed because the settled error replaced retained files with an empty list.
- `cd apps && pnpm --filter @kandev/web test hooks/domains/github/use-pr-workspace-scope.test.ts && pnpm --filter @kandev/web lint && pnpm --filter @kandev/web typecheck` passed: 5 tests, zero lint warnings, and clean TypeScript compilation.
- Review remediation: a reverse-order T2/T3 regression failed before the desired-key guard because late T2 replaced T3, then the same test passed after superseded success/failure handlers were rejected. The focused suite now passes 6 tests with lint and typecheck clean.
## Implementation Waves And Parallel Candidates
Sequential:
- [x] [task-01-stabilize-pr-file-refresh](task-01-stabilize-pr-file-refresh.md)
No parallel candidates. The implementation and regression test exercise one hook contract.
## Risks
- Retaining by an identity that is too broad can leak files across task, workspace, repository, or PR switches. The regression test must cover those invalidation boundaries.
- Retaining by the timestamped request key recreates the flicker. Request freshness and displayed-data identity must be modeled separately.