Skip to content

feat(viewer): show live reviews as running, add collapse-all and agent handoff - #2

Closed
SaahiilM wants to merge 11 commits into
mainfrom
feat/viewer-live-session-and-agent-handoff
Closed

SaahiilM wants to merge 11 commits into
mainfrom
feat/viewer-live-session-and-agent-handoff

Conversation

@SaahiilM

@SaahiilM SaahiilM commented Oct 2, 2026 •

Copy link
Copy Markdown
Owner

Description

The viewer reported every review still in progress as aborted. Aborted defaulted to true and was cleared only by a session_end record — but a running review has not written session_end yet, so the field really meant "no session_end", conflating in-progress with dead.

This PR separates them using an exclusive OS lock the writing process holds on a <session-id>.lock sidecar for the life of the run, so the kernel decides liveness. It also adds a collapse-all control for finding groups, and a per-finding handoff into Claude Code / Codex / Cursor.

Verified in a real browser, not just tests: three session fixtures rendered through the actual handlers showed running for the locked session, aborted for the crashed one, legacy for the finished one.

What each commit does

Commit Change
feat(session) Hold a liveness lock for a run's duration; add Close for an abandoned run
feat(viewer) Report running ahead of aborted; tick duration client-side; reload on completion
feat(viewer) Collapse-all control; per-finding agent handoff
fix(viewer) Build deep links before the click, not inside it
docs viewer.md in all five locales

Each builds and passes tests standalone — verified by checking out every commit individually.

Two things worth reviewing closely

The liveness mechanism. A sidecar file plus an OS lock, rather than an mtime heuristic. The kernel releases the lock however the process dies, so a crash needs no cleanup step. The tradeoff: the writer changes, and Windows needs LockFileEx rather than flock. Worth a look at whether you would rather have the heuristic and keep the writer untouched.

The deep-link fix is a separate commit on purpose. The first implementation did not work on any platform: the click handler cancelled the click, assigned the href, then re-dispatched the click via setTimeout. That deferred click runs outside the transient user activation browsers require before handing an external protocol to the desktop, so it was silently dropped. Headless Chromium showed zero navigations requested before the fix, one after. The commits are split so the regression and its fix are each reviewable on their own.

Known limitation

cursor:// is documented by Cursor but no handler is registered on this machine, so that entry does nothing here until Cursor is installed. claude-cli://, codex:// and vscode:// are all registered and verified working.

No vendor documents a prefilled-prompt scheme for its CLI, so the CLI is reached by copying the prompt rather than by a link that would silently do nothing. Every target prefills without submitting — the user always confirms before an agent runs.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Refactoring (no functional changes)
  • Documentation update
  • CI / Build / Tooling

How Has This Been Tested?

  • make test passes locally
  • Manual testing (describe below)

make check, make test (0 failures) and make coverage (91.1%, threshold 90%) all pass. Cross-compiles clean for linux, darwin and windows.

Manual testing:

  • Three session fixtures rendered through the real handlers confirmed running / aborted / legacy respectively.
  • The deep-link regression was reproduced and fixed under headless Chromium over the DevTools protocol, driving a trusted click and counting the navigations the browser actually attempted.
  • Scheme handlers on this machine confirmed with xdg-mime query default.

New tests cover the lock (held, released, missing sidecar, idempotent close), the status rendering, the agent handoff context, and a regression test that fails if the re-dispatched click is ever reintroduced.

Checklist

  • My code follows the project's coding style (go fmt, go vet)
  • I have performed a self-review of my code
  • I have added tests that prove my fix is effective or my feature works
  • New and existing unit tests pass locally with my changes
  • I have updated the documentation accordingly (if applicable)
  • I have signed the CLA
  • I did not use AI/LLM to create this PR, or I disclosed the tool/model below and reviewed its output; I did not attribute commits to AI and will answer maintainer questions and review comments myself without AI/LLM.

AI disclosure

Tool: Claude Code (this session), model claude-opus-5 (anthropic/claude-opus-5[1m]).
Also used: two read-only research subagents to verify vendor deep-link schemes against primary documentation, and one to check whether VS Code's --open-url is a supported flag (it is not — it is an internal marker, so it is unused here).

Every commit was reviewed and rewritten by hand before being pushed. Two mistakes I introduced and corrected:

  • The first commit message carried a Co-Authored-By trailer. AGENTS.md rule 6 forbids AI attribution, so it was removed; git log shows no trailers on any commit.
  • The deep-link implementation was wrong on first write and did not work on any platform (see above). It was kept as a separate fix commit rather than squashed, so the regression is visible rather than hidden.

I understand every line above and can answer questions on the reasoning without consulting the tool.

Prompts used

For an agent reviewing this PR, these are the prompts that shaped it — they state the goal and the changes, so the intent is checkable against the diff.

Goal (the bug as reported):

can we update the web ui when we run the version command to show if there is an active session in a repo, also for some reason it says staus as aborted in this for an actively running session. in the session view also therei sa proble with the status and durations which ned ot besame as he ones we see in the f possible. or we could mark the start dtm and then have the js do the calculationsof the druation.

This is the verbatim request. It named three symptoms — a live session shown as aborted, status/duration disagreeing with the CLI, and duration computed from the start timestamp in JS. It also contained a misremembering ("version command"); I asked and it meant the viewer, not ocr version.

Follow-up (agent menu and ordering):

i think that we should also have a collapse all button on the review screen . the agents menu when clicked shold for the code should open the native codex application and not the cdex extension in vscode. same for claude code. native app-> cli-> vscode extension.

This set the app → CLI → extension ordering. Verifying what each vendor actually supports changed the outcome: no CLI scheme exists for any of them, so the CLI became the copy action instead of a menu entry.

Report of the failure (leading to commit 4):

it is not opening the apps or deeplink on linux did we implement it for linux?

This is what exposed the user-gesture bug. I then verified it rather than assuming, by driving headless Chromium before and after the change and counting navigations.

Two design decisions I raised rather than assumed, both answered before implementing: how to distinguish a live run from a crashed one (lock file over mtime heuristic) and where to put the work (a worktree, to keep unrelated in-progress changes separate).

Related Issues

🤖 Generated with Claude Code

A session file with no session_end record was indistinguishable from a
session whose run was still in progress, because a running review has not
written session_end yet.

A writing process now holds an exclusive OS lock on a <session-id>.lock
sidecar for the life of the run, so the kernel - not a heuristic over
timestamps - decides whether a session is alive. The lock is released
however the process ends, so a crashed or killed run needs no cleanup to
read as finished.

Close joins Finalize for a run that is abandoned rather than completed:
it releases the file handles and the lock without claiming completion.
The viewer reported every session without a session_end as "aborted",
which described every review still in progress as well as every one that
had died. Status now consults the liveness lock first, so an in-flight
run reads as running and a dead one still reads as aborted.

Duration is computed in the browser from the start timestamp, because a
run that has not written session_end has no final duration to report. The
page reloads itself once the run ends, so the terminal figures always
come from the server rather than from a guess made client-side.

The repository list shows a running badge linking straight to the live
session, and both pages poll while a run is in flight.
Two affordances for working through a review from the session page.

Collapse all folds every per-file finding group to a one-line summary, so
a page with hundreds of findings becomes a scannable index of files and
their counts. Groups hidden by the current filters are left alone, keeping
what looks open in step with what is visible.

Fix with agent carries one finding - file, line range, severity, category,
description and both versions of the code - into Claude Code, Codex or
Cursor, prefilled. Targets are ordered app then editor extension, using
each vendor's documented scheme. No vendor documents a prefilled-prompt
scheme for its CLI, so the CLI is reached by copying the same text instead
of a link that would silently do nothing.

Every one of these prefills a composer without submitting, so the user
always confirms before an agent runs.
The menu item handler cancelled the click, assigned the deep link to the
anchor's href, and re-dispatched the click on a later turn of the event
loop. That deferred click runs outside the transient user activation every
current browser requires before handing an external protocol to the
desktop, so the navigation was dropped and the click did nothing at all.

Verified in headless Chromium: zero navigations requested before this
change, one after.

The href is now built while the menu opens, which is itself a real click,
and the item's own click is left to the browser.

Placeholder hrefs become about:blank rather than "#", so a link followed
before the script rewrites it cannot scroll the page.
Documents the running status and ticking duration, what the .lock sidecar
is for, the collapse-all control, and the agent handoff including that no
vendor prefills a CLI prompt and that every target waits for the user to
confirm before running.

All five locales updated.
…ndoff

The repositories page could not detect a review *starting*. watchRepoActivity
returned early when no repo was active, and then compared against a marker
element that was truthy on every path reaching it, so it only ever noticed
runs ending. Opening the viewer and then launching a review - the ordinary
workflow, and the one this feature exists for - showed no badge at all.
It now captures the initial state and compares against the server each
poll, in either direction.

The same function checked document.hidden at setup rather than per tick,
so a page opened in a background tab never started polling at all.

agent.js registered one document-level click listener per finding, so a
session with hundreds of findings ran hundreds of listeners on every click
anywhere in the viewer. The dismiss handler is now installed once and only
touches menus that are open: 2 listeners for 40 findings, down from 41.

Prompt truncation used a plain slice, which could cut inside a fenced code
block and leave it unclosed, or split a surrogate pair and emit invalid
UTF-16. It now backs off to a line boundary, never splits a pair, and closes
an unbalanced fence.

Also drops an unread field on the lock handle and replaces two `var`s with
`const` in code that uses const throughout.
@SaahiilM
SaahiilM force-pushed the feat/viewer-live-session-and-agent-handoff branch from 088fedf to e846655 Compare October 2, 2026 22:49
fix(llm): allow unlisted OpenRouter model overrides (alibaba#1610)
Pulls in main's commits (ACP service, ChatGPT auth, scan token-budget
and prompt-overflow fixes, provider docs, dependency bumps, etc.) into
the live-session and agent-handoff viewer branch.

The only conflict was go.mod (version bumps for golang.org/x/sys, x/net,
x/sync, x/text), resolved to main's versions; the branch builds cleanly
afterwards.
The list test simulated an abandoned run by closing the session file
directly, which left the liveness sidecar locked. An open handle cannot
be deleted on Windows, so the test's temp dir cleanup failed there.
Closing the session the way an abandoned run does releases the lock too.
os.UserHomeDir() prefers USERPROFILE on Windows, so setting HOME alone
left the sessions these tests create in the real profile instead of the
temp dir they inspect. The session package already redirects both; the
viewer tests now do the same.
@SaahiilM

Copy link
Copy Markdown
Owner Author

Superseded by #5.

This PR bundled two independent things: the live-session-status work (showing a running review as running rather than aborted, via an OS lock on a <session-id>.lock sidecar) and the per-finding agent handoff with the collapse-all control.

Upstream already has its own implementation of the liveness half in alibaba#1757, which uses the same sidecar path and defines the same lockExclusive helper in package session. Merging both would have produced a symbol-level conflict, and the two would have competed over the same sidecar file.

#5 is based on upstream main and carries only the handoff and the collapse-all control, neither of which upstream has. The liveness work is left to upstream's implementation.

Closing this one.

@SaahiilM

Copy link
Copy Markdown
Owner Author

Closed in favour of #5, which contains the agent handoff and collapse-all control on top of upstream main. The liveness half is covered by upstream's own PR, so keeping this one open would have produced a conflict.

@SaahiilM SaahiilM closed this Oct 19, 2026
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