fix(workflows): defer parent-chat questions behind focused graph overlay - #1356
Conversation
Add a host inline custom UI focus-state seam and make the workflow graph overlay yield while parent inline questions are active. Preserve synchronous custom UI factory invocation, clean up host state on all exits, and avoid host-state churn for pre-aborted requests.\n\nAdd regression coverage for overlay yield/restore, focus suppression, factory timing, abort behavior, and pre-aborted custom UI calls.\n\nFixes #1353 Assistant-model: GPT-5.5
Implementation NotesTask: fix issue #1353 Running Notes
Iteration 2 Notes
Iteration 3 Notes
Iteration 4 Notes
Iteration 5 Notes
|
|
Code Review — PR #1356 (fix(workflows): yield overlay for host custom ui) Reviewed the focus-arbitration seam, the showExtensionCustom lifecycle changes, the overlay yield/restore logic, the new tests, and the design spec. Overall this is high-quality, well-scoped work: the new host-state API is optional and backward-compatible (workflows feature-detect and default to current behavior), lifecycle cleanup is funneled through a single closed guard, and the host-side state-machine tests (sync throw, async reject, pre-abort, sync factory timing) are excellent. Nice job preserving the synchronous factory contract while still catching sync throws. POTENTIAL ISSUES
NITS
TEST COVERAGE Strong. Host-state tests cover sync-resolve timing, pre-aborted (no factory, no notifications), immediate post-return abort, sync throw, and async reject. Integration tests cover yield/restore-without-remount, user-hidden-not-restored, refocus suppression, stage-chat focus-hold suppression, same-turn no-remount through the real host path, and pre-abort no-flicker. Suggestion: the fuzz/property tests described in section 8 of the spec (random active/inactive vs open/hide/close, asserting depth never goes negative and focus() never fires while host-active) are not present — given the state-machine nature here, a small randomized test would add real value. CONVENTIONS
Nice work overall — none of the above are blockers; items 1 and 3 are most worth a second look. |
Assistant-model: GPT-5.5
Code Review —
|
Assistant-model: GPT-5.5
Review — fix(workflows): yield graph overlay to host inline custom UINice work. This is a well-scoped, genuinely backward-compatible fix for the #1353 freeze. The single-owner focus arbitration seam is the right shape, the ref-counted depth with an idempotent release closure is correct, and the lifecycle edge cases (pre-abort, sync throw, async reject, double-release) are all handled and individually tested. Test coverage is excellent — unit tests on the host side and integration tests driving the real A few mostly-minor observations: 1. Doc comment is now orphaned from its interface (
|
Keep the workflow graph focused and interactive when main-chat inline custom UI appears. Defer the inline UI focus until the graph overlay is hidden, and show a status hint while the question is pending. Update overlay regression tests for issue #1353 and preserve stage-local HIL focus behavior.
Review —
|
Code Review — #1356Reviewed by reading the full diff and surrounding source. Overall a careful, well-tested fix: the ref-counted host-state seam, idempotent releasers, the pre-abort ordering guard, and the additive/optional API surface for backward compatibility are all solid. Test coverage is genuinely thorough (sync throw, async reject, pre-abort no-op, no-remount, defer/restore, hide-focuses-pending, unsubscribe-on-close, user-hidden-not-restored). Nice work. A few things worth addressing before merge. 🔴 PR description contradicts the implementation. The Summary and first two Key Changes bullets describe an auto-yield design — "the overlay now non-destructively yields ( 🟡 🟡 ✅ Things I checked that look correct.
⚪ Minor. I could not run Nothing here is blocking except the PR-description/implementation mismatch, which is documentation-only but important for an accurate merge record. |
…lay (#1356) * fix(workflows): yield overlay for host custom ui Add a host inline custom UI focus-state seam and make the workflow graph overlay yield while parent inline questions are active. Preserve synchronous custom UI factory invocation, clean up host state on all exits, and avoid host-state churn for pre-aborted requests.\n\nAdd regression coverage for overlay yield/restore, focus suppression, factory timing, abort behavior, and pre-aborted custom UI calls.\n\nFixes #1353 Assistant-model: GPT-5.5 * fix(workflows): clean up overlay host question handoff Assistant-model: GPT-5.5 * fix(workflows): restore overlay status after host questions Assistant-model: GPT-5.5 * fix(workflows): defer main chat questions behind graph overlay Keep the workflow graph focused and interactive when main-chat inline custom UI appears. Defer the inline UI focus until the graph overlay is hidden, and show a status hint while the question is pending. Update overlay regression tests for issue #1353 and preserve stage-local HIL focus behavior. * docs: finalize issue 1353 overlay focus spec
Summary
Fixes a TUI freeze (#1353) where the full-screen workflow graph overlay became input-dead when the parent/main-chat agent opened `ask_user_question` via `ctx.ui.custom()`. The fix adopts a graph-overlay-first UX: the overlay keeps keyboard focus while a parent question is pending, a clear status hint points the user to exit/hide the graph to answer, and focus transfers to the pending question the moment the user hides or exits the graph.
Key Changes
Host focus-deferral seam (
packages/coding-agent)HostCustomUiStateinterface (blockingInlineCustomUiDepth,blockingInlineCustomUiActive,blockingInlineCustomUiFocusDeferred) andHostCustomUiStateListenertype toExtensionUIContextas optional, additive APIsblockingInlineCustomUiDepthtracker inInteractiveModewith idempotent release viabeginHostInlineCustomUi()deferInlineCustomUiFocusoption toctx.ui.custom(): while an overlay holds the deferral, inline custom UI can mount but its focus is stored aspendingInlineCustomUiFocus—setFocus()is called only when the deferral is releasedctx.ui.custom()calls: abort signal checked beforebeginHostInlineCustomUi()— no host token acquired, no state notification emitted, no factory invokedtry/catch+Promise.resolve(factoryResult), routing sync throws through the normal cleanup pathgetHostCustomUiState,onHostCustomUiStateChange, andfocusHostInlineCustomUionExtensionUIContextGraph overlay keeps focus (
packages/workflows)WorkflowGraphOverlayAdapteropens withdeferInlineCustomUiFocus: true— the graph holds keyboard focus even when a parent/main-chat question mounts behind itonHostCustomUiStateChangeto display/clear a"Main chat needs input — exit graph to answer."status hint (pi-workflows:main-chat-input) while the question is pending; does not auto-hide, unfocus, or remount the overlayclose()), the deferral releases andfocusHostInlineCustomUi()transfers focus to the pending questionWORKFLOW_STATUS_KEYconstant intoworkflow-status.tsto eliminate duplication betweenWorkflowAttachPaneandoverlay-adaptergetHostCustomUiState,onHostCustomUiStateChange, andfocusHostInlineCustomUionPiUISurfaceandOverlayUISurface(optional, additive)Test coverage
ctx.ui.custom()returns; immediate abort after return does not allow a deferred factory runopen()calls do not remount; pre-aborted host UI causes zero overlaysetHidden/focus/unfocuscallsBackwards Compatibility
All changes are additive.
getHostCustomUiState,onHostCustomUiStateChange, anddeferInlineCustomUiFocusare optional onExtensionUIContext,PiUISurface, andOverlayUISurface— older/minimal hosts that omit them continue to compile and run unchanged. The workflow overlay defaults toblockingInlineCustomUiActive === falsewhen the host does not expose the observer.Validation
Fixes #1353