Skip to content

feat(ui): Analyze/Refine top-bar flow with incremental refinement - #17

Open
jakob1379 wants to merge 3 commits into
jamesaphoenix:mainfrom
jakob1379:t3code/improve-analyze-refine-ux
Open

jakob1379 wants to merge 3 commits into
jamesaphoenix:mainfrom
jakob1379:t3code/improve-analyze-refine-ux

Conversation

@jakob1379

@jakob1379 jakob1379 commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Top bar. Two plain buttons replace the in-panel refinement banner.

  • Analyze is blue before an analysis exists and red once one is loaded. In the red state a first click arms it ("Confirm re-analyze?") and a second click within 3 s runs it; blur or timeout disarms.
  • Refine runs LLM refinement, reads "Refine (update)" when a baseline exists, swaps to "Refining… ✕" while a job runs, and is disabled with a tooltip naming the reason (no analysis / analysis running / no AI provider).

Refinement backend.

  • Incremental refinement carries the previous refined grouping onto a fresh analysis and asks the LLM only for adjustments (carry_over_grouping).
  • In-flight refinement jobs can be cancelled.
  • RefinementResult records head_sha and the covered files; older cached JSON still deserializes (tested).
  • After re-analysis a cached refinement is auto-applied only when its head commit and its full file set (grouped + infrastructure) match the fresh diff; otherwise it becomes the baseline for the next incremental update. This fixes File 'src/sepseeq/cli.py' not found in diff after re-analyzing with a stale cached refinement.

Also: PR/MR head polling with a "New commits" bar, and a typecheck:e2e npm script so Playwright spec files are type-checked without a browser.

Preview

out.mp4

Tests

  • cargo test -p diffcore-core -p diffcore-tauri: 2229 passed, 0 failed.
  • npx tsc --noEmit and npm run typecheck:e2e: clean.
  • npm run build: OK.
  • Playwright was not run: its bundled Chromium cannot launch on this machine (missing libglib). The top-bar states were verified manually in the built app instead.

@jamesaphoenix

Copy link
Copy Markdown
Owner

Here's a thought, should we just have 2 buttons.

  1. Analyze
  2. Analyze & Refine

Then allow the user to specifically set a default in the UI? We want to minimize clicks here.

@jakob1379

jakob1379 commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor Author

Here's a thought, should we just have 2 buttons.

1. Analyze

2. Analyze & Refine

Then allow the user to specifically set a default in the UI? We want to minimize clicks here.

I'm not sure I made it clear enough, the the red re-analyze was also meant for when something upstream is pushed to e.g. the PR you are looking at.

To continue on this it might be worth thinking about the other potential clicks around the UI. What about analyze flow, though it might be a token-heavy operation, its almost always the feedback I reach for, at least for larger MRs. What are your thought here, do you think there is an elegant way to bind them together?

@jamesaphoenix

Copy link
Copy Markdown
Owner

Having thought about it, my suggestion is as follows:

  1. Have a single button called Analyze.
  2. To the right of that button add a pre-checked checkbox that is refine (so people naturally use an LLM

  • Let's also make the button not grow in size between the words Analyze and Reanalyze
  • If there is a change upstream, we should change the button red with Reanalyze

Then people can opt out of using an LLM pass but it's a single click for users that just want to run the automated review.

The other thing to consider here is that we might want to pass the existing groupings to claude if we are doing a reanalysis (changed code upstream). It's worth checking we do this rather than starting from scratch, as we could then just look at new files and merge/change groupings rather than reading the entire context again.

@jakob1379

Copy link
Copy Markdown
Contributor Author

Agreed. Red and a double verify as to not accidentally lose the current state. This will make the width of but button constant without changing wording.

Regarding context forwarded I partly agree. The simple approach is purge an re-run. Alternatively we will have to follow the edges to find the affected code by the recent changed, and updated relevant groups based on this.

What would be your primary concern on what to optimize here? I am not sure incremental updates like this necessarily saves tokens.

@jamesaphoenix

Copy link
Copy Markdown
Owner

My view point would be on the concern is as the diffsize grows, the search grows.

Imagine if 1 file had changed and we throw away at 200 file grouping to start re-grouping.

Re-analysis - small edits: We probably want something like if there is less than 10% of new files -> add new files to be classified into the existing groups.
Re-analysis - large edits: Purge and re-run.

@jakob1379

Copy link
Copy Markdown
Contributor Author

I'll see if a can make the traversal work to ensure we only hit the right stuff, then :D

@jamesaphoenix

Copy link
Copy Markdown
Owner

Cool, just to clarify:

  • I think traversal is fine to throw away -> re-run.
  • We want to keep the LLM groupings of an existing refine step (because the LLM has already done a lot of work there).

Hope that clarifies that we're trying to save tokens + search time on LLMs, not necessarily graph traversal.

Top bar: plain Analyze and Refine buttons replace the in-panel refinement
banner. Analyze is blue before an analysis exists, red once one is
loaded, and asks for a second click before discarding current groups.
Refine greys out with a tooltip naming why it is unavailable.

Refinement: incremental mode carries the previous refined grouping onto
a fresh analysis and asks the LLM only for adjustments; in-flight jobs
can be cancelled; a cached refinement is only auto-applied after
re-analysis when its head commit and full file set match the fresh
diff, otherwise it becomes the baseline for the next update.

Review fixes: re-analysis cancels an in-flight refinement and rejects
its pending stream so the top bar cannot stick on "Refining"; a job
that finishes after a fresh analyze no longer overwrites it; the
in-memory refinement baseline is cleared on every analyze so it cannot
leak across repos; carried-over groups only keep files the fresh pass
still groups; the PR head watcher no longer emits a phantom push when
its seed poll failed. Adds a `typecheck:e2e` script so spec files are
type-checked without launching a browser.
Applies the review decisions from PR jamesaphoenix#17.

Top bar: the standalone Refine button is gone. Analyze is the only
button, with a pre-checked "Refine" box beside it, so the common path
is one click and opting out of the LLM pass is one more. The box is
bound to the persisted refinement_enabled setting, so unchecking it is
also how the user changes the default, and it stays in sync with the
settings panel. The button holds a fixed width so swapping between
Analyze, Reanalyze, Analyzing and Confirm cannot shift the bar under
the pointer, and it only turns red for the case that warrants it: new
commits upstream. That click re-resolves the PR tip rather than
re-analyzing a stale checkout. The second-click confirmation stays.

Every explicit analyze — the button, Enter in the repo field, Refresh
on the new-commits bar — arms the refine chain from the checkbox, so
auto-analysis on load never spends tokens unasked.

Refinement: carrying the previous LLM grouping onto a fresh analysis
now only pays off while the diff has barely moved, so it is gated on
new_file_ratio against INCREMENTAL_MAX_NEW_FILE_RATIO. Past 10% of the
files being new since the last refinement, repair costs the model more
context than regrouping, and the pass starts from scratch instead.
Graph traversal is re-run either way; only the model's grouping work is
worth preserving.
Toggling Refine (or Annotations, or the metadata pass) off in the app
stuck for as long as no repository was loaded, then silently flipped
back on. The app writes only the global config, but a config loaded
from a repo directory merged the two enable flags with OR — and those
flags are plain bools defaulting to true, so a repo that never mentions
refinement contributes true and the global false can never win. Every
repo without a .diffcore.toml hit this, which is most of them.

Merge with AND instead: these passes cost the user money, so either side
may switch one off and neither can force it back on. A repo opting out
still wins, which it did before. annotations_enabled was not merged at
all, leaving its toggle equally stuck, so it joins the other two.
@jakob1379
jakob1379 force-pushed the t3code/improve-analyze-refine-ux branch from ae7af55 to 77fe11d Compare September 23, 2026 05:34
@jakob1379

jakob1379 commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor Author

I iimplemented your decisions from above, the preview has been updated to the new layout

Top bar

  • Now has a single Analyze button; the standalone Refine button is gone. A pre-checked
    Refine box sits to its right, so the common path is one click and opting out
    of the LLM pass is one more.
  • The box is bound to the persisted refinement_enabled setting rather than
    component state, so unchecking it is how you change the default, and it stays
    in sync with the settings panel.
  • Fixed width on the button, so swapping between Analyze / Reanalyze / Analyzing /
    Confirm can't shift the bar under the pointer.
  • Red only for the case where needed: new commits upstream. That click re-resolves
    the PR tip instead of re-analyzing a stale checkout. Previously it was red
    whenever any analysis was loaded, which made the color meaningless.
  • Second-click confirmation kept, so a re-analyze can't silently discard refined
    groups.

Incremental refinement

Carrying the previous LLM grouping onto a fresh analysis is now gated on how far
the diff actually moved: past 10% of files being new since the last refinement,
repair costs the model more context than regrouping, so the pass starts from
scratch. Below that, the previous groups carry over and the model is asked only
for adjustments. Graph traversal is re-run either way, per your clarification.

A bug was found

Putting the refine flag in the top bar exposed that it didn't stick: toggle it off,
and it silently flipped back on as soon as a repo was loaded. apply_global_llm_defaults
merged the enable flags with OR, and those flags are plain bools defaulting to
true, so a repo that never mentions refinement contributes true, and the
global false can never win. Every repo without a .diffcore.toml hit this.

Merged with AND instead: these passes cost money, so either side may switch one off
and neither can force it back on. A repo opting out still wins, as before.
annotations_enabled wasn't merged at all, leaving its toggle stuck the same way,
so it joins the other two. Regression test added; reproduced and confirmed fixed
end-to-end against diffcore-web.

One open question

There's no longer a way to refine without re-analyzing. That falls out of the
single-button design, analysis is deterministic and cheap, and the expensive half
now carries over, but if you want "refine what's on screen" back, I'd hang it off
the checkbox rather than reintroduce a button. what do you think?

jakob1379 added a commit to jakob1379/diff-core that referenced this pull request Oct 5, 2026
Applies the review decisions from PR jamesaphoenix#17.

Top bar: the standalone Refine button is gone. Analyze is the only
button, with a pre-checked "Refine" box beside it, so the common path
is one click and opting out of the LLM pass is one more. The box is
bound to the persisted refinement_enabled setting, so unchecking it is
also how the user changes the default, and it stays in sync with the
settings panel. The button holds a fixed width so swapping between
Analyze, Reanalyze, Analyzing and Confirm cannot shift the bar under
the pointer, and it only turns red for the case that warrants it: new
commits upstream. That click re-resolves the PR tip rather than
re-analyzing a stale checkout. The second-click confirmation stays.

Every explicit analyze — the button, Enter in the repo field, Refresh
on the new-commits bar — arms the refine chain from the checkbox, so
auto-analysis on load never spends tokens unasked.

Refinement: carrying the previous LLM grouping onto a fresh analysis
now only pays off while the diff has barely moved, so it is gated on
new_file_ratio against INCREMENTAL_MAX_NEW_FILE_RATIO. Past 10% of the
files being new since the last refinement, repair costs the model more
context than regrouping, and the pass starts from scratch instead.
Graph traversal is re-run either way; only the model's grouping work is
worth preserving.
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.

2 participants