Skip to content

refactor(ui): keep history reviews in one renderer - #992

Merged
benvinegar merged 3 commits into
feat/hunk-log-handofffrom
feat/hunk-log-panel
Sep 6, 2026
Merged

refactor(ui): keep history reviews in one renderer#992
benvinegar merged 3 commits into
feat/hunk-log-handofffrom
feat/hunk-log-panel

Conversation

@benvinegar

@benvinegar benvinegar commented Sep 5, 2026

Copy link
Copy Markdown
Member

Stack

Problem

The process-level readiness handoff in #989 shortened review startup but still crossed two alternate-screen renderers. Some terminals briefly displayed unreadable stale content between history and review. Renderer/root transitions also exposed unsafe initial redraw behavior when mounting the review dynamically.

Approach

  • keep one CliRenderer and one React/OpenTUI root alive for the complete hunk log session
  • route retained history and fresh commit reviews inside that root
  • prepare provider-owned Git, jj, and extension review actions in-process through the normal startup pipeline
  • keep the themed history loading state visible before provider startup begins
  • create a fresh review producer, broker session, extension authority, notes, drafts, and AppHost generation for every opened commit
  • wait for the dynamically mounted review's requested first frame before treating it as ready
  • skip unsafe initial intermediateRender() calls while preserving redraws for later geometry changes
  • retire extension and broker resources before returning to retained history
  • propagate cancellation through startup and VCS review loading
  • preserve invocation-relative extension paths and broker cwd metadata
  • remove the superseded child-process IPC and terminal-handoff protocol

Animation is intentionally excluded. This PR stabilizes the single-renderer snap transition first.

Validation

  • full unit/CLI/session suite passed in both shards
  • full PTY integration: 146 passed, 1 platform-specific skip
  • focused open/review/back PTY scenario passed 10 consecutive runs
  • TTY smoke: 9 passed
  • dynamic AppHost mount and post-mount redraw regression tests passed
  • typecheck and lint passed
  • docs, dependency, and source-boundary checks passed
  • independent lifecycle/architecture reviews completed and valid findings fixed
  • git diff --check passed

The PTY scenario asserts that opening, returning, and repeated review cycles do not emit an alternate-screen exit between surfaces.

Residual risk

Bundled provider implementations still contain some synchronous repository probes inherited from ordinary startup. The loading frame is committed before those probes begin, and VCS loading receives cancellation where the adapter supports it. Fully preempting a non-cooperative synchronous third-party extension requires broader asynchronous provider-runtime work outside this transition refactor.

This PR description was generated by Pi using GPT-5.6-sol

@vercel

vercel Bot commented Sep 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
hunk-web Ignored Ignored Preview Sep 6, 2026 1:00am UTC

Request Review

@greptile-apps

greptile-apps Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces child-process terminal handoff with a single retained OpenTUI renderer and React root for history and commit reviews. It also introduces cancellable asynchronous bundled-VCS loading and workspace-owned extension reuse.

  • Routes history and dynamically mounted reviews through LogSessionHost.
  • Creates fresh broker, producer, notes, drafts, and app generations for each opened commit.
  • Propagates cancellation through startup and Git, Jujutsu, and Sapling review loading.
  • Adds first-frame and post-mount redraw handling for dynamic review mounts.
  • Removes the superseded terminal-handoff IPC implementation.
  • One functional gap remains in the extension-trust reload path, alongside filename-rule and documentation issues.

Confidence Score: 4/5

The PR is not ready to merge because trusting repository extensions from an embedded history review does not actually load them, and the explicit filename requirement must also be satisfied.

The borrowed-extension ownership guard suppresses the registry replacement requested by the trust workflow, leaving newly trusted extensions unavailable in the current workspace; the remaining findings concern an explicit naming rule and contradictory lifecycle documentation.

Files Needing Attention: src/ui/AppHost.tsx, src/extensions/default/vcs/asyncProcess.ts, src/ui/log/LogSessionHost.tsx, src/ui/hooks/useIntermediateRenderAfterMount.ts, website/src/content/docs/docs/workflows/git-history.md

Important Files Changed

Filename Overview
src/ui/log/LogSessionHost.tsx Introduces the single-root history/review router and per-review broker lifecycle; behavior is broadly covered, but the new filename violates the repository naming rule.
src/ui/AppHost.tsx Adds borrowed extension ownership and embedded-review behavior; its ownership guard makes trust-triggered extension reloads ineffective.
src/ui/runInteractiveApp.tsx Extracts reusable review-session creation and in-process embedded-review startup while preserving standalone rendering.
src/extensions/default/vcs/asyncProcess.ts Adds cancellable asynchronous subprocess execution with process-tree termination and reaping; the filename violates the dash-case rule.
src/extensions/default/vcs/git/index.ts Moves Git review loading to cancellation-aware asynchronous commands while retaining existing review semantics.
src/extensions/default/vcs/jujutsu/index.ts Moves Jujutsu review loading and immutable endpoint resolution to asynchronous cancellable commands.
src/extensions/default/vcs/sapling/index.ts Moves Sapling review loading and unknown-file discovery to asynchronous cancellable commands.
website/src/content/docs/docs/workflows/git-history.md Documents the single-renderer transition but incorrectly describes extension state as fresh for each opened commit.

Sequence Diagram

sequenceDiagram
  participant User
  participant Log as LogApp
  participant Host as LogSessionHost
  participant Startup as Startup/VCS pipeline
  participant Review as AppHost
  participant Broker as Session broker

  User->>Log: Open selected commit
  Log->>Host: Provider-planned review action
  Host->>Startup: Prepare with cancellation signal
  Startup-->>Host: Review bootstrap
  Host->>Broker: Start fresh review session
  Host->>Review: Mount into retained renderer/root
  Review-->>User: Commit review frame
  User->>Review: Quit / return
  Review->>Host: Retire review
  Host->>Broker: Stop session
  Host-->>User: Restore retained history
Loading

Comments Outside Diff (1)

  1. website/src/content/docs/docs/workflows/git-history.md, line 77-81 (link)

    P2 Extension Lifecycle Is Misdocumented

    This says every opened commit gets fresh extension state, but the implementation and extension documentation intentionally reuse one history-owned extension instance across all opened commits. That gives extension authors conflicting lifecycle guidance and may lead them to assume module state is reset per review. Clarify that review and broker state are fresh while extension state remains shared for the history workspace.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: website/src/content/docs/docs/workflows/git-history.md
    Line: 77-81
    
    Comment:
    **Extension Lifecycle Is Misdocumented**
    
    This says every opened commit gets fresh extension state, but the implementation and extension documentation intentionally reuse one history-owned extension instance across all opened commits. That gives extension authors conflicting lifecycle guidance and may lead them to assume module state is reset per review. Clarify that review and broker state are fresh while extension state remains shared for the history workspace.
    
    ---
    
    For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Prompt To Fix All With AI
### Issue 1
src/ui/AppHost.tsx:293-295
**Trust Reload Is Skipped**

When a user trusts repository extensions from an embedded history review, the trust flow requests an extension reload. This ownership check skips that reload for borrowed reviews, so the review reloads with the same registry that omitted those extensions. The newly trusted extensions therefore remain unavailable even though the normal trust flow promises to enable them immediately. The owning history session should perform the registry replacement, or the UI should clearly require a workspace restart.

### Issue 2
src/extensions/default/vcs/asyncProcess.ts:1
**New Filenames Violate Dash-Case**

The repository requires `.ts` and `.tsx` filenames to use dash-case, but this PR adds `asyncProcess.ts`, `LogSessionHost.tsx`, and `useIntermediateRenderAfterMount.ts`. Rename these files, their colocated tests, and their imports to dash-case before merging.

### Issue 3
website/src/content/docs/docs/workflows/git-history.md:77-81
**Extension Lifecycle Is Misdocumented**

This says every opened commit gets fresh extension state, but the implementation and extension documentation intentionally reuse one history-owned extension instance across all opened commits. That gives extension authors conflicting lifecycle guidance and may lead them to assume module state is reset per review. Clarify that review and broker state are fresh while extension state remains shared for the history workspace.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "fix(ui): cancel history review loading s..." | Re-trigger Greptile

Comment thread src/ui/AppHost.tsx
Comment on lines +293 to +295
if (
extensionOwnership === "owned" &&
(options?.reloadExtensions || cwd !== extensionsCwdRef.current)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Trust Reload Is Skipped

When a user trusts repository extensions from an embedded history review, the trust flow requests an extension reload. This ownership check skips that reload for borrowed reviews, so the review reloads with the same registry that omitted those extensions. The newly trusted extensions therefore remain unavailable even though the normal trust flow promises to enable them immediately. The owning history session should perform the registry replacement, or the UI should clearly require a workspace restart.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/ui/AppHost.tsx
Line: 293-295

Comment:
**Trust Reload Is Skipped**

When a user trusts repository extensions from an embedded history review, the trust flow requests an extension reload. This ownership check skips that reload for borrowed reviews, so the review reloads with the same registry that omitted those extensions. The newly trusted extensions therefore remain unavailable even though the normal trust flow promises to enable them immediately. The owning history session should perform the registry replacement, or the UI should clearly require a workspace restart.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in c66b0ab. Borrowed embedded reviews now tell the trust controller that they cannot replace the session-owned extension registry, so granting trust records the decision and clearly reports that Hunk must be restarted to load those extensions instead of pretending the current review reloaded them.

Responded by Pi using gpt-5.6-sol.

This comment was generated by Pi using gpt-5.6-sol

@@ -0,0 +1,98 @@
const DEFAULT_TERMINATION_GRACE_MS = 250;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 New Filenames Violate Dash-Case

The repository requires .ts and .tsx filenames to use dash-case, but this PR adds asyncProcess.ts, LogSessionHost.tsx, and useIntermediateRenderAfterMount.ts. Rename these files, their colocated tests, and their imports to dash-case before merging.

Context Used: guidelines.mdc Cursor rule (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/extensions/default/vcs/asyncProcess.ts
Line: 1

Comment:
**New Filenames Violate Dash-Case**

The repository requires `.ts` and `.tsx` filenames to use dash-case, but this PR adds `asyncProcess.ts`, `LogSessionHost.tsx`, and `useIntermediateRenderAfterMount.ts`. Rename these files, their colocated tests, and their imports to dash-case before merging.

**Context Used:** guidelines.mdc Cursor rule ([source](https://github.com/modem-dev/modem/blob/main/.cursor/rules/guidelines.mdc))

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

No change. Hunk does not require dash-case TypeScript filenames; the repository consistently uses camelCase for helpers/hooks and PascalCase for React components. The proposed renames would conflict with established local conventions.

Responded by Pi using gpt-5.6-sol.

This comment was generated by Pi using gpt-5.6-sol

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You're right. The dash-case finding was incorrect: this repository's local convention uses camelCase for helpers/hooks and PascalCase for React components (for example, currentReviewRefresh.ts and AppHost.tsx). No rename is needed; I'll withdraw this comment.

@benvinegar
benvinegar merged commit 554cfc0 into main Sep 6, 2026
17 of 18 checks passed
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