Skip to content

fix: open PRs and issues with one click and close them when switching lists - #736

Open
penso wants to merge 17 commits into
mainfrom
worktree/detailed-view
Open

penso wants to merge 17 commits into
mainfrom
worktree/detailed-view

Conversation

@penso

@penso penso commented Oct 20, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Switching between the Pull requests and Issues lists left the previous item's detail pane open with content from the other list. Changing lists now closes the pane and its merge menu, and stops following the old pull request's conversation.

  • A row needed a double-click to open its details; it now opens on a single click, like the vulnerability list. The row's expander and Start agent controls still only do their own action.

  • Fixes the Windows native GUI jobs failing on main and every branch: the native sidebar probe still subtracted the fold-chevron column that cc727de gave back to child rows (child label width: actual 179px, expected 161px).

  • Includes feat: lead pull request and issue pages with a status box, like security alerts #744 (merged into this branch): pull request and issue pages lead with a security-style status box, security alerts open in a resizable pane, an item several checkouts list shows once, and only the open row wears the accent.

  • The Security icon is always in the activity bar. It waited for the first full read to find a repository with alerts on, which took a while at startup and never happened for an account with nothing turned on.

  • The Security section searches and filters like Issues: a search field, a pill per source (reports, Dependabot) and per repository, applied to the cards, the list beside a page, and stepping. Every row, in both lists, has Start agent, opening the workspace's agent dialog with the page's fix or investigation prompt.

  • Folders outside Git are left out of the pull request and issue lists: no red "not inside a Git repository" banner, project pill, or count in the header.

  • Issue rows keep Start agent while a page is open beside the list.

  • A sign-in that cannot read Dependabot alerts says so instead of "no open alerts": an empty GraphQL listing is checked against the REST endpoint, whose refusal reads as hidden in the section and the CLI.

  • Instant restart. The orchestrator's database is now the app's own, in its state directory; agent-launcher's is copied in once per repository. Pull request reviews and checks are stored too (schema 2). A new private app cache (cache/cache.sqlite3) keeps each checkout's repository, so lists paint before git/SSH answer, and the Security section's last read per GitHub sign-in (alerts and report titles, no descriptions or tokens). The existing timers sync right after.

QA

  • cargo test -p herdr-gpui --locked orchestrator — 140 passed (updated a_click_opens_the_item_beside_the_list covers single-click open and the pane closing on list switch)
  • just ci on 5ced939 (main merged) — passed (macOS arm64)
  • security::tests::section::the_icon_is_listed_before_any_read — icon shown before a read and after one with alerts off
  • security::tests::filter — typing narrows to the matching alert and drops an empty source's pill; source and repository pills narrow, "nothing matches" shows; a row's Start agent does not open its page; the list beside a page has Start agent; stepping skips what the search hides
  • orchestrator::view::tests::outside_git::a_folder_outside_git_is_left_out_quietly — no banner or project pill, one project counted; fails without the fix
  • switching_lists_closes_the_open_pull_request — opens a looked-up PR with the merge menu open, switches to Issues, checks detail, merge menu, and followed target are cleared; fails if the cleanup is removed
  • orchestrator::view::tests::row_start — Start agent sits in the list beside an open issue and opens the dispatch dialog
  • security::tests::unreadable and the CLI alerts_a_sign_in_cannot_read_say_so_rather_than_none — a REST refusal (403/404) reads as hidden, other failures keep the listing, the section names the repository, another account is preferred
  • orchestrator::tests::restart — checks stored per repository and replaced whole; agent-launcher's database copied in once (private file, runs and checks kept, later legacy writes ignored); a cached repository is dropped when the folder is no longer one
  • security::tests::saved, cache::tests — the read is kept per sign-in, a failed repository keeps its last entry, no account or checkouts are kept, the run's own read wins over a late restore; cache entries round-trip and the file is private
  • cargo test -p herdr-gpui sidebar:: — 270 passed
  • just test-sidebar (macOS) — sidebar-child probe passes; the run then fails at the workspace PR popover glyph check, which passes on Windows CI (main at 26443bc)
  • In the app, open a pull request, switch to Issues: no detail pane is shown
  • Single-click an issue and a pull request row: the detail pane opens; clicking another row switches to it
  • Click an issue's expander chevron and Start agent: they act without opening the pane
  • Launch the app: the shield icon is in the activity bar right away; its count appears after the read
  • Security: type a package name, pick a source and a repository pill; the cards, the list beside a page, and Next follow
  • Security: press Start agent on a card row and on the list beside a page; the agent dialog opens with the prompt, the page does not change
  • With the herdr-ui App's Dependabot alerts permission approved, Refresh lists herdr-gpui's and moltis's alerts
  • With a workspace on a plain folder open: the Pull requests list shows no banner or pill for it
  • Open an issue's page: the rows in the list beside it still have Start agent
  • Before approving the herdr-ui permission: Security names herdr-gpui and moltis as "not readable with this GitHub sign-in"
  • Quit and relaunch: Pull requests (with check pills), Issues, and Security alerts show at once, then refresh; an existing agent-launcher repository's runs appear on first open

No screenshots: capturing the real window needs input on a shared display; the visible changes (status box, icon, banner) are covered by headless tests and the QA steps above.

… lists

Switching between the Pull requests and Issues lists kept the previous
item's detail pane open, showing content that no longer belonged to the
shown list. Changing lists now closes the pane and its merge menu, and
stops following the old pull request's conversation.

A row also needed a double-click to open, unlike the vulnerability list.
One click now selects and opens it; the row's expander and Start agent
controls still stop the press from reaching the row.
@penso

penso commented Oct 20, 2026 •

Copy link
Copy Markdown
Owner Author

just ci ran locally and passed on 5ced939 (macOS arm64).

@greptile-apps

greptile-apps Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 4/5

[Medium impact] The changes affect the app’s pull-request, issue, and security workflows.

Fix the first-import race before merging; concurrent opens can prevent old runs from being carried over.

Findings

  1. P1 Concurrent opens skip old runs ▶
  2. P2 Page shows another checkout's text ▶
  3. P2 Selected filters disappear ▶

Summary

The PR makes issue and pull request rows open with one click and closes their pages when switching lists.

  • Pull request and issue rows open their pages with one click.
  • Security lists search, filter, and show alert pages beside the list.
  • Saved issues, pull requests, and alerts appear while fresh reads start.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Open repository] --> B{Saved repository found?}
  B -->|Yes| C[Show stored items and runs]
  B -->|No| D[Detect repository]
  C --> D
  D --> E{App database exists?}
  E -->|No| F[Import old database if present]
  E -->|Yes| G[Open app database]
  F --> G
  G --> H[Sync items and checks]
Loading

Reviews (2) · Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." · Reviewed by Greptile

Comment thread crates/herdr-gpui/src/orchestrator/view/tests/clicks.rs
penso added 8 commits October 19, 2026 22:43
The list-switch assertions only closed an issue, so removing the pull
request cleanup still passed. The new test opens a looked-up pull
request with its merge menu open, switches to Issues, and checks that
the detail, the merge menu, and the followed target are all cleared.
…tive probe

cc727de stopped child rows that draw no fold chevron from reserving its
column, and the headless row test was updated for it, but the native
paint probe still subtracted ARROW_RESERVE. Windows native GUI jobs on
main and every branch since failed with "child label width: actual
179px, expected 161px": exactly the 18px reserve.
…ity alerts

The detail pane now reads like a security alert's page. Its header
holds the crumbs with an "N of M" stepper (previous, next, open in the
browser, close), colored tags for state, priority and branches, and a
title that wraps instead of truncating.

A status box leads the item's first page (the conversation for a pull
request, the description for an issue): one row each for the checks
(naming the failing ones), the review decision, the merge state, any
blockers, and the newest agent run, with Dispatch, Review, Open agent
and Merge in its footer. Merge lists its methods inline under the
button instead of in a menu pinned to the old header's geometry. The
overview's facts fold into one line above the description.

The security page's tag moves to the shared Look so both pages draw
it the same way.
… issues

An open alert or report took everything but a fixed 320 px list, so its
page was much wider than an issue's. The list now keeps the room and
the page sits in a 520 px pane whose left edge drags to resize it, or
shut, exactly as the Pull requests and Issues sections do. Folding the
list away still gives the page the whole section.

The pane's width bounds, handle, and drag arithmetic move out of the
orchestrator view into a shared `pane` module both sections use.
The same repository checked out on two hosts listed each of its GitHub
issues and pull requests twice, and selecting one lit both rows: the
selection, the open item, and the repository a page and its actions go
to were all looked up by item key, which the copies share.

The merged list now keeps one row per item, where its first copy came,
as the copy with an agent run when only another has one. Its Project
cell says how many hosts list it, and the backend pills count it once.
A row's page, Start agent, and dispatch now go to the checkout that row
shows rather than the first one holding the key; the dispatch dialog's
host tiles still offer every connected host to send the agent to.
…s open

The accent lit both the open row and a row the arrow keys moved to, so the last row clicked still looked open after its page closed. The selected row without a page now wears the hover tint, keeping the selection visible without claiming a page.
penso added 8 commits October 20, 2026 16:32
feat: lead pull request and issue pages with a status box, like security alerts
The icon waited for a finished read to find a repository with Dependabot alerts or private reporting on. That read needs the GitHub sign-in, every host's workspaces, and every repository read in turn, so the icon came late, and never for an account with nothing turned on. The section already explains an empty read, so the icon is listed from the start and its count fills in when the read lands.
A workspace opened on a plain folder became a project that reported "This folder is not inside a Git repository" in a red banner, took a project pill, and counted toward the header's projects. Such a folder is no project: the view now leaves it out of the header, pills, project column, and banners, while other failures are still reported.
…its rows

The Security section listed every report and alert with no way to narrow it, unlike Pull requests and Issues. It now has their search field and pills: the field matches summary, package, advisory, repository, reporter, severity, manifest, and alert number; one pill per source (reports, Dependabot) and per repository narrows to those picked. The cards, the list beside a page, and stepping with its N of M count all follow it.

Every row, in the cards and beside a page, has Start agent, which opens the workspace's agent dialog with the fix or investigation prompt the page's own button writes.

The pill, compact button, and search outline move onto the shared Look so both sections draw the same ones.
…list

The compact list beside an open issue dropped the row's Start agent
button with the author and comment columns. The action column now stays
in both layouts, so an agent can be started on another issue without
closing the page first.
GitHub's GraphQL API answers a GitHub App token without the Dependabot
alerts permission with an empty listing, so such repositories read as
"no open alerts". An empty listing is now checked against the REST
alerts endpoint, which refuses such a token; a refusal reads as a new
Alerts::Hidden state. The section names those repositories, the CLI
prints and reports "hidden", and another host's sign-in is tried for
them as it is for reports. Any other REST failure keeps the empty
listing.
…restart

The orchestrator's per-repository database moves from agent-launcher's
data directory into the app's own state directory; agent-launcher's
file is copied in (VACUUM INTO a private file) the first time a
repository opens, and never written again. Schema version 2 adds each
open pull request's last read review and checks, so they show before
the first sync.

A new app-wide cache (`cache/cache.sqlite3`, private like the
databases) keeps which repository each checkout held, so the lists
paint before git or SSH answer; a checkout that holds none any more
drops what was shown. It also keeps the Security section's last read
per set of GitHub logins: alerts and report titles and metadata, never
a report's description or a token. A repository whose read fails keeps
its last kept entry. The restored rows have no account or checkouts,
so an item's page waits for the run's first read.

The private file and directory checks move from the orchestrator store
into `private_file`, shared by both databases. Syncing is unchanged: the
existing timers read again right after the cached state is shown.
# Conflicts:
#	crates/herdr-gpui/src/sidebar/layout_tests.rs
@penso

penso commented Oct 20, 2026

Copy link
Copy Markdown
Owner Author

@greptileai review

Comment on lines +532 to 541
if let Some(legacy) = legacy.filter(|legacy| !path.exists() && legacy.is_file())
&& let Err(error) = Store::import(&legacy, &path)
{
tracing::warn!(
category = "orchestrator",
%error,
"Cannot carry over agent-launcher's database"
);
}
let store = Store::open(&path)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Concurrent opens skip old runs

Opening the same repository in two windows during its first import can leave an empty database instead of its old runs and history. prepare(path) creates the destination before the copy starts. Another worker can see that file, skip the import, and create an empty database there. The first worker's copy then fails because the destination is no longer empty.

Both workers continue, and later launches skip the import because the file exists. Copy into a temporary file and publish it only after success, with coordination between workers.

Comment on lines 71 to +78
pub(super) fn repo_of(&self, key: &str) -> Option<usize> {
self.repos.iter().position(|repo| repo.item(key).is_some())
self.lines
.iter()
.find_map(|line| match line {
rows::Line::Row(row) if self.row_item(row).key.canonical() == key => Some(row.repo),
_ => None,
})
.or_else(|| self.repos.iter().position(|repo| repo.item(key).is_some()))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Page shows another checkout's text

repo_of now chooses the checkout shown by the row, but item still returns the first checkout's copy. When two checkouts have different reads of the same issue, filtering to the second project opens its page with the first project's title, description, and labels. This also happens when dedupe chooses the second copy because it has a run.

The page then combines one checkout's issue text with another checkout's runs. Read the item from the chosen checkout before falling back to other copies.

Comment on lines +233 to +234
let many_sources = sources.len() > 1;
if many_sources {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Selected filters disappear

A selected filter disappears when it becomes the only pill in its group. The code keeps selected sources and repositories in the counts, but draws each group only when its length exceeds one. The filter stays active even though the user cannot see or turn it off, so Security can keep hiding items.

Draw a group whenever it has a selected pill, even if only one remains. Apply this to both source and repository pills, including the toolbar's final visibility check.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant