181 lines
8.7 KiB
Markdown
181 lines
8.7 KiB
Markdown
---
|
|
created: 2026-08-26
|
|
status: complete
|
|
requirements:
|
|
- REQ-UI-PR-TASK-STATUS-SUMMARY-001
|
|
system_design:
|
|
- ../../specs/ui/system-design/pr-task-status-summary.md
|
|
legacy_specs: []
|
|
---
|
|
|
|
# Implementation Plan: PR Task Status Hover Hydration
|
|
|
|
## Overview
|
|
|
|
Make every GitHub task PR indicator disclose details consistently. First add a
|
|
task-scoped hydration path for compact indicators. Then add the author identity
|
|
to the shared summary and the existing mobile drawer.
|
|
|
|
This order makes the data available before the presentation and end-to-end
|
|
work depends on it.
|
|
|
|
## Scope
|
|
|
|
### In scope
|
|
|
|
- Open a tooltip for compact GitHub PR indicators on mouse hover and keyboard
|
|
focus.
|
|
- Load persisted full PR records for only the disclosed task.
|
|
- Deduplicate concurrent loads, reuse store data, guard HTTP and WebSocket
|
|
races, and permit retry after an error.
|
|
- Preserve workspace ownership for live TaskPR updates. The browser must reject
|
|
missing or foreign workspace IDs, and typed backend events must route by their
|
|
owning workspace without falling back to a global broadcast.
|
|
- Show the GitHub author login in the task summary.
|
|
- Show the same author login in the existing coarse-pointer PR-status drawer.
|
|
- Add focused component, desktop Playwright, and mobile Playwright coverage.
|
|
|
|
### Out of scope
|
|
|
|
- Upstream GitHub refreshes from the task-row indicator.
|
|
- Workspace-wide PR loading for the sidebar.
|
|
- Changes to task-summary persistence or projection fields.
|
|
- New task-row touch targets, drawers, navigation, or provider associations.
|
|
- Author support for GitLab or registered provider summaries.
|
|
|
|
## Technical approach
|
|
|
|
### Task-scoped disclosure hydration
|
|
|
|
Add a hook under `hooks/domains/github` that uses `listTaskPRs([taskId])` when
|
|
`taskPRs.byTaskId[taskId]` is empty for the active workspace context. Scope the
|
|
in-flight registry to the Zustand store and key it by workspace, workspace
|
|
context generation, and task. Preserve deletion tombstones so a late response
|
|
cannot restore an association removed while it was pending.
|
|
|
|
Update `PRTaskIcon` and `TaskContributionIcons` so compact and full data use one
|
|
tooltip trigger. Compact content shows localized loading or unavailable text.
|
|
Successful data updates replace the content without unmounting the trigger or
|
|
closing the tooltip.
|
|
|
|
Before response application, compare the active workspace and current store
|
|
records. Preserve matching records that arrived from WebSocket events. Add only
|
|
missing PR identities from the HTTP result.
|
|
|
|
### Workspace-scoped live PR updates
|
|
|
|
Treat `TaskPR.workspace_id` as the authoritative owner of a live update. The
|
|
frontend handler applies an update only when that ID is present and matches the
|
|
active workspace, preserving the current scoped cache for missing or foreign
|
|
payloads. The backend exposes `TaskPR.WorkspaceID` to the notification
|
|
broadcaster and routes typed PR updates and detachments through the fail-closed
|
|
workspace path when authentication is enforced, so an unattributed event is
|
|
dropped instead of broadcast globally.
|
|
|
|
### Author presentation
|
|
|
|
Add optional author data to `ChangeRequestTaskStatusSummaryData`. Derive the
|
|
value from `TaskPR.author_login` and show localized `task:byAuthor` copy below
|
|
the title.
|
|
|
|
Extend `ChangeRequestPopoverHeader` with an optional author value. Pass the
|
|
GitHub author from `PRCIPopover` so desktop status content and the existing
|
|
mobile drawer use the same text.
|
|
|
|
### Localization
|
|
|
|
Add localized loading and unavailable copy to the GitHub namespace in English,
|
|
Portuguese, Simplified Chinese, Hong Kong Chinese, and Taiwan Chinese. Reuse
|
|
the existing localized `task:byAuthor` key for author presentation.
|
|
|
|
## Tests
|
|
|
|
| Acceptance criteria | Evidence |
|
|
| --- | --- |
|
|
| `AC-UI-PR-TASK-STATUS-SUMMARY-001.13` to `.16` | A new hydration-hook test covers task scope, one in-flight request, cache reuse, workspace and task-context changes, deletion during an HTTP request, empty/error retry, and HTTP/WS ordering. Render tests cover mouse and keyboard disclosure states, including focus continuity. |
|
|
| `AC-UI-PR-TASK-STATUS-SUMMARY-001.17` | Summary derivation and shared render tests cover a present and missing author login. |
|
|
| Existing `.1` to `.12` | Existing PR icon and shared summary suites remain green. |
|
|
| `AC-UI-PR-TASK-STATUS-SUMMARY-001.18` | Existing status-chip component tests and mobile Playwright coverage prove drawer author visibility and unchanged task-row navigation. |
|
|
| `AC-UI-PR-TASK-STATUS-SUMMARY-001.19` | `github.test.ts` proves missing and foreign live updates leave the active scoped cache unchanged; `task_notifications_test.go` proves typed events reach only the owning workspace and unattributed events are dropped when auth is enforced; the singleton panel test preserves taskPR scope metadata during task switching. |
|
|
|
|
## E2E tests
|
|
|
|
- `apps/web/e2e/tests/pr/pr-sidebar-hover-hydration.spec.ts` uses the `chromium`
|
|
project. It opens an unrelated task, hovers an inactive task PR indicator,
|
|
observes the task-scoped request, and checks the author and structured rows.
|
|
- `apps/web/e2e/tests/task/mobile-task-status-summary.spec.ts` uses the
|
|
`mobile-chrome` project. It taps the row, opens the current PR-status drawer,
|
|
checks the author, and checks for horizontal overflow.
|
|
|
|
## Mobile design contract
|
|
|
|
- **Desktop outcome:** Hover or keyboard focus opens and hydrates the PR task
|
|
summary.
|
|
- **Mobile entry:** The task row remains the entry point. The user opens the
|
|
task and then uses the existing PR-status chip.
|
|
- **Exemplars:** `session-task-switcher-sheet.tsx` owns task navigation.
|
|
`pr-status-chip.tsx` owns the coarse-pointer drawer.
|
|
- **Hierarchy:** The task row stays primary. The PR author is secondary content
|
|
below the title in the existing drawer.
|
|
- **Presentation:** Reuse the inset PR-status drawer. Do not add another drawer
|
|
or a compact icon hit target.
|
|
- **Scroll and geometry:** Keep the drawer's existing internal scroll owner,
|
|
safe-area behavior, and viewport containment.
|
|
- **Shared state:** Desktop and mobile read the same `TaskPR.author_login`.
|
|
- **Proof:** The mobile Playwright scenario checks navigation, drawer content,
|
|
and horizontal containment.
|
|
|
|
## Work orders
|
|
|
|
- [x] [Task 01: Hydrate PR details on disclosure](task-01-hydrate-pr-details-on-disclosure.md)
|
|
- [x] [Task 02: Show PR author identity](task-02-show-pr-author-identity.md)
|
|
|
|
Task 02 depends on Task 01. The work orders are sequential and do not authorize
|
|
subagents.
|
|
|
|
## Verification results
|
|
|
|
- Focused web unit suite: 163 tests passed across 10 files after the review
|
|
fixup, including the workspace-context generation regression test.
|
|
- Web typecheck passed.
|
|
- Web i18n check and ratchet passed.
|
|
- Full web lint passed.
|
|
- Desktop Playwright hydration scenario passed, including task-scoped request
|
|
deduplication, structured details, author identity, and cache reuse.
|
|
- Mobile Chrome Playwright scenario passed, including task navigation, the
|
|
existing PR drawer, author identity, and horizontal containment.
|
|
- Web E2E build passed.
|
|
- Fresh desktop and mobile captures were generated, inspected, compressed,
|
|
and validated through `apps/web/.pr-assets/manifest.json`.
|
|
- The repository-wide E2E sleep-policy lint remains baseline-red with 188
|
|
unrelated errors. Both changed E2E files pass the same lint directly.
|
|
- PR review fixup coverage also verifies task-id and workspace-context races,
|
|
deletion tombstones, and keyboard focus continuity during hydration.
|
|
- Workspace-routing review fixup on 2026-08-26 added the `workspace_id` frontend
|
|
contract, missing/foreign event guards, typed backend workspace extraction, and
|
|
fail-closed notification tests. The focused frontend suite passed 95 tests
|
|
across 6 files and the backend gateway/GitHub suites passed 2,176 tests. The
|
|
full frontend Vitest run was stopped after the requested 15-minute local
|
|
window without a failure result or terminal summary; the PR checks remain the
|
|
authoritative broad-suite validation.
|
|
|
|
## Documentation impact
|
|
|
|
No public documentation change is required. This package updates the durable UI
|
|
requirement and adds its system design.
|
|
|
|
## Risks
|
|
|
|
- A late HTTP result can replace newer WebSocket data unless current store
|
|
identities win during response application.
|
|
- One hook instance per row can produce duplicate requests unless the in-flight
|
|
registry is scoped above each component.
|
|
- A request that starts on pointer entry can finish after pointer leave. The
|
|
result must remain cached without reopening the tooltip.
|
|
- Radix can keep hidden tooltip portals mounted. E2E locators must target the
|
|
active tooltip content.
|
|
- Adding author text can increase summary height and can wrap in translated
|
|
layouts. Desktop and mobile tests must retain viewport containment.
|
|
- A missing or mismatched workspace ID must never be replaced with the active
|
|
workspace or reach a global notification broadcast.
|