Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 144 additions & 0 deletions Tools/windows/Authorize-FocusedUiCapture.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
param(
[Parameter(Mandatory)][string]$Repository,
[Parameter(Mandatory)][string]$OwnedRoot,
[Parameter(Mandatory)][string]$FocusedPlanPath,
[Parameter(Mandatory)][ValidatePattern('^[A-Fa-f0-9]{64}$')][string]$FocusedPlanSha256,
[ValidateRange(3,30)][long]$AuthorizationMinutes = 20
)
$ErrorActionPreference='Stop'
$root=[IO.Path]::GetFullPath($OwnedRoot).TrimEnd('\')
$repositoryRoot=[IO.Path]::GetFullPath($Repository).TrimEnd('\')
$planPath=[IO.Path]::GetFullPath($FocusedPlanPath)
if (-not $planPath.StartsWith($root + '\',[StringComparison]::OrdinalIgnoreCase) -or
-not (Test-Path -LiteralPath $planPath -PathType Leaf)) {
throw 'Focused plan must be an existing file below the exact owned root.'
}
if ((Get-FileHash -LiteralPath $planPath -Algorithm SHA256).Hash -ne
$FocusedPlanSha256.ToUpperInvariant()) {
throw 'Focused plan hash mismatch.'
}
$plan=Get-Content -LiteralPath $planPath -Raw | ConvertFrom-Json -DateKind String
if ($plan.schemaVersion -ne 1 -or $plan.status -cne 'PREPARED_NO_AUTHORITY' -or
$plan.ownedRoot -ine $root -or $plan.repository -ine $repositoryRoot -or
$plan.officialParityEvidence -cne $false -or $plan.phases.Count -ne 1 -or
$plan.phases[0] -cne 'after' -or $plan.sceneCount -ne $plan.sceneNames.Count -or
$plan.sceneCount -le 0 -or $plan.pngArtifacts -ne 0 -or $plan.processLaunches -ne 0) {
throw 'Focused plan shape or no-authority boundary differs.'
}
if (@(Get-ChildItem -LiteralPath $root -Recurse -File -Filter '*.png').Count -ne 0) {
throw 'Focused authorization refuses a prepared root containing PNG artifacts.'
}
$modulePath=Join-Path $repositoryRoot 'Tools\windows\UiCaptureDriver.psm1'
$workerPath=Join-Path $repositoryRoot 'Tools\windows\Invoke-UiCaptureWorker.ps1'
$supervisorPath=Join-Path $repositoryRoot 'Tools\windows\Invoke-UiCapture.ps1'
Import-Module $modulePath -Force -WarningAction SilentlyContinue
$jobPath=[IO.Path]::GetFullPath([string]$plan.jobPath)
$preflightPath=[IO.Path]::GetFullPath([string]$plan.preflightReceiptPath)
foreach ($binding in @(
@($modulePath,$plan.modulePath,$plan.moduleSha256),
@($workerPath,$plan.workerPath,$plan.workerSha256),
@($supervisorPath,$plan.supervisorPath,$plan.supervisorSha256),
@($jobPath,$plan.jobPath,$plan.jobSha256),
@($preflightPath,$plan.preflightReceiptPath,$plan.preflightReceiptSha256),
@($plan.fixture.mappingPath,$plan.fixture.mappingPath,$plan.fixture.mappingSha256),
@($plan.fixture.canonicalSourcePath,$plan.fixture.canonicalSourcePath,$plan.fixture.canonicalSourceSha256),
@($plan.fixture.rewrittenPath,$plan.fixture.rewrittenPath,$plan.fixture.rewrittenSha256),
@($plan.binaries.graphCodePath,$plan.binaries.graphCodePath,$plan.binaries.graphCodeSha256),
@($plan.binaries.zmxPath,$plan.binaries.zmxPath,$plan.binaries.zmxSha256),
@($plan.binaries.daemonPath,$plan.binaries.daemonPath,$plan.binaries.daemonSha256),
@($plan.binaries.backendFixturePath,$plan.binaries.backendFixturePath,$plan.binaries.backendFixtureSha256),
@($plan.binaries.offlinePeerPath,$plan.binaries.offlinePeerPath,$plan.binaries.offlinePeerSha256)
)) {
$actual=[IO.Path]::GetFullPath([string]$binding[0])
$declared=[IO.Path]::GetFullPath([string]$binding[1])
if ($actual -ine $declared -or -not (Test-Path -LiteralPath $actual -PathType Leaf) -or
(Get-FileHash -LiteralPath $actual -Algorithm SHA256).Hash -ne [string]$binding[2]) {
throw "Focused prepared input changed: $declared"
}
}
$head=(& git -C $repositoryRoot rev-parse HEAD).Trim()
$tree=(& git -C $repositoryRoot rev-parse 'HEAD^{tree}').Trim()
if ($head -cne $plan.sourceHead -or $tree -cne $plan.sourceTree -or
@(& git -C $repositoryRoot status --porcelain --untracked-files=all).Count -ne 0) {
throw 'Focused authorization requires the unchanged clean prepared source head and tree.'
}
$job=Get-Content -LiteralPath $jobPath -Raw | ConvertFrom-Json -DateKind String
Assert-CapSceneContract -Job $job
$capturePlan=Get-CapMatchedCapturePlan -Job $job
if ($capturePlan.profile -cne $plan.profile -or
$capturePlan.evidenceClassification -cne $plan.evidenceClassification -or
($capturePlan.sceneNames -join ',') -cne ($plan.sceneNames -join ',') -or
($capturePlan.phases -join ',') -cne ($plan.phases -join ',') -or
$plan.authorizationBounds.requestedMaxMinutes -ne $AuthorizationMinutes -or
$plan.authorizationBounds.cleanupReserveSeconds -ne 90 -or
$plan.cleanupExpectations.kernelExitAndEofRequired -cne $true -or
$plan.cleanupExpectations.pendingLaunchesExpected -ne 0 -or
$plan.cleanupExpectations.ownedProcessSurvivorsExpected -ne 0 -or
$plan.cleanupExpectations.cleanupFailuresExpected -ne 0) {
throw 'Focused authorization plan, geometry, deadline or cleanup expectations differ.'
}
$preflight=Get-Content -LiteralPath $preflightPath -Raw | ConvertFrom-Json -DateKind String
if ($preflight.executed -ne 185 -or $preflight.passed -ne 185 -or
$preflight.failed -ne 0 -or $preflight.skipped -ne 0 -or
$preflight.gui -cne 'NotExecuted' -or $preflight.images -ne 0 -or
@($preflight.cases | Where-Object {$_.passed -cne $true}).Count -ne 0 -or
$preflight.controls.moduleSha256 -ne $plan.moduleSha256 -or
$preflight.controls.workerSha256 -ne $plan.workerSha256 -or
$preflight.controls.supervisorSha256 -ne $plan.supervisorSha256) {
throw 'Focused authorization preflight no longer proves the prepared controls.'
}
$now=[DateTimeOffset]::UtcNow
$granted=$now.AddSeconds(-1)
$authorization=[ordered]@{
schemaVersion=1L
authorizationId=[guid]::NewGuid().ToString()
status='AUTHORIZED'
authorizedAction='focused-matched-capture'
evidenceClassification=$plan.evidenceClassification
officialParityEvidence=$false
desktopAuthorized=$true
captureEvidenceAuthorized=$true
pngAuthorized=$true
lane='S'
head=$head
sourceTree=$tree
jobManifestSha256=$plan.jobSha256
moduleSha256=$plan.moduleSha256
workerSha256=$plan.workerSha256
supervisorSha256=$plan.supervisorSha256
preflightReceiptSha256=$plan.preflightReceiptSha256
fixtureMappingSha256=$plan.fixture.mappingSha256
beforeBinarySha256=$plan.binaries.graphCodeSha256
afterBinarySha256=$plan.binaries.graphCodeSha256
profile=$plan.profile
sceneNames=@($plan.sceneNames)
phases=@($plan.phases)
focusedPlanSha256=$FocusedPlanSha256.ToUpperInvariant()
grantedUtc=$granted.ToString('o')
expiresUtc=$granted.AddMinutes($AuthorizationMinutes).ToString('o')
maxMinutes=$AuthorizationMinutes
}
$authorizationPath=Join-Path $root 'focused-authorization.json'
if (Test-Path -LiteralPath $authorizationPath) {
throw 'Focused authorization has already been granted for this prepared root.'
}
[IO.File]::WriteAllText($authorizationPath,($authorization | ConvertTo-Json -Depth 20),
[Text.UTF8Encoding]::new($false))
[ordered]@{
ownedRoot=$root
focusedAuthorizationPath=$authorizationPath
focusedAuthorizationSha256=(Get-FileHash -LiteralPath $authorizationPath -Algorithm SHA256).Hash
focusedPlanPath=$planPath
focusedPlanSha256=$FocusedPlanSha256.ToUpperInvariant()
jobPath=$jobPath
jobSha256=$plan.jobSha256
fixtureMappingSha256=$plan.fixture.mappingSha256
preflightReceiptPath=$preflightPath
preflightReceiptSha256=$plan.preflightReceiptSha256
outputDirectory=Join-Path $root 'outputs'
sourceHead=$head
sourceTree=$tree
profile=$plan.profile
sceneNames=@($plan.sceneNames)
evidenceClassification=$plan.evidenceClassification
} | ConvertTo-Json -Depth 10
6 changes: 4 additions & 2 deletions Tools/windows/Invoke-UiCapture.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ $authorizationPath = $null
$authorizationHash = $null
$authorityId = $null
$authorityHash = $null
$focusedPlan = $null
$authorityPath = $null
$deadlineUtc = [DateTime]::MinValue
$jobHash = $null
Expand Down Expand Up @@ -1242,8 +1243,9 @@ try {
$fixtureMappingHashExpected = $job.fixture.mappingSha256
}
if ($focusedCapture) {
$focusedPlan=Get-CapMatchedCapturePlan -Job $job
if ($job.executionMode -cne $ExecutionMode -or $job.desktopAuthorized -cne $true -or
$job.evidenceClassification -cne 'F2_TERMINAL_CHROME_FOCUSED_VALIDATION_V2') {
$job.evidenceClassification -cne $focusedPlan.evidenceClassification) {
throw 'Expected an explicitly authorized focused matched capture job.'
}
$preflightHash=(Get-FileHash -Algorithm SHA256 -LiteralPath $preflightPath).Hash
Expand Down Expand Up @@ -1599,7 +1601,7 @@ try {
evidenceClassification = if ($transitionRehearsal) {
'LIVE_TRANSITION_REHEARSAL_NOT_CAPTURE_EVIDENCE'
} elseif ($focusedCapture) {
'F2_TERMINAL_CHROME_FOCUSED_VALIDATION_V2'
$focusedPlan.evidenceClassification
} else { $null }
officialParityEvidence = if ($focusedCapture) {$false} else {$null}
authorityId = $authorityId
Expand Down
105 changes: 77 additions & 28 deletions Tools/windows/Invoke-UiCaptureWorker.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2161,6 +2161,9 @@ function Invoke-CapMatchedScenes {
$app.SObservation=[pscustomobject]@{readSucceeded=$true;evidenceMode='OWNED_UIA_READ_NOT_VISUAL_PARITY'
fixtureState=$state[0];card=$card[0];wireState='HASH_BOUND_STATIC_PEER_NOT_LIVE_BACKEND'}
$recovery=$null
$cycleRoute=$null
$postFixCardFocused=$focusedCapture -and
$matchedCapturePlan.profile -ceq 'PostFixCards'
if ($scene -cmatch '^s-goal-unwired-') {
$wireIdentity="wire-node:$($fixture.graphs[0].project.path):$($state[0].nodeId)"
$markIdentity="mark-entry:$($fixture.graphs[0].project.path):$($state[0].nodeId)"
Expand Down Expand Up @@ -2235,41 +2238,87 @@ function Invoke-CapMatchedScenes {
controlType='Text';invokable=$false;bounds=@($reasonBounds.Left,$reasonBounds.Top,
$reasonBounds.Right,$reasonBounds.Bottom)}
selected=[bool]$state[0].selected;cardIdentity=$card[0].AutomationId})
$cycleRoute=[pscustomobject]@{actionIdentity=$actionIdentity;actionAutomationId=$actionId
reasonAutomationId=$reasonId;cardAutomationId=$cardId;cardName=$card[0].Name}
}
Save-CapSceneImage -AppRun $app -SceneName $scene -ZoomEvidence $zoom -Phase $Phase
if ($null -ne $recovery) {
$null=Invoke-CapSceneAction -AppRun $app -Action 'wire-node' -SubjectId $recovery.wireIdentity
$wireStatus=Find-CapAutomationElement -RootHandle $app.Window -ProcessIdentity $app.Run.Identity `
-ExpectedProcessId $app.Run.Identity.Pid -Name 'Choose a target loop to wire this entry' `
-AutomationId 'status'
if ($null -eq $wireStatus) {throw "Wire it up did not expose its armed result for $scene."}
if (-not (Send-CapMatchedEscape -AppRun $app)) {
throw "Wire it up could not be cancelled after matched evidence for $scene."
$runWire=-not $postFixCardFocused -or $scene -ceq 's-goal-unwired-unselected'
$runMark=-not $postFixCardFocused -or $scene -ceq 's-goal-unwired-selected'
$wireReceipt=$null
$markReceipt=$null
if ($runWire) {
$null=Invoke-CapSceneAction -AppRun $app -Action 'wire-node' -SubjectId $recovery.wireIdentity
$wireStatus=Find-CapAutomationElement -RootHandle $app.Window -ProcessIdentity $app.Run.Identity `
-ExpectedProcessId $app.Run.Identity.Pid -Name 'Choose a target loop to wire this entry' `
-AutomationId 'status'
if ($null -eq $wireStatus) {throw "Wire it up did not expose its armed result for $scene."}
if (-not (Send-CapMatchedEscape -AppRun $app)) {
throw "Wire it up could not be cancelled after matched evidence for $scene."
}
Start-Sleep -Milliseconds 100
$graph=Find-CapAutomationElement -RootHandle $app.Window -ProcessIdentity $app.Run.Identity `
-ExpectedProcessId $app.Run.Identity.Pid -Name 'Graph' -AutomationId 'graph'
$wireAfter=@(Get-CapDirectChildren -Parent $graph)
if (@($wireAfter | Where-Object {$_.Current.AutomationId -ceq $recovery.wireAutomationId}).Count -ne 1) {
throw "Wire it up cancellation did not preserve the unwired control for $scene."
}
$wireReceipt=[pscustomobject]@{automationId=$recovery.wireAutomationId
result='Choose a target loop to wire this entry';cancelledWith='Escape'
mutation='NONE_ARMED_THEN_CANCELLED';controlPreserved=$true}
}
if ($runMark) {
$null=Invoke-CapSceneAction -AppRun $app -Action 'mark-entry' -SubjectId $recovery.markIdentity
$entryStatus=Find-CapAutomationElement -RootHandle $app.Window -ProcessIdentity $app.Run.Identity `
-ExpectedProcessId $app.Run.Identity.Pid -Name 'Marked as an entry for this session' `
-AutomationId 'status'
if ($null -eq $entryStatus) {throw "Mark as entry did not expose its result for $scene."}
$graph=Find-CapAutomationElement -RootHandle $app.Window -ProcessIdentity $app.Run.Identity `
-ExpectedProcessId $app.Run.Identity.Pid -Name 'Graph' -AutomationId 'graph'
$after=@(Get-CapDirectChildren -Parent $graph)
if (@($after | Where-Object {$_.Current.AutomationId -cin @(
$recovery.wireAutomationId,$recovery.markAutomationId)}).Count -ne 0) {
throw "Mark as entry left stale unwired controls for $scene."
}
$cardAfter=@($after | Where-Object {$_.Current.AutomationId -ceq $cardId -and
$_.Current.Name -ceq $card[0].Name})
if ($cardAfter.Count -ne 1 -or $cardAfter[0].Current.ProcessId -ne $app.Run.Identity.Pid) {
throw "Mark as entry did not preserve the owned Goal card for $scene."
}
$markReceipt=[pscustomobject]@{automationId=$recovery.markAutomationId
result='Marked as an entry for this session';mutation='DECLARED_ENTRY_ADDED'
controlsRemoved=$true;cardPreserved=$cardAfter[0].Current.AutomationId
graphReadback='UNWIRED_CONTROLS_ABSENT_SAME_CARD_IDENTITY_PRESENT'}
}
Start-Sleep -Milliseconds 100
$null=Invoke-CapSceneAction -AppRun $app -Action 'mark-entry' -SubjectId $recovery.markIdentity
$entryStatus=Find-CapAutomationElement -RootHandle $app.Window -ProcessIdentity $app.Run.Identity `
-ExpectedProcessId $app.Run.Identity.Pid -Name 'Marked as an entry for this session' `
-AutomationId 'status'
if ($null -eq $entryStatus) {throw "Mark as entry did not expose its result for $scene."}
$app.SObservation | Add-Member -NotePropertyName recoveryRoutes -NotePropertyValue `
([pscustomobject]@{fixtureIsolation='ONE_SCENE_ONE_DISPOSABLE_PHASE_ROOT'
wire=$wireReceipt;markEntry=$markReceipt})
}
if ($postFixCardFocused -and $scene -ceq 's-main-cycle-selected') {
$null=Invoke-CapSceneAction -AppRun $app -Action 'attention-action' `
-SubjectId $cycleRoute.actionIdentity
$null=Invoke-CapSceneAction -AppRun $app -Action 'workspace-show-graph'
$rowsAfter=@(Read-CapMatchedRows -AppRun $app)
$cardAfter=@($rowsAfter | Where-Object {$_.AutomationId -ceq $cycleRoute.cardAutomationId -and
$_.Name -ceq $cycleRoute.cardName})
$graph=Find-CapAutomationElement -RootHandle $app.Window -ProcessIdentity $app.Run.Identity `
-ExpectedProcessId $app.Run.Identity.Pid -Name 'Graph' -AutomationId 'graph'
$after=@(Get-CapDirectChildren -Parent $graph)
if (@($after | Where-Object {$_.Current.AutomationId -cin @(
$recovery.wireAutomationId,$recovery.markAutomationId)}).Count -ne 0) {
throw "Mark as entry left stale unwired controls for $scene."
}
$cardAfter=@($after | Where-Object {$_.Current.AutomationId -ceq $cardId -and
$_.Current.Name -ceq $card[0].Name})
if ($cardAfter.Count -ne 1 -or $cardAfter[0].Current.ProcessId -ne $app.Run.Identity.Pid) {
throw "Mark as entry did not preserve the owned Goal card for $scene."
$childrenAfter=@(Get-CapDirectChildren -Parent $graph)
$inspectAfter=@($childrenAfter | Where-Object {
$_.Current.AutomationId -ceq $cycleRoute.actionAutomationId -and
$_.Current.Name -ceq 'Inspect'})
$reasonAfter=@($childrenAfter | Where-Object {
$_.Current.AutomationId -ceq $cycleRoute.reasonAutomationId -and
$_.Current.Name -ceq 'cycle · no entry point'})
if ($cardAfter.Count -ne 1 -or $cardAfter[0].Selected -cne $true -or
$inspectAfter.Count -ne 1 -or $reasonAfter.Count -ne 1) {
throw 'Selected cycle Inspect/Show in Graph route did not preserve stable card, action and reason identities.'
}
$app.SObservation | Add-Member -NotePropertyName recoveryRoutes -NotePropertyValue `
([pscustomobject]@{wire=[pscustomobject]@{automationId=$recovery.wireAutomationId
result='Choose a target loop to wire this entry';cancelledWith='Escape'}
markEntry=[pscustomobject]@{automationId=$recovery.markAutomationId
result='Marked as an entry for this session';controlsRemoved=$true
cardPreserved=$cardAfter[0].Current.AutomationId}})
$app.SObservation | Add-Member -NotePropertyName cycleRoute -NotePropertyValue `
([pscustomobject]@{inspectAutomationId=$cycleRoute.actionAutomationId
returnAction='Show in Graph';cardAutomationId=$cycleRoute.cardAutomationId
reasonAutomationId=$cycleRoute.reasonAutomationId;selectedAfterReturn=$true
identitiesStable=$true})
}
} else {
$null=Invoke-CapSceneAction -AppRun $app -Action 'open-loop' -Name $node.title
Expand Down
Loading
Loading