Skip to content

fix(desktop): render the sidebar PR badge on session rows and branch lanes - #89709

Draft
ethernet8023 wants to merge 3 commits into
mainfrom
ethie/desktop-pr-on-branch
Draft

fix(desktop): render the sidebar PR badge on session rows and branch lanes#89709
ethernet8023 wants to merge 3 commits into
mainfrom
ethie/desktop-pr-on-branch

Conversation

@ethernet8023

@ethernet8023 ethernet8023 commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

The desktop sidebar can badge a session with its pull request. The badge did not appear. This PR repairs the data that the badge needs, and adds the badge to project branch lanes.

There are two defects, one behind the other.

1. The gateway did not record git_branch on desktop session rows.

The sidebar joins pull requests on git_repo_root + git_branch from the persisted sessions row (sessionPrKey, apps/desktop/src/store/pull-requests.ts). The composer status line shows a PR for the same branch, because it probes git live. That difference is why the feature looked half broken: the status line was correct and the sidebar was empty.

In a real profile database, 146 of 156 rows with a git_repo_root had a NULL git_branch. Both arms of session start failed to run the enrichment:

  • Create: _ensure_session_db_row inserted the row AFTER the only call to the enrichment scheduler. update_session_cwd claims a generation only for a row that exists, so the call returned None and the branch probe never started.
  • Resume: _init_session adopted the cwd of an existing row and skipped enrichment. A row with no branch stayed that way.

2. The project branch lanes never read the pull-request store.

The projects/ folder had no reference to it. "Show PR" badged the session rows under a lane, but not the lane itself, although the lane label IS the branch name.

Related Issue

No existing issue. Related but separate: #79623 reports the same NULL columns for cron and CLI sessions. This PR repairs the desktop gateway path only. The cron path writes its row elsewhere and needs its own fix.

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)

Changes Made

Gateway (commit 1)

  • tui_gateway/server.py_ensure_session_db_row claims a generation and starts the branch probe AFTER create_session inserts the row.
  • tui_gateway/server.py_init_session starts the same enrichment when the adopted row has no git metadata. Rows from before this fix repair themselves on the next resume. The generation guard in publish_session_git_metadata keeps a concurrent workspace move authoritative.
  • tests/tui_gateway/test_session_git_branch_persistence.py — 3 tests against a real SessionDB on a temp path.

Desktop (commit 2)

  • apps/desktop/src/store/pull-requests.ts — new branchLanePrKey(repoRoot, branch), the trunk-guarded key. sessionPrKey now uses it, so both surfaces build the key one way.
  • apps/desktop/src/app/chat/sidebar/projects/workspace-group.tsx — the lane reads the store behind the same rowMeta.includes('pr') toggle and requests its own branch.
  • apps/desktop/src/app/chat/sidebar/projects/workspace-header.tsx — new meta slot. The chip is a SIBLING of the toggle button, because a button inside a button is invalid HTML and the keyboard cannot reach it.
  • apps/desktop/src/app/chat/sidebar/projects/entered-content.tsx — passes repoPath to the lane.
  • Tests: apps/desktop/src/store/pull-requests.test.ts, apps/desktop/src/app/chat/sidebar/projects/workspace-group.test.tsx.

Two store changes that a second caller made necessary:

  • Staleness is now per lookup. A repo that is fresh for the branches of the last fetch still fetches for a branch it has not asked about. Without this, a lane that appears after the fetch waits 60 seconds for a badge.
  • A narrow ask merges the lookups of the last fetch. The store replaces the pull requests of a repo wholesale, so a one-branch ask would otherwise delete the PRs that the session rows show.

How to Test

Both screenshots below come from a real Electron run: a real git repository, four real linked worktrees, four real sessions built through the gateway and the agent loop, and a gh stand-in that answers the real GraphQL query shape.

Session rows (the gateway fix). Before this PR every row here had a NULL branch and no badge.

Session rows with PR badges

Branch lanes (the new surface). Each lane carries the PR for its branch: #8077 closed, #8140 draft, #8098 merged, #8123 open.

Branch lanes with PR badges

Both at once. An expanded lane: the lane header carries #8123 from its label, and the session row inside it carries the same #8123 from the branch its session recorded. The lane badge and the gateway fix, in one frame.

Lane and session row with the same PR

Manual steps:

  1. Open a chat in a worktree on a branch that has an open PR.
  2. Turn on Filters → Show → PR in the sidebar.
  3. The session row shows the PR number.
  4. Group by project and enter the project. The branch lane shows the same PR.

Automated:

nix develop -c scripts/run_tests.sh tests/tui_gateway/test_session_git_branch_persistence.py
cd apps/desktop && npx vitest run src/app/chat/sidebar/ src/store/pull-requests.test.ts

Results on this branch:

  • Python: 36 tests pass (the new file plus every neighbour that touches this machinery).
  • Desktop: 181 tests pass. npx tsc --noEmit and npx eslint are clean.

Fault detection, which is the part that matters:

  • Remove the gateway change: the two regression tests fail, the guard test still passes.
  • Remove the renderer change: the two badge tests fail, the three guard tests (toggle off, no PR, kanban) still pass.

Notes for review

  • Existing rows repair themselves lazily, on the next resume of each session. There is no migration. Say the word if you want one.
  • The lane badge appears in the entered-project view. Profile and source groups have no single repository, so they take no badge.
  • The kanban lane takes no badge. It collects many branches into one lane.
  • The Playwright spec that produced the screenshots above is included (apps/desktop/e2e/sidebar-pr-badges.spec.ts). Every test body passes and writes its snapshot. The run then reports a per-test timeout during teardown. The untouched worktree-branch-status.spec.ts fails the same way on the same machine, so the cause is the harness or this host, not this spec. CI runs no Playwright lane, so this spec does not gate the merge. It is here for a maintainer who runs the suite locally.
  • RealSessionSpec takes an optional cwd (apps/desktop/e2e/real-session-builder.ts). The builder placed every session at the repository root before. A session must start in a named worktree for this spec, and any later test that needs a specific checkout can now do the same.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature
  • I've run the tests and all pass (see How to Test)
  • I've added tests for my changes
  • I've tested on my platform: NixOS (Linux 7.1.8)

Documentation & Housekeeping

  • I've updated relevant documentation — N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — N/A
  • I've considered cross-platform impact. The gateway change is path independent. The renderer change is layout only.

The desktop sidebar joins pull requests on git_repo_root and
git_branch from the session row. The gateway did not write
git_branch on the two usual paths:

- Create: _ensure_session_db_row inserted the row after the only
  enrichment call. The generation claim found no row, and the
  enrichment did not start.
- Resume: _init_session adopted the cwd of an existing row and did
  not start enrichment. A branchless row stayed branchless.

As a result, the PR badge did not show in the sidebar. The composer
statusline showed the PR because it probes git live.

Now the create path starts enrichment after the insert. The adopt
path starts enrichment when the row has no git metadata. Rows from
before this fix heal on the next resume. The generation guard in
publish_session_git_metadata prevents a stale probe from a write
against a newer cwd claim.

Tests run the real SessionDB against a temp state.db. With the fix
removed, the two regression tests fail and the guard test passes.
The "Show PR" toggle badged session rows only. A branch lane in a
project showed no PR, although the lane label is the branch name.
The projects folder never read the pull-request store.

The lane now renders the same PrTag as a session row, behind the
same toggle. The kanban lane takes no badge, because it collects
many branches into one lane. The trunk guard stays in the key
helper, so a lane on main does not ask GitHub about main.

Two changes make the store safe for a second caller:

- Staleness is now per lookup. A repo that is fresh for the
  branches of the last fetch still fetches for a new branch. A lane
  that appears after the fetch gets an answer.
- A narrow ask merges the lookups of the last fetch. The store
  replaces the PRs of a repo wholesale, so a one-branch ask would
  otherwise drop the PRs that the session rows show.

Tests drive the real component and the real stores. With the
renderer change removed, the two badge tests fail. The three guard
tests (toggle off, no PR, kanban) pass in both states.
@github-actions

github-actions Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on 855ea04 — test(desktop): add an end-to-end check for the sidebar PR ba

⚠️ Warnings

CI timings · View report · View job

Wall time 7m4s vs 4m8s (+71.0%). 18 job(s) slower, 16 faster, 4 unchanged.

  • JS & TS checks / apps/desktop / check:test:ui:shard-1of3: -48.0s
  • JS & TS checks / apps/desktop / check:test:ui:shard-2of3: -38.0s
  • Python tests / Run tests slice 12/12: -30.0s
  • Python tests / Run tests slice 1/12: +26.0s
  • Python tests / Run tests slice 6/12: +20.0s

OSV vulnerability scan · View job

7 known vulnerabilities found in pinned dependencies.

How to fix:

Review the findings in the Security tab. Update the affected dependencies if a patched version is available.

The spec builds a real git repository with four linked worktrees, creates
four real desktop sessions through the gateway and the agent loop, and
puts a `gh` stand-in on PATH that answers the real GraphQL query shape.
Nothing about the pull-request join is faked.

It covers both halves of this branch:

- A session row badges the PR of the branch its session recorded. This
  fails without the gateway fix, because the row holds no branch.
- A branch lane badges the PR for its own branch, for each PR state.

The spec also writes the screenshots in the pull-request description.

`RealSessionSpec` takes an optional `cwd`, so a session can start in a
named checkout. The builder used the repository root for every session
before this change.

Known state on this machine: each test body passes and writes its
snapshot, and then the run reports a per-test timeout during teardown.
The untouched `worktree-branch-status.spec.ts` fails the same way on the
same machine, so the cause is the harness or this host, not this spec.
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/desktop Electron desktop app (apps/desktop/*) comp/tui Terminal UI (ui-tui/ + tui_gateway/) area/sessions Session lifecycle, resume, persistence, history sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Aug 19, 2026
@ethernet8023
ethernet8023 marked this pull request as draft August 19, 2026 06:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/sessions Session lifecycle, resume, persistence, history comp/desktop Electron desktop app (apps/desktop/*) comp/tui Terminal UI (ui-tui/ + tui_gateway/) P3 Low — cosmetic, nice to have sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants