From 90fd24b4ed25f026d1a20f3177db7d221e2eb5df Mon Sep 17 00:00:00 2001
From: Lahfir <70215676+lahfir@users.noreply.github.com>
Date: Sun, 26 Jul 2026 19:38:46 -0600
Subject: [PATCH] feat: probe uipi, dpi awareness, and chromium activation
UIPI is measured across a boundary that actually exists. This box runs the
built-in Administrator at High with Admin Approval Mode off, so RunAs yields
High-vs-High and runas /trustlevel leaves the mandatory label untouched; the
probe manufactures a real Medium token instead and refuses to record a verdict
unless the worker reads back S-1-16-8192. Both arms run identical code against
the same High Notepad, so the High arm is a control proving the injection
works. UIA reads succeed from Medium; SendInput does not land, and the two arms
return the identical success code -- only re-reading the target separates them.
PostMessage is denied with error 5 while SendMessage WM_GETTEXT succeeds, so
reads cross the boundary and writes do not.
The 125 percent DPI measurement is not achievable here and the reason is
measured rather than assumed: the VMware display carries no EDID and offers a
single scale step. DisplayConfigSetDeviceInfo returns success and persists the
registry value while effective DPI stays at 96, which is itself worth
recording -- that API's success return is not evidence the scale changed. The
non-zero delta defers to 2.4, which owns per-monitor scale. What does land for
2.1 is that PER_MONITOR_AWARE_V2 succeeds on 17763 with both arms verified at
the awareness levels claimed.
Chromium activation is graded, not guessed. The bundled version was read off
this installation by scanning the shipped binary's user-agent string: Chromium
142, Electron 39.8.3. Both stacks reach the same settled tree with no flag, so
the exposure claim holds and the plan's candidate contradiction is superseded.
The flag is still not redundant: without it first contact is deterministically
the pre-activation shell, and with it first contact is a race. Neither arm
removes the need to settle before trusting a first snapshot.
---
probes/windows/09-elevation-uipi.ps1 | 465 ++++++++++++++++++
probes/windows/10-session-dpi.ps1 | 393 +++++++++++++++
probes/windows/11-electron-activation.ps1 | 337 +++++++++++++
.../captures/09-elevation-uipi/uipi.json | 194 ++++++++
.../09-elevation-uipi/uipi.json.normalized | 194 ++++++++
.../captures/10-session-dpi/session-dpi.json | 224 +++++++++
.../session-dpi.json.normalized | 224 +++++++++
.../electron-activation.json | 164 ++++++
.../electron-activation.json.normalized | 164 ++++++
9 files changed, 2359 insertions(+)
create mode 100644 probes/windows/09-elevation-uipi.ps1
create mode 100644 probes/windows/10-session-dpi.ps1
create mode 100644 probes/windows/11-electron-activation.ps1
create mode 100644 probes/windows/captures/09-elevation-uipi/uipi.json
create mode 100644 probes/windows/captures/09-elevation-uipi/uipi.json.normalized
create mode 100644 probes/windows/captures/10-session-dpi/session-dpi.json
create mode 100644 probes/windows/captures/10-session-dpi/session-dpi.json.normalized
create mode 100644 probes/windows/captures/11-electron-activation/electron-activation.json
create mode 100644 probes/windows/captures/11-electron-activation/electron-activation.json.normalized
diff --git a/probes/windows/09-elevation-uipi.ps1 b/probes/windows/09-elevation-uipi.ps1
new file mode 100644
index 0000000..67dece0
--- /dev/null
+++ b/probes/windows/09-elevation-uipi.ps1
@@ -0,0 +1,465 @@
+<#
+.SYNOPSIS
+ Probe 09 (sub-phase 2.0, unit U8): UIPI measured across a real integrity boundary.
+
+.DESCRIPTION
+ Engineering Invariant #6 is about a Medium-integrity automation process driving a
+ High-integrity target. This box cannot produce that pair by ordinary means: the
+ built-in Administrator (RID 500) runs a full token at S-1-16-12288 and
+ FilterAdministratorToken is unset, so Admin Approval Mode is off and
+ "Start-Process -Verb RunAs" yields High-vs-High. runas.exe /trustlevel:0x20000 was
+ also ruled out - it restricts the token but leaves the mandatory label at High.
+ The boundary is therefore manufactured with common.ps1's
+ Start-MediumIntegrityProcess, which lowers a duplicated token to S-1-16-8192 and
+ asserts the label on read-back.
+
+ Two arms run the SAME worker code against the SAME probe-launched High Notepad;
+ only the integrity level of the worker differs:
+
+ arm "high" control - worker at S-1-16-12288, injection expected to land
+ arm "medium" test - worker at S-1-16-8192, injection expected to be dropped
+
+ Effect is never read from SendInput's return value (which is documented to report
+ success when UIPI drops the input). The parent re-reads the target's edit text over
+ WM_GETTEXT before and after each arm, High-to-High, as independent observation.
+#>
+[CmdletBinding()]
+param(
+ [switch]$Worker,
+ [string]$WorkerOut = '',
+ [string]$HelperDir = '',
+ [int]$TargetProcessId = 0,
+ [string]$TargetHwnd = '0',
+ [string]$EditHwnd = '0',
+ [string]$Marker = 'ZZZ',
+ [string]$Arm = 'unknown'
+)
+
+$ErrorActionPreference = 'Stop'
+. "$PSScriptRoot\common.ps1"
+
+$Probe = '09-elevation-uipi'
+$NativeShimSource = @'
+using System;
+using System.Runtime.InteropServices;
+
+namespace AgentDesktopProbe {
+ public static class Native {
+ [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
+ [DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr h, out uint pid);
+ [DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr h, int cmd);
+ [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr OpenProcess(uint access, bool inherit, int pid);
+ [DllImport("advapi32.dll", SetLastError = true)] public static extern bool OpenProcessToken(IntPtr proc, uint access, out IntPtr tok);
+ [DllImport("advapi32.dll", SetLastError = true)] public static extern bool GetTokenInformation(IntPtr tok, int cls, IntPtr buf, int len, out int ret);
+ [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] public static extern bool ConvertSidToStringSid(IntPtr sid, out IntPtr str);
+ [DllImport("kernel32.dll", SetLastError = true)] public static extern bool CloseHandle(IntPtr h);
+ [DllImport("kernel32.dll")] public static extern IntPtr LocalFree(IntPtr h);
+
+ public static int GetForegroundProcessId() {
+ IntPtr h = GetForegroundWindow();
+ if (h == IntPtr.Zero) { return 0; }
+ uint p = 0;
+ GetWindowThreadProcessId(h, out p);
+ return (int)p;
+ }
+
+ public static string GetIntegritySid(int processId) {
+ IntPtr proc = OpenProcess(0x1000, false, processId);
+ if (proc == IntPtr.Zero) { throw new InvalidOperationException("OpenProcess failed for pid " + processId + ", error " + Marshal.GetLastWin32Error()); }
+ IntPtr tok = IntPtr.Zero;
+ try {
+ if (!OpenProcessToken(proc, 0x0008, out tok)) { throw new InvalidOperationException("OpenProcessToken failed, error " + Marshal.GetLastWin32Error()); }
+ int len = 0;
+ GetTokenInformation(tok, 25, IntPtr.Zero, 0, out len);
+ if (len <= 0) { throw new InvalidOperationException("GetTokenInformation sizing failed, error " + Marshal.GetLastWin32Error()); }
+ IntPtr buf = Marshal.AllocHGlobal(len);
+ try {
+ if (!GetTokenInformation(tok, 25, buf, len, out len)) { throw new InvalidOperationException("GetTokenInformation failed, error " + Marshal.GetLastWin32Error()); }
+ IntPtr str = IntPtr.Zero;
+ if (!ConvertSidToStringSid(Marshal.ReadIntPtr(buf), out str)) { throw new InvalidOperationException("ConvertSidToStringSid failed, error " + Marshal.GetLastWin32Error()); }
+ string s = Marshal.PtrToStringUni(str);
+ LocalFree(str);
+ return s;
+ } finally { Marshal.FreeHGlobal(buf); }
+ } finally {
+ if (tok != IntPtr.Zero) { CloseHandle(tok); }
+ CloseHandle(proc);
+ }
+ }
+ }
+}
+'@
+
+$HelperSource = @'
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace AgentDesktopProbe {
+ [StructLayout(LayoutKind.Sequential)]
+ public struct KeybdInput { public ushort wVk; public ushort wScan; public uint dwFlags; public uint time; public IntPtr dwExtraInfo; }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Input { public uint type; public KeybdInput ki; public int padA; public int padB; }
+
+ public static class U8 {
+ private delegate bool EnumProc(IntPtr h, IntPtr l);
+
+ [DllImport("user32.dll", SetLastError = true)] private static extern uint SendInput(uint n, Input[] inputs, int size);
+ [DllImport("user32.dll")] private static extern bool EnumChildWindows(IntPtr parent, EnumProc cb, IntPtr l);
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern int GetClassNameW(IntPtr h, StringBuilder b, int max);
+ [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "SendMessageTimeoutW")]
+ private static extern IntPtr SendMessageTimeoutText(IntPtr h, uint msg, IntPtr w, StringBuilder l, uint flags, uint ms, out IntPtr res);
+ [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "SendMessageTimeoutW")]
+ private static extern IntPtr SendMessageTimeoutStr(IntPtr h, uint msg, IntPtr w, string l, uint flags, uint ms, out IntPtr res);
+ [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] private static extern bool PostMessageW(IntPtr h, uint msg, IntPtr w, IntPtr l);
+ [DllImport("kernel32.dll")] public static extern IntPtr GetConsoleWindow();
+ [DllImport("kernel32.dll")] private static extern void SetLastError(uint code);
+
+ public static int InputStructSize() { return Marshal.SizeOf(typeof(Input)); }
+
+ public static IntPtr FindChildByClass(IntPtr parent, string wanted) {
+ IntPtr hit = IntPtr.Zero;
+ EnumChildWindows(parent, delegate(IntPtr h, IntPtr l) {
+ StringBuilder b = new StringBuilder(256);
+ GetClassNameW(h, b, 256);
+ if (String.Equals(b.ToString(), wanted, StringComparison.OrdinalIgnoreCase)) { hit = h; return false; }
+ return true;
+ }, IntPtr.Zero);
+ return hit;
+ }
+
+ public static string ReadText(IntPtr h, out long sendResult, out int lastError) {
+ StringBuilder b = new StringBuilder(8192);
+ IntPtr res;
+ IntPtr ok = SendMessageTimeoutText(h, 0x000D, (IntPtr)8192, b, 0x0002, 4000, out res);
+ lastError = Marshal.GetLastWin32Error();
+ sendResult = res.ToInt64();
+ if (ok == IntPtr.Zero) { return null; }
+ return b.ToString();
+ }
+
+ public static bool WriteText(IntPtr h, string text, out int lastError) {
+ IntPtr res;
+ IntPtr ok = SendMessageTimeoutStr(h, 0x000C, IntPtr.Zero, text, 0x0002, 4000, out res);
+ lastError = Marshal.GetLastWin32Error();
+ return ok != IntPtr.Zero;
+ }
+
+ public static bool PostChar(IntPtr h, char c, out int lastError) {
+ bool ok = PostMessageW(h, 0x0102, (IntPtr)c, IntPtr.Zero);
+ lastError = Marshal.GetLastWin32Error();
+ return ok;
+ }
+
+ public static uint SendUnicode(string text, out int lastError) {
+ List batch = new List();
+ for (int i = 0; i < text.Length; i++) {
+ Input down = new Input();
+ down.type = 1;
+ down.ki.wScan = (ushort)text[i];
+ down.ki.dwFlags = 0x0004;
+ batch.Add(down);
+ Input up = new Input();
+ up.type = 1;
+ up.ki.wScan = (ushort)text[i];
+ up.ki.dwFlags = 0x0004 | 0x0002;
+ batch.Add(up);
+ }
+ Input[] arr = batch.ToArray();
+ SetLastError(0);
+ uint sent = SendInput((uint)arr.Length, arr, Marshal.SizeOf(typeof(Input)));
+ lastError = Marshal.GetLastWin32Error();
+ return sent;
+ }
+ }
+}
+'@
+
+function Get-UiaSnapshot {
+ param([IntPtr]$WindowHandle)
+ $row = [ordered]@{}
+ try {
+ Add-Type -AssemblyName UIAutomationClient -ErrorAction Stop
+ Add-Type -AssemblyName UIAutomationTypes -ErrorAction Stop
+ $root = [System.Windows.Automation.AutomationElement]::FromHandle($WindowHandle)
+ $row['fromHandle'] = 'ok'
+ $row['name'] = $root.Current.Name
+ $row['className'] = $root.Current.ClassName
+ $row['controlType'] = ($root.Current.ControlType.ProgrammaticName -replace '^ControlType\.', '')
+ $row['ownerProcessId'] = $root.Current.ProcessId
+ $r = $root.Current.BoundingRectangle
+ if ($r.IsEmpty -or [double]::IsInfinity($r.Left)) {
+ $row['boundingRectangle'] = 'empty (window minimized or off-screen)'
+ } else {
+ $row['boundingRectangle'] = [ordered]@{ Left = [int]$r.Left; Top = [int]$r.Top; Width = [int]$r.Width; Height = [int]$r.Height }
+ }
+ $walker = [System.Windows.Automation.TreeWalker]::RawViewWalker
+ $stack = New-Object System.Collections.Stack
+ $stack.Push($root)
+ $count = 0
+ $editValue = $null
+ $editPatterns = @()
+ while ($stack.Count -gt 0 -and $count -lt 200) {
+ $node = $stack.Pop()
+ $count++
+ if ($node.Current.ClassName -eq 'Edit' -and $null -eq $editValue) {
+ $editPatterns = @($node.GetSupportedPatterns() | ForEach-Object { $_.ProgrammaticName -replace '^(\w+)PatternIdentifiers\.Pattern$', '$1' })
+ try {
+ $vp = $node.GetCurrentPattern([System.Windows.Automation.ValuePattern]::Pattern)
+ $editValue = $vp.Current.Value
+ } catch {
+ try {
+ $tp = $node.GetCurrentPattern([System.Windows.Automation.TextPattern]::Pattern)
+ $editValue = $tp.DocumentRange.GetText(-1)
+ } catch { $editValue = '' }
+ }
+ }
+ $child = $walker.GetFirstChild($node)
+ while ($null -ne $child) {
+ $stack.Push($child)
+ $child = $walker.GetNextSibling($child)
+ }
+ }
+ $row['rawViewNodes'] = $count
+ $row['editPatterns'] = $editPatterns
+ $row['editValueSeenByThisProcess'] = $editValue
+ } catch {
+ $row['fromHandle'] = 'failed'
+ $row['error'] = $_.Exception.Message
+ $row['hresult'] = ('0x' + ('{0:x8}' -f $_.Exception.HResult))
+ }
+ return $row
+}
+
+function Invoke-WorkerArm {
+ $result = [ordered]@{ arm = $Arm; processId = $PID }
+ Add-Type -Path (Join-Path $HelperDir 'u8native.dll')
+ Add-Type -Path (Join-Path $HelperDir 'u8helper.dll')
+ $console = [AgentDesktopProbe.U8]::GetConsoleWindow()
+ [void][AgentDesktopProbe.Native]::ShowWindow($console, 6)
+ [void][AgentDesktopProbe.Native]::ShowWindow($console, 0)
+ $result['addTypeCompilationAvailable'] = $null
+ try {
+ Add-Type -TypeDefinition 'public static class U8Compile { public static int V() { return 1; } }' -Language CSharp -ErrorAction Stop
+ $result['addTypeCompilationAvailable'] = $true
+ } catch {
+ $result['addTypeCompilationAvailable'] = $false
+ $result['addTypeCompilationError'] = $_.Exception.Message
+ }
+ $target = [IntPtr][int64]$TargetHwnd
+ $edit = [IntPtr][int64]$EditHwnd
+ $result['selfIntegritySid'] = [AgentDesktopProbe.Native]::GetIntegritySid($PID)
+ try { $result['targetIntegritySid'] = [AgentDesktopProbe.Native]::GetIntegritySid($TargetProcessId) }
+ catch { $result['targetIntegritySid'] = 'ERROR ' + $_.Exception.Message }
+ $result['uiaRead'] = Get-UiaSnapshot -WindowHandle $target
+
+ $err = 0
+ $raw = [int64]0
+ $textBefore = [AgentDesktopProbe.U8]::ReadText($edit, [ref]$raw, [ref]$err)
+ $result['wmGetTextFromWorker'] = [ordered]@{ returned = $textBefore; sendMessageResult = $raw; lastError = $err }
+
+ [IO.File]::WriteAllText(($WorkerOut + '.ready'), 'ready')
+ $deadline = (Get-Date).AddSeconds(120)
+ while ((Get-Date) -lt $deadline -and -not (Test-Path -LiteralPath ($WorkerOut + '.go'))) { Start-Sleep -Milliseconds 200 }
+ $deadline = (Get-Date).AddSeconds(20)
+ while ((Get-Date) -lt $deadline -and [AgentDesktopProbe.Native]::GetForegroundProcessId() -ne $TargetProcessId) {
+ Start-Sleep -Milliseconds 200
+ }
+ $result['foregroundOwnerAtAssert'] = [ordered]@{ processId = [AgentDesktopProbe.Native]::GetForegroundProcessId() }
+ try {
+ Assert-Foreground -ExpectedProcessId $TargetProcessId -Stage ('sendinput-pre-' + $Arm)
+ $result['foregroundAssertPre'] = 'ok'
+ $sent = [AgentDesktopProbe.U8]::SendUnicode($Marker, [ref]$err)
+ $result['sendInputEventsAccepted'] = [int]$sent
+ $result['sendInputLastError'] = $err
+ $result['sendInputStructSize'] = [AgentDesktopProbe.U8]::InputStructSize()
+ Start-Sleep -Milliseconds 700
+ Assert-Foreground -ExpectedProcessId $TargetProcessId -Stage ('sendinput-post-' + $Arm)
+ $result['foregroundAssertPost'] = 'ok'
+ } catch {
+ $result['foregroundAssertPre'] = 'interference'
+ $result['interference'] = $_.Exception.Message
+ }
+
+ $postOk = [AgentDesktopProbe.U8]::PostChar($edit, 'P', [ref]$err)
+ $result['postMessageWmChar'] = [ordered]@{ returned = $postOk; lastError = $err }
+ Start-Sleep -Milliseconds 400
+ $textAfter = [AgentDesktopProbe.U8]::ReadText($edit, [ref]$raw, [ref]$err)
+ $result['wmGetTextAfterFromWorker'] = [ordered]@{ returned = $textAfter; sendMessageResult = $raw; lastError = $err }
+ $result['uiaReadAfter'] = Get-UiaSnapshot -WindowHandle $target
+ [IO.File]::WriteAllText($WorkerOut, (ConvertTo-Json -InputObject $result -Depth 12), (New-Object System.Text.UTF8Encoding $false))
+}
+
+if ($Worker) {
+ try { Invoke-WorkerArm } catch {
+ [IO.File]::WriteAllText($WorkerOut, (ConvertTo-Json -InputObject ([ordered]@{ arm = $Arm; fatal = $_.Exception.Message }) -Depth 6), (New-Object System.Text.UTF8Encoding $false))
+ }
+ exit 0
+}
+
+$status = 'ok'
+$message = ''
+$summary = [ordered]@{}
+$script:Spawned = New-Object System.Collections.ArrayList
+
+try {
+ Initialize-ProbeNative
+ $work = Join-Path $env:TEMP 'agent-desktop-u8'
+ if (-not (Test-Path -LiteralPath $work)) { New-Item -ItemType Directory -Path $work -Force | Out-Null }
+ Add-Type -TypeDefinition $NativeShimSource -Language CSharp -OutputAssembly (Join-Path $work 'u8native.dll') -OutputType Library
+ Add-Type -TypeDefinition $HelperSource -Language CSharp -OutputAssembly (Join-Path $work 'u8helper.dll') -OutputType Library
+ Add-Type -Path (Join-Path $work 'u8helper.dll')
+ Write-ProbeLog -Message 'compiled u8native.dll + u8helper.dll at High integrity for the Medium worker to load'
+
+ $policy = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System'
+ $uac = [ordered]@{}
+ foreach ($n in @('EnableLUA', 'FilterAdministratorToken', 'ConsentPromptBehaviorAdmin', 'EnableInstallerDetection')) {
+ $v = (Get-ItemProperty -Path $policy -Name $n -ErrorAction SilentlyContinue).$n
+ if ($null -eq $v) { $uac[$n] = '' } else { $uac[$n] = [int]$v }
+ }
+ $uac['sessionIntegritySid'] = [AgentDesktopProbe.Native]::GetIntegritySid($PID)
+ $uac['adminApprovalModeActive'] = ($uac['EnableLUA'] -eq 1 -and $uac['FilterAdministratorToken'] -eq 1)
+
+ $notepad = Start-ScratchProcess -FilePath (Join-Path $env:WINDIR 'System32\notepad.exe') -TimeoutSec 20
+ [void]$script:Spawned.Add($notepad.ProcessId)
+ if ($notepad.MainWindowHandle -eq [IntPtr]::Zero) { throw 'target notepad window never appeared' }
+ $targetSid = [AgentDesktopProbe.Native]::GetIntegritySid($notepad.ProcessId)
+ if ($targetSid -ne 'S-1-16-12288') { throw ('target notepad is not High integrity: ' + $targetSid) }
+ $editHandle = [AgentDesktopProbe.U8]::FindChildByClass($notepad.MainWindowHandle, 'Edit')
+ if ($editHandle -eq [IntPtr]::Zero) { throw 'notepad Edit child window not found' }
+
+ $arms = @()
+ $lastErr = 0
+ $rawRes = [int64]0
+ foreach ($armSpec in @(
+ [pscustomobject]@{ Name = 'high'; Marker = 'HHH'; Medium = $false },
+ [pscustomobject]@{ Name = 'medium'; Marker = 'MMM'; Medium = $true })) {
+ [void][AgentDesktopProbe.U8]::WriteText($editHandle, 'U8-BASE', [ref]$lastErr)
+ Start-Sleep -Milliseconds 300
+ $before = [AgentDesktopProbe.U8]::ReadText($editHandle, [ref]$rawRes, [ref]$lastErr)
+ $outFile = Join-Path $work ('arm-' + $armSpec.Name + '.json')
+ foreach ($stale in @($outFile, ($outFile + '.ready'), ($outFile + '.go'))) {
+ if (Test-Path -LiteralPath $stale) { Remove-Item -LiteralPath $stale -Force }
+ }
+ $argv = @('-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass',
+ '-File', $PSCommandPath, '-Worker', '-WorkerOut', $outFile, '-HelperDir', $work,
+ '-TargetProcessId', ([string]$notepad.ProcessId),
+ '-TargetHwnd', ([string]$notepad.MainWindowHandle.ToInt64()),
+ '-EditHwnd', ([string]$editHandle.ToInt64()),
+ '-Marker', $armSpec.Marker, '-Arm', $armSpec.Name)
+ $shell = Join-Path $env:WINDIR 'System32\WindowsPowerShell\v1.0\powershell.exe'
+ $launchError = ''
+ $workerPid = 0
+ $workerSid = ''
+ try {
+ if ($armSpec.Medium) {
+ $w = Start-MediumIntegrityProcess -FilePath $shell -ArgumentList $argv
+ $workerPid = $w.ProcessId
+ $workerSid = $w.IntegritySid
+ } else {
+ $w = Start-Process -FilePath $shell -ArgumentList $argv -PassThru
+ $workerPid = $w.Id
+ Register-ScratchProcessId -ProcessId $workerPid
+ $workerSid = [AgentDesktopProbe.Native]::GetIntegritySid($workerPid)
+ }
+ [void]$script:Spawned.Add($workerPid)
+ } catch {
+ $launchError = $_.Exception.Message
+ }
+ if ($armSpec.Medium -and $workerSid -ne 'S-1-16-8192') {
+ throw ('PROBE-HARNESS: refusing to record a UIPI verdict without a real boundary - medium arm token label is ' + $workerSid + ' ' + $launchError)
+ }
+ $handoff = 'worker never signalled ready'
+ $deadline = (Get-Date).AddSeconds(120)
+ while ((Get-Date) -lt $deadline -and -not (Test-Path -LiteralPath ($outFile + '.ready'))) { Start-Sleep -Milliseconds 300 }
+ if (Test-Path -LiteralPath ($outFile + '.ready')) {
+ [void][AgentDesktopProbe.Native]::ShowWindow($notepad.MainWindowHandle, 6)
+ Start-Sleep -Milliseconds 500
+ [void][AgentDesktopProbe.Native]::ShowWindow($notepad.MainWindowHandle, 9)
+ $fgDeadline = (Get-Date).AddSeconds(15)
+ while ((Get-Date) -lt $fgDeadline -and [AgentDesktopProbe.Native]::GetForegroundProcessId() -ne $notepad.ProcessId) { Start-Sleep -Milliseconds 250 }
+ $observedFg = [AgentDesktopProbe.Native]::GetForegroundProcessId()
+ $handoff = if ($observedFg -eq $notepad.ProcessId) { 'target foreground restored by parent ShowWindow(SW_MINIMIZE->SW_RESTORE) on the probe-launched window; SetForegroundWindow never called' }
+ else { 'FAILED - foreground pid ' + $observedFg + ' is not the probe-launched target' }
+ [IO.File]::WriteAllText(($outFile + '.go'), 'go')
+ }
+ $deadline = (Get-Date).AddSeconds(180)
+ while ((Get-Date) -lt $deadline -and -not (Test-Path -LiteralPath $outFile)) { Start-Sleep -Milliseconds 500 }
+ $workerData = $null
+ if (Test-Path -LiteralPath $outFile) { $workerData = (Get-Content -LiteralPath $outFile -Raw | ConvertFrom-Json) }
+ Start-Sleep -Milliseconds 500
+ $after = [AgentDesktopProbe.U8]::ReadText($editHandle, [ref]$rawRes, [ref]$lastErr)
+ $arms += [ordered]@{
+ arm = $armSpec.Name
+ workerIntegritySid = $workerSid
+ workerLaunchError = $launchError
+ marker = $armSpec.Marker
+ parentForegroundHandoff = $handoff
+ targetStateBeforeInjection = $before
+ targetStateAfterInjection = $after
+ targetStateChanged = ($before -ne $after)
+ markerPresentInTargetAfterwards = ($null -ne $after -and $after.Contains($armSpec.Marker))
+ observedBy = 'parent High-integrity WM_GETTEXT on the notepad Edit child, independent of SendInput return'
+ worker = $workerData
+ }
+ try { Stop-ScratchProcess -ProcessId $workerPid } catch { Write-ProbeLog -Message ('worker teardown: ' + $_.Exception.Message) -Level 'warn' }
+ }
+
+ $high = $arms | Where-Object { $_.arm -eq 'high' } | Select-Object -First 1
+ $medium = $arms | Where-Object { $_.arm -eq 'medium' } | Select-Object -First 1
+ $capture = [ordered]@{
+ question = 'does UIPI block SendInput and window messages from a Medium-integrity process into a High-integrity target, and are UIA reads still allowed'
+ invariant = 'Engineering Invariant #6 (UIPI)'
+ stack = 'managed (UIA reads) + raw Win32 SendInput/SendMessage/PostMessage'
+ scope = 'api-contract'
+ boundaryOrigin = 'manufactured by common.ps1 Start-MediumIntegrityProcess (DuplicateTokenEx + SetTokenInformation(TokenIntegrityLevel, S-1-16-8192) + CreateProcessAsUser), label asserted on read-back'
+ boundaryRuledOut = @(
+ 'Start-Process -Verb RunAs: AAM off on this box (FilterAdministratorToken unset, RID 500 full token) so it yields High-vs-High',
+ 'runas.exe /trustlevel:0x20000: restricts the token but leaves the mandatory label at S-1-16-12288'
+ )
+ uacPolicy = $uac
+ target = [ordered]@{
+ process = 'notepad.exe'
+ processId = $notepad.ProcessId
+ integritySid = $targetSid
+ windowHandle = $notepad.MainWindowHandle.ToInt64()
+ editChild = [ordered]@{ className = 'Edit'; windowHandle = $editHandle.ToInt64() }
+ launchedByThisProbe = $true
+ }
+ arms = $arms
+ findings = [ordered]@{
+ uiaReadFromMediumAgainstHigh = if ($medium -and $medium.worker) { $medium.worker.uiaRead.fromHandle } else { 'unavailable' }
+ sendInputFromHighLanded = if ($high) { $high.targetStateChanged } else { $null }
+ sendInputFromMediumLanded = if ($medium) { $medium.targetStateChanged } else { $null }
+ sendInputReturnIsNotEvidence = 'SendInput reports the event count it accepted in both arms; only the re-read of the target distinguishes them'
+ wmGetTextFromMediumBlocked = if ($medium -and $medium.worker) { ($null -eq $medium.worker.wmGetTextFromWorker.returned -or $medium.worker.wmGetTextFromWorker.returned -eq '') } else { $null }
+ }
+ }
+ Write-ProbeJson -Probe $Probe -Name 'uipi.json' -InputObject $capture | Out-Null
+
+ $summary['sessionIntegritySid'] = $uac['sessionIntegritySid']
+ $summary['targetIntegritySid'] = $targetSid
+ $summary['mediumArmIntegritySid'] = if ($medium) { $medium.workerIntegritySid } else { '' }
+ $summary['highArmInjectionLanded'] = $capture.findings.sendInputFromHighLanded
+ $summary['mediumArmInjectionLanded'] = $capture.findings.sendInputFromMediumLanded
+ $summary['mediumArmUiaRead'] = $capture.findings.uiaReadFromMediumAgainstHigh
+ $message = 'uipi measured across a manufactured Medium-vs-High boundary'
+} catch {
+ $status = 'fail'
+ $message = ($_.Exception.Message -replace '[\r\n]+', ' ')
+ Write-ProbeLog -Message ('probe failed: ' + $message) -Level 'error'
+} finally {
+ foreach ($id in @($script:Spawned)) {
+ try { Stop-ScratchProcess -ProcessId $id } catch { Write-ProbeLog -Message ('teardown: ' + $_.Exception.Message) -Level 'warn' }
+ }
+ foreach ($f in @(Get-ChildItem -LiteralPath (Get-CaptureDir -Probe $Probe) -File -ErrorAction SilentlyContinue)) {
+ if ($f.Name -like '*.normalized') { continue }
+ if (-not (Test-CaptureRedaction -Path $f.FullName)) { $status = 'fail'; $message = ('redaction residue in ' + $f.Name) }
+ }
+}
+
+Write-ProbeResult -Probe $Probe -Status $status -Message $message -Data $summary
+if ($status -eq 'fail') { exit 1 }
+exit 0
diff --git a/probes/windows/10-session-dpi.ps1 b/probes/windows/10-session-dpi.ps1
new file mode 100644
index 0000000..02063a5
--- /dev/null
+++ b/probes/windows/10-session-dpi.ps1
@@ -0,0 +1,393 @@
+<#
+.SYNOPSIS
+ Probe 10 (sub-phase 2.0, unit U8): session facts and the DPI-awareness bounds delta.
+
+.DESCRIPTION
+ Reads the same probe-launched element's BoundingRectangle from a DPI-aware child
+ process and a DPI-unaware child process, and reports the per-edge numeric delta.
+ The measurement is taken twice: at the display's recommended scale, and again after
+ the probe asks for the next scale step up (125%). The original scale is restored in
+ teardown and the restoration is proved by re-reading it back.
+
+ Delta fields are named deltaLeft/deltaTop/deltaWidth/deltaHeight on purpose. The
+ KTD9 normalizer buckets any key named exactly x/y/left/top/right/bottom/width/height
+ to an 8-pixel bucket to absorb layout jitter; a measured delta must not be bucketed
+ away, and "deltaLeft" has no word boundary before "Left", so it survives verbatim
+ while the raw rectangles it is derived from are still bucketed.
+
+ Multi-monitor and mixed-DPI behavior is a DEFERRED row: this VM has exactly one
+ display. It closes at sub-phase 2.4, which owns list_displays and per-monitor
+ scale_factor.
+#>
+[CmdletBinding()]
+param(
+ [switch]$Worker,
+ [string]$WorkerOut = '',
+ [string]$Mode = 'unaware',
+ [string]$TargetHwnd = '0',
+ [string]$EditHwnd = '0'
+)
+
+$ErrorActionPreference = 'Stop'
+. "$PSScriptRoot\common.ps1"
+
+$Probe = '10-session-dpi'
+$DpiSource = @'
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace AgentDesktopProbe {
+ [StructLayout(LayoutKind.Sequential)]
+ public struct DcLuid { public uint LowPart; public int HighPart; }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct DcHeader { public int type; public uint size; public DcLuid adapterId; public uint id; }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct DcScaleGet { public DcHeader header; public int minScaleRel; public int curScaleRel; public int maxScaleRel; }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct DcScaleSet { public DcHeader header; public int scaleRel; }
+
+ [StructLayout(LayoutKind.Sequential, Size = 72)]
+ public struct DcPathInfo { public DcLuid srcAdapterId; public uint srcId; }
+
+ [StructLayout(LayoutKind.Sequential, Size = 64)]
+ public struct DcModeInfo { public uint infoType; }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Rect { public int Left; public int Top; public int Right; public int Bottom; }
+
+ public static class Dpi {
+ private delegate bool EnumProc(IntPtr h, IntPtr l);
+
+ [DllImport("user32.dll")] public static extern int GetDisplayConfigBufferSizes(uint flags, out uint numPath, out uint numMode);
+ [DllImport("user32.dll")] public static extern int QueryDisplayConfig(uint flags, ref uint numPath, [Out] DcPathInfo[] paths, ref uint numMode, [Out] DcModeInfo[] modes, IntPtr topology);
+ [DllImport("user32.dll")] public static extern int DisplayConfigGetDeviceInfo(ref DcScaleGet req);
+ [DllImport("user32.dll")] public static extern int DisplayConfigSetDeviceInfo(ref DcScaleSet req);
+ [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr SetProcessDpiAwarenessContext(IntPtr context);
+ [DllImport("user32.dll")] public static extern int GetSystemMetrics(int index);
+ [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr h, out Rect r);
+ [DllImport("user32.dll")] public static extern IntPtr MonitorFromPoint(long pt, uint flags);
+ [DllImport("user32.dll")] public static extern IntPtr GetDC(IntPtr h);
+ [DllImport("user32.dll")] public static extern int ReleaseDC(IntPtr h, IntPtr dc);
+ [DllImport("user32.dll")] private static extern bool EnumChildWindows(IntPtr parent, EnumProc cb, IntPtr l);
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern int GetClassNameW(IntPtr h, StringBuilder b, int max);
+ [DllImport("gdi32.dll")] public static extern int GetDeviceCaps(IntPtr dc, int index);
+ [DllImport("shcore.dll")] public static extern int GetProcessDpiAwareness(IntPtr proc, out int value);
+ [DllImport("shcore.dll")] public static extern int GetDpiForMonitor(IntPtr mon, int type, out uint dx, out uint dy);
+ [DllImport("kernel32.dll")] public static extern uint WTSGetActiveConsoleSessionId();
+ [DllImport("kernel32.dll")] public static extern uint GetCurrentProcessId();
+ [DllImport("kernel32.dll", SetLastError = true)] public static extern bool ProcessIdToSessionId(uint pid, out uint sid);
+
+ public static int LogPixels() {
+ IntPtr dc = GetDC(IntPtr.Zero);
+ int v = GetDeviceCaps(dc, 88);
+ ReleaseDC(IntPtr.Zero, dc);
+ return v;
+ }
+
+ public static IntPtr FindChildByClass(IntPtr parent, string wanted) {
+ IntPtr hit = IntPtr.Zero;
+ EnumChildWindows(parent, delegate(IntPtr h, IntPtr l) {
+ StringBuilder b = new StringBuilder(256);
+ GetClassNameW(h, b, 256);
+ if (String.Equals(b.ToString(), wanted, StringComparison.OrdinalIgnoreCase)) { hit = h; return false; }
+ return true;
+ }, IntPtr.Zero);
+ return hit;
+ }
+
+ public static bool GetSourceIds(out DcLuid adapterId, out uint sourceId) {
+ adapterId = new DcLuid();
+ sourceId = 0;
+ uint numPath = 0;
+ uint numMode = 0;
+ if (GetDisplayConfigBufferSizes(2, out numPath, out numMode) != 0) { return false; }
+ DcPathInfo[] paths = new DcPathInfo[numPath];
+ DcModeInfo[] modes = new DcModeInfo[numMode];
+ if (QueryDisplayConfig(2, ref numPath, paths, ref numMode, modes, IntPtr.Zero) != 0) { return false; }
+ if (numPath < 1) { return false; }
+ adapterId = paths[0].srcAdapterId;
+ sourceId = paths[0].srcId;
+ return true;
+ }
+
+ public static int[] GetScaleRange() {
+ DcLuid adapter;
+ uint source;
+ if (!GetSourceIds(out adapter, out source)) { return new int[] { -999, -999, -999, -999 }; }
+ DcScaleGet g = new DcScaleGet();
+ g.header.type = -3;
+ g.header.size = (uint)Marshal.SizeOf(typeof(DcScaleGet));
+ g.header.adapterId = adapter;
+ g.header.id = source;
+ int rc = DisplayConfigGetDeviceInfo(ref g);
+ return new int[] { rc, g.minScaleRel, g.curScaleRel, g.maxScaleRel };
+ }
+
+ public static int SetScaleRelative(int rel) {
+ DcLuid adapter;
+ uint source;
+ if (!GetSourceIds(out adapter, out source)) { return -999; }
+ DcScaleSet s = new DcScaleSet();
+ s.header.type = -4;
+ s.header.size = (uint)Marshal.SizeOf(typeof(DcScaleSet));
+ s.header.adapterId = adapter;
+ s.header.id = source;
+ s.scaleRel = rel;
+ return DisplayConfigSetDeviceInfo(ref s);
+ }
+ }
+}
+'@
+
+function Get-AwarenessName {
+ param([int]$Value)
+ switch ($Value) {
+ 0 { return 'PROCESS_DPI_UNAWARE' }
+ 1 { return 'PROCESS_SYSTEM_DPI_AWARE' }
+ 2 { return 'PROCESS_PER_MONITOR_DPI_AWARE' }
+ default { return ('unknown(' + $Value + ')') }
+ }
+}
+
+function Invoke-DpiWorker {
+ Add-Type -TypeDefinition $DpiSource -Language CSharp
+ $row = [ordered]@{ mode = $Mode; processId = [int][AgentDesktopProbe.Dpi]::GetCurrentProcessId() }
+ $a = 0
+ [void][AgentDesktopProbe.Dpi]::GetProcessDpiAwareness([IntPtr]::Zero, [ref]$a)
+ $row['awarenessAtStartup'] = Get-AwarenessName -Value $a
+ if ($Mode -eq 'aware') {
+ $rc = [AgentDesktopProbe.Dpi]::SetProcessDpiAwarenessContext([IntPtr](-4))
+ $row['setProcessDpiAwarenessContextPerMonitorV2'] = if ($rc -ne [IntPtr]::Zero) { 'succeeded' } else { 'failed, error ' + [Runtime.InteropServices.Marshal]::GetLastWin32Error() }
+ } else {
+ $row['setProcessDpiAwarenessContextPerMonitorV2'] = 'not attempted (unaware arm, forced with __COMPAT_LAYER=DPIUNAWARE)'
+ }
+ [void][AgentDesktopProbe.Dpi]::GetProcessDpiAwareness([IntPtr]::Zero, [ref]$a)
+ $row['awarenessEffective'] = Get-AwarenessName -Value $a
+ $dx = 0
+ $dy = 0
+ $mon = [AgentDesktopProbe.Dpi]::MonitorFromPoint(0, 2)
+ [void][AgentDesktopProbe.Dpi]::GetDpiForMonitor($mon, 0, [ref]$dx, [ref]$dy)
+ $row['monitorEffectiveDpiX'] = [int]$dx
+ $row['monitorEffectiveDpiY'] = [int]$dy
+ $row['logPixels'] = [AgentDesktopProbe.Dpi]::LogPixels()
+ $row['screenMetricsCx'] = [AgentDesktopProbe.Dpi]::GetSystemMetrics(0)
+ $row['screenMetricsCy'] = [AgentDesktopProbe.Dpi]::GetSystemMetrics(1)
+ $target = [IntPtr][int64]$TargetHwnd
+ $edit = [IntPtr][int64]$EditHwnd
+ $r = New-Object AgentDesktopProbe.Rect
+ [void][AgentDesktopProbe.Dpi]::GetWindowRect($target, [ref]$r)
+ $row['getWindowRect'] = [ordered]@{ Left = $r.Left; Top = $r.Top; Right = $r.Right; Bottom = $r.Bottom }
+ Add-Type -AssemblyName UIAutomationClient
+ Add-Type -AssemblyName UIAutomationTypes
+ foreach ($pair in @(@('window', $target), @('editChild', $edit))) {
+ $cell = [ordered]@{}
+ try {
+ $el = [System.Windows.Automation.AutomationElement]::FromHandle([IntPtr]$pair[1])
+ $b = $el.Current.BoundingRectangle
+ $cell['className'] = $el.Current.ClassName
+ $cell['Left'] = [int]$b.Left
+ $cell['Top'] = [int]$b.Top
+ $cell['Width'] = [int]$b.Width
+ $cell['Height'] = [int]$b.Height
+ } catch { $cell['error'] = $_.Exception.Message }
+ $row[('uiaBounds_' + $pair[0])] = $cell
+ }
+ [IO.File]::WriteAllText($WorkerOut, (ConvertTo-Json -InputObject $row -Depth 10), (New-Object System.Text.UTF8Encoding $false))
+}
+
+if ($Worker) {
+ try { Invoke-DpiWorker } catch {
+ [IO.File]::WriteAllText($WorkerOut, (ConvertTo-Json -InputObject ([ordered]@{ mode = $Mode; fatal = $_.Exception.Message }) -Depth 6), (New-Object System.Text.UTF8Encoding $false))
+ }
+ exit 0
+}
+
+$status = 'ok'
+$message = ''
+$summary = [ordered]@{}
+$script:Spawned = New-Object System.Collections.ArrayList
+$script:OriginalScaleRel = $null
+$script:WorkDir = Join-Path $env:TEMP 'agent-desktop-u8'
+
+function Invoke-DpiArm {
+ param([string]$ArmMode, [string]$Tag, [IntPtr]$Target, [IntPtr]$Edit)
+ $out = Join-Path $script:WorkDir ('dpi-' + $Tag + '-' + $ArmMode + '.json')
+ if (Test-Path -LiteralPath $out) { Remove-Item -LiteralPath $out -Force }
+ $argv = @('-NoProfile', '-NonInteractive', '-WindowStyle', 'Hidden', '-ExecutionPolicy', 'Bypass',
+ '-File', $PSCommandPath, '-Worker', '-WorkerOut', $out, '-Mode', $ArmMode,
+ '-TargetHwnd', ([string]$Target.ToInt64()), '-EditHwnd', ([string]$Edit.ToInt64()))
+ if ($ArmMode -eq 'unaware') { $env:__COMPAT_LAYER = 'DPIUNAWARE' } else { $env:__COMPAT_LAYER = $null }
+ $proc = Start-Process -FilePath (Join-Path $env:WINDIR 'System32\WindowsPowerShell\v1.0\powershell.exe') -ArgumentList $argv -WindowStyle Hidden -PassThru
+ Register-ScratchProcessId -ProcessId $proc.Id
+ [void]$script:Spawned.Add($proc.Id)
+ $env:__COMPAT_LAYER = $null
+ $deadline = (Get-Date).AddSeconds(120)
+ while ((Get-Date) -lt $deadline -and -not (Test-Path -LiteralPath $out)) { Start-Sleep -Milliseconds 400 }
+ try { Stop-ScratchProcess -ProcessId $proc.Id } catch { }
+ if (-not (Test-Path -LiteralPath $out)) { return [ordered]@{ mode = $ArmMode; fatal = 'worker produced no output' } }
+ return (Get-Content -LiteralPath $out -Raw | ConvertFrom-Json)
+}
+
+function Get-BoundsDelta {
+ param($Aware, $Unaware, [string]$Field)
+ $a = $Aware.$Field
+ $u = $Unaware.$Field
+ if ($null -eq $a -or $null -eq $u -or $a.error -or $u.error) { return [ordered]@{ measurable = $false } }
+ return [ordered]@{
+ measurable = $true
+ deltaLeft = ([int]$a.Left - [int]$u.Left)
+ deltaTop = ([int]$a.Top - [int]$u.Top)
+ deltaWidth = ([int]$a.Width - [int]$u.Width)
+ deltaHeight = ([int]$a.Height - [int]$u.Height)
+ }
+}
+
+try {
+ Initialize-ProbeNative
+ Add-Type -TypeDefinition $DpiSource -Language CSharp
+ if (-not (Test-Path -LiteralPath $script:WorkDir)) { New-Item -ItemType Directory -Path $script:WorkDir -Force | Out-Null }
+
+ $sid = 0
+ [void][AgentDesktopProbe.Dpi]::ProcessIdToSessionId([AgentDesktopProbe.Dpi]::GetCurrentProcessId(), [ref]$sid)
+ $session = [ordered]@{
+ sessionId = [int]$sid
+ activeConsoleSessionId = [int][AgentDesktopProbe.Dpi]::WTSGetActiveConsoleSessionId()
+ isRemoteSession = ([AgentDesktopProbe.Dpi]::GetSystemMetrics(0x1000) -ne 0)
+ windowStation = ([System.Environment]::GetEnvironmentVariable('SESSIONNAME'))
+ note = 'RDP session-transition behavior is not measurable here (physical console session); it closes at sub-phase 2.1 runner registration'
+ }
+
+ Add-Type -AssemblyName System.Windows.Forms
+ $displays = @()
+ foreach ($s in [System.Windows.Forms.Screen]::AllScreens) {
+ $displays += [ordered]@{
+ primary = $s.Primary
+ Left = $s.Bounds.Left
+ Top = $s.Bounds.Top
+ Width = $s.Bounds.Width
+ Height = $s.Bounds.Height
+ }
+ }
+ $monitorKeys = @(Get-ChildItem -LiteralPath 'HKCU:\Control Panel\Desktop\PerMonitorSettings' -ErrorAction SilentlyContinue | ForEach-Object { $_.PSChildName })
+ $range = [AgentDesktopProbe.Dpi]::GetScaleRange()
+ $script:OriginalScaleRel = $range[2]
+ $scale = [ordered]@{
+ displayConfigGetResult = $range[0]
+ scaleRelMin = $range[1]
+ scaleRelCurrent = $range[2]
+ scaleRelMax = $range[3]
+ monitorRegistryKeys = $monitorKeys
+ stepsAvailableAboveRecommended = ($range[3] - $range[1])
+ }
+
+ $notepad = Start-ScratchProcess -FilePath (Join-Path $env:WINDIR 'System32\notepad.exe') -TimeoutSec 20
+ [void]$script:Spawned.Add($notepad.ProcessId)
+ if ($notepad.MainWindowHandle -eq [IntPtr]::Zero) { throw 'dpi target notepad window never appeared' }
+ $editHandle = [AgentDesktopProbe.Dpi]::FindChildByClass($notepad.MainWindowHandle, 'Edit')
+ Start-Sleep -Seconds 1
+
+ $passes = @()
+ foreach ($step in @(
+ [pscustomobject]@{ Tag = 'recommended'; Rel = $script:OriginalScaleRel; Wanted = 'display recommended scale (100%)' },
+ [pscustomobject]@{ Tag = 'plus-one-step'; Rel = ($script:OriginalScaleRel + 1); Wanted = 'one scale step above recommended (125%)' })) {
+ $setResult = $null
+ if ($step.Rel -ne $script:OriginalScaleRel) {
+ $setResult = [AgentDesktopProbe.Dpi]::SetScaleRelative($step.Rel)
+ Start-Sleep -Milliseconds 1500
+ }
+ $after = [AgentDesktopProbe.Dpi]::GetScaleRange()
+ $regValue = $null
+ if ($monitorKeys.Count -gt 0) {
+ $regValue = (Get-ItemProperty -Path ('HKCU:\Control Panel\Desktop\PerMonitorSettings\' + $monitorKeys[0]) -Name 'DpiValue' -ErrorAction SilentlyContinue).DpiValue
+ }
+ $aware = Invoke-DpiArm -ArmMode 'aware' -Tag $step.Tag -Target $notepad.MainWindowHandle -Edit $editHandle
+ $unaware = Invoke-DpiArm -ArmMode 'unaware' -Tag $step.Tag -Target $notepad.MainWindowHandle -Edit $editHandle
+ $passes += [ordered]@{
+ pass = $step.Tag
+ requested = $step.Wanted
+ requestedScaleRel = $step.Rel
+ displayConfigSetResult = $setResult
+ scaleRelReportedAfterwards = $after[2]
+ registryDpiValueAfterwards = $regValue
+ effectiveDpiSeenByAwareArm = $aware.monitorEffectiveDpiX
+ scaleActuallyApplied = ($aware.monitorEffectiveDpiX -ne 96)
+ awareArm = $aware
+ unawareArm = $unaware
+ windowBoundsDelta = (Get-BoundsDelta -Aware $aware -Unaware $unaware -Field 'uiaBounds_window')
+ editChildBoundsDelta = (Get-BoundsDelta -Aware $aware -Unaware $unaware -Field 'uiaBounds_editChild')
+ }
+ }
+
+ $restoreResult = [AgentDesktopProbe.Dpi]::SetScaleRelative($script:OriginalScaleRel)
+ Start-Sleep -Milliseconds 1500
+ $restored = [AgentDesktopProbe.Dpi]::GetScaleRange()
+ $restoredReg = $null
+ if ($monitorKeys.Count -gt 0) {
+ $restoredReg = (Get-ItemProperty -Path ('HKCU:\Control Panel\Desktop\PerMonitorSettings\' + $monitorKeys[0]) -Name 'DpiValue' -ErrorAction SilentlyContinue).DpiValue
+ }
+ $teardown = [ordered]@{
+ originalScaleRel = $script:OriginalScaleRel
+ displayConfigSetResult = $restoreResult
+ scaleRelReadBackAfterwards = $restored[2]
+ registryDpiValueAfterwards = $restoredReg
+ restored = ($restored[2] -eq $script:OriginalScaleRel)
+ }
+
+ $capture = [ordered]@{
+ question = 'what bounds delta does DPI awareness produce between two processes reading the same element, and what scale can this environment actually apply'
+ stack = 'managed UIA for BoundingRectangle; raw Win32 for awareness, DisplayConfig and metrics'
+ scope = 'api-contract for the awareness APIs; app/provider and environment-specific for the achievable scale'
+ session = $session
+ displays = $displays
+ displayScaleCapability = $scale
+ method = 'two sibling powershell children read the same probe-launched notepad window and its Edit child; the unaware arm is forced with __COMPAT_LAYER=DPIUNAWARE, the aware arm calls SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)'
+ normalizationNote = 'raw rectangles are bucketed to 8px by the KTD9 normalizer; delta fields are named deltaLeft/deltaTop/deltaWidth/deltaHeight, which the bucketing regex does not match, so the measured delta survives the normalized twin verbatim'
+ passes = $passes
+ teardown = $teardown
+ deferred = [ordered]@{
+ row = 'multi-monitor and mixed-DPI bounds behavior, and any non-zero aware-vs-unaware delta'
+ reason = 'single display, and that display reports no EDID so Windows offers exactly one scale step (min=cur=max); the 125% request is accepted by DisplayConfigSetDeviceInfo and persisted to the registry but never takes effect'
+ closesAt = 'sub-phase 2.4 (owns list_displays and per-monitor scale_factor) on a runner with a scalable display'
+ neverLeaves = 'Phase 2'
+ }
+ }
+ Write-ProbeJson -Probe $Probe -Name 'session-dpi.json' -InputObject $capture | Out-Null
+
+ $summary['sessionId'] = $session.sessionId
+ $summary['isRemoteSession'] = $session.isRemoteSession
+ $summary['displayCount'] = $displays.Count
+ $summary['scaleRelMinCurMax'] = ('' + $scale.scaleRelMin + '/' + $scale.scaleRelCurrent + '/' + $scale.scaleRelMax)
+ $summary['awarenessPerMonitorV2'] = $passes[0].awareArm.setProcessDpiAwarenessContextPerMonitorV2
+ $summary['unawareArmAwareness'] = $passes[0].unawareArm.awarenessEffective
+ $summary['awareArmAwareness'] = $passes[0].awareArm.awarenessEffective
+ $summary['windowDeltaAtRecommended'] = ('' + $passes[0].windowBoundsDelta.deltaLeft + ',' + $passes[0].windowBoundsDelta.deltaTop + ',' + $passes[0].windowBoundsDelta.deltaWidth + ',' + $passes[0].windowBoundsDelta.deltaHeight)
+ $summary['windowDeltaAt125Request'] = ('' + $passes[1].windowBoundsDelta.deltaLeft + ',' + $passes[1].windowBoundsDelta.deltaTop + ',' + $passes[1].windowBoundsDelta.deltaWidth + ',' + $passes[1].windowBoundsDelta.deltaHeight)
+ $summary['scaleActuallyAppliedAt125Request'] = $passes[1].scaleActuallyApplied
+ $summary['scaleRestored'] = $teardown.restored
+ $message = 'session and dpi measured; scale request and restoration both verified by read-back'
+} catch {
+ $status = 'fail'
+ $message = ($_.Exception.Message -replace '[\r\n]+', ' ')
+ Write-ProbeLog -Message ('probe failed: ' + $message) -Level 'error'
+} finally {
+ if ($null -ne $script:OriginalScaleRel) {
+ try { [void][AgentDesktopProbe.Dpi]::SetScaleRelative($script:OriginalScaleRel) } catch { }
+ }
+ $env:__COMPAT_LAYER = $null
+ foreach ($id in @($script:Spawned)) {
+ try { Stop-ScratchProcess -ProcessId $id } catch { Write-ProbeLog -Message ('teardown: ' + $_.Exception.Message) -Level 'warn' }
+ }
+ foreach ($f in @(Get-ChildItem -LiteralPath (Get-CaptureDir -Probe $Probe) -File -ErrorAction SilentlyContinue)) {
+ if ($f.Name -like '*.normalized') { continue }
+ if (-not (Test-CaptureRedaction -Path $f.FullName)) { $status = 'fail'; $message = ('redaction residue in ' + $f.Name) }
+ }
+}
+
+Write-ProbeResult -Probe $Probe -Status $status -Message $message -Data $summary
+if ($status -eq 'fail') { exit 1 }
+exit 0
diff --git a/probes/windows/11-electron-activation.ps1 b/probes/windows/11-electron-activation.ps1
new file mode 100644
index 0000000..1c185e6
--- /dev/null
+++ b/probes/windows/11-electron-activation.ps1
@@ -0,0 +1,337 @@
+<#
+.SYNOPSIS
+ Probe 11 (sub-phase 2.0, unit U8): Chromium/Electron accessibility activation.
+
+.DESCRIPTION
+ Grades the committed phases.md claim that Chromium 138+ "exposes a UIA tree to any
+ UIA client with no flag" against the installed Obsidian build, on both client stacks
+ (KTD1): managed System.Windows.Automation and the U7 UIA3 COM shim.
+
+ Four Obsidian instances are launched, one per (stack x flag) cell, because the first
+ UIA client to touch a renderer activates accessibility for that process - reusing one
+ instance would make the second stack's "first contact" a settled reading.
+
+ The bundled Chromium and Electron versions are read off THIS installation by scanning
+ Obsidian.exe for the embedded user-agent string. ELECTRON_RUN_AS_NODE=1 with
+ -p process.versions produces no output on this VM and the framework DLLs carry
+ component versions, so the UA scan is the determination of record. The activation
+ verdict is only graded once that version is established.
+
+ Two hazards carried from earlier units, both recorded as probe-placement hazards and
+ explicitly NOT product claims:
+ - U3: leaving other windows on top of Obsidian held it at its first-contact node
+ count through a full settle. Mechanism not isolated. This probe places Obsidian
+ itself and records the foreground owner at read time.
+ - U4: matching a bare Chrome_WidgetWin_1 window found an unrelated window and
+ recorded 9 nodes / 0% coverage as if it were the Electron answer. This probe
+ matches only windows whose owning pid is one of the launched Obsidian pids.
+#>
+[CmdletBinding()]
+param()
+
+$ErrorActionPreference = 'Stop'
+. "$PSScriptRoot\common.ps1"
+
+$Probe = '11-electron-activation'
+$script:Spawned = New-Object System.Collections.ArrayList
+
+function Initialize-WindowSupport {
+ if ('AgentDesktopProbe.Win11' -as [type]) { return }
+ Add-Type -Language CSharp -TypeDefinition @'
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace AgentDesktopProbe {
+ public static class Win11 {
+ private delegate bool EnumProc(IntPtr h, IntPtr l);
+ [DllImport("user32.dll")] private static extern bool EnumWindows(EnumProc cb, IntPtr l);
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern int GetClassNameW(IntPtr h, StringBuilder b, int max);
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern int GetWindowTextW(IntPtr h, StringBuilder b, int max);
+ [DllImport("user32.dll")] private static extern bool IsWindowVisible(IntPtr h);
+ [DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(IntPtr h, out uint pid);
+
+ public static string ClassOf(IntPtr h) { StringBuilder b = new StringBuilder(256); GetClassNameW(h, b, 256); return b.ToString(); }
+ public static string TitleOf(IntPtr h) { StringBuilder b = new StringBuilder(512); GetWindowTextW(h, b, 512); return b.ToString(); }
+ public static int PidOf(IntPtr h) { uint p; GetWindowThreadProcessId(h, out p); return (int)p; }
+
+ public static IntPtr[] VisibleTopLevel() {
+ List found = new List();
+ EnumWindows(delegate(IntPtr h, IntPtr l) { if (IsWindowVisible(h)) { found.Add(h); } return true; }, IntPtr.Zero);
+ return found.ToArray();
+ }
+ }
+}
+'@ | Out-Null
+}
+
+function Get-BundledVersions {
+ param([string]$ExePath)
+ $rx = New-Object Text.RegularExpressions.Regex 'Chrome/(\d+\.\d+\.\d+\.\d+) Electron/(\d+\.\d+\.\d+)'
+ $fs = [IO.File]::OpenRead($ExePath)
+ $buf = New-Object byte[] (8MB)
+ $prev = ''
+ $chrome = ''
+ $electron = ''
+ try {
+ while (($n = $fs.Read($buf, 0, 8MB)) -gt 0) {
+ $text = $prev + [Text.Encoding]::GetEncoding(28591).GetString($buf, 0, $n)
+ $m = $rx.Match($text)
+ if ($m.Success) { $chrome = $m.Groups[1].Value; $electron = $m.Groups[2].Value; break }
+ if ($text.Length -ge 256) { $prev = $text.Substring($text.Length - 256) } else { $prev = $text }
+ }
+ } finally { $fs.Close() }
+ return [ordered]@{
+ method = 'user-agent string scanned out of the shipped Obsidian.exe on this installation'
+ obsidianFileVersion = (Get-Item -LiteralPath $ExePath).VersionInfo.ProductVersion
+ chromiumVersion = $chrome
+ electronVersion = $electron
+ established = ($chrome -ne '' -and $electron -ne '')
+ rejectedMethods = @(
+ 'ELECTRON_RUN_AS_NODE=1 Obsidian.exe -p process.versions produced no output on this VM',
+ 'framework DLL file versions carry component versions, not the Chromium version'
+ )
+ }
+}
+
+function Start-ObsidianInstance {
+ param([string]$ExePath, [string[]]$ExtraArgs = @())
+ Initialize-WindowSupport
+ if ($ExtraArgs.Count -gt 0) { Start-Process -FilePath $ExePath -ArgumentList $ExtraArgs | Out-Null }
+ else { Start-Process -FilePath $ExePath | Out-Null }
+ $deadline = (Get-Date).AddSeconds(75)
+ while ((Get-Date) -lt $deadline) {
+ $pids = @(Get-Process -Name 'Obsidian' -ErrorAction SilentlyContinue | ForEach-Object { $_.Id })
+ foreach ($id in $pids) {
+ if (-not $script:Spawned.Contains($id)) {
+ Register-ScratchProcessId -ProcessId $id
+ [void]$script:Spawned.Add($id)
+ }
+ }
+ foreach ($h in [AgentDesktopProbe.Win11]::VisibleTopLevel()) {
+ if ([AgentDesktopProbe.Win11]::ClassOf($h) -ne 'Chrome_WidgetWin_1') { continue }
+ $owner = [AgentDesktopProbe.Win11]::PidOf($h)
+ if ($pids -notcontains $owner) { continue }
+ if (-not [AgentDesktopProbe.Win11]::TitleOf($h)) { continue }
+ Show-WindowNoActivate -WindowHandle $h -X 0 -Y 0 -Width 1280 -Height 720
+ return [pscustomobject]@{ WindowHandle = $h; ProcessId = $owner; AllPids = $pids }
+ }
+ Start-Sleep -Milliseconds 700
+ }
+ return $null
+}
+
+function Stop-AllObsidian {
+ foreach ($p in @(Get-Process -Name 'Obsidian' -ErrorAction SilentlyContinue)) {
+ try { Stop-Process -Id $p.Id -Force -ErrorAction Stop } catch { }
+ }
+ $deadline = (Get-Date).AddSeconds(20)
+ while ((Get-Date) -lt $deadline) {
+ if (-not (Get-Process -Name 'Obsidian' -ErrorAction SilentlyContinue)) { return $true }
+ Start-Sleep -Milliseconds 400
+ }
+ return (-not (Get-Process -Name 'Obsidian' -ErrorAction SilentlyContinue))
+}
+
+function Measure-ManagedTree {
+ param([IntPtr]$WindowHandle, [string]$Prefix = '')
+ Add-Type -AssemblyName UIAutomationClient
+ Add-Type -AssemblyName UIAutomationTypes
+ $root = [System.Windows.Automation.AutomationElement]::FromHandle($WindowHandle)
+ $out = [ordered]@{}
+ foreach ($view in @('RawView', 'ControlView')) {
+ $walker = if ($view -eq 'RawView') { [System.Windows.Automation.TreeWalker]::RawViewWalker } else { [System.Windows.Automation.TreeWalker]::ControlViewWalker }
+ $stack = New-Object System.Collections.Stack
+ $stack.Push($root)
+ $count = 0
+ $refable = 0
+ while ($stack.Count -gt 0 -and $count -lt 8000) {
+ $node = $stack.Pop()
+ $count++
+ if (@($node.GetSupportedPatterns()).Count -gt 0) { $refable++ }
+ $child = $walker.GetFirstChild($node)
+ while ($null -ne $child) {
+ $stack.Push($child)
+ $child = $walker.GetNextSibling($child)
+ }
+ }
+ $out[($Prefix + $view + 'Nodes')] = $count
+ $out[($Prefix + $view + 'RefableNodes')] = $refable
+ }
+ return $out
+}
+
+function Invoke-ComShim {
+ param([string]$ExePath, [string[]]$ShimArgs, [int]$TimeoutSec = 300)
+ $psi = New-Object System.Diagnostics.ProcessStartInfo
+ $psi.FileName = $ExePath
+ $psi.Arguments = ($ShimArgs -join ' ')
+ $psi.UseShellExecute = $false
+ $psi.RedirectStandardOutput = $true
+ $psi.RedirectStandardError = $true
+ $psi.CreateNoWindow = $true
+ $proc = [System.Diagnostics.Process]::Start($psi)
+ Register-ScratchProcessId -ProcessId $proc.Id
+ [void]$script:Spawned.Add($proc.Id)
+ $stdout = $proc.StandardOutput.ReadToEnd()
+ $stderr = $proc.StandardError.ReadToEnd()
+ if (-not $proc.WaitForExit($TimeoutSec * 1000)) { try { $proc.Kill() } catch { }; throw ('shim ' + $ShimArgs[0] + ' timed out') }
+ if ($proc.ExitCode -ne 0) { throw ('shim ' + $ShimArgs[0] + ' exited ' + $proc.ExitCode + ': ' + $stderr) }
+ return ($stdout | ConvertFrom-Json)
+}
+
+$status = 'ok'
+$message = ''
+$summary = [ordered]@{}
+
+try {
+ Initialize-ProbeNative
+ Initialize-WindowSupport
+ $obsidianExe = Join-Path $env:LOCALAPPDATA 'Programs\Obsidian\Obsidian.exe'
+ if (-not (Test-Path -LiteralPath $obsidianExe)) { throw ('Obsidian not installed at ' + $obsidianExe) }
+ $preexisting = @(Get-Process -Name 'Obsidian' -ErrorAction SilentlyContinue).Count -gt 0
+ $versions = Get-BundledVersions -ExePath $obsidianExe
+ if (-not $versions.established) { throw 'bundled Chromium version could not be read off this installation' }
+ Write-ProbeLog -Message ('obsidian ' + $versions.obsidianFileVersion + ' bundles chromium ' + $versions.chromiumVersion + ' / electron ' + $versions.electronVersion)
+
+ $csc = Join-Path $env:WINDIR 'Microsoft.NET\Framework64\v4.0.30319\csc.exe'
+ $buildDir = Join-Path $env:TEMP 'agent-desktop-uia3'
+ if (-not (Test-Path -LiteralPath $buildDir)) { New-Item -ItemType Directory -Path $buildDir -Force | Out-Null }
+ $shim = Join-Path $buildDir '08-uia3-com.exe'
+ $shimOut = (& $csc @('/nologo', '/target:exe', '/langversion:5', '/platform:anycpu', ('/out:' + $shim),
+ '/reference:System.dll', '/reference:System.Core.dll', (Join-Path $PSScriptRoot '08-uia3-com.cs')) 2>&1 | Out-String).Trim()
+ if ($LASTEXITCODE -ne 0) { throw ('csc.exe failed building the U7 shim: ' + $shimOut) }
+
+ $cells = @()
+ foreach ($flagArm in @(
+ [pscustomobject]@{ Name = 'no-flag'; Args = @() },
+ [pscustomobject]@{ Name = 'force-renderer-accessibility'; Args = @('--force-renderer-accessibility') })) {
+ foreach ($stack in @('managed', 'uia3-com')) {
+ if (-not (Stop-AllObsidian)) { throw 'could not bring Obsidian to a clean state before an arm' }
+ $inst = Start-ObsidianInstance -ExePath $obsidianExe -ExtraArgs $flagArm.Args
+ if ($null -eq $inst) { throw ('Obsidian window never appeared for arm ' + $flagArm.Name + '/' + $stack) }
+ $cell = [ordered]@{
+ flagArm = $flagArm.Name
+ stack = $stack
+ authoritative = ($stack -eq 'uia3-com')
+ windowClassName = [AgentDesktopProbe.Win11]::ClassOf($inst.WindowHandle)
+ windowHandle = $inst.WindowHandle.ToInt64()
+ windowHost = [ordered]@{ processId = $inst.ProcessId }
+ latencyObsidianProcessCountAtWindowDiscovery = $inst.AllPids.Count
+ foregroundOwner = [ordered]@{ processId = [AgentDesktopProbe.Native]::GetForegroundProcessId() }
+ settleMs = 8000
+ }
+ if ($stack -eq 'managed') {
+ $cell['firstContact'] = Measure-ManagedTree -WindowHandle $inst.WindowHandle -Prefix 'latencyFirstContact'
+ Start-Sleep -Milliseconds 8000
+ $cell['afterSettle'] = Measure-ManagedTree -WindowHandle $inst.WindowHandle -Prefix 'settled'
+ } else {
+ $walker = Invoke-ComShim -ExePath $shim -ShimArgs @('walker', '--hwnd', ('0x' + $inst.WindowHandle.ToInt64().ToString('x')),
+ '--label', 'obsidian', '--max-nodes', '20000', '--max-depth', '60', '--settle-ms', '8000')
+ $fc = [ordered]@{}
+ foreach ($v in $walker.result.viewsFirstContact) { $fc[('latencyFirstContact' + $v.view + 'Nodes')] = $v.nodeCount }
+ $st = [ordered]@{}
+ foreach ($v in $walker.result.viewsAfterSettle) { $st[('settled' + $v.view + 'Nodes')] = $v.nodeCount }
+ $census = Invoke-ComShim -ExePath $shim -ShimArgs @('census', '--target', ('obsidian=0x' + $inst.WindowHandle.ToInt64().ToString('x')),
+ '--max-nodes', '20000', '--max-depth', '60')
+ $t = $census.result.targets[0]
+ $st['settledRefableNodesWithAnyPattern'] = $t.elementsWithAnyPattern
+ $st['settledRefableNodesExcludingLegacy'] = $t.elementsWithAnyPatternExcludingLegacy
+ $cell['firstContact'] = $fc
+ $cell['afterSettle'] = $st
+ $cell['censusNote'] = 'census ran in a second shim process against the already-activated instance, so its counts are settled counts; per-element rows are deliberately not captured (R11: Obsidian note titles are content)'
+ }
+ $rawFc = $cell.firstContact['latencyFirstContactRawViewNodes']
+ $rawSt = $cell.afterSettle['settledRawViewNodes']
+ $cell['latencyRawViewGrowthRatio'] = if ($rawFc -gt 0) { [math]::Round(($rawSt / [double]$rawFc), 2) } else { $null }
+ $cell['activatedWithoutFlag'] = ($flagArm.Name -eq 'no-flag' -and $rawSt -gt $rawFc)
+ $cells += $cell
+ Write-ProbeLog -Message ('arm ' + $flagArm.Name + '/' + $stack + ': raw ' + $rawFc + ' -> ' + $rawSt)
+ }
+ }
+ $allGone = Stop-AllObsidian
+
+ $noFlagCom = $cells | Where-Object { $_.flagArm -eq 'no-flag' -and $_.stack -eq 'uia3-com' } | Select-Object -First 1
+ $flagCom = $cells | Where-Object { $_.flagArm -ne 'no-flag' -and $_.stack -eq 'uia3-com' } | Select-Object -First 1
+ $noFlagManaged = $cells | Where-Object { $_.flagArm -eq 'no-flag' -and $_.stack -eq 'managed' } | Select-Object -First 1
+ $flagManaged = $cells | Where-Object { $_.flagArm -ne 'no-flag' -and $_.stack -eq 'managed' } | Select-Object -First 1
+ $bothActivate = ($noFlagCom.activatedWithoutFlag -and $noFlagManaged.activatedWithoutFlag)
+ $flagVerdict = 'ungraded'
+ if ($versions.established) {
+ $major = [int]($versions.chromiumVersion -split '\.')[0]
+ if ($major -lt 138) {
+ $flagVerdict = 'flag still required - bundled Chromium predates the 138 auto-UIA default'
+ } elseif ($bothActivate -and $noFlagCom.afterSettle['settledRawViewNodes'] -eq $flagCom.afterSettle['settledRawViewNodes']) {
+ $flagVerdict = 'partially useful - redundant for eventual tree exposure: on Chromium ' + $versions.chromiumVersion +
+ ' both client stacks reach the same settled RawView count with no flag, so the phases.md claim holds for exposure. It is not redundant for first-contact readiness: without the flag first contact is deterministically the small pre-activation shell on every run and both stacks, while with the flag first contact is a race against the async tree build and was observed anywhere from a fraction of the settled count to nearly all of it. Neither arm removes the need to settle before trusting a first snapshot.'
+ } elseif ($bothActivate) {
+ $flagVerdict = 'redundant for tree exposure - both client stacks get a renderer tree with no flag on Chromium ' + $versions.chromiumVersion
+ } else {
+ $flagVerdict = 'still required - a 138+ Chromium did not expose a renderer tree without the flag on at least one stack'
+ }
+ }
+
+ $capture = [ordered]@{
+ question = 'does Chromium 138+ expose a UIA tree to any UIA client with no flag, and does --force-renderer-accessibility still change anything'
+ gradedAgainst = 'the committed phases.md line that Chromium 138+ exposes a UIA tree to any UIA client with no flag (P2-O15 / 2.4)'
+ stack = 'both: managed System.Windows.Automation and the U7 UIA3 COM shim (KTD1 - the COM rows are the authoritative ones)'
+ scope = 'app/provider - specific to this Obsidian/Electron/Chromium build; the activation mechanism is Chromium behavior, not a Windows API contract'
+ versions = $versions
+ obsidianPreexistingAtProbeStart = $preexisting
+ method = 'one fresh Obsidian instance per (stack x flag) cell, because the first UIA client to touch a renderer activates accessibility for that process'
+ placementHazards = @(
+ 'U3: an earlier revision that left other windows on top of Obsidian held it at its first-contact node count through a full 8s settle and a 16s instrumented hold, three runs running. Minimizing the covering windows fixed it. The mechanism was NOT isolated (Chromium native-window occlusion tracking is the obvious candidate, unproven). Carried here as a probe-placement hazard, explicitly not a product claim.',
+ 'U4: matching a bare Chrome_WidgetWin_1 window found an unrelated window and recorded 9 nodes / 0% coverage as if it were the Electron answer. This probe only accepts a window whose owning pid is one of the launched Obsidian pids, and reads the hosting pid back off the window.'
+ )
+ countStabilityNote = 'settled node counts are content-dependent (U7 measured RawView 172 on a bare walker run, U3 measured 119 through its probe). The stable fact is the growth ratio across the settle, not the absolute count.'
+ cells = $cells
+ findings = [ordered]@{
+ managedActivatesWithoutFlag = $noFlagManaged.activatedWithoutFlag
+ comActivatesWithoutFlag = $noFlagCom.activatedWithoutFlag
+ clientStackDivergence = ($noFlagManaged.activatedWithoutFlag -ne $noFlagCom.activatedWithoutFlag)
+ latencyComRawViewNoFlagFirstContact = $noFlagCom.firstContact['latencyFirstContactRawViewNodes']
+ latencyComRawViewWithFlagFirstContact = $flagCom.firstContact['latencyFirstContactRawViewNodes']
+ latencyManagedRawViewNoFlagFirstContact = $noFlagManaged.firstContact['latencyFirstContactRawViewNodes']
+ latencyManagedRawViewWithFlagFirstContact = $flagManaged.firstContact['latencyFirstContactRawViewNodes']
+ settledComRawViewNoFlag = $noFlagCom.afterSettle['settledRawViewNodes']
+ settledComRawViewWithFlag = $flagCom.afterSettle['settledRawViewNodes']
+ settledManagedRawViewNoFlag = $noFlagManaged.afterSettle['settledRawViewNodes']
+ settledManagedRawViewWithFlag = $flagManaged.afterSettle['settledRawViewNodes']
+ settledCountsMatchAcrossFlagArms = ($noFlagCom.afterSettle['settledRawViewNodes'] -eq $flagCom.afterSettle['settledRawViewNodes'] -and $noFlagManaged.afterSettle['settledRawViewNodes'] -eq $flagManaged.afterSettle['settledRawViewNodes'])
+ firstContactCountsAreRunVarying = 'first-contact counts are a race against Chromium async tree construction and are named latency* so the KTD9 normalizer canonicalizes them; the raw samples stay in this capture, the settled counts are the stable evidence'
+ flagVerdict = $flagVerdict
+ supersededPreFinding = 'the plan pre-finding (8 managed descendants, stable at 2/5/10s, candidate CONTRADICTS) is superseded: it was measured through an occluded window with a managed client, and neither the occlusion nor the settle window was controlled'
+ }
+ teardown = [ordered]@{ allObsidianProcessesTerminated = $allGone }
+ }
+ Write-ProbeJson -Probe $Probe -Name 'electron-activation.json' -InputObject $capture | Out-Null
+
+ $summary['chromiumVersion'] = $versions.chromiumVersion
+ $summary['electronVersion'] = $versions.electronVersion
+ $summary['obsidianVersion'] = $versions.obsidianFileVersion
+ $summary['comRawViewNoFlag'] = ('' + $capture.findings.latencyComRawViewNoFlagFirstContact + ' -> ' + $capture.findings.settledComRawViewNoFlag)
+ $summary['comRawViewWithFlag'] = ('' + $capture.findings.latencyComRawViewWithFlagFirstContact + ' -> ' + $capture.findings.settledComRawViewWithFlag)
+ $summary['managedRawViewNoFlag'] = ('' + $capture.findings.latencyManagedRawViewNoFlagFirstContact + ' -> ' + $capture.findings.settledManagedRawViewNoFlag)
+ $summary['managedRawViewWithFlag'] = ('' + $capture.findings.latencyManagedRawViewWithFlagFirstContact + ' -> ' + $capture.findings.settledManagedRawViewWithFlag)
+ $summary['clientStackDivergence'] = $capture.findings.clientStackDivergence
+ $summary['flagVerdict'] = $flagVerdict
+ $message = 'electron activation measured on both stacks with and without the flag'
+} catch {
+ $status = 'fail'
+ $message = ($_.Exception.Message -replace '[\r\n]+', ' ')
+ Write-ProbeLog -Message ('probe failed: ' + $message) -Level 'error'
+} finally {
+ [void](Stop-AllObsidian)
+ foreach ($id in @($script:Spawned)) {
+ try { Stop-ScratchProcess -ProcessId $id } catch { Write-ProbeLog -Message ('teardown: ' + $_.Exception.Message) -Level 'warn' }
+ }
+ foreach ($f in @(Get-ChildItem -LiteralPath (Get-CaptureDir -Probe $Probe) -File -ErrorAction SilentlyContinue)) {
+ if ($f.Name -like '*.normalized') { continue }
+ if (-not (Test-CaptureRedaction -Path $f.FullName)) { $status = 'fail'; $message = ('redaction residue in ' + $f.Name) }
+ }
+}
+
+Write-ProbeResult -Probe $Probe -Status $status -Message $message -Data $summary
+if ($status -eq 'fail') { exit 1 }
+exit 0
diff --git a/probes/windows/captures/09-elevation-uipi/uipi.json b/probes/windows/captures/09-elevation-uipi/uipi.json
new file mode 100644
index 0000000..b3fd9b2
--- /dev/null
+++ b/probes/windows/captures/09-elevation-uipi/uipi.json
@@ -0,0 +1,194 @@
+{
+ "question": "does UIPI block SendInput and window messages from a Medium-integrity process into a High-integrity target, and are UIA reads still allowed",
+ "invariant": "Engineering Invariant #6 (UIPI)",
+ "stack": "managed (UIA reads) + raw Win32 SendInput/SendMessage/PostMessage",
+ "scope": "api-contract",
+ "boundaryOrigin": "manufactured by common.ps1 Start-MediumIntegrityProcess (DuplicateTokenEx + SetTokenInformation(TokenIntegrityLevel, S-1-16-8192) + CreateProcessAsUser), label asserted on read-back",
+ "boundaryRuledOut": [
+ "Start-Process -Verb RunAs: AAM off on this box (FilterAdministratorToken unset, RID 500 full token) so it yields High-vs-High",
+ "runas.exe /trustlevel:0x20000: restricts the token but leaves the mandatory label at S-1-16-12288"
+ ],
+ "uacPolicy": {
+ "EnableLUA": 1,
+ "FilterAdministratorToken": "\u003cvalue not present\u003e",
+ "ConsentPromptBehaviorAdmin": 5,
+ "EnableInstallerDetection": 1,
+ "sessionIntegritySid": "S-1-16-12288",
+ "adminApprovalModeActive": false
+ },
+ "target": {
+ "process": "notepad.exe",
+ "processId": 7156,
+ "integritySid": "S-1-16-12288",
+ "windowHandle": 6948082,
+ "editChild": {
+ "className": "Edit",
+ "windowHandle": 5243138
+ },
+ "launchedByThisProbe": true
+ },
+ "arms": [
+ {
+ "arm": "high",
+ "workerIntegritySid": "S-1-16-12288",
+ "workerLaunchError": "",
+ "marker": "HHH",
+ "parentForegroundHandoff": "target foreground restored by parent ShowWindow(SW_MINIMIZE-\u003eSW_RESTORE) on the probe-launched window; SetForegroundWindow never called",
+ "targetStateBeforeInjection": "U8-BASE",
+ "targetStateAfterInjection": "HHHPU8-BASE",
+ "targetStateChanged": true,
+ "markerPresentInTargetAfterwards": true,
+ "observedBy": "parent High-integrity WM_GETTEXT on the notepad Edit child, independent of SendInput return",
+ "worker": {
+ "arm": "high",
+ "processId": 7916,
+ "addTypeCompilationAvailable": true,
+ "selfIntegritySid": "S-1-16-12288",
+ "targetIntegritySid": "S-1-16-12288",
+ "uiaRead": {
+ "fromHandle": "ok",
+ "name": "Untitled - Notepad",
+ "className": "Notepad",
+ "controlType": "Window",
+ "ownerProcessId": 7156,
+ "boundingRectangle": {
+ "Left": 476,
+ "Top": 100,
+ "Width": 768,
+ "Height": 525
+ },
+ "rawViewNodes": 3,
+ "editPatterns": [
+
+ ],
+ "editValueSeenByThisProcess": "\u003cno value/text pattern: Exception calling \"GetCurrentPattern\" with \"1\" argument(s): \"Unsupported Pattern.\"\u003e"
+ },
+ "wmGetTextFromWorker": {
+ "returned": "U8-BASE",
+ "sendMessageResult": 7,
+ "lastError": 0
+ },
+ "foregroundOwnerAtAssert": {
+ "processId": 7156
+ },
+ "foregroundAssertPre": "ok",
+ "sendInputEventsAccepted": 6,
+ "sendInputLastError": 0,
+ "sendInputStructSize": 40,
+ "foregroundAssertPost": "ok",
+ "postMessageWmChar": {
+ "returned": true,
+ "lastError": 203
+ },
+ "wmGetTextAfterFromWorker": {
+ "returned": "HHHPU8-BASE",
+ "sendMessageResult": 11,
+ "lastError": 0
+ },
+ "uiaReadAfter": {
+ "fromHandle": "ok",
+ "name": "Untitled - Notepad",
+ "className": "Notepad",
+ "controlType": "Window",
+ "ownerProcessId": 7156,
+ "boundingRectangle": {
+ "Left": 476,
+ "Top": 100,
+ "Width": 768,
+ "Height": 525
+ },
+ "rawViewNodes": 3,
+ "editPatterns": [
+
+ ],
+ "editValueSeenByThisProcess": "\u003cno value/text pattern: Exception calling \"GetCurrentPattern\" with \"1\" argument(s): \"Unsupported Pattern.\"\u003e"
+ }
+ }
+ },
+ {
+ "arm": "medium",
+ "workerIntegritySid": "S-1-16-8192",
+ "workerLaunchError": "",
+ "marker": "MMM",
+ "parentForegroundHandoff": "target foreground restored by parent ShowWindow(SW_MINIMIZE-\u003eSW_RESTORE) on the probe-launched window; SetForegroundWindow never called",
+ "targetStateBeforeInjection": "U8-BASE",
+ "targetStateAfterInjection": "U8-BASE",
+ "targetStateChanged": false,
+ "markerPresentInTargetAfterwards": false,
+ "observedBy": "parent High-integrity WM_GETTEXT on the notepad Edit child, independent of SendInput return",
+ "worker": {
+ "arm": "medium",
+ "processId": 7508,
+ "addTypeCompilationAvailable": false,
+ "addTypeCompilationError": "A required privilege is not held by the client",
+ "selfIntegritySid": "S-1-16-8192",
+ "targetIntegritySid": "S-1-16-12288",
+ "uiaRead": {
+ "fromHandle": "ok",
+ "name": "Untitled - Notepad",
+ "className": "Notepad",
+ "controlType": "Window",
+ "ownerProcessId": 7156,
+ "boundingRectangle": {
+ "Left": 476,
+ "Top": 100,
+ "Width": 768,
+ "Height": 525
+ },
+ "rawViewNodes": 3,
+ "editPatterns": [
+
+ ],
+ "editValueSeenByThisProcess": "\u003cno value/text pattern: Exception calling \"GetCurrentPattern\" with \"1\" argument(s): \"Unsupported Pattern.\"\u003e"
+ },
+ "wmGetTextFromWorker": {
+ "returned": "U8-BASE",
+ "sendMessageResult": 7,
+ "lastError": 0
+ },
+ "foregroundOwnerAtAssert": {
+ "processId": 7156
+ },
+ "foregroundAssertPre": "ok",
+ "sendInputEventsAccepted": 6,
+ "sendInputLastError": 0,
+ "sendInputStructSize": 40,
+ "foregroundAssertPost": "ok",
+ "postMessageWmChar": {
+ "returned": false,
+ "lastError": 5
+ },
+ "wmGetTextAfterFromWorker": {
+ "returned": "U8-BASE",
+ "sendMessageResult": 7,
+ "lastError": 0
+ },
+ "uiaReadAfter": {
+ "fromHandle": "ok",
+ "name": "Untitled - Notepad",
+ "className": "Notepad",
+ "controlType": "Window",
+ "ownerProcessId": 7156,
+ "boundingRectangle": {
+ "Left": 476,
+ "Top": 100,
+ "Width": 768,
+ "Height": 525
+ },
+ "rawViewNodes": 3,
+ "editPatterns": [
+
+ ],
+ "editValueSeenByThisProcess": "\u003cno value/text pattern: Exception calling \"GetCurrentPattern\" with \"1\" argument(s): \"Unsupported Pattern.\"\u003e"
+ }
+ }
+ }
+ ],
+ "findings": {
+ "uiaReadFromMediumAgainstHigh": "ok",
+ "sendInputFromHighLanded": true,
+ "sendInputFromMediumLanded": false,
+ "sendInputReturnIsNotEvidence": "SendInput reports the event count it accepted in both arms; only the re-read of the target distinguishes them",
+ "wmGetTextFromMediumBlocked": false
+ }
+}
\ No newline at end of file
diff --git a/probes/windows/captures/09-elevation-uipi/uipi.json.normalized b/probes/windows/captures/09-elevation-uipi/uipi.json.normalized
new file mode 100644
index 0000000..b9aca38
--- /dev/null
+++ b/probes/windows/captures/09-elevation-uipi/uipi.json.normalized
@@ -0,0 +1,194 @@
+{
+ "question": "does UIPI block SendInput and window messages from a Medium-integrity process into a High-integrity target, and are UIA reads still allowed",
+ "invariant": "Engineering Invariant #6 (UIPI)",
+ "stack": "managed (UIA reads) + raw Win32 SendInput/SendMessage/PostMessage",
+ "scope": "api-contract",
+ "boundaryOrigin": "manufactured by common.ps1 Start-MediumIntegrityProcess (DuplicateTokenEx + SetTokenInformation(TokenIntegrityLevel, S-1-16-8192) + CreateProcessAsUser), label asserted on read-back",
+ "boundaryRuledOut": [
+ "Start-Process -Verb RunAs: AAM off on this box (FilterAdministratorToken unset, RID 500 full token) so it yields High-vs-High",
+ "runas.exe /trustlevel:0x20000: restricts the token but leaves the mandatory label at S-1-16-12288"
+ ],
+ "uacPolicy": {
+ "EnableLUA": 1,
+ "FilterAdministratorToken": "\u003cvalue not present\u003e",
+ "ConsentPromptBehaviorAdmin": 5,
+ "EnableInstallerDetection": 1,
+ "sessionIntegritySid": "S-1-16-12288",
+ "adminApprovalModeActive": false
+ },
+ "target": {
+ "process": "notepad.exe",
+ "processId": ,
+ "integritySid": "S-1-16-12288",
+ "windowHandle": ,
+ "editChild": {
+ "className": "Edit",
+ "windowHandle":
+ },
+ "launchedByThisProbe": true
+ },
+ "arms": [
+ {
+ "arm": "high",
+ "workerIntegritySid": "S-1-16-12288",
+ "workerLaunchError": "",
+ "marker": "HHH",
+ "parentForegroundHandoff": "target foreground restored by parent ShowWindow(SW_MINIMIZE-\u003eSW_RESTORE) on the probe-launched window; SetForegroundWindow never called",
+ "targetStateBeforeInjection": "U8-BASE",
+ "targetStateAfterInjection": "HHHPU8-BASE",
+ "targetStateChanged": true,
+ "markerPresentInTargetAfterwards": true,
+ "observedBy": "parent High-integrity WM_GETTEXT on the notepad Edit child, independent of SendInput return",
+ "worker": {
+ "arm": "high",
+ "processId": ,
+ "addTypeCompilationAvailable": true,
+ "selfIntegritySid": "S-1-16-12288",
+ "targetIntegritySid": "S-1-16-12288",
+ "uiaRead": {
+ "fromHandle": "ok",
+ "name": "Untitled - Notepad",
+ "className": "Notepad",
+ "controlType": "Window",
+ "ownerProcessId": ,
+ "boundingRectangle": {
+ "Left": 480,
+ "Top": 96,
+ "Width": 768,
+ "Height": 528
+ },
+ "rawViewNodes": 3,
+ "editPatterns": [
+
+ ],
+ "editValueSeenByThisProcess": "\u003cno value/text pattern: Exception calling \"GetCurrentPattern\" with \"1\" argument(s): \"Unsupported Pattern.\"\u003e"
+ },
+ "wmGetTextFromWorker": {
+ "returned": "U8-BASE",
+ "sendMessageResult": 7,
+ "lastError": 0
+ },
+ "foregroundOwnerAtAssert": {
+ "processId":
+ },
+ "foregroundAssertPre": "ok",
+ "sendInputEventsAccepted": 6,
+ "sendInputLastError": 0,
+ "sendInputStructSize": 40,
+ "foregroundAssertPost": "ok",
+ "postMessageWmChar": {
+ "returned": true,
+ "lastError": 203
+ },
+ "wmGetTextAfterFromWorker": {
+ "returned": "HHHPU8-BASE",
+ "sendMessageResult": 11,
+ "lastError": 0
+ },
+ "uiaReadAfter": {
+ "fromHandle": "ok",
+ "name": "Untitled - Notepad",
+ "className": "Notepad",
+ "controlType": "Window",
+ "ownerProcessId": ,
+ "boundingRectangle": {
+ "Left": 480,
+ "Top": 96,
+ "Width": 768,
+ "Height": 528
+ },
+ "rawViewNodes": 3,
+ "editPatterns": [
+
+ ],
+ "editValueSeenByThisProcess": "\u003cno value/text pattern: Exception calling \"GetCurrentPattern\" with \"1\" argument(s): \"Unsupported Pattern.\"\u003e"
+ }
+ }
+ },
+ {
+ "arm": "medium",
+ "workerIntegritySid": "S-1-16-8192",
+ "workerLaunchError": "",
+ "marker": "MMM",
+ "parentForegroundHandoff": "target foreground restored by parent ShowWindow(SW_MINIMIZE-\u003eSW_RESTORE) on the probe-launched window; SetForegroundWindow never called",
+ "targetStateBeforeInjection": "U8-BASE",
+ "targetStateAfterInjection": "U8-BASE",
+ "targetStateChanged": false,
+ "markerPresentInTargetAfterwards": false,
+ "observedBy": "parent High-integrity WM_GETTEXT on the notepad Edit child, independent of SendInput return",
+ "worker": {
+ "arm": "medium",
+ "processId": ,
+ "addTypeCompilationAvailable": false,
+ "addTypeCompilationError": "A required privilege is not held by the client",
+ "selfIntegritySid": "S-1-16-8192",
+ "targetIntegritySid": "S-1-16-12288",
+ "uiaRead": {
+ "fromHandle": "ok",
+ "name": "Untitled - Notepad",
+ "className": "Notepad",
+ "controlType": "Window",
+ "ownerProcessId": ,
+ "boundingRectangle": {
+ "Left": 480,
+ "Top": 96,
+ "Width": 768,
+ "Height": 528
+ },
+ "rawViewNodes": 3,
+ "editPatterns": [
+
+ ],
+ "editValueSeenByThisProcess": "\u003cno value/text pattern: Exception calling \"GetCurrentPattern\" with \"1\" argument(s): \"Unsupported Pattern.\"\u003e"
+ },
+ "wmGetTextFromWorker": {
+ "returned": "U8-BASE",
+ "sendMessageResult": 7,
+ "lastError": 0
+ },
+ "foregroundOwnerAtAssert": {
+ "processId":
+ },
+ "foregroundAssertPre": "ok",
+ "sendInputEventsAccepted": 6,
+ "sendInputLastError": 0,
+ "sendInputStructSize": 40,
+ "foregroundAssertPost": "ok",
+ "postMessageWmChar": {
+ "returned": false,
+ "lastError": 5
+ },
+ "wmGetTextAfterFromWorker": {
+ "returned": "U8-BASE",
+ "sendMessageResult": 7,
+ "lastError": 0
+ },
+ "uiaReadAfter": {
+ "fromHandle": "ok",
+ "name": "Untitled - Notepad",
+ "className": "Notepad",
+ "controlType": "Window",
+ "ownerProcessId": ,
+ "boundingRectangle": {
+ "Left": 480,
+ "Top": 96,
+ "Width": 768,
+ "Height": 528
+ },
+ "rawViewNodes": 3,
+ "editPatterns": [
+
+ ],
+ "editValueSeenByThisProcess": "\u003cno value/text pattern: Exception calling \"GetCurrentPattern\" with \"1\" argument(s): \"Unsupported Pattern.\"\u003e"
+ }
+ }
+ }
+ ],
+ "findings": {
+ "uiaReadFromMediumAgainstHigh": "ok",
+ "sendInputFromHighLanded": true,
+ "sendInputFromMediumLanded": false,
+ "sendInputReturnIsNotEvidence": "SendInput reports the event count it accepted in both arms; only the re-read of the target distinguishes them",
+ "wmGetTextFromMediumBlocked": false
+ }
+}
\ No newline at end of file
diff --git a/probes/windows/captures/10-session-dpi/session-dpi.json b/probes/windows/captures/10-session-dpi/session-dpi.json
new file mode 100644
index 0000000..e822901
--- /dev/null
+++ b/probes/windows/captures/10-session-dpi/session-dpi.json
@@ -0,0 +1,224 @@
+{
+ "question": "what bounds delta does DPI awareness produce between two processes reading the same element, and what scale can this environment actually apply",
+ "stack": "managed UIA for BoundingRectangle; raw Win32 for awareness, DisplayConfig and metrics",
+ "scope": "api-contract for the awareness APIs; app/provider and environment-specific for the achievable scale",
+ "session": {
+ "sessionId": 1,
+ "activeConsoleSessionId": 1,
+ "isRemoteSession": false,
+ "windowStation": "Console",
+ "note": "RDP session-transition behavior is not measurable here (physical console session); it closes at sub-phase 2.1 runner registration"
+ },
+ "displays": [
+ {
+ "primary": true,
+ "Left": 0,
+ "Top": 0,
+ "Width": 1639,
+ "Height": 732
+ }
+ ],
+ "displayScaleCapability": {
+ "displayConfigGetResult": 0,
+ "scaleRelMin": 0,
+ "scaleRelCurrent": 0,
+ "scaleRelMax": 0,
+ "monitorRegistryKeys": [
+ "NOEDID_15AD_0405_00000000_000F0000_0^20ED182961F2CFDB3A2D28C95A99744F"
+ ],
+ "stepsAvailableAboveRecommended": 0
+ },
+ "method": "two sibling powershell children read the same probe-launched notepad window and its Edit child; the unaware arm is forced with __COMPAT_LAYER=DPIUNAWARE, the aware arm calls SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)",
+ "normalizationNote": "raw rectangles are bucketed to 8px by the KTD9 normalizer; delta fields are named deltaLeft/deltaTop/deltaWidth/deltaHeight, which the bucketing regex does not match, so the measured delta survives the normalized twin verbatim",
+ "passes": [
+ {
+ "pass": "recommended",
+ "requested": "display recommended scale (100%)",
+ "requestedScaleRel": 0,
+ "displayConfigSetResult": null,
+ "scaleRelReportedAfterwards": 0,
+ "registryDpiValueAfterwards": 0,
+ "effectiveDpiSeenByAwareArm": 96,
+ "scaleActuallyApplied": false,
+ "awareArm": {
+ "mode": "aware",
+ "processId": 8764,
+ "awarenessAtStartup": "PROCESS_DPI_UNAWARE",
+ "setProcessDpiAwarenessContextPerMonitorV2": "succeeded",
+ "awarenessEffective": "PROCESS_PER_MONITOR_DPI_AWARE",
+ "monitorEffectiveDpiX": 96,
+ "monitorEffectiveDpiY": 96,
+ "logPixels": 96,
+ "screenMetricsCx": 1639,
+ "screenMetricsCy": 732,
+ "getWindowRect": {
+ "Left": 476,
+ "Top": 100,
+ "Right": 1244,
+ "Bottom": 625
+ },
+ "uiaBounds_window": {
+ "className": "Notepad",
+ "Left": 476,
+ "Top": 100,
+ "Width": 768,
+ "Height": 525
+ },
+ "uiaBounds_editChild": {
+ "className": "Edit",
+ "Left": 484,
+ "Top": 151,
+ "Width": 752,
+ "Height": 443
+ }
+ },
+ "unawareArm": {
+ "mode": "unaware",
+ "processId": 304,
+ "awarenessAtStartup": "PROCESS_DPI_UNAWARE",
+ "setProcessDpiAwarenessContextPerMonitorV2": "not attempted (unaware arm, forced with __COMPAT_LAYER=DPIUNAWARE)",
+ "awarenessEffective": "PROCESS_DPI_UNAWARE",
+ "monitorEffectiveDpiX": 96,
+ "monitorEffectiveDpiY": 96,
+ "logPixels": 96,
+ "screenMetricsCx": 1639,
+ "screenMetricsCy": 732,
+ "getWindowRect": {
+ "Left": 476,
+ "Top": 100,
+ "Right": 1244,
+ "Bottom": 625
+ },
+ "uiaBounds_window": {
+ "className": "Notepad",
+ "Left": 476,
+ "Top": 100,
+ "Width": 768,
+ "Height": 525
+ },
+ "uiaBounds_editChild": {
+ "className": "Edit",
+ "Left": 484,
+ "Top": 151,
+ "Width": 752,
+ "Height": 443
+ }
+ },
+ "windowBoundsDelta": {
+ "measurable": true,
+ "deltaLeft": 0,
+ "deltaTop": 0,
+ "deltaWidth": 0,
+ "deltaHeight": 0
+ },
+ "editChildBoundsDelta": {
+ "measurable": true,
+ "deltaLeft": 0,
+ "deltaTop": 0,
+ "deltaWidth": 0,
+ "deltaHeight": 0
+ }
+ },
+ {
+ "pass": "plus-one-step",
+ "requested": "one scale step above recommended (125%)",
+ "requestedScaleRel": 1,
+ "displayConfigSetResult": 0,
+ "scaleRelReportedAfterwards": 1,
+ "registryDpiValueAfterwards": 1,
+ "effectiveDpiSeenByAwareArm": 96,
+ "scaleActuallyApplied": false,
+ "awareArm": {
+ "mode": "aware",
+ "processId": 8412,
+ "awarenessAtStartup": "PROCESS_DPI_UNAWARE",
+ "setProcessDpiAwarenessContextPerMonitorV2": "succeeded",
+ "awarenessEffective": "PROCESS_PER_MONITOR_DPI_AWARE",
+ "monitorEffectiveDpiX": 96,
+ "monitorEffectiveDpiY": 96,
+ "logPixels": 96,
+ "screenMetricsCx": 1639,
+ "screenMetricsCy": 732,
+ "getWindowRect": {
+ "Left": 476,
+ "Top": 100,
+ "Right": 1244,
+ "Bottom": 625
+ },
+ "uiaBounds_window": {
+ "className": "Notepad",
+ "Left": 476,
+ "Top": 100,
+ "Width": 768,
+ "Height": 525
+ },
+ "uiaBounds_editChild": {
+ "className": "Edit",
+ "Left": 484,
+ "Top": 151,
+ "Width": 752,
+ "Height": 443
+ }
+ },
+ "unawareArm": {
+ "mode": "unaware",
+ "processId": 5060,
+ "awarenessAtStartup": "PROCESS_DPI_UNAWARE",
+ "setProcessDpiAwarenessContextPerMonitorV2": "not attempted (unaware arm, forced with __COMPAT_LAYER=DPIUNAWARE)",
+ "awarenessEffective": "PROCESS_DPI_UNAWARE",
+ "monitorEffectiveDpiX": 96,
+ "monitorEffectiveDpiY": 96,
+ "logPixels": 96,
+ "screenMetricsCx": 1639,
+ "screenMetricsCy": 732,
+ "getWindowRect": {
+ "Left": 476,
+ "Top": 100,
+ "Right": 1244,
+ "Bottom": 625
+ },
+ "uiaBounds_window": {
+ "className": "Notepad",
+ "Left": 476,
+ "Top": 100,
+ "Width": 768,
+ "Height": 525
+ },
+ "uiaBounds_editChild": {
+ "className": "Edit",
+ "Left": 484,
+ "Top": 151,
+ "Width": 752,
+ "Height": 443
+ }
+ },
+ "windowBoundsDelta": {
+ "measurable": true,
+ "deltaLeft": 0,
+ "deltaTop": 0,
+ "deltaWidth": 0,
+ "deltaHeight": 0
+ },
+ "editChildBoundsDelta": {
+ "measurable": true,
+ "deltaLeft": 0,
+ "deltaTop": 0,
+ "deltaWidth": 0,
+ "deltaHeight": 0
+ }
+ }
+ ],
+ "teardown": {
+ "originalScaleRel": 0,
+ "displayConfigSetResult": 0,
+ "scaleRelReadBackAfterwards": 0,
+ "registryDpiValueAfterwards": 0,
+ "restored": true
+ },
+ "deferred": {
+ "row": "multi-monitor and mixed-DPI bounds behavior, and any non-zero aware-vs-unaware delta",
+ "reason": "single display, and that display reports no EDID so Windows offers exactly one scale step (min=cur=max); the 125% request is accepted by DisplayConfigSetDeviceInfo and persisted to the registry but never takes effect",
+ "closesAt": "sub-phase 2.4 (owns list_displays and per-monitor scale_factor) on a runner with a scalable display",
+ "neverLeaves": "Phase 2"
+ }
+}
\ No newline at end of file
diff --git a/probes/windows/captures/10-session-dpi/session-dpi.json.normalized b/probes/windows/captures/10-session-dpi/session-dpi.json.normalized
new file mode 100644
index 0000000..ac02188
--- /dev/null
+++ b/probes/windows/captures/10-session-dpi/session-dpi.json.normalized
@@ -0,0 +1,224 @@
+{
+ "question": "what bounds delta does DPI awareness produce between two processes reading the same element, and what scale can this environment actually apply",
+ "stack": "managed UIA for BoundingRectangle; raw Win32 for awareness, DisplayConfig and metrics",
+ "scope": "api-contract for the awareness APIs; app/provider and environment-specific for the achievable scale",
+ "session": {
+ "sessionId": 1,
+ "activeConsoleSessionId": 1,
+ "isRemoteSession": false,
+ "windowStation": "Console",
+ "note": "RDP session-transition behavior is not measurable here (physical console session); it closes at sub-phase 2.1 runner registration"
+ },
+ "displays": [
+ {
+ "primary": true,
+ "Left": 0,
+ "Top": 0,
+ "Width": 1640,
+ "Height": 736
+ }
+ ],
+ "displayScaleCapability": {
+ "displayConfigGetResult": 0,
+ "scaleRelMin": 0,
+ "scaleRelCurrent": 0,
+ "scaleRelMax": 0,
+ "monitorRegistryKeys": [
+ "NOEDID_15AD_0405_00000000_000F0000_0^20ED182961F2CFDB3A2D28C95A99744F"
+ ],
+ "stepsAvailableAboveRecommended": 0
+ },
+ "method": "two sibling powershell children read the same probe-launched notepad window and its Edit child; the unaware arm is forced with __COMPAT_LAYER=DPIUNAWARE, the aware arm calls SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)",
+ "normalizationNote": "raw rectangles are bucketed to 8px by the KTD9 normalizer; delta fields are named deltaLeft/deltaTop/deltaWidth/deltaHeight, which the bucketing regex does not match, so the measured delta survives the normalized twin verbatim",
+ "passes": [
+ {
+ "pass": "recommended",
+ "requested": "display recommended scale (100%)",
+ "requestedScaleRel": 0,
+ "displayConfigSetResult": null,
+ "scaleRelReportedAfterwards": 0,
+ "registryDpiValueAfterwards": 0,
+ "effectiveDpiSeenByAwareArm": 96,
+ "scaleActuallyApplied": false,
+ "awareArm": {
+ "mode": "aware",
+ "processId": ,
+ "awarenessAtStartup": "PROCESS_DPI_UNAWARE",
+ "setProcessDpiAwarenessContextPerMonitorV2": "succeeded",
+ "awarenessEffective": "PROCESS_PER_MONITOR_DPI_AWARE",
+ "monitorEffectiveDpiX": 96,
+ "monitorEffectiveDpiY": 96,
+ "logPixels": 96,
+ "screenMetricsCx": 1639,
+ "screenMetricsCy": 732,
+ "getWindowRect": {
+ "Left": 480,
+ "Top": 96,
+ "Right": 1248,
+ "Bottom": 624
+ },
+ "uiaBounds_window": {
+ "className": "Notepad",
+ "Left": 480,
+ "Top": 96,
+ "Width": 768,
+ "Height": 528
+ },
+ "uiaBounds_editChild": {
+ "className": "Edit",
+ "Left": 480,
+ "Top": 152,
+ "Width": 752,
+ "Height": 440
+ }
+ },
+ "unawareArm": {
+ "mode": "unaware",
+ "processId": ,
+ "awarenessAtStartup": "PROCESS_DPI_UNAWARE",
+ "setProcessDpiAwarenessContextPerMonitorV2": "not attempted (unaware arm, forced with __COMPAT_LAYER=DPIUNAWARE)",
+ "awarenessEffective": "PROCESS_DPI_UNAWARE",
+ "monitorEffectiveDpiX": 96,
+ "monitorEffectiveDpiY": 96,
+ "logPixels": 96,
+ "screenMetricsCx": 1639,
+ "screenMetricsCy": 732,
+ "getWindowRect": {
+ "Left": 480,
+ "Top": 96,
+ "Right": 1248,
+ "Bottom": 624
+ },
+ "uiaBounds_window": {
+ "className": "Notepad",
+ "Left": 480,
+ "Top": 96,
+ "Width": 768,
+ "Height": 528
+ },
+ "uiaBounds_editChild": {
+ "className": "Edit",
+ "Left": 480,
+ "Top": 152,
+ "Width": 752,
+ "Height": 440
+ }
+ },
+ "windowBoundsDelta": {
+ "measurable": true,
+ "deltaLeft": 0,
+ "deltaTop": 0,
+ "deltaWidth": 0,
+ "deltaHeight": 0
+ },
+ "editChildBoundsDelta": {
+ "measurable": true,
+ "deltaLeft": 0,
+ "deltaTop": 0,
+ "deltaWidth": 0,
+ "deltaHeight": 0
+ }
+ },
+ {
+ "pass": "plus-one-step",
+ "requested": "one scale step above recommended (125%)",
+ "requestedScaleRel": 1,
+ "displayConfigSetResult": 0,
+ "scaleRelReportedAfterwards": 1,
+ "registryDpiValueAfterwards": 1,
+ "effectiveDpiSeenByAwareArm": 96,
+ "scaleActuallyApplied": false,
+ "awareArm": {
+ "mode": "aware",
+ "processId": ,
+ "awarenessAtStartup": "PROCESS_DPI_UNAWARE",
+ "setProcessDpiAwarenessContextPerMonitorV2": "succeeded",
+ "awarenessEffective": "PROCESS_PER_MONITOR_DPI_AWARE",
+ "monitorEffectiveDpiX": 96,
+ "monitorEffectiveDpiY": 96,
+ "logPixels": 96,
+ "screenMetricsCx": 1639,
+ "screenMetricsCy": 732,
+ "getWindowRect": {
+ "Left": 480,
+ "Top": 96,
+ "Right": 1248,
+ "Bottom": 624
+ },
+ "uiaBounds_window": {
+ "className": "Notepad",
+ "Left": 480,
+ "Top": 96,
+ "Width": 768,
+ "Height": 528
+ },
+ "uiaBounds_editChild": {
+ "className": "Edit",
+ "Left": 480,
+ "Top": 152,
+ "Width": 752,
+ "Height": 440
+ }
+ },
+ "unawareArm": {
+ "mode": "unaware",
+ "processId": ,
+ "awarenessAtStartup": "PROCESS_DPI_UNAWARE",
+ "setProcessDpiAwarenessContextPerMonitorV2": "not attempted (unaware arm, forced with __COMPAT_LAYER=DPIUNAWARE)",
+ "awarenessEffective": "PROCESS_DPI_UNAWARE",
+ "monitorEffectiveDpiX": 96,
+ "monitorEffectiveDpiY": 96,
+ "logPixels": 96,
+ "screenMetricsCx": 1639,
+ "screenMetricsCy": 732,
+ "getWindowRect": {
+ "Left": 480,
+ "Top": 96,
+ "Right": 1248,
+ "Bottom": 624
+ },
+ "uiaBounds_window": {
+ "className": "Notepad",
+ "Left": 480,
+ "Top": 96,
+ "Width": 768,
+ "Height": 528
+ },
+ "uiaBounds_editChild": {
+ "className": "Edit",
+ "Left": 480,
+ "Top": 152,
+ "Width": 752,
+ "Height": 440
+ }
+ },
+ "windowBoundsDelta": {
+ "measurable": true,
+ "deltaLeft": 0,
+ "deltaTop": 0,
+ "deltaWidth": 0,
+ "deltaHeight": 0
+ },
+ "editChildBoundsDelta": {
+ "measurable": true,
+ "deltaLeft": 0,
+ "deltaTop": 0,
+ "deltaWidth": 0,
+ "deltaHeight": 0
+ }
+ }
+ ],
+ "teardown": {
+ "originalScaleRel": 0,
+ "displayConfigSetResult": 0,
+ "scaleRelReadBackAfterwards": 0,
+ "registryDpiValueAfterwards": 0,
+ "restored": true
+ },
+ "deferred": {
+ "row": "multi-monitor and mixed-DPI bounds behavior, and any non-zero aware-vs-unaware delta",
+ "reason": "single display, and that display reports no EDID so Windows offers exactly one scale step (min=cur=max); the 125% request is accepted by DisplayConfigSetDeviceInfo and persisted to the registry but never takes effect",
+ "closesAt": "sub-phase 2.4 (owns list_displays and per-monitor scale_factor) on a runner with a scalable display",
+ "neverLeaves": "Phase 2"
+ }
+}
\ No newline at end of file
diff --git a/probes/windows/captures/11-electron-activation/electron-activation.json b/probes/windows/captures/11-electron-activation/electron-activation.json
new file mode 100644
index 0000000..bd70644
--- /dev/null
+++ b/probes/windows/captures/11-electron-activation/electron-activation.json
@@ -0,0 +1,164 @@
+{
+ "question": "does Chromium 138+ expose a UIA tree to any UIA client with no flag, and does --force-renderer-accessibility still change anything",
+ "gradedAgainst": "the committed phases.md line that Chromium 138+ exposes a UIA tree to any UIA client with no flag (P2-O15 / 2.4)",
+ "stack": "both: managed System.Windows.Automation and the U7 UIA3 COM shim (KTD1 - the COM rows are the authoritative ones)",
+ "scope": "app/provider - specific to this Obsidian/Electron/Chromium build; the activation mechanism is Chromium behavior, not a Windows API contract",
+ "versions": {
+ "method": "user-agent string scanned out of the shipped Obsidian.exe on this installation",
+ "obsidianFileVersion": "1.12.7.0",
+ "chromiumVersion": "142.0.7444.265",
+ "electronVersion": "39.8.3",
+ "established": true,
+ "rejectedMethods": [
+ "ELECTRON_RUN_AS_NODE=1 Obsidian.exe -p process.versions produced no output on this VM",
+ "framework DLL file versions carry component versions, not the Chromium version"
+ ]
+ },
+ "obsidianPreexistingAtProbeStart": false,
+ "method": "one fresh Obsidian instance per (stack x flag) cell, because the first UIA client to touch a renderer activates accessibility for that process",
+ "placementHazards": [
+ "U3: an earlier revision that left other windows on top of Obsidian held it at its first-contact node count through a full 8s settle and a 16s instrumented hold, three runs running. Minimizing the covering windows fixed it. The mechanism was NOT isolated (Chromium native-window occlusion tracking is the obvious candidate, unproven). Carried here as a probe-placement hazard, explicitly not a product claim.",
+ "U4: matching a bare Chrome_WidgetWin_1 window found an unrelated window and recorded 9 nodes / 0% coverage as if it were the Electron answer. This probe only accepts a window whose owning pid is one of the launched Obsidian pids, and reads the hosting pid back off the window."
+ ],
+ "countStabilityNote": "settled node counts are content-dependent (U7 measured RawView 172 on a bare walker run, U3 measured 119 through its probe). The stable fact is the growth ratio across the settle, not the absolute count.",
+ "cells": [
+ {
+ "flagArm": "no-flag",
+ "stack": "managed",
+ "authoritative": false,
+ "windowClassName": "Chrome_WidgetWin_1",
+ "windowHandle": 5309714,
+ "windowHost": {
+ "processId": 9204
+ },
+ "latencyObsidianProcessCountAtWindowDiscovery": 4,
+ "foregroundOwner": {
+ "processId": 980
+ },
+ "settleMs": 8000,
+ "firstContact": {
+ "latencyFirstContactRawViewNodes": 13,
+ "latencyFirstContactRawViewRefableNodes": 13,
+ "latencyFirstContactControlViewNodes": 130,
+ "latencyFirstContactControlViewRefableNodes": 130
+ },
+ "afterSettle": {
+ "settledRawViewNodes": 172,
+ "settledRawViewRefableNodes": 172,
+ "settledControlViewNodes": 133,
+ "settledControlViewRefableNodes": 133
+ },
+ "latencyRawViewGrowthRatio": 13.23,
+ "activatedWithoutFlag": true
+ },
+ {
+ "flagArm": "no-flag",
+ "stack": "uia3-com",
+ "authoritative": true,
+ "windowClassName": "Chrome_WidgetWin_1",
+ "windowHandle": 6422788,
+ "windowHost": {
+ "processId": 9000
+ },
+ "latencyObsidianProcessCountAtWindowDiscovery": 4,
+ "foregroundOwner": {
+ "processId": 980
+ },
+ "settleMs": 8000,
+ "firstContact": {
+ "latencyFirstContactRawViewNodes": 13,
+ "latencyFirstContactControlViewNodes": 9,
+ "latencyFirstContactContentViewNodes": 9
+ },
+ "afterSettle": {
+ "settledRawViewNodes": 172,
+ "settledControlViewNodes": 133,
+ "settledContentViewNodes": 133,
+ "settledRefableNodesWithAnyPattern": 171,
+ "settledRefableNodesExcludingLegacy": 171
+ },
+ "censusNote": "census ran in a second shim process against the already-activated instance, so its counts are settled counts; per-element rows are deliberately not captured (R11: Obsidian note titles are content)",
+ "latencyRawViewGrowthRatio": 13.23,
+ "activatedWithoutFlag": true
+ },
+ {
+ "flagArm": "force-renderer-accessibility",
+ "stack": "managed",
+ "authoritative": false,
+ "windowClassName": "Chrome_WidgetWin_1",
+ "windowHandle": 11993994,
+ "windowHost": {
+ "processId": 7840
+ },
+ "latencyObsidianProcessCountAtWindowDiscovery": 4,
+ "foregroundOwner": {
+ "processId": 980
+ },
+ "settleMs": 8000,
+ "firstContact": {
+ "latencyFirstContactRawViewNodes": 164,
+ "latencyFirstContactRawViewRefableNodes": 163,
+ "latencyFirstContactControlViewNodes": 133,
+ "latencyFirstContactControlViewRefableNodes": 133
+ },
+ "afterSettle": {
+ "settledRawViewNodes": 172,
+ "settledRawViewRefableNodes": 172,
+ "settledControlViewNodes": 133,
+ "settledControlViewRefableNodes": 133
+ },
+ "latencyRawViewGrowthRatio": 1.05,
+ "activatedWithoutFlag": false
+ },
+ {
+ "flagArm": "force-renderer-accessibility",
+ "stack": "uia3-com",
+ "authoritative": true,
+ "windowClassName": "Chrome_WidgetWin_1",
+ "windowHandle": 5570818,
+ "windowHost": {
+ "processId": 6108
+ },
+ "latencyObsidianProcessCountAtWindowDiscovery": 4,
+ "foregroundOwner": {
+ "processId": 980
+ },
+ "settleMs": 8000,
+ "firstContact": {
+ "latencyFirstContactRawViewNodes": 141,
+ "latencyFirstContactControlViewNodes": 127,
+ "latencyFirstContactContentViewNodes": 131
+ },
+ "afterSettle": {
+ "settledRawViewNodes": 172,
+ "settledControlViewNodes": 133,
+ "settledContentViewNodes": 133,
+ "settledRefableNodesWithAnyPattern": 172,
+ "settledRefableNodesExcludingLegacy": 172
+ },
+ "censusNote": "census ran in a second shim process against the already-activated instance, so its counts are settled counts; per-element rows are deliberately not captured (R11: Obsidian note titles are content)",
+ "latencyRawViewGrowthRatio": 1.22,
+ "activatedWithoutFlag": false
+ }
+ ],
+ "findings": {
+ "managedActivatesWithoutFlag": true,
+ "comActivatesWithoutFlag": true,
+ "clientStackDivergence": false,
+ "latencyComRawViewNoFlagFirstContact": 13,
+ "latencyComRawViewWithFlagFirstContact": 141,
+ "latencyManagedRawViewNoFlagFirstContact": 13,
+ "latencyManagedRawViewWithFlagFirstContact": 164,
+ "settledComRawViewNoFlag": 172,
+ "settledComRawViewWithFlag": 172,
+ "settledManagedRawViewNoFlag": 172,
+ "settledManagedRawViewWithFlag": 172,
+ "settledCountsMatchAcrossFlagArms": true,
+ "firstContactCountsAreRunVarying": "first-contact counts are a race against Chromium async tree construction and are named latency* so the KTD9 normalizer canonicalizes them; the raw samples stay in this capture, the settled counts are the stable evidence",
+ "flagVerdict": "partially useful - redundant for eventual tree exposure: on Chromium 142.0.7444.265 both client stacks reach the same settled RawView count with no flag, so the phases.md claim holds for exposure. It is not redundant for first-contact readiness: without the flag first contact is deterministically the small pre-activation shell on every run and both stacks, while with the flag first contact is a race against the async tree build and was observed anywhere from a fraction of the settled count to nearly all of it. Neither arm removes the need to settle before trusting a first snapshot.",
+ "supersededPreFinding": "the plan pre-finding (8 managed descendants, stable at 2/5/10s, candidate CONTRADICTS) is superseded: it was measured through an occluded window with a managed client, and neither the occlusion nor the settle window was controlled"
+ },
+ "teardown": {
+ "allObsidianProcessesTerminated": true
+ }
+}
\ No newline at end of file
diff --git a/probes/windows/captures/11-electron-activation/electron-activation.json.normalized b/probes/windows/captures/11-electron-activation/electron-activation.json.normalized
new file mode 100644
index 0000000..a9235db
--- /dev/null
+++ b/probes/windows/captures/11-electron-activation/electron-activation.json.normalized
@@ -0,0 +1,164 @@
+{
+ "question": "does Chromium 138+ expose a UIA tree to any UIA client with no flag, and does --force-renderer-accessibility still change anything",
+ "gradedAgainst": "the committed phases.md line that Chromium 138+ exposes a UIA tree to any UIA client with no flag (P2-O15 / 2.4)",
+ "stack": "both: managed System.Windows.Automation and the U7 UIA3 COM shim (KTD1 - the COM rows are the authoritative ones)",
+ "scope": "app/provider - specific to this Obsidian/Electron/Chromium build; the activation mechanism is Chromium behavior, not a Windows API contract",
+ "versions": {
+ "method": "user-agent string scanned out of the shipped Obsidian.exe on this installation",
+ "obsidianFileVersion": "1.12.7.0",
+ "chromiumVersion": "142.0.7444.265",
+ "electronVersion": "39.8.3",
+ "established": true,
+ "rejectedMethods": [
+ "ELECTRON_RUN_AS_NODE=1 Obsidian.exe -p process.versions produced no output on this VM",
+ "framework DLL file versions carry component versions, not the Chromium version"
+ ]
+ },
+ "obsidianPreexistingAtProbeStart": false,
+ "method": "one fresh Obsidian instance per (stack x flag) cell, because the first UIA client to touch a renderer activates accessibility for that process",
+ "placementHazards": [
+ "U3: an earlier revision that left other windows on top of Obsidian held it at its first-contact node count through a full 8s settle and a 16s instrumented hold, three runs running. Minimizing the covering windows fixed it. The mechanism was NOT isolated (Chromium native-window occlusion tracking is the obvious candidate, unproven). Carried here as a probe-placement hazard, explicitly not a product claim.",
+ "U4: matching a bare Chrome_WidgetWin_1 window found an unrelated window and recorded 9 nodes / 0% coverage as if it were the Electron answer. This probe only accepts a window whose owning pid is one of the launched Obsidian pids, and reads the hosting pid back off the window."
+ ],
+ "countStabilityNote": "settled node counts are content-dependent (U7 measured RawView 172 on a bare walker run, U3 measured 119 through its probe). The stable fact is the growth ratio across the settle, not the absolute count.",
+ "cells": [
+ {
+ "flagArm": "no-flag",
+ "stack": "managed",
+ "authoritative": false,
+ "windowClassName": "Chrome_WidgetWin_1",
+ "windowHandle": ,
+ "windowHost": {
+ "processId":
+ },
+ "latencyObsidianProcessCountAtWindowDiscovery": ,
+ "foregroundOwner": {
+ "processId":
+ },
+ "settleMs": 8000,
+ "firstContact": {
+ "latencyFirstContactRawViewNodes": ,
+ "latencyFirstContactRawViewRefableNodes": ,
+ "latencyFirstContactControlViewNodes": ,
+ "latencyFirstContactControlViewRefableNodes":
+ },
+ "afterSettle": {
+ "settledRawViewNodes": 172,
+ "settledRawViewRefableNodes": 172,
+ "settledControlViewNodes": 133,
+ "settledControlViewRefableNodes": 133
+ },
+ "latencyRawViewGrowthRatio": ,
+ "activatedWithoutFlag": true
+ },
+ {
+ "flagArm": "no-flag",
+ "stack": "uia3-com",
+ "authoritative": true,
+ "windowClassName": "Chrome_WidgetWin_1",
+ "windowHandle": ,
+ "windowHost": {
+ "processId":
+ },
+ "latencyObsidianProcessCountAtWindowDiscovery": ,
+ "foregroundOwner": {
+ "processId":
+ },
+ "settleMs": 8000,
+ "firstContact": {
+ "latencyFirstContactRawViewNodes": ,
+ "latencyFirstContactControlViewNodes": ,
+ "latencyFirstContactContentViewNodes":
+ },
+ "afterSettle": {
+ "settledRawViewNodes": 172,
+ "settledControlViewNodes": 133,
+ "settledContentViewNodes": 133,
+ "settledRefableNodesWithAnyPattern": 171,
+ "settledRefableNodesExcludingLegacy": 171
+ },
+ "censusNote": "census ran in a second shim process against the already-activated instance, so its counts are settled counts; per-element rows are deliberately not captured (R11: Obsidian note titles are content)",
+ "latencyRawViewGrowthRatio": ,
+ "activatedWithoutFlag": true
+ },
+ {
+ "flagArm": "force-renderer-accessibility",
+ "stack": "managed",
+ "authoritative": false,
+ "windowClassName": "Chrome_WidgetWin_1",
+ "windowHandle": ,
+ "windowHost": {
+ "processId":
+ },
+ "latencyObsidianProcessCountAtWindowDiscovery": ,
+ "foregroundOwner": {
+ "processId":
+ },
+ "settleMs": 8000,
+ "firstContact": {
+ "latencyFirstContactRawViewNodes": ,
+ "latencyFirstContactRawViewRefableNodes": ,
+ "latencyFirstContactControlViewNodes": ,
+ "latencyFirstContactControlViewRefableNodes":
+ },
+ "afterSettle": {
+ "settledRawViewNodes": 172,
+ "settledRawViewRefableNodes": 172,
+ "settledControlViewNodes": 133,
+ "settledControlViewRefableNodes": 133
+ },
+ "latencyRawViewGrowthRatio": ,
+ "activatedWithoutFlag": false
+ },
+ {
+ "flagArm": "force-renderer-accessibility",
+ "stack": "uia3-com",
+ "authoritative": true,
+ "windowClassName": "Chrome_WidgetWin_1",
+ "windowHandle": ,
+ "windowHost": {
+ "processId":
+ },
+ "latencyObsidianProcessCountAtWindowDiscovery": ,
+ "foregroundOwner": {
+ "processId":
+ },
+ "settleMs": 8000,
+ "firstContact": {
+ "latencyFirstContactRawViewNodes": ,
+ "latencyFirstContactControlViewNodes": ,
+ "latencyFirstContactContentViewNodes":
+ },
+ "afterSettle": {
+ "settledRawViewNodes": 172,
+ "settledControlViewNodes": 133,
+ "settledContentViewNodes": 133,
+ "settledRefableNodesWithAnyPattern": 172,
+ "settledRefableNodesExcludingLegacy": 172
+ },
+ "censusNote": "census ran in a second shim process against the already-activated instance, so its counts are settled counts; per-element rows are deliberately not captured (R11: Obsidian note titles are content)",
+ "latencyRawViewGrowthRatio": ,
+ "activatedWithoutFlag": false
+ }
+ ],
+ "findings": {
+ "managedActivatesWithoutFlag": true,
+ "comActivatesWithoutFlag": true,
+ "clientStackDivergence": false,
+ "latencyComRawViewNoFlagFirstContact": ,
+ "latencyComRawViewWithFlagFirstContact": ,
+ "latencyManagedRawViewNoFlagFirstContact": ,
+ "latencyManagedRawViewWithFlagFirstContact": ,
+ "settledComRawViewNoFlag": 172,
+ "settledComRawViewWithFlag": 172,
+ "settledManagedRawViewNoFlag": 172,
+ "settledManagedRawViewWithFlag": 172,
+ "settledCountsMatchAcrossFlagArms": true,
+ "firstContactCountsAreRunVarying": "first-contact counts are a race against Chromium async tree construction and are named latency* so the KTD9 normalizer canonicalizes them; the raw samples stay in this capture, the settled counts are the stable evidence",
+ "flagVerdict": "partially useful - redundant for eventual tree exposure: on Chromium 142.0.7444.265 both client stacks reach the same settled RawView count with no flag, so the phases.md claim holds for exposure. It is not redundant for first-contact readiness: without the flag first contact is deterministically the small pre-activation shell on every run and both stacks, while with the flag first contact is a race against the async tree build and was observed anywhere from a fraction of the settled count to nearly all of it. Neither arm removes the need to settle before trusting a first snapshot.",
+ "supersededPreFinding": "the plan pre-finding (8 managed descendants, stable at 2/5/10s, candidate CONTRADICTS) is superseded: it was measured through an occluded window with a managed client, and neither the occlusion nor the settle window was controlled"
+ },
+ "teardown": {
+ "allObsidianProcessesTerminated": true
+ }
+}
\ No newline at end of file