feat(reliability): error boundaries + cancellation + retry + iframe error reporting#11
Merged
feat(reliability): error boundaries + cancellation + retry + iframe error reporting#11
Conversation
…rror reporting
Adds the seven reliability gaps tracked in
docs/research/09-polish-parity-backlog.md:
C1 App-shell React error boundary with Reload + Copy stack actions
C2 Per-pane boundaries (Sidebar / Preview / TopBar) so a single crash
never blanks the window
C5 AbortController threaded through core.generate -> providers.complete
-> pi-ai signal; renderer races IPC against abort and shows Cancel
button while generating
C6 completeWithRetry wrapper in packages/providers: max 3 attempts,
base 500ms with +/-20% jitter, retries only 5xx / network /
429-with-Retry-After. Every attempt is surfaced via onRetry — no
silent retries (PRINCIPLES §10)
C7 429 rate-limit handling: parse Retry-After, store rateLimitedUntil,
show "Rate limited until HH:MM:SS" toast with countdown
C10 Sandbox iframe error reporting: overlay.ts catches window.error +
unhandledrejection and postMessages IFRAME_ERROR; surfaced as a
slim red CanvasErrorBar above the iframe
C11 Overlay defense: setInterval(reattach, 200) re-installs listeners
in case generated HTML strips them; capture-phase listeners
New files:
packages/providers/src/retry.ts (+12 vitest cases in retry.test.ts)
packages/runtime/src/iframe-errors.ts
apps/desktop/src/renderer/src/components/ErrorBoundary.tsx
apps/desktop/src/renderer/src/components/CanvasErrorBar.tsx
No new prod dependencies. Tier 1 — no animations, no new libraries.
All UI uses var(--color-*) tokens.
Signed-off-by: hqhq1025 <1506751656@qq.com>
159bce3 to
94ad787
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Goal
Close the seven Tier-1 reliability gaps from
docs/research/09-polish-parity-backlog.mdso a single rendering bug, network blip, or rate-limit never blanks the app or leaves the user staring at silence.components/ErrorBoundary.tsx,App.tsxApp.tsxcore,providers,store.ts,App.tsxproviders/src/retry.tsstore.ts,App.tsxruntime/src/overlay.ts,runtime/src/iframe-errors.ts,components/CanvasErrorBar.tsxsetInterval(reattach, 200))runtime/src/overlay.tsFiles changed
New
packages/providers/src/retry.ts—completeWithRetry(),classifyError(),sleepWithAbort()packages/providers/src/retry.test.ts— 12 vitest casespackages/runtime/src/iframe-errors.ts—IframeErrorMessage+isIframeErrorMessageguardapps/desktop/src/renderer/src/components/ErrorBoundary.tsx— class boundary, copy-stack fallbackapps/desktop/src/renderer/src/components/CanvasErrorBar.tsx— slim red strip above iframeModified
packages/runtime/src/overlay.ts— capture-phase listeners,window.error+unhandledrejection,setInterval(reattach, 200)packages/runtime/src/index.ts— re-exportsIframeErrorMessage+ guardpackages/core/src/index.ts—GenerateInput.signal+onRetry, wrapscompletewithcompleteWithRetrypackages/core/src/generate.test.ts— mock now exportscompleteWithRetrypassthroughpackages/providers/src/index.ts— re-exports retry surfacepackages/shared/src/index.ts— addsIframeErrorEventzod schemaapps/desktop/src/renderer/src/App.tsx— split into Sidebar / PreviewPane / TopBar, each wrapped in<ErrorBoundary>; CanvasErrorBar above iframe; iframe message listener; Send -> Cancel toggle while generating; rate-limit toast with countdownapps/desktop/src/renderer/src/store.ts—currentAbortController,cancelGeneration(),iframeErrors[],rateLimitedUntil,statusLines[]Files NOT touched (boundary):
apps/desktop/src/main/**,apps/desktop/src/preload/**,packages/exporters/**,packages/i18n/**,apps/desktop/src/renderer/src/onboarding/**,packages/templates/**,packages/artifacts/**,packages/ui/**.NO new prod dependencies
Confirmed —
git diff main...HEAD -- '**/package.json'only touches workspace ranges, no new deps. The four PRINCIPLES §5b checks all stay green:completeWithRetryis a thin pure wrapper —complete()itself is unchangedAcceptance test outcomes
The 12 new retry tests cover: success first-try, transient retry success surfaced via onRetry, exhausted retries, non-retryable 401, 429 honouring Retry-After, AbortSignal pre-call cancellation, AbortSignal mid-backoff cancellation, plus 5
classifyErrorcases (5xx / 4xx / 429 / AbortError / TypeError).Integration notes
wt/preview-ux-v2: that branch is expected to restructureApp.tsxinto Sidebar / PreviewPane / TopBar components. This PR already does that split and wraps each in<ErrorBoundary scope="...">. When merging, keep theErrorBoundarywrappers and let preview-ux-v2 swap the inner JSX as it likes — the boundaries are cosmetic-agnostic.apps/desktop/src/main/**boundary is hands-off. So the renderer races the IPC promise against anAbortControllerand discards the result on cancel. Once the IPC layer learns to forwardsignal, the wiring incore.generate(signal)is already in place — main just needs to pass it through.onRetrycallbacks fire in main-process land today; surfacing them in the renderer needs a future IPC channel (codesign:generation-status). This PR ships the loud surface (statusLines[]+ Sidebar list) ready to receive those events.Test plan
pnpm dev, paste an invalid model name, confirm error lands in chat (not silent)<script>throw new Error('boom')</script>into a generated artifact and confirm the red CanvasErrorBar appears with "boom (about:srcdoc:LINE)"<script>setTimeout(()=>{throw 1},10);document.removeEventListener('click',()=>{},true)</script>and verify clicks still trigger ELEMENT_SELECTED after 200ms (overlay re-attach)