Skip to content

Fix diff workflows and enforce clean TypeScript builds - #80

Merged
peterjthomson merged 2 commits into
masterfrom
codex/resolve-open-issues
Sep 25, 2026
Merged

peterjthomson merged 2 commits into
masterfrom
codex/resolve-open-issues

Conversation

@peterjthomson

@peterjthomson peterjthomson commented Sep 24, 2026 •

Copy link
Copy Markdown
Owner

Fix diff workflows and enforce clean TypeScript builds

Selecting one replacement beside another could reorder staged content or make discard fail. Partial line actions now preserve neighboring edits, line order, and missing final newlines. The legacy and context-based staging services share one parser and patch builder.

Diff panels now highlight changes within a line and support Command/Ctrl-F across old and new text in expanded files, with match navigation and Escape to close. Branch lists keep the current branch first and main/master next. Branch status links to the worktree containing its checkout.

Typechecking now covers app, Electron, shared, configuration, and test sources once through a single configuration. The former command silently skipped the referenced projects. The underlying errors are fixed: asynchronous IPC return types and schema mismatches, local/remote repository contexts, React Flow/tldraw API types, plugin refresh/status contracts, and unused declarations. Builds now require a clean typecheck.

Closes #68, closes #69, closes #70, closes #71, closes #72.

Validation

  • Failing local reproductions added before fixes for missing diff features, branch ordering/status, and partial line actions. The missing-newline boundary also failed before its fix.

  • Real Git tests use explicit expected file contents. UI checks verify the index and working tree after stage, unstage, discard, and selections across two hunks.

  • npm test: production build and all 73 tests pass.

  • Changed TypeScript files pass ESLint; git diff --check passes.

  • Unsigned macOS app and ZIP built successfully; npm run test:packaged passes startup, bundled module resolution, and a native SQLite query. The explicit command rejects missing or invalid executable paths before launching tests.

  • npm run typecheck: zero diagnostics, with existing strictness and unused checks retained. All source files remain covered; no suppression comments or any casts were added to silence errors.

  • A temporary intentional type error was rejected by the build hook before Vite ran, then removed; the clean production build passes.

  • Added regressions for numeric PR checkout IDs, canvas configuration records, commit detail data across asynchronous IPC, and captured repository contexts after close.

  • Manual computer-use walkthrough on a disposable repository: Radar/Focus ordering, branch-to-worktree navigation, inline highlighting, search forward/backward and Escape, selective stage/unstage/discard, historical commit browsing, and read-only commit search. Git assertions independently verified index contents, retained neighboring edits, and unchanged HEAD/stashes.

Build and release status

The unsigned app and ZIP rebuild and packaged smoke test pass. DMG creation is blocked locally: macOS returns Operation not permitted when the packager copies into /Volumes/Ledger/Ledger.app. A standard hdiutil create -srcfolder attempt returned the same denial. This needs resolving before a DMG release. These local artifacts are unsigned and retain the existing 1.5.1 version; the final approved release needs its version bump, signed build, notarization, and artifact verification. No tag or release has been published.

Scope and remaining verification

#24 already behaves safely on master; a regression verifies that double-clicking a commit leaves HEAD, dirty files, and stashes unchanged. #48's historical dependency exclusion is already removed; the packaged smoke test covers startup and bundled/native dependencies on macOS, but Linux execution remains unverified. #76 has no actionable description and its temporary screenshot is unavailable.

README and contributor guidance describe the controls, test commands, typed IPC path, and the enforced typecheck/build gate. Investigation logs and screenshots are kept locally under wip/issue-validation, rather than in permanent project documentation.


Note

Medium Risk
Changes touch core Git staging patch application and shared IPC/types across main and renderer; regressions could affect index/working-tree state or plugin/repo switching, though the PR adds focused Git and UI tests.

Overview
This PR fixes partial line staging/unstaging/discard by centralizing diff parsing and patch building in lib/services/staging/ (including noNewline handling) and wiring the legacy git-service path to the same helpers, so adjacent unselected edits and line order are preserved.

Diff UX adds Command/Ctrl-F search across expanded diffs (DiffSearch / DiffLineContent), character-level change highlighting (inline-diff), and reuses DiffViewer in commit diffs. Branch UX pins current branch and main/master at the top of every sort and links branch status to the checked-out worktree.

Broader hardening includes stricter typed IPC (e.g. PR checkout by number, discriminated local/remote repo contexts, plugin manifest Zod validation), renderer/plugin API alignment, graph/tldraw typing updates, npm run typecheck as a pre-build gate, npm run test:packaged for smoke-testing a built app, and contributor/README/changelog updates.

Reviewed by Cursor Bugbot for commit 3800038. Bugbot is set up for automated code reviews on this repo. Configure here.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

for (let j = 0; j < Math.max(removed.length, added.length); j++) {
emitChange(removed[j])
emitChange(added[j])
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Partial patches can emit mid-hunk no-newline markers

Medium Severity

buildPartialPatch appends \ No newline at end of file whenever a line has noNewline, then pairing can emit that marker before later hunk lines. That yields an inconsistent patch when a missing-EOF line is kept as context or paired ahead of remaining deletes or adds, so git apply can fail or write the wrong bytes. Full-line selections of a missing-newline file still work; partial selections around the last line do not.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 42df54c. Configure here.

event.preventDefault()
move(event.shiftKey ? -1 : 1)
}
}}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Escape closes find only from the search bar

Medium Severity

Escape is handled only on the find bar, while mousedown on the diff moves focus to the panel root. After clicking a match or the diff, Escape no longer closes search even though Command/Ctrl-F is registered on document and the panel advertises Escape-to-close.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 42df54c. Configure here.

Comment thread app/styles/app.css
.diff-line-add .diff-inline-change { background: rgba(46, 160, 67, 0.3); border-radius: 2px; }
.diff-line-delete .diff-inline-change { background: rgba(248, 81, 73, 0.3); border-radius: 2px; }
.diff-search-match { background: #ffe58a; color: #242424; }
.diff-search-match.active { background: #ffb84d; outline: 1px solid #986000; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Search highlight loses to inline change styles

Low Severity

A search hit that overlaps an inline change gets both diff-search-match and diff-inline-change. The two-class inline-change rules beat the single-class search background, so inactive matches on edited characters keep the green or red tint instead of the find highlight. Only the active match still wins.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 42df54c. Configure here.

@peterjthomson peterjthomson changed the title Fix partial diff actions and improve diff and branch navigation Fix diff workflows and enforce clean TypeScript builds Sep 24, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

There are 5 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 3800038. Configure here.

Comment thread app/app.tsx
const result = await window.conveyor.repo.openRepository(path)
if (!result.success) {
throw new Error(result.message || 'Failed to open repository')
throw new Error(result.error || 'Failed to open repository')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Open-repo errors drop the real reason

Medium Severity

Failed openRepository calls now read result.error, but the handler still returns message. Zod return validation strips that unknown key, so the thrown error always falls back to the generic Failed to open repository text and the actual failure reason never reaches the UI.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3800038. Configure here.

const newList = await window.conveyor.repo.listRepositories()
const newActive = newList.find(r => r.isActive)
if (newActive) {
if (newActive?.path) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Close skips pathless active repository

Medium Severity

After a successful close, the switcher only calls onRepoChange when the newly active repo has a filesystem path. Remote repos have a null path, so the chips can mark the remote active while the main view keeps showing the closed local repository.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3800038. Configure here.

@peterjthomson
peterjthomson merged commit 90bcb82 into master Sep 25, 2026
4 checks passed
@peterjthomson
peterjthomson deleted the codex/resolve-open-issues branch September 25, 2026 00:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant