Skip to content

Add a repository-scoped PR board and native inspection - #484

Merged
wing-anara merged 56 commits into
zeronsh:mainfrom
gaelcado:wip/pr-board
Oct 9, 2026
Merged

wing-anara merged 56 commits into
zeronsh:mainfrom
gaelcado:wip/pr-board

Conversation

@gaelcado

@gaelcado gaelcado commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

Adds a Pull requests board for one GitHub repository, a native pull request view (Summary, Code, Activity), comment posting, and "hand off to an agent" actions. Everything goes through the gh CLI already installed and signed in on the selected device.

Builds on José Gurruchaga's (@jsgrrchg) pull request dashboard from #181 (closed, unmerged). His pull request contracts, GitHub CLI queries and board foundation were ported onto current main; the commits carrying that work credit him with Co-authored-by. The detail view, code review, activity, media, handoff and paging are new.

Board

  • Opens on the repository it is opened from: the sidebar's project, else the open session's project, else the repository viewed last, else the most recently active local Git project that is on GitHub. Opening it from another project follows that project; a repository chosen by hand stays until then.
  • Authored / Reviewing / All filters, local search and sorting, grouped by what needs attention. 50 pull requests per page with "Load more" and the total count.
  • Works against another device through the relay. Devices whose engine predates this feature report "Update Zeron".
  • The Pull requests entry sits in the sidebar footer on every screen, Settings included.

Pull request view

  • Summary: status, review, branch and changes; checks; description with images.
  • Code: a file tree beside one continuous, syntax-highlighted review stream with a pinned file header, fold-all, split view and file search.
  • Activity: comments and reviews as a thread, with a composer that matches the chat composer. @ suggests the pull request's participants.
  • Hand off to an agent: Review, Address feedback, or Fix failing checks open a new session with the prompt staged, not sent. Pull request text in the prompt is reduced to one plain line and marked as data.
  • PR badges open this view for github.com pull requests when "Open pull requests in" is set to "Pull request view" (the default); other hosts and the "Default browser" setting open the browser.

Engine and protocol

  • New RPC methods: ListChangeRequestPage, GetChangeRequestRepository, GetChangeRequest, GetChangeRequestDiff, PostChangeRequestComment. Repository, URL and cursor are validated before they reach gh; GH_HOST is pinned to github.com.
  • Provider failures cross the wire as stable codes (RpcError::Capability), never as gh output. The UI turns them into instructions.
  • A 24-entry engine cache: results last five minutes (15 seconds under Refresh), failures one minute unless retried explicitly, and a rate limit pauses requests for 15 minutes. Identical reads share one gh call, distinct reads run side by side, and a read finishes and is cached even if its caller leaves. Writes are never retried.
  • Comment text is sent to gh on standard input. A comment keeps posting if the view is closed; a failed one comes back as a draft with the reason.
  • ServerFrame now delivers a unary reply whose value is null (previously the caller waited for its own timeout). This affects every RPC that can answer null.

Privacy

  • Images in pull request text load from GitHub's hosts only. Images on other hosts show a "Click to load" tile.
  • The image transport reaches public HTTPS addresses only, on every redirect hop and after DNS resolution: no plain HTTP, loopback, private, link-local or CGNAT addresses.

Out of scope

Review submission and merging stay on GitHub. GitHub Enterprise hosts open in the browser.

Rebase validation

Rebased the 37-commit series onto main at d5c1cdc124c8d403eb0a11e94a8dae4a381fc958. New head: 12c9d5241844040efcfed651354c33d5fb24fb32.

  • Desktop, engine, UI and mobile: cargo check -p zeron -p zeron-engine -p zeron-ui -p zeron-mobile --all-targets passed at 8300b646. The final head differs from that tested head only by formatting the shared tooltip signature.
  • UI: cargo test -p zeron-ui --lib -- --test-threads=1: 1,617 passed at 8300b646.
  • At 12c9d524, cargo test -p zeron-engine -p zeron-rpc -p zeron-proto -p zeron-client --lib -- --test-threads=1: engine 406 passed / 2 ignored, RPC 17 passed, protocol 64 passed, client 21 passed.
  • At 12c9d524, the engine integration test pull_request_list_dispatch_returns_provider_items passed.
  • Every one of the 37 commits passes cargo check -p zeron -p zeron-engine --all-targets independently.
  • At 12c9d524, cargo check -p zeron-ui --example pull-request-fixture --features pull-request-fixture passed.
  • No formatting differences introduced beyond current main; git diff --check passes.

The rebase preserves upstream's repository identity fields, composer attachment chips, icon registry and animated sidebar glyph. No new runtime screenshots were captured during this rebase. The visual evidence below is from the earlier published head.

Earlier validation

At 9a236a8b, on macOS:

  • cargo test -p zeron-ui --lib -- --test-threads=1: 1,466 pass.
  • Keyboard-only walks of the board, Summary, Code and Activity (offscreen fixture): every stop shows a focus ring; search, file search and the comment box are reachable; menus return focus.
  • cargo test -p zeron-engine --lib: 405 pass. cargo test -p zeron-rpc -p zeron-proto: pass.
  • cargo test -p zeron-engine --test e2e: 26 pass; generated_image_is_materialized_before_publication_and_survives_reopen fails locally and fails the same way on main.
  • cargo test -p zeron-client -p zeron-mobile -p zeron-markdown: pass.
  • The offscreen fixture (cargo run -p zeron-ui --example pull-request-fixture --features pull-request-fixture -- <dir> zeronsh/zeron 484) loads the board, Summary, Code and Activity against real GitHub in dark and light themes.

All comment submissions in tests use fakes; no live comment was posted.

@gaelcado gaelcado changed the title Add a pull request board with native inspection Add a repository-scoped PR board and native inspection Sep 20, 2026
@jsgrrchg jsgrrchg mentioned this pull request Sep 22, 2026
9 tasks done
@gaelcado
gaelcado force-pushed the wip/pr-board branch 6 times, most recently from 7d8447c to 56b46c0 Compare September 29, 2026 18:40
@gaelcado
gaelcado marked this pull request as ready for review September 29, 2026 20:04
@gaelcado
gaelcado force-pushed the wip/pr-board branch 2 times, most recently from 5f15414 to dbcde26 Compare October 1, 2026 12:07
@gaelcado
gaelcado force-pushed the wip/pr-board branch 2 times, most recently from 7ff7f8c to 67157dd Compare October 7, 2026 18:09
@jsgrrchg

jsgrrchg commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Findings, all validated manually, here's my clanker's briefing of each one of them:

I reviewed commit 197ad52a. The newer-draft preservation fix addresses the previously reported draft-loss issue. Four findings remain:

1. Duplicate comment submission after closing and reopening the PR

Relevant code:

  • crates/ui/src/pull_request_detail.rs:452 — initializes each new view with sending: false.
  • crates/ui/src/pull_request_interactions.rs:78–105 — checks the view-local sending flag and starts a detached submission.

Reproduction:

  1. Submit a comment while the request is slow enough to remain pending.
  2. Close the PR detail view and reopen the same PR.
  3. The new view restores the submitted text as a draft and allows another submission.
  4. Submit again before the original request completes.

The original request continues after the view closes, but the replacement view has no knowledge of it. Both requests can succeed, publishing the same comment twice. The existing guard prevents duplicate submissions only within one view instance.

Recommended fix:

Track pending submissions in shared state keyed by device and PR URL. Reopened views should observe that state, display the pending submission, and prevent resubmitting it until its outcome is known. Keep the submitted body separate from subsequent draft edits so completing the request cannot overwrite newer text.

Add a regression test that holds the first response pending, closes and reopens the view, attempts another submission, and asserts that only one POST occurred.

2. Refreshing the board can still reopen an outdated detail and diff

Relevant code:

  • crates/ui/src/pull_request_detail.rs:499–507 — restores the cached detail and parsed diff without checking the board preview's head SHA.
  • crates/ui/src/pull_request_detail.rs:521–522 — loads details only when no cached detail exists.
  • crates/ui/src/shell.rs:13062–13073 — supplies the current board preview when opening the detail view.
  • crates/ui/src/pull_requests.rs:740 — refreshes the board independently.

Reproduction:

  1. Open a PR at commit A and load its Code tab.
  2. Close the detail view.
  3. Push commit B to the PR.
  4. Refresh the board so its item now reports B.
  5. Reopen the PR.

The detail view can restore commit A and its cached diff despite receiving a preview that already identifies commit B. This can cause the user to review an outdated patch.

The new diff_refresh_owed handling fixes a related but different scenario: explicitly refreshing the detail view and preserving that invalidation across reopening or a failed diff request. Refreshing only the board still does not invalidate the detail snapshot.

Recommended fix:

Compare the cached detail's head_ref_oid against the incoming preview's SHA before accepting the snapshot. When both are available and differ, invalidate the cached detail and diff and request fresh data. Ensure the engine cache cannot immediately reintroduce the known outdated snapshot.

Add a regression test with a cached detail/diff for A and an incoming board preview for B, without calling the detail view's Refresh action. Assert that the old patch is invalidated and a fresh request is made.

3. Agent handoff can use the wrong project or device

Relevant code:

  • crates/ui/src/pull_request_handoff.rs:252–253 — dispatches StartPullRequestSession with only the prompt.
  • crates/ui/src/shell.rs:13093–13100 — calls open_new_session(None, cx).
  • crates/ui/src/shell/tabs.rs:229 — resolves the new session's context from the existing sidebar/canvas selection.

Reproduction:

  1. Start from a chat or canvas associated with project A on the local device.
  2. Select a different repository or device in the PR board.
  3. Open a PR there and choose Review, Address feedback, or Fix failing checks.

The generated prompt identifies the selected PR, but session creation does not receive its repository/project or target device. The session can therefore inherit project A or the previous canvas's device.

This leaves the agent with a prompt for one repository and a working environment associated with another. Checkout commands may fail, or the agent may begin work from an unintended checkout or machine.

Recommended fix:

Extend the handoff action to carry the selected device and repository/project context alongside the prompt. Resolve a matching checkout on that device before staging the session. When no matching checkout exists, offer an explicit project selection or clone flow instead of silently inheriting unrelated context.

Add coverage where the sidebar/canvas context differs from the board's selected repository and device, and assert that the resulting session uses the intended environment.

4. “Address feedback” omits inline review comments

Relevant code:

  • crates/ui/src/pull_request_handoff.rs:169–170 — instructs the agent to retrieve feedback using gh pr view ... --comments.
  • crates/engine/src/source_control.rs, GitHubCli::fetch_detail — requests comments,reviews, but does not retrieve review threads.
  • crates/ui/src/pull_request_detail.rs, Activity rendering — builds the thread from those comments and reviews.

gh pr view --comments does not include the individual review comments attached to lines in the diff. The current detail query also omits them. This was verified against a real PR containing inline review comments.

A reviewer can request changes and put the actionable instructions entirely in inline threads. The agent then sees the review summary or decision without receiving the actual feedback it was asked to address. Those threads are also absent from Activity.

Recommended fix:

Retrieve review threads through the GitHub GraphQL API, including:

  • Thread resolution and outdated status.
  • File path and available line/location information.
  • Comment bodies, authors, URLs, and replies.
  • Pagination for both threads and their comments.

Update the handoff instructions to retrieve and inspect these threads, prioritizing unresolved feedback while retaining enough context to interpret it. If Activity is intended to show review feedback, include the threads there as well.

Add a fixture with a changes-requested review whose actionable feedback exists only in an unresolved inline thread. Verify that the handoff's retrieval path includes that feedback and distinguishes resolved threads.

@jsgrrchg

jsgrrchg commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

This includes the text selection bug plus a few others.

  1. [P2] Refresh can associate an outdated diff with the latest commit
    File: crates/ui/src/pull_request_detail.rs:695

    Refreshing from the Code tab while a diff request is pending does
    not cancel that request. If it completes before the updated detail
    request, its old patch is retained and cached alongside the new commit.

    Impact: users can review outdated code without any warning.

    Fix: cancel pending diff requests on refresh and verify that each
    response matches the currently displayed revisions.

    Validation: reproduced with a test controlling response order.

  2. [P2] Agent handoff rejects valid fork checkouts with an upstream remote
    File: crates/ui/src/pull_request_handoff.rs:279

    matching_checkout compares the PR repository against a single checkout
    remote, usually origin. When origin points to a fork and upstream
    points to the main repository, it rejects the available checkout.

    Impact: review and remediation actions cannot start an agent session
    for a PR targeting the main repository.

    Fix: check the checkout’s other remotes while preserving device
    validation.

    Validation: reproduced with a temporary Git repository containing
    origin and upstream remotes.

  3. [P2] Clicking PR body text can unexpectedly select multiple paragraphs
    User-reported issue.
    File: crates/ui/src/pull_request_detail.rs:2101
    Related code: crates/ui/src/markdown/render.rs:1507

    The PR view uses Markdown selection without resetting its text-position
    registry on each repaint. After content moves, stale and duplicate
    entries remain mixed with the current geometry.

    Impact: a minimal mouse-move event during a click can unexpectedly
    select multiple paragraphs. Descriptions also reuse the same
    pr-description selection key across different PRs.

    Fix: integrate selection_frame_reset_for before painting the content,
    isolate selection by PR, and clear selection state when leaving.

    Validation: after moving the content, a click followed by a mouse-move
    event at the same coordinates selected duplicated paragraphs.
    Resetting the registry prevented selection with the same events.
    Reproduced in PR body rendering; Queue has not been independently tested.

Overall validation: 123 existing tests passed.
No changes were left in the branch.

gaelcado and others added 14 commits October 8, 2026 19:12
Co-authored-by: José Gurruchaga <jsgrrchg@gmail.com>
Co-authored-by: José Gurruchaga <jsgrrchg@gmail.com>
Co-authored-by: José Gurruchaga <jsgrrchg@gmail.com>
Co-authored-by: José Gurruchaga <jsgrrchg@gmail.com>
Show every changed file in one virtualized stream with foldable,
sticky file headers, a directory-grouped tree that follows the scroll
position, and a compact picker on narrow windows. The tab bar gains
counts, neutral selection, and tablist keyboard navigation.
Add ListChangeRequestPage, which follows GitHub search cursors 50 at a
time and reports the total match count. Cursors are validated before
they reach gh, and the single-page listing keeps serving older clients.
The board shows GitHub's total count and appends further pages on
request, without repeating a PR that moved between pages. Engines
without paging fall back to the single-page listing.
katulevskiy and others added 2 commits October 8, 2026 11:24
…eronsh#777)

* Fix durable remote send and host wake recovery

* Couple accepted chat rows to durable server host wakes

* Make Linux browser captures wait for mapped and presented frames

---------

Co-authored-by: Wing <contact@winglee.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Explorer: group finished subagents, show running counts

The Subagents section lists running subagents as plain rows, then folds
everything finished into a Finished dropdown (closed by default) with
Completed and Failed lists inside it. Each list collapses on its own and
pages at ten rows.

A "● N" pill shows how many subagents are running: on sidebar chat rows
(in place of the working spinner), under the titlebar's explorer button,
and beside the Subagents title. To badge chats that are not open, the
session row now carries `running_subagents`, published by the engine as
subagents open and settle. It is optional on the wire and stale rows
count as zero, like the working indicator.

* Explorer button: pulse and count badge for running subagents

The pill hung under the button and crossed the titlebar hairline. Mark the
button itself instead: its face breathes in the activity tone and a small
count badge sits on its corner, both drawn over the button so titlebar
layout is unchanged.

* Sidebar: running-subagents pill takes the time stamp's place

The pill no longer replaces the row's own activity indicator, so compact
titles stop shifting. In compact rows it stands where the time stamp is
while subagents run; in regular rows it leads the status label.

* Running-count pill and badge: centre the digits

The titlebar badge had no explicit line height and used the UI font, so its
digit sat about a pixel high; the sidebar pill's digits sat half a pixel low.
Both now draw their count through one helper: mono digits in a box exactly as
tall as the pill or badge, with a small optical lift (and, for the badge, a
rightward shift) measured from rendered captures at one, two and three
characters.

* Running count: centre digits by font metrics, drop hand-tuned offsets

Geist Mono's digits are centred on the line box and each digit's ink is
centred in its advance (checked from the font tables), so with the line height
equal to the box height nothing needs nudging. The earlier optical offsets
were fitted to two monitor scales and gave the same result there as none; at
a whole-number scale they would only add error. Residual misalignment is
device-pixel rounding of the baseline, at most half a device pixel.

* Running-count pill: hang dot and digits off one baseline; balance the 99+ badge

The pill's dot was a box centred in the pill while the digits' vertical
position followed the text engine's own metrics and rounding, so at
fractional display scales the two could differ by a device pixel. The pill
now paints its background, dot and digits in one canvas: the digits' baseline
is snapped to a device pixel and the dot is centred on the digits' middle
from that baseline, so they share a centre at any scale.

A capped label ends in '+', whose thin arms leave the eye a wider gap than a
digit does; trim the padding after it by a pixel in both the pill and the
titlebar badge.

* Titlebar count badge: paint it so the digit is centred exactly

The badge was a laid-out box, whose text landed on a rounded device pixel and
read as the digit sitting left of centre. Paint the badge and its digits in
one canvas instead: the digits are centred on the badge's real middle, with
the baseline placed as in the sidebar pill. Measured for every digit 1-9 on
1.25x and 1.875x captures: centred to within half a device pixel, exactly for
most.

* Add running_subagents to Session fixtures added on main

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: wing-anara <wing@anara.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@jsgrrchg

jsgrrchg commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

@wingleeio couldn't find any more edge cases, bugs or improvements for now, LGTM. Thanks @gaelcado 😊

katulevskiy and others added 2 commits October 8, 2026 18:07
…eronsh#522)

* feat(transcript): open tool-call files in the files panel

Clicking the file badge on a Read/Write/Edit/Patch tool row now opens
that file in the right-hand files panel, instead of only expanding the
row to show its path. The badge goes through the transcript's existing
workspace-link handler, the same route Markdown file links take, so it
resolves against the same roots (the chat's checkout, its parent's, and
this device's projects) and opens outside absolute paths read-only.

The badge stops propagation, so clicking it does not also toggle the
row's accordion; the rest of the header still expands the output or
diff. Paths the link handler cannot open keep an inert badge rather
than looking clickable and doing nothing.

* feat(files): open the active file in the default editor

Add an "Open in default editor" button to the file viewer's breadcrumb
toolbar, next to "Reveal file in tree". It hands the file to the
platform's default application (xdg-open, open, ShellExecute) via
gpui's open_with_system.

The button only appears for workspaces on this device: a remote
space's files live on another machine, where the local default app
cannot reach them.
@jsgrrchg

jsgrrchg commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Found one more issue CMD/Ctrl+C does not copy selected text in Summary. After selecting “First” in the description and pressing Ctrl+C, the clipboard remained unchanged. The PR view lacks the copy handler used by chat and Markdown preview.

wing-anara and others added 2 commits October 9, 2026 14:26
…views

Security
- Images in pull request text load through the app's bounded decoder
  (4096 px per side, 64 MiB, one frame, sanitized SVG), at most four at
  once, instead of GPUI's unbounded image loader.
- The image transport refuses NAT64, 6to4, Teredo, IPv4-compatible,
  multicast, reserved and benchmarking addresses, never uses a proxy
  (which would resolve names past the public-address check), and sends
  no Referer on redirects.
- Check links open only https pages; handoff prompts drop invisible
  bidi, zero-width and tag characters; the handoff names the repository
  from the engine's URL; GetChangeRequestRepository accepts only chat or
  project checkouts on the device.

Board
- Virtualized results list (the transcript's ListState pattern), so long
  boards scroll smoothly; scroll position holds across paging.
- The engine keeps the board's first pages in SQLite, one row per pull
  request per gh account plus each query's ordered results, so the board
  opens instantly and a pull request reads the same in every filter. An
  aged page shows at once and refreshes behind it; Refresh always reads
  GitHub.
- Filters read as controls; header controls share one height, refresh
  sits left of the repository; Load more is a filled button.
- Repository identity follows renames and SSH host aliases.

Pull request view
- Back/forward history covers the board and each pull request; the
  header is a plain title like the session header.
- The code viewer clips to its rounded card per pixel
  (zui 361ff20, Window::with_rounded_clip).
- Your reviews and comments read as yours before you comment (the
  engine reports the viewer's login); quieter posting and inline-thread
  rows; Cmd/Ctrl+C copies selected text.
- Summary/Code/Activity pill uses the composer's material with spaced
  segments; the Pull requests entry sits beside Settings; the copy icon
  no longer overlaps itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main's UI test build fails after zeronsh#522 and zeronsh#813 met: the fixture predates
ToolItem::images.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@wing-anara wing-anara left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks, Gaël. Approving with follow-ups pushed onto the branch:

  • Security: images in PR text now load through the app's bounded decoder (4096 px, 64 MiB, one frame, sanitized SVG, at most 4 at once); the image transport refuses more private/embedded address ranges, never uses a proxy, and sends no Referer; check links open only https; handoff prompts drop invisible bidi/zero-width/tag characters; GetChangeRequestRepository only accepts chat or project checkouts.
  • Board: virtualized list, SQLite cache of first pages (one row per PR per gh account), repo identity that follows renames and SSH host aliases, back/forward history, header/filter polish.
  • Detail: per-pixel rounded clipping for the code card (zui 361ff20), own-comment detection via the viewer's login, Cmd/Ctrl+C copy, quieter posting and inline-thread rows.
  • Also fixes main's broken ReadFile fixture (#522/#813) so UI tests build.

CI is green on 38e2f57.

@wing-anara
wing-anara merged commit afca583 into zeronsh:main Oct 9, 2026
23 checks passed
@gaelcado
gaelcado deleted the wip/pr-board branch October 10, 2026 11:18
hoangvu12 added a commit to hoangvu12/roboco that referenced this pull request Oct 11, 2026
…acklog window

Source: .scratch/upstream-drift/2026-10-11.md. Tickets 01-18 actionable
(quick ports, chips supersedes wave-6 03, the sidebar series 06-09, the
older missed fixes 13-16, parity verification 17-18); 19 is the GPUI pin
sync gating 20 (zeronsh#856) and 22 (zeronsh#813 Windows browser); 21 (zeronsh#484 PR board)
PARKED on a product decision.
hoangvu12 added a commit to hoangvu12/roboco that referenced this pull request Oct 11, 2026
… push URL is disabled

Upstream 73051ec (wave7 ticket 03, upstream zeronsh#863): a push URL that
doesn't parse as a remote (a fork's DISABLED--push-to-your-fork-origin-
instead placeholder) is ignored, so the checkout inspector falls back to
the remote's fetch URL — which still identifies the repository.

Deliberately excluded: upstream's repository_for_checkout assertion and
helper — that machinery belongs to the zeronsh#484 PR board (ticket 21). Test
fixture uses acme/roboco. 28/28 source_control tests.
hoangvu12 added a commit to hoangvu12/roboco that referenced this pull request Oct 23, 2026
Wave-7 ticket 06 — port of upstream 05f5f2b + ae19fe3 + 672cc14,
with the 2026-10-22 riders 26c8e84 (zeronsh#909, drag-reorder) and
81e04e1 (zeronsh#901, Archived toggle) folded in; the 05f5f2b/ae19fe31
head start came from the saved ticket-06 fold-ins patch.

Upstream 672cc14 (searchable popovers, ordered sidebar sections,
titlebar sidebar options):
- popover: every searchable popover (project, device, branch, spaces,
  changes ref, font) shares the model picker's search row (40px,
  magnifier, full-bleed hairline) and 32px picker rows in one tone with
  scroll edge fades and rails (search_input_frame/search_row rework +
  picker_row/picker_icon_slot/picker_divider).
- Project rows in both project dropdowns wear the project artwork
  (ProjectIcons extracted from Shell's inline cache into the shared
  pipeline shape, now owned by Shell and Pickers); the device tag is
  gone from the spaces dropdown and the sidebar filter trigger.
- All sidebar section headings share one header; collapsed sections
  show a muted count badge (monospace, cut like the PR badge) instead
  of a "(N)" label suffix; header glyphs share the label's tone.
- Custom sections keep a placed order, as Pinned does: assign carries
  optional after/before anchors (section_insert_index, the pin rule),
  drags reorder members in place (sidebar_section_drop_anchors reads
  the preview gap), and the sidebar lists section members in placed
  order rather than by activity.
- The view options moved to the titlebar at the sidebar's trailing
  edge (render_sidebar_options_titlebar, sliding/fading with the
  sidebar, occluded over the drag strip); View > Show gains a Project
  filter toggle (hiding it clears the filter) and SIDEBAR_MIN widened
  224 -> 242 so the options button fits beside the titlebar controls.
- Polish: agent-update pill summary fits the card border (no left
  strip), thinking/tool-call group headers roll as their summary
  changes.

Rider 26c8e84 (zeronsh#909): dragging a section header lifts the section
(frosted, in the list's movement layer) and slides its siblings aside;
releasing outside the sidebar sends it home. Roboco's engine-local
equivalent of SidebarSectionChange::Move: the reorder splices the
sections list and pushes the SetSidebarSections snapshot.

Rider 81e04e1 (zeronsh#901): sidebar toggle for the Archived section
(sidebar_show_archived) — hidden removes the whole shelf without
touching its chats or disclosure state; a reveal into it re-enables it.

Wire change: the SidebarSection proto doc now states the placed-order
contract — membership and the section list carry their placed order in
the array order through the engine-local LWW replaces. Upstream's
per-member fractional orderKey registry rows + HLC clocks exist only
for cross-device convergence, which roboco excludes (ADR 0004); the
engine store already preserves list order, so no per-key minting and
no sidebar-section-order-v1 capability gate were carried. Wiregen
regenerated (SidebarSection.ts doc); --check gate green. The web peer
(sectionRows) lists members in placed order, with tests.

Excluded by intent: crates/client hunks (cloud), pull_requests.rs
popover hunks (rides parked zeronsh#484/ticket 21), crates/mobile FFI
pin_session_at/place_session (Android parity — held for a mobile
follow-up per ticket step 6), iOS hunks, and the registry
cross-device convergence tests (replaced by the engine-side
placed-order restart test plus UI placed-order/drag/anchor tests).

Verification: cargo check -p roboco-doc -p roboco-proto -p roboco-ui;
cargo test -p roboco-ui (1618 passed); sidebar-sections/spaces/
pickers/appearance/changes/chat-rename/transcript/harness-updates
tests; cargo test -p roboco-engine sidebar_state (10 passed);
wiregen --check green; pnpm -r build green; web vitest sidebar suites
green (6 unrelated failures reproduce on clean main).
hoangvu12 added a commit to hoangvu12/roboco that referenced this pull request Oct 23, 2026
Upstream a408a81 (wave7 ticket 09, the standalone half):

- toast.rs (new, verbatim port): Sonner-style toasts stacked in the
  window's bottom-right corner — 4s countdown that pauses while hovered
  or the window is inactive, up to three visible with 5% step-narrowed
  cards, fan-out on hover, rise-in/fade-out motion, swipe-right-to-
  dismiss, and repeat-toasts restarting the front one instead of
  stacking. Push with show/error/success/info; take_error lifts a
  view's pending Option<error> into a toast (deferred — never
  mid-render). The shell mounts render_toaster once per window, over
  every phase and route.
- shell.rs: show_notice() replaces every direct sidebar_notice write
  (deep-link notices Info, copy confirmations Success, mutation/
  rename/pin failures Error); the field survives as the toast-message
  mirror the pin-write seam matches (clear_pin_write_notice). The
  sidebar's inline mutation-failure strip is gone. The chat context
  menu's Copy row now opens a nested submenu beside the card — hover
  intent with a grace toward the open submenu (the sidebar options
  menu's HoverIntent recipe), side-aware placement, presses inside the
  submenu no longer dismiss the menu first, and the old in-place Copy
  page (Back row) is deleted. Copy confirmations are success toasts.
- Call-sites swapped to toasts: actions_ui (setup action failures),
  harness_updates (agent update failure), sidebar_pins
  (set_pin_write_notice), sidebar_sections (engine/archive failures),
  spaces (project-filter rename refusal).
- Settings + dictation: accounts, appearance (background_error),
  archived, devices, harnesses, thread_naming and the voice card
  surface their transient action errors as toasts via take_error at
  render top; the inline dismissable error strips are removed.
  Roboco-specific: devices keeps the durable registry
  configuration_error inline (engine-local config state, not a
  transient action error). notify.rs (OS desktop banners) and
  notice.rs (composer/transcript notice chip) are not the surface
  this commit replaces — untouched.
- Tests: the toast.rs suite (expiry/pause, dedupe, glide reversal) and
  copying_from_the_copy_submenu_shows_a_toast in sidebar_sections
  (chat-copy-roboco); the archived delete-all failure test now
  asserts the toast surface.

Deliberately excluded: every pull_requests.rs repository-menu hunk
(roboco has no pull_requests.rs; the picker-style repository menu
rides parked ticket 21 / upstream zeronsh#484), and the cloud sign-in /
cancel-sign-in notice hunks in shell.rs (roboco has no cloud sync —
no counterpart call sites). No wire changes.

Verification: cargo check -p roboco-ui clean (no new warnings);
cargo test -p roboco-ui 1617 passed.
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.

4 participants