From 6164703a9a22e0b83ac36c4a63396549ae4002c0 Mon Sep 17 00:00:00 2001 From: zhaoyilun Date: Sat, 11 Jul 2026 00:25:54 +0800 Subject: [PATCH] docs: record send sent diagnostic intake --- docs/current-status-card.md | 13 +- ...10-returned-send-sent-record-diagnostic.md | 55 +++++ ...07-10-r6f-r14-continuous-execution-plan.md | 35 +++ ...te-send-sent-record-diagnostic-package.ps1 | 137 ++++++++++++ ...te-send-sent-record-diagnostic-package.ps1 | 202 ++++++++++++++++++ 5 files changed, 440 insertions(+), 2 deletions(-) create mode 100644 docs/source-discovery/2026-07-10-returned-send-sent-record-diagnostic.md create mode 100644 scripts/test-validate-send-sent-record-diagnostic-package.ps1 create mode 100644 scripts/validate-send-sent-record-diagnostic-package.ps1 diff --git a/docs/current-status-card.md b/docs/current-status-card.md index 2fd9f62..0941ddc 100644 --- a/docs/current-status-card.md +++ b/docs/current-status-card.md @@ -59,7 +59,7 @@ N13/N14/N15 are pre-business validation results. They can help identify UI eleme ## Current loop -Current loop: `R10 receive-file download resolver v2 diagnostic complete`; next R11 receive-file download preview contract. +Current loop: `R10a returned send-sent record diagnostic intake complete`; next R11 receive-file download preview contract. Active continuous execution plan: `docs/superpowers/plans/2026-07-10-r6f-r14-continuous-execution-plan.md`. @@ -240,7 +240,7 @@ Continuous execution plan R6f-R14 is approved and execution has started: - Plan file: `docs/superpowers/plans/2026-07-10-r6f-r14-continuous-execution-plan.md`. - Scope: 15 ordered rounds covering send-message connector/gate hardening, send-file preview/idempotency/package, receive-file download preview, receive-message reconciliation, end-to-end business smoke, and release-candidate report. - Execution rule after approval: one round at a time, status-card update, verification, commit, push, then continue automatically until an evidence-only blocker requires user action. -- Last completed round: R10 receive-file download resolver v2 diagnostic. +- Last completed round: R10a returned send-sent record diagnostic intake. - Next active round: R11 receive-file download preview contract. R6g send audit and idempotency replay diagnostics is complete: @@ -334,4 +334,13 @@ R10 receive-file download resolver v2 diagnostic is complete: - Fixture result: `records_checked=3`, `cache_candidates_checked=4`, `accepted_matches=2`, `ambiguous_matches=0`, `score_100_matches=1`, `score_80_matches=1`, and `score_40_only_matches=1`. - Diagnostic output keeps `raw_paths_printed=false`, `file_contents_read=false`, and `file_paths_returned=false`. - Decision: resolver logic is proven, but real production file download remains blocked until a real cache mapping package produces accepted non-ambiguous matches. +- Next node: R10a returned send-sent record diagnostic intake, because two new operator packages arrived. + +R10a returned send-sent record diagnostic intake is complete: + +- Added `scripts\validate-send-sent-record-diagnostic-package.ps1` and `scripts\test-validate-send-sent-record-diagnostic-package.ps1`. +- Reviewed `send-sent-record-diagnostic-package1(1).zip` and `send-sent-record-diagnostic-package2(1).zip` from Downloads using safe summary output only. +- Both packages report `operator_observed_success=true`, `do_not_send_second_time_confirmed=true`, and `content_sha256_matches_input=true`. +- Both packages also report `exact_content_seen_after=false`, `content_sha256_seen_after=false`, `after_offline_blocked=true`, `strict_send_record_pass=false`, and `production_send_unlock_recommended=false`. +- Decision: production `isphere_send_message` remains blocked; the packages are useful manual evidence but not enough to open the send gate. - Next node: R11 receive-file download preview contract. diff --git a/docs/source-discovery/2026-07-10-returned-send-sent-record-diagnostic.md b/docs/source-discovery/2026-07-10-returned-send-sent-record-diagnostic.md new file mode 100644 index 0000000..aac4c4f --- /dev/null +++ b/docs/source-discovery/2026-07-10-returned-send-sent-record-diagnostic.md @@ -0,0 +1,55 @@ +# Returned send-sent record diagnostic intake - 2026-07-10 + +## Inputs reviewed + +- `send-sent-record-diagnostic-package1(1).zip` +- `send-sent-record-diagnostic-package2(1).zip` + +The raw packages stay outside the repository. This note records only safe summary fields. + +## Validator + +Added `scripts/validate-send-sent-record-diagnostic-package.ps1` for this package family. It outputs booleans and counts only; it does not print raw target id, raw message body, raw idempotency key, or local operator paths. + +## Safe validation summaries + +Package 1: + +```json +{"ok":true,"package_type":"send-sent-record-diagnostic","package_file_name":"send-sent-record-diagnostic-package1(1).zip","summary_json_present":false,"input_json_present":true,"content_sha256_matches_input":true,"before_run_count":1,"after_run_count":1,"exact_content_seen_after":false,"content_sha256_seen_after":false,"after_offline_blocked":true,"operator_observed_success":true,"do_not_send_second_time_confirmed":true,"production_send_enabled":false,"automatic_send_enabled":false,"strict_send_record_pass":false,"production_send_unlock_recommended":false,"decision":"manual_success_without_machine_sent_record"} +``` + +Package 2: + +```json +{"ok":true,"package_type":"send-sent-record-diagnostic","package_file_name":"send-sent-record-diagnostic-package2(1).zip","summary_json_present":true,"input_json_present":true,"content_sha256_matches_input":true,"before_run_count":1,"after_run_count":1,"exact_content_seen_after":false,"content_sha256_seen_after":false,"after_offline_blocked":true,"latest_after_route_hints":["A_ROUTE_INCOMPLETE","A_ROUTE_OFFLINE_BLOCKED"],"operator_observed_success":true,"do_not_send_second_time_confirmed":true,"production_send_enabled":false,"automatic_send_enabled":false,"strict_send_record_pass":false,"production_send_unlock_recommended":false,"decision":"manual_success_without_machine_sent_record"} +``` + +Strict mode was also checked against package 2 and failed, as expected, because `strict_send_record_pass=false`. + +## Business interpretation + +The two packages improve confidence that a human operator successfully sent the fixed sandbox message once. They do not prove that the bridge can safely enable production send, because the machine-verifiable sent-record evidence is still missing: + +- `exact_content_seen_after=false` +- `content_sha256_seen_after=false` +- `after_offline_blocked=true` +- package 2 after route hints include `A_ROUTE_INCOMPLETE` and `A_ROUTE_OFFLINE_BLOCKED` + +## Decision + +Production `isphere_send_message` remains blocked. Current usable state remains: + +- send-message preview/dry-run: usable +- audit and idempotency: usable +- production send: blocked by missing strict machine sent-record evidence + +## Next requirement for unlocking production send + +A future online package must show all of the following without printing raw body/target/path values: + +1. before and after recorder outputs are present; +2. content hash or sent-record marker is visible after send; +3. offline blocker is absent after send; +4. operator confirms only one send was performed; +5. strict validator returns `strict_send_record_pass=true` and `production_send_unlock_recommended=true`. diff --git a/docs/superpowers/plans/2026-07-10-r6f-r14-continuous-execution-plan.md b/docs/superpowers/plans/2026-07-10-r6f-r14-continuous-execution-plan.md index 7b1506a..101e3d1 100644 --- a/docs/superpowers/plans/2026-07-10-r6f-r14-continuous-execution-plan.md +++ b/docs/superpowers/plans/2026-07-10-r6f-r14-continuous-execution-plan.md @@ -752,6 +752,41 @@ git push gitea main --- +### R10a: Returned send-sent record diagnostic intake + +**Purpose:** 用户在 R10 后提供了两个 `send-sent-record-diagnostic` 返回包。本插入轮次只做安全复核:判断是否能打开生产发消息门禁;不能则记录明确阻断。 + +**Files:** +- Create: `scripts/validate-send-sent-record-diagnostic-package.ps1` +- Create: `scripts/test-validate-send-sent-record-diagnostic-package.ps1` +- Create: `docs/source-discovery/2026-07-10-returned-send-sent-record-diagnostic.md` +- Modify: `docs/current-status-card.md` + +- [x] **Step 1: Write validator test first** + +Test covers a blocked fixture and a strict-pass fixture. Initial run failed before the validator existed. + +- [x] **Step 2: Implement safe validator** + +Validator accepts `-ZipPath`, emits booleans/counts only, and supports `-Strict` non-zero exit when strict sent-record evidence is missing. + +- [x] **Step 3: Validate returned packages** + +Both returned packages produced `decision="manual_success_without_machine_sent_record"` and `strict_send_record_pass=false`. + +- [x] **Step 4: Document decision** + +Created `docs/source-discovery/2026-07-10-returned-send-sent-record-diagnostic.md`. Production send remains blocked. + +**R10a Result:** + +- Package 1 and package 2 both have before/after runs and operator success. +- Neither package has machine-verifiable after sent-record/content-hash evidence. +- Both show `after_offline_blocked=true`. +- `production_send_unlock_recommended=false`; default `isphere_send_message` remains production-blocked. + +--- + ### R11: Receive-file download preview contract **Purpose:** 即使真实下载还不能做,也让 `isphere_receive_files` 对 download 请求返回可机器解析的 preview/blocked 结果,而不是只有泛化拒绝。 diff --git a/scripts/test-validate-send-sent-record-diagnostic-package.ps1 b/scripts/test-validate-send-sent-record-diagnostic-package.ps1 new file mode 100644 index 0000000..df2c57a --- /dev/null +++ b/scripts/test-validate-send-sent-record-diagnostic-package.ps1 @@ -0,0 +1,137 @@ +param() + +$ErrorActionPreference = "Stop" +Set-StrictMode -Version Latest + +$repo = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot "..")).Path +$validator = Join-Path $repo "scripts\validate-send-sent-record-diagnostic-package.ps1" +$tempDir = Join-Path ([System.IO.Path]::GetTempPath()) ("isphere-send-sent-validator-test-" + [guid]::NewGuid().ToString("N")) + +function Assert-True([bool]$Condition, [string]$Message) { + if (-not $Condition) { throw $Message } +} + +function New-StringFromCodes([int[]]$Codes) { + return -join ($Codes | ForEach-Object { [char]$_ }) +} + +$inputFileName = New-StringFromCodes @(0x586B,0x5199,0x2D,0x53D1,0x9001,0x4FE1,0x606F,0x2E,0x6A,0x73,0x6F,0x6E) +$summaryFileName = New-StringFromCodes @(0x53D1,0x9001,0x8BB0,0x5F55,0x8BCA,0x65AD,0x6458,0x8981,0x2E,0x6A,0x73,0x6F,0x6E) + +function ConvertTo-Sha256Hex([string]$Text) { + $bytes = [System.Text.Encoding]::UTF8.GetBytes($Text) + $sha = [System.Security.Cryptography.SHA256]::Create() + try { + return -join ($sha.ComputeHash($bytes) | ForEach-Object { $_.ToString("x2") }) + } + finally { + $sha.Dispose() + } +} + +function New-FixturePackage([string]$Name, [bool]$StrictPass) { + $root = Join-Path $tempDir $Name + New-Item -ItemType Directory -Force -Path $root | Out-Null + $before = Join-Path $root "return-evidence\before\probe-before" + $after = Join-Path $root "return-evidence\after\probe-after" + New-Item -ItemType Directory -Force -Path $before | Out-Null + New-Item -ItemType Directory -Force -Path $after | Out-Null + + $content = "hello" + $hash = ConvertTo-Sha256Hex $content + [ordered]@{ + gate = "R6e sent-record diagnostic" + generated_by_script = $true + operator_language = "zh-CN" + ONLINE_SANDBOX_REQUIRED = $true + MANUAL_ONE_SEND_ONLY = $true + DO_NOT_SEND_SECOND_TIME = $true + production_send_enabled = $false + automatic_send_enabled = $false + sandbox_target_type = "direct" + sandbox_target_id = "fixture-target" + content_text_to_send = $content + content_sha256 = $hash + idempotency_key = ("fixture-" + $Name) + manual_send_started_at_local = "2026-07-10T19:00:00+08:00" + manual_send_finished_at_local = "2026-07-10T19:00:10+08:00" + operator_observed_success = $true + success_ack_or_sent_record = "fixture operator observed success" + do_not_send_second_time_confirmed = $true + } | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $root $inputFileName) -Encoding UTF8 + + [ordered]@{ + ok = $true + package = "send-sent-record-diagnostic" + operator_language = "zh-CN" + production_send_enabled = $false + automatic_send_enabled = $false + content_sha256_matches_input = $true + before_run_count = 1 + after_run_count = 1 + exact_content_seen_before = $false + exact_content_seen_after = $StrictPass + content_sha256_seen_before = $false + content_sha256_seen_after = $StrictPass + latest_before_route_hints = @("A_ROUTE_OFFLINE_BLOCKED") + latest_after_route_hints = if ($StrictPass) { @("B_ROUTE_SENT_RECORD_SEEN") } else { @("A_ROUTE_OFFLINE_BLOCKED") } + after_offline_blocked = -not $StrictPass + operator_observed_success = $true + do_not_send_second_time_confirmed = $true + raw_message_body_written = $false + raw_target_id_written = $false + raw_idempotency_key_written = $false + local_paths_written = $false + } | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $root $summaryFileName) -Encoding UTF8 + + "[]" | Set-Content -LiteralPath (Join-Path $before "send_uia_controls_preflight.json") -Encoding UTF8 + "[]" | Set-Content -LiteralPath (Join-Path $after "send_uia_controls_preflight.json") -Encoding UTF8 + + $zip = Join-Path $tempDir ($Name + ".zip") + Compress-Archive -LiteralPath $root -DestinationPath $zip -Force + return $zip +} + +try { + if (-not (Test-Path -LiteralPath $validator)) { + throw "missing validator script: $validator" + } + + New-Item -ItemType Directory -Force -Path $tempDir | Out-Null + $blockedZip = New-FixturePackage "blocked-package" $false + $passZip = New-FixturePackage "pass-package" $true + + $blockedOutput = & powershell -NoProfile -ExecutionPolicy Bypass -File $validator -ZipPath $blockedZip + if ($LASTEXITCODE -ne 0) { throw "blocked fixture validator failed: $blockedOutput" } + $blocked = ($blockedOutput | Select-Object -Last 1) | ConvertFrom-Json + Assert-True ($blocked.ok -eq $true) "blocked ok should be true" + Assert-True ($blocked.strict_send_record_pass -eq $false) "blocked strict pass should be false" + Assert-True ($blocked.production_send_unlock_recommended -eq $false) "blocked should not unlock production send" + Assert-True ($blocked.after_offline_blocked -eq $true) "blocked offline flag should be true" + Assert-True ($blocked.raw_message_body_written -eq $false) "raw body must not be written" + Assert-True ($blocked.raw_target_id_written -eq $false) "raw target must not be written" + Assert-True ($blocked.local_paths_written -eq $false) "local paths must not be written" + + $passOutput = & powershell -NoProfile -ExecutionPolicy Bypass -File $validator -ZipPath $passZip -Strict + if ($LASTEXITCODE -ne 0) { throw "pass fixture strict validator failed: $passOutput" } + $pass = ($passOutput | Select-Object -Last 1) | ConvertFrom-Json + Assert-True ($pass.strict_send_record_pass -eq $true) "pass strict should be true" + Assert-True ($pass.production_send_unlock_recommended -eq $true) "pass should unlock gated production path" + + $strictBlocked = & powershell -NoProfile -ExecutionPolicy Bypass -File $validator -ZipPath $blockedZip -Strict + if ($LASTEXITCODE -eq 0) { + throw "Strict unexpectedly passed for blocked fixture: $strictBlocked" + } + + [ordered]@{ + ok = $true + blocked_strict_send_record_pass = $blocked.strict_send_record_pass + pass_strict_send_record_pass = $pass.strict_send_record_pass + production_send_unlock_recommended = $pass.production_send_unlock_recommended + } | ConvertTo-Json -Depth 4 -Compress +} +finally { + if (Test-Path -LiteralPath $tempDir) { + Remove-Item -LiteralPath $tempDir -Recurse -Force + } +} diff --git a/scripts/validate-send-sent-record-diagnostic-package.ps1 b/scripts/validate-send-sent-record-diagnostic-package.ps1 new file mode 100644 index 0000000..6c5100c --- /dev/null +++ b/scripts/validate-send-sent-record-diagnostic-package.ps1 @@ -0,0 +1,202 @@ +param( + [string]$PackagePath = "", + [string]$ZipPath = "", + [string]$WorkDir = "", + [switch]$Strict +) + +$ErrorActionPreference = "Stop" +Set-StrictMode -Version Latest + +function New-StringFromCodes([int[]]$Codes) { + return -join ($Codes | ForEach-Object { [char]$_ }) +} + +$inputFileName = New-StringFromCodes @(0x586B,0x5199,0x2D,0x53D1,0x9001,0x4FE1,0x606F,0x2E,0x6A,0x73,0x6F,0x6E) +$summaryFileName = New-StringFromCodes @(0x53D1,0x9001,0x8BB0,0x5F55,0x8BCA,0x65AD,0x6458,0x8981,0x2E,0x6A,0x73,0x6F,0x6E) + +function ConvertTo-Sha256Hex([string]$Text) { + $bytes = [System.Text.Encoding]::UTF8.GetBytes($Text) + $sha = [System.Security.Cryptography.SHA256]::Create() + try { + return -join ($sha.ComputeHash($bytes) | ForEach-Object { $_.ToString("x2") }) + } + finally { + $sha.Dispose() + } +} + +function Get-JsonPropertyValue($Object, [string]$Name, $Default = $null) { + if ($null -eq $Object) { return $Default } + $property = $Object.PSObject.Properties[$Name] + if ($null -eq $property) { return $Default } + return $property.Value +} + +function Test-NonEmptyString($Value) { + if ($null -eq $Value) { return $false } + return -not [string]::IsNullOrWhiteSpace([string]$Value) +} + +function ConvertTo-Bool($Value, [bool]$Default = $false) { + if ($null -eq $Value) { return $Default } + if ($Value -is [bool]) { return [bool]$Value } + $text = ([string]$Value).Trim().ToLowerInvariant() + if ($text -eq "true" -or $text -eq "1" -or $text -eq "yes") { return $true } + if ($text -eq "false" -or $text -eq "0" -or $text -eq "no") { return $false } + return $Default +} + +function Get-RunCountFromEvidence($AllFiles, [string]$Phase) { + $pattern = "[\\/]return-evidence[\\/]$Phase[\\/]" + $manifestDirs = @($AllFiles | Where-Object { $_.FullName -match $pattern -and $_.Name -eq "manifest.json" } | ForEach-Object { $_.DirectoryName } | Sort-Object -Unique) + if ($manifestDirs.Count -gt 0) { return $manifestDirs.Count } + $phaseFiles = @($AllFiles | Where-Object { $_.FullName -match $pattern }) + if ($phaseFiles.Count -gt 0) { return 1 } + return 0 +} + +function Test-AfterOfflineBlocked($AllFiles, $Summary) { + $summaryValue = Get-JsonPropertyValue $Summary "after_offline_blocked" $null + if ($null -ne $summaryValue) { return ConvertTo-Bool $summaryValue $false } + $afterFiles = @($AllFiles | Where-Object { $_.FullName -match "[\\/]return-evidence[\\/]after[\\/]" -and $_.Name -eq "send_uia_controls_preflight.json" }) + foreach ($file in $afterFiles) { + $text = Get-Content -LiteralPath $file.FullName -Raw + if ($text.Contains("A_ROUTE_OFFLINE_BLOCKED") -or $text.Contains('"offline_blocker_visible":true')) { + return $true + } + } + return $false +} + +function Get-ArrayValue($Object, [string]$Name) { + $value = Get-JsonPropertyValue $Object $Name @() + if ($null -eq $value) { return @() } + return @($value) +} + +if (-not [string]::IsNullOrWhiteSpace($ZipPath)) { + $PackagePath = $ZipPath +} +if ([string]::IsNullOrWhiteSpace($PackagePath)) { + throw "PackagePath or ZipPath is required" +} +if (-not (Test-Path -LiteralPath $PackagePath)) { + throw "package not found" +} + +$resolvedPackage = (Resolve-Path -LiteralPath $PackagePath).Path +$cleanup = $false +if ([string]::IsNullOrWhiteSpace($WorkDir)) { + $WorkDir = Join-Path ([System.IO.Path]::GetTempPath()) ("isphere-send-sent-diagnostic-" + [guid]::NewGuid().ToString("N")) + $cleanup = $true +} + +if (Test-Path -LiteralPath $WorkDir) { + Remove-Item -LiteralPath $WorkDir -Recurse -Force +} +New-Item -ItemType Directory -Force -Path $WorkDir | Out-Null + +try { + if ($resolvedPackage.EndsWith(".zip", [System.StringComparison]::OrdinalIgnoreCase)) { + Expand-Archive -LiteralPath $resolvedPackage -DestinationPath $WorkDir -Force + $root = $WorkDir + } + else { + $root = $resolvedPackage + } + + $allFiles = @(Get-ChildItem -LiteralPath $root -Recurse -File) + $summaryFile = $allFiles | Where-Object { $_.Name -eq $summaryFileName } | Sort-Object FullName | Select-Object -First 1 + $inputFile = $allFiles | Where-Object { $_.Name -eq $inputFileName } | Sort-Object FullName | Select-Object -First 1 + + $summaryJson = $null + if ($summaryFile) { + $summaryJson = Get-Content -LiteralPath $summaryFile.FullName -Raw | ConvertFrom-Json + } + $inputJson = $null + if ($inputFile) { + $inputJson = Get-Content -LiteralPath $inputFile.FullName -Raw | ConvertFrom-Json + } + + $contentHashMatches = ConvertTo-Bool (Get-JsonPropertyValue $summaryJson "content_sha256_matches_input" $null) $false + if ($null -ne $inputJson -and (Test-NonEmptyString (Get-JsonPropertyValue $inputJson "content_text_to_send")) -and (Test-NonEmptyString (Get-JsonPropertyValue $inputJson "content_sha256"))) { + $contentHashMatches = ((ConvertTo-Sha256Hex ([string](Get-JsonPropertyValue $inputJson "content_text_to_send"))) -eq ([string](Get-JsonPropertyValue $inputJson "content_sha256"))) + } + + $beforeRunCount = [int](Get-JsonPropertyValue $summaryJson "before_run_count" (Get-RunCountFromEvidence $allFiles "before")) + $afterRunCount = [int](Get-JsonPropertyValue $summaryJson "after_run_count" (Get-RunCountFromEvidence $allFiles "after")) + $exactContentSeenAfter = ConvertTo-Bool (Get-JsonPropertyValue $summaryJson "exact_content_seen_after" $false) $false + $contentShaSeenAfter = ConvertTo-Bool (Get-JsonPropertyValue $summaryJson "content_sha256_seen_after" $false) $false + $afterOfflineBlocked = Test-AfterOfflineBlocked $allFiles $summaryJson + $operatorObservedSuccess = ConvertTo-Bool (Get-JsonPropertyValue $summaryJson "operator_observed_success" (Get-JsonPropertyValue $inputJson "operator_observed_success" $false)) $false + $noSecondSend = ConvertTo-Bool (Get-JsonPropertyValue $summaryJson "do_not_send_second_time_confirmed" (Get-JsonPropertyValue $inputJson "do_not_send_second_time_confirmed" $false)) $false + $productionSendEnabled = ConvertTo-Bool (Get-JsonPropertyValue $summaryJson "production_send_enabled" (Get-JsonPropertyValue $inputJson "production_send_enabled" $false)) $false + $automaticSendEnabled = ConvertTo-Bool (Get-JsonPropertyValue $summaryJson "automatic_send_enabled" (Get-JsonPropertyValue $inputJson "automatic_send_enabled" $false)) $false + $rawBodyWritten = ConvertTo-Bool (Get-JsonPropertyValue $summaryJson "raw_message_body_written" $false) $false + $rawTargetWritten = ConvertTo-Bool (Get-JsonPropertyValue $summaryJson "raw_target_id_written" $false) $false + $rawIdemWritten = ConvertTo-Bool (Get-JsonPropertyValue $summaryJson "raw_idempotency_key_written" $false) $false + $localPathsWritten = ConvertTo-Bool (Get-JsonPropertyValue $summaryJson "local_paths_written" $false) $false + + $strictPass = ( + $contentHashMatches -and + $beforeRunCount -ge 1 -and + $afterRunCount -ge 1 -and + ($exactContentSeenAfter -or $contentShaSeenAfter) -and + -not $afterOfflineBlocked -and + $operatorObservedSuccess -and + $noSecondSend -and + -not $productionSendEnabled -and + -not $automaticSendEnabled -and + -not $rawBodyWritten -and + -not $rawTargetWritten -and + -not $rawIdemWritten -and + -not $localPathsWritten + ) + + $decision = "not_usable" + if ($strictPass) { + $decision = "strict_send_record_pass" + } + elseif ($operatorObservedSuccess -and $beforeRunCount -ge 1 -and $afterRunCount -ge 1 -and $contentHashMatches) { + $decision = "manual_success_without_machine_sent_record" + } + elseif ($beforeRunCount -ge 1 -or $afterRunCount -ge 1) { + $decision = "partial_probe_only" + } + + $summaryOut = [ordered]@{ + ok = $true + package_type = "send-sent-record-diagnostic" + package_file_name = [System.IO.Path]::GetFileName($resolvedPackage) + summary_json_present = ($null -ne $summaryFile) + input_json_present = ($null -ne $inputFile) + content_sha256_matches_input = $contentHashMatches + before_run_count = $beforeRunCount + after_run_count = $afterRunCount + exact_content_seen_after = $exactContentSeenAfter + content_sha256_seen_after = $contentShaSeenAfter + after_offline_blocked = $afterOfflineBlocked + latest_after_route_hints = Get-ArrayValue $summaryJson "latest_after_route_hints" + operator_observed_success = $operatorObservedSuccess + do_not_send_second_time_confirmed = $noSecondSend + production_send_enabled = $productionSendEnabled + automatic_send_enabled = $automaticSendEnabled + raw_message_body_written = $rawBodyWritten + raw_target_id_written = $rawTargetWritten + raw_idempotency_key_written = $rawIdemWritten + local_paths_written = $localPathsWritten + strict_send_record_pass = $strictPass + production_send_unlock_recommended = $strictPass + decision = $decision + } + $summaryOut | ConvertTo-Json -Depth 6 -Compress + if ($Strict -and -not $strictPass) { + exit 1 + } +} +finally { + if ($cleanup -and (Test-Path -LiteralPath $WorkDir)) { + Remove-Item -LiteralPath $WorkDir -Recurse -Force + } +}