Overhaul History tab with GitKraken-style commit graph and unified activity#332
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
📝 WalkthroughWalkthroughAdds mode-aware git operations (pull modes, createTag, resetToCommit), head-change undo/redo, IPC/preload typing and handlers, CLI history commands and export, lane startPoint support, and a new history UI (commit graph, commit/detail panels, actions, timeline store). Comprehensive tests across services, IPC, CLI, and renderer. ChangesUnified Git + History feature
🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
41e6fcc to
86cc82a
Compare
|
Capy auto-review is paused for this organization because the monthly auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews. |
There was a problem hiding this comment.
Stale comment
PR Review
Scope: 46 file(s), +5886 / −212
Verdict: Needs changesThis PR overhauls the History tab with a commit graph, unified activity feed, lane/git context menus, and supporting main-process + CLI plumbing. The main blocker is URL/state handling when switching lanes on the Commits surface: a prior lane's
commitShacan stick in the query string and get re-applied to the wrong lane.
🐛 Functionality
[High] Lane switch keeps stale
commitShain the URLFile:
apps/desktop/src/renderer/components/history/HistoryPage.tsx:L133-L143,apps/desktop/src/renderer/components/history/HistoryPage.tsx:L208-L235,apps/desktop/src/renderer/components/history/useTimelineStore.ts:L294-L299
Issue: Changing the lane on the Commits surface clearsselectedCommitShain the store, but the URL writer only removescommitShawhensurface === "activity". The hydrator then reads the oldcommitShafrom the URL and re-selects it whilelaneIdalready points at the new lane, so the detail pane and selection can show a commit from the previous lane (or nothing useful if that SHA is absent locally).
Repro: Open Commits with?surface=commits&laneId=<lane-A>&commitSha=<sha-on-A>. Use the lane dropdown to switch to lane B. ObservecommitSharemains in the address bar and selection/detail try to use SHA from lane A on lane B.
Fix: WhenfocusLaneIdchanges on the commits surface, deletecommitShafrom URL params (and clearselectedCommitSha/selectedCommit) in the same transaction—e.g. extend the URL-write effect tonext.delete("commitSha")when the lane param changes, or clear commit selection insidesetFocusLaneId.[Medium] Git actions disabled after any load error
File:
apps/desktop/src/renderer/components/history/CommitHistoryView.tsx:L316
Issue:hasWorktreeis computed asBoolean(laneId) && !error, so any failedlistRecentCommits/listBranchesload (timeout, transient git error, etc.) marks the worktree missing and disables every context-menu git action with the "Lane worktree is missing" reason—even when the lane worktree is fine.
Repro: Provoke a transient git IPC failure (or temporarily breakgit log), reload Commits; after the error banner appears, right-click a row—the destructive/apply actions stay disabled with the missing-worktree message.
Fix: Pass a dedicatedhasWorktreeflag (e.g. from lane metadata or a narrow "worktree missing" error classifier) instead of coupling it to the genericerrorstring.[Medium] Deep-linked commit not found outside first 200 SHAs
File:
apps/desktop/src/renderer/components/history/HistoryPage.tsx:L195-L201,apps/desktop/src/renderer/components/history/CommitDetailPanel.tsx:L68-L74
Issue: Resolving?commitSha=only scanslistRecentCommits({ limit: 200 }). Commits older than that window never hydrate intoselectedCommit/resolvedCommit, so shared URLs and detail actions silently show an empty or partial commit even though the SHA is valid.
Repro: Copy a commit link for a SHA beyond the 200th commit on a long branch, paste into History, open Commits—the graph may not select it and the detail panel stays empty unless the user scrolls to load more and click manually.
Fix: On URL/commit selection, call a single-commit lookup (e.g.git show/ existinggetCommitMessage+ metadata) whenfindon the recent list misses, or raise the initial fetch limit whencommitShais present.
⚡ Performance
[Medium] Commits surface still loads full activity feed
File:
apps/desktop/src/renderer/components/history/HistoryPage.tsx:L168-L171,apps/desktop/src/renderer/components/history/useTimelineStore.ts:L374-L388
Issue:fetchEventsruns whenever History is active, including onsurface === "commits"(silent mode). That still pulls up to 500 operations plus supplemental chats/missions/CTO/worker runs on every tab visit and lane/surface change, even though the Commits UI does not render them.
Impact: Several IPC round-trips and JSON enrichment on each History open while the user only wantedgit log; scales with project activity volume.
Fix: SkipfetchEvents(andfetchSupplementalTimelineRecords) whensurface === "commits", or gate supplemental fetch behind the Activity surface only.[Medium] Copy patch fans out unbounded per-file diff IPC
File:
apps/desktop/src/renderer/components/history/historyGitActions.ts:L114-L126
Issue:copyCommitPatchloads every changed file for the commit and issues onegetFilePatchper path in parallel viaPromise.allSettled, with no cap on file count.
Impact: Large commits (hundreds/thousands of files) can spawn hundreds of concurrent IPC/git processes and freeze the renderer for seconds.
Fix: Cap files (e.g. first 50), batch sequentially, or stream a singlegit showpatch from main instead of N per-file calls.
🎨 UI/UX
[Low] Icon-only refresh control lacks accessible name
File:
apps/desktop/src/renderer/components/history/CommitHistoryView.tsx:L191-L202
Issue: The refresh control is a 28×28px icon button withtitleonly; screen readers get no accessible name and the control fails the icon-button guidance in the review rubric.
Fix: Addaria-label="Refresh commits"(keeptitleif desired).[Low] Activity view-mode toggles lack accessible names
File:
apps/desktop/src/renderer/components/history/TimelineToolbar.tsx:L237-L251
Issue: Graph/List/Compact switches are icon-only buttons sized 28×28px withtitletooltips but noaria-label, so assistive tech users cannot discern the active view mode.
Fix: Addaria-labelper mode (e.g.aria-label="Graph view") andaria-pressed={viewMode === mode}.[Low] Commits lane
<select>has no associated labelFile:
apps/desktop/src/renderer/components/history/TimelineToolbar.tsx:L211-L221
Issue: The lane picker is a bare<select>on the Commits toolbar row without<label htmlFor=…>oraria-label, so it is unnamed in accessibility trees.
Fix: Add a visually hidden<label htmlFor="history-lane-select">Lane</label>and matchingid, or setaria-label="Lane"on the select.
Notes
- Undo/redo head changes are guarded by recorded
preHeadSha/postHeadShachecks beforegit reset --hard, which limits accidental rewinds when local HEAD diverged.- Commit graph virtualization and lane read caching (
readLaneCached) are sensible performance choices.- New sync RPC entries for undo/redo follow the existing
viewerAllowed: truegit-command pattern used elsewhere insyncRemoteCommandService.ts; worth a separate hardening pass if mobile viewers should be read-only for destructive git ops.Sent by Cursor Automation: BUGBOT in Versic
There was a problem hiding this comment.
Actionable comments posted: 12
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/desktop/src/renderer/components/history/TimelineListView.tsx (1)
140-145:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd an explicit accessible name to timeline row buttons.
With column toggles, a row can become effectively unlabeled (or label-hidden on smaller breakpoints) for screen readers. Add a stable
aria-labelso selection remains accessible regardless of visible columns.Suggested fix
<button key={ev.id} type="button" data-tour="history.entry" onClick={() => onSelectEvent(ev.id)} + aria-label={`${ev.label} (${ev.kind})`} className={cn(Also applies to: 174-191, 226-230
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/desktop/src/renderer/components/history/TimelineListView.tsx` around lines 140 - 145, The timeline row buttons (the button with key={ev.id} that calls onSelectEvent(ev.id) and the similar buttons around the other entries) lack an explicit accessible name; add an aria-label to each row button so screen readers always have a stable name (e.g., aria-label derived from the event title with a fallback to the id such as "Select timeline event {ev.title || ev.id}"). Update every similar button instance in TimelineListView.tsx (the button using onSelectEvent(ev.id) and the other row buttons referenced in the review) to include this aria-label.
🧹 Nitpick comments (5)
apps/desktop/src/renderer/components/history/commitGraphLayout.test.ts (1)
38-45: ⚡ Quick winAdd a regression assertion for branch lane separation.
This test only checks edge count. It should also assert that merge parents are in distinct columns, otherwise a collapsed graph can still pass.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/desktop/src/renderer/components/history/commitGraphLayout.test.ts` around lines 38 - 45, The test currently only checks edge count for the merge commit but must also assert that the merge parents occupy distinct lanes/columns to prevent collapsed layouts; after calling buildCommitGraphLayout([merge, main, feature]) inspect layout.nodes to locate the nodes for "main" and "feature" (by their sha) and add an assertion that their column/lane property (e.g., node.column or node.lane) are not equal, ensuring the parents are placed in separate columns; keep the existing mergeEdges check and add this new inequality assertion referencing commit("m"), buildCommitGraphLayout, layout.edges, and layout.nodes.apps/desktop/src/renderer/components/history/historyLaneActions.ts (1)
50-95: ⚡ Quick winRename
LANE_ACTION_GROUPSto camelCase.This constant is in changed TypeScript code, and the repo rule for TS/JS files asks for camelCase variable names.
As per coding guidelines,
**/*.{ts,tsx,js,jsx}: Use camelCase for variable names in TypeScript/JavaScript files.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/desktop/src/renderer/components/history/historyLaneActions.ts` around lines 50 - 95, Rename the constant LANE_ACTION_GROUPS to camelCase (e.g., laneActionGroups) and update all references in this file to use the new identifier; ensure the exported name (if any) and any imports/uses (components, functions, or tests that reference LANE_ACTION_GROUPS) are updated too so there are no unresolved identifiers, and keep the value and type annotation unchanged (the array shape and HistoryLaneActionId references stay the same).apps/desktop/src/renderer/components/history/historyGitActions.ts (1)
36-61: ⚡ Quick winRename
COMMIT_ACTION_GROUPSto camelCase.This constant is in changed TypeScript code, and the repo rule for TS/JS files asks for camelCase variable names.
As per coding guidelines,
**/*.{ts,tsx,js,jsx}: Use camelCase for variable names in TypeScript/JavaScript files.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/desktop/src/renderer/components/history/historyGitActions.ts` around lines 36 - 61, Rename the top-level constant COMMIT_ACTION_GROUPS to camelCase (commitActionGroups) and update every reference to it across the codebase (including imports/exports and usages in historyGitActions.ts and any consumers) so identifiers match; preserve the const declaration shape and its type annotation Array<{ id: string; label: string; actionIds: HistoryGitActionId[] }>, and run/typecheck to catch missed references (search for COMMIT_ACTION_GROUPS and replace with commitActionGroups, and update any named exports/imports accordingly).apps/desktop/src/renderer/components/automations/adeActionSchemas.ts (1)
344-354: 💤 Low valueConsider using
enumtype for mode parameters.Both
resetToCommit.modeandpull.modeusetype: "string"with descriptions listing valid values. For consistency with other enum-like parameters in this file (e.g.,conflicts.rebaseLane.provider,pr.submitReview.event) and to enable better form validation in the automation rule editor, consider usingtype: "enum"withenumValues.♻️ Suggested improvement
{ domain: "git", action: "resetToCommit", label: "Reset to commit", description: "Move the lane branch to a commit using soft, mixed, or hard reset.", params: [ LANE_ID_PARAM, COMMIT_SHA_PARAM, - { name: "mode", type: "string", required: true, description: "Reset mode: soft, mixed, or hard." }, + { name: "mode", type: "enum", required: true, enumValues: ["soft", "mixed", "hard"], description: "Reset mode." }, ], },{ domain: "git", action: "pull", label: "Pull from remote", description: "Pull the lane's branch from its upstream tracking branch.", params: [ LANE_ID_PARAM, - { name: "mode", type: "string", description: "Pull mode: ff-only, rebase, or merge." }, + { name: "mode", type: "enum", enumValues: ["ff-only", "rebase", "merge"], description: "Pull mode." }, ], },Also applies to: 367-371
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/desktop/src/renderer/components/automations/adeActionSchemas.ts` around lines 344 - 354, The mode params for the git actions should be changed from type: "string" to type: "enum" and supply an enumValues array matching the documented valid options; specifically update the param object in the git action with domain:"git", action:"resetToCommit" (the "mode" param alongside LANE_ID_PARAM and COMMIT_SHA_PARAM) to type: "enum" and add enumValues: ["soft","mixed","hard"], and likewise update the git action with domain:"git", action:"pull" to use type: "enum" with enumValues that match its description (e.g., ["rebase","merge","ff-only"] or the exact valid values used elsewhere in the codebase) so the automation editor can validate choices.apps/desktop/src/renderer/components/history/HistoryPage.tsx (1)
310-314: ⚡ Quick winMemoize
laneDatato prevent unnecessary re-renders.This creates a new array with new objects on every render. Since
laneDatais passed toTimelineGraph, it will cause the child component to re-render even when the underlyinglanesdata hasn't changed.♻️ Suggested fix
- const laneData = lanes.map((l) => ({ - id: l.id, - name: l.name, - color: l.color ?? null, - })); + const laneData = useMemo( + () => + lanes.map((l) => ({ + id: l.id, + name: l.name, + color: l.color ?? null, + })), + [lanes], + );🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/desktop/src/renderer/components/history/HistoryPage.tsx` around lines 310 - 314, The laneData array is recreated on every render causing TimelineGraph to re-render; wrap the laneData computation in React.useMemo inside the HistoryPage component so it only rebuilds when the lanes input changes (useMemo(() => lanes.map(...), [lanes])), keeping the same id/name/color shape and using lanes as the dependency to prevent unnecessary child re-renders.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/ade-cli/src/cli.ts`:
- Around line 5147-5149: The history show flow calls
actionStep("result","operation","list",{limit: readIntOption(args, ["--limit"],
1000)}) and only scans the most recent 1000 operations so older operation IDs
can return “not found”; change this to perform a proper id-based lookup or a
paginated search: either call a dedicated lookup endpoint if available (e.g.,
operation.getById or similar) or iterate operation.list with a reasonable page
size using the returned cursor/next token until the requested id is found or the
pages are exhausted, and remove the hard-coded 1000 window (also surface a
user-facing error if exhaustive search fails). Ensure you update the code paths
in the history show logic that resolve IDs so they use this paginated/search
approach instead of the fixed limit.
- Around line 13395-13425: The history status filter is being applied after the
fetch/limit step so `history list/export` uses an already-capped window (rows)
and drops matching items outside that window; change the logic so the status
predicate participates in pagination: either pass plan.historyStatusFilter down
into the server query that produces the raw rows (so the server applies the
status before limiting), or if server-side change isn’t possible, apply the
status filter immediately after asOperationRows(raw) and before any
limit/pagination, i.e. call filterHistoryOperations(asOperationRows(raw),
plan.historyStatusFilter) earlier (or re-run pagination on the filtered array)
so variables like rows, rowCount and the exported rows reflect the
status-filtered set for both "history list" and "history export".
- Around line 2884-2895: The code currently allows mixing --mode with flag forms
and silently prefers flags (see explicitMode, flagModes, rawMode, mode), so add
a validation that if explicitMode is set (non-empty) and flagModes.length > 0
you throw a CliUsageError refusing mixed inputs; keep the existing single-flag
check for flagModes and the ff_only -> ff-only mapping, but perform the new
explicitMode+flagModes conflict check before deriving rawMode/mode and before
normalizing/validating the final mode.
In `@apps/desktop/src/main/services/sync/syncRemoteCommandService.test.ts`:
- Around line 1219-1227: Add the missing negative assertion for redo lane
validation: after the existing undo rejection check, call
service.execute(makePayload("git.redoLastHeadChange", {})) and assert it rejects
with the same error message ("git.redoLastHeadChange requires laneId."); this
mirrors the undo test and ensures git.redoLastHeadChange's laneId validation is
covered for service.execute and makePayload interactions with
git.redoLastHeadChange.
In `@apps/desktop/src/renderer/components/history/CommitDetailPanel.tsx`:
- Around line 100-127: The effect that fetches commit data (useEffect watching
resolvedCommit/laneId) can flash the previous commit's UI; to fix, immediately
clear commit-scoped state at the start of that effect by calling
setFullMessage(null), setFiles([]) and setLoadingFiles(true) (or appropriate
initial values) before invoking window.ade.git.getCommitMessage and
window.ade.git.listCommitFiles, keep the existing cancelled flag and cleanup
logic around the async .then/.catch/.finally handlers on getCommitMessage and
listCommitFiles to avoid updates after unmount or new selection; ensure you
still setLoadingFiles(false) in the finally only when not cancelled.
In `@apps/desktop/src/renderer/components/history/commitGraphLayout.ts`:
- Around line 76-79: Single-parent commits currently always inherit their
parent's column (shaToCol/parentCol), which collapses forks; change the logic in
commitGraphLayout.ts so that when parents.length === 1 you check whether that
parent has multiple children (i.e., out-degree > 1) and only reuse parentCol
when the parent has a single child; if the parent is forked, call allocCol() to
allocate a new lane for this sibling instead. Locate the block using shaToCol,
parents, parentCol and ensure you use the existing commit graph/children map (or
build a temporary children count) to detect multi-child parents before deciding
between parentCol and allocCol.
In `@apps/desktop/src/renderer/components/history/CommitHistoryView.tsx`:
- Around line 111-114: The current search only filters the locally loaded
commits array (commits) via filterCommitsForSearch, so when filtered becomes
empty the UI shows EmptyState and stops pagination, making matches beyond the
current page undiscoverable; fix by making the search operate against the full
bounded history (or trigger loading more pages while search is active) before
deciding there are no matches — update the CommitHistoryView logic around the
useMemo that computes filtered (and the EmptyState check) to either (a) call a
variant like filterCommitsForSearchOnFullHistory that uses the complete
backend/boundedHistory source instead of just commits, or (b) kick off loading
additional pages when search is non-empty and filtered.length === 0, then only
show EmptyState after the full-bounded-history search completes; ensure you
reference and update the same symbols (filterCommitsForSearch, commits,
refsBySha, search, filtered, EmptyState) so pagination is not aborted
prematurely.
- Around line 167-176: The current early return when error is truthy in
CommitHistoryView hides the header/toolbar (removing the refresh affordance);
instead of returning early, keep rendering the normal layout (header/toolbar)
and render the error banner inline inside that layout (e.g., place the existing
error JSX block below the header/toolbar and above the commit list) so the
toolbar remains visible and users can retry; modify the conditional from an
early return to a conditional insertion of the banner using the existing error
variable inside the CommitHistoryView render.
In `@apps/desktop/src/renderer/components/history/historyActivitySources.ts`:
- Around line 316-326: The current checks dereference window.ade before optional
chaining and can throw when window.ade is undefined; update each capability
check (e.g., window.ade.agentChat?.list, window.ade.missions?.list,
window.ade.cto?.getState, window.ade.cto?.listAgentRuns) to first guard the
parent object (either use window.ade && typeof window.ade.agentChat?.list ===
"function" or typeof window.ade?.agentChat?.list === "function") so you never
access properties on undefined, then call settle(...) or Promise.resolve(null)
as before, preserving the safeLimit usage for the missions/cto calls.
In `@apps/desktop/src/renderer/components/history/historyLaneActions.ts`:
- Around line 607-625: The four IPC git calls in the dispatcher call the bridge
with a raw laneId string; update the calls to pass an object payload { laneId }
instead: change window.ade.git.rebaseContinue(laneId), rebaseAbort(laneId),
mergeContinue(laneId), and mergeAbort(laneId) to use
window.ade.git.rebaseContinue({ laneId }), window.ade.git.rebaseAbort({ laneId
}), window.ade.git.mergeContinue({ laneId }), and window.ade.git.mergeAbort({
laneId }) respectively so they match the rest of the file's IPC contract.
In `@apps/desktop/src/renderer/components/history/TimelineToolbar.tsx`:
- Around line 528-549: The run callback currently awaits runHistoryLaneAction
and then calls setRunningAction(null) only on success; wrap the await in a
try/catch/finally inside the run function (the function declared as run =
useCallback(...)) so that any thrown error is caught (call setError with the
error message or a fallback) and setRunningAction(null) is always called in the
finally block; keep existing onError/onComplete/onNotice props when calling
runHistoryLaneAction and do not remove the navigate handler.
---
Outside diff comments:
In `@apps/desktop/src/renderer/components/history/TimelineListView.tsx`:
- Around line 140-145: The timeline row buttons (the button with key={ev.id}
that calls onSelectEvent(ev.id) and the similar buttons around the other
entries) lack an explicit accessible name; add an aria-label to each row button
so screen readers always have a stable name (e.g., aria-label derived from the
event title with a fallback to the id such as "Select timeline event {ev.title
|| ev.id}"). Update every similar button instance in TimelineListView.tsx (the
button using onSelectEvent(ev.id) and the other row buttons referenced in the
review) to include this aria-label.
---
Nitpick comments:
In `@apps/desktop/src/renderer/components/automations/adeActionSchemas.ts`:
- Around line 344-354: The mode params for the git actions should be changed
from type: "string" to type: "enum" and supply an enumValues array matching the
documented valid options; specifically update the param object in the git action
with domain:"git", action:"resetToCommit" (the "mode" param alongside
LANE_ID_PARAM and COMMIT_SHA_PARAM) to type: "enum" and add enumValues:
["soft","mixed","hard"], and likewise update the git action with domain:"git",
action:"pull" to use type: "enum" with enumValues that match its description
(e.g., ["rebase","merge","ff-only"] or the exact valid values used elsewhere in
the codebase) so the automation editor can validate choices.
In `@apps/desktop/src/renderer/components/history/commitGraphLayout.test.ts`:
- Around line 38-45: The test currently only checks edge count for the merge
commit but must also assert that the merge parents occupy distinct lanes/columns
to prevent collapsed layouts; after calling buildCommitGraphLayout([merge, main,
feature]) inspect layout.nodes to locate the nodes for "main" and "feature" (by
their sha) and add an assertion that their column/lane property (e.g.,
node.column or node.lane) are not equal, ensuring the parents are placed in
separate columns; keep the existing mergeEdges check and add this new inequality
assertion referencing commit("m"), buildCommitGraphLayout, layout.edges, and
layout.nodes.
In `@apps/desktop/src/renderer/components/history/historyGitActions.ts`:
- Around line 36-61: Rename the top-level constant COMMIT_ACTION_GROUPS to
camelCase (commitActionGroups) and update every reference to it across the
codebase (including imports/exports and usages in historyGitActions.ts and any
consumers) so identifiers match; preserve the const declaration shape and its
type annotation Array<{ id: string; label: string; actionIds:
HistoryGitActionId[] }>, and run/typecheck to catch missed references (search
for COMMIT_ACTION_GROUPS and replace with commitActionGroups, and update any
named exports/imports accordingly).
In `@apps/desktop/src/renderer/components/history/historyLaneActions.ts`:
- Around line 50-95: Rename the constant LANE_ACTION_GROUPS to camelCase (e.g.,
laneActionGroups) and update all references in this file to use the new
identifier; ensure the exported name (if any) and any imports/uses (components,
functions, or tests that reference LANE_ACTION_GROUPS) are updated too so there
are no unresolved identifiers, and keep the value and type annotation unchanged
(the array shape and HistoryLaneActionId references stay the same).
In `@apps/desktop/src/renderer/components/history/HistoryPage.tsx`:
- Around line 310-314: The laneData array is recreated on every render causing
TimelineGraph to re-render; wrap the laneData computation in React.useMemo
inside the HistoryPage component so it only rebuilds when the lanes input
changes (useMemo(() => lanes.map(...), [lanes])), keeping the same id/name/color
shape and using lanes as the dependency to prevent unnecessary child re-renders.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 2eb41010-8289-4289-9c1a-412354d41d6d
📒 Files selected for processing (46)
apps/ade-cli/src/adeRpcServer.test.tsapps/ade-cli/src/adeRpcServer.tsapps/ade-cli/src/cli.test.tsapps/ade-cli/src/cli.tsapps/ade-cli/src/services/sync/syncRemoteCommandService.tsapps/desktop/src/main/services/adeActions/registry.tsapps/desktop/src/main/services/ai/tools/ctoOperatorTools.tsapps/desktop/src/main/services/cto/ctoStateService.tsapps/desktop/src/main/services/git/gitOperationsService.test.tsapps/desktop/src/main/services/git/gitOperationsService.tsapps/desktop/src/main/services/ipc/registerIpc.tsapps/desktop/src/main/services/lanes/laneService.test.tsapps/desktop/src/main/services/lanes/laneService.tsapps/desktop/src/main/services/sync/syncRemoteCommandService.test.tsapps/desktop/src/preload/global.d.tsapps/desktop/src/preload/preload.tsapps/desktop/src/renderer/browserMock.tsapps/desktop/src/renderer/components/automations/adeActionSchemas.tsapps/desktop/src/renderer/components/history/CommitDetailPanel.tsxapps/desktop/src/renderer/components/history/CommitHistoryView.tsxapps/desktop/src/renderer/components/history/EventDetailPanel.tsxapps/desktop/src/renderer/components/history/HistoryGitContextMenu.tsxapps/desktop/src/renderer/components/history/HistoryPage.tsxapps/desktop/src/renderer/components/history/TimelineCompactView.tsxapps/desktop/src/renderer/components/history/TimelineListView.tsxapps/desktop/src/renderer/components/history/TimelineToolbar.tsxapps/desktop/src/renderer/components/history/commitGraphLayout.test.tsapps/desktop/src/renderer/components/history/commitGraphLayout.tsapps/desktop/src/renderer/components/history/eventTaxonomy.test.tsapps/desktop/src/renderer/components/history/eventTaxonomy.tsapps/desktop/src/renderer/components/history/historyActivitySources.test.tsapps/desktop/src/renderer/components/history/historyActivitySources.tsapps/desktop/src/renderer/components/history/historyGitActions.test.tsapps/desktop/src/renderer/components/history/historyGitActions.tsapps/desktop/src/renderer/components/history/historyLaneActions.test.tsapps/desktop/src/renderer/components/history/historyLaneActions.tsapps/desktop/src/renderer/components/history/historySearch.test.tsapps/desktop/src/renderer/components/history/historySearch.tsapps/desktop/src/renderer/components/history/timelineTypes.tsapps/desktop/src/renderer/components/history/useTimelineStore.test.tsapps/desktop/src/renderer/components/history/useTimelineStore.tsapps/desktop/src/renderer/components/lanes/LanesPage.tsxapps/desktop/src/shared/ipc.tsapps/desktop/src/shared/types/git.tsapps/desktop/src/shared/types/lanes.tsapps/desktop/src/shared/types/sync.ts
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
apps/desktop/src/main/services/git/gitOperationsService.ts (1)
1298-1306:⚠️ Potential issue | 🟠 Major | ⚡ Quick winGuard undo/redo against dirty worktrees.
Both paths call
git reset --hardafter only checking HEAD, so any staged or unstaged edits on the current HEAD get discarded. Reject the action when the lane has uncommitted changes before resetting.Proposed fix
fn: async (lane) => { + if (await isWorktreeDirty(lane.worktreePath)) { + throw new Error("Cannot undo while the lane has uncommitted changes. Commit or stash them first."); + } const currentHead = await getHeadSha(lane.worktreePath); if (currentHead !== operation.postHeadSha) { throw new Error("Cannot undo because the lane head has changed since that operation."); } await runGitOrThrow(["reset", "--hard", operation.preHeadSha], { @@ fn: async (lane) => { + if (await isWorktreeDirty(lane.worktreePath)) { + throw new Error("Cannot redo while the lane has uncommitted changes. Commit or stash them first."); + } const currentHead = await getHeadSha(lane.worktreePath); if (currentHead !== operation.postHeadSha) { throw new Error("Cannot redo because the lane head has changed since the undo."); } await runGitOrThrow(["reset", "--hard", redoHeadSha], {Also applies to: 1322-1330
apps/desktop/src/renderer/components/history/HistoryPage.tsx (2)
255-298:⚠️ Potential issue | 🟠 Major | ⚡ Quick winKeep
selectedCommitShain sync with commit detail state.These handlers clear
selectedCommit, but they never clear the backingselectedCommitShaand they do not set it eagerly on commit selection. On the commits surface, the URL sync effect can write the oldcommitShastraight back, and the resolver effect can hydrate the previous commit again after close or event selection.Proposed fix
const handleSelectEvent = useCallback( (id: string) => { setSelectedEventId(id); setSelectedCommit(null); + setSelectedCommitSha(null); setSearchParams((prev) => { const next = new URLSearchParams(prev); next.set("eventId", id); next.delete("commitSha"); next.set("surface", "activity"); lastWrittenUrlRef.current = next.toString(); return next; }); }, - [setSelectedEventId, setSelectedCommit, setSearchParams], + [setSelectedEventId, setSelectedCommit, setSelectedCommitSha, setSearchParams], ); const handleSelectCommit = useCallback( (commit: GitCommitSummary) => { setSelectedCommit(commit); + setSelectedCommitSha(commit.sha); setSelectedEventId(null); setSearchParams((prev) => { const next = new URLSearchParams(prev); next.set("commitSha", commit.sha); next.delete("eventId"); next.set("surface", "commits"); if (focusLaneId) next.set("laneId", focusLaneId); lastWrittenUrlRef.current = next.toString(); return next; }); }, - [setSelectedCommit, setSelectedEventId, setSearchParams, focusLaneId], + [setSelectedCommit, setSelectedCommitSha, setSelectedEventId, setSearchParams, focusLaneId], ); const handleCloseDetail = useCallback(() => { setSelectedEventId(null); setSelectedCommit(null); + setSelectedCommitSha(null); setSearchParams((prev) => { const next = new URLSearchParams(prev); next.delete("eventId"); next.delete("commitSha"); lastWrittenUrlRef.current = next.toString(); return next; }); - }, [setSelectedEventId, setSelectedCommit, setSearchParams]); + }, [setSelectedEventId, setSelectedCommit, setSelectedCommitSha, setSearchParams]);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/desktop/src/renderer/components/history/HistoryPage.tsx` around lines 255 - 298, The handlers manipulate selectedCommit and URL but neglect the backing selectedCommitSha; update handleSelectCommit to call setSelectedCommitSha(commit.sha) when selecting a commit, and update handleSelectEvent and handleCloseDetail to call setSelectedCommitSha(null) when clearing selection; also add setSelectedCommitSha to the useCallback dependency arrays for handleSelectEvent, handleSelectCommit, and handleCloseDetail so the closures stay correct.
190-201:⚠️ Potential issue | 🟠 Major | ⚡ Quick winClear stale commit details when the SHA lookup misses.
If a deep link points to a commit outside the 500-row window, or just an invalid SHA,
foundstays undefined and the previousselectedCommitremains mounted. That leaves the detail panel and commit actions targeting the wrong commit.Proposed fix
void window.ade.git .listRecentCommits({ laneId: focusLaneId, limit: 500 }) .then((rows) => { if (cancelled) return; - const found = rows.find((r) => r.sha === selectedCommitSha); - if (found) setSelectedCommit(found); + const found = rows.find((r) => r.sha === selectedCommitSha) ?? null; + setSelectedCommit(found); }) - .catch(() => {}); + .catch(() => { + if (!cancelled) setSelectedCommit(null); + });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/desktop/src/renderer/components/history/HistoryPage.tsx` around lines 190 - 201, The effect that looks up a commit by selectedCommitSha via window.ade.git.listRecentCommits may leave a stale selectedCommit when the SHA is not found; update the handler in the useEffect (the block using active, focusLaneId, selectedCommitSha, selectedCommit, setSelectedCommit and listRecentCommits) so that when rows.find(...) returns undefined you explicitly clear the selection (e.g., setSelectedCommit(null) or undefined) instead of leaving the previous commit, and keep the cancelled check; also ensure any promise rejection is handled to avoid leaving stale state on error.apps/desktop/src/renderer/components/history/useTimelineStore.ts (1)
382-392:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftApply the lane filter before limiting supplemental history.
Line 388 caps supplemental records globally, and Line 391 filters by
opts.laneIdafterward. If supplemental history exceedslimit, valid records for the selected lane can be dropped before the lane filter runs, so the lane view becomes incomplete. Please push lane scoping intofetchSupplementalTimelineRecords(or over-fetch before truncating) so each lane gets its own top-limitslice.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/desktop/src/renderer/components/history/useTimelineStore.ts` around lines 382 - 392, The supplemental records are being globally limited before lane-scoping, which can drop valid per-lane items; update the call to fetchSupplementalTimelineRecords so lane scoping happens inside that helper (e.g., add a laneId parameter or options object: fetchSupplementalTimelineRecords(limit, opts?.laneId) or fetchSupplementalTimelineRecords({limit, laneId: opts?.laneId})), remove the later per-lane filter on scopedSupplemental, and ensure fetchSupplementalTimelineRecords returns the top `limit` records already filtered by laneId; alternatively, if you prefer not to change the helper signature, over-fetch (e.g., fetch more than `limit`) and then apply the lane filter before truncating to `limit` in this code path.
♻️ Duplicate comments (1)
apps/desktop/src/renderer/components/history/historySearch.ts (1)
57-60:⚠️ Potential issue | 🟠 Major | ⚡ Quick winSupport quoted shorthand filters in the tokenizer.
searchTokenFromRaw()now handles@,#, and=, but the regex still splits@"Jane Doe"/="fix parser"into multiple tokens. Those searches miss the keyed path and regress back to loose free-text matching.Proposed fix
function tokenizeSearch(query: string): SearchToken[] { - const matches = query.match(/[^\s"]+:"[^"]+"|"[^"]+"|\S+/g) ?? []; + const matches = query.match(/[^\s"]+:"[^"]+"|[@#=]"[^"]+"|"[^"]+"|\S+/g) ?? []; return matches .map(searchTokenFromRaw) .filter((token): token is SearchToken => token != null); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/desktop/src/renderer/components/history/historySearch.ts` around lines 57 - 60, The tokenizer regex in tokenizeSearch still splits shorthand quoted filters like @"Jane Doe" or ="fix parser" into multiple tokens; update the regex so it treats an optional shorthand symbol (@, #, =) directly attached to a quoted phrase or to a key:value pair as a single token. Specifically, change the match pattern used in tokenizeSearch to include alternatives such as [@#=]?[^\s"]+:"[^"]+" and [@#=]"[^"]+" (in addition to the existing quoted and non-space alternatives) so searchTokenFromRaw receives @"...", ="..." or key:"..." as one token.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/desktop/src/preload/preload.ts`:
- Around line 6830-6831: The handler rebaseContinue (and other IPC handlers that
extract laneId) currently assumes args is non-null and can be string or object,
which will crash when args === null; add a runtime guard that first checks args
!== null/undefined and then if typeof args === "string" use it, else if typeof
args === "object" && typeof args.laneId === "string" use args.laneId, otherwise
return a safe failure GitActionResult (or throw a controlled IPC error) so
malformed inputs don't cause runtime exceptions when extracting laneId.
---
Outside diff comments:
In `@apps/desktop/src/renderer/components/history/HistoryPage.tsx`:
- Around line 255-298: The handlers manipulate selectedCommit and URL but
neglect the backing selectedCommitSha; update handleSelectCommit to call
setSelectedCommitSha(commit.sha) when selecting a commit, and update
handleSelectEvent and handleCloseDetail to call setSelectedCommitSha(null) when
clearing selection; also add setSelectedCommitSha to the useCallback dependency
arrays for handleSelectEvent, handleSelectCommit, and handleCloseDetail so the
closures stay correct.
- Around line 190-201: The effect that looks up a commit by selectedCommitSha
via window.ade.git.listRecentCommits may leave a stale selectedCommit when the
SHA is not found; update the handler in the useEffect (the block using active,
focusLaneId, selectedCommitSha, selectedCommit, setSelectedCommit and
listRecentCommits) so that when rows.find(...) returns undefined you explicitly
clear the selection (e.g., setSelectedCommit(null) or undefined) instead of
leaving the previous commit, and keep the cancelled check; also ensure any
promise rejection is handled to avoid leaving stale state on error.
In `@apps/desktop/src/renderer/components/history/useTimelineStore.ts`:
- Around line 382-392: The supplemental records are being globally limited
before lane-scoping, which can drop valid per-lane items; update the call to
fetchSupplementalTimelineRecords so lane scoping happens inside that helper
(e.g., add a laneId parameter or options object:
fetchSupplementalTimelineRecords(limit, opts?.laneId) or
fetchSupplementalTimelineRecords({limit, laneId: opts?.laneId})), remove the
later per-lane filter on scopedSupplemental, and ensure
fetchSupplementalTimelineRecords returns the top `limit` records already
filtered by laneId; alternatively, if you prefer not to change the helper
signature, over-fetch (e.g., fetch more than `limit`) and then apply the lane
filter before truncating to `limit` in this code path.
---
Duplicate comments:
In `@apps/desktop/src/renderer/components/history/historySearch.ts`:
- Around line 57-60: The tokenizer regex in tokenizeSearch still splits
shorthand quoted filters like @"Jane Doe" or ="fix parser" into multiple tokens;
update the regex so it treats an optional shorthand symbol (@, #, =) directly
attached to a quoted phrase or to a key:value pair as a single token.
Specifically, change the match pattern used in tokenizeSearch to include
alternatives such as [@#=]?[^\s"]+:"[^"]+" and [@#=]"[^"]+" (in addition to the
existing quoted and non-space alternatives) so searchTokenFromRaw receives
@"...", ="..." or key:"..." as one token.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: bada1424-dc39-44a9-87eb-bd1da1986544
📒 Files selected for processing (30)
apps/ade-cli/src/cli.test.tsapps/ade-cli/src/cli.tsapps/desktop/src/main/services/adeActions/registry.tsapps/desktop/src/main/services/git/gitOperationsService.test.tsapps/desktop/src/main/services/git/gitOperationsService.tsapps/desktop/src/main/services/history/operationService.test.tsapps/desktop/src/main/services/history/operationService.tsapps/desktop/src/main/services/ipc/registerIpc.tsapps/desktop/src/main/services/sync/syncRemoteCommandService.test.tsapps/desktop/src/preload/global.d.tsapps/desktop/src/preload/preload.test.tsapps/desktop/src/preload/preload.tsapps/desktop/src/renderer/components/automations/adeActionSchemas.tsapps/desktop/src/renderer/components/history/CommitDetailPanel.tsxapps/desktop/src/renderer/components/history/CommitHistoryView.tsxapps/desktop/src/renderer/components/history/HistoryPage.tsxapps/desktop/src/renderer/components/history/TimelineListView.tsxapps/desktop/src/renderer/components/history/TimelineToolbar.tsxapps/desktop/src/renderer/components/history/commitGraphLayout.test.tsapps/desktop/src/renderer/components/history/commitGraphLayout.tsapps/desktop/src/renderer/components/history/historyActivitySources.tsapps/desktop/src/renderer/components/history/historyGitActions.test.tsapps/desktop/src/renderer/components/history/historyGitActions.tsapps/desktop/src/renderer/components/history/historyLaneActions.test.tsapps/desktop/src/renderer/components/history/historyLaneActions.tsapps/desktop/src/renderer/components/history/historySearch.test.tsapps/desktop/src/renderer/components/history/historySearch.tsapps/desktop/src/renderer/components/history/useTimelineStore.test.tsapps/desktop/src/renderer/components/history/useTimelineStore.tsapps/desktop/src/shared/types/git.ts
Add dual-surface History: Commits (DAG graph + virtualized list) and Activity (existing operations timeline). Commit view includes branch ref labels, search, right-click git actions, and a detail panel with file list and related ADE operations. Activity detail links to commits, PRs, missions, and work. Raise listRecentCommits cap to 500 for deeper history. Co-authored-by: Arul Sharma <arul28@users.noreply.github.com>
Hydrate commit selection from URL, clear selection on lane change, fetch operations on commits surface, virtualize SVG edges/nodes, fix HEAD badge and bottom-scroll load-more, isolate git action errors, sync surface/lane to URL, and confirm destructive git ops. Co-authored-by: Arul Sharma <arul28@users.noreply.github.com>
- TimelineToolbar: Export button calling history.exportOperations with headless fallback notice; gear kept for column settings - EventDetailPanel: show missionId and sessionId from parsed metadata - CommitHistoryView: refresh control and author on commit rows - useTimelineStore: default listOperations limit 500 - HistoryPage: lane header strip on commits surface; consolidate URL hydration and remove duplicate activity refresh listeners Co-authored-by: Arul Sharma <arul28@users.noreply.github.com>
Implement list, show, commits, and headless export subcommands wired to operation.list and git.listRecentCommits with text/json formatters and CLI plan tests. Co-authored-by: Arul Sharma <arul28@users.noreply.github.com>
Phase 2 simplifier polish across 14 files (history feature, git/lanes, glue/IPC) plus /automate test consolidation: drop single-test eventTaxonomy file into historyActivitySources, extend operationService contract tests, refresh docs/features/history + lanes + sync remote- commands + PRD for new head-change undo/redo, tag/reset/pull-mode surface. Tests were cancelled mid-run on a clean tree; CI 8-way shard verifies. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9a496cc to
2bdfdf4
Compare
|
@copilot review but do not make fixes |
|
@codex review |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Review completed (no fixes applied). I still see these issues in the current branch: (1) |
There was a problem hiding this comment.
PR Review
Scope: 59 file(s), +7004 / −360
Verdict: Minor issues
This PR overhauls the History tab with a GitKraken-style commit graph, unified activity feed (operations + chats/missions/CTO/worker runs), and extensive git/lane actions wired through IPC and remote commands. The main risks are deep-link/commit-window limits and a few UX/perf edges on large histories—not trust-boundary regressions.
🐛 Functionality
[Medium] Deep-linked commits outside the 500-commit window never hydrate
File: apps/desktop/src/renderer/components/history/HistoryPage.tsx:L190-L206
Issue: When commitSha is present in the URL (or store) but that commit is not in the newest 500 rows returned by listRecentCommits, the code only scans that capped list and silently leaves selectedCommit null.
Repro: Open /history?surface=commits&laneId=<lane>&commitSha=<sha> for a commit older than the 500th entry (or after loading only 120 until scroll). The detail pane stays empty while selectedCommitSha remains set.
Fix: Resolve by SHA with a targeted git call (e.g. git rev-parse --verify + one-off log -1 <sha> / existing commit lookup IPC) instead of scanning listRecentCommits; mirror the same pattern in CommitDetailPanel.tsx:L65-L79.
[Medium] Stale laneId in the URL is not reconciled after lane deletion
File: apps/desktop/src/renderer/components/history/HistoryPage.tsx:L111-L122
Issue: URL hydration applies laneId only when it still exists in lanes. If the param references a deleted/unknown lane, neither branch runs, so focusLaneId and the URL can stay out of sync with the lane picker (empty selection + broken git calls).
Repro: Bookmark a commits URL with laneId for a lane you later delete; reload History. The toolbar shows “Select lane…” while the query string still carries the dead id.
Fix: Add an else branch for laneFromUrl && !lanes.some(...) that clears laneId/commitSha from the URL and applies the same fallback used when laneId is absent.
⚡ Performance
[Medium] “Copy patch” fans out up to 50 concurrent file-patch IPC calls
File: apps/desktop/src/renderer/components/history/historyGitActions.ts:L106-L118
Issue: copyCommitPatch loads up to 50 changed files and issues Promise.allSettled over diff.getFilePatch for each path in one user gesture.
Impact: Large commits can spike main-process git/diff work and freeze the renderer briefly on a laptop.
Fix: Batch patches sequentially or with a small concurrency cap (e.g. 4–8), or add a single git.show/aggregate patch IPC for whole-commit export.
[Medium] Activity load merges full operations list plus four supplemental sources every fetch
File: apps/desktop/src/renderer/components/history/useTimelineStore.ts:L381-L396, apps/desktop/src/renderer/components/history/historyActivitySources.ts:L315-L328
Issue: Each fetchEvents pulls up to 500 history.listOperations rows and, in parallel, agent chats (up to 500), missions, CTO snapshot, and worker runs, then sorts/dedupes in the renderer. The 4s poll while any event is running repeats this work.
Impact: Noticeable delay switching to Activity on busy projects; extra CPU/IPC during long-running operations.
Fix: Cache supplemental records with a TTL, pass laneId into supplemental fetchers when scoped, or raise the poll interval unless the visible surface is Activity.
🎨 UI/UX
[Low] Commit history refetch hides in-progress loading when rows already exist
File: apps/desktop/src/renderer/components/history/CommitHistoryView.tsx:L140-L150, L162-L169
Issue: loading is only surfaced via EmptyState when commits.length === 0. Scroll/search bumps limit (120→500) and triggers load() without a visible busy state, so the graph can look frozen during refetch.
Fix: Show a slim top progress indicator or dim the list whenever loading && commits.length > 0.
[Low] Export control is icon-only without an accessible name
File: apps/desktop/src/renderer/components/history/TimelineToolbar.tsx:L313-L326
Issue: The Export button relies on title text only; screen readers get no aria-label, unlike the adjacent refresh control in the commit search bar.
Fix: Add aria-label="Export activity history" (and keep title for tooltip parity).
Notes
Positive patterns worth keeping: request sequencing in CommitHistoryView (loadRequestSeq), virtualized rows + edge culling in the SVG graph, destructive git/lane actions gated with confirm/prompt text checks, and server-side head guards on undo/redo (gitOperationsService.ts:L1292-L1318). No new package.json dependencies in this diff. I could not pull the GitHub PR body via gh (401); review is based on origin/main...origin/cursor/history-tab-gitcraken-overhaul-9c6f at 9a496cc0.
Sent by Cursor Automation: BUGBOT in Versic
- ensureGitTagName: reject whitespace in tag names - create_branch/create_lane: pre-validate branch names against git ref rules - confirmOrCancel: default to false when window.confirm is unavailable - HistoryPage: strip unknown surface URL params, reconcile stale laneId, and hydrate deep-linked commits outside the 500-row window via new git.getCommit IPC; mirror commit-fallback in CommitDetailPanel - copyCommitPatch: cap concurrent file-patch IPC fanout to 6 - useTimelineStore: tighten polling — skip supplemental sources on in-flight polls, refresh fully on focus - CommitHistoryView: show slim top loading stripe during refetch - TimelineToolbar: add aria-label and prompt for custom export limit Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
@copilot review but do not make fixes |
|
@codex review |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |


Summary
History tab overhaul plus CLI parity so agents and terminals can read the same data as the History pane.
Desktop (History tab)
surface,laneId,commitSha,eventId)CLI (
ade history)ade history list— operations timeline (same store as the pane)ade history show --id <op>— single operationade history commits --lane <lane>— recent commits with parentsade history export— JSON to stdout or--out(headless)Demo (Linux cloud VM)
history-tab-demo.mp4
Recorded on the cloud VM: Vite renderer at
localhost:5173/#/history(browser mock + live CLI). Full Electron shell is limited on this VM (no stable CDP); local macOS dev remains the reference for full desktop parity.Example CLI
Follow-ups
To show artifacts inline, enable in settings.
Summary by CodeRabbit
Greptile Summary
This PR adds a GitKraken-style commit graph to the History tab, unifies activity and commit surfaces under a single timeline store, and adds
ade historyCLI subcommands for querying the operation timeline and recent commits.commitGraphLayout.ts), branch ref labels, search, context-menu actions (create tag, reset, cherry-pick, undo/redo HEAD), and a commit detail panel with related ADE operations.ade history list/show/commits/exportrouted through the action registry; newgit pull --mode,git undo,git redoCLI flags;operation.getdirect-lookup added.Confidence Score: 4/5
The new commit graph, CLI commands, and git action set are largely well-constructed, but several behavioural gaps from earlier review rounds remain open in the changed code.
The undo/redo HEAD-change chain, supplemental-record merging, and direct operation.get lookup are solid additions. Several concerns from prior threads remain open in the diff. Newly identified issues in historyGitActions.ts and HistoryPage.tsx erode correctness at the feature boundary but do not crash the app.
apps/desktop/src/renderer/components/history/historyGitActions.ts (create_lane baseBranch and compare_parent/view_files navigation), apps/desktop/src/renderer/components/history/HistoryPage.tsx (refreshToken coupling)
Important Files Changed
Sequence Diagram
sequenceDiagram participant UI as HistoryPage (renderer) participant Store as useTimelineStore participant IPC as preload / IPC participant Git as gitOperationsService participant Op as operationService UI->>Store: "fetchEvents({ skipSupplemental })" Store->>IPC: "history.listOperations({ laneId, status, limit })" IPC->>Op: operationService.list(args) Op-->>IPC: OperationRecord[] IPC-->>Store: raw[] Store->>Store: merge supplemental + sort Store-->>UI: rawEvents / events UI->>IPC: "git.listRecentCommits({ laneId, limit })" IPC->>Git: gitService.listRecentCommits(args) Git-->>IPC: GitCommitSummary[] IPC-->>UI: commits (CommitHistoryView) UI->>IPC: "git.undoLastHeadChange({ laneId })" IPC->>Git: undoLastHeadChange(args) Git->>Op: "listHeadChanges({ laneId, limit:100 })" Op-->>Git: HeadChangeOperationRecord[] Git->>Git: "verify currentHead === operation.postHeadSha" Git->>Git: git reset --hard preHeadSha Git-->>IPC: GitActionResult IPC-->>UI: resultPrompt To Fix All With AI
Reviews (7): Last reviewed commit: "ship: address Copilot and Cursor review ..." | Re-trigger Greptile