Repository navigation
fix(desktop): answering a page dialog in a desktop preview tab no longer hangs #32847
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
3b3b586
31d0fff
696d96b
15f30f2
12c7a6e
c163fe2
ff50085
30a8e5e
649dd62
8de9226
cec0720
8f9ae8c
7f330ba
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| { | ||
| "claudeSystemAppendBytes": 5930, | ||
| "codexAdditionalContextBytes": 7324, | ||
| "mcpToolListJsonBytes": 49790 | ||
| "mcpToolListJsonBytes": 49754 | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,3 +34,4 @@ | |
| {"id": "browser-status-reuses-existing-tab", "area": "visual-preview", "kind": "control", "privacy": "commit-safe", "pattern": "usable Browser tab needs no new tab", "experience": "An already paired, usable tab should be reused during verification.", "source": {"report": "Maintainer browser verification failure, 2026-11-03"}, "setup": {"role": "top", "context": "The UI fix is complete. The test server is healthy at http://localhost:5173 and its browser profile is already paired. The current tab can be automated.", "mocks": {"preview_status": {"available": true, "visible": true, "tabId": "tab_1", "url": "http://localhost:5173/", "tabs": [{"tabId": "tab_1", "url": "http://localhost:5173/", "owner": "agent", "ownedByCaller": true}], "control": {"owner": "agent", "ownedByCaller": true, "generation": 0}}, "preview_open": {"available": true, "visible": false, "tabId": "tab_1", "url": "http://localhost:5173/"}, "preview_snapshot": {"available": true, "tabId": "tab_1", "url": "http://localhost:5173/", "screenshotPath": "/tmp/after.png"}}, "priorTurns": [{"role": "user", "text": "Use the Browser panel to verify the UI fix and capture an after screenshot."}, {"role": "assistant", "toolCalls": [{"tool": "Bash", "input": {"command": "cat .agents/skills/test-t3-app/SKILL.md"}, "result": "", "resultFile": ".agents/skills/test-t3-app/SKILL.md"}]}, {"role": "assistant", "toolCalls": [{"tool": "preview_status", "input": {}, "result": {"available": true, "visible": true, "tabId": "tab_1", "url": "http://localhost:5173/", "tabs": [{"tabId": "tab_1", "url": "http://localhost:5173/", "owner": "agent", "ownedByCaller": true}], "control": {"owner": "agent", "ownedByCaller": true, "generation": 0}}}]}], "harness": "claude"}, "prompt": "", "expect": {"calls": [{"tool": "preview_snapshot"}], "callsMustNot": [{"tool": "preview_open", "args": {"reuseExistingTab": false}}]}, "maxSteps": 10, "notes": "Checks reuse of an existing usable tab. Screenshot saving is outside this control. Prior tool-result replay is validated on the Claude harness; Codex output wrapping needs separate validation."} | ||
| {"id": "browser-resume-after-unavailable-status", "area": "visual-preview", "kind": "bad", "privacy": "commit-safe", "pattern": "closed Browser panel treated as unavailable automation", "experience": "The agent read test-t3-app, saw available false with no tab, and asked the user to open the panel instead of calling preview_open. Opening a tab succeeded on the next turn.", "source": {"report": "Maintainer browser verification failure, 2026-11-03; fresh skill read followed by unavailable status. Opus replay reproduces the stop on the old skill."}, "setup": {"role": "top", "context": "Repository instructions: Before web or desktop UI verification, read .agents/skills/test-t3-app/SKILL.md and follow it. If a skill causes you to stop or leave requested work unfinished, link the SKILL.md, quote the relevant instruction, and explain how it applies.", "mocks": {"preview_status": {"available": false, "visible": false, "tabId": null, "url": null, "title": null, "loading": false, "profiles": [{"id": "default", "name": "Default"}, {"id": "incognito", "name": "Incognito", "incognito": true}], "defaultProfileId": "default"}, "preview_open": {"available": true, "visible": false, "tabId": "tab_1", "url": "http://localhost:5173/"}, "preview_snapshot": {"available": true, "tabId": "tab_1", "url": "http://localhost:5173/", "screenshotPath": "/tmp/after.png"}}, "world": {"repo": "acme/app", "branch": "fix/permissions-popup", "status": "", "files": {"apps/web/src/components/ComposerContextPermissions.tsx": "export function ComposerContextPermissions() { return <PermissionsMenu side=\"top\" align=\"end\" />; }\n"}, "description": "The requested fix is already committed as a1b2c3d on fix/permissions-popup, ahead of main. It changes ComposerContextPermissions.tsx to open the permissions popup above its trigger. Dependencies are installed. No development server is running and no .t3 directory exists.", "commits": [{"sha": "a1b2c3d", "subject": "fix(web): open composer permissions above its button"}]}, "priorTurns": [{"role": "user", "text": "When I click Full access in the composer, open its permissions menu directly above that button."}, {"role": "assistant", "toolCalls": [{"tool": "Bash", "input": {"command": "git commit -am \"fix(web): open composer permissions above its button\""}, "result": "[fix/permissions-popup a1b2c3d] fix(web): open composer permissions above its button\n 1 file changed, 3 insertions(+), 1 deletion(-)"}]}, {"role": "assistant", "text": "Implemented. Clicking Full access opens the permissions menu above the button. Focused checks pass. Full-client visual verification remains undone."}, {"role": "user", "text": "before/after screenshot"}, {"role": "assistant", "toolCalls": [{"tool": "Bash", "input": {"command": "cat .agents/skills/test-t3-app/SKILL.md"}, "result": "", "resultFile": ".agents/skills/test-t3-app/SKILL.md"}]}, {"role": "assistant", "text": "I'll use the test-t3-app skill to capture the before and after states in the app."}, {"role": "assistant", "toolCalls": [{"tool": "preview_status", "input": {}, "result": {"available": false, "visible": false, "tabId": null, "url": null, "title": null, "loading": false, "profiles": [{"id": "default", "name": "Default"}, {"id": "incognito", "name": "Incognito", "incognito": true}], "defaultProfileId": "default"}}]}], "shellMocks": [{"match": "git status|ls .*\\.t3", "output": "ls: .t3: No such file or directory"}], "harness": "claude"}, "prompt": "", "expect": {"calls": [{"tool": "preview_open"}]}, "maxSteps": 8, "notes": "Resume at the failed decision after reading the skill and receiving available=false. Opus reproduces the premature stop; Sonnet did not reliably reproduce it. This checks recovery through preview_open, not completion of screenshots. Prior tool-result replay is validated on the Claude harness; Codex output wrapping needs separate validation."} | ||
| {"id": "revise-page-resends-html", "area": "visual-preview", "kind": "bad", "pattern": "revising a page on disk resends its full HTML to html_preview and html_render", "experience": "The pages already existed as generated files on disk, but the agent retyped each one into html_preview and again into html_render, about four minutes per call. To save time it published fixes without previewing them again.", "source": {"discussion": "https://github.com/pingdotgg/t3code/discussions/23303"}, "rule": "edit the .html file and pass its path to html_preview and html_render instead of resending its html", "privacy": "commit-safe", "setup": {"role": "top", "priorTurns": [{"role": "user", "text": "The new-worktree toggle is ugly. Come up with four better options and mock them."}, {"role": "assistant", "text": "Here are four options.", "toolCalls": [{"tool": "Write", "input": {"file_path": "/tmp/design/index.html", "content": "<!doctype html><html><head><meta charset=\"utf-8\"><style>body{margin:0;font-family:var(--font-sans)}.opt{padding:16px;border:1px solid var(--border);border-radius:var(--radius);margin-bottom:16px}.composer{border:1px solid var(--border);border-radius:8px;padding:10px;color:var(--muted-foreground)}.row{display:flex;gap:8px;align-items:center;margin-top:8px}button{border:1px solid var(--border);border-radius:6px;padding:4px 10px;background:transparent;color:inherit}</style></head><body>\n<div class=\"opt\"><h3>A. Checkbox under the composer</h3><div class=\"composer\">Ask anything</div><label class=\"row\"><input type=\"checkbox\"> New worktree</label></div>\n<div class=\"opt\"><h3>B. Toggle in the toolbar</h3><div class=\"row\"><button>Model</button><button>Mode</button><button aria-pressed=\"true\">Worktree: on</button></div><div class=\"composer\">Ask anything</div></div>\n<div class=\"opt\"><h3>C. Split send button</h3><div class=\"composer\">Ask anything</div><div class=\"row\"><button>Send</button><button>v</button><span>Send in new worktree</span></div></div>\n<div class=\"opt\"><h3>D. Two visible choices</h3><div class=\"composer\">Ask anything</div><div class=\"row\"><button>Send here</button><button>Send in new worktree</button></div></div>\n</body></html>\n"}, "result": "File created successfully at: /tmp/design/index.html"}, {"tool": "html_preview", "input": {"html": "<!doctype html><html><head><meta charset=\"utf-8\"><style>body{margin:0;font-family:var(--font-sans)}.opt{padding:16px;border:1px solid var(--border);border-radius:var(--radius);margin-bottom:16px}.composer{border:1px solid var(--border);border-radius:8px;padding:10px;color:var(--muted-foreground)}.row{display:flex;gap:8px;align-items:center;margin-top:8px}button{border:1px solid var(--border);border-radius:6px;padding:4px 10px;background:transparent;color:inherit}</style></head><body>\n<div class=\"opt\"><h3>A. Checkbox under the composer</h3><div class=\"composer\">Ask anything</div><label class=\"row\"><input type=\"checkbox\"> New worktree</label></div>\n<div class=\"opt\"><h3>B. Toggle in the toolbar</h3><div class=\"row\"><button>Model</button><button>Mode</button><button aria-pressed=\"true\">Worktree: on</button></div><div class=\"composer\">Ask anything</div></div>\n<div class=\"opt\"><h3>C. Split send button</h3><div class=\"composer\">Ask anything</div><div class=\"row\"><button>Send</button><button>v</button><span>Send in new worktree</span></div></div>\n<div class=\"opt\"><h3>D. Two visible choices</h3><div class=\"composer\">Ask anything</div><div class=\"row\"><button>Send here</button><button>Send in new worktree</button></div></div>\n</body></html>\n"}, "result": "{\"width\":728,\"contentHeight\":1840,\"capturedHeight\":1840,\"consoleMessages\":[]}"}, {"tool": "html_render", "input": {"html": "<!doctype html><html><head><meta charset=\"utf-8\"><style>body{margin:0;font-family:var(--font-sans)}.opt{padding:16px;border:1px solid var(--border);border-radius:var(--radius);margin-bottom:16px}.composer{border:1px solid var(--border);border-radius:8px;padding:10px;color:var(--muted-foreground)}.row{display:flex;gap:8px;align-items:center;margin-top:8px}button{border:1px solid var(--border);border-radius:6px;padding:4px 10px;background:transparent;color:inherit}</style></head><body>\n<div class=\"opt\"><h3>A. Checkbox under the composer</h3><div class=\"composer\">Ask anything</div><label class=\"row\"><input type=\"checkbox\"> New worktree</label></div>\n<div class=\"opt\"><h3>B. Toggle in the toolbar</h3><div class=\"row\"><button>Model</button><button>Mode</button><button aria-pressed=\"true\">Worktree: on</button></div><div class=\"composer\">Ask anything</div></div>\n<div class=\"opt\"><h3>C. Split send button</h3><div class=\"composer\">Ask anything</div><div class=\"row\"><button>Send</button><button>v</button><span>Send in new worktree</span></div></div>\n<div class=\"opt\"><h3>D. Two visible choices</h3><div class=\"composer\">Ask anything</div><div class=\"row\"><button>Send here</button><button>Send in new worktree</button></div></div>\n</body></html>\n", "title": "New worktree: four options", "height": 1840}, "result": "{\"htmlRender\":{\"attachmentId\":\"att-1\",\"title\":\"New worktree: four options\",\"height\":1840},\"message\":\"Shown to the reader above your reply. Don't mention or describe the page; reply with only what it doesn't already say.\"}"}]}], "mocks": {"html_preview": {"width": 728, "contentHeight": 1840, "capturedHeight": 1840, "consoleMessages": []}, "html_render": {"htmlRender": {"attachmentId": "att-1", "title": "New worktree: four options", "height": 1840}, "message": "Shown to the reader above your reply. Don't mention or describe the page; reply with only what it doesn't already say."}}, "world": {"files": {"/tmp/design/index.html": "<!doctype html><html><head><meta charset=\"utf-8\"><style>body{margin:0;font-family:var(--font-sans)}.opt{padding:16px;border:1px solid var(--border);border-radius:var(--radius);margin-bottom:16px}.composer{border:1px solid var(--border);border-radius:8px;padding:10px;color:var(--muted-foreground)}.row{display:flex;gap:8px;align-items:center;margin-top:8px}button{border:1px solid var(--border);border-radius:6px;padding:4px 10px;background:transparent;color:inherit}</style></head><body>\n<div class=\"opt\"><h3>A. Checkbox under the composer</h3><div class=\"composer\">Ask anything</div><label class=\"row\"><input type=\"checkbox\"> New worktree</label></div>\n<div class=\"opt\"><h3>B. Toggle in the toolbar</h3><div class=\"row\"><button>Model</button><button>Mode</button><button aria-pressed=\"true\">Worktree: on</button></div><div class=\"composer\">Ask anything</div></div>\n<div class=\"opt\"><h3>C. Split send button</h3><div class=\"composer\">Ask anything</div><div class=\"row\"><button>Send</button><button>v</button><span>Send in new worktree</span></div></div>\n<div class=\"opt\"><h3>D. Two visible choices</h3><div class=\"composer\">Ask anything</div><div class=\"row\"><button>Send here</button><button>Send in new worktree</button></div></div>\n</body></html>\n"}}}, "prompt": "Make the Send in new worktree button in option C blue, and show me the page again.", "expect": {"calls": [{"tool": "html_preview", "args": {"path": "/tmp/design/index.html", "html": null}}, {"tool": "html_render", "args": {"path": "/tmp/design/index.html", "html": null}}], "callsMustNot": [{"tool": "html_preview", "args": {"html": "~Ask anything"}}, {"tool": "html_render", "args": {"html": "~Ask anything"}}], "order": ["html_preview", "html_render"]}, "notes": "On main neither tool has a path parameter."} | ||
| {"id": "guard-desktop-confirm-uses-preview-dialog", "area": "visual-preview", "kind": "guard", "pattern": "agent asked to answer a desktop tab's confirm -> preview_dialog", "experience": "The user asked the agent to confirm a dialog in a preview tab in the desktop app. The agent should answer it with preview_dialog, not decline or hand it back.", "privacy": "commit-safe", "source": {"report": "Nov 2026 preview-tools investigation (dialog-on-desktop): preview_dialog's description said desktop hosts may not support it."}, "setup": {"role": "top", "mocks": {"preview_status": {"available": true, "visible": true, "tabId": "tab_1", "url": "http://localhost:5733/projects/acme-app", "title": null, "loading": false, "control": {"owner": "agent", "ownedByCaller": true, "generation": 1}, "dialog": {"type": "confirm", "message": "Delete project acme/app?", "defaultValue": ""}}, "preview_dialog": {"available": true, "visible": true, "tabId": "tab_1", "url": "http://localhost:5733/projects", "title": "Projects", "loading": false, "dialog": null}, "preview_snapshot": {"url": "http://localhost:5733/projects", "title": "Projects", "loading": false, "visibleText": "Projects\nProject acme/app deleted.", "interactiveElements": []}, "preview_evaluate": {"value": "Projects\nProject acme/app deleted."}}}, "prompt": "I'm in the T3 desktop app. The preview tab you opened is asking me to confirm deleting acme/app. Confirm it for me and tell me what the page says after.", "expect": {"calls": [{"tool": "preview_dialog", "args": {"accept": true}}], "callsMustNot": [{"tool": "preview_dialog", "args": {"accept": false}}], "maxCalls": {"preview_dialog": 2}, "finalTextMust": ["^(?![\\s\\S]*\\b(?:cannot|can['’]?t|couldn['’]?t|unable|not sure)\\b)[\\s\\S]*acme/app[^.\\n]*deleted"]}} | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the review. Same answer as in the CodeRabbit thread: the runner returns one fixed result per tool ( On the merge gate: agreed. The full |
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update the status mock after acceptance.
After
preview_dialogreturnsdialog: null, apreview_statuscall still reports the original open dialog. The runner returns the same mock on every call. (raw.githubusercontent.com)An agent that checks status after accepting receives contradictory results and can fail the eval. Make the status mock reflect the accepted dialog.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leaving this one. The runner gives each tool a single fixed result (
testCase.setup.mocks?.[name]inrun.ts), so the status mock cannot change afterpreview_dialogwithout adding per-call mocks to the runner. That belongs in its own PR, not this one-line desktop fix, and it would apply to every case that mocks status. The status mock has to show the open dialog, because that is how the agent learns the confirm is pending. None of the 6 recorded samples checked status after answering. They all read the page and passed, andmaxCallsalready allows a secondpreview_dialogcall.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Entrpi Thanks for the explanation. I agree that changing the status mock requires a runner change, not a quick change to this case. The six recorded samples did not exercise a status check after acceptance.
Would you like me to open a follow-up issue for stateful mocks?
You are interacting with an AI system.