* fix: prevent agentctl OOM from unbounded diff generation in workspace tracker
Add per-file size cap (10MB), binary detection (null-byte in 8KB),
diff output cap (256KB), and total budget (2MB) to workspace tracker
diff enrichment. Strip diff content from event bus path. Surface skip
reasons in frontend. Add pprof endpoints to agentctl.
* fix: address PR review feedback
Close stdout pipe on cmd.Start failure to prevent fd leak.
Limit untracked file read to maxDiffOutputSize (256KB) instead of
maxDiffFileSize (10MB) to reduce peak allocation from ~20MB to ~512KB.
* fix: add filepath.Clean to os.Stat call to satisfy CodeQL path-injection check
* fix: address second round of PR review feedback
Extract all DiffSkipReason strings as named constants. Add diff_skip_reason
to UncommittedFile type removing unsafe cast. Add unit tests for
isBinaryContent, totalDiffBytes, capDiffOutput truncation, and all
enrichUntrackedFileDiffs skip-reason paths.
* fix: use bytes.IndexByte for null-byte check clarity
* fix: show truncation banner when diff has partial content
* fix: revert diff stripping from event bus — frontend needs diffs from this path
E2E tests confirmed the frontend receives git status diffs through the
event bus (agentctl → backend WS → event bus → gateway → frontend).
Stripping diffs broke the changes panel. The per-file size caps in
workspace_git_diff.go already bound the data.
* fix: extract renderDiffContent to keep FileDiffSection under 100-line lint limit
* fix: correct onCommentRun type in renderDiffContent helper
* fix: format long destructuring line for prettier
* fix: move diffSkipReasonLabel to types.ts to keep review-diff-list under 600 lines
Co-authored-by: José Almeida <undefined@users.noreply.github.com>
* fix: coerce optional forceLoad to boolean for renderDiffContent
* fix: shorten className and comment to stay within prettier printWidth
* fix: apply prettier formatting to review-diff-list
* fix: correct off-by-one in untracked file line count
---------
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: José Almeida <undefined@users.noreply.github.com>
* fix: use dynamic merge-base for git commits to filter main branch commits
When a feature branch is rebased on main, the stored base_commit_sha
becomes outdated. Using git log base_commit..HEAD would include main
branch commits that were merged after the stored base commit.
This fix adds dynamic merge-base calculation using the session's
base_branch (target branch like origin/main). When fetching commits:
- The backend computes git merge-base HEAD target_branch
- This returns only commits not yet merged into the target branch
- Works correctly even after rebases
Changes:
- Extended SessionReader interface with GetSessionBaseBranch method
- Added GetMergeBase to GitOperator for computing merge-base
- Updated git log API to accept target_branch parameter
- Modified wsGitCommits handler to pass target branch for filtering
* fix: update executor_test mock to match new GetGitLog signature
* fix: update remaining mocks to match new GetGitLog signature
* fix: update integration test mock for GetGitLog signature
* fix: address code review feedback
- Use GetOrEnsureExecution in wsGitCommits to recover workspaces after backend restarts
- Fix archive snapshot to use baseBranch for merge-base when baseCommit is empty
- Extended ExecutionLookup interface with GetOrEnsureExecution method
* fix: distinguish not-ready errors from unexpected errors in wsGitCommits
Address CodeRabbit review feedback:
- Use errors.Is() to detect ErrSessionWorkspaceNotReady sentinel error
- Return ready:false gracefully for workspace-not-ready and session-not-ready errors
- Return actual errors for unexpected failures (database errors, etc.)
- Add comprehensive tests for error handling paths
* fix: include uncommitted changes in review dialog cumulative diff
The review dialog only showed uncommitted changes instead of the full
diff from the task's base commit. Two issues combined:
1. Backend used `git diff base..HEAD` (committed only); changed to
`git diff base` which compares against the working tree.
2. Frontend gave priority to git status over cumulative diff; reversed
the merge order so cumulative diff takes precedence.
Adds E2E test with a mock-agent scenario that commits changes then
leaves uncommitted modifications, verifying the review dialog shows
the complete base→working-tree diff.
* fix(e2e): remove incorrect BASE_CONTENT assertion in review cumulative diff test
The file is created after the base commit, so the entire file shows as
added in the cumulative diff. BASE_CONTENT was replaced and doesn't
appear. The key assertion is that COMMITTED_CHANGE is visible — proving
the diff includes committed changes (not just the uncommitted portion).
* fix: address CodeRabbit review feedback
- Update 3 docstrings to reflect working-tree diff (not HEAD)
- Generate scenario list dynamically in error message
- Use data-testid selector in E2E test for resilience
* style: format E2E test file
* fix(e2e): use toContainText and click file to ensure diff is scrollable
Pierre diff viewer content inside a scroll container may not be
considered "visible" by Playwright. Switch to toContainText which
checks text presence without element-level visibility. Also click
the file in the tree to ensure its diff section is scrolled into view.
* feat(backend): add support for fetching file content at git ref
Add GetFileContentAtRef method to WorkspaceTracker for retrieving file content
at specific git refs (branches, commits, HEAD). This enables diff expansion by
fetching old file versions without checking out.
Changes:
- Extract maxFileSize constant to package level (10MB)
- Add GetFileContentAtRef to workspace_files.go using git show
- Add workspace.file.get_at_ref WebSocket action and handler
- Add agentctl client method for the new endpoint
- Register handler in workspace file handlers
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(web): add diff expansion support for code review
Add lazy-loaded diff expansion to show more context in code reviews. When expansion
is enabled, users can expand hunks to see more surrounding code by clicking expand
buttons. Full file content is fetched on-demand via WebSocket.
Changes:
- Add useExpandableDiff hook for lazy-loading old/new file content
- Add requestFileContentAtRef to workspace-files.ts
- Integrate expansion into diff viewer components
- Add enableExpansion and baseRef props to DiffViewer
- Auto-load expansion content when enabled
- Pass expansion state through diff viewer hierarchy
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(web): gracefully handle expansion props for Monaco diff viewer
Monaco diff viewer has built-in expansion that doesn't require fetching
old file content. Strip pierre-diffs-only expansion props (enableExpansion,
baseRef) when Monaco is selected to avoid passing unsupported props.
This ensures the feature works correctly regardless of which diff viewer
the user selects in settings.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(backend): add diff expansion e2e scenario and fix unstaged diff base ref
- Add scenarioDiffExpansionSetup mock-agent scenario that creates a
200-line file, commits it, then modifies lines 50 and 150 to produce
a two-hunk uncommitted diff for testing diff expansion in the UI
- Add workspace_file_handlers for workspace.file.get_at_ref WS action
- Fix enrichWithDiffData to always diff against HEAD for unstaged/staged
content so locally committed files show correct diffs
- Add unit tests for GetFileContentAtRef (6 sub-tests)
* feat(web): add diff expansion UI and e2e test
- Wire useExpandableDiff hook into FileDiffViewer and ReviewDiffList
with enableExpansion and baseRef props
- Gracefully handle expansion props for Monaco diff viewer fallback
- Add e2e Playwright test for diff expansion between hunks
* fix(web): fix diff expansion by correcting hunk separators and line splitting
Three bugs prevented @pierre/diffs expand buttons from rendering:
- hunkSeparators was "simple" (no expand UI); changed to "line-info" when expansion enabled
- expandUnchanged:true showed all lines expanded (no separators); removed the option
- .split("\n") stripped trailing newlines; now uses SPLIT_WITH_NEWLINES regex to match library
* feat(web): add editor provider settings UI
Add an "Editor Providers" section to the Editors settings page with
Select dropdowns for each configurable context (Code Editor, Diff Viewer,
Chat Code Blocks, Chat Diff Blocks). Reads from and writes to the
existing localStorage-backed editor resolver store. Changes apply
immediately without requiring a save action.
* test(web): add e2e tests for editor provider settings
Verify that the editor provider dropdowns render with correct defaults,
update on selection, and persist changes across page reloads via
localStorage.
* feat(web): add expand/collapse unchanged regions toggle to diff toolbar
Refactor diff header toolbar into composable ToolbarButtons component and
add a toggle button to expand or collapse unchanged regions in both
Pierre Diffs and Monaco diff viewers. Pass enableExpansion through to
Monaco to control hideUnchangedRegions.
* fix(web): handle deleted files in diff expansion
Treat "file not found" / "no such file" errors as empty content instead
of throwing, so diff expansion works correctly for deleted files.
* chore(web): add diff expansion debug e2e helpers
* test(web): split diff expansion tests by provider (Pierre Diffs + Monaco)
Restructure diff-expansion e2e tests into two describe blocks, one per
diff viewer provider. Each block sets the provider via localStorage
before navigation and verifies the correct viewer renders:
- Pierre Diffs: checks for diffs-container element and shadow DOM
expand buttons between hunks
- Monaco: checks for .monaco-diff-viewer wrapper and the built-in
hideUnchangedRegions widget with "Show Unchanged Region" link
Both suites verify that clicking expand reveals collapsed middle lines.
* fix(web): fix Pierre Diffs expand button selectors in e2e tests
Use shadow DOM data attributes ([data-expand-button], [data-expand-up])
instead of XPath parent traversal which doesn't work inside shadow DOM.
Wait for expand buttons to load asynchronously before clicking.
* fix(e2e): make diff-expansion-setup scenario idempotent
The mock-agent scenario now removes and re-commits the test file before
each run, so subsequent tests in the same repo don't fail with "git
commit failed" when the file already exists. Also increase test timeouts
to match other heavy e2e tests.
* refactor(web): remove editor provider settings UI, enable expand-all in review
Remove the editor provider switching UI and its tests — Monaco's diff
expansion doesn't work and exposing provider switching adds confusion.
Show Pierre Diffs header toolbar in the review panel (remove hideHeader)
so the expand-all button is accessible. Add e2e test for expand-all.
* style(web): fix prettier formatting and lint warnings
Run prettier on files with formatting drift. Extract ToolbarBtn helper
in diff-header-toolbar to stay within max-lines-per-function limit.
Consolidate useMonacoDiffViewerState to reduce line count.
* chore(web): remove debug scaffolding, revert Monaco changes, drop stray console.log
Revert Monaco diff-viewer-toolbar and monaco-diff-viewer to main
(expansion not functional with Monaco). Remove debug-expansion
screenshot scripts and spec. Strip console.log from commit-detail-panel.
Strip enableExpansion from Monaco path in diff-viewer-resolver.
* fix(web): use commit-relative base ref for diff expansion
Use `${commitSha}^` instead of hardcoded `HEAD~1` so expansion fetches
the correct old content when viewing non-HEAD commits.
* fix(web,backend): address CodeRabbit review feedback
- Add aria-label to icon-only toolbar buttons for screen reader access
- Guard auto-load expansion effect against error state to prevent retry
loops when WebSocket fetch fails
- Invalidate expansion cache on file/ref change with request versioning
to prevent stale content
- Preflight blob size with git cat-file -s before git show to avoid
memory spikes on large files
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>