Repository navigation
feat: add the windows observation read path to the windows adapter - #119
Conversation
Ten units grounded in the probe corpus, the shipped walker, and the macOS reference implementation, with every load-bearing claim carrying a file or ledger citation. Research disproved five statements in docs/phases.md §2.4 - the observe_tree seam, the nonexistent HtmlClass property, the display scale field name, and two macOS-pattern claims - and the plan carries their corrections as its own unit rather than planning around them. A five-persona document review ran before commit and its findings are folded in: the Windows interaction-lease override core's activation loop requires, the descriptor group rehomed inside NodePresentation because both host structs sit at the seven-field cap, window-title corroboration against same-process HWND reuse, a committed home for the force-activation flag on an at-cap request struct, and three new probe questions (AriaRole passthrough, settle latency, the integrity boundary).
The round hunted what the first round's fixes missed and what they broke. Real defects closed: the surface-advertisement gap that made U6's own verification unpassable (supported_surfaces defaults empty and the crate pins it); the title corroboration rescoped to the macOS split after the adversarial pass showed a hard title check breaks drill-down on any legitimately retitled window; the still-thin guidance restructured so it can actually escape core's activation loop instead of decaying to a bare timeout; the settle-latency branch given a real lever (--timeout-ms) instead of a recommendation with no knob; activation gated to Window-rooted walks; trace redaction extended to the two descriptor keys no existing token covers; and four edit-introduced contradictions reconciled (split-seam staleness twice, the U2 cap attribution, the wrapper value pin, the corrections count).
… (sub-phase 2.4 U4)
…ettle (sub-phase 2.4 U7)
… (sub-phase 2.4 U9 dogfood)
… (sub-phase 2.4 U10)
… inventory identity (review fixes)
…ags and descriptor fields
Greptile SummaryThe PR implements the Windows observation read path, including live inventories, strict ref resolution, Chromium settling, surface discovery, and optional descriptor evidence.
Confidence Score: 4/5The PR is not yet safe to merge because stored references can resolve unintended elements or recycled windows, and Chromium activation state can leak across FFI snapshots. Evidence-free refs still accept a sole unrelated candidate, stored HWND resolution does not verify the handle's current owner, and the adapter-wide activation flag causes a reused FFI adapter to treat a new Chromium target as already settled. Files Needing Attention: crates/windows/src/tree/resolve.rs, crates/windows/src/tree/resolve_match.rs, crates/windows/src/system/window_identity.rs, crates/windows/src/adapter.rs, crates/windows/src/tree/observe.rs
|
| Filename | Overview |
|---|---|
| crates/windows/src/tree/resolve.rs | Implements stored-ref root verification, bounded traversal, candidate selection, and strict-resolution error handling. |
| crates/windows/src/tree/resolve_match.rs | Defines candidate identity matching and stale or ambiguous resolution errors. |
| crates/windows/src/adapter.rs | Connects Windows observation and inventory operations while retaining renderer-activation state on the adapter instance. |
| crates/windows/src/tree/observe.rs | Builds Windows observed trees and coordinates Chromium shell detection, settle behavior, and root re-verification. |
| crates/windows/src/system/window_identity.rs | Introduces strict and stored window identity verification using HWND, PID, process-generation, and title evidence. |
| crates/core/src/commands/snapshot.rs | Threads timeout and renderer-accessibility options into fresh snapshots and ref-based drill-down. |
| crates/core/src/node_descriptor.rs | Adds optional serializable descriptor fields while preserving the legacy output shape when absent. |
Sequence Diagram
sequenceDiagram
participant CLI
participant Core
participant Win as WindowsAdapter
participant UIA
CLI->>Core: snapshot options
Core->>Win: observe_tree(root, request)
Win->>UIA: enumerate and read properties
alt Chromium shell requires activation
Win-->>Core: activation required
Core->>Win: activate renderer accessibility
Core->>Win: retry observation
Win->>UIA: settle and walk tree
end
Win-->>Core: ObservedTree
Core-->>CLI: reffed snapshot JSON
Reviews (3): Last reviewed commit: "fix: strip remaining plan ids and versio..." | Re-trigger Greptile
| if !crate::system::process_identity::matches_instance(entry.process.pid, instance)? { | ||
| return Err(stale_ref_error(entry)); | ||
| } | ||
| } else { | ||
| return Err(stale_ref_error(entry)); |
There was a problem hiding this comment.
Recycled HWND crosses window identity
When a source window closes and Windows recycles its HWND to another window in the same still-running process, this check accepts the unchanged process-generation token and resolves the current UIA root for the recycled handle, causing drill-down or a later action to target a matching element in the wrong window instead of returning STALE_REF.
Context Used: CLAUDE.md (source)
Twelve findings survived independent validation out of fourteen raised by a nine-reviewer pass; every one is closed here with a test that fails when its fix is reverted. The Chromium settle is deadline-bounded through core's retry loop instead of dying after one 50ms attempt, so --timeout-ms actually extends the cold settle A16-11 measured; activation state is keyed per target process instead of one adapter-global flag; the force path and Element-rooted walks pass the same liveness re-verification every other complete claim does. ObservationRequest returns to seven fields with skeleton folded into ObservationMode, and the two six-parameter snapshot entry points merge into five-parameter forms over small target structs. The boundary children_count read gets its own 25ms budget mirroring the macOS child-read budget; the resolver's ambiguity tie-break is extracted pure and pinned (two identical candidates stay ambiguous, a sole hash match resolves); name corroboration is gated on core's mutable-value-role rule; the ToolHelp process lookup is one shared helper; EnumWindows failures and failed DPI reads are errors instead of fabricated defaults. The CI probe build failure that silently uploaded no observation captures is fixed and now fails loudly under the ci label, and the A16-7 row records honestly that the flat WALK_SET decision awaits its second environment. The phase-reference gate learns bare unit tokens and backtick-quoted literals, with both added to its committed self-test; docs/phases.md gains the four deferral write-backs review found missing.
|
Too many files changed for review. ( Bypass the limit by tagging |
…ond environment The rebuilt binary was driven against all five real targets again after the review fixes. The settle knob was proven live with a 50ms-versus-30s contrast at the same cold instant, the force path failed closed on a dead window in 198ms, the boundary-count budget stamped counts on real Explorer and WPF boundaries, and the killed-fixture drill-down returned STALE_REF in 35ms with no application text. Depth-50 Chromium richness improved 57 to 166 refs. The fixed CI probe build uploaded the missing captures, so the A16-7 flat-set decision now rests on two environments (1.14x Win32, 1.24x WPF on the hosted runner).
…fixture line The must_pass fixture deliberately carries a backtick-quoted literal to prove the gate does not false-fire on one; the backticks inside a single-quoted shell string trip the same informational lint the two awk programs already suppress, and for the same reason: the quoting is load-bearing.
… settle, drill-down (#119) Lands the full Windows read path: observe_tree wired to the shipped walker with honest completeness (partial trees, liveness-verified complete claims, budgeted boundary counts), HWND-first window identity corroborated by a process-generation token, list-windows/list-apps/list-displays inventories, focused-window composition, the four P2-O8 descriptor fields as absent-by-default cross-platform schema, Chromium detection with a deadline-bounded activation settle (--timeout-ms, --force-electron-a11y), the provenance-gated web-wrapper depth-skip, and fail-closed drill-down resolution. Verified by a nine-reviewer code review (twelve validated findings fixed with revert-sensitive tests, two rejected with evidence), a two-wave post-fix dogfood against real applications including cold-start Chromium, and a two-environment cost corroboration (A16 ledger).
… settle, drill-down (#119) Lands the full Windows read path: observe_tree wired to the shipped walker with honest completeness (partial trees, liveness-verified complete claims, budgeted boundary counts), HWND-first window identity corroborated by a process-generation token, list-windows/list-apps/list-displays inventories, focused-window composition, the four P2-O8 descriptor fields as absent-by-default cross-platform schema, Chromium detection with a deadline-bounded activation settle (--timeout-ms, --force-electron-a11y), the provenance-gated web-wrapper depth-skip, and fail-closed drill-down resolution. Verified by a nine-reviewer code review (twelve validated findings fixed with revert-sensitive tests, two rejected with evidence), a two-wave post-fix dogfood against real applications including cold-start Chromium, and a two-environment cost corroboration (A16 ledger).
Summary
An agent can now observe and identify real Windows applications:
snapshotreturns reffed trees with honest completeness,list-windows/list-apps/list-displays/focused_windoware live with recycling-safe identity, and the dense-app cases actually work — Chromium/Electron trees are detected and settled, transparent web wrappers stop eating depth budget, drill-down (snapshot --root @ref) and skeleton (--skeleton) resolve fail-closed, and a Chromium modal is reachable as a sheet surface.This sub-phase (2.4 of
docs/phases.md) lands the full read path on Windows — the first timesnapshotreturns refs on this platform. It also ships the four P2-O8 evidence fields (subrole,role_description,placeholder,dom_classes) as optional core schema with Windows producers from named, measured sources — they were previously absent on every platform — and the--timeout-msknob for cold Chromium settles, per probe A16-11.Related issues
Related: none. Sub-phase branch per
docs/phases.mdPlatform Delivery Model — this PR targetsfeat/windows-adapter, notmain.Type of change
feat:— new featureHow tested
Read-path correctness was established by running it, not by the test suite alone (the sub-phase's carried principle): the release binary was pointed at Notepad, Explorer, the WinForms and WPF fixtures, and Obsidian (Chromium/Electron), and the JSON read and judged. The durable record is
docs/dogfood-reports/2026-08-01-feat-windows-2-4-observation-dogfood.md. Verified live:snapshotreturns reffed trees on all four UI stacks and Chromium;--skeletonclamps to depth 3 with reffed boundary counts;--root @refre-resolves and drills down; a missing ref returnsSTALE_REF.list-windows/list-apps/list-displaysreturn real inventories with corroborated process-generation tokens; the single 96-DPI display readsscale1.0.switchnow resolves (fixtureToggleButton);cellstill does not on WPFDataGridcells (they presentControlType.Custom), with the measured reason recorded for the reader.Gates run on the dev box (Windows Server 2019):
cargo fmt --all -- --checkcargo clippy --locked -p agent-desktop-core -p agent-desktop-windows -p agent-desktop -p agent-desktop-ffi --all-targets -- -D warningscargo test --locked -p agent-desktop-core -p agent-desktop-windows --lib— 969 core, 299 windowscargo test --locked -p agent-desktopcargo test --locked -p agent-desktop-ffi --testscargo check --locked -p agent-desktop-windows --all-targets --target x86_64-unknown-linux-gnu(cross-compile lane)probes/windows/13-ledger-check.ps1(hunk index bijective)bash tests/e2e/run.shis macOS-only and not runnable off-macOS; the Windows read path was instead exercised by driving the release binary directly (the dogfood report).The macOS lane runs on this PR's CI and is the detector for byte-identical goldens after the core schema change — the U2 unit asserts
tests/fixtures/is untouched.Checklist
feat:— lowercase, imperative, no capital after colon)cargo fmtandcargo clippy -D warningsare cleanunwrap()in non-test code (adenylint on the crate enforces it)///doc-comments on public itemsskills/agent-desktop/references/commands-observation.md)docs/phases.mdcorrected in place by U10;CONCEPTS.mdgained Window Identity and Web Wrapper)Session-settled decisions carried from planning: 2.4 planned as
docs/phases.mddefines it with contradictions corrected rather than planned around (user-directed); correctness established by running it rather than unit tests alone (user-directed); no test asserts a machine-specific or application-specific fact (user-directed). The plan'scarries_target_textclassification ofLocalizedControlType/AriaRoleas provider vocabulary (not withheld on secure fields) is the settled decision the reviewer flag on that point was reconciled against; theAriaRoleauthor-text verbatim sub-question is recorded as a WebView2-capable-environment follow-up in probe A16-6.Known Residuals
Recorded from code review, accepted as low-risk with rationale:
cellrefinement on WPF grid cells is unsettled (P2, design). WPFDataGridcells carryGridItem/TableItembut presentControlType.Custom(resolvesunknown). WhetherCustom+ those patterns should refine tocellis a decision recorded in the dogfood report for the reader; not changed here because it involves the role-map contract, not the read path.AriaRole/LocalizedControlTypeare not withheld on secure fields (P3, design, reconciled). The plan classifies them as provider vocabulary (carries_target_text=false, backed by A16-6's measurement that author tokens did not pass through), so they are not inVALUE_BEARING. A hypothetical hostile provider placing text inAriaRolewould bypass the secure gate; the plan's settled classification stands and this is documented.STALE_REFfor a bounded search. Fails closed (never a wrong target); low probability (10k siblings on one parent). Left as-is for the 2.4/2.5 boundary.list_apps/list_windowsidentity corroboration tightened but differs from macOS's full two-source merge (P3). Windows now fails the inventory when the window token disagrees with a fresh process read; macOS additionally merges a full app inventory. Windows 2.4's shape is documented as the shipped contract.Post-Deploy Monitoring & Validation
No production/runtime impact —
agent-desktopis a stateless CLI/adapter library; there is no server, daemon, or always-on service. The observable runtime surface (CLI JSON output) is covered by the unit/live tests and the committed dogfood report. No additional operational monitoring required; the macOS CI lane on this PR is the only always-running gate and it verifies the golden fixtures remain byte-identical.