4.1 KiB
spec, created, status
| spec | created | status |
|---|---|---|
| docs/specs/tasks/requirements/multi-branch.md | 2026-08-12 | 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.tsso request invalidation and display identity are separate:last_synced_atstill 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.tsxand its desktop/mobile composition unchanged. Stabilizing hook output preventshasPRFilesfrom oscillating, so the existingPRFilesSectionstays mounted and preserves user collapse state.
Tests
- What: After an initial PR file response resolves, advancing
last_synced_atstarts 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-lockfilepassed and installed the fresh-worktree dependencies.- RED: the deferred timestamp-refresh test failed because
filesByPRKeybecame 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 typecheckpassed: 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:
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.