You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Activate target windows and verify ui focus outcomes - #881
Corrects ui focus reporting success for a control in an inactive window. The command requests activation of the selected control's top-level window when needed, focuses that control, and confirms both strict foreground ownership and live keyboard focus before reporting success.
Focus confirmation allows up to 500 ms for asynchronous UI Automation providers, observing the same retained control at 50 ms intervals. It does not repeat activation or SetFocus. Every observation rechecks cancellation, target/root/process validity, and strict foreground ownership before and after the provider read.
Activation refusal, lost foreground, unconfirmed focus, stale targets, and provider failures return nonzero. Existing desktop coordination, explicit-window isolation, and wrong-window input guards remain in place. An owned popup is not accepted as foreground for a control in its owner. The selector is accurately required in help, generated schema, and npm types.
Usage Example
winapp ui focus SearchTasksBox -a Daylight --on sandbox --json
Before: the original Sandbox reproduction returned success and HasKeyboardFocus=True while Daylight's main window remained inactive. An immediate one-shot confirmation could also report failure before a provider published a successful focus change.
After: success requires the selected control's window to be foreground and that control to confirm keyboard focus within the bounded observation period. Refused activation returns foreground_not_target; unconfirmed keyboard focus returns focus_not_acquired. Check for a blocking dialog and activate the intended window manually before retrying. Locked sessions receive unlock-and-retry guidance. There is no forced activation or local-target fallback.
Textual outcome evidence is used; no interaction recording was captured. Before the asynchronous polling change, a guest-only fixture verified native foreground HWND and actual control focus together:
Scenario
Observed outcome
Already active target
Exit 0, target foreground and control focused
Eligible inactive target with stable decoy foreground
Exit 0, target became foreground and control focused
Disabled owner behind owned popup
Nonzero foreground_not_target
Selected control inside popup
Exit 0, popup foreground and its control focused
Focus handler activates decoy
Nonzero foreground_not_target
These results identify source 1f9fe8331dfc0fefd07b9545c13edab4fed8e619, ARM64 SHA256 81C0F7E2FDF0EA8B6F07C6FB3B38B1EDE27B3765461C7076AD8674C7494B1064, not the later polling implementation. Original Daylight WinUI control focus and explicit-window refusal were validated on earlier source 27eda8ff3193ee133284cd94d663c183bfd97eca (ARM64 SHA256 6D321BDEB62E154B1D943BCBEE01349F4BB1D64E33FCE2968365D98EC5B815AA).
Additional Notes
The asynchronous-focus and activation-delay regressions were run failing before their fixes. Focused ownership/encoder tests pass 54/54; the broader command/ownership/routing/encoder suite passes 855, with one opt-in recording test skipped. Local NativeAOT x64/ARM64 and npm/NuGet builds passed with -SkipTests -SkipMsix. Full repository validation also passed on the current integration, including both CLI shards, UI Automation, UI end-to-end tests, auxiliary checks, documentation, and all sample jobs.
Live timing-validation limitation: Notepad was absent from the allocated guest. Two bounded custom-provider fixture attempts against polling candidate 708ed58bafc85e0f711dc8e175cce4c3cd72d9c6 (ARM64 SHA256 66B7EC6C8F4C0DF4335ECB3942DA5338E6FC50869EFD1697130FAC9A0DBA60BA) did not faithfully exercise delayed focus: UIA focused the native host before the provider callback. Those attempts are explicitly not counted as live confirmation of the timing fix. The fixture windows and exact test artifacts were cleaned; no host UI, installation, user-data edits, or guest reset was used. The reported Notepad scenario was not independently reproduced by these fixtures.
Windows may legitimately refuse activation; the fix detects refusal rather than bypassing policy.
Copilot reviewed the current head 27eda8ff3193ee133284cd94d663c183bfd97eca and generated no inline findings. The final live-smoke item mentioned in the review summary is now complete (details and exact final binary hash are in the PR validation section): all five controlled Sandbox outcomes, original Daylight control focus, and unrelated explicit-window refusal passed on final bytes. Repository CI is still running; this PR remains draft until it finishes.
Other suites are reflected in the overall validation status above.
❌ 7832 passed, 1 failed, 37 skipped out of 7870 tests in 1175.3s (+38 tests, -10.1s vs. baseline)
Test Coverage
✅ 86% line coverage, 80.7% branch coverage · ✅ +0.4% vs. baseline
CLI Startup Time
63ms median (x64, winapp --version) · ✅ +7ms vs. baseline
Try This Build
Installs the MSIX for your architecture, replacing any previously installed build. Needs the GitHub CLI — the command offers to install it and sign you in if it is missing.
State: agent-preparing Assessed: head 0ea3d1536313f544a5627eb283656a2eff98dff7 against main@3520ee6ff3525c9af1eb5b279930225eae8e8876; normal merges, no rewritten history. Existing non-draft state preserved. Evidence: Current runtime code passed 779 command/guard/routing tests (one opt-in recording skip), 311 npm tests and a subsequent 40/40 focus/ownership run at 5f6b6e09. NativeAOT x64/ARM64/npm/NuGet build passed at 20cd2edb with -SkipTests -SkipMsix; subsequent changes are independently verified comments and a single shipped Markdown line. Plugin validation and diff-check pass. All checks at 8c9e09ca subsequently passed, but main advanced with screenshot/empty-value fixes (#884/#878), so strict-base integration was normally updated. New current-head command/ownership/routing suite: 826 passed, one opt-in recording skip. Fresh local NativeAOT/package build and current-head CI/review are running. No old run is counted as the required current-head gate. Review: Independent complete feature coverage at afdf9c28, message-fix delta at 742f0ae4, and each subsequent merge/docs delta through current 8c9e09ca completed with no remaining actionable findings. Before the last one-line Markdown correction, all 15 feature files had exactly identical added/deleted text across merged ranges (stable patch ID bf012d5c4dff6c41d1190e5e9e540dec19869711); final one-line correction separately reviewed. Static precheck covered four production C# files plus one TestSupport file, with no obvious violations (not a substitute for CodeQL). Feedback disposition: Locked-session wording fixed with a red/green regression and replied/resolved. Shipped Copilot reference corrected and replied/resolved. The intervening top-level popup-PID allegation was refuted by 12 passing targeted tests and live owner-chain analysis, independently reassessed; test/guard unchanged. Current-head Copilot completed with no findings and acknowledges resolved feedback. This is technical review evidence, not formal human approval. Limits: Previous Sandbox bytes (27eda8ff, ARM64 SHA256 6D321BDEB62E154B1D943BCBEE01349F4BB1D64E33FCE2968365D98EC5B815AA) are historical feature evidence only; no fresh host/Sandbox UI was run in this update. Current-head CI UI E2E is distinct evidence. Main e9ab2ea introduced unrelated certInfo generated-description drift in npm docs/type comments; removed only our self-generated unrelated hunks with parent agreement. Required CI gates are not waived. Pending: narrow independent latest-merge assessment, new current-head build/review/strict required build-and-package check (GitHub Actions integration 15368) and remaining integration checks, then fresh head/base/discussion/policy/conflict assessment. Human/codeowner/last-push approvals remain separate and pending. Blocker / owner / next step: No external intervention known; awaiting CI normally. Worktree clean; no unresolved actionable findings known. Resume: Same-session 10-minute automation active. New pr-lifecycle/code-quality instructions were read from merged local files because the old registry could not load pr-lifecycle. This is the only lifecycle comment. Updated user policy: Lifecycle ready label is now ready-for-review (renamed globally). agent-blocked only means feedback/CI cannot be completed or author input is needed; ordinary waiting for re-review after addressed feedback does not block ready when other technical gates pass. Current CI remains pending, so this PR stays agent-preparing. Fresh bounded live proof: On 1f9fe833 ARM64 SHA256 81C0F7E2FDF0EA8B6F07C6FB3B38B1EDE27B3765461C7076AD8674C7494B1064 (host/guest match), a new parent-allocated guest-only WinForms fixture verified all five native-foreground/control-focus scenarios: active/inactive success, modal-owner refusal, selected-popup success, foreground-loss refusal. Exact test artifacts cleaned and slot released, guest healthy. Daylight absent: no fresh WinUI claim. Main then advanced to publisher-DN/README changes; current cd6f8190 normally integrates them, 40/40 focus/ownership tests pass, fresh CI/review and mergerange review pending. No runtime focus changes after tested bytes. Latest strict-base update: Main added unrelated control-search sample fixes; normal merge 0ea3d153 preserves focus code. Current-head focus/ownership tests40/40passed; prior publisher-integration NativeAOT/packages passed at cd6f8190. Fresh current-head CI/Copilot and narrow independent range-equivalence pending; still preparing.
The popup success test configures the popup as another process.ConfigureVerifiedFocus targets PID 1234, but this line makes HWND 5000 report PID 8888. ConfirmTarget() therefore classifies the handle as recycled and returns stale_element, so the test cannot reach its expected success result. Keep the popup in the target process (PID 1234) so this test exercises popup foreground handling rather than the stale-target guard.
Responding to the current-head Copilot review's “Previously missed” popup PID note: this is intentional cross-process owned-dialog coverage, not a failing fixture. In Focus_ControlInOwnedPopup_VerifiesPopupNotMainWindow, HWND 5000 has PID 8888 and owner HWND 4242; that live owner has the requested PID 1234. DesktopTargetValidation.ClassifyTargetWindow rejects a mismatched PID only when IsOwnedByExpectedProcess is also false (lines 103–105). Its owner-chain check validates the live owner's PID (lines 160–173). This is required for app-owned system dialogs running in a different process and does not broaden the strict foreground window check.
On current head 742f0ae4d341de02a97f5f984a76eb700131b40c, reran:
dotnet run --project src\winapp-CLI\WinApp.Cli.Tests\WinApp.Cli.Tests.csproj -c Debug --no-build -- --filter "FullyQualifiedName~Focus_ControlInOwnedPopup_VerifiesPopupNotMainWindow|FullyQualifiedName~DesktopTargetValidation"
12 passed, 0 failed, 0 skipped. The broader current-source suite also passed 779 with one unrelated opt-in skip. Changing the popup to PID 1234 would remove useful cross-process ownership coverage, so it is intentionally unchanged. Independent reassessment of this specific note is also being recorded in the lifecycle status. The prior locked-session wording finding was fixed and its inline thread answered/resolved.
Cover activation settle ordering with an injected-delay regression and remove an obsolete selector comment.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Update documentation for focus activation and failure behavior
docs/ui-automation.md:811
The canonical CLI command list still says focus only “Move keyboard focus” in docs/usage.md:2177, even though this change now activates the window, verifies foreground ownership and keyboard focus, and can fail with recovery-required errors. Users who start from docs/usage.md will therefore get an outdated contract; update that hand-authored list together with this focus reference.
Nikola Metulev (nmetulev)
added
agent-blocked
Agent cannot address feedback or fix CI without help, or needs author input
agent-preparing
Agent is addressing feedback or completing required validation and CI
and removed
agent-preparing
Agent is addressing feedback or completing required validation and CI
agent-blocked
Agent cannot address feedback or fix CI without help, or needs author input
labels
Sep 22, 2026
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
ready-for-reviewAgent work and technical checks complete; awaiting review or re-review, not approval or merge
3 participants
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.
Description
Corrects
ui focusreporting success for a control in an inactive window. The command requests activation of the selected control's top-level window when needed, focuses that control, and confirms both strict foreground ownership and live keyboard focus before reporting success.Focus confirmation allows up to 500 ms for asynchronous UI Automation providers, observing the same retained control at 50 ms intervals. It does not repeat activation or
SetFocus. Every observation rechecks cancellation, target/root/process validity, and strict foreground ownership before and after the provider read.Activation refusal, lost foreground, unconfirmed focus, stale targets, and provider failures return nonzero. Existing desktop coordination, explicit-window isolation, and wrong-window input guards remain in place. An owned popup is not accepted as foreground for a control in its owner. The selector is accurately required in help, generated schema, and npm types.
Usage Example
Before: the original Sandbox reproduction returned success and
HasKeyboardFocus=Truewhile Daylight's main window remained inactive. An immediate one-shot confirmation could also report failure before a provider published a successful focus change.After: success requires the selected control's window to be foreground and that control to confirm keyboard focus within the bounded observation period. Refused activation returns
foreground_not_target; unconfirmed keyboard focus returnsfocus_not_acquired. Check for a blocking dialog and activate the intended window manually before retrying. Locked sessions receive unlock-and-retry guidance. There is no forced activation or local-target fallback.Related Issue
Fixes #877.
Type of Change
Checklist
docs/ui-automation.md, generated CLI/npm references, shipped skill, and Copilot agent reference updatedScreenshots / Demo
Textual outcome evidence is used; no interaction recording was captured. Before the asynchronous polling change, a guest-only fixture verified native foreground HWND and actual control focus together:
foreground_not_targetforeground_not_targetThese results identify source
1f9fe8331dfc0fefd07b9545c13edab4fed8e619, ARM64 SHA25681C0F7E2FDF0EA8B6F07C6FB3B38B1EDE27B3765461C7076AD8674C7494B1064, not the later polling implementation. Original Daylight WinUI control focus and explicit-window refusal were validated on earlier source27eda8ff3193ee133284cd94d663c183bfd97eca(ARM64 SHA2566D321BDEB62E154B1D943BCBEE01349F4BB1D64E33FCE2968365D98EC5B815AA).Additional Notes
The asynchronous-focus and activation-delay regressions were run failing before their fixes. Focused ownership/encoder tests pass 54/54; the broader command/ownership/routing/encoder suite passes 855, with one opt-in recording test skipped. Local NativeAOT x64/ARM64 and npm/NuGet builds passed with
-SkipTests -SkipMsix. Full repository validation also passed on the current integration, including both CLI shards, UI Automation, UI end-to-end tests, auxiliary checks, documentation, and all sample jobs.Live timing-validation limitation: Notepad was absent from the allocated guest. Two bounded custom-provider fixture attempts against polling candidate
708ed58bafc85e0f711dc8e175cce4c3cd72d9c6(ARM64 SHA25666B7EC6C8F4C0DF4335ECB3942DA5338E6FC50869EFD1697130FAC9A0DBA60BA) did not faithfully exercise delayed focus: UIA focused the native host before the provider callback. Those attempts are explicitly not counted as live confirmation of the timing fix. The fixture windows and exact test artifacts were cleaned; no host UI, installation, user-data edits, or guest reset was used. The reported Notepad scenario was not independently reproduced by these fixtures.Windows may legitimately refuse activation; the fix detects refusal rather than bypassing policy.