fix(agent99): reconcile accepted ignored dispatch safely
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 1s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / build-and-deploy (push) Has been cancelled
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / tests (push) Has been cancelled

This commit is contained in:
ogt
2026-07-14 22:38:43 +08:00
parent 8939e6e92f
commit 1de58df509
4 changed files with 47 additions and 4 deletions

View File

@@ -17,6 +17,7 @@ $BinDir = Join-Path $AgentRoot "bin"
$QueueDir = Join-Path $AgentRoot "queue"
$ProcessedDir = Join-Path $QueueDir "processed"
$SameRunStateDir = Join-Path $AgentRoot "state\same-run-reconcile"
$AcceptedIgnoredDir = Join-Path $AlertRoot "ignored"
$SreAlertInboxScript = Join-Path $BinDir "agent99-sre-alert-inbox.ps1"
New-Item -ItemType Directory -Force -Path $EvidenceDir, $IncomingDir, $LogDir, $QueueDir, $ProcessedDir, $SameRunStateDir | Out-Null
@@ -381,6 +382,9 @@ function Convert-AgentStoredDispatchIdentity {
$identity = Get-AgentField $Stored "identity" $null
if ($identity) { return $identity }
$awoooi = Get-AgentField $Stored "awoooi" $null
$acceptedAlertIdentity = Get-AgentField $awoooi "agent99DispatchIdentity" $null
if ($acceptedAlertIdentity) { return $acceptedAlertIdentity }
return [pscustomobject]@{
schema_version = "agent99_controlled_dispatch_identity_v1"
project_id = [string](Get-AgentField $Stored "projectId" "")
@@ -407,13 +411,32 @@ function Get-AgentExistingRecoverIdentity {
$files += @(Get-ChildItem -LiteralPath $QueueDir -Filter "running-*.json" -File -ErrorAction SilentlyContinue)
$files += @(Get-ChildItem -LiteralPath $QueueDir -Filter "*.json" -File -ErrorAction SilentlyContinue |
Where-Object { $_.Name -notmatch "^(processed|failed|running|same-run-status)-" })
foreach ($file in @($files | Sort-Object LastWriteTime -Descending | Select-Object -First 256)) {
# The API's durable receipt predates the inbox's final classification. The
# exact legacy Recover envelope was accepted by the relay and then moved to
# ignored by an older inbox build. Admit only that immutable source envelope
# as identity evidence so the externally-resolved incident can run Status;
# never replay Recover or accept a generic ignored/reconcile payload.
$acceptedIgnoredPattern = "ignored-awoooi-agent99-$RunId-*.json"
$files = @($files | Sort-Object LastWriteTime -Descending | Select-Object -First 256)
$files += @(Get-ChildItem -LiteralPath $AcceptedIgnoredDir -Filter $acceptedIgnoredPattern -File -ErrorAction SilentlyContinue)
foreach ($file in $files) {
try {
$stored = Get-Content -LiteralPath $file.FullName -Raw | ConvertFrom-Json
if ([bool](Get-AgentField $stored "reconcileOnly" $false)) { continue }
if ([string](Get-AgentField $stored "mode" "") -ne "Recover") { continue }
if ([string](Get-AgentField $stored "automationRunId" "") -ne $RunId) { continue }
$storedMode = [string](Get-AgentField $stored "mode" (Get-AgentField $stored "suggestedMode" ""))
if ($storedMode -ne "Recover") { continue }
$identity = Convert-AgentStoredDispatchIdentity $stored
if ([string](Get-AgentField $identity "run_id" "") -ne $RunId) { continue }
if ($file.DirectoryName -eq $AcceptedIgnoredDir) {
if (
[string](Get-AgentField $stored "id" "") -ne "awoooi-agent99-$RunId" -or
[string](Get-AgentField $stored "source" "") -ne "awoooi-api-alertmanager" -or
[string](Get-AgentField $stored "kind" "") -ne "host_recovery" -or
[string](Get-AgentField $stored "service" "") -ne "cold-start-gate" -or
(Get-AgentField $stored "controlledApply" $null) -isnot [bool] -or
-not [bool](Get-AgentField $stored "controlledApply" $false)
) { continue }
}
$check = Test-AgentSameRunDispatchIdentity $identity
if ($check.ok) { return $identity }
} catch {
@@ -782,6 +805,8 @@ try {
event = "same_run_status_reconcile"
ok = [bool]$reconcile.ok
status = [string]$reconcile.status
reason = [string](Get-AgentField $reconcile "reason" "")
httpStatus = [int]$reconcile.httpStatus
remote = $remote
runId = if ($reconcile.ok) { [string]$reconcile.automationRunId } else { "rejected" }
reconcileOnly = $true

View File

@@ -734,6 +734,15 @@ def test_windows_relay_and_control_plane_enforce_same_run_no_write_contract() ->
assert 'work_item_id" "") -ne "agent99-dispatch:awoooi:' in relay
assert 'trace_id" "") -notmatch "^00-[0-9a-f]{32}-[0-9a-f]{16}-01$"' in relay
assert "function Get-AgentExistingRecoverIdentity" in relay
assert '$AcceptedIgnoredDir = Join-Path $AlertRoot "ignored"' in relay
assert '$acceptedIgnoredPattern = "ignored-awoooi-agent99-$RunId-*.json"' in relay
assert '$files = @($files | Sort-Object LastWriteTime -Descending | Select-Object -First 256)' in relay
assert 'Get-ChildItem -LiteralPath $AcceptedIgnoredDir -Filter $acceptedIgnoredPattern' in relay
assert 'Get-AgentField $awoooi "agent99DispatchIdentity"' in relay
assert 'Get-AgentField $stored "id" "") -ne "awoooi-agent99-$RunId"' in relay
assert 'Get-AgentField $stored "source" "") -ne "awoooi-api-alertmanager"' in relay
assert 'Get-AgentField $stored "kind" "") -ne "host_recovery"' in relay
assert 'Get-AgentField $stored "service" "") -ne "cold-start-gate"' in relay
assert "existing_dispatch_identity_not_found" in relay
assert "existing_dispatch_identity_mismatch" in relay
assert "function Test-AgentSameRunOutcomeEligible" in relay
@@ -748,6 +757,8 @@ def test_windows_relay_and_control_plane_enforce_same_run_no_write_contract() ->
assert 'reconcileOnly = $true' in relay
assert 'suppressTelegram = $true' in relay
assert 'sourceEventResolutionPolicy = "external_source_receipt_required"' in relay
assert 'reason = [string](Get-AgentField $reconcile "reason" "")' in relay
assert 'httpStatus = [int]$reconcile.httpStatus' in relay
assert '"-ReconcileQueueOnly", "-ReconcileQueueId", $queueId' in relay
assert '"-SuppressAlerts", "-SuppressReason"' in relay

View File

@@ -426,7 +426,11 @@ if(Test-Path -LiteralPath $p -PathType Leaf){try{$m=Get-Content -LiteralPath $p
$c=[ordered]@{executed=$false;ok=$false;exitCode=-1;errorType=''}
try{& powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File (Join-Path $b 'agent99-contract-check.ps1') -SourceRoot $b 2>$null|Out-Null;$c.executed=$true;$c.exitCode=$LASTEXITCODE;$c.ok=[bool]($c.exitCode-eq 0)}catch{$c.executed=$true;$c.errorType=$_.Exception.GetType().Name}
$z=[ordered]@{};foreach($n in 'remoteWritePerformed,liveRuntimeWritePerformed,livePromotionAttempted,livePromotionPerformed,secretValueRead,privateKeyValueRead,tokenValueRead,environmentSecretRead,uiInteraction,vmPowerChange,hostReboot,serviceRestart,scheduledTaskRestart,scheduledTaskModification'.Split(',')){$z[$n]=$false}
[ordered]@{schemaVersion='agent99_remote_atomic_deploy_receipt_v1';status='check_ready';mode='check';sourceRevision='__SOURCE_REVISION__';expectedRuntimeFileCount=14;manifestSha256='__MANIFEST_SHA256__';plannedStagingPath='C:\Wooo\Agent99\deploy\remote-source-__STAGE_TOKEN__';runtimeManifest=[pscustomobject]$r;runtimeContract=[pscustomobject]$c;livePreflight=[ordered]@{executed=$false;reason='apply_only_after_validate_only'};transportPayloadMode='control_command_no_stdin';transientTransportPayloadStored=$false;transientTransportPayloadDeletedBeforeReceiver=$false;transportPayloadContainsSecrets=$false;operationBoundaries=$z;nextSafeAction='rerun_with_apply_and_trace_run_work_item_after_source_commit_and_transport_check'}|ConvertTo-Json -Compress -Depth 8'''
$ready=[bool]($c.executed-and$c.ok-and$c.exitCode-eq 0)
$status=if($ready){'check_ready'}else{'check_failed_no_apply'}
$next=if($ready){'rerun_with_apply_and_trace_run_work_item_after_source_commit_and_transport_check'}else{'repair_runtime_contract_before_apply'}
[ordered]@{schemaVersion='agent99_remote_atomic_deploy_receipt_v1';status=$status;mode='check';sourceRevision='__SOURCE_REVISION__';expectedRuntimeFileCount=14;manifestSha256='__MANIFEST_SHA256__';plannedStagingPath='C:\Wooo\Agent99\deploy\remote-source-__STAGE_TOKEN__';runtimeManifest=[pscustomobject]$r;runtimeContract=[pscustomobject]$c;livePreflight=[ordered]@{executed=$false;reason='apply_only_after_validate_only'};transportPayloadMode='control_command_no_stdin';transientTransportPayloadStored=$false;transientTransportPayloadDeletedBeforeReceiver=$false;transportPayloadContainsSecrets=$false;operationBoundaries=$z;nextSafeAction=$next}|ConvertTo-Json -Compress -Depth 8
if(-not$ready){exit 65}'''
script = (
script.replace("__SOURCE_REVISION__", source_revision)
.replace("__MANIFEST_SHA256__", manifest_sha256)

View File

@@ -671,6 +671,9 @@ def test_check_mode_uses_bounded_control_command_without_ssh_stdin(
assert "agent99-contract-check.ps1" in check_script
assert "Get-FileHash" in check_script
assert "runtimeContract" in check_script
assert "$ready=[bool]($c.executed-and$c.ok-and$c.exitCode-eq 0)" in check_script
assert "'check_failed_no_apply'" in check_script
assert "if(-not$ready){exit 65}" in check_script
assert check_script.count("ProgressPreference") == 1
assert "remoteWritePerformed" in check_script
assert "$z[$n]=$false" in check_script