Files
2026-08-23 17:34:52 +00:00

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.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:

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.