Skip to content

fix: clear stale host call info and add error boundary#5

Merged
tomusdrw merged 4 commits intomainfrom
td-fix-ecalli-crash-pending
Apr 10, 2026
Merged

fix: clear stale host call info and add error boundary#5
tomusdrw merged 4 commits intomainfrom
td-fix-ecalli-crash-pending

Conversation

@tomusdrw
Copy link
Copy Markdown
Member

Summary

  • Fix "Pending changes" showing during continuous execution: hostCallInfo entries were never cleared after auto-continue resumed host calls. The stale entries caused usePendingChanges to always show the "Pending Host Call Changes" banner even while the PVM was running. Fixed by clearing entries in onStateChanged when lifecycle leaves paused_host_call, and gating <PendingChanges> render on lifecycle === "paused_host_call".
  • Add ErrorBoundary: Uncaught render errors previously crashed the app to a blank page (no error boundary existed). Added one around the app routes with a recovery UI.

Test plan

  • All 672 unit tests pass
  • Lint clean
  • Build succeeds
  • Manual: load a trace example (io-trace or trace-001), click Run with auto-continue — "Pending changes" banner should NOT appear during execution
  • Manual: pause at a host call — "Pending changes" banner appears correctly
  • Manual: trigger a render error — error boundary shows recovery UI instead of blank page

🤖 Generated with Claude Code

Host call info entries were never removed from React state after
auto-continue resumed them, causing "Pending changes" to display
during continuous execution. Fix by clearing entries in onStateChanged
when lifecycle leaves paused_host_call, and guarding PendingChanges
render on lifecycle === "paused_host_call".

Add ErrorBoundary to catch uncaught render errors gracefully instead
of crashing to a blank page.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 10, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 30487d7d-dedf-4731-b351-8c9fbb50d36d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The changes introduce an error boundary component to catch rendering failures in the app's routing layer and fix a state synchronization issue where pending host call changes were displayed incorrectly during continuous execution. Updates ensure pending changes only render when lifecycle is paused and stale state is cleared when execution resumes.

Changes

Cohort / File(s) Summary
Error Boundary Integration
apps/web/src/App.tsx, apps/web/src/components/ErrorBoundary.tsx
New ErrorBoundary class component captures uncaught render errors, displays error message with recovery UI, and provides reload functionality. App.tsx wraps RestoreGate and Routes with this boundary for error isolation.
Pending Changes State Management
apps/web/src/hooks/useOrchestratorState.ts, apps/web/src/components/debugger/RegistersPanel.tsx
Hook clears per-PVM hostCallInfo entries when lifecycle transitions away from "paused_host_call". Panel now conditionally renders PendingChanges UI only when both lifecycle === "paused_host_call" and pending changes exist.
Documentation
spec/ui/sprint-47-pending-changes-fix-and-error-boundary.md
Sprint 47 specification documenting error boundary implementation and pending changes state fix.

Sequence Diagrams

sequenceDiagram
    participant App as App
    participant EB as ErrorBoundary
    participant Routes as Routes/Components
    
    App->>EB: Render with children
    EB->>Routes: Render children
    Routes-->>EB: Throws Error
    EB->>EB: getDerivedStateFromError()<br/>componentDidCatch()
    EB->>EB: Display error UI
    Note over EB: Error message + Reload button
    EB->>EB: onClick: navigate to `#/load`
    EB->>EB: window.location.reload()
Loading
sequenceDiagram
    participant Orch as Orchestrator
    participant Hook as useOrchestratorState
    participant State as Hook State
    participant Panel as RegistersPanel
    
    Orch->>Hook: pvmStateChanged event
    Hook->>Hook: Check lifecycle value
    alt lifecycle !== "paused_host_call"
        Hook->>State: Remove hostCallInfo for pvmId
        Hook->>State: Assign updated Map
    end
    Hook->>Panel: Update pendingHostCallInfo state
    Panel->>Panel: Check lifecycle === "paused_host_call"<br/>AND pendingChanges.pending
    alt Conditions met
        Panel->>Panel: Render PendingChanges
    else
        Panel->>Panel: Hide PendingChanges
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the two main changes: clearing stale host call info and adding an error boundary component.
Description check ✅ Passed The description clearly relates to the changeset, detailing both the host call info fix and the error boundary addition with specific technical context.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch td-fix-ecalli-crash-pending

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
apps/web/src/components/ErrorBoundary.tsx (2)

41-44: Remove redundant state reset before full reload

Line 42 is unnecessary because Line 44 triggers a full page reload immediately.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/ErrorBoundary.tsx` around lines 41 - 44, Remove the
redundant this.setState({ error: null }) inside the ErrorBoundary onClick
handler: since window.location.reload() (called after setting
window.location.hash) performs a full page reload, delete the this.setState call
in the onClick callback to avoid unnecessary state mutation; locate the onClick
handler in ErrorBoundary.tsx (the anonymous function containing this.setState,
window.location.hash = "#/load", window.location.reload) and remove only the
this.setState({ error: null }) line.

39-47: Import and use shared-ui Button component instead of a local <button> element

For consistency with the rest of the codebase, use the Button component from @fluffylabs/shared-ui, which is already widely imported throughout the app (e.g., LoadPage.tsx, DebuggerPage.tsx, ExecutionControls.tsx).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/ErrorBoundary.tsx` around lines 39 - 47, Replace the
local <button> inside the ErrorBoundary render with the shared Button component:
add an import for Button from "@fluffylabs/shared-ui" at the top of the file,
then swap the element used in the ErrorBoundary component (the block that calls
this.setState({ error: null }); window.location.hash = "#/load";
window.location.reload();) to use <Button> and forward the same onClick handler,
type prop (if the shared Button accepts it) and className/visual props so
behavior and styling remain identical; ensure the Button receives the same
children content and that the import name matches the component reference in the
file.
spec/ui/sprint-47-pending-changes-fix-and-error-boundary.md (1)

14-55: Add an explicit verification checklist section

The doc captures behavior and pitfalls well, but it should include concrete verification steps (including edge-case runs) as an explicit checklist for this sprint’s acceptance criteria.

As per coding guidelines spec/**/*.md: Mark spec files as implemented by adding Status: Implemented to the top and enhance acceptance criteria with edge cases, pitfalls, and verification steps discovered during implementation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@spec/ui/sprint-47-pending-changes-fix-and-error-boundary.md` around lines 14
- 55, Add "Status: Implemented" at the top of the spec and append an explicit
"Verification Checklist" section that enumerates concrete acceptance steps and
edge-case checks; specifically, include tests to reproduce the stale
hostCallInfo leak (continuous run loop with auto-continue), manual host-call
pause/resume behavior, rAF flush propagation when pendingHostCallInfo.current is
null vs set, RegistersPanel rendering gating on lifecycle ===
"paused_host_call", and the ErrorBoundary display/reload behavior. Ensure the
checklist references the documented areas ("What Works After This Sprint", "Bug
Details", and the new ErrorBoundary) and includes pass/fail criteria for each
item so reviewers can mark the sprint as accepted.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/hooks/useOrchestratorState.ts`:
- Around line 143-149: The flush is creating an empty Map (new Map()) which can
wipe other PVM entries; instead, initialize the temporary map from the latest
committed host-call map and then delete only the target pvmId. Concretely, when
building hcBase, use the committed map (e.g.,
pendingHostCallInfoCommitted.current) or fallback to pendingHostCallInfo.current
to clone existing entries, then call hcBase.delete(pvmId) and assign
pendingHostCallInfo.current = hcBase so only that pvmId is removed and unrelated
paused host-call entries are preserved.

---

Nitpick comments:
In `@apps/web/src/components/ErrorBoundary.tsx`:
- Around line 41-44: Remove the redundant this.setState({ error: null }) inside
the ErrorBoundary onClick handler: since window.location.reload() (called after
setting window.location.hash) performs a full page reload, delete the
this.setState call in the onClick callback to avoid unnecessary state mutation;
locate the onClick handler in ErrorBoundary.tsx (the anonymous function
containing this.setState, window.location.hash = "#/load",
window.location.reload) and remove only the this.setState({ error: null }) line.
- Around line 39-47: Replace the local <button> inside the ErrorBoundary render
with the shared Button component: add an import for Button from
"@fluffylabs/shared-ui" at the top of the file, then swap the element used in
the ErrorBoundary component (the block that calls this.setState({ error: null
}); window.location.hash = "#/load"; window.location.reload();) to use <Button>
and forward the same onClick handler, type prop (if the shared Button accepts
it) and className/visual props so behavior and styling remain identical; ensure
the Button receives the same children content and that the import name matches
the component reference in the file.

In `@spec/ui/sprint-47-pending-changes-fix-and-error-boundary.md`:
- Around line 14-55: Add "Status: Implemented" at the top of the spec and append
an explicit "Verification Checklist" section that enumerates concrete acceptance
steps and edge-case checks; specifically, include tests to reproduce the stale
hostCallInfo leak (continuous run loop with auto-continue), manual host-call
pause/resume behavior, rAF flush propagation when pendingHostCallInfo.current is
null vs set, RegistersPanel rendering gating on lifecycle ===
"paused_host_call", and the ErrorBoundary display/reload behavior. Ensure the
checklist references the documented areas ("What Works After This Sprint", "Bug
Details", and the new ErrorBoundary) and includes pass/fail criteria for each
item so reviewers can mark the sprint as accepted.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9cf3dcae-5447-44cc-b5a3-15e5a94c9a02

📥 Commits

Reviewing files that changed from the base of the PR and between 710f0ca and bdc92ef.

📒 Files selected for processing (5)
  • apps/web/src/App.tsx
  • apps/web/src/components/ErrorBoundary.tsx
  • apps/web/src/components/debugger/RegistersPanel.tsx
  • apps/web/src/hooks/useOrchestratorState.ts
  • spec/ui/sprint-47-pending-changes-fix-and-error-boundary.md

The reference/pvm-debugger submodule pointed to a commit that no
longer exists on the remote, breaking Netlify builds. Update both
submodules to current main branch heads.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@netlify
Copy link
Copy Markdown

netlify bot commented Apr 10, 2026

Deploy Preview for pvm-debugger ready!

Name Link
🔨 Latest commit ea0d7e9
🔍 Latest deploy log https://app.netlify.com/projects/pvm-debugger/deploys/69d96869fd8fd30008df4189
😎 Deploy Preview https://deploy-preview-5--pvm-debugger.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

tomusdrw and others added 2 commits April 10, 2026 23:07
The HostCallTab mounted during auto-continue when the rAF fired between
host-call detection and resume (Worker postMessage is a macrotask).
Its render-time setState and async orchestrator.setGas() calls created
cascading updates that exceeded React's max update depth (error #185).

Fix by returning null from useHostCallState when isRunning is true,
preventing the transient paused_host_call state from reaching React.

Add e2e tests that reproduce the crash with trace examples.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Verifies that activeHostCall is suppressed when isRunning is true,
preventing the max-update-depth crash during auto-continue.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tomusdrw tomusdrw merged commit dc84e3f into main Apr 10, 2026
7 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