Skip to content

Add action failure retry and diagnostics - #548

Open
bjesus wants to merge 3 commits into
mainfrom
feature/copy-action-error-details
Open

bjesus wants to merge 3 commits into
mainfrom
feature/copy-action-error-details

Conversation

@bjesus

@bjesus bjesus commented Sep 22, 2026 •

Copy link
Copy Markdown
Member

What does this change?

Currently, the "Failed to approve action" leaves the user unable to do anything. The real error is hidden in the browser console. This PR adds a "Copy error details" and a "Try again" button.

screenshot_20260922_152305-region

The error schema looks like this:

{
  "operation": "approve-action",
  "actionId": 5,
  "error": {
    "type": "Error",
    "message": "Home Assistant WS command failed (not_found): Service cover.press not found.",
    "stack": "evaluateImpl@http://localhost:3000/node_modules/.vite/deps/capnweb.js?v=f70653e6:1560:75\nevaluateWithDepth@http://localhost:3000/node_modules/.vite/deps/capnweb.js?v=f70653e6:1470:28\nevaluate@http://localhost:3000/node_modules/.vite/deps/capnweb.js?v=f70653e6:1465:17\nreadLoop@http://localhost:3000/node_modules/.vite/deps/capnweb.js?v=f70653e6:2369:69\n"
  }
}

Why is this obviously correct and trivially verifiable?

It's a small change that would help users act upon seeing this error, either by retrying or by copying the error and reporting it to the devs, or by pasting it back in the chat and letting the agent understand what went wrong.

Checklist

Checking every item does not guarantee acceptance. Maintainers determine whether
a pull request meets the contribution policy.

  • This is a small, concrete change; it is not a feature, refactor, or low-value cleanup.
  • I understand that maintainers decide whether the change is obviously correct and trivially verifiable.
  • I have read and followed the contribution guidelines.

Devin Review

@github-actions github-actions Bot added the workshop/frontend Changes to the Workshop frontend label Sep 22, 2026
@github-actions

Copy link
Copy Markdown

Preview: pr548-feature-copy-48784bba

https://pr548-feature-copy-48784bba-router.cloudflare-os-previews.workers.dev

Dashboard · deleted when this PR closes

@ask-bonk

ask-bonk Bot commented Sep 22, 2026

Copy link
Copy Markdown
  • P1 useResolveAction.ts:49: “Try again” can overlap with a retry from the original action card. setProcessing only disables the card and does not guard resolveAction, so concurrent approvals may apply the external action twice. Add a synchronous per-action in-flight guard.
  • P2 useResolveAction.ts:40: Kumo toasts default to a 5-second timeout, so both recovery actions disappear quickly. Set timeout: 0 for this actionable error toast.

github run

@devin-ai-integration devin-ai-integration 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.

Note

Newer findings are available below. Devin Review posted a newer report on this PR, in addition to the findings presented here.

Devin Review found 3 potential issues.

Devin Review

Comment thread packages/workshop-frontend/src/useResolveAction.ts
Comment thread packages/workshop-frontend/src/useResolveAction.ts
Comment on lines 28 to +31
const onResolvedRef = useRef(onResolved)
onResolvedRef.current = onResolved

return useCallback(async (actionId: number, decision: ActionDecision) => {
return useCallback(async function resolveAction(actionId: number, decision: ActionDecision) {

@devin-ai-integration devin-ai-integration Bot Sep 22, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Hook style diverges from convention

The edited hook remains a function declaration despite the repository's named-arrow default. Review whether recursion warrants this departure.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

console.error(`Failed to ${decision} action:`, error)
toasts.add({ title: `Failed to ${decision} action`, variant: 'error' })
const diagnostics = actionErrorDiagnostics(error, { actionId, decision })
const toastId = toasts.add({

@ask-bonk ask-bonk Bot Sep 22, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Clear stale failure toasts when resolution starts elsewhere

This persistent toast is closed only by its own Try again callback. After a failure, retrying from the still-visible action card can succeed while this toast remains indefinitely; clicking its retry afterward targets an already-resolved action and creates another misleading persistent error. Repeated card failures also stack distinct timeout: 0 toasts. Track one toast per action and close/replace it whenever any new resolution attempt starts (and when the action is observed resolved).

@ask-bonk

ask-bonk Bot commented Sep 22, 2026

Copy link
Copy Markdown

Submitted 1 actionable inline finding.

github run


return useCallback(async function resolveAction(actionId: number, decision: ActionDecision) {
if (currentOverseer.current !== overseer) return
closeFailureToast.current?.()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Keep failure toasts for other pending actions

closeFailureToast is shared across every action handled by this hook, so starting a resolution for action B immediately dismisses action A’s persistent error toast here, even if A is still pending. For example, after A fails, clicking Approve on B removes A’s only “Try again” and “Copy error details” controls; concurrent failures also overwrite each other in the catch block. Track the failure toast by action ID and close only the toast for the action being retried/resolved.

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.

I think that's okay. If you moved on the approve action B and so lost the toast for the failing action A, you can just reapprove action A and you'll get the toast again.

@ask-bonk

ask-bonk Bot commented Sep 23, 2026

Copy link
Copy Markdown

Submitted one actionable inline finding: starting another action dismisses the first action’s failure toast, removing its retry and copy-details controls while that action is still pending.

github run

@devin-ai-integration devin-ai-integration 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.

Devin Review found 2 new potential issues.

Devin Review

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Contribution exceeds the external-review bar

This outside contribution adds 129 lines of retry, reconnection, toast-lifecycle, and concurrency logic. Repository policy limits such contributions to small, trivially verified changes.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.


return useCallback(async function resolveAction(actionId: number, decision: ActionDecision) {
if (currentOverseer.current !== overseer) return
closeFailureToast.current?.()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Unrelated actions dismiss recovery toast

Starting any action through resolveAction dismisses the previous failed action's recovery toast, even when their IDs differ. The first failure then loses both retry and diagnostic controls.

Learn more

Each hook instance owns one closeFailureToast slot, but its callers can resolve several pending actions independently. A failure stores its recovery controls in that single slot. Any later action closes the slot before checking whether that action is related to the failure. Concurrent failures also replace one another when they settle, so only the last failure remains recoverable.

Example: Action 5 fails and displays retry and diagnostic controls. The user then approves action 6. Starting action 6 closes action 5's toast, although action 5 remains pending and unresolved.

Recommended fix: Track failure toasts by action ID, or keep independent failure toasts. Close only the toast for the action being retried or resolved, and clean up every tracked toast when the overseer changes or the hook unmounts.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

workshop/frontend Changes to the Workshop frontend

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant