agent-desktop/probes/windows/captures/02-cache-timing/cache-timing.json
Lahfir e6f4ecc14e feat: dump notepad, explorer, settings, and obsidian trees with real geometry
Targets launch restored-but-not-activated, so bounds are real: 100/95.9/100/99.2
percent non-empty across the four dumps, with focus asserted identical before
and after all seven walks. Settings resolves through ApplicationFrameWindow to
its CoreWindow's SystemSettings pid, because the frame window belongs to
ApplicationFrameHost and no predicate in either script keys on a display name.

Minimizing a window does not shrink its tree -- the node count is identical at
3 -- but it degenerates geometry in two different shapes. Only the top-level
window reports an empty rect; its descendants report real dimensions anchored
at -32000, and every node still reports IsOffscreen as false. An occlusion gate
testing emptiness or IsOffscreen alone would accept both as visible.

Chromium activation is not a client-stack difference. The managed client takes
Obsidian from 9 to 119 nodes across an 8 second settle with no flag, matching
the COM shim's shape. What suppresses it is occlusion: an earlier revision that
left other targets restored on top held the count at first contact for a 16
second instrumented hold across three runs. The mechanism is not isolated, so
it is recorded as a probe-placement hazard rather than a product claim.

The managed timing cross-check reproduces the COM stack's direction but not its
magnitude, and on Notepad caching is a pessimization at 0.21x, because
client-side providers run in the client process and the cache is pure overhead.
2026-07-26 17:57:44 -06:00

77 lines
No EOL
6.2 KiB
JSON

{
"Probe": "02-cache-timing",
"CapturedAtUtc": "2026-07-26T23:51:52Z",
"Stack": "managed",
"Scope": "api-contract",
"Authority": "NON-AUTHORITATIVE managed cross-check (KTD1). The authoritative CacheRequest measurement is U7 08-uia3-com/cache-timing.json on the UIA3 COM stack — the stack the Rust adapter uses.",
"Method": "per-property pass calls FindAll(Subtree) then GetCurrentPropertyValue for each of 8 properties on each node; cached pass activates a CacheRequest carrying the same 8 properties over the same TreeScope.Subtree with the ControlView tree filter, then reads GetCachedPropertyValue. One discarded warm-up pass of each precedes the measured repetitions.",
"Targets": [
{
"Label": "notepad",
"Stack": "managed",
"NodeCount": 3,
"CachedNodeCount": 3,
"PropertiesPerNode": 8,
"PropertyNames": "Name,ControlType,AutomationId,ClassName,BoundingRectangle,IsEnabled,IsOffscreen,FrameworkId",
"Repetitions": 5,
"PropertyReadsPerRepetition": 24,
"CachedPropertyReadsPerRepetition": 24,
"AutomationElementMode": "Full (CacheRequest default) — matched to the per-property pass so the delta is caching, not element-mode weight",
"timingPerPropertyTotalMs": 8.3474,
"timingCachedTotalMs": 14.485,
"timingPerPropertyFindMs": 6.1763,
"timingCachedFindMs": 14.1648,
"timingPerPropertyReadMs": 2.1711,
"timingCachedReadMs": 0.3202,
"timingOverallMultiplier": 0.5763,
"timingFindPhaseMultiplier": 0.436,
"timingReadPhaseMultiplier": 6.7804
},
{
"Label": "explorer",
"Stack": "managed",
"NodeCount": 196,
"CachedNodeCount": 196,
"PropertiesPerNode": 8,
"PropertyNames": "Name,ControlType,AutomationId,ClassName,BoundingRectangle,IsEnabled,IsOffscreen,FrameworkId",
"Repetitions": 5,
"PropertyReadsPerRepetition": 1568,
"CachedPropertyReadsPerRepetition": 1568,
"AutomationElementMode": "Full (CacheRequest default) — matched to the per-property pass so the delta is caching, not element-mode weight",
"timingPerPropertyTotalMs": 3825.822,
"timingCachedTotalMs": 2950.4104,
"timingPerPropertyFindMs": 1101.6988,
"timingCachedFindMs": 2943.8284,
"timingPerPropertyReadMs": 2724.1232,
"timingCachedReadMs": 6.582,
"timingOverallMultiplier": 1.2967,
"timingFindPhaseMultiplier": 0.3742,
"timingReadPhaseMultiplier": 413.8747
}
],
"Uia3ComReference": {
"Source": "probes/windows/captures/08-uia3-com/cache-timing.json",
"Stack": "uia3-com",
"Target": "explorer folder window over %WINDIR%\\System32",
"NodeCount": 220,
"timingComOverallMultiplier": 2.6942,
"timingComReadPhaseMultiplier": 298.158,
"ComFindPhaseFinding": "the cache-building find phase is SLOWER than the uncached find (180.25 ms vs 117.38 ms): building the cache costs time up front and pays it back on reads",
"ClaimVerdict": "below-3x-claim against the phases.md 3-5x batched-read claim"
},
"Comparison": "stated in ComparisonToAuthoritative below; the managed numbers are reported to show whether the managed stack reproduces the COM shape, not to replace it",
"Interpretation": {
"Notepad": "NEW-EDGE: batching is a PESSIMIZATION here — timingOverallMultiplier is below 1.0 on classic Notepad. Notepad is a plain Win32 control tree served by UIAutomationClientsideProviders, which run inside the client process, so an uncached property read costs no cross-process RPC and the CacheRequest adds pure setup cost. A Windows adapter that batches unconditionally will make simple Win32 windows slower.",
"Explorer": "on a genuinely out-of-process provider the managed stack reproduces the COM shape exactly: timingFindPhaseMultiplier below 1.0 (building the cache makes the find phase slower) and timingReadPhaseMultiplier in the hundreds, leaving a modest overall multiplier.",
"Headline": "U7 uia3-com stays authoritative at 2.6942x overall and 298.158x on the read phase. The managed cross-check reproduces the direction and the phase split, not the magnitude, and adds the client-side-provider caveat above."
},
"ComparisonToAuthoritative": {
"ComOverallMultiplier": 2.6942,
"timingManagedOverallMultiplier": 1.2967,
"ComReadPhaseMultiplier": 298.158,
"timingManagedReadPhaseMultiplier": 413.8747,
"timingManagedFindPhaseMultiplier": 0.3742,
"SameTargetShape": "both measure an explorer folder window over %WINDIR%\\System32 with the same 8 properties and 5 repetitions; managed NodeCount differs from the COM NodeCount because the two client stacks do not present the same control view (KTD1)",
"Note": "a find-phase multiplier below 1.0 reproduces the COM finding that cache-building makes the find phase slower; the payback is entirely in the read phase"
}
}