fix(activity): keep PR timelines and recency authoritative - #913
Conversation
Activity's cursor poll can miss events persisted behind newer leading rows. A successful detail read already contains those events, but visible Activity, Pulls, and Issues can continue to show an older last-change time. Reconcile visible lists after the initial detail envelope is accepted. Make the scheduled full Activity poll authoritative so the feed also self-heals without detail navigation or server-sent events. Generated with Codex Co-authored-by: Codex <noreply@openai.com>
roborev: Combined Review (
|
The Commits control is a default-branch filter, but Activity also removed PR commits from its requests. Threaded rows then derived their age from the remaining visible children, so Activity could contradict an already-synced PR or issue detail. Keep PR commits in item-scoped Activity results and carry each parent's true last-activity time through the feed. Successful initial detail reads also invalidate visible lists even when the selection changes before installation. Generated with Codex Co-authored-by: Codex <noreply@openai.com>
roborev: Combined Review (
|
Activity rebuilt its filter controls from the API type list. That list must retain pull request commits and can omit hidden default-branch rows, so reloads could change the item scope or the Commits toggle. Persist item and event selections independently. Keep filtered child events authoritative for parent recency and cover both pull request and issue detail reads through the real HTTP and SQLite path. Generated with Codex Co-authored-by: Codex <noreply@openai.com>
|
Addressed both medium findings in a0954c0.
Verification includes 3,498 frontend unit tests, 45 affected full-stack browser tests, 25 affected mock browser tests, frontend type/effect checks, Go tests, formatting, and lint. |
roborev: Combined Review (
|
Activity could drop a pull request or issue from its time window when the only recent child event was hidden by a filter. Event selection then decided the parent's timestamp and position even though detail had newer provider state. Return a separate parent snapshot ordered by last activity, and merge it into threaded views without inventing timeline events. Migrate legacy route filters into independent item and event selections so existing bookmarks keep their meaning. Generated with Codex Co-authored-by: Codex <noreply@openai.com>
|
Addressed all three findings in 4ac670f:
Coverage includes database and API tests plus full-stack HTTP-and-SQLite verification for an old parent promoted by a newer hidden child event. The full pre-commit gate passed, including generated API checks, frontend checks, Go short tests, and lint. |
roborev: Combined Review (
|
The authoritative parent snapshot emits Activity wire item types, while workspace references use canonical workspace item types. Pull request parents therefore lost their workspace association when the response was built. Normalize only the snapshot lookup key and keep the wire response unchanged. Treat every matching parent as valid in the mobile repository filter check. Generated with Codex Co-authored-by: Codex <noreply@openai.com>
|
Addressed the remaining review finding in 625c762.
Verification: the full mobile Chromium file passed 17/17, the focused case passed in Firefox, focused server tests passed, and the final commit and push hooks passed. The first commit-hook run encountered an unrelated tmux cleanup timing failure under host load; that test passed in isolation and the complete hook passed on retry. |
roborev: Combined Review (
|
Explicit legacy Activity bookmarks could inherit canonical session filters, and hidden-branch URLs could silently disable Commits during normalization. Filtered feeds could also show parent-only threads while the status bar reported no pull requests or issues. Keep legacy route ownership intact during migration. Derive Activity totals from one deduplicated subject projection, with authoritative parent lifecycle state taking precedence over event and workspace snapshots. Generated with Codex Co-authored-by: Codex <noreply@openai.com>
|
Addressed all three findings in
Validation includes the three focused regression files (95 tests), the full frontend unit suite (3,515 passed, 1 skipped), frontend formatting/lint/Svelte/Effect checks, and the Chromium app-shell count smoke test. |
roborev: Combined Review (
|
Activity bookmarks could re-enable default-branch commits from a stale token, and parent snapshot overflow was being treated as event overflow. Keep the legacy commit token authoritative. Report parent truncation separately so polling and event-count warnings respond only to event overflow. Generated with Codex Co-authored-by: Codex <noreply@openai.com>
|
Addressed both findings in
Validation: 3,516 frontend unit tests passed; the full server package, frontend package checks, API generation, and commit/push hooks passed. |
roborev: Combined Review (
|
The Commits toggle represents default-branch activity, but selecting it alone also requested pull request and issue opening rows. A commit-only feed therefore displayed unrelated item activity. Require a timeline-controlled event before requesting opening rows. Keep pull request timeline commits independent from the top-level Commits toggle. Generated with Codex Co-authored-by: Codex <noreply@openai.com>
|
Addressed in
Validation: Activity store tests passed (73/73), the complete default-branch Activity Playwright suite passed (17 passed, 1 existing Firefox mobile skip), and |
roborev: Combined Review (
|
Activity can return a complete event page with a capped parent snapshot. The frontend dropped that second cap flag, so feed and status totals appeared authoritative while omitting items. Preserve parent truncation through every projection and warn separately. Only event overflow can trigger event replacement. Generated with Codex Co-authored-by: Codex <noreply@openai.com>
|
Addressed in
Validation: full frontend unit suite passed (3,521 passed, 1 existing skip), the complete default-branch Activity Playwright suite passed (19 passed, 1 existing Firefox mobile skip), and |
roborev: Combined Review (
|
Legacy Activity URLs still exist in bookmarks and session-restored routes. Treat their commit token as the old Commits toggle so normalization does not silently disable default-branch commits. Keep the notification-only sentinel distinct, and ignore obsolete default-branch tokens without commit. Generated with Codex Co-authored-by: Codex <noreply@openai.com>
|
Fixed in Legacy Activity URL migration now treats Regression coverage includes store hydration with a fresh instance and Chromium browser loading of the legacy bookmark. Verification: 3,522 frontend unit tests passed (1 skipped), 11 default-branch Activity Playwright tests passed, and |
roborev: Combined Review (
|
Incremental Activity polling keeps old event rows while replacing parent snapshots. Route-based grouping can split one repository after a rename or combine two repositories when a route is reused. Carry provider-stable repository identity through every Activity projection and use it for threaded, mobile, and status grouping. Keep the existing route key for legacy payloads without a stable ID. Generated with Codex Co-authored-by: Codex <noreply@openai.com>
|
Addressed both findings in
Verified in Chromium and Firefox, plus focused frontend tests, Go DB/server tests, API generation, and the full pre-commit hook suite. |
roborev: Combined Review (
|
Incremental Activity polling keeps old event rows while replacing parent snapshots. Route-based grouping can split one repository after a rename or combine two repositories when a route is reused. Carry provider-stable repository identity through every Activity projection and use it for threaded, mobile, and status grouping. Keep the existing route key for legacy payloads without a stable ID. Generated with Codex Co-authored-by: Codex <noreply@openai.com> fix(activity): reconcile retained events with parent snapshots Incremental polls keep earlier event rows while refreshing authoritative parent snapshots. After a repository rename, those rows could still point at an old route that a different repository later reused. A click could then open the wrong pull request or issue. Reconcile retained rows by provider-stable repository and item identity before downstream projections. This keeps parent metadata and mobile labels current without merging route-reused histories. Generated with Codex Co-authored-by: Codex <noreply@openai.com>
|
Addressed both findings in 50e10b6.
Coverage now clicks both the renamed original and the replacement that reused its old route through real HTTP and SQLite data and verifies each destination. Focused store/mobile suites pass (97 tests), the Chromium full-stack scenario passes, and make frontend-check-no-deps passes. |
roborev: Combined Review (
|
Hide bots filtered event actors, but authoritative parent and workspace snapshots bypassed that filter. Bot-authored pull requests and issues could still appear as empty threads and inflate Activity totals. Apply the same visibility rule to every Activity projection. Keep source snapshots intact so toggling the preference does not discard authoritative state. Generated with Codex Co-authored-by: Codex <noreply@openai.com>
|
Addressed in 38db8c8.
Verified with 63 focused component tests, Svelte analysis, make frontend-check-no-deps, commit hooks, and push hooks. |
roborev: Combined Review (
|
An incremental Activity search can keep a matching event from an earlier page while the cursor-scoped response omits that event. Parent selection searched only titles and authors, so it could omit the authoritative parent and leave retained metadata or routes stale. Use the full search result to retain matching parent identities. Cover bot filtering against parent-only and workspace-only subjects through the real HTTP and SQLite path. Generated with Codex Co-authored-by: Codex <noreply@openai.com>
|
Addressed both findings in 6f3a26c.
|
roborev: Combined Review (
|
An incremental Activity search can keep a matching event from an earlier page while the cursor-scoped response omits that event. Parent selection searched only titles and authors, so it could omit the authoritative parent and leave retained metadata or routes stale. Use the full search result to retain matching parent identities. Cover bot filtering against parent-only and workspace-only subjects through the real HTTP and SQLite path. Generated with Codex Co-authored-by: Codex <noreply@openai.com> fix(activity): reconcile notification provider links Incremental Activity polling refreshed authoritative parent routes but left a notification's cached provider URL unchanged. After a repository rename and route reuse, the flat feed could open the replacement repository's item. Notification URLs are parent links, so refresh them from the current parent snapshot while preserving event-specific deep links. Generated with Codex Co-authored-by: Codex <noreply@openai.com>
|
Addressed in
The linked CI failure was a five-second timeout in Local verification:
|
roborev: Combined Review (
|
Notification rows in the Activity union carried the title and URL that were persisted when the notification synced. After a repository rename, route reuse, or title edit those values were stale, and the feed only repaired them when the frontend matched the row against the item_activity parent snapshot, which is independently capped. A visible notification whose parent fell outside that window kept a stale item_url, activity_url, and title, so its provider link could open the old or replacement route. ListActivity already LEFT JOINs the linked pull request or issue for author, lifecycle state, and recency. Take item_title, item_url, and activity_url from that same join, falling back to the persisted subject_title and web_url only when the subject has not synced yet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
roborev: Combined Review (
|
Activity used each pull request's and issue's last_activity_at as its authoritative recency, and that column is the provider's updated_at. GitHub bumps updated_at for bookkeeping that is not activity: mergeability recomputation after a base-branch push, head-branch deletion after merge, and similar. A stacked pull request therefore rose to the top of the feed and read "16 minutes ago" while its newest visible timeline entry was a comment from fourteen hours earlier, and merged pull requests reappeared as recent days after merging. Define Activity recency from the ledger the feed can render instead: the opening, the newest issue_comment/review/commit/force_push event for pull requests (issue_comment for issues), merge, and close. Ledger rows the feed never renders, such as cross references and assignments, do not count, and provider updated_at no longer participates. The same expression drives the parent snapshot's time window and ordering and each event row's item_last_activity_at, so events and parents agree, and event visibility filters still cannot hide the responsible event. The event page is materialized before the recency lookup so the correlated ledger walk runs once per returned row rather than once per candidate row; on a real database this adds roughly 20 ms per poll. The e2e fixture endpoint now reports the ledger comment time and bumps provider updated_at further ahead so the scenario proves the ledger wins. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
roborev: Combined Review (
|
Issue opening rows were requested whenever any timeline event toggle was on, including Reviews and Force pushes, which cannot occur on an issue. Turning Force pushes off could therefore make every issue "opened" row disappear. Opening rows now follow only the timeline events that exist for that item kind: comments, reviews, and force pushes for pull requests, and comments for issues. Authoritative parent summaries also replaced a thread's author with an empty string when the parent had no recorded author, dropping the event actor the thread previously showed. Keep the existing author when the summary has none. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Activity could show an old parent time while detail already contained newer activity because filter state and feed recency were coupled.