diff --git a/.github/scripts/package-smoke-re07.NuGet.Config b/.github/scripts/package-smoke-re07.NuGet.Config new file mode 100644 index 000000000..2ac2e0e76 --- /dev/null +++ b/.github/scripts/package-smoke-re07.NuGet.Config @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/.github/scripts/smoke-re07-runtime-evidence-interop.ps1 b/.github/scripts/smoke-re07-runtime-evidence-interop.ps1 new file mode 100644 index 000000000..b5b3fd248 --- /dev/null +++ b/.github/scripts/smoke-re07-runtime-evidence-interop.ps1 @@ -0,0 +1,84 @@ +param( + [Parameter( Mandatory = $true )] + [string] $ArtifactDirectory, + + [ValidateSet( 'Debug', 'Staging', 'Release' )] + [string] $Configuration = 'Staging' +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version Latest + +$repositoryRoot = [System.IO.Path]::GetFullPath( + [System.IO.Path]::Combine( $PSScriptRoot, '..', '..' ) +) +$artifactRoot = if ( [System.IO.Path]::IsPathRooted( $ArtifactDirectory ) ) { + [System.IO.Path]::GetFullPath( $ArtifactDirectory ) +} else { + [System.IO.Path]::GetFullPath( + [System.IO.Path]::Combine( $repositoryRoot, $ArtifactDirectory ) + ) +} +if ( ![System.IO.Directory]::Exists( $artifactRoot ) ) { + throw "RE07 artifact directory does not exist: $artifactRoot" +} + +[xml] $buildProperties = Get-Content -LiteralPath ( + Join-Path $repositoryRoot 'Directory.Build.props' +) -Raw +$versionNode = $buildProperties.SelectSingleNode( + '/Project/PropertyGroup/IcodTermInfoSuiteVersion' +) +if ( $null -eq $versionNode ) { + throw 'Directory.Build.props does not declare IcodTermInfoSuiteVersion.' +} +$version = $versionNode.InnerText +$workRoot = Join-Path ( + [System.IO.Path]::GetTempPath() +) ( + 'Icod.TermInfo.RE07RuntimeEvidence.' + [System.Guid]::NewGuid().ToString( 'N' ) +) +$projectPath = Join-Path $workRoot 'Icod.TermInfo.RuntimeEvidence.PackageSmoke.csproj' +$configPath = Join-Path $repositoryRoot '.github/scripts/package-smoke-re07.NuGet.Config' +$previousNugetPackages = $env:NUGET_PACKAGES +$previousArtifactDirectory = $env:ICOD_TERMINFO_ARTIFACT_DIR + +try { + New-Item -ItemType Directory -Path $workRoot -Force | Out-Null + Copy-Item -LiteralPath ( + Join-Path $repositoryRoot 'tools/runtime-evidence-package-smoke/Icod.TermInfo.RuntimeEvidence.PackageSmoke.csproj' + ) -Destination $projectPath + Copy-Item -LiteralPath ( + Join-Path $repositoryRoot 'tools/runtime-evidence-package-smoke/Program.cs' + ) -Destination $workRoot + + $env:ICOD_TERMINFO_ARTIFACT_DIR = $artifactRoot + $env:NUGET_PACKAGES = Join-Path $workRoot 'packages' + + & dotnet restore $projectPath ` + --configfile $configPath ` + -p:IcodTermInfoInspectionPackageVersion=$version + if ( 0 -ne $LASTEXITCODE ) { + throw "RE07 package-only interoperability restore failed for Icod.TermInfo.Inspection $version and Icod.Terminal 1.12.0." + } + + foreach ( $framework in @( 'net8.0', 'net9.0', 'net10.0' ) ) { + & dotnet run ` + --project $projectPath ` + -c $Configuration ` + -f $framework ` + --no-restore ` + -p:IcodTermInfoInspectionPackageVersion=$version + if ( 0 -ne $LASTEXITCODE ) { + throw "RE07 package-only runtime-evidence interoperability consumer failed on $framework." + } + } + + Write-Host "RE07 package-only runtime-evidence interoperability consumer passed on net8.0, net9.0, and net10.0 for Icod.TermInfo.Inspection $version with Icod.Terminal 1.12.0." +} finally { + $env:NUGET_PACKAGES = $previousNugetPackages + $env:ICOD_TERMINFO_ARTIFACT_DIR = $previousArtifactDirectory + if ( Test-Path -LiteralPath $workRoot ) { + Remove-Item -LiteralPath $workRoot -Recurse -Force + } +} diff --git a/.github/scripts/verify-inspection-compatibility-history.ps1 b/.github/scripts/verify-inspection-compatibility-history.ps1 new file mode 100644 index 000000000..b4447d5fa --- /dev/null +++ b/.github/scripts/verify-inspection-compatibility-history.ps1 @@ -0,0 +1,401 @@ +param( + [Parameter(Mandatory = $true)] + [ValidateSet('Debug', 'Staging', 'Release')] + [string]$Configuration, + + [Parameter(Mandatory = $true)] + [string]$AssemblyPath +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version Latest + +$repositoryRoot = [System.IO.Path]::GetFullPath( + (Join-Path (Join-Path $PSScriptRoot '..') '..') +) +$baselinePath = Join-Path $repositoryRoot 'docs/1.10.0-INSPECTION-PUBLIC-API-BASELINE.txt' +$oneElevenTypesPath = Join-Path $repositoryRoot 'docs/1.11.0-INSPECTION-PUBLIC-API-ADDITIONS.txt' +$oneElevenMembersPath = Join-Path $repositoryRoot 'docs/1.11.0-INSPECTION-PUBLIC-API-ADDITIVE-MEMBERS.txt' +$oneTwelveTypesPath = Join-Path $repositoryRoot 'docs/1.12.0-PG01-INSPECTION-PUBLIC-API-ADDITIONS.txt' +$oneTwelveMembersPath = Join-Path $repositoryRoot 'docs/1.12.0-PG06-INSPECTION-PUBLIC-API-ADDITIVE-MEMBERS.txt' +$oneThirteenTypesPath = Join-Path $repositoryRoot 'docs/1.13.0-RE01-INSPECTION-PUBLIC-API-ADDITIONS.txt' +$oneThirteenMembersPath = Join-Path $repositoryRoot 'docs/1.13.0-RE06-INSPECTION-PUBLIC-API-ADDITIVE-MEMBERS.txt' +$oneElevenApiSha256 = '69c7350d5d44d502ecf1698c8fe1c1336f03d38eb1a36e36219f50ac33585a86' +$oneTwelveApiSha256 = 'f71501dcd27a530051c1a02083325144ced2b6173b6b967b9571c620815198f0' +$assemblyFullPath = if ([System.IO.Path]::IsPathRooted($AssemblyPath)) { + [System.IO.Path]::GetFullPath($AssemblyPath) +} else { + [System.IO.Path]::GetFullPath((Join-Path $repositoryRoot $AssemblyPath)) +} + +foreach ($requiredPath in @( + $baselinePath, + $oneElevenTypesPath, + $oneElevenMembersPath, + $oneTwelveTypesPath, + $oneTwelveMembersPath, + $oneThirteenTypesPath, + $oneThirteenMembersPath, + $assemblyFullPath +)) { + if (-not (Test-Path -LiteralPath $requiredPath -PathType Leaf)) { + throw "Required Inspection compatibility input not found: $requiredPath" + } +} + +function Normalize-Text { + param( + [Parameter(Mandatory = $true)] + [string]$Text + ) + + return (($Text -replace "`r`n", "`n" -replace "`r", "`n").TrimEnd("`n") + "`n") +} + +function Get-NormalizedSha256 { + param( + [Parameter(Mandatory = $true)] + [string]$Text + ) + + $normalized = Normalize-Text -Text $Text + $bytes = [System.Text.Encoding]::UTF8.GetBytes($normalized) + $sha256 = [System.Security.Cryptography.SHA256]::Create() + try { + $digest = $sha256.ComputeHash($bytes) + } finally { + $sha256.Dispose() + } + + return [System.BitConverter]::ToString($digest).Replace('-', '').ToLowerInvariant() +} + +function Read-ApprovedTypes { + param( + [Parameter(Mandatory = $true)] + [string]$Path, + + [Parameter(Mandatory = $true)] + [string]$RequiredPrefix, + + [Parameter(Mandatory = $true)] + [string]$ReleaseLabel + ) + + $approved = [System.Collections.Generic.HashSet[string]]::new( + [System.StringComparer]::Ordinal + ) + foreach ($line in [System.IO.File]::ReadAllLines($Path)) { + $candidate = $line.Trim() + if ($candidate.Length -eq 0 -or $candidate.StartsWith('#', [System.StringComparison]::Ordinal)) { + continue + } + if (-not $candidate.StartsWith($RequiredPrefix, [System.StringComparison]::Ordinal)) { + throw "Approved $ReleaseLabel Inspection public type is outside the required prefix: $candidate" + } + if (-not $approved.Add($candidate)) { + throw "Approved $ReleaseLabel Inspection public types file contains a duplicate: $candidate" + } + } + + if ($approved.Count -eq 0) { + throw "Approved $ReleaseLabel Inspection public types file is empty." + } + + return $approved +} + +function Read-ApprovedRendererMembers { + param( + [Parameter(Mandatory = $true)] + [string]$Path, + + [Parameter(Mandatory = $true)] + [string]$RequiredToken, + + [Parameter(Mandatory = $true)] + [string]$ReleaseLabel + ) + + $approved = [System.Collections.Generic.HashSet[string]]::new( + [System.StringComparer]::Ordinal + ) + foreach ($line in [System.IO.File]::ReadAllLines($Path)) { + $candidate = $line.TrimEnd() + $classification = $candidate.Trim() + if ($classification.Length -eq 0 -or $classification.StartsWith('#', [System.StringComparison]::Ordinal)) { + continue + } + + $isField = $candidate.StartsWith(' FIELD ', [System.StringComparison]::Ordinal) + $isMethod = $candidate.StartsWith(' METHOD ', [System.StringComparison]::Ordinal) + if (-not $isField -and -not $isMethod) { + throw "Approved $ReleaseLabel additive API member is not a public API manifest field or method line: $candidate" + } + if ($candidate.IndexOf($RequiredToken, [System.StringComparison]::Ordinal) -lt 0) { + throw "Approved $ReleaseLabel additive API member is outside the required renderer surface: $candidate" + } + if (-not $approved.Add($candidate)) { + throw "Approved $ReleaseLabel additive API members file contains a duplicate member: $candidate" + } + } + + if ($approved.Count -eq 0) { + throw "Approved $ReleaseLabel additive API members file is empty." + } + + return $approved +} + +function Remove-ApprovedRendererMembers { + param( + [Parameter(Mandatory = $true)] + [string]$Manifest, + + [Parameter(Mandatory = $true)] + [System.Collections.Generic.HashSet[string]]$ApprovedMembers, + + [Parameter(Mandatory = $true)] + [string]$RequiredToken, + + [Parameter(Mandatory = $true)] + [string]$ReleaseLabel + ) + + $rendererTypeHeader = 'TYPE class Icod.TermInfo.Inspection.TermInfoJsonRenderer [static]' + $lines = (Normalize-Text -Text $Manifest).Split("`n") + $result = [System.Collections.Generic.List[string]]::new() + $removedMembers = [System.Collections.Generic.HashSet[string]]::new( + [System.StringComparer]::Ordinal + ) + $insideRenderer = $false + + foreach ($line in $lines) { + if ($line -eq $rendererTypeHeader) { + $insideRenderer = $true + $result.Add($line) + continue + } + + if ($insideRenderer -and $line -eq 'END') { + $insideRenderer = $false + $result.Add($line) + continue + } + + $matchesToken = $line.IndexOf($RequiredToken, [System.StringComparison]::Ordinal) -ge 0 + if ($insideRenderer -and $matchesToken) { + if (-not $ApprovedMembers.Contains($line)) { + throw "Unapproved $ReleaseLabel additive public member on TermInfoJsonRenderer: $line" + } + if (-not $removedMembers.Add($line)) { + throw "Inspection API manifest contains duplicate approved $ReleaseLabel member lines: $line" + } + continue + } + + $result.Add($line) + } + + foreach ($approvedMember in $ApprovedMembers) { + if (-not $removedMembers.Contains($approvedMember)) { + throw "Approved $ReleaseLabel additive public API member is missing from the current assembly: $approvedMember" + } + } + + return [PSCustomObject]@{ + Manifest = Normalize-Text -Text ($result -join "`n") + RemovedMemberCount = $removedMembers.Count + } +} + +function Remove-ApprovedTypes { + param( + [Parameter(Mandatory = $true)] + [string]$Manifest, + + [Parameter(Mandatory = $true)] + [System.Collections.Generic.HashSet[string]]$ApprovedTypes, + + [Parameter(Mandatory = $true)] + [string]$RequiredPrefix, + + [Parameter(Mandatory = $true)] + [string]$ReleaseLabel + ) + + $lines = (Normalize-Text -Text $Manifest).Split("`n") + $result = [System.Collections.Generic.List[string]]::new() + $removedTypes = [System.Collections.Generic.HashSet[string]]::new( + [System.StringComparer]::Ordinal + ) + $skipBlock = $false + $skipTrailingBlank = $false + + foreach ($line in $lines) { + if ($skipTrailingBlank) { + if ($line.Length -eq 0) { + $skipTrailingBlank = $false + continue + } + $skipTrailingBlank = $false + } + + if (-not $skipBlock -and $line.StartsWith('TYPE ', [System.StringComparison]::Ordinal)) { + if ($line -match '^TYPE\s+\S+\s+(\S+)\s+\[') { + $typeName = $Matches[1] + if ($typeName.StartsWith($RequiredPrefix, [System.StringComparison]::Ordinal)) { + if (-not $ApprovedTypes.Contains($typeName)) { + throw "Unapproved $ReleaseLabel Inspection public API addition: $typeName" + } + if (-not $removedTypes.Add($typeName)) { + throw "Inspection API manifest contains duplicate public type blocks: $typeName" + } + $skipBlock = $true + continue + } + } + } + + if ($skipBlock) { + if ($line -eq 'END') { + $skipBlock = $false + $skipTrailingBlank = $true + } + continue + } + + $result.Add($line) + } + + if ($skipBlock) { + throw "Inspection API manifest ended inside an approved $ReleaseLabel type block." + } + + foreach ($approvedType in $ApprovedTypes) { + if (-not $removedTypes.Contains($approvedType)) { + throw "Approved $ReleaseLabel Inspection public API type is missing from the current assembly: $approvedType" + } + } + + return [PSCustomObject]@{ + Manifest = Normalize-Text -Text ($result -join "`n") + RemovedTypeCount = $removedTypes.Count + } +} + +Push-Location $repositoryRoot +try { + $temporaryManifest = Join-Path ([System.IO.Path]::GetTempPath()) ("Icod.TermInfo.Inspection-api-{0}.txt" -f [Guid]::NewGuid().ToString('N')) + try { + & dotnet run ` + --project tools/public-api-snapshot/Icod.TermInfo.PublicApiSnapshot.csproj ` + -c $Configuration ` + --no-build ` + -- ` + --write ` + $temporaryManifest ` + $assemblyFullPath + if (0 -ne $LASTEXITCODE) { + throw "Public API snapshot generation exited with status $LASTEXITCODE." + } + + $frozen = Normalize-Text -Text ([System.IO.File]::ReadAllText($baselinePath)) + $current = [System.IO.File]::ReadAllText($temporaryManifest) + + $approvedOneThirteenMembers = Read-ApprovedRendererMembers ` + -Path $oneThirteenMembersPath ` + -RequiredToken 'PersistentRasterRuntime' ` + -ReleaseLabel '1.13 RE06' + $oneThirteenMemberFiltered = Remove-ApprovedRendererMembers ` + -Manifest $current ` + -ApprovedMembers $approvedOneThirteenMembers ` + -RequiredToken 'PersistentRasterRuntime' ` + -ReleaseLabel '1.13 RE06' + + $approvedOneThirteenTypes = Read-ApprovedTypes ` + -Path $oneThirteenTypesPath ` + -RequiredPrefix 'Icod.TermInfo.Inspection.PersistentRasterRuntime' ` + -ReleaseLabel '1.13' + $oneTwelveCandidate = Remove-ApprovedTypes ` + -Manifest $oneThirteenMemberFiltered.Manifest ` + -ApprovedTypes $approvedOneThirteenTypes ` + -RequiredPrefix 'Icod.TermInfo.Inspection.PersistentRasterRuntime' ` + -ReleaseLabel '1.13' + $oneTwelveCandidateSha256 = Get-NormalizedSha256 -Text $oneTwelveCandidate.Manifest + if (-not [string]::Equals($oneTwelveApiSha256, $oneTwelveCandidateSha256, [System.StringComparison]::Ordinal)) { + throw "Icod.TermInfo.Inspection reconstructed 1.12 public API fingerprint changed. Expected $oneTwelveApiSha256, actual $oneTwelveCandidateSha256." + } + + $approvedOneTwelveMembers = Read-ApprovedRendererMembers ` + -Path $oneTwelveMembersPath ` + -RequiredToken 'PersistentRasterPlacement' ` + -ReleaseLabel '1.12 PG06' + $oneTwelveMemberFiltered = Remove-ApprovedRendererMembers ` + -Manifest $oneTwelveCandidate.Manifest ` + -ApprovedMembers $approvedOneTwelveMembers ` + -RequiredToken 'PersistentRasterPlacement' ` + -ReleaseLabel '1.12 PG06' + + $approvedOneTwelveTypes = Read-ApprovedTypes ` + -Path $oneTwelveTypesPath ` + -RequiredPrefix 'Icod.TermInfo.Inspection.PersistentRasterPlacement' ` + -ReleaseLabel '1.12' + $oneElevenCandidate = Remove-ApprovedTypes ` + -Manifest $oneTwelveMemberFiltered.Manifest ` + -ApprovedTypes $approvedOneTwelveTypes ` + -RequiredPrefix 'Icod.TermInfo.Inspection.PersistentRasterPlacement' ` + -ReleaseLabel '1.12' + $oneElevenCandidateSha256 = Get-NormalizedSha256 -Text $oneElevenCandidate.Manifest + if (-not [string]::Equals($oneElevenApiSha256, $oneElevenCandidateSha256, [System.StringComparison]::Ordinal)) { + throw "Icod.TermInfo.Inspection reconstructed 1.11 public API fingerprint changed. Expected $oneElevenApiSha256, actual $oneElevenCandidateSha256." + } + + $approvedOneElevenMembers = Read-ApprovedRendererMembers ` + -Path $oneElevenMembersPath ` + -RequiredToken 'PersistentRasterLifecycle' ` + -ReleaseLabel '1.11' + $oneElevenMemberFiltered = Remove-ApprovedRendererMembers ` + -Manifest $oneElevenCandidate.Manifest ` + -ApprovedMembers $approvedOneElevenMembers ` + -RequiredToken 'PersistentRasterLifecycle' ` + -ReleaseLabel '1.11' + $approvedOneElevenTypes = Read-ApprovedTypes ` + -Path $oneElevenTypesPath ` + -RequiredPrefix 'Icod.TermInfo.Inspection.PersistentRasterLifecycle' ` + -ReleaseLabel '1.11' + $filtered = Remove-ApprovedTypes ` + -Manifest $oneElevenMemberFiltered.Manifest ` + -ApprovedTypes $approvedOneElevenTypes ` + -RequiredPrefix 'Icod.TermInfo.Inspection.PersistentRasterLifecycle' ` + -ReleaseLabel '1.11' + + if (-not [string]::Equals($frozen, $filtered.Manifest, [System.StringComparison]::Ordinal)) { + throw 'Icod.TermInfo.Inspection changed the frozen 1.10 public API outside explicitly approved 1.11, 1.12, and 1.13 additions.' + } + + Write-Host ( + "Verified reconstructed exact 1.12 Inspection public API SHA-256 {0} after excluding {1} approved 1.13 type block(s) and {2} RE06 renderer member(s)." -f ` + $oneTwelveCandidateSha256, ` + $oneTwelveCandidate.RemovedTypeCount, ` + $oneThirteenMemberFiltered.RemovedMemberCount + ) + Write-Host ( + "Verified reconstructed exact 1.11 Inspection public API SHA-256 {0} after excluding {1} approved 1.12 type block(s) and {2} PG06 renderer member(s)." -f ` + $oneElevenCandidateSha256, ` + $oneElevenCandidate.RemovedTypeCount, ` + $oneTwelveMemberFiltered.RemovedMemberCount + ) + Write-Host ( + "Verified frozen 1.10 Inspection API compatibility after excluding {0} explicitly approved 1.11 public type block(s) and {1} additive member(s)." -f ` + $filtered.RemovedTypeCount, ` + $oneElevenMemberFiltered.RemovedMemberCount + ) + } finally { + if (Test-Path -LiteralPath $temporaryManifest) { + Remove-Item -LiteralPath $temporaryManifest -Force + } + } +} finally { + Pop-Location +} diff --git a/.github/scripts/verify-inspection-compatibility.ps1 b/.github/scripts/verify-inspection-compatibility.ps1 index 3de828c3c..e4d18f401 100644 --- a/.github/scripts/verify-inspection-compatibility.ps1 +++ b/.github/scripts/verify-inspection-compatibility.ps1 @@ -13,13 +13,23 @@ Set-StrictMode -Version Latest $repositoryRoot = [System.IO.Path]::GetFullPath( (Join-Path (Join-Path $PSScriptRoot '..') '..') ) -$baselinePath = Join-Path $repositoryRoot 'docs/1.10.0-INSPECTION-PUBLIC-API-BASELINE.txt' +$freezePath = Join-Path $repositoryRoot 'docs/1.13.0-INSPECTION-PUBLIC-API-FREEZE.md' +$historyVerifierPath = Join-Path $PSScriptRoot 'verify-inspection-compatibility-history.ps1' + +# Keep every historical compatibility authority explicit at the public verifier +# entry point. The delegated history verifier consumes these same frozen files +# and fingerprints; these declarations also make the full reconstruction chain +# visible to release-closure audits without duplicating its implementation. +$oneTenBaselinePath = Join-Path $repositoryRoot 'docs/1.10.0-INSPECTION-PUBLIC-API-BASELINE.txt' $oneElevenTypesPath = Join-Path $repositoryRoot 'docs/1.11.0-INSPECTION-PUBLIC-API-ADDITIONS.txt' $oneElevenMembersPath = Join-Path $repositoryRoot 'docs/1.11.0-INSPECTION-PUBLIC-API-ADDITIVE-MEMBERS.txt' $oneTwelveTypesPath = Join-Path $repositoryRoot 'docs/1.12.0-PG01-INSPECTION-PUBLIC-API-ADDITIONS.txt' $oneTwelveMembersPath = Join-Path $repositoryRoot 'docs/1.12.0-PG06-INSPECTION-PUBLIC-API-ADDITIVE-MEMBERS.txt' +$oneThirteenTypesPath = Join-Path $repositoryRoot 'docs/1.13.0-RE01-INSPECTION-PUBLIC-API-ADDITIONS.txt' +$oneThirteenMembersPath = Join-Path $repositoryRoot 'docs/1.13.0-RE06-INSPECTION-PUBLIC-API-ADDITIVE-MEMBERS.txt' $oneElevenApiSha256 = '69c7350d5d44d502ecf1698c8fe1c1336f03d38eb1a36e36219f50ac33585a86' $oneTwelveApiSha256 = 'f71501dcd27a530051c1a02083325144ced2b6173b6b967b9571c620815198f0' +$oneThirteenApiSha256 = 'fd827a25abafb8e9ff3915567f45f9f2ec51b332bfd8a82dd4e4bca20290e764' $assemblyFullPath = if ([System.IO.Path]::IsPathRooted($AssemblyPath)) { [System.IO.Path]::GetFullPath($AssemblyPath) } else { @@ -27,11 +37,15 @@ $assemblyFullPath = if ([System.IO.Path]::IsPathRooted($AssemblyPath)) { } foreach ($requiredPath in @( - $baselinePath, + $freezePath, + $historyVerifierPath, + $oneTenBaselinePath, $oneElevenTypesPath, $oneElevenMembersPath, $oneTwelveTypesPath, $oneTwelveMembersPath, + $oneThirteenTypesPath, + $oneThirteenMembersPath, $assemblyFullPath )) { if (-not (Test-Path -LiteralPath $requiredPath -PathType Leaf)) { @@ -66,223 +80,11 @@ function Get-NormalizedSha256 { return [System.BitConverter]::ToString($digest).Replace('-', '').ToLowerInvariant() } -function Read-ApprovedTypes { - param( - [Parameter(Mandatory = $true)] - [string]$Path, - - [Parameter(Mandatory = $true)] - [string]$RequiredPrefix, - - [Parameter(Mandatory = $true)] - [string]$ReleaseLabel - ) - - $approved = [System.Collections.Generic.HashSet[string]]::new( - [System.StringComparer]::Ordinal - ) - foreach ($line in [System.IO.File]::ReadAllLines($Path)) { - $candidate = $line.Trim() - if ($candidate.Length -eq 0 -or $candidate.StartsWith('#', [System.StringComparison]::Ordinal)) { - continue - } - if (-not $candidate.StartsWith($RequiredPrefix, [System.StringComparison]::Ordinal)) { - throw "Approved $ReleaseLabel Inspection public type is outside the required prefix: $candidate" - } - if (-not $approved.Add($candidate)) { - throw "Approved $ReleaseLabel Inspection public types file contains a duplicate: $candidate" - } - } - - if ($approved.Count -eq 0) { - throw "Approved $ReleaseLabel Inspection public types file is empty." - } - - return $approved -} - -function Read-ApprovedRendererMembers { - param( - [Parameter(Mandatory = $true)] - [string]$Path, - - [Parameter(Mandatory = $true)] - [string]$RequiredToken, - - [Parameter(Mandatory = $true)] - [string]$ReleaseLabel - ) - - $approved = [System.Collections.Generic.HashSet[string]]::new( - [System.StringComparer]::Ordinal - ) - foreach ($line in [System.IO.File]::ReadAllLines($Path)) { - $candidate = $line.TrimEnd() - $classification = $candidate.Trim() - if ($classification.Length -eq 0 -or $classification.StartsWith('#', [System.StringComparison]::Ordinal)) { - continue - } - - $isField = $candidate.StartsWith(' FIELD ', [System.StringComparison]::Ordinal) - $isMethod = $candidate.StartsWith(' METHOD ', [System.StringComparison]::Ordinal) - if (-not $isField -and -not $isMethod) { - throw "Approved $ReleaseLabel additive API member is not a public API manifest field or method line: $candidate" - } - if ($candidate.IndexOf($RequiredToken, [System.StringComparison]::Ordinal) -lt 0) { - throw "Approved $ReleaseLabel additive API member is outside the required renderer surface: $candidate" - } - if (-not $approved.Add($candidate)) { - throw "Approved $ReleaseLabel additive API members file contains a duplicate member: $candidate" - } - } - - if ($approved.Count -eq 0) { - throw "Approved $ReleaseLabel additive API members file is empty." - } - - return $approved -} - -function Remove-ApprovedRendererMembers { - param( - [Parameter(Mandatory = $true)] - [string]$Manifest, - - [Parameter(Mandatory = $true)] - [System.Collections.Generic.HashSet[string]]$ApprovedMembers, - - [Parameter(Mandatory = $true)] - [string]$RequiredToken, - - [Parameter(Mandatory = $true)] - [string]$ReleaseLabel - ) - - $rendererTypeHeader = 'TYPE class Icod.TermInfo.Inspection.TermInfoJsonRenderer [static]' - $lines = (Normalize-Text -Text $Manifest).Split("`n") - $result = [System.Collections.Generic.List[string]]::new() - $removedMembers = [System.Collections.Generic.HashSet[string]]::new( - [System.StringComparer]::Ordinal - ) - $insideRenderer = $false - - foreach ($line in $lines) { - if ($line -eq $rendererTypeHeader) { - $insideRenderer = $true - $result.Add($line) - continue - } - - if ($insideRenderer -and $line -eq 'END') { - $insideRenderer = $false - $result.Add($line) - continue - } - - $matchesToken = $line.IndexOf($RequiredToken, [System.StringComparison]::Ordinal) -ge 0 - if ($insideRenderer -and $matchesToken) { - if (-not $ApprovedMembers.Contains($line)) { - throw "Unapproved $ReleaseLabel additive public member on TermInfoJsonRenderer: $line" - } - if (-not $removedMembers.Add($line)) { - throw "Inspection API manifest contains duplicate approved $ReleaseLabel member lines: $line" - } - continue - } - - $result.Add($line) - } - - foreach ($approvedMember in $ApprovedMembers) { - if (-not $removedMembers.Contains($approvedMember)) { - throw "Approved $ReleaseLabel additive public API member is missing from the current assembly: $approvedMember" - } - } - - return [PSCustomObject]@{ - Manifest = Normalize-Text -Text ($result -join "`n") - RemovedMemberCount = $removedMembers.Count - } -} - -function Remove-ApprovedTypes { - param( - [Parameter(Mandatory = $true)] - [string]$Manifest, - - [Parameter(Mandatory = $true)] - [System.Collections.Generic.HashSet[string]]$ApprovedTypes, - - [Parameter(Mandatory = $true)] - [string]$RequiredPrefix, - - [Parameter(Mandatory = $true)] - [string]$ReleaseLabel - ) - - $lines = (Normalize-Text -Text $Manifest).Split("`n") - $result = [System.Collections.Generic.List[string]]::new() - $removedTypes = [System.Collections.Generic.HashSet[string]]::new( - [System.StringComparer]::Ordinal - ) - $skipBlock = $false - $skipTrailingBlank = $false - - foreach ($line in $lines) { - if ($skipTrailingBlank) { - if ($line.Length -eq 0) { - $skipTrailingBlank = $false - continue - } - $skipTrailingBlank = $false - } - - if (-not $skipBlock -and $line.StartsWith('TYPE ', [System.StringComparison]::Ordinal)) { - if ($line -match '^TYPE\s+\S+\s+(\S+)\s+\[') { - $typeName = $Matches[1] - if ($typeName.StartsWith($RequiredPrefix, [System.StringComparison]::Ordinal)) { - if (-not $ApprovedTypes.Contains($typeName)) { - throw "Unapproved $ReleaseLabel Inspection public API addition: $typeName" - } - if (-not $removedTypes.Add($typeName)) { - throw "Inspection API manifest contains duplicate public type blocks: $typeName" - } - $skipBlock = $true - continue - } - } - } - - if ($skipBlock) { - if ($line -eq 'END') { - $skipBlock = $false - $skipTrailingBlank = $true - } - continue - } - - $result.Add($line) - } - - if ($skipBlock) { - throw "Inspection API manifest ended inside an approved $ReleaseLabel type block." - } - - foreach ($approvedType in $ApprovedTypes) { - if (-not $removedTypes.Contains($approvedType)) { - throw "Approved $ReleaseLabel Inspection public API type is missing from the current assembly: $approvedType" - } - } - - return [PSCustomObject]@{ - Manifest = Normalize-Text -Text ($result -join "`n") - RemovedTypeCount = $removedTypes.Count - } -} - Push-Location $repositoryRoot try { - $temporaryManifest = Join-Path ([System.IO.Path]::GetTempPath()) ("Icod.TermInfo.Inspection-api-{0}.txt" -f [Guid]::NewGuid().ToString('N')) + $temporaryManifest = Join-Path ( + [System.IO.Path]::GetTempPath() + ) ("Icod.TermInfo.Inspection-1.13-api-{0}.txt" -f [Guid]::NewGuid().ToString('N')) try { & dotnet run ` --project tools/public-api-snapshot/Icod.TermInfo.PublicApiSnapshot.csproj ` @@ -296,77 +98,36 @@ try { throw "Public API snapshot generation exited with status $LASTEXITCODE." } - $frozen = Normalize-Text -Text ([System.IO.File]::ReadAllText($baselinePath)) $current = [System.IO.File]::ReadAllText($temporaryManifest) $currentSha256 = Get-NormalizedSha256 -Text $current - if (-not [string]::Equals($oneTwelveApiSha256, $currentSha256, [System.StringComparison]::Ordinal)) { - throw "Icod.TermInfo.Inspection exact 1.12 public API fingerprint changed. Expected $oneTwelveApiSha256, actual $currentSha256." - } - - $approvedOneTwelveMembers = Read-ApprovedRendererMembers ` - -Path $oneTwelveMembersPath ` - -RequiredToken 'PersistentRasterPlacement' ` - -ReleaseLabel '1.12 PG06' - $oneTwelveMemberFiltered = Remove-ApprovedRendererMembers ` - -Manifest $current ` - -ApprovedMembers $approvedOneTwelveMembers ` - -RequiredToken 'PersistentRasterPlacement' ` - -ReleaseLabel '1.12 PG06' - - $approvedOneTwelveTypes = Read-ApprovedTypes ` - -Path $oneTwelveTypesPath ` - -RequiredPrefix 'Icod.TermInfo.Inspection.PersistentRasterPlacement' ` - -ReleaseLabel '1.12' - $oneElevenCandidate = Remove-ApprovedTypes ` - -Manifest $oneTwelveMemberFiltered.Manifest ` - -ApprovedTypes $approvedOneTwelveTypes ` - -RequiredPrefix 'Icod.TermInfo.Inspection.PersistentRasterPlacement' ` - -ReleaseLabel '1.12' - $oneElevenCandidateSha256 = Get-NormalizedSha256 -Text $oneElevenCandidate.Manifest - if (-not [string]::Equals($oneElevenApiSha256, $oneElevenCandidateSha256, [System.StringComparison]::Ordinal)) { - throw "Icod.TermInfo.Inspection reconstructed 1.11 public API fingerprint changed. Expected $oneElevenApiSha256, actual $oneElevenCandidateSha256." + if (-not [string]::Equals( + $oneThirteenApiSha256, + $currentSha256, + [System.StringComparison]::Ordinal + )) { + throw "Icod.TermInfo.Inspection 1.13 public API fingerprint changed. Expected $oneThirteenApiSha256, actual $currentSha256." } - $approvedOneElevenMembers = Read-ApprovedRendererMembers ` - -Path $oneElevenMembersPath ` - -RequiredToken 'PersistentRasterLifecycle' ` - -ReleaseLabel '1.11' - $oneElevenMemberFiltered = Remove-ApprovedRendererMembers ` - -Manifest $oneElevenCandidate.Manifest ` - -ApprovedMembers $approvedOneElevenMembers ` - -RequiredToken 'PersistentRasterLifecycle' ` - -ReleaseLabel '1.11' - $approvedOneElevenTypes = Read-ApprovedTypes ` - -Path $oneElevenTypesPath ` - -RequiredPrefix 'Icod.TermInfo.Inspection.PersistentRasterLifecycle' ` - -ReleaseLabel '1.11' - $filtered = Remove-ApprovedTypes ` - -Manifest $oneElevenMemberFiltered.Manifest ` - -ApprovedTypes $approvedOneElevenTypes ` - -RequiredPrefix 'Icod.TermInfo.Inspection.PersistentRasterLifecycle' ` - -ReleaseLabel '1.11' - - if (-not [string]::Equals($frozen, $filtered.Manifest, [System.StringComparison]::Ordinal)) { - throw 'Icod.TermInfo.Inspection changed the frozen 1.10 public API outside explicitly approved 1.11 and 1.12 additions.' + $freeze = [System.IO.File]::ReadAllText($freezePath) + if ($freeze.IndexOf($oneThirteenApiSha256, [System.StringComparison]::Ordinal) -lt 0) { + throw '1.13.0-INSPECTION-PUBLIC-API-FREEZE.md does not record the expected whole-surface fingerprint.' } - Write-Host "Verified exact 1.12 Inspection public API SHA-256 $currentSha256." - Write-Host ( - "Verified reconstructed exact 1.11 Inspection public API SHA-256 {0} after excluding {1} approved 1.12 type block(s) and {2} PG06 renderer member(s)." -f ` - $oneElevenCandidateSha256, ` - $oneElevenCandidate.RemovedTypeCount, ` - $oneTwelveMemberFiltered.RemovedMemberCount - ) - Write-Host ( - "Verified frozen 1.10 Inspection API compatibility after excluding {0} explicitly approved 1.11 public type block(s) and {1} additive member(s)." -f ` - $filtered.RemovedTypeCount, ` - $oneElevenMemberFiltered.RemovedMemberCount - ) + Write-Host "Verified exact 1.13 Inspection public API SHA-256 $currentSha256." } finally { if (Test-Path -LiteralPath $temporaryManifest) { Remove-Item -LiteralPath $temporaryManifest -Force } } + + & $historyVerifierPath ` + -Configuration $Configuration ` + -AssemblyPath $assemblyFullPath + if (0 -ne $LASTEXITCODE) { + throw "Historical Inspection compatibility verification exited with status $LASTEXITCODE." + } + + Write-Host "Historical reconstruction authorities remain frozen at 1.11 SHA-256 $oneElevenApiSha256 and 1.12 SHA-256 $oneTwelveApiSha256." } finally { Pop-Location } diff --git a/Directory.Build.props b/Directory.Build.props index 63fbad1a0..5e1176b8d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 1.12.0 + 1.13.0 13.0 enable enable diff --git a/Icod.TermInfo-1.13.0-Persistent-Raster-Runtime-Evidence-Interchange-and-Integration-Roadmap.md b/Icod.TermInfo-1.13.0-Persistent-Raster-Runtime-Evidence-Interchange-and-Integration-Roadmap.md new file mode 100644 index 000000000..4bd256e52 --- /dev/null +++ b/Icod.TermInfo-1.13.0-Persistent-Raster-Runtime-Evidence-Interchange-and-Integration-Roadmap.md @@ -0,0 +1,679 @@ +# Icod.TermInfo 1.13.0 — Persistent-Raster Runtime Evidence Interchange and Integration Roadmap + +**Project:** `Icod.TermInfo` +**Release:** `1.13.0` +**Development branch:** `1.13.0` +**Theme:** Persistent-Raster Runtime Evidence Interchange and Deterministic Integration +**Primary package:** `Icod.TermInfo.Inspection` +**Baseline:** stable `1.12.0` +**Downstream qualification target:** `Icod.Terminal` semantic capability inspection and verification +**Frozen contracts:** existing 1.x Runtime/Source/Compiler/Termcap APIs, Inspection contracts through 1.12, JSON schemas v1-v4, database-set precedence, persistent-raster lifecycle semantics, and advanced persistent-raster placement semantics except for unavoidable defect corrections +**Status:** RE01-RE08 complete; Alpha-8 and stable `1.13.0` product candidates validated; release-ready pending merge/tag/publication +**Tranche prefix:** `RE` +**Release audit:** `docs/1.13.0-RELEASE-AUDIT.md` when created + +--- + +## 1. Release objective + +`Icod.TermInfo 1.13.0` SHALL provide a protocol-neutral, bounded, deterministic way for caller-owned runtime verification results to cross into the existing persistent-raster evidence, classification, and planning system. + +The current architecture is correct but mechanically awkward for consumers: + +```text +TermInfo static evidence + -> classify + -> plan + -> runtime verification required + -> consumer performs live verification + -> consumer manually creates TermInfo evidence + -> consumer calculates source ordinals + -> consumer merges evidence + -> reclassify + -> replan +``` + +Version 1.13 SHALL make that handoff explicit: + +```text +TermInfo static evidence + -> classify / plan + -> runtime verification required + -> external verifier + -> protocol-neutral runtime observations + -> deterministic evidence integration + -> existing 1.11 / 1.12 classifiers + -> existing planners +``` + +TermInfo SHALL still perform no terminal I/O. + +The purpose of 1.13 is not to invent another planning model. It is to make verified runtime facts portable, inspectable, replayable, bounded, and safely integrable with the frozen lifecycle and placement models. + +--- + +## 2. Architectural boundary + +### 2.1 Preserve the existing planners + +The following remain authoritative and frozen except for unavoidable defect correction: + +- 1.11 persistent-raster lifecycle evidence, profile, request, classification, and planner semantics; +- 1.12 persistent-raster placement evidence, profile, request, classification, and planner semantics. + +1.13 SHALL NOT replace, reinterpret, widen, or silently bypass those models. + +Runtime integration ultimately SHALL produce ordinary existing: + +```text +PersistentRasterLifecycleEvidence +PersistentRasterPlacementEvidence +``` + +which are then consumed by the frozen classifiers and planners. + +### 2.2 TermInfo does not own verification + +TermInfo SHALL NOT: + +- open a terminal session; +- send probe traffic; +- select a protocol backend; +- inspect PTY/TTY handles; +- determine current endpoint availability; +- retain terminal generation/session identity; +- decide whether a live capability is presently usable; +- execute persistent-raster operations. + +Those responsibilities remain with consumers such as `Icod.Terminal`. + +### 2.3 Runtime observation is not static evidence + +A new runtime-observation model SHALL describe facts supplied by an external verifier. + +It SHALL distinguish at least: + +```text +Supported +Unsupported +Inconclusive +``` + +`Supported` and `Unsupported` may become existing `Verified` evidence. + +`Inconclusive` SHALL remain visible in integration/audit results but SHALL NOT be silently converted into either positive or negative evidence. + +### 2.4 No backend identities + +The interchange model SHALL remain semantic. + +It SHALL NOT expose public protocol/backend values such as: + +```text +Kitty +Sixel +iTerm +OSC +CSI +protocol backend ID +terminal brand +``` + +A runtime verifier may internally use any protocol necessary to establish a semantic result. + +### 2.5 Deterministic provenance + +Runtime observations SHALL retain bounded provenance sufficient for deterministic auditing: + +- stable caller-supplied source label; +- deterministic source-local ordinal; +- semantic subject; +- outcome. + +They SHALL NOT require timestamps, host names, process IDs, session IDs, generation IDs, or other nondeterministic environmental metadata. + +--- + +## 3. Proposed public vocabulary + +RE01 SHALL freeze public concepts equivalent in responsibility to: + +```text +PersistentRasterRuntimeObservationOutcome + +PersistentRasterLifecycleRuntimeObservation +PersistentRasterPlacementRuntimeObservation + +PersistentRasterRuntimeObservationSet + +PersistentRasterRuntimeIntegrationIssueKind +PersistentRasterRuntimeIntegrationIssue +PersistentRasterRuntimeIntegrationResult + +PersistentRasterRuntimeEvidenceIntegrator +``` + +Exact names may be adjusted before RE01 freezes the public surface, but responsibilities SHALL remain bounded to this architecture. + +### 3.1 Observation outcome + +The initial runtime-observation outcome vocabulary SHALL contain exactly: + +```text +Supported +Unsupported +Inconclusive +``` + +No `Advertised` state belongs in runtime interchange. Advertised/static evidence already has an existing representation. + +### 3.2 Subject reuse + +Lifecycle runtime observations SHALL target the existing: + +```text +PersistentRasterLifecycleEvidenceSubject +``` + +Placement runtime observations SHALL target the existing: + +```text +PersistentRasterPlacementSubject +``` + +1.13 SHALL NOT introduce a third duplicate unified persistent-raster subject enumeration merely for interchange. + +--- + +## 4. Observation-set semantics + +A `PersistentRasterRuntimeObservationSet` SHALL be: + +- immutable; +- bounded; +- snapshot-based; +- culture-independent; +- deterministic; +- explicit about lifecycle versus placement observations. + +The set SHALL permit: + +- positive observations; +- negative observations; +- inconclusive observations; +- multiple observations for one semantic subject; +- contradictory observations supplied by the caller. + +Contradiction is evidence, not an exception. + +Invalid enum values, negative source-local ordinals, null elements, excessive counts, malformed source labels, and other programming errors SHALL fail through ordinary argument validation. + +--- + +## 5. Deterministic evidence integration + +The integration layer SHALL solve the mechanical work consumers currently perform themselves. + +Given: + +```text +existing lifecycle profile/evidence +existing placement profile/evidence +runtime observation set +``` + +the integrator SHALL: + +1. validate and snapshot the incoming observations; +2. preserve the complete existing evidence snapshots; +3. retain inconclusive observations without strengthening support; +4. map supported observations to positive existing `Verified` evidence; +5. map unsupported observations to negative existing `Verified` evidence; +6. append mapped evidence deterministically; +7. assign safe final source ordinals; +8. enforce resulting combined-evidence bounds; +9. detect ordinal-space exhaustion rather than overflowing; +10. re-run the frozen lifecycle and placement classifiers; +11. expose strengthened resulting profiles; +12. expose structured integration issues and audit evidence. + +The integrator SHALL NOT hide contradictions. + +For example: + +```text +static CapabilityDerived support + + +runtime Unsupported observation + -> +Verified negative evidence + -> +Unsupported or Contradicted according to the frozen classifier rules +``` + +1.13 SHALL not invent a second support-precedence system. + +--- + +## 6. Source ordinal handling + +Consumers SHALL no longer need application-specific code equivalent to: + +```text +max(existing.SourceOrdinal) + 1 +``` + +Runtime observations SHALL carry **source-local ordering**, not assumed final classifier ordinals. + +The integration layer SHALL assign safe final ordinals after the existing evidence snapshot while preserving the deterministic relative ordering of imported observations. + +If a safe append cannot be represented, integration SHALL fail deterministically through a structured integration outcome/issue rather than integer overflow, wrapping, arbitrary renumbering, or evidence loss. + +--- + +## 7. Replanning boundary + +1.13 SHALL deliberately reuse the existing planners rather than introduce a new aggregate support/status model. + +The intended flow is: + +```text +integrationResult = + integrate(existingProfiles, runtimeObservations) + +lifecyclePlan = + PersistentRasterLifecyclePlanner.Plan( + integrationResult.LifecycleProfile, + lifecycleRequest + ) + +placementPlan = + PersistentRasterPlacementPlanner.Plan( + lifecyclePlan, + integrationResult.PlacementProfile, + placementRequest + ) +``` + +Convenience APIs MAY perform this orchestration if they delegate to the frozen planners and do not reinterpret their outcomes. + +A new third persistent-raster planner or a new combined plan-status enumeration is explicitly disfavored. + +--- + +## 8. Machine-readable automation — JSON version 5 + +JSON versions 1 through 4 SHALL remain immutable historical contracts. + +Version 1.13 SHALL add JSON schema **version 5**. + +The preferred version-5 document kinds are exactly: + +```text +persistentRasterRuntimeObservationSet +persistentRasterRuntimeIntegration +``` + +### 8.1 Observation document + +The observation document SHALL expose deterministic bounded representations of: + +- source label; +- lifecycle observations; +- placement observations; +- semantic subjects; +- observation outcomes; +- source-local ordinals. + +### 8.2 Integration document + +The integration document SHALL expose deterministic bounded representations of: + +- imported observations; +- mapped verified lifecycle evidence; +- mapped verified placement evidence; +- inconclusive observations; +- structured integration issues; +- resulting lifecycle support states; +- resulting placement support states. + +It SHALL NOT expose: + +- protocol/backend identifiers; +- terminal resource IDs; +- terminal placement IDs; +- timestamps; +- current terminal endpoint availability; +- current session usability; +- raw probe responses. + +`TermInfoJsonRenderer` SHALL remain deterministic, bounded, cancelable, and culture-independent. + +### 8.3 JSON input is deferred + +1.13 SHALL NOT introduce a general-purpose JSON deserializer for historical Inspection document kinds. + +The CLR runtime-observation model SHALL be the authoritative ingestion surface. + +Version-5 JSON is a deterministic interchange/audit representation. A future release may add carefully bounded parsing only if a concrete cross-process consumer justifies it. + +--- + +## 9. Terminal interoperability + +A package-only qualification consumer SHALL demonstrate loose coupling with `Icod.Terminal`. + +The intended downstream mapping is: + +```text +Terminal-owned live verification + | + v +semantic live result + | + v +consumer adapter + | + v +PersistentRasterRuntimeObservationSet + | + v +TermInfo integration + | + v +existing lifecycle / placement classifiers + | + v +existing planners +``` + +The adapter SHALL remain outside production `Icod.TermInfo`. + +No production TermInfo API SHALL reference: + +```text +TerminalCapabilityStatus +TerminalSession +Icod.Terminal +``` + +The downstream qualification sample SHOULD replace the hand-written evidence-factory and ordinal-merging pattern currently required by consumers. + +--- + +## 10. Bounds + +1.13 SHALL reuse existing Inspection-wide defensive bounds wherever they serve the same denial-of-service purpose. + +The runtime-observation model SHALL explicitly bound: + +- observation count; +- source-label size; +- resulting combined evidence count; +- JSON UTF-8 output size. + +The evidence count MUST remain compatible with the existing lifecycle and placement classifier limits. + +No unbounded provenance strings, arbitrary nested metadata dictionaries, opaque extension bags, or unbounded caller payloads SHALL be introduced. + +--- + +## 11. Error model + +Programming errors SHALL throw ordinary argument exceptions: + +- null required arguments; +- invalid enum values; +- negative source-local ordinal; +- malformed source labels; +- observation count above the configured maximum. + +Normal semantic/integration outcomes SHALL be represented, not thrown: + +- inconclusive verification; +- verified support; +- verified non-support; +- contradiction; +- evidence which cannot strengthen the current conclusion; +- ordinal-space exhaustion; +- resulting combined-evidence limit exhaustion. + +Cancellation remains ordinary `OperationCanceledException` for applicable rendering/API operations accepting cancellation tokens. + +--- + +## 12. Tranche plan + +Development SHALL proceed through eight tranches: + +```text +RE01 -> 1.13.0-Alpha-1 +RE02 -> 1.13.0-Alpha-2 +RE03 -> 1.13.0-Alpha-3 +RE04 -> 1.13.0-Alpha-4 +RE05 -> 1.13.0-Alpha-5 +RE06 -> 1.13.0-Alpha-6 +RE07 -> 1.13.0-Alpha-7 +RE08 -> 1.13.0-Alpha-8 +``` + +Each completed tranche SHALL update the coordinated suite version before the tranche is considered complete. Stable `1.13.0` SHALL promote the validated Alpha-8 surface without adding new semantics. + +### RE01 — Architecture, vocabulary, and public API regret gate + +Freeze: + +- runtime-observation versus static-evidence distinction; +- subject reuse; +- observation outcome vocabulary; +- deterministic provenance; +- bounds; +- integration responsibilities; +- JSON v5 decision; +- Terminal ownership boundary; +- explicit exclusions. + +**Gate:** the public model can carry external verified facts without introducing Terminal/protocol dependencies or mutating frozen 1.11/1.12 evidence models. + +### RE02 — Immutable runtime observations + +Implement immutable lifecycle and placement runtime-observation values plus bounded observation sets. + +Cover: + +- Supported; +- Unsupported; +- Inconclusive; +- deterministic source-local ordering; +- snapshotting; +- validation; +- equality expectations; +- culture independence. + +**Gate:** a consumer can capture a complete bounded runtime-verification result without constructing classifier evidence itself. + +### RE03 — Deterministic evidence integration + +Implement conversion of conclusive runtime observations into existing `Verified` lifecycle/placement evidence. + +Add: + +- safe ordinal assignment; +- complete existing-evidence preservation; +- combined-evidence limits; +- deterministic append ordering; +- overflow handling; +- inconclusive retention. + +**Gate:** consumers no longer need custom evidence factories or `GetNextSourceOrdinal` logic. + +### RE04 — Classification and contradiction integration + +Apply integrated evidence through the frozen lifecycle and placement classifiers. + +Exercise: + +- static-positive + verified-negative; +- static-negative + verified-positive; +- verified-positive + verified-negative; +- repeated compatible runtime observations; +- inconclusive-only observation sets; +- mixed lifecycle/placement observations. + +**Gate:** runtime evidence strengthens or contradicts knowledge exclusively according to the frozen classifier precedence rules. + +### RE05 — Replanning composition and audit result + +Complete the orchestration boundary above the integration result. + +Provide ergonomic APIs for: + +```text +integrate +classify +replan +inspect resulting issues +``` + +without introducing a replacement planner or new aggregate plan status. + +Preserve both resulting profiles and the runtime observation/integration audit trail. + +**Gate:** the normal static-plan → runtime-verification → replan workflow can be expressed without application-specific evidence-merging boilerplate. + +### RE06 — JSON v5 runtime-evidence automation + +Add deterministic rendering and schema for exactly: + +```text +persistentRasterRuntimeObservationSet +persistentRasterRuntimeIntegration +``` + +Freeze v1-v4 fingerprints unchanged. + +Add normalized fixtures, UTF-8 bounds, cancellation, culture, repetition, and schema validation. + +**Gate:** CI and tooling can persist and inspect the runtime-evidence handoff without parsing prose. + +### RE07 — Terminal interoperability and sample qualification + +Add a package-only consumer against stable `Icod.Terminal`. + +Demonstrate: + +- Terminal-owned verification; +- caller-owned adapter; +- TermInfo runtime observation set; +- deterministic integration; +- reclassification; +- replanning; +- no production TermInfo → Terminal dependency. + +Update persistent-raster samples to remove hand-written evidence-merging and ordinal-management where the 1.13 API supersedes it. + +Execute package/sample consumers on: + +```text +net8.0 +net9.0 +net10.0 +``` + +**Gate:** a real downstream consumer can perform the complete handoff using stable packages rather than repository-internal assumptions. + +### RE08 — Hardening, exact freeze, documentation, and release closure + +Complete: + +- adversarial bounds; +- duplicate and contradictory observation stress; +- ordinal boundary tests; +- deterministic cross-culture/cross-process tests; +- exact Inspection public API fingerprint; +- exact v1-v5 schema fingerprints; +- package-only consumers; +- samples; +- README/versioning/compatibility updates; +- release audit; +- Windows PowerShell verification; +- Windows/Linux/macOS Build/Test; +- installed-tool smoke; +- six archive RIDs. + +**Gate:** Alpha-8 is releasable without semantic changes during stable promotion. + +--- + +## 13. Stable-release acceptance criteria + +`Icod.TermInfo 1.13.0` is ready for stable release when: + +1. callers can represent supported, unsupported, and inconclusive runtime observations; +2. lifecycle and placement subjects reuse the frozen 1.11/1.12 semantic enumerations; +3. no protocol/backend enumeration enters the TermInfo public API; +4. no TermInfo production project references `Icod.Terminal`; +5. conclusive observations map deterministically to existing `Verified` evidence; +6. inconclusive observations remain visible without becoming support or non-support; +7. consumers no longer calculate final evidence ordinals manually; +8. existing evidence is not rewritten or discarded; +9. contradictions remain visible; +10. frozen classifiers remain authoritative; +11. frozen planners remain authoritative; +12. JSON v1-v4 are unchanged; +13. JSON v5 contains exactly the runtime observation and integration documents; +14. observation and resulting evidence counts are bounded; +15. package-only interoperability with Terminal is proven; +16. multi-TFM consumers and samples pass; +17. Windows PowerShell 5.1 release verification remains green; +18. Windows/Linux/macOS package validation and all six archive RIDs pass. + +--- + +## 14. Explicit exclusions + +1.13 SHALL NOT include: + +- live terminal probing implemented by TermInfo; +- Terminal session or endpoint types; +- public Kitty/Sixel/iTerm identifiers; +- backend selection; +- backend preference ordering; +- multi-protocol negotiation; +- terminal-brand heuristics; +- relative placement graphs; +- animation/frame lifecycle; +- Unicode placeholders; +- scene graphs; +- image codecs; +- raster pixel transport; +- terminal resource/placement identities; +- arbitrary timestamps/host provenance; +- generic JSON deserialization; +- changes to database-set precedence; +- changes to 1.11 lifecycle semantics; +- changes to 1.12 placement semantics. + +--- + +## 15. Post-1.13 handoff + +If 1.13 succeeds, it creates the foundation for the previously discussed multi-protocol preference/negotiation track. + +A later release—likely 1.14 or beyond—can then reason over: + +```text +static semantic knowledge ++ +runtime evidence ++ +multiple genuinely viable execution backends ++ +caller preference policy +``` + +without conflating evidence gathering, backend discovery, and preference policy. + +That later track SHOULD begin only when at least two meaningful implementations exist for the same semantic operation and choosing between them has real behavioral value. + +Until then, backend ranking remains intentionally out of TermInfo. diff --git a/Icod.TermInfo-Post-1.0-Development-Roadmap.md b/Icod.TermInfo-Post-1.0-Development-Roadmap.md index d209d2845..64e2c3750 100644 --- a/Icod.TermInfo-Post-1.0-Development-Roadmap.md +++ b/Icod.TermInfo-Post-1.0-Development-Roadmap.md @@ -10,7 +10,8 @@ **Language:** C# 13 **Target frameworks:** `net8.0`; `net9.0`; `net10.0` **Frozen runtime contract:** `1.0.0` -**Current coordinated version:** `1.12.0` +**Current coordinated version:** `1.13.0` +**Next development line:** `TBD` **Final 1.6 prerelease:** `1.6.0-Alpha-8` **Final 1.7 prerelease:** `1.7.0-Alpha-8` **Final 1.8 prerelease:** `1.8.0-Alpha-8` @@ -18,11 +19,13 @@ **Final 1.10 prerelease:** `1.10.0-Alpha-8` **Final 1.11 prerelease:** `1.11.0-Alpha-8` **Final 1.12 prerelease:** `1.12.0-Alpha-8` -**Latest completed line:** `1.12.0` - Advanced Persistent-Raster Placement Semantics and Planning -**Status:** 1.12.0 implementation and stable promotion complete; PR #42 remains open pending merge -**Completed tranches:** PG01-PG08 -**Primary objective:** Completed - protocol-neutral source-rectangle and signed-z-order placement semantics beside the frozen 1.11 persistent-raster lifecycle model, preserving JSON v1-v3 and downstream execution ownership. -**Release audit:** `docs/1.12.0-RELEASE-AUDIT.md` +**Final 1.13 prerelease:** `1.13.0-Alpha-8` +**Latest completed line:** `1.13.0` - Persistent-Raster Runtime Evidence Interchange and Integration +**Status:** RE01-RE08 complete; Alpha-8 and stable `1.13.0` product candidates validated; release-ready in draft PR #43; merge/tag/publication not performed +**Planned tranches:** RE01-RE08 +**Primary objective:** Add protocol-neutral runtime-evidence interchange and deterministic integration so external verification can strengthen the frozen 1.11 lifecycle and 1.12 placement models without manual evidence construction or source-ordinal management. +**Active development roadmap:** `Icod.TermInfo-1.13.0-Persistent-Raster-Runtime-Evidence-Interchange-and-Integration-Roadmap.md` +**Latest completed release audit:** `docs/1.13.0-RELEASE-AUDIT.md` --- @@ -97,7 +100,12 @@ in a new version-specific roadmap, not in the retired inventory. | **1.10.0** | Deterministic multi-database inspection, comparison, and planning automation | Aggregate ordered explicit catalogs with stable evidence, then add precedence, conflict analysis, set comparison, multi-catalog planning, and versioned automation | | **1.11.0** | Persistent-raster lifecycle semantics and planning | Classify protocol-neutral persistent-raster lifecycle evidence and produce deterministic advisory plans without owning terminal execution | | **1.12.0** | Advanced persistent-raster placement semantics and planning | Classify source-rectangle and signed-z-order placement support and compose those requirements with the frozen 1.11 lifecycle model | -| **later** | Exotic storage/formats and broader graphics policy | Berkeley DB, historical Unix dialects, multi-protocol preference/negotiation, and other deferred work as justified | +| **1.13.0** | Persistent-raster runtime evidence interchange and integration | Normalize caller-owned runtime observations, integrate conclusive results as existing verified lifecycle/placement evidence, and remove manual evidence/ordinal bridging without owning live verification | +| **later** | Exotic storage/formats and broader graphics policy | Berkeley DB, historical Unix dialects, multi-protocol preference/negotiation after runtime-evidence interchange is mature, and other deferred work as justified | + +Version 1.13.0 is governed by +[`Icod.TermInfo-1.13.0-Persistent-Raster-Runtime-Evidence-Interchange-and-Integration-Roadmap.md`](Icod.TermInfo-1.13.0-Persistent-Raster-Runtime-Evidence-Interchange-and-Integration-Roadmap.md). +RE01 freezes the runtime-observation vocabulary and API regret gate while preserving the frozen 1.11 lifecycle and 1.12 placement semantics. RE02 adds immutable bounded runtime observations. RE03 maps conclusive observations into existing `Verified` evidence with deterministic safe ordinal assignment. RE04 proves integration through the frozen classifiers and contradiction rules. RE05 composes strengthened profiles back through the frozen planners without inventing a replacement planning model. RE06 adds additive JSON version 5 observation/integration documents while preserving v1-v4. RE07 qualifies loose coupling with `Icod.Terminal` and removes hand-written evidence/ordinal bridging from the downstream sample. RE08 hardens, fingerprints, documents, and closes the release. Multi-protocol preference/negotiation remains a later track after runtime-evidence interchange is mature and multiple meaningful backends justify policy. The completed 1.5 release contract is recorded in [`Icod.TermInfo-1.5.0-Coordinated-Distribution-Roadmap.md`](Icod.TermInfo-1.5.0-Coordinated-Distribution-Roadmap.md) diff --git a/Icod.TermInfo.Inspection/Icod.TermInfo.Inspection.csproj b/Icod.TermInfo.Inspection/Icod.TermInfo.Inspection.csproj index 7ab1cfb93..749e633c5 100644 --- a/Icod.TermInfo.Inspection/Icod.TermInfo.Inspection.csproj +++ b/Icod.TermInfo.Inspection/Icod.TermInfo.Inspection.csproj @@ -22,7 +22,7 @@ Icod.TermInfo.Inspection Timothy J. Bruce Managed inspection, semantic-comparison, planning, and machine-readable automation foundation for Icod.TermInfo. - 1.12.0 adds protocol-neutral persistent-raster source-rectangle and signed-z-order evidence, classification, lifecycle-aware semantic planning, TerminalDescription/database-set composition, additive version-4 profile/plan JSON automation, and package-only interoperability qualification against Icod.Terminal 1.12.0 without adding a production Terminal dependency. + 1.13.0 adds bounded immutable persistent-raster runtime observations, deterministic atomic evidence integration, planner-delegating replanning, additive version-5 observation/integration JSON automation, and package-only interoperability qualification against Icod.Terminal 1.12.0 without adding a production Terminal dependency. README.md icon.png https://github.com/uniblab/Icod.TermInfo @@ -32,7 +32,7 @@ snupkg true true - terminfo;libtinfo;terminal;inspection;infocmp;comparison;decompiler;planning;persistent-raster;raster-placement;dotnet;csharp + terminfo;libtinfo;terminal;inspection;infocmp;comparison;decompiler;planning;persistent-raster;raster-placement;runtime-evidence;dotnet;csharp https://github.com/uniblab/Icod.TermInfo git false @@ -81,6 +81,7 @@ + diff --git a/Icod.TermInfo.Inspection/README.md b/Icod.TermInfo.Inspection/README.md index ccdfdcc92..0a99cc686 100644 --- a/Icod.TermInfo.Inspection/README.md +++ b/Icod.TermInfo.Inspection/README.md @@ -3,6 +3,36 @@ `Icod.TermInfo.Inspection` is the optional managed inspection and semantic- comparison layer for the `Icod.TermInfo` package family. +## 1.13 release-ready status + +`1.13.0` promotes the validated Alpha-8 additive +`PersistentRasterRuntime*` interchange contract without semantic, public-API, +schema, dependency, target-framework, or command changes. Caller-owned lifecycle +and placement runtime observations are immutable, bounded, canonical, and +protocol-neutral. Conclusive observations integrate as existing `Verified` +evidence through the frozen classifiers; inconclusive observations remain +audit-visible, and represented family capacity/ordinal limitations fail atomically +per family. + +`PersistentRasterRuntimeIntegrationResult` delegates lifecycle and placement +replanning to the existing planners. JSON version 5 adds exactly +`persistentRasterRuntimeObservationSet` and +`persistentRasterRuntimeIntegration`, while JSON versions 1 through 4 remain +byte-frozen. The whole 1.13 Inspection reflection manifest contains 90 +exported public types and has normalized-LF SHA-256 +`fd827a25abafb8e9ff3915567f45f9f2ec51b332bfd8a82dd4e4bca20290e764`. + +Inspection still has no production `Icod.Terminal` dependency. RE07 qualifies +the caller adapter boundary using published `Icod.Terminal 1.12.0` beside the +fresh Inspection package on net8.0, net9.0, and net10.0. Exact Alpha-8 +product head `f236c33d8239e80379bf8cf0f1123abd6c93c3cb` passed qualification run +`34797445315`; stable product head `6e9217b16c3023fb10fa34dbaa74afe48448d858` +then passed qualification run `34799272472`, again with all 12 jobs green. PR #43 +remains unmerged and no tag or package publication has been performed. See +`../docs/1.13.0-PERSISTENT-RASTER-RUNTIME-EVIDENCE-GUIDE.md`, +`../docs/1.13.0-INSPECTION-PUBLIC-API-FREEZE.md`, and +`../docs/1.13.0-RELEASE-AUDIT.md`. + ## 1.12 release status Version `1.12.0` promotes the additive advanced persistent-raster placement diff --git a/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeEvidenceIntegrator.cs b/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeEvidenceIntegrator.cs new file mode 100644 index 000000000..eb2db20f3 --- /dev/null +++ b/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeEvidenceIntegrator.cs @@ -0,0 +1,257 @@ +namespace Icod.TermInfo.Inspection; + +/// +/// Deterministically integrates caller-owned persistent-raster runtime +/// observations into the frozen lifecycle and placement evidence models. +/// +public static class PersistentRasterRuntimeEvidenceIntegrator { + /// + /// Integrates conclusive runtime observations as existing Verified + /// evidence, retains inconclusive observations for audit, and delegates final + /// support classification to the frozen lifecycle and placement classifiers. + /// + /// + /// The existing classified lifecycle profile and complete evidence snapshot. + /// + /// + /// The existing classified placement profile and complete evidence snapshot. + /// + /// + /// The immutable canonical caller-owned runtime-observation snapshot. + /// + /// An immutable deterministic integration audit result. + public static PersistentRasterRuntimeIntegrationResult Integrate( + PersistentRasterLifecycleProfile lifecycleProfile, + PersistentRasterPlacementProfile placementProfile, + PersistentRasterRuntimeObservationSet observations + ) { + ArgumentNullException.ThrowIfNull( lifecycleProfile ); + ArgumentNullException.ThrowIfNull( placementProfile ); + ArgumentNullException.ThrowIfNull( observations ); + + PersistentRasterRuntimeLifecycleObservation[] conclusiveLifecycle = + observations.LifecycleObservations + .Where( + item => item.Outcome + != PersistentRasterRuntimeObservationOutcome.Inconclusive + ) + .ToArray(); + PersistentRasterRuntimePlacementObservation[] conclusivePlacement = + observations.PlacementObservations + .Where( + item => item.Outcome + != PersistentRasterRuntimeObservationOutcome.Inconclusive + ) + .ToArray(); + PersistentRasterRuntimeLifecycleObservation[] inconclusiveLifecycle = + observations.LifecycleObservations + .Where( + item => item.Outcome + == PersistentRasterRuntimeObservationOutcome.Inconclusive + ) + .ToArray(); + PersistentRasterRuntimePlacementObservation[] inconclusivePlacement = + observations.PlacementObservations + .Where( + item => item.Outcome + == PersistentRasterRuntimeObservationOutcome.Inconclusive + ) + .ToArray(); + + ( + IReadOnlyList importedLifecycle, + PersistentRasterLifecycleProfile resultingLifecycleProfile, + PersistentRasterRuntimeIntegrationIssue? lifecycleIssue + ) = IntegrateLifecycleFamily( + lifecycleProfile, + conclusiveLifecycle + ); + ( + IReadOnlyList importedPlacement, + PersistentRasterPlacementProfile resultingPlacementProfile, + PersistentRasterRuntimeIntegrationIssue? placementIssue + ) = IntegratePlacementFamily( + placementProfile, + conclusivePlacement + ); + + List issues = []; + if ( lifecycleIssue is not null ) { + issues.Add( lifecycleIssue ); + } + if ( placementIssue is not null ) { + issues.Add( placementIssue ); + } + + return new PersistentRasterRuntimeIntegrationResult( + observations, + importedLifecycle, + importedPlacement, + inconclusiveLifecycle, + inconclusivePlacement, + resultingLifecycleProfile, + resultingPlacementProfile, + issues + ); + } + + private static ( + IReadOnlyList ImportedEvidence, + PersistentRasterLifecycleProfile Profile, + PersistentRasterRuntimeIntegrationIssue? Issue + ) IntegrateLifecycleFamily( + PersistentRasterLifecycleProfile profile, + IReadOnlyList observations + ) { + int existingCount = profile.Evidence.Count; + int importCount = observations.Count; + int maximumCount = + PersistentRasterLifecycleEvidenceOptions.MaximumSupportedEvidenceCount; + if ( importCount > maximumCount - existingCount ) { + return ( + Array.Empty(), + profile, + new PersistentRasterRuntimeIntegrationIssue( + PersistentRasterRuntimeIntegrationIssueKind + .LifecycleEvidenceCapacityExhausted, + existingCount, + importCount + ) + ); + } + if ( importCount == 0 ) { + return ( + Array.Empty(), + profile, + null + ); + } + + int firstImportedOrdinal = 0; + if ( existingCount > 0 ) { + int maximumExistingOrdinal = + profile.Evidence.Max( item => item.SourceOrdinal ); + long availableOrdinalCount = + (long)int.MaxValue - maximumExistingOrdinal; + if ( importCount > availableOrdinalCount ) { + return ( + Array.Empty(), + profile, + new PersistentRasterRuntimeIntegrationIssue( + PersistentRasterRuntimeIntegrationIssueKind + .LifecycleOrdinalSpaceExhausted, + existingCount, + importCount + ) + ); + } + firstImportedOrdinal = maximumExistingOrdinal + 1; + } + + PersistentRasterLifecycleEvidence[] imported = + new PersistentRasterLifecycleEvidence[ importCount ]; + for ( int index = 0; index < observations.Count; index++ ) { + PersistentRasterRuntimeLifecycleObservation observation = + observations[ index ]; + imported[ index ] = new PersistentRasterLifecycleEvidence( + observation.Subject, + observation.Outcome + == PersistentRasterRuntimeObservationOutcome.Supported, + PersistentRasterLifecycleEvidenceKind.Verified, + observation.SourceLabel, + checked( firstImportedOrdinal + index ) + ); + } + + PersistentRasterLifecycleProfile resultingProfile = + PersistentRasterLifecycleClassifier.Classify( + profile.Evidence.Concat( imported ), + new PersistentRasterLifecycleEvidenceOptions( maximumCount ) + ); + return ( + Array.AsReadOnly( imported ), + resultingProfile, + null + ); + } + + private static ( + IReadOnlyList ImportedEvidence, + PersistentRasterPlacementProfile Profile, + PersistentRasterRuntimeIntegrationIssue? Issue + ) IntegratePlacementFamily( + PersistentRasterPlacementProfile profile, + IReadOnlyList observations + ) { + int existingCount = profile.Evidence.Count; + int importCount = observations.Count; + int maximumCount = + PersistentRasterPlacementEvidenceOptions.MaximumSupportedEvidenceCount; + if ( importCount > maximumCount - existingCount ) { + return ( + Array.Empty(), + profile, + new PersistentRasterRuntimeIntegrationIssue( + PersistentRasterRuntimeIntegrationIssueKind + .PlacementEvidenceCapacityExhausted, + existingCount, + importCount + ) + ); + } + if ( importCount == 0 ) { + return ( + Array.Empty(), + profile, + null + ); + } + + int firstImportedOrdinal = 0; + if ( existingCount > 0 ) { + int maximumExistingOrdinal = + profile.Evidence.Max( item => item.SourceOrdinal ); + long availableOrdinalCount = + (long)int.MaxValue - maximumExistingOrdinal; + if ( importCount > availableOrdinalCount ) { + return ( + Array.Empty(), + profile, + new PersistentRasterRuntimeIntegrationIssue( + PersistentRasterRuntimeIntegrationIssueKind + .PlacementOrdinalSpaceExhausted, + existingCount, + importCount + ) + ); + } + firstImportedOrdinal = maximumExistingOrdinal + 1; + } + + PersistentRasterPlacementEvidence[] imported = + new PersistentRasterPlacementEvidence[ importCount ]; + for ( int index = 0; index < observations.Count; index++ ) { + PersistentRasterRuntimePlacementObservation observation = + observations[ index ]; + imported[ index ] = new PersistentRasterPlacementEvidence( + observation.Subject, + observation.Outcome + == PersistentRasterRuntimeObservationOutcome.Supported, + PersistentRasterPlacementEvidenceKind.Verified, + observation.SourceLabel, + checked( firstImportedOrdinal + index ) + ); + } + + PersistentRasterPlacementProfile resultingProfile = + PersistentRasterPlacementClassifier.Classify( + profile.Evidence.Concat( imported ), + new PersistentRasterPlacementEvidenceOptions( maximumCount ) + ); + return ( + Array.AsReadOnly( imported ), + resultingProfile, + null + ); + } +} diff --git a/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeIntegrationIssue.cs b/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeIntegrationIssue.cs new file mode 100644 index 000000000..74d28e73a --- /dev/null +++ b/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeIntegrationIssue.cs @@ -0,0 +1,59 @@ +namespace Icod.TermInfo.Inspection; + +/// +/// Represents one immutable structured limitation encountered while integrating +/// persistent-raster runtime observations. +/// +public sealed class PersistentRasterRuntimeIntegrationIssue { + internal PersistentRasterRuntimeIntegrationIssue( + PersistentRasterRuntimeIntegrationIssueKind kind, + int existingEvidenceCount, + int requestedImportCount + ) { + if ( !Enum.IsDefined( kind ) ) { + throw new ArgumentOutOfRangeException( + nameof( kind ), + kind, + "The runtime integration issue kind must be a defined value." + ); + } + if ( existingEvidenceCount < 0 ) { + throw new ArgumentOutOfRangeException( + nameof( existingEvidenceCount ), + existingEvidenceCount, + "The existing evidence count cannot be negative." + ); + } + if ( requestedImportCount < 0 ) { + throw new ArgumentOutOfRangeException( + nameof( requestedImportCount ), + requestedImportCount, + "The requested import count cannot be negative." + ); + } + + Kind = kind; + ExistingEvidenceCount = existingEvidenceCount; + RequestedImportCount = requestedImportCount; + } + + /// Gets the represented integration limitation. + public PersistentRasterRuntimeIntegrationIssueKind Kind { + get; + } + + /// + /// Gets the number of existing evidence assertions in the affected family. + /// + public int ExistingEvidenceCount { + get; + } + + /// + /// Gets the number of conclusive runtime observations requested for import into + /// the affected family. + /// + public int RequestedImportCount { + get; + } +} diff --git a/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeIntegrationIssueKind.cs b/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeIntegrationIssueKind.cs new file mode 100644 index 000000000..02b29f41a --- /dev/null +++ b/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeIntegrationIssueKind.cs @@ -0,0 +1,31 @@ +namespace Icod.TermInfo.Inspection; + +/// +/// Describes one represented limitation encountered while importing persistent- +/// raster runtime observations into frozen lifecycle or placement evidence. +/// +public enum PersistentRasterRuntimeIntegrationIssueKind { + /// + /// Conclusive lifecycle observations cannot fit within the frozen lifecycle + /// evidence-count bound. + /// + LifecycleEvidenceCapacityExhausted = 0, + + /// + /// Conclusive placement observations cannot fit within the frozen placement + /// evidence-count bound. + /// + PlacementEvidenceCapacityExhausted = 1, + + /// + /// Consecutive final lifecycle evidence ordinals cannot be assigned without + /// overflowing . + /// + LifecycleOrdinalSpaceExhausted = 2, + + /// + /// Consecutive final placement evidence ordinals cannot be assigned without + /// overflowing . + /// + PlacementOrdinalSpaceExhausted = 3, +} diff --git a/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeIntegrationResult.cs b/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeIntegrationResult.cs new file mode 100644 index 000000000..0046c5d39 --- /dev/null +++ b/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeIntegrationResult.cs @@ -0,0 +1,169 @@ +namespace Icod.TermInfo.Inspection; + +/// +/// Represents the immutable deterministic audit result of integrating one runtime- +/// observation set into persistent-raster lifecycle and placement evidence. +/// +public sealed class PersistentRasterRuntimeIntegrationResult { + internal PersistentRasterRuntimeIntegrationResult( + PersistentRasterRuntimeObservationSet observations, + IEnumerable importedLifecycleEvidence, + IEnumerable importedPlacementEvidence, + IEnumerable + inconclusiveLifecycleObservations, + IEnumerable + inconclusivePlacementObservations, + PersistentRasterLifecycleProfile lifecycleProfile, + PersistentRasterPlacementProfile placementProfile, + IEnumerable issues + ) { + ArgumentNullException.ThrowIfNull( observations ); + ArgumentNullException.ThrowIfNull( importedLifecycleEvidence ); + ArgumentNullException.ThrowIfNull( importedPlacementEvidence ); + ArgumentNullException.ThrowIfNull( inconclusiveLifecycleObservations ); + ArgumentNullException.ThrowIfNull( inconclusivePlacementObservations ); + ArgumentNullException.ThrowIfNull( lifecycleProfile ); + ArgumentNullException.ThrowIfNull( placementProfile ); + ArgumentNullException.ThrowIfNull( issues ); + + Observations = observations; + ImportedLifecycleEvidence = Array.AsReadOnly( + importedLifecycleEvidence.ToArray() + ); + ImportedPlacementEvidence = Array.AsReadOnly( + importedPlacementEvidence.ToArray() + ); + InconclusiveLifecycleObservations = Array.AsReadOnly( + inconclusiveLifecycleObservations.ToArray() + ); + InconclusivePlacementObservations = Array.AsReadOnly( + inconclusivePlacementObservations.ToArray() + ); + LifecycleProfile = lifecycleProfile; + PlacementProfile = placementProfile; + Issues = Array.AsReadOnly( issues.ToArray() ); + Succeeded = Issues.Count == 0; + } + + /// + /// Gets the exact immutable runtime-observation set supplied to the integrator. + /// + public PersistentRasterRuntimeObservationSet Observations { + get; + } + + /// + /// Gets the lifecycle evidence imported from conclusive runtime observations. + /// + public IReadOnlyList + ImportedLifecycleEvidence { + get; + } + + /// + /// Gets the placement evidence imported from conclusive runtime observations. + /// + public IReadOnlyList + ImportedPlacementEvidence { + get; + } + + /// + /// Gets lifecycle runtime observations retained as inconclusive audit evidence. + /// + public IReadOnlyList + InconclusiveLifecycleObservations { + get; + } + + /// + /// Gets placement runtime observations retained as inconclusive audit evidence. + /// + public IReadOnlyList + InconclusivePlacementObservations { + get; + } + + /// + /// Gets the resulting lifecycle profile after successful lifecycle-family + /// integration, or the original lifecycle profile when that family could not be + /// imported atomically. + /// + public PersistentRasterLifecycleProfile LifecycleProfile { + get; + } + + /// + /// Gets the resulting placement profile after successful placement-family + /// integration, or the original placement profile when that family could not be + /// imported atomically. + /// + public PersistentRasterPlacementProfile PlacementProfile { + get; + } + + /// Gets represented integration limitations in deterministic order. + public IReadOnlyList Issues { + get; + } + + /// + /// Gets whether all conclusive lifecycle and placement observations were safely + /// imported. This does not imply that downstream plans are satisfiable. + /// + public bool Succeeded { + get; + } + + /// + /// Creates a lifecycle plan from the resulting integrated lifecycle profile by + /// delegating to the frozen lifecycle planner. + /// + /// The bounded semantic lifecycle request. + /// The frozen lifecycle planner result. + /// + /// is . + /// + public PersistentRasterLifecyclePlan CreateLifecyclePlan( + PersistentRasterLifecycleRequest request + ) { + ArgumentNullException.ThrowIfNull( request ); + + return PersistentRasterLifecyclePlanner.Plan( + LifecycleProfile, + request + ); + } + + /// + /// Creates an advanced placement plan by first planning the supplied lifecycle + /// request from the resulting integrated lifecycle profile and then delegating + /// the placement request to the frozen placement planner. + /// + /// The bounded semantic lifecycle request. + /// + /// The non-empty advanced-placement requirement request. + /// + /// The frozen placement planner result. + /// + /// Either request is . + /// + public PersistentRasterPlacementPlan CreatePlacementPlan( + PersistentRasterLifecycleRequest lifecycleRequest, + PersistentRasterPlacementRequest placementRequest + ) { + ArgumentNullException.ThrowIfNull( lifecycleRequest ); + ArgumentNullException.ThrowIfNull( placementRequest ); + + PersistentRasterLifecyclePlan lifecyclePlan = + PersistentRasterLifecyclePlanner.Plan( + LifecycleProfile, + lifecycleRequest + ); + return PersistentRasterPlacementPlanner.Plan( + lifecyclePlan, + PlacementProfile, + placementRequest + ); + } +} diff --git a/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeLifecycleObservation.cs b/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeLifecycleObservation.cs new file mode 100644 index 000000000..626c3330b --- /dev/null +++ b/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeLifecycleObservation.cs @@ -0,0 +1,86 @@ +namespace Icod.TermInfo.Inspection; + +/// +/// Represents one immutable protocol-neutral runtime observation about a +/// persistent-raster lifecycle semantic. +/// +public sealed class PersistentRasterRuntimeLifecycleObservation { + /// + /// Initializes one immutable lifecycle runtime observation. + /// + /// The lifecycle semantic being observed. + /// The caller-owned runtime observation outcome. + /// + /// A bounded caller-owned provenance label preserved exactly and compared + /// ordinally for deterministic ordering. + /// + /// + /// A non-negative deterministic source-local ordinal supplied by the runtime + /// verifier. + /// + public PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject subject, + PersistentRasterRuntimeObservationOutcome outcome, + string sourceLabel, + int sourceOrdinal + ) { + if ( !Enum.IsDefined( subject ) ) { + throw new ArgumentOutOfRangeException( + nameof( subject ), + subject, + "The lifecycle runtime-observation subject must be a defined value." + ); + } + if ( !Enum.IsDefined( outcome ) ) { + throw new ArgumentOutOfRangeException( + nameof( outcome ), + outcome, + "The runtime-observation outcome must be a defined value." + ); + } + ArgumentException.ThrowIfNullOrWhiteSpace( sourceLabel ); + if ( + sourceLabel.Length + > PersistentRasterRuntimeObservationOptions.MaximumSourceLabelLength + ) { + throw new ArgumentException( + $"The runtime-observation source label cannot exceed {PersistentRasterRuntimeObservationOptions.MaximumSourceLabelLength} UTF-16 code units.", + nameof( sourceLabel ) + ); + } + if ( sourceOrdinal < 0 ) { + throw new ArgumentOutOfRangeException( + nameof( sourceOrdinal ), + sourceOrdinal, + "The lifecycle runtime-observation source ordinal cannot be negative." + ); + } + + Subject = subject; + Outcome = outcome; + SourceLabel = sourceLabel; + SourceOrdinal = sourceOrdinal; + } + + /// Gets the lifecycle semantic being observed. + public PersistentRasterLifecycleEvidenceSubject Subject { + get; + } + + /// Gets the caller-owned runtime observation outcome. + public PersistentRasterRuntimeObservationOutcome Outcome { + get; + } + + /// Gets the exact bounded provenance label supplied by the caller. + public string SourceLabel { + get; + } + + /// + /// Gets the non-negative source-local ordinal supplied by the runtime verifier. + /// + public int SourceOrdinal { + get; + } +} diff --git a/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeObservationOptions.cs b/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeObservationOptions.cs new file mode 100644 index 000000000..4fe3d4643 --- /dev/null +++ b/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeObservationOptions.cs @@ -0,0 +1,64 @@ +namespace Icod.TermInfo.Inspection; + +/// +/// Defines deterministic resource bounds for persistent-raster runtime +/// observation snapshots. +/// +public sealed class PersistentRasterRuntimeObservationOptions { + /// + /// Gets the default maximum number of lifecycle and placement observations in + /// one combined runtime-observation set. + /// + public const int DefaultMaximumObservationCount = 256; + + /// + /// Gets the largest supported configured runtime-observation count. + /// + public const int MaximumSupportedObservationCount = 4096; + + /// + /// Gets the maximum permitted runtime-observation source-label length in UTF-16 + /// code units. + /// + public const int MaximumSourceLabelLength = 256; + + /// + /// Initializes options using the default observation-count bound. + /// + public PersistentRasterRuntimeObservationOptions() + : this( DefaultMaximumObservationCount ) { + } + + /// + /// Initializes options using an explicit observation-count bound. + /// + /// + /// Maximum combined lifecycle and placement observation count. + /// + /// + /// is outside the supported range. + /// + public PersistentRasterRuntimeObservationOptions( + int maximumObservationCount + ) { + if ( + maximumObservationCount < 1 + || maximumObservationCount > MaximumSupportedObservationCount + ) { + throw new ArgumentOutOfRangeException( + nameof( maximumObservationCount ), + maximumObservationCount, + $"The maximum runtime-observation count must be between 1 and {MaximumSupportedObservationCount}." + ); + } + + MaximumObservationCount = maximumObservationCount; + } + + /// + /// Gets the maximum combined lifecycle and placement observation count. + /// + public int MaximumObservationCount { + get; + } +} diff --git a/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeObservationOutcome.cs b/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeObservationOutcome.cs new file mode 100644 index 000000000..d6c70e04b --- /dev/null +++ b/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeObservationOutcome.cs @@ -0,0 +1,22 @@ +namespace Icod.TermInfo.Inspection; + +/// +/// Describes the result of one caller-owned protocol-neutral persistent-raster +/// runtime observation. +/// +public enum PersistentRasterRuntimeObservationOutcome { + /// + /// Runtime observation establishes support for the semantic subject. + /// + Supported = 0, + + /// + /// Runtime observation establishes non-support for the semantic subject. + /// + Unsupported = 1, + + /// + /// Runtime observation does not establish either support or non-support. + /// + Inconclusive = 2, +} diff --git a/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeObservationSet.cs b/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeObservationSet.cs new file mode 100644 index 000000000..6f7b766f6 --- /dev/null +++ b/Icod.TermInfo.Inspection/src/PersistentRasterRuntimeObservationSet.cs @@ -0,0 +1,112 @@ +namespace Icod.TermInfo.Inspection; + +/// +/// Represents one immutable bounded canonical snapshot of caller-owned +/// persistent-raster runtime observations. +/// +public sealed class PersistentRasterRuntimeObservationSet { + /// + /// Initializes an immutable canonical runtime-observation snapshot. + /// + /// + /// Lifecycle runtime observations to snapshot. + /// + /// + /// Placement runtime observations to snapshot. + /// + /// Optional deterministic resource bounds. + public PersistentRasterRuntimeObservationSet( + IEnumerable lifecycleObservations, + IEnumerable placementObservations, + PersistentRasterRuntimeObservationOptions? options = null + ) { + ArgumentNullException.ThrowIfNull( lifecycleObservations ); + ArgumentNullException.ThrowIfNull( placementObservations ); + + PersistentRasterRuntimeObservationOptions effectiveOptions = + options ?? new PersistentRasterRuntimeObservationOptions(); + List lifecycleItems = []; + List placementItems = []; + int count = 0; + + foreach ( + PersistentRasterRuntimeLifecycleObservation item + in lifecycleObservations + ) { + if ( item is null ) { + throw new ArgumentException( + "A lifecycle runtime-observation collection cannot contain null.", + nameof( lifecycleObservations ) + ); + } + if ( count >= effectiveOptions.MaximumObservationCount ) { + throw new ArgumentException( + $"The runtime-observation set exceeds the configured maximum of {effectiveOptions.MaximumObservationCount} observations.", + nameof( lifecycleObservations ) + ); + } + + lifecycleItems.Add( item ); + count++; + } + + foreach ( + PersistentRasterRuntimePlacementObservation item + in placementObservations + ) { + if ( item is null ) { + throw new ArgumentException( + "A placement runtime-observation collection cannot contain null.", + nameof( placementObservations ) + ); + } + if ( count >= effectiveOptions.MaximumObservationCount ) { + throw new ArgumentException( + $"The runtime-observation set exceeds the configured maximum of {effectiveOptions.MaximumObservationCount} observations.", + nameof( placementObservations ) + ); + } + + placementItems.Add( item ); + count++; + } + + PersistentRasterRuntimeLifecycleObservation[] orderedLifecycle = + lifecycleItems + .OrderBy( item => (int)item.Subject ) + .ThenBy( item => item.SourceLabel, StringComparer.Ordinal ) + .ThenBy( item => item.SourceOrdinal ) + .ThenBy( item => (int)item.Outcome ) + .ToArray(); + PersistentRasterRuntimePlacementObservation[] orderedPlacement = + placementItems + .OrderBy( item => (int)item.Subject ) + .ThenBy( item => item.SourceLabel, StringComparer.Ordinal ) + .ThenBy( item => item.SourceOrdinal ) + .ThenBy( item => (int)item.Outcome ) + .ToArray(); + + LifecycleObservations = Array.AsReadOnly( orderedLifecycle ); + PlacementObservations = Array.AsReadOnly( orderedPlacement ); + Count = count; + } + + /// Gets the canonical immutable lifecycle observation snapshot. + public IReadOnlyList + LifecycleObservations { + get; + } + + /// Gets the canonical immutable placement observation snapshot. + public IReadOnlyList + PlacementObservations { + get; + } + + /// + /// Gets the combined number of lifecycle and placement observations. + /// + public int Count { + get; + } +} diff --git a/Icod.TermInfo.Inspection/src/PersistentRasterRuntimePlacementObservation.cs b/Icod.TermInfo.Inspection/src/PersistentRasterRuntimePlacementObservation.cs new file mode 100644 index 000000000..97f907af7 --- /dev/null +++ b/Icod.TermInfo.Inspection/src/PersistentRasterRuntimePlacementObservation.cs @@ -0,0 +1,86 @@ +namespace Icod.TermInfo.Inspection; + +/// +/// Represents one immutable protocol-neutral runtime observation about an +/// advanced persistent-raster placement semantic. +/// +public sealed class PersistentRasterRuntimePlacementObservation { + /// + /// Initializes one immutable placement runtime observation. + /// + /// The advanced placement semantic being observed. + /// The caller-owned runtime observation outcome. + /// + /// A bounded caller-owned provenance label preserved exactly and compared + /// ordinally for deterministic ordering. + /// + /// + /// A non-negative deterministic source-local ordinal supplied by the runtime + /// verifier. + /// + public PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject subject, + PersistentRasterRuntimeObservationOutcome outcome, + string sourceLabel, + int sourceOrdinal + ) { + if ( !Enum.IsDefined( subject ) ) { + throw new ArgumentOutOfRangeException( + nameof( subject ), + subject, + "The placement runtime-observation subject must be a defined value." + ); + } + if ( !Enum.IsDefined( outcome ) ) { + throw new ArgumentOutOfRangeException( + nameof( outcome ), + outcome, + "The runtime-observation outcome must be a defined value." + ); + } + ArgumentException.ThrowIfNullOrWhiteSpace( sourceLabel ); + if ( + sourceLabel.Length + > PersistentRasterRuntimeObservationOptions.MaximumSourceLabelLength + ) { + throw new ArgumentException( + $"The runtime-observation source label cannot exceed {PersistentRasterRuntimeObservationOptions.MaximumSourceLabelLength} UTF-16 code units.", + nameof( sourceLabel ) + ); + } + if ( sourceOrdinal < 0 ) { + throw new ArgumentOutOfRangeException( + nameof( sourceOrdinal ), + sourceOrdinal, + "The placement runtime-observation source ordinal cannot be negative." + ); + } + + Subject = subject; + Outcome = outcome; + SourceLabel = sourceLabel; + SourceOrdinal = sourceOrdinal; + } + + /// Gets the advanced placement semantic being observed. + public PersistentRasterPlacementSubject Subject { + get; + } + + /// Gets the caller-owned runtime observation outcome. + public PersistentRasterRuntimeObservationOutcome Outcome { + get; + } + + /// Gets the exact bounded provenance label supplied by the caller. + public string SourceLabel { + get; + } + + /// + /// Gets the non-negative source-local ordinal supplied by the runtime verifier. + /// + public int SourceOrdinal { + get; + } +} diff --git a/Icod.TermInfo.Inspection/src/TermInfoJsonRenderer.PersistentRasterRuntime.cs b/Icod.TermInfo.Inspection/src/TermInfoJsonRenderer.PersistentRasterRuntime.cs new file mode 100644 index 000000000..f8cf7718c --- /dev/null +++ b/Icod.TermInfo.Inspection/src/TermInfoJsonRenderer.PersistentRasterRuntime.cs @@ -0,0 +1,510 @@ +namespace Icod.TermInfo.Inspection; + +public static partial class TermInfoJsonRenderer { + /// + /// The additive schema identifier used by 1.13 persistent-raster runtime + /// evidence automation. Versions 1 through 4 retain their frozen identifiers. + /// + public const string PersistentRasterRuntimeSchemaIdentifier = + "urn:icod:terminfo:inspection:json:5"; + + /// + /// The additive schema version used by 1.13 persistent-raster runtime evidence + /// automation. + /// + public const int PersistentRasterRuntimeSchemaVersion = 5; + + private const string PersistentRasterRuntimeObservationSetDocumentKind = + "persistentRasterRuntimeObservationSet"; + private const string PersistentRasterRuntimeIntegrationDocumentKind = + "persistentRasterRuntimeIntegration"; + + /// + /// Renders one immutable canonical persistent-raster runtime-observation set + /// using the additive version-5 automation contract. + /// + /// The immutable canonical observation snapshot. + /// The deterministic JSON document. + public static string Render( + PersistentRasterRuntimeObservationSet observations + ) => + Render( + observations, + new TermInfoJsonRendererOptions(), + CancellationToken.None + ); + + /// + /// Renders one immutable canonical persistent-raster runtime-observation set + /// using explicit deterministic JSON policy. + /// + /// The immutable canonical observation snapshot. + /// The immutable JSON rendering policy. + /// + /// A token observed at deterministic rendering boundaries. + /// + /// The deterministic JSON document. + public static string Render( + PersistentRasterRuntimeObservationSet observations, + TermInfoJsonRendererOptions options, + CancellationToken cancellationToken = default + ) { + ArgumentNullException.ThrowIfNull( observations ); + ArgumentNullException.ThrowIfNull( options ); + cancellationToken.ThrowIfCancellationRequested(); + + return RenderPersistentRasterRuntimeObservationSetV5( + observations, + options, + cancellationToken + ); + } + + /// + /// Renders one immutable persistent-raster runtime integration audit result + /// using the additive version-5 automation contract. + /// + /// The immutable runtime integration audit result. + /// The deterministic JSON document. + public static string Render( + PersistentRasterRuntimeIntegrationResult integration + ) => + Render( + integration, + new TermInfoJsonRendererOptions(), + CancellationToken.None + ); + + /// + /// Renders one immutable persistent-raster runtime integration audit result + /// using explicit deterministic JSON policy. + /// + /// The immutable runtime integration audit result. + /// The immutable JSON rendering policy. + /// + /// A token observed at deterministic rendering boundaries. + /// + /// The deterministic JSON document. + public static string Render( + PersistentRasterRuntimeIntegrationResult integration, + TermInfoJsonRendererOptions options, + CancellationToken cancellationToken = default + ) { + ArgumentNullException.ThrowIfNull( integration ); + ArgumentNullException.ThrowIfNull( options ); + cancellationToken.ThrowIfCancellationRequested(); + + return RenderPersistentRasterRuntimeIntegrationV5( + integration, + options, + cancellationToken + ); + } + + private static string RenderPersistentRasterRuntimeObservationSetV5( + PersistentRasterRuntimeObservationSet observations, + TermInfoJsonRendererOptions options, + CancellationToken cancellationToken + ) { + ArgumentNullException.ThrowIfNull( observations ); + ArgumentNullException.ThrowIfNull( options ); + cancellationToken.ThrowIfCancellationRequested(); + + BoundedJsonOutput output = new( options.MaximumOutputByteCount ); + DeterministicJsonWriter writer = new( output, options.WriteIndented ); + try { + writer.WriteStartObject(); + WritePersistentRasterRuntimeEnvelopePrefix( + writer, + PersistentRasterRuntimeObservationSetDocumentKind + ); + writer.WriteStartObject( "data" ); + writer.WriteNumber( "observationCount", observations.Count ); + writer.WriteNumber( + "lifecycleObservationCount", + observations.LifecycleObservations.Count + ); + writer.WriteNumber( + "placementObservationCount", + observations.PlacementObservations.Count + ); + WritePersistentRasterRuntimeLifecycleObservations( + writer, + "lifecycleObservations", + observations.LifecycleObservations, + cancellationToken + ); + WritePersistentRasterRuntimePlacementObservations( + writer, + "placementObservations", + observations.PlacementObservations, + cancellationToken + ); + writer.WriteEndObject(); + writer.WriteEndObject(); + cancellationToken.ThrowIfCancellationRequested(); + } catch ( JsonOutputLimitExceededException exception ) { + throw CreateOutputLimitException( + options, + exception + ); + } + + return output.GetString(); + } + + private static string RenderPersistentRasterRuntimeIntegrationV5( + PersistentRasterRuntimeIntegrationResult integration, + TermInfoJsonRendererOptions options, + CancellationToken cancellationToken + ) { + ArgumentNullException.ThrowIfNull( integration ); + ArgumentNullException.ThrowIfNull( options ); + cancellationToken.ThrowIfCancellationRequested(); + + BoundedJsonOutput output = new( options.MaximumOutputByteCount ); + DeterministicJsonWriter writer = new( output, options.WriteIndented ); + try { + writer.WriteStartObject(); + WritePersistentRasterRuntimeEnvelopePrefix( + writer, + PersistentRasterRuntimeIntegrationDocumentKind + ); + writer.WriteStartObject( "data" ); + writer.WriteBoolean( "succeeded", integration.Succeeded ); + writer.WriteNumber( + "observationCount", + integration.Observations.Count + ); + writer.WriteStartObject( "observations" ); + WritePersistentRasterRuntimeLifecycleObservations( + writer, + "lifecycle", + integration.Observations.LifecycleObservations, + cancellationToken + ); + WritePersistentRasterRuntimePlacementObservations( + writer, + "placement", + integration.Observations.PlacementObservations, + cancellationToken + ); + writer.WriteEndObject(); + WritePersistentRasterRuntimeLifecycleEvidence( + writer, + "importedLifecycleEvidence", + integration.ImportedLifecycleEvidence, + cancellationToken + ); + WritePersistentRasterRuntimePlacementEvidence( + writer, + "importedPlacementEvidence", + integration.ImportedPlacementEvidence, + cancellationToken + ); + WritePersistentRasterRuntimeLifecycleObservations( + writer, + "inconclusiveLifecycleObservations", + integration.InconclusiveLifecycleObservations, + cancellationToken + ); + WritePersistentRasterRuntimePlacementObservations( + writer, + "inconclusivePlacementObservations", + integration.InconclusivePlacementObservations, + cancellationToken + ); + WritePersistentRasterRuntimeIntegrationIssues( + writer, + integration.Issues, + cancellationToken + ); + WritePersistentRasterRuntimeLifecycleStates( + writer, + integration.LifecycleProfile + ); + WritePersistentRasterRuntimePlacementStates( + writer, + integration.PlacementProfile + ); + writer.WriteEndObject(); + writer.WriteEndObject(); + cancellationToken.ThrowIfCancellationRequested(); + } catch ( JsonOutputLimitExceededException exception ) { + throw CreateOutputLimitException( + options, + exception + ); + } + + return output.GetString(); + } + + private static void WritePersistentRasterRuntimeEnvelopePrefix( + DeterministicJsonWriter writer, + string documentKind + ) { + ArgumentNullException.ThrowIfNull( writer ); + ArgumentException.ThrowIfNullOrWhiteSpace( documentKind ); + + writer.WriteString( + "schema", + PersistentRasterRuntimeSchemaIdentifier + ); + writer.WriteNumber( + "schemaVersion", + PersistentRasterRuntimeSchemaVersion + ); + writer.WriteString( "documentKind", documentKind ); + } + + private static void WritePersistentRasterRuntimeLifecycleObservations( + DeterministicJsonWriter writer, + string propertyName, + IReadOnlyList observations, + CancellationToken cancellationToken + ) { + ArgumentNullException.ThrowIfNull( writer ); + ArgumentException.ThrowIfNullOrWhiteSpace( propertyName ); + ArgumentNullException.ThrowIfNull( observations ); + + writer.WriteStartArray( propertyName ); + foreach ( PersistentRasterRuntimeLifecycleObservation observation in observations ) { + cancellationToken.ThrowIfCancellationRequested(); + writer.WriteStartObjectValue(); + writer.WriteString( + "subject", + GetPersistentRasterLifecycleEvidenceSubjectName( + observation.Subject + ) + ); + writer.WriteString( + "outcome", + GetPersistentRasterRuntimeObservationOutcomeName( + observation.Outcome + ) + ); + writer.WriteString( "sourceLabel", observation.SourceLabel ); + writer.WriteNumber( "sourceOrdinal", observation.SourceOrdinal ); + writer.WriteEndObject(); + } + writer.WriteEndArray(); + } + + private static void WritePersistentRasterRuntimePlacementObservations( + DeterministicJsonWriter writer, + string propertyName, + IReadOnlyList observations, + CancellationToken cancellationToken + ) { + ArgumentNullException.ThrowIfNull( writer ); + ArgumentException.ThrowIfNullOrWhiteSpace( propertyName ); + ArgumentNullException.ThrowIfNull( observations ); + + writer.WriteStartArray( propertyName ); + foreach ( PersistentRasterRuntimePlacementObservation observation in observations ) { + cancellationToken.ThrowIfCancellationRequested(); + writer.WriteStartObjectValue(); + writer.WriteString( + "subject", + GetPersistentRasterPlacementSubjectName( + observation.Subject + ) + ); + writer.WriteString( + "outcome", + GetPersistentRasterRuntimeObservationOutcomeName( + observation.Outcome + ) + ); + writer.WriteString( "sourceLabel", observation.SourceLabel ); + writer.WriteNumber( "sourceOrdinal", observation.SourceOrdinal ); + writer.WriteEndObject(); + } + writer.WriteEndArray(); + } + + private static void WritePersistentRasterRuntimeLifecycleEvidence( + DeterministicJsonWriter writer, + string propertyName, + IReadOnlyList evidence, + CancellationToken cancellationToken + ) { + ArgumentNullException.ThrowIfNull( writer ); + ArgumentException.ThrowIfNullOrWhiteSpace( propertyName ); + ArgumentNullException.ThrowIfNull( evidence ); + + writer.WriteStartArray( propertyName ); + foreach ( PersistentRasterLifecycleEvidence item in evidence ) { + cancellationToken.ThrowIfCancellationRequested(); + writer.WriteStartObjectValue(); + writer.WriteString( + "subject", + GetPersistentRasterLifecycleEvidenceSubjectName( item.Subject ) + ); + writer.WriteBoolean( "isPositive", item.IsPositive ); + writer.WriteString( + "kind", + GetPersistentRasterLifecycleEvidenceKindName( item.Kind ) + ); + writer.WriteString( "sourceLabel", item.SourceLabel ); + writer.WriteNumber( "sourceOrdinal", item.SourceOrdinal ); + writer.WriteEndObject(); + } + writer.WriteEndArray(); + } + + private static void WritePersistentRasterRuntimePlacementEvidence( + DeterministicJsonWriter writer, + string propertyName, + IReadOnlyList evidence, + CancellationToken cancellationToken + ) { + ArgumentNullException.ThrowIfNull( writer ); + ArgumentException.ThrowIfNullOrWhiteSpace( propertyName ); + ArgumentNullException.ThrowIfNull( evidence ); + + writer.WriteStartArray( propertyName ); + foreach ( PersistentRasterPlacementEvidence item in evidence ) { + cancellationToken.ThrowIfCancellationRequested(); + writer.WriteStartObjectValue(); + writer.WriteString( + "subject", + GetPersistentRasterPlacementSubjectName( item.Subject ) + ); + writer.WriteBoolean( "isPositive", item.IsPositive ); + writer.WriteString( + "kind", + GetPersistentRasterPlacementEvidenceKindName( item.Kind ) + ); + writer.WriteString( "sourceLabel", item.SourceLabel ); + writer.WriteNumber( "sourceOrdinal", item.SourceOrdinal ); + writer.WriteEndObject(); + } + writer.WriteEndArray(); + } + + private static void WritePersistentRasterRuntimeIntegrationIssues( + DeterministicJsonWriter writer, + IReadOnlyList issues, + CancellationToken cancellationToken + ) { + ArgumentNullException.ThrowIfNull( writer ); + ArgumentNullException.ThrowIfNull( issues ); + + writer.WriteStartArray( "issues" ); + foreach ( PersistentRasterRuntimeIntegrationIssue issue in issues ) { + cancellationToken.ThrowIfCancellationRequested(); + writer.WriteStartObjectValue(); + writer.WriteString( + "kind", + GetPersistentRasterRuntimeIntegrationIssueKindName( issue.Kind ) + ); + writer.WriteNumber( + "existingEvidenceCount", + issue.ExistingEvidenceCount + ); + writer.WriteNumber( + "requestedImportCount", + issue.RequestedImportCount + ); + writer.WriteEndObject(); + } + writer.WriteEndArray(); + } + + private static void WritePersistentRasterRuntimeLifecycleStates( + DeterministicJsonWriter writer, + PersistentRasterLifecycleProfile profile + ) { + ArgumentNullException.ThrowIfNull( writer ); + ArgumentNullException.ThrowIfNull( profile ); + + writer.WriteStartObject( "lifecycleStates" ); + writer.WriteString( + "rasterDisplay", + GetPersistentRasterLifecycleSupportStatusName( profile.RasterDisplay ) + ); + writer.WriteString( + "persistentUpload", + GetPersistentRasterLifecycleSupportStatusName( profile.PersistentUpload ) + ); + writer.WriteString( + "acknowledgedUpload", + GetPersistentRasterLifecycleSupportStatusName( profile.AcknowledgedUpload ) + ); + writer.WriteString( + "placementCreation", + GetPersistentRasterLifecycleSupportStatusName( profile.PlacementCreation ) + ); + writer.WriteString( + "multiplePlacements", + GetPersistentRasterLifecycleSupportStatusName( profile.MultiplePlacements ) + ); + writer.WriteString( + "placementUpdate", + GetPersistentRasterLifecycleSupportStatusName( profile.PlacementUpdate ) + ); + writer.WriteString( + "placementDeletion", + GetPersistentRasterLifecycleSupportStatusName( profile.PlacementDeletion ) + ); + writer.WriteString( + "resourceDeletion", + GetPersistentRasterLifecycleSupportStatusName( profile.ResourceDeletion ) + ); + writer.WriteEndObject(); + } + + private static void WritePersistentRasterRuntimePlacementStates( + DeterministicJsonWriter writer, + PersistentRasterPlacementProfile profile + ) { + ArgumentNullException.ThrowIfNull( writer ); + ArgumentNullException.ThrowIfNull( profile ); + + writer.WriteStartObject( "placementStates" ); + writer.WriteString( + "sourceRectangle", + GetPersistentRasterLifecycleSupportStatusName( profile.SourceRectangle ) + ); + writer.WriteString( + "signedZOrder", + GetPersistentRasterLifecycleSupportStatusName( profile.SignedZOrder ) + ); + writer.WriteEndObject(); + } + + private static string GetPersistentRasterRuntimeObservationOutcomeName( + PersistentRasterRuntimeObservationOutcome outcome + ) => + outcome switch { + PersistentRasterRuntimeObservationOutcome.Supported => "supported", + PersistentRasterRuntimeObservationOutcome.Unsupported => "unsupported", + PersistentRasterRuntimeObservationOutcome.Inconclusive => "inconclusive", + _ => throw new ArgumentOutOfRangeException( + nameof( outcome ), + outcome, + "The runtime-observation outcome must be a defined value." + ), + }; + + private static string GetPersistentRasterRuntimeIntegrationIssueKindName( + PersistentRasterRuntimeIntegrationIssueKind kind + ) => + kind switch { + PersistentRasterRuntimeIntegrationIssueKind.LifecycleEvidenceCapacityExhausted => + "lifecycleEvidenceCapacityExhausted", + PersistentRasterRuntimeIntegrationIssueKind.PlacementEvidenceCapacityExhausted => + "placementEvidenceCapacityExhausted", + PersistentRasterRuntimeIntegrationIssueKind.LifecycleOrdinalSpaceExhausted => + "lifecycleOrdinalSpaceExhausted", + PersistentRasterRuntimeIntegrationIssueKind.PlacementOrdinalSpaceExhausted => + "placementOrdinalSpaceExhausted", + _ => throw new ArgumentOutOfRangeException( + nameof( kind ), + kind, + "The runtime integration issue kind must be a defined value." + ), + }; +} diff --git a/README.md b/README.md index a79e1c271..8bc568f25 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,45 @@ `Icod.TermInfo` is a managed, dependency-free .NET implementation of the low-level terminal-capability model traditionally supplied by `libtinfo`. -Version `1.12.0` is the current stable coordinated release. It adds -protocol-neutral advanced persistent-raster placement evidence, classification, -lifecycle-aware planning, description/database-set composition, and additive -version-4 profile/plan JSON through `Icod.TermInfo.Inspection` while preserving -the frozen 1.11 lifecycle surface and version-1/version-2/version-3 JSON -contracts. +Version `1.13.0` is the validated coordinated stable release candidate. It +promotes the persistent-raster runtime-evidence interchange surface without +changing feature semantics, public API, schemas, dependencies, target frameworks, +command behavior, package-consumer topology, or archive RIDs. Stable `1.12.0` +remains the published release until PR #43 is merged and the normal tag-based +publication flow is performed. + +## 1.13 release-ready status + +Version `1.13.0` promotes the fully validated `1.13.0-Alpha-8` contract +without feature, public API, schema, dependency, target-framework, +command-semantic, package-consumer-topology, or archive-RID changes. The additive +`Icod.TermInfo.Inspection` runtime-evidence interchange layer provides bounded +immutable `PersistentRasterRuntime*` observations, deterministic atomic-per-family +conversion of conclusive observations into existing `Verified` evidence, +audit-visible inconclusive observations and integration issues, planner-delegating +replanning conveniences, and additive version-5 JSON for runtime observation sets +and integration results. + +The complete 1.13 Inspection surface contains 90 exported public types with +normalized-LF SHA-256 +`fd827a25abafb8e9ff3915567f45f9f2ec51b332bfd8a82dd4e4bca20290e764`. +JSON versions 1 through 4 remain unchanged; version 5 contains exactly +`persistentRasterRuntimeObservationSet` and +`persistentRasterRuntimeIntegration`. Production `Icod.TermInfo.Inspection` +still has no `Icod.Terminal` dependency. Downstream qualification remains pinned +to published `Icod.Terminal 1.12.0`. + +The Alpha-8 contract was accepted on exact product head +`f236c33d8239e80379bf8cf0f1123abd6c93c3cb` by qualification run `34797445315`. +Stable product head `6e9217b16c3023fb10fa34dbaa74afe48448d858` then passed qualification run +`34799272472` with all 12 jobs green, including Windows whole-surface/historical +Inspection compatibility, package verification, three installed-tool smokes, and +all six archive RIDs. PR #43 remains unmerged; tag and package publication remain +gated by the normal release workflow. The install commands below target +`1.13.0`. See +`docs/1.13.0-PERSISTENT-RASTER-RUNTIME-EVIDENCE-GUIDE.md`, +`docs/1.13.0-INSPECTION-PUBLIC-API-FREEZE.md`, and +`docs/1.13.0-RELEASE-AUDIT.md`. The package family targets `net8.0`, `net9.0`, and `net10.0`; packages use C# 13, contain no native ncurses/terminfo payload, and are intended to run on Windows, @@ -106,35 +139,35 @@ The final post-documentation Staging gate is green (`33736812176`, head Runtime-only consumers use: ```text -dotnet add package Icod.TermInfo --version 1.12.0 +dotnet add package Icod.TermInfo --version 1.13.0 ``` Applications which need terminfo source-language support use: ```text -dotnet add package Icod.TermInfo.Source --version 1.12.0 +dotnet add package Icod.TermInfo.Source --version 1.13.0 ``` Applications which need opt-in termcap parsing, conversion, rendering, or explicit historical termcap acquisition use: ```text -dotnet add package Icod.TermInfo.Termcap --version 1.12.0 +dotnet add package Icod.TermInfo.Termcap --version 1.13.0 ``` Applications which compile terminfo source or write conventional compiled terminfo databases use: ```text -dotnet add package Icod.TermInfo.Compiler --version 1.12.0 +dotnet add package Icod.TermInfo.Compiler --version 1.13.0 ``` Applications which need canonical rendering, semantic comparison, provider-aware -inspection, database-set automation, or persistent-raster lifecycle/placement -planning use: +inspection, database-set automation, persistent-raster lifecycle/placement +planning, or 1.13 runtime-evidence interchange and integration use: ```text -dotnet add package Icod.TermInfo.Inspection --version 1.12.0 +dotnet add package Icod.TermInfo.Inspection --version 1.13.0 ``` `Icod.TermInfo.Source` and `Icod.TermInfo.Termcap` each depend on the matching @@ -146,8 +179,8 @@ Inspection does not depend on Compiler, Termcap, `Icod.Terminal`, or The same validated package artifacts are published to NuGet.org and GitHub Packages. Historical release contracts remain recorded in the versioned release -audits; the current stable publication contract is recorded in -`docs/1.12.0-RELEASE-AUDIT.md`. +audits; the 1.13 promotion and release contract is recorded in +`docs/1.13.0-RELEASE-AUDIT.md`. ## Tool Suite @@ -174,7 +207,7 @@ distribution-only router package. Install the coordinated router as a .NET tool with: ```text -dotnet tool install --global Icod.TermInfo.Tools --version 1.12.0 +dotnet tool install --global Icod.TermInfo.Tools --version 1.13.0 icod-terminfo tic -V icod-terminfo infocmp -V @@ -199,7 +232,7 @@ Icod.TermInfo.Tools..osx-x64.tar.gz Icod.TermInfo.Tools..osx-arm64.tar.gz ``` -Each 1.12.0 archive contains the traditional `tic`, `infocmp`, `toe`, +Each 1.13.0 archive contains the traditional `tic`, `infocmp`, `toe`, `captoinfo`, and `infotocap` command names and their required managed dependencies. The user supplies the .NET 10 runtime and controls where the archive is unpacked and whether that location is placed on `PATH`. The archive @@ -219,7 +252,7 @@ remains unsigned. The frozen 1.0 and 1.1 releases support `net8.0` and target-framework policy are documented in `docs/VERSIONING.md` and `docs/COMPATIBILITY.md`. -The runtime 1.0 public API remains frozen. Version 1.1 adds source-language functionality in the separate `Icod.TermInfo.Source` package rather than making the runtime package depend on parser/front-end code. The 1.2 line adds deterministic compiled-entry writing in the separate `Icod.TermInfo.Compiler` package. The 1.3 line adds canonical rendering and semantic comparison in the separate `Icod.TermInfo.Inspection` package. The 1.4 line composes those libraries into the separate `tic`, `infocmp`, and `toe` command layer without moving command policy into the reusable packages. Live terminal sessions, input decoding, and active probing belong to the sibling `Icod.Terminal` layer; curses-style screen/window behavior belongs to `Icod.DCurses`. Version 1.11 adds protocol-neutral persistent-raster lifecycle evidence and planning to Inspection while preserving that live-session ownership boundary. Version 1.12 adds protocol-neutral source-rectangle and signed-z-order placement semantics and planning while keeping concrete execution values and live protocol work downstream. PTYs, terminal emulation, and graphics protocol execution remain separate later or sibling work. +The runtime 1.0 public API remains frozen. Version 1.1 adds source-language functionality in the separate `Icod.TermInfo.Source` package rather than making the runtime package depend on parser/front-end code. The 1.2 line adds deterministic compiled-entry writing in the separate `Icod.TermInfo.Compiler` package. The 1.3 line adds canonical rendering and semantic comparison in the separate `Icod.TermInfo.Inspection` package. The 1.4 line composes those libraries into the separate `tic`, `infocmp`, and `toe` command layer without moving command policy into the reusable packages. Live terminal sessions, input decoding, and active probing belong to the sibling `Icod.Terminal` layer; curses-style screen/window behavior belongs to `Icod.DCurses`. Version 1.11 adds protocol-neutral persistent-raster lifecycle evidence and planning to Inspection while preserving that live-session ownership boundary. Version 1.12 adds protocol-neutral source-rectangle and signed-z-order placement semantics and planning while keeping concrete execution values and live protocol work downstream. Version 1.13 adds protocol-neutral caller-owned runtime observations, deterministic integration into existing verified evidence, and runtime-evidence audit JSON while keeping live verification downstream. PTYs, terminal emulation, and graphics protocol execution remain separate later or sibling work. ## What 1.0 provides @@ -546,6 +579,42 @@ See `docs/1.9.0-MI07-API-SCHEMA-PACKAGING-AND-RELEASE-CLOSURE.md`, and `docs/1.9.0-RELEASE-AUDIT.md` for the 1.9 machine-readable contract. +## What 1.13 adds + +Version 1.13.0 adds protocol-neutral runtime-evidence interchange above the +frozen 1.11 lifecycle and 1.12 placement models without moving live terminal I/O +into TermInfo: + +- immutable bounded `PersistentRasterRuntimeLifecycleObservation` and + `PersistentRasterRuntimePlacementObservation` values plus canonical observation + sets; +- deterministic mapping of conclusive runtime outcomes into the existing + `Verified` evidence model with safe final source ordinals; +- atomic-per-family handling of evidence-capacity and ordinal-space exhaustion; +- audit-visible `Inconclusive` observations and structured integration issues; +- `CreateLifecyclePlan(...)` and `CreatePlacementPlan(...)` conveniences which + delegate directly to the existing frozen planners; +- additive JSON version 5 documents for + `persistentRasterRuntimeObservationSet` and + `persistentRasterRuntimeIntegration`; and +- package-only qualification against published `Icod.Terminal 1.12.0` while the + production `Icod.TermInfo.Inspection` package remains free of any + `Icod.Terminal` dependency. + +The intended consumer flow is: + +```text +static TermInfo evidence -> classify / plan -> runtime verification required + -> caller-owned verifier -> runtime observations + -> PersistentRasterRuntimeEvidenceIntegrator -> frozen classifiers / planners +``` + +TermInfo does not infer protocol/backend identity, perform live probing, own +terminal resource identities, or expand a sibling layer's coarse capability into +TermInfo subjects. Those adapter decisions remain explicit consumer policy. See +`samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/README.md` and +`docs/1.13.0-PERSISTENT-RASTER-RUNTIME-EVIDENCE-GUIDE.md`. + ## Getting started Terminal resolution remains explicit and conservative. A normal application can @@ -983,7 +1052,7 @@ The first provider which resolves the requested name wins. ## Sample applications -The repository contains six executable API samples plus one command-suite +The repository contains seven executable API samples plus one command-suite walkthrough with deliberately different purposes. ### General terminal API sample @@ -1004,7 +1073,7 @@ walkthrough with deliberately different purposes. - redirection handling and explicit Windows VT enablement; - a custom provider implementation. -All six executable API sample projects target `net8.0`, `net9.0`, and +All seven executable API sample projects target `net8.0`, `net9.0`, and `net10.0`; `dotnet run` therefore needs an explicit framework. Run the ordinary demonstration with: @@ -1102,12 +1171,14 @@ The permanent release verifier checks the sample's normalized JSON fixtures on ### Persistent-raster lifecycle sample `samples/Icod.TermInfo.PersistentRasterLifecycle.Sample` is the focused 1.11 -reusable-API example. It starts from ordinary Sixel evidence, demonstrates that -persistent upload and placement remain `Unknown`, plans an indeterminate request, -then appends caller-owned `Verified` evidence, reclassifies, and obtains a -successful protocol-neutral upload/placement plan. It also renders the version-3 -profile and plan JSON documents. The sample performs no terminal I/O and has no -`Icod.Terminal` dependency. +lifecycle example updated for the 1.13 integration path. It starts from ordinary +Sixel evidence, demonstrates that persistent upload and placement remain +`Unknown`, plans an indeterminate request, then represents consumer-owned runtime +results as immutable lifecycle observations. `PersistentRasterRuntimeEvidenceIntegrator` +maps the conclusive observations into existing `Verified` evidence with safe final +ordinals, and `CreateLifecyclePlan(...)` delegates replanning to the frozen +lifecycle planner. The sample performs no terminal I/O and has no `Icod.Terminal` +dependency. Run it with: @@ -1127,11 +1198,12 @@ but no advanced-placement evidence, so both `SourceRectangle` and `SignedZOrder` remain `Unknown` and the placement planner returns `RequiresRuntimeVerification`. -The consumer then contributes its own `Verified` evidence, reclassifies the -placement profile, and obtains a `Satisfied` plan. The sample renders both the -version-4 placement profile and placement plan before constructing any concrete -Terminal execution values. Only after semantic planning succeeds does it create -a `TerminalRasterSourceRectangle` and signed `ZIndex`. +The consumer then contributes immutable placement runtime observations for +`SourceRectangle` and `SignedZOrder`. `PersistentRasterRuntimeEvidenceIntegrator` +maps those conclusive observations into the frozen placement evidence model, and +`CreatePlacementPlan(...)` delegates replanning to produce `Satisfied`. Only after +TermInfo has finished semantic planning does the sample create a +`TerminalRasterSourceRectangle` and signed `ZIndex`. Run it with: @@ -1143,6 +1215,40 @@ Release verification executes the sample on `net8.0`, `net9.0`, and `net10.0`. See `samples/Icod.TermInfo.PersistentRasterPlacement.Sample/README.md` and `docs/1.12.0-ADVANCED-PERSISTENT-RASTER-PLACEMENT-GUIDE.md`. +### Persistent-raster runtime-integration sample + +`samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample` is the focused +1.13 caller-adapter example. It begins with a static persistent-raster lifecycle +plan that requires runtime verification, then optionally asks published +`Icod.Terminal 1.12.0` to verify its coarse `PersistentRasterGraphics` semantic +capability. Consumer code maps that sibling-layer result into TermInfo's +protocol-neutral `Supported` / `Unsupported` / `Inconclusive` runtime outcomes, +expands the coarse capability into the explicitly chosen lifecycle subjects, and +passes the resulting observations to `PersistentRasterRuntimeEvidenceIntegrator`. + +The default mode is deterministic and performs no terminal I/O; `--live` performs +the actual `VerifyCapabilityAsync(...)` call on an interactive terminal. Static +advertisement is never promoted to runtime support: non-live evidence and +`Unknown` / `Advertised` support map to `Inconclusive`. The sample then renders +the version-5 integration audit and replans through `CreateLifecyclePlan(...)`. + +Run the deterministic form with: + +```text +dotnet run --project samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample.csproj -f net10.0 +``` + +For interactive verification: + +```text +dotnet run --project samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample.csproj -f net10.0 -- --live +``` + +Release verification executes the deterministic form on `net8.0`, `net9.0`, and +`net10.0`. See +`samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/README.md` and +`docs/1.13.0-PERSISTENT-RASTER-RUNTIME-EVIDENCE-GUIDE.md`. + ### Managed tool-suite walkthrough `samples/ToolSuite` is a data-and-command walkthrough for `tic`, `infocmp`, `toe`, @@ -1162,7 +1268,8 @@ See `samples/README.md`, `samples/ToolSuite/README.md`, `samples/Icod.TermInfo.Acquisition.Sample/README.md`, `samples/Icod.TermInfo.Toolchain.Sample/README.md`, `samples/Icod.TermInfo.PersistentRasterLifecycle.Sample/README.md`, -`samples/Icod.TermInfo.PersistentRasterPlacement.Sample/README.md`, and +`samples/Icod.TermInfo.PersistentRasterPlacement.Sample/README.md`, +`samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/README.md`, and `docs/0.9.0-ACQUISITION-GUIDE.md` for the complete examples. ## Project-family boundary @@ -1174,7 +1281,7 @@ The intended family boundary is now explicit: - **`Icod.TermInfo`** — descriptions, compiled-database acquisition, capability semantics, parameter expansion, and output transformation; - **`Icod.TermInfo.Source`** — `.ti` lexical analysis, source diagnostics, unresolved entries, cancellation, `use=` inheritance, and materialization into `TerminalDescription`; - **`Icod.TermInfo.Compiler`** — deterministic compiled-entry writing, source compilation, and explicit conventional database-layout publication; -- **`Icod.TermInfo.Inspection`** — canonical effective/source rendering, relative-source synthesis and parent planning, structured semantic comparison, provider/database-set inspection, persistent-raster lifecycle and advanced-placement evidence/classification/planning, and version-3/version-4 machine-readable views; +- **`Icod.TermInfo.Inspection`** — canonical effective/source rendering, relative-source synthesis and parent planning, structured semantic comparison, provider/database-set inspection, persistent-raster lifecycle and advanced-placement evidence/classification/planning, protocol-neutral runtime-evidence interchange/integration, and versioned machine-readable views through JSON version 5; - **`Icod.TermInfo.Termcap`** — bounded termcap parsing, classification, `tc=` resolution, Runtime conversion, reverse rendering, and explicit termcap acquisition; - **`tic`, `infocmp`, `toe`, `captoinfo`, and `infotocap`** — managed command applications which compose the reusable libraries and own command-line policy; - **`Icod.TermInfo.Tools` / `icod-terminfo`** — distribution-only .NET tool router which dispatches to the five command applications; diff --git a/docs/1.13.0-INSPECTION-PUBLIC-API-FREEZE.md b/docs/1.13.0-INSPECTION-PUBLIC-API-FREEZE.md new file mode 100644 index 000000000..9b5c58319 --- /dev/null +++ b/docs/1.13.0-INSPECTION-PUBLIC-API-FREEZE.md @@ -0,0 +1,43 @@ +# Icod.TermInfo 1.13.0 Inspection Public API Freeze + +## Exact surface identity + +The complete `Icod.TermInfo.Inspection` 1.13 public reflection manifest is frozen by normalized-LF SHA-256: + +```text +fd827a25abafb8e9ff3915567f45f9f2ec51b332bfd8a82dd4e4bca20290e764 +``` + +The manifest uses `Icod.TermInfo.PublicApiSnapshot/v1`, retains assembly version `1.0.0.0`, and contains exactly **90 exported public types**. The fingerprint was generated from the exact `1.13.0-Alpha-7` Staging package/API artifact produced by head `0167ec187442a7b4319523a9f431367c66616a41` in workflow #819 / run `34794952192` before RE08 changed release-facing metadata. + +RE08 release verification generates the complete manifest from every candidate assembly and rejects a normalized fingerprint other than the value above. + +## Compatibility decomposition + +The 1.13 freeze is additive above the frozen 1.12 surface: + +1. the complete 1.12 Inspection manifest remains frozen by normalized-LF SHA-256 `f71501dcd27a530051c1a02083325144ced2b6173b6b967b9571c620815198f0`; +2. the exact nine reviewed 1.13 `PersistentRasterRuntime*` public type blocks are listed in `docs/1.13.0-RE01-INSPECTION-PUBLIC-API-ADDITIONS.txt`; +3. the exact six additive version-5 `TermInfoJsonRenderer` member lines are listed in `docs/1.13.0-RE06-INSPECTION-PUBLIC-API-ADDITIVE-MEMBERS.txt`; and +4. the complete resulting 1.13 manifest is independently locked by the SHA-256 above. + +Release verification therefore proves the complete 1.13 surface first. It then removes exactly the reviewed RE06 renderer members and cumulative 1.13 runtime-evidence type blocks and requires the result to reproduce the frozen 1.12 SHA-256. The established 1.12 → 1.11 → 1.10 reconstruction chain then runs unchanged. + +An accidental change to a historical member or to an approved 1.13 addition fails the release gate. + +## Cross-target equality + +The package targets `net8.0`, `net9.0`, and `net10.0`. Package verification requires equivalent public API manifests on all three TFMs; no target framework owns a divergent 1.13 surface. + +## Dependency boundary + +Runtime, Source, Compiler, and Termcap retain their previously frozen public contracts. `Icod.TermInfo.Inspection` also retains its production dependency boundary: it does not depend on `Icod.Terminal`. RE07 qualifies caller-owned adaptation against the published `Icod.Terminal 1.12.0` package only in package-consumer and sample code. + +## Machine-readable contract + +The public API freeze is independent from the JSON schema freeze. Exact normalized-LF fingerprints for versions 1 through 5 are recorded in `docs/1.13.0-RE08-FREEZE-FINGERPRINTS.txt`. Versions 1 through 4 remain immutable historical contracts. Version 5 contains exactly the two 1.13 runtime-evidence document kinds: + +```text +persistentRasterRuntimeObservationSet +persistentRasterRuntimeIntegration +``` diff --git a/docs/1.13.0-PERSISTENT-RASTER-RUNTIME-EVIDENCE-GUIDE.md b/docs/1.13.0-PERSISTENT-RASTER-RUNTIME-EVIDENCE-GUIDE.md new file mode 100644 index 000000000..3aa70d913 --- /dev/null +++ b/docs/1.13.0-PERSISTENT-RASTER-RUNTIME-EVIDENCE-GUIDE.md @@ -0,0 +1,64 @@ +# Icod.TermInfo 1.13.0 Persistent-Raster Runtime Evidence Guide + +Version 1.13 extends `Icod.TermInfo.Inspection` with a protocol-neutral boundary for caller-owned runtime observations. TermInfo still does not probe a terminal, choose a graphics backend, transmit protocol commands, or own terminal resource/placement identities. + +## Static evidence versus runtime observations + +The 1.11/1.12 evidence models remain authoritative for classified lifecycle and placement support. Static inspection produces ordinary `PersistentRasterLifecycleEvidence` and `PersistentRasterPlacementEvidence`. A caller which performs live verification outside TermInfo represents the result with `PersistentRasterRuntimeLifecycleObservation` or `PersistentRasterRuntimePlacementObservation` and places those values in a bounded `PersistentRasterRuntimeObservationSet`. + +Runtime outcomes are deliberately small: + +- `Supported` — the caller obtained conclusive positive runtime evidence; +- `Unsupported` — the caller obtained conclusive negative runtime evidence; +- `Inconclusive` — the runtime operation did not establish either conclusion. + +Each observation retains a semantic subject, exact caller-owned source label, and source-local ordinal. Labels are bounded to 256 UTF-16 code units. One observation set defaults to 256 combined lifecycle/placement entries and may be configured up to 4096. + +## Canonical immutable snapshots + +`PersistentRasterRuntimeObservationSet` snapshots each input sequence exactly once, retains compatible duplicates, contradictions, and inconclusive observations, and orders each family deterministically by subject numeric value, ordinal source label, source-local ordinal, and outcome numeric value. Input enumeration order and current culture do not change the snapshot. + +## Integrating runtime evidence + +Call `PersistentRasterRuntimeEvidenceIntegrator.Integrate(...)` with an existing lifecycle profile, placement profile, and observation set. Conclusive observations are converted to the existing `Verified` evidence kind; inconclusive observations remain visible in the audit result and are not silently promoted. + +Final classifier evidence ordinals are assigned by the integrator. Callers do not calculate or append final `Verified` evidence manually. Existing evidence ordinals are never renumbered. + +Integration is atomic per family. Before importing conclusive observations, the integrator verifies the frozen evidence-count bound and available consecutive `int` ordinal space. If one family cannot be imported safely, that family keeps its original profile and reports a structured `PersistentRasterRuntimeIntegrationIssue`; the other family may still integrate successfully. + +Represented issue kinds are lifecycle/placement evidence-capacity exhaustion and lifecycle/placement ordinal-space exhaustion. These issues describe bounded integration limitations, not terminal protocol failures. + +## Classification and replanning + +The integrator does not implement its own support precedence. It delegates to the frozen lifecycle and placement classifiers, preserving `Verified > Declared > CapabilityDerived` precedence and same-precedence contradiction behavior. + +`PersistentRasterRuntimeIntegrationResult.CreateLifecyclePlan(...)` and `CreatePlacementPlan(...)` are convenience composition methods. They delegate to the frozen planners over the strengthened profiles; no new combined planning status or negotiation policy is introduced. + +## JSON version 5 + +`TermInfoJsonRenderer` version 5 uses schema identifier: + +```text +urn:icod:terminfo:inspection:json:5 +``` + +It renders exactly two document kinds: + +```text +persistentRasterRuntimeObservationSet +persistentRasterRuntimeIntegration +``` + +The integration document is an audit: canonical observations, imported evidence, retained inconclusive observations, structured issues, and resulting subject states. It does not nest complete historical profile documents. Rendering uses the existing deterministic bounded JSON infrastructure, including exact UTF-8 limits and cancellation. + +## Icod.Terminal adapter boundary + +Production TermInfo has no `Icod.Terminal` dependency. RE07 qualifies a package-only consumer using freshly packed `Icod.TermInfo.Inspection` beside published `Icod.Terminal 1.12.0` on net8.0, net9.0, and net10.0. + +Terminal 1.12 exposes the coarse semantic capability `PersistentRasterGraphics`. Any expansion of that coarse result into individual lifecycle subjects is explicit caller policy. The caller maps `TerminalCapabilityStatus` to runtime observations and then passes only TermInfo-owned semantic values into the integrator. `TerminalSession`, endpoint availability, backend identity, and raw protocol responses never enter the TermInfo model. + +See `samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample` for a dry-run-safe example and an explicit `--live` path using `VerifyCapabilityAsync` on an interactive terminal. + +## Explicit exclusions + +Version 1.13 does not add live probing, backend ranking, protocol negotiation, timestamps, host/process provenance, raw protocol payloads, JSON deserialization, session/resource IDs, animation policy, or hidden replay. Those concerns remain downstream or future work. diff --git a/docs/1.13.0-RE01-INSPECTION-PUBLIC-API-ADDITIONS.txt b/docs/1.13.0-RE01-INSPECTION-PUBLIC-API-ADDITIONS.txt new file mode 100644 index 000000000..5a96be127 --- /dev/null +++ b/docs/1.13.0-RE01-INSPECTION-PUBLIC-API-ADDITIONS.txt @@ -0,0 +1,10 @@ +# Icod.TermInfo.Inspection 1.13 approved public API additions through RE03 +Icod.TermInfo.Inspection.PersistentRasterRuntimeObservationOutcome +Icod.TermInfo.Inspection.PersistentRasterRuntimeObservationOptions +Icod.TermInfo.Inspection.PersistentRasterRuntimeLifecycleObservation +Icod.TermInfo.Inspection.PersistentRasterRuntimePlacementObservation +Icod.TermInfo.Inspection.PersistentRasterRuntimeObservationSet +Icod.TermInfo.Inspection.PersistentRasterRuntimeIntegrationIssueKind +Icod.TermInfo.Inspection.PersistentRasterRuntimeIntegrationIssue +Icod.TermInfo.Inspection.PersistentRasterRuntimeIntegrationResult +Icod.TermInfo.Inspection.PersistentRasterRuntimeEvidenceIntegrator diff --git a/docs/1.13.0-RE01-RUNTIME-EVIDENCE-CONTRACT-AND-PUBLIC-API-REGRET-GATE.md b/docs/1.13.0-RE01-RUNTIME-EVIDENCE-CONTRACT-AND-PUBLIC-API-REGRET-GATE.md new file mode 100644 index 000000000..4cf93fe7d --- /dev/null +++ b/docs/1.13.0-RE01-RUNTIME-EVIDENCE-CONTRACT-AND-PUBLIC-API-REGRET-GATE.md @@ -0,0 +1,180 @@ +# Icod.TermInfo 1.13.0 — RE01 Runtime Evidence Contract and Public API Regret Gate + +**Tranche:** RE01 — architecture, vocabulary, bounds, and public API regret gate +**Development version:** `1.13.0-Alpha-1` +**Baseline:** stable `1.12.0` +**Primary package:** `Icod.TermInfo.Inspection` + +## Purpose + +RE01 freezes the smallest durable public foundation for caller-owned persistent-raster runtime evidence before the observation values, evidence integrator, replanning conveniences, or JSON v5 automation are implemented. + +The 1.13 line exists to remove consumer-side evidence/ordinal plumbing after live or otherwise external verification. TermInfo still performs no terminal I/O and does not acquire a production dependency on `Icod.Terminal`. + +## Frozen runtime outcome vocabulary + +RE01 adds exactly this three-state runtime outcome vocabulary: + +```text +Supported = 0 +Unsupported = 1 +Inconclusive = 2 +``` + +`Supported` and `Unsupported` are conclusive runtime facts which later tranches may map to the frozen existing `Verified` evidence kinds. + +`Inconclusive` is retained audit knowledge and must not be converted into either positive or negative support evidence. + +This is deliberately distinct from the frozen four-state classifier result vocabulary (`Unknown`, `Supported`, `Unsupported`, `Contradicted`). Runtime observations describe one externally obtained result; classifier status describes the conclusion after all evidence and precedence rules are applied. + +## Frozen observation resource bounds + +RE01 freezes: + +```text +DefaultMaximumObservationCount = 256 +MaximumSupportedObservationCount = 4096 +MaximumSourceLabelLength = 256 UTF-16 code units +``` + +The observation-count limit applies to the combined lifecycle plus placement observation set introduced by RE02. + +The count ceiling matches the established maximum supported lifecycle/placement evidence cardinality so runtime interchange cannot introduce an independently unbounded collection family. + +The source-label limit is new to the runtime-observation boundary. Existing historical evidence types are not retroactively changed. + +## Public surface introduced by RE01 + +RE01 adds exactly two public types: + +```text +PersistentRasterRuntimeObservationOutcome +PersistentRasterRuntimeObservationOptions +``` + +All 1.13-owned public types SHALL use the common prefix: + +```text +Icod.TermInfo.Inspection.PersistentRasterRuntime +``` + +That naming boundary is functional as well as organizational: release verification removes only the reviewed 1.13 `PersistentRasterRuntime*` type blocks before independently reconstructing the frozen 1.12 Inspection manifest. + +RE01 intentionally does **not** add a unified `PersistentRasterRuntimeSubject` enum. Lifecycle observations will reuse `PersistentRasterLifecycleEvidenceSubject`; placement observations will reuse `PersistentRasterPlacementSubject`. + +## Frozen historical boundaries + +RE01 reasserts without modification: + +### Lifecycle subjects + +```text +RasterDisplay = 0 +PersistentUpload = 1 +AcknowledgedUpload = 2 +PlacementCreation = 3 +MultiplePlacements = 4 +PlacementUpdate = 5 +PlacementDeletion = 6 +ResourceDeletion = 7 +``` + +### Lifecycle and placement evidence kinds + +```text +CapabilityDerived = 0 +Declared = 1 +Verified = 2 +``` + +### Placement subjects + +```text +SourceRectangle = 0 +SignedZOrder = 1 +``` + +### JSON v4 identity + +```text +urn:icod:terminfo:inspection:json:4 +schemaVersion = 4 +``` + +JSON v1-v4 remain immutable. JSON v5 is reserved for RE06. + +## Dependency boundary + +Production `Icod.TermInfo.Inspection` continues to reference only the TermInfo package family required by its historical design. It does not reference `Icod.Terminal` as a package or project. + +External verifiers may later adapt their own live semantic results into 1.13 runtime observations. Session types, endpoint availability, backend/protocol identities, resource identities, timestamps, and raw probe responses remain outside the production interchange model. + +## Compatibility reconstruction + +The frozen complete 1.12 Inspection public API remains identified by normalized-LF SHA-256: + +```text +f71501dcd27a530051c1a02083325144ced2b6173b6b967b9571c620815198f0 +``` + +RE01 adds `docs/1.13.0-RE01-INSPECTION-PUBLIC-API-ADDITIONS.txt` and changes the compatibility verifier from “the current assembly must equal 1.12” to the additive-minor-release rule: + +1. generate the current Inspection reflection manifest; +2. remove exactly the reviewed `PersistentRasterRuntime*` 1.13 type blocks; +3. require the reconstructed manifest to equal the exact frozen 1.12 SHA-256 above; +4. apply the existing reviewed 1.12 delta subtraction to reconstruct exact 1.11; +5. apply the established 1.11 subtraction to reconstruct the frozen 1.10 baseline. + +The Windows verification implementation remains compatible with Windows PowerShell 5.1. + +## TDD RED witness + +RE01 was introduced test-first. + +Exact RED head: + +```text +2e0be76cc502ed7f78e4133c8df78bba8297e593 +``` + +Pull-request workflow #758 / run `34769062313` failed during Build exactly because these planned types did not exist: + +```text +PersistentRasterRuntimeObservationOutcome +PersistentRasterRuntimeObservationOptions +``` + +Linux reported 42 compile errors and 0 warnings across `net8.0`, `net9.0`, and `net10.0`, all originating from those missing RE01 symbols. No production RE01 type or version/API-verifier change existed on the RED head. + +## Explicit exclusions + +RE01 does not add: + +- lifecycle or placement observation value objects yet; +- evidence integration; +- new evidence kinds; +- a new support/classifier status enum; +- planner behavior; +- a combined planner; +- JSON v5 rendering; +- JSON parsing/deserialization; +- live terminal probing; +- protocol/backend identities; +- backend preference/ranking; +- multi-protocol negotiation; +- terminal-brand heuristics; +- session/resource/placement identity; +- scene/layout/animation/image-transport policy. + +## Acceptance gate + +RE01 is accepted only when an exact `1.13.0-Alpha-1` head passes the ordinary read-only pull-request matrix, including: + +- Windows/Linux/macOS Build + Test; +- Windows PowerShell 5.1 Inspection compatibility reconstruction; +- coordinated package/API verification; +- existing package consumers; +- installed-tool smokes; and +- all six archive RIDs. + +The next implementation tranche is RE02 — immutable runtime observations and the bounded canonical observation set. diff --git a/docs/1.13.0-RE02-IMMUTABLE-RUNTIME-OBSERVATIONS.md b/docs/1.13.0-RE02-IMMUTABLE-RUNTIME-OBSERVATIONS.md new file mode 100644 index 000000000..2dd5e8fee --- /dev/null +++ b/docs/1.13.0-RE02-IMMUTABLE-RUNTIME-OBSERVATIONS.md @@ -0,0 +1,200 @@ +# Icod.TermInfo 1.13.0 — RE02 Immutable Runtime Observations + +**Tranche:** RE02 — immutable runtime observations and canonical observation set +**Development version:** `1.13.0-Alpha-2` +**Baseline:** RE01 / `1.13.0-Alpha-1` +**Primary package:** `Icod.TermInfo.Inspection` + +## Purpose + +RE02 introduces the caller-owned protocol-neutral runtime-observation values required to capture persistent-raster lifecycle and advanced-placement verification results without constructing classifier evidence directly. + +The tranche deliberately stops before evidence conversion. `Supported`, `Unsupported`, and `Inconclusive` remain runtime observations only; RE03 owns deterministic conversion of conclusive observations into the frozen existing `Verified` evidence kinds. + +## Public surface introduced by RE02 + +RE02 adds exactly three public types: + +```text +PersistentRasterRuntimeLifecycleObservation +PersistentRasterRuntimePlacementObservation +PersistentRasterRuntimeObservationSet +``` + +Together with the two RE01 types, the cumulative reviewed 1.13 public type ledger is maintained in: + +```text +docs/1.13.0-RE01-INSPECTION-PUBLIC-API-ADDITIONS.txt +``` + +All 1.13-owned public types continue to use the frozen `PersistentRasterRuntime*` prefix so the compatibility verifier and historical release tests can subtract the reviewed 1.13 delta before reconstructing exact 1.12, 1.11, and 1.10 surfaces. + +## Lifecycle runtime observation + +`PersistentRasterRuntimeLifecycleObservation` is immutable and records: + +```text +Subject : PersistentRasterLifecycleEvidenceSubject +Outcome : PersistentRasterRuntimeObservationOutcome +SourceLabel : string +SourceOrdinal : int +``` + +The subject vocabulary is reused directly from the frozen 1.11 lifecycle model. No duplicate or unified subject enum is introduced. + +## Placement runtime observation + +`PersistentRasterRuntimePlacementObservation` is immutable and records: + +```text +Subject : PersistentRasterPlacementSubject +Outcome : PersistentRasterRuntimeObservationOutcome +SourceLabel : string +SourceOrdinal : int +``` + +The subject vocabulary is reused directly from the frozen 1.12 placement model. + +## Validation contract + +Each observation validates its own fields at construction time: + +- the subject enum must be defined; +- the runtime outcome enum must be defined; +- `SourceLabel` must be non-null, non-empty, and non-whitespace; +- `SourceLabel` may contain leading or trailing whitespace when the complete value is not whitespace and is preserved exactly; +- `SourceLabel.Length` may not exceed 256 UTF-16 code units; +- `SourceOrdinal` must be non-negative; +- `int.MaxValue` remains a valid source-local ordinal. + +The source-local ordinal is provenance ordering supplied by the external verifier. It is not yet a final classifier evidence ordinal. + +## Observation-set snapshot contract + +`PersistentRasterRuntimeObservationSet` accepts lifecycle and placement observation sequences plus optional `PersistentRasterRuntimeObservationOptions`. + +Construction: + +1. rejects null lifecycle or placement sequences; +2. enumerates each caller sequence exactly once; +3. rejects null elements; +4. preserves every valid observation, including repeated values, compatible duplicates, contradictions, and inconclusive outcomes; +5. enforces the configured combined `lifecycle + placement` observation count while enumerating; +6. copies caller input into internal snapshots; +7. canonically orders each family; and +8. exposes immutable read-only collections plus the combined `Count`. + +An empty set is valid. + +The frozen RE01 limits apply unchanged: + +```text +DefaultMaximumObservationCount = 256 +MaximumSupportedObservationCount = 4096 +MaximumSourceLabelLength = 256 UTF-16 code units +``` + +With an explicit maximum of 4096, exactly 4096 combined observations are accepted and the 4097th is rejected. No family receives an independent additional 4096-element allowance. + +## Canonical ordering + +Lifecycle and placement collections are ordered independently using exactly these keys: + +```text +1. semantic subject numeric identity +2. SourceLabel using StringComparer.Ordinal +3. source-local SourceOrdinal +4. runtime outcome numeric identity +``` + +This ordering is independent of caller enumeration order, current culture, current UI culture, hash ordering, and collection implementation. + +The test contract explicitly exercises Turkish (`tr-TR`) and French (`fr-FR`) cultures and reversed input order. + +Cross-family ordering is not exposed by the CLR aggregate. RE06 may render lifecycle before placement when JSON v5 requires one deterministic cross-family representation. + +## Duplicate and contradiction semantics + +The observation set is a snapshot, not a classifier. + +It therefore retains without deduplication or conflict resolution: + +- repeated identical observations; +- `Supported` and `Unsupported` observations for the same subject/provenance key; +- `Inconclusive` observations; and +- multiple observations for the same semantic subject. + +Contradiction remains evidence for later integration/classification rather than a construction-time exception. + +## Equality expectations + +RE02 does not introduce a new semantic equality protocol for observation objects. Observation values are immutable reference objects whose public state is fully inspectable and deterministic. Snapshot tests assert exact retained object instances and ordering. + +If a future cross-process or machine-readable consumer requires value equality beyond public-state comparison, that must be justified separately rather than silently changing the 1.13 CLR contract. + +## TDD RED witness + +RE02 was introduced test-first. + +Exact RED head: + +```text +085586a8ba5278332c11684a64bc16eeeb0adfec +``` + +Pull-request workflow #774 / run `34770416758` failed during Build exactly because these planned types did not yet exist: + +```text +PersistentRasterRuntimeLifecycleObservation +PersistentRasterRuntimePlacementObservation +PersistentRasterRuntimeObservationSet +``` + +Linux reported 18 `CS0246` errors and 0 warnings across `net8.0`, `net9.0`, and `net10.0`. The errors originated only from `RE02PersistentRasterRuntimeObservationTests.cs`; no RE02 production type existed on the RED head. + +## Intermediate GREEN evidence + +Implementation head: + +```text +5566f4d7004145941bb2b4934de0b67225521518 +``` + +On pull-request workflow #777 / run `34770612282`, Linux Build completed successfully with 0 warnings and 0 errors. The subsequent test step exposed only historical release-surface ownership checks because the cumulative 1.13 API ledger had not yet been advanced for the three RE02 public types: + +```text +1.11 reconstructed exported type count: expected 67, observed 70 +1.12 reconstructed exported type count: expected 81, observed 84 +``` + +That exact +3 delta matched the three new RE02 types. The historical tests were not weakened. The canonical cumulative 1.13 API ledger was advanced instead so those tests continue to reconstruct the older frozen surfaces from one reviewed source of truth. + +## Explicit exclusions + +RE02 does not add: + +- mapping to `PersistentRasterLifecycleEvidence` or `PersistentRasterPlacementEvidence`; +- final classifier ordinal assignment; +- evidence capacity or ordinal-exhaustion outcomes; +- integration issues/results; +- lifecycle or placement reclassification; +- replanning conveniences; +- JSON v5 rendering or parsing; +- live probing or terminal I/O; +- protocol/backend identifiers; +- terminal/session/resource/placement identities; +- timestamps or raw probe responses; +- backend preference/ranking or negotiation. + +## Acceptance gate + +RE02 is accepted only when an exact `1.13.0-Alpha-2` head passes the ordinary pull-request matrix, including: + +- Windows/Linux/macOS Build + Test; +- exact 1.12 Inspection API reconstruction after subtracting the cumulative reviewed 1.13 type ledger; +- historical 1.11 and 1.10 reconstruction; +- coordinated package/API verification; +- existing package consumers and installed-tool smokes; and +- all six archive RIDs. + +The next implementation tranche is RE03 — deterministic evidence integration, safe final ordinal assignment, combined-evidence bounds, inconclusive retention, and atomic-per-family integration failures. diff --git a/docs/1.13.0-RE03-DETERMINISTIC-EVIDENCE-INTEGRATION.md b/docs/1.13.0-RE03-DETERMINISTIC-EVIDENCE-INTEGRATION.md new file mode 100644 index 000000000..3be6e6a1a --- /dev/null +++ b/docs/1.13.0-RE03-DETERMINISTIC-EVIDENCE-INTEGRATION.md @@ -0,0 +1,192 @@ +# Icod.TermInfo 1.13.0 — RE03 Deterministic Evidence Integration + +**Tranche:** RE03 — deterministic runtime-evidence integration +**Development version:** `1.13.0-Alpha-3` +**Baseline:** RE02 / `1.13.0-Alpha-2` +**Primary package:** `Icod.TermInfo.Inspection` + +## Purpose + +RE03 removes the mechanical evidence-factory and final-ordinal work previously left to downstream consumers after live verification. It converts conclusive protocol-neutral runtime observations into the existing frozen lifecycle and placement `Verified` evidence forms, retains inconclusive observations for audit, preserves all pre-existing evidence, and delegates resulting support classification to the existing 1.11/1.12 classifiers. + +RE03 does not add a second precedence model, replacement classifier, planner, or terminal-probing responsibility. + +## Public surface introduced by RE03 + +RE03 adds exactly four public types: + +```text +PersistentRasterRuntimeIntegrationIssueKind +PersistentRasterRuntimeIntegrationIssue +PersistentRasterRuntimeIntegrationResult +PersistentRasterRuntimeEvidenceIntegrator +``` + +The cumulative reviewed 1.13 public type ledger remains: + +```text +docs/1.13.0-RE01-INSPECTION-PUBLIC-API-ADDITIONS.txt +``` + +The frozen issue vocabulary is: + +```text +LifecycleEvidenceCapacityExhausted = 0 +PlacementEvidenceCapacityExhausted = 1 +LifecycleOrdinalSpaceExhausted = 2 +PlacementOrdinalSpaceExhausted = 3 +``` + +Each structured issue exposes the affected family limitation together with `ExistingEvidenceCount` and `RequestedImportCount`. + +## Integration API + +The core entry point is: + +```csharp +PersistentRasterRuntimeEvidenceIntegrator.Integrate( + PersistentRasterLifecycleProfile lifecycleProfile, + PersistentRasterPlacementProfile placementProfile, + PersistentRasterRuntimeObservationSet observations +) +``` + +The immutable result retains: + +- the exact original `PersistentRasterRuntimeObservationSet`; +- imported lifecycle evidence; +- imported placement evidence; +- inconclusive lifecycle observations; +- inconclusive placement observations; +- the resulting lifecycle profile; +- the resulting placement profile; +- deterministic structured integration issues; and +- `Succeeded`, meaning every conclusive observation was safely imported. + +`Succeeded` does not imply that a downstream planner can satisfy a request. + +## Evidence mapping + +Conclusive lifecycle observations map only to the existing lifecycle evidence type: + +```text +Supported -> IsPositive = true, Kind = Verified +Unsupported -> IsPositive = false, Kind = Verified +``` + +Conclusive placement observations map identically to the existing placement evidence type. + +The validated runtime `SourceLabel` is copied exactly. No new evidence kind is introduced. + +`Inconclusive` produces no classifier evidence. It remains visible in the result audit collections and does not consume final evidence capacity or ordinal space. + +## Deterministic final ordinal assignment + +Lifecycle and placement ordinal spaces are handled independently. + +For each family: + +1. preserve the complete existing evidence snapshot without renumbering; +2. use final ordinal `0` when no existing evidence is present; +3. otherwise determine `max(existing.SourceOrdinal) + 1` only after verifying sufficient integer space; +4. consume conclusive observations in the RE02 canonical order; +5. assign consecutive final ordinals; and +6. append the complete imported family atomically. + +Runtime observation `SourceOrdinal` remains source-local provenance. It does not become the final classifier ordinal. + +An existing `int.MaxValue` ordinal therefore produces a represented ordinal-space-exhaustion issue when any conclusive observation must be appended. Integer wrap, arbitrary renumbering, truncation, and partial family append are forbidden. + +## Capacity handling + +The existing frozen family maximum of 4096 evidence assertions remains authoritative. + +Before allocating imported family evidence, the integrator checks: + +```text +existing evidence count + requested conclusive import count <= 4096 +``` + +If the full family import cannot fit, the family produces one capacity issue, imports no evidence, and returns its original profile unchanged. + +Inconclusive observations are excluded from the requested evidence import count. + +## Atomicity and independent families + +Integration is atomic per evidence family, not globally transactional across both families. + +A lifecycle capacity or ordinal failure: + +- imports no lifecycle evidence; +- preserves the original lifecycle profile unchanged; and +- does not prevent an independently valid placement import. + +Placement failures have the symmetric behavior. + +This makes partial success explicit without ever exposing a partially imported failing family. + +## Classification boundary + +Successful family imports are reclassified only through the existing frozen classifiers: + +```text +PersistentRasterLifecycleClassifier.Classify(...) +PersistentRasterPlacementClassifier.Classify(...) +``` + +RE03 contains no support-precedence algorithm of its own. Existing verified/declared/capability-derived precedence and contradiction semantics therefore remain authoritative. + +RE04 owns explicit contradiction/classification matrix hardening rather than changing that policy here. + +## TDD RED witness + +RE03 was introduced test-first. + +Exact RED head: + +```text +2fbfbba9a9c534d1bb619edccbeee11bbe9b18e1 +``` + +Pull-request workflow #781 / run `34771216550` reached Linux Build with the test-only RE03 contract and no RE03 production types. Linux reported 99 errors and 0 warnings, all confined to `RE03PersistentRasterRuntimeEvidenceIntegrationTests.cs` and all caused by the four intentionally absent RE03 integration types. + +The RED contract covers: + +- frozen issue-kind numerics; +- Supported/Unsupported mapping to positive/negative `Verified` evidence; +- inconclusive retention; +- exact source-label preservation; +- empty-profile ordinal zero; +- append after the maximum existing ordinal without renumbering existing evidence; +- 4096 evidence-capacity exhaustion; +- `int.MaxValue` ordinal-space exhaustion; +- atomic failure within each evidence family; and +- independent success of the unaffected family. + +## Explicit exclusions + +RE03 does not add: + +- new lifecycle or placement support precedence; +- new contradiction semantics; +- replanning convenience APIs; +- combined planner statuses; +- JSON v5 rendering or parsing; +- live terminal I/O or probing; +- protocol/backend identities; +- session/resource/placement identities; +- timestamps or raw probe responses; +- backend preference, ranking, or negotiation. + +## Acceptance gate + +RE03 is accepted only when an exact `1.13.0-Alpha-3` head passes the ordinary pull-request matrix, including: + +- Windows/Linux/macOS Build + Test; +- exact historical Inspection API reconstruction after subtracting the cumulative reviewed 1.13 type ledger; +- Windows PowerShell compatibility verification; +- package creation and exact artifact verification; +- isolated package/installed-tool consumers; and +- all six archive RIDs. + +The next tranche after that gate is RE04 — explicit classification and contradiction integration across the frozen lifecycle and placement classifier semantics. diff --git a/docs/1.13.0-RE04-CLASSIFICATION-AND-CONTRADICTION-INTEGRATION.md b/docs/1.13.0-RE04-CLASSIFICATION-AND-CONTRADICTION-INTEGRATION.md new file mode 100644 index 000000000..2c470b0d5 --- /dev/null +++ b/docs/1.13.0-RE04-CLASSIFICATION-AND-CONTRADICTION-INTEGRATION.md @@ -0,0 +1,83 @@ +# Icod.TermInfo 1.13.0 — RE04 Classification and Contradiction Integration + +**Tranche:** RE04 — classifier/contradiction behavior through runtime integration +**Development version:** `1.13.0-Alpha-4` +**Baseline:** accepted `1.13.0-Alpha-3` / RE03 +**Primary package:** `Icod.TermInfo.Inspection` + +## Purpose + +RE04 qualifies the semantic boundary between RE03 runtime-evidence integration and the frozen lifecycle/placement classifiers. It adds regression coverage proving that runtime observations do not introduce a second precedence engine inside the integrator. + +The integrator remains responsible only for deterministic observation-to-`Verified` evidence conversion, per-family resource/ordinal atomicity, and audit retention. Final support status remains owned by `PersistentRasterLifecycleClassifier` and `PersistentRasterPlacementClassifier`. + +## Qualified precedence behavior + +RE04 freezes the following integration outcomes without changing the historical classifier vocabulary: + +- existing `CapabilityDerived` positive evidence plus runtime `Unsupported` becomes `Unsupported` because imported runtime evidence is existing `Verified` evidence; +- existing `Declared` negative evidence plus runtime `Supported` becomes `Supported`; +- existing `Verified` positive evidence plus imported `Verified` negative evidence becomes `Contradicted`; +- existing `Verified` negative evidence plus imported `Verified` positive evidence becomes `Contradicted`; +- repeated compatible runtime observations remain conclusively `Supported` or `Unsupported` rather than becoming contradictory merely because more than one compatible assertion exists. + +No precedence table or subject-classification algorithm is copied into `PersistentRasterRuntimeEvidenceIntegrator`. + +## Inconclusive observations + +Runtime observations whose outcome is `Inconclusive` continue to produce no classifier evidence. RE04 verifies that an inconclusive-only integration preserves the pre-existing lifecycle and placement support states while retaining the exact observations in the integration audit result. + +## Per-family independence + +RE04 also qualifies RE03's family-isolation contract at the classification level: + +- a lifecycle capacity/ordinal failure leaves the original lifecycle profile unchanged while a valid placement family may still import evidence and classify normally; +- a placement capacity/ordinal failure leaves the original placement profile unchanged while a valid lifecycle family may still import evidence and classify normally. + +`PersistentRasterRuntimeIntegrationResult.Succeeded` remains false when either family reports an integration issue, even though the unaffected family may have integrated successfully. + +## Test-first qualification result + +RE04 added `RE04PersistentRasterRuntimeClassificationIntegrationTests.cs` before making any production-code change. + +Qualification head: + +```text +1ad9a67b594bf52573ac5e8496cf0dbed009e41a +``` + +The new regression suite compiled and passed on the existing RE03 implementation. Because the required behavior was already present through delegation to the frozen classifiers, RE04 required **no production implementation correction**. Adding one would have duplicated classifier policy or introduced unnecessary semantic drift. + +This is intentionally a GREEN characterization/qualification tranche rather than a manufactured RED production change: the tests establish that the behavior introduced by RE03 already satisfies the separately reviewed RE04 classifier contract. + +## Public API surface + +RE04 adds no public types, members, enums, statuses, or evidence kinds. The cumulative 1.13 public API ledger remains exactly the RE03 ledger. + +## Explicit exclusions + +RE04 does not add or change: + +- classifier precedence; +- lifecycle or placement support-status vocabularies; +- evidence-kind vocabularies; +- runtime observation vocabularies; +- planner behavior; +- replanning helpers; +- JSON v5; +- live terminal probing; +- protocol/backend identity or ranking; +- production dependencies. + +## Acceptance gate + +RE04 is accepted only when an exact `1.13.0-Alpha-4` head passes the ordinary read-only pull-request matrix, including: + +- Windows/Linux/macOS Build + Test; +- Windows PowerShell Inspection compatibility reconstruction; +- coordinated package/API verification; +- existing isolated package consumers; +- installed-tool/package smokes; and +- all six archive RID smokes. + +The next tranche is RE05 — replanning conveniences on the integration audit result. diff --git a/docs/1.13.0-RE05-REPLANNING-COMPOSITION.md b/docs/1.13.0-RE05-REPLANNING-COMPOSITION.md new file mode 100644 index 000000000..cf5e974f1 --- /dev/null +++ b/docs/1.13.0-RE05-REPLANNING-COMPOSITION.md @@ -0,0 +1,135 @@ +# Icod.TermInfo 1.13.0 — RE05 Replanning Composition + +**Tranche:** RE05 — replanning conveniences on the runtime-integration audit result +**Development version:** `1.13.0-Alpha-5` +**Baseline:** accepted `1.13.0-Alpha-4` / RE04 +**Primary package:** `Icod.TermInfo.Inspection` + +## Purpose + +RE05 removes the remaining caller-side planner-composition boilerplate after runtime observations have been integrated. It adds exactly two convenience methods to `PersistentRasterRuntimeIntegrationResult` and deliberately introduces no combined planner, plan status, or orchestration result type. + +The integration result remains an audit object containing the strengthened lifecycle and placement profiles. RE05 simply delegates those profiles into the existing frozen planners. + +## Public replanning conveniences + +RE05 adds these members to the existing 1.13-owned `PersistentRasterRuntimeIntegrationResult` type: + +```csharp +public PersistentRasterLifecyclePlan CreateLifecyclePlan( + PersistentRasterLifecycleRequest request +) + +public PersistentRasterPlacementPlan CreatePlacementPlan( + PersistentRasterLifecycleRequest lifecycleRequest, + PersistentRasterPlacementRequest placementRequest +) +``` + +`CreateLifecyclePlan` is exactly equivalent to: + +```csharp +PersistentRasterLifecyclePlanner.Plan( + integration.LifecycleProfile, + request +) +``` + +`CreatePlacementPlan` is exactly equivalent to: + +```csharp +PersistentRasterLifecyclePlan lifecyclePlan = + PersistentRasterLifecyclePlanner.Plan( + integration.LifecycleProfile, + lifecycleRequest + ); +PersistentRasterPlacementPlanner.Plan( + lifecyclePlan, + integration.PlacementProfile, + placementRequest +) +``` + +Both methods reject null request arguments and otherwise leave validation and planning semantics with the frozen request/planner contracts. + +## No new planning policy + +RE05 does not copy or reinterpret lifecycle or placement planning rules. In particular, it does not: + +- invent a combined runtime-integration plan status; +- collapse lifecycle and placement outcomes into a new vocabulary; +- alter `Success`, `Indeterminate`, or `Impossible` lifecycle semantics; +- alter `Satisfied`, `RequiresRuntimeVerification`, `Indeterminate`, or `Impossible` placement semantics; +- add retry, probing, execution, backend selection, or protocol policy. + +The convenience methods return the exact immutable plan shapes already produced by the historical planners. + +## Test-first witness + +RE05 was introduced test-first. + +Exact RED head: + +```text +73fc7b99e8ba61ba1dd0932f6855f387b1340008 +``` + +Pull-request workflow #792 / run `34772452211` failed during Build with **24 errors and 0 warnings** across `net8.0`, `net9.0`, and `net10.0`. Every error was `CS1061` from `RE05PersistentRasterRuntimeReplanningTests.cs` because the two planned methods did not yet exist: + +```text +CreateLifecyclePlan +CreatePlacementPlan +``` + +No unrelated compile error was present. + +The minimal implementation head is: + +```text +3612972b45d5b1a9cfbdd33d03cc219a0ee1181b +``` + +On that implementation head, Linux and macOS both passed Build and Test before the tranche bookkeeping advanced. The tests compare the convenience methods against direct frozen-planner composition rather than against duplicated expected planning algorithms. + +## Qualified planning paths + +The RE05 test suite covers: + +- runtime-strengthened lifecycle requirements producing the same lifecycle plan as direct invocation; +- placement `Satisfied` after lifecycle and placement runtime strengthening; +- placement `RequiresRuntimeVerification` when advanced placement support remains unknown while lifecycle is conclusive; +- placement `Indeterminate` when lifecycle remains indeterminate while requested placement support is conclusive; +- placement `Impossible` when lifecycle is conclusively impossible; +- null lifecycle/placement request rejection. + +## Compatibility and API ledger + +RE05 adds no new public type. The cumulative `PersistentRasterRuntime*` type ledger therefore remains unchanged. + +The new methods live on a 1.13-owned `PersistentRasterRuntime*` type whose complete type block is already removed when reconstructing the frozen 1.12 Inspection surface. No separate historical-member subtraction ledger is required for RE05. + +## Explicit exclusions + +RE05 does not add: + +- a combined plan/result/status type; +- planner precedence changes; +- classifier changes; +- automatic runtime probing; +- retry/execution orchestration; +- JSON v5; +- Icod.Terminal production dependency; +- protocol/backend identity, ranking, or negotiation. + +## Acceptance gate + +RE05 is accepted only when an exact `1.13.0-Alpha-5` head passes the ordinary read-only pull-request matrix, including: + +- Windows/Linux/macOS Build + Test; +- Windows PowerShell Inspection compatibility reconstruction; +- coordinated package/API verification; +- existing isolated package consumers; +- installed-tool/package smokes; and +- all six archive RID smokes. + +The next tranche is RE06 — JSON v5 runtime observation/integration automation. diff --git a/docs/1.13.0-RE06-INSPECTION-PUBLIC-API-ADDITIVE-MEMBERS.txt b/docs/1.13.0-RE06-INSPECTION-PUBLIC-API-ADDITIVE-MEMBERS.txt new file mode 100644 index 000000000..797d4db19 --- /dev/null +++ b/docs/1.13.0-RE06-INSPECTION-PUBLIC-API-ADDITIVE-MEMBERS.txt @@ -0,0 +1,8 @@ +# Icod.TermInfo.Inspection 1.13 RE06 reviewed public members added to TermInfoJsonRenderer. +# Exact public-api-snapshot manifest lines; all earlier renderer members remain frozen. + FIELD public static const System.Int32 PersistentRasterRuntimeSchemaVersion null=not-null/not-null value=5 + FIELD public static const System.String PersistentRasterRuntimeSchemaIdentifier null=not-null/not-null value="urn:icod:terminfo:inspection:json:5" + METHOD public static System.String Render(Icod.TermInfo.Inspection.PersistentRasterRuntimeIntegrationResult integration null=not-null/not-null) return-null=not-null/not-null + METHOD public static System.String Render(Icod.TermInfo.Inspection.PersistentRasterRuntimeIntegrationResult integration null=not-null/not-null, Icod.TermInfo.Inspection.TermInfoJsonRendererOptions options null=not-null/not-null, System.Threading.CancellationToken cancellationToken null=not-null/not-null default=null) return-null=not-null/not-null + METHOD public static System.String Render(Icod.TermInfo.Inspection.PersistentRasterRuntimeObservationSet observations null=not-null/not-null) return-null=not-null/not-null + METHOD public static System.String Render(Icod.TermInfo.Inspection.PersistentRasterRuntimeObservationSet observations null=not-null/not-null, Icod.TermInfo.Inspection.TermInfoJsonRendererOptions options null=not-null/not-null, System.Threading.CancellationToken cancellationToken null=not-null/not-null default=null) return-null=not-null/not-null diff --git a/docs/1.13.0-RE06-RUNTIME-EVIDENCE-AUTOMATION.md b/docs/1.13.0-RE06-RUNTIME-EVIDENCE-AUTOMATION.md new file mode 100644 index 000000000..b4945becf --- /dev/null +++ b/docs/1.13.0-RE06-RUNTIME-EVIDENCE-AUTOMATION.md @@ -0,0 +1,204 @@ +# Icod.TermInfo 1.13.0 — RE06 Runtime Evidence Automation + +**Tranche:** RE06 — JSON v5 runtime observation/integration automation +**Development version:** `1.13.0-Alpha-6` +**Baseline:** accepted `1.13.0-Alpha-5` / RE05 +**Primary package:** `Icod.TermInfo.Inspection` + +## Purpose + +RE06 adds the machine-readable interchange layer for the 1.13 runtime-evidence model while preserving every historical JSON contract through version 4. The new version-5 surface renders caller-owned canonical runtime observations and deterministic integration audit results without adding parsing, live probing, protocol policy, or nested copies of historical profile documents. + +## JSON v5 identity + +RE06 adds these reviewed members to `TermInfoJsonRenderer`: + +```csharp +public const string PersistentRasterRuntimeSchemaIdentifier = + "urn:icod:terminfo:inspection:json:5"; +public const int PersistentRasterRuntimeSchemaVersion = 5; + +public static string Render( + PersistentRasterRuntimeObservationSet observations +); + +public static string Render( + PersistentRasterRuntimeObservationSet observations, + TermInfoJsonRendererOptions options, + CancellationToken cancellationToken = default +); + +public static string Render( + PersistentRasterRuntimeIntegrationResult integration +); + +public static string Render( + PersistentRasterRuntimeIntegrationResult integration, + TermInfoJsonRendererOptions options, + CancellationToken cancellationToken = default +); +``` + +The corresponding schema is: + +```text +docs/Icod.TermInfo.Inspection.schema.v5.json +``` + +with exact identifier: + +```text +urn:icod:terminfo:inspection:json:5 +``` + +and exactly two top-level document alternatives: + +```text +persistentRasterRuntimeObservationSet +persistentRasterRuntimeIntegration +``` + +## Observation-set document + +The observation-set document renders the already canonical immutable `PersistentRasterRuntimeObservationSet` without reinterpreting or deduplicating it. Its data payload contains: + +- combined observation count; +- lifecycle observation count; +- placement observation count; +- canonical lifecycle observations; and +- canonical placement observations. + +Each observation contains only the frozen semantic subject, runtime outcome, exact caller source label, and source-local ordinal. + +## Integration audit document + +The integration document is intentionally an audit of the 1.13 adapter boundary rather than a nested historical profile serialization. It contains: + +- `Succeeded`; +- the original canonical runtime observations; +- imported lifecycle `Verified` evidence; +- imported placement `Verified` evidence; +- retained inconclusive lifecycle observations; +- retained inconclusive placement observations; +- structured integration issues; +- resulting lifecycle subject support states; and +- resulting placement subject support states. + +It does not embed complete version-3 lifecycle-profile or version-4 placement-profile JSON documents. + +## Deterministic rendering + +The renderer reuses the established Inspection JSON infrastructure: + +- `BoundedJsonOutput` for exact UTF-8 output limits; +- `DeterministicJsonWriter` for stable property and array rendering; +- `TermInfoJsonRendererOptions` for compact/indented output and byte limits; +- explicit cancellation boundaries; and +- the frozen lifecycle/placement semantic-name helpers. + +No serializer reflection, culture-sensitive formatting, parser, or deserializer is introduced. + +## Test-first witness + +RE06 was introduced test-first. + +Exact RED head: + +```text +c332b2609191692c645fe3ca4bed0d7adf135905 +``` + +Pull-request workflow #796 / run `34773104935` failed during Build with **51 errors and 0 warnings** on Linux, with the same missing-surface pattern reproduced on macOS. The failures were confined to `RE06PersistentRasterRuntimeJsonTests.cs` and were caused only by the absent version-5 renderer overloads and constants: + +```text +PersistentRasterRuntimeSchemaIdentifier +PersistentRasterRuntimeSchemaVersion +Render(PersistentRasterRuntimeObservationSet ...) +Render(PersistentRasterRuntimeIntegrationResult ...) +``` + +The renderer/schema/package implementation advanced through these heads: + +```text +361e11d27c77d22366bf85ed78b15cd8480685a4 v5 renderer + +d1a2aaf588ae8652e4db6df1c0989d56c6ccfeca v5 schema + +660dba2757617f8158baace4c0062af700272e6d package schema wiring +``` + +Compatibility approval then added the exact six reviewed renderer manifest lines and extended the historical reconstruction chain: + +```text +3d8288a62bad74ec6156c7f505f47390c4cb797f RE06 additive-member ledger +f117c8895be0b811fd5b44cd86d20b7a883cfd57 compatibility reconstruction +``` + +On `f117c8895be0b811fd5b44cd86d20b7a883cfd57`, Linux passed Build and Test, macOS passed Build and Test, and Windows passed Build plus the PowerShell Inspection compatibility reconstruction before the Alpha-6 bookkeeping advanced. + +## Qualified JSON behavior + +The RE06 tests freeze: + +- exact compact observation-set JSON property order and values; +- exact compact integration-audit JSON property order and values; +- structured capacity/ordinal integration issue records; +- schema identifier/version identity; +- repeated-render determinism; +- `tr-TR` and `fr-FR` culture independence; +- exact UTF-8 byte-limit success; +- one-byte-less fail-closed behavior; +- cancellation; +- version-5 schema identity and exactly two document branches; +- package-project inclusion of the v5 schema; and +- unchanged normalized SHA-256 fingerprints for schemas v1 through v4. + +## Historical compatibility reconstruction + +RE06 adds public members to the historical `TermInfoJsonRenderer` type, so the existing cumulative type ledger alone is not sufficient to reconstruct 1.12. + +The reviewed member ledger is: + +```text +docs/1.13.0-RE06-INSPECTION-PUBLIC-API-ADDITIVE-MEMBERS.txt +``` + +The compatibility verifier now reconstructs 1.12 in this order: + +1. remove exactly the six reviewed RE06 `PersistentRasterRuntime*` renderer members; +2. remove the cumulative reviewed 1.13 `PersistentRasterRuntime*` type blocks; +3. require the exact frozen 1.12 API SHA-256; +4. continue the established 1.12 → 1.11 → 1.10 subtraction/fingerprint chain unchanged. + +No earlier public member or JSON schema is relaxed. + +## Package wiring + +`Icod.TermInfo.Inspection` now packs `Icod.TermInfo.Inspection.schema.v5.json` in `docs/` alongside the frozen version-1 through version-4 schemas. + +## Explicit exclusions + +RE06 does not add: + +- JSON deserialization; +- live terminal probing; +- protocol/backend identifiers; +- backend ranking or negotiation; +- timestamps, process/host provenance, or raw protocol responses; +- combined planner/status vocabulary; +- changes to lifecycle or placement classifier/planner semantics; +- changes to JSON versions 1 through 4; or +- a production dependency on `Icod.Terminal`. + +## Acceptance gate + +RE06 is accepted only when an exact `1.13.0-Alpha-6` head passes the ordinary read-only pull-request matrix, including: + +- Windows/Linux/macOS Build + Test; +- Windows PowerShell Inspection compatibility reconstruction; +- coordinated package/API verification including the packed v5 schema; +- existing isolated package consumers; +- installed-tool/package smokes; and +- all six archive RID smokes. + +The next tranche is RE07 — package-only `Icod.Terminal` interoperability and executable runtime-integration samples. diff --git a/docs/1.13.0-RE07-TERMINAL-INTEROPERABILITY-AND-PACKAGE-QUALIFICATION.md b/docs/1.13.0-RE07-TERMINAL-INTEROPERABILITY-AND-PACKAGE-QUALIFICATION.md new file mode 100644 index 000000000..188696f22 --- /dev/null +++ b/docs/1.13.0-RE07-TERMINAL-INTEROPERABILITY-AND-PACKAGE-QUALIFICATION.md @@ -0,0 +1,189 @@ +# Icod.TermInfo 1.13.0 — RE07 Terminal Interoperability and Package Qualification + +**Tranche:** RE07 — package-only `Icod.Terminal` interoperability and executable runtime-integration samples +**Development version:** `1.13.0-Alpha-7` +**Baseline:** accepted `1.13.0-Alpha-6` / RE06 +**Primary package:** `Icod.TermInfo.Inspection` + +## Purpose + +RE07 qualifies the 1.13 runtime-evidence model against a real downstream terminal-session package without coupling production TermInfo to `Icod.Terminal`. The adapter boundary remains consumer-owned: an application may obtain a semantic live result from Terminal, translate that result into immutable TermInfo runtime observations, integrate those observations with static TermInfo evidence, and continue through the existing frozen classifiers and planners. + +The production `Icod.TermInfo.Inspection` project still has no `Icod.Terminal` package or project dependency. + +## Qualified downstream dependency + +RE07 intentionally pins qualification to the published stable package: + +```text +Icod.Terminal 1.12.0 +``` + +`Icod.Terminal 1.13.0` was published after this roadmap was approved, but RE07 does not silently widen the frozen qualification target. Newer-Terminal qualification can be considered separately without changing this tranche's contract. + +The package-only consumer references exactly: + +```text +Icod.TermInfo.Inspection $(IcodTermInfoInspectionPackageVersion) +Icod.Terminal 1.12.0 +``` + +and contains no project references. + +## Adapter boundary + +The adapter maps externally obtained Terminal semantics into TermInfo observations. It does not pass any of these values into production TermInfo APIs: + +- `TerminalSession`; +- `TerminalCapabilityStatus`; +- Terminal endpoint-availability state; +- backend or protocol identity; +- raw terminal responses; or +- Terminal-owned raster resource/placement identity. + +The package-only consumer maps the stable `TerminalCapabilitySupport` vocabulary as follows: + +```text +Verified -> PersistentRasterRuntimeObservationOutcome.Supported +Unsupported -> PersistentRasterRuntimeObservationOutcome.Unsupported +Unknown -> PersistentRasterRuntimeObservationOutcome.Inconclusive +Advertised -> PersistentRasterRuntimeObservationOutcome.Inconclusive +``` + +`PersistentRasterGraphics` in Terminal 1.12 is intentionally coarser than TermInfo's lifecycle evidence subjects. Therefore the consumer, not TermInfo, owns the explicit expansion from that single Terminal semantic result into the lifecycle subjects it chooses to represent. + +## Package-only qualification consumer + +RE07 adds: + +```text +tools/runtime-evidence-package-smoke/ +.github/scripts/smoke-re07-runtime-evidence-interop.ps1 +.github/scripts/package-smoke-re07.NuGet.Config +``` + +The runner restores the freshly packed Inspection package from the workflow artifact directory while resolving stable `Icod.Terminal 1.12.0` and its normal dependencies from NuGet. It then executes the consumer on: + +```text +net8.0 +net9.0 +net10.0 +``` + +The consumer demonstrates caller-owned mapping from Terminal semantic support into `PersistentRasterRuntimeLifecycleObservation`, construction of `PersistentRasterRuntimeObservationSet`, integration through `PersistentRasterRuntimeEvidenceIntegrator`, and replanning through the existing 1.13 convenience/frozen planner path. + +## Focused runtime-integration sample + +RE07 adds: + +```text +samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/ +``` + +The sample demonstrates this complete boundary: + +```text +static TermInfo inspection + -> static plan requires runtime verification + -> caller obtains/maps runtime result + -> immutable runtime observations + -> PersistentRasterRuntimeEvidenceIntegrator + -> strengthened profiles + -> frozen lifecycle planner + -> Terminal-owned live/execution boundary +``` + +The default mode is deterministic and CI-safe. It uses the published Terminal support vocabulary without opening a terminal session. Passing `--live` on an interactive terminal exercises the real `TerminalSession.VerifyCapabilityAsync(TerminalCapability.PersistentRasterGraphics)` path before mapping the result into TermInfo observations. + +The sample does not calculate final classifier evidence ordinals and does not append `Verified` evidence directly. + +## Existing sample migration + +The existing persistent-raster lifecycle and placement samples now use the 1.13 runtime-observation/integrator path where they demonstrate post-verification strengthening. + +The lifecycle sample remains Inspection-only and performs no live Terminal I/O. It now constructs caller-owned runtime observations and lets the integrator map them to final classifier evidence. + +The placement sample still demonstrates Terminal-owned crop and signed-z-order execution values, but its lifecycle and placement strengthening now flow through runtime observations and integration rather than direct final `Verified` evidence construction. + +Historical RL07 and PG07 package-only qualification consumers remain intact; RE07 adds a new downstream witness rather than replacing those historical compatibility witnesses. + +## Test-first witness + +RE07 began with topology/qualification tests only. + +Exact RED head: + +```text +b3c2668386246ac8f5bf06fb8340827b4023b2bf +``` + +Pull-request workflow #804 / run `34773963368` passed Build with **0 warnings and 0 errors**, then failed the new RE07 assertions exactly where expected. On each supported Inspection TFM, six new RE07 facts failed while the production dependency-isolation fact already passed. The failures were limited to: + +- absent package-only runtime-evidence consumer; +- absent RE07 smoke runner/source mapping; +- absent focused runtime-integration sample; +- missing package-verifier wiring; +- lifecycle sample still using manual final `Verified` evidence; and +- placement sample still using manual final `Verified` evidence. + +No production compile failure was involved. + +## Implementation and correction checkpoints + +The RE07 implementation introduced the package consumer, smoke runner/config, focused sample, sample migrations, sample index update, and package-verifier wiring through: + +```text +c10427b55f3608cba0caa11a21c42d2a2968576a +``` + +That implementation compiled successfully but exposed two repository-level qualification mismatches: + +1. the new focused sample had one multiline ternary that did not use the repository's required parenthesized-condition/aligned-branch/own-line-semicolon layout; and +2. the historical RL07 sample qualification test still required the obsolete manual `PersistentRasterLifecycleEvidenceKind.Verified` token even though RE07 intentionally replaces that sample-side boilerplate. + +The corrections advanced to: + +```text +7a6968b5223ef491e7a7a4b4156ac18f66143ede +``` + +The RL07 package-only historical consumer remained unchanged; only its sample-specific assertion was advanced to require the new observation/integrator pattern. + +## Qualified implementation witness + +Exact implementation head: + +```text +7a6968b5223ef491e7a7a4b4156ac18f66143ede +``` + +Pull-request workflow #817 / run `34774425487` completed `success` across all twelve jobs: + +- Windows Build + Test + PowerShell Inspection compatibility reconstruction; +- Linux Build + Test + coordinated Staging package verification; +- macOS Build + Test; +- installed-tool/package smoke on Windows, Linux, and macOS; and +- six archive RID smokes: Windows x64/ARM64, Linux x64/ARM64, and macOS x64/ARM64. + +The Linux package-verification path included the newly wired RE07 package-only consumer and focused sample, so the fresh Inspection package was restored and exercised beside published `Icod.Terminal 1.12.0` on `net8.0`, `net9.0`, and `net10.0`. + +## Compatibility and scope + +RE07 adds no public Inspection API. The cumulative 1.13 public-type ledger and the RE06 additive renderer-member ledger therefore remain unchanged. + +RE07 does not add: + +- a production dependency on `Icod.Terminal`; +- automatic terminal probing in TermInfo; +- backend/protocol identity or selection; +- backend ranking or negotiation; +- Terminal resource/placement handles in TermInfo; +- implicit subject expansion inside TermInfo; +- new classifier or planner precedence; or +- changes to JSON v1-v5 contracts. + +## Acceptance gate + +The implementation has passed the complete ordinary twelve-job PR matrix at workflow #817 / run `34774425487`. The coordinated development identity is now advanced to `1.13.0-Alpha-7`; the exact Alpha-7 bookkeeping head must also pass the same complete matrix before RE07 is considered fully closed. + +The next tranche is RE08 — hardening, whole-surface freeze, documentation, and release closure. diff --git a/docs/1.13.0-RE08-FREEZE-FINGERPRINTS.txt b/docs/1.13.0-RE08-FREEZE-FINGERPRINTS.txt new file mode 100644 index 000000000..993c5abcc --- /dev/null +++ b/docs/1.13.0-RE08-FREEZE-FINGERPRINTS.txt @@ -0,0 +1,15 @@ +# Icod.TermInfo 1.13.0 RE08 frozen fingerprints + +inspection-public-api exported-types 90 +inspection-public-api normalized-lf sha256 fd827a25abafb8e9ff3915567f45f9f2ec51b332bfd8a82dd4e4bca20290e764 +reconstructed-1.12-inspection-public-api normalized-lf sha256 f71501dcd27a530051c1a02083325144ced2b6173b6b967b9571c620815198f0 +reconstructed-1.11-inspection-public-api normalized-lf sha256 69c7350d5d44d502ecf1698c8fe1c1336f03d38eb1a36e36219f50ac33585a86 +json-schema-v1 normalized-lf sha256 76578f421b254802d24453af6868edaf8c23c4b78a87c7e8ef86b233ff0e8500 +json-schema-v2 normalized-lf sha256 ae4d53608881344e902f02303c71e2d432500969e60cfb005d70feea607499d0 +json-schema-v3 normalized-lf sha256 33ca95aee120f84d0d160ac189f8ddb4db183361b7bd83885c99c1c8ed355a97 +json-schema-v4 normalized-lf sha256 6383052f389d903683a9e24d55b73c97eb165db89c6ab5f26dbcb5a3c7fdda87 +json-schema-v5 normalized-lf sha256 a151c7915d8b637d8bb64ef9d649168a4c120d7b9b7104299b6a78dab1f0a394 + +# The complete 1.13 API fingerprint and version-5 schema fingerprint were derived +# from the exact qualified 1.13.0-Alpha-7 Staging package artifact produced by +# head 0167ec187442a7b4319523a9f431367c66616a41 in workflow #819 / 34794952192. diff --git a/docs/1.13.0-RE08-RELEASE-HARDENING-AND-FREEZE.md b/docs/1.13.0-RE08-RELEASE-HARDENING-AND-FREEZE.md new file mode 100644 index 000000000..911ed2fc4 --- /dev/null +++ b/docs/1.13.0-RE08-RELEASE-HARDENING-AND-FREEZE.md @@ -0,0 +1,57 @@ +# Icod.TermInfo 1.13.0 RE08 — Release Hardening and Freeze + +## Status + +RE08 assembles the frozen `1.13.0-Alpha-8` release candidate. Exact-head validation is required before this document may describe Alpha-8 as accepted. + +## Objective + +RE08 adds no runtime-evidence semantics. It freezes and hardens RE01-RE07, completes consumer/release documentation, and establishes the promotion boundary between the validated Alpha-8 candidate and stable `1.13.0`. + +Stable promotion is permitted only as coordinated release identity and stable-facing documentation. It may not change public API, JSON schema fields, runtime-observation/integration behavior, production dependencies, target frameworks, command behavior, or archive RIDs. + +## Exact Inspection API freeze + +The exact complete 1.13 Inspection public reflection manifest contains 90 exported public types and has normalized-LF SHA-256: + +```text +fd827a25abafb8e9ff3915567f45f9f2ec51b332bfd8a82dd4e4bca20290e764 +``` + +The fingerprint was generated from the exact accepted Alpha-7 Staging package/API artifact at head `0167ec187442a7b4319523a9f431367c66616a41`, workflow #819 / `34794952192`. + +Release verification first requires that whole 1.13 fingerprint. It then removes exactly the six reviewed RE06 renderer members and nine reviewed `PersistentRasterRuntime*` public type blocks and requires exact reconstruction of frozen 1.12 SHA-256 `f71501dcd27a530051c1a02083325144ced2b6173b6b967b9571c620815198f0`. The established 1.12 → 1.11 → 1.10 chain remains unchanged. + +## JSON schema freeze + +All five schema contracts are fingerprinted independently: + +```text +v1 76578f421b254802d24453af6868edaf8c23c4b78a87c7e8ef86b233ff0e8500 +v2 ae4d53608881344e902f02303c71e2d432500969e60cfb005d70feea607499d0 +v3 33ca95aee120f84d0d160ac189f8ddb4db183361b7bd83885c99c1c8ed355a97 +v4 6383052f389d903683a9e24d55b73c97eb165db89c6ab5f26dbcb5a3c7fdda87 +v5 a151c7915d8b637d8bb64ef9d649168a4c120d7b9b7104299b6a78dab1f0a394 +``` + +Versions 1-4 remain immutable historical contracts. Version 5 contains exactly the runtime observation-set and integration-audit document kinds. + +## Adversarial closure + +The RE08 closure suite re-exercises maximum observation count 4096 and rejection of 4097, source-label 256/257 boundaries, `int.MaxValue` local ordinals, immutable snapshots, family evidence-capacity exhaustion, final ordinal exhaustion, contradictory runtime observations, repeated/culture-independent rendering, production dependency topology, and RE07 package qualification topology. + +The corrected test-first RED head is `b576965197788fc0e1f9c6b1b75b8ea05ee38df1`. Workflow #821 / `34796368003` built Linux with 0 warnings and 0 errors and failed only the four intentionally absent RE08 closure surfaces: whole-1.13 verifier lock, API/schema freeze records, consolidated guide, and Alpha-8 metadata/documentation. + +## Downstream and distribution qualification + +RE07 retains a package-only adapter consumer which uses the freshly packed Inspection package plus published `Icod.Terminal 1.12.0` on net8.0/net9.0/net10.0. Production Inspection remains Terminal-free. The focused runtime-integration sample has CI-safe dry-run behavior plus an explicit live verification mode. + +Alpha-8 must rerun historical RL07/PG07 consumers, RE07 package consumer/sample, package/API/schema verification, installed-tool smoke on Windows/Linux/macOS, and all six archive RIDs: win-x64, win-arm64, linux-x64, linux-arm64, osx-x64, and osx-arm64. + +## Documentation and metadata closure + +At Alpha-8, current stable install commands remain pinned to `1.12.0`; stable `1.13.0` is not advertised as published before promotion. The 1.13 guide documents static-versus-runtime evidence, adapter ownership, represented integration issues, planner delegation, JSON v5, package/sample use, and explicit exclusions. + +## Promotion boundary + +After the exact Alpha-8 head passes the complete Staging matrix, stable `1.13.0` may change coordinated version identity and stable-facing status only. It requires a fresh full validation. No merge, tag, registry publication, or release action is performed by the Alpha-8 freeze. diff --git a/docs/1.13.0-RELEASE-AUDIT.md b/docs/1.13.0-RELEASE-AUDIT.md new file mode 100644 index 000000000..c5f176858 --- /dev/null +++ b/docs/1.13.0-RELEASE-AUDIT.md @@ -0,0 +1,83 @@ +# Icod.TermInfo 1.13.0 Release Audit + +## Candidate identity + +RE08 completed and exact `1.13.0-Alpha-8` was accepted. Coordinated stable `1.13.0` has now passed its full product qualification and is release-ready subject only to final documentation-head validation, merge, tag, and publication gates. + +The accepted pre-freeze baseline is exact `1.13.0-Alpha-7` head: + +```text +0167ec187442a7b4319523a9f431367c66616a41 +``` + +Pull-request workflow #819 / run `34794952192` completed successfully across all 12 jobs: Windows/Linux/macOS Build+Test, Windows Inspection compatibility reconstruction, coordinated package verification including the RE07 package-only consumer and sample, all three installed-tool smokes, and all six archive RID smokes. + +## RE01-RE07 delivered contract + +- RE01 froze the runtime-observation outcome vocabulary and resource bounds. +- RE02 added immutable lifecycle/placement observations and a canonical bounded observation set. +- RE03 added deterministic atomic-per-family mapping of conclusive observations to existing `Verified` evidence, safe final ordinals, inconclusive retention, and represented capacity/ordinal issues. +- RE04 proved frozen classifier precedence and contradiction behavior through integration without duplicating classifier policy. +- RE05 added planner-delegating lifecycle/placement replanning conveniences. +- RE06 added deterministic JSON version 5 with exactly `persistentRasterRuntimeObservationSet` and `persistentRasterRuntimeIntegration` documents. +- RE07 qualified the caller-owned adapter boundary against published `Icod.Terminal 1.12.0`, migrated samples away from manual final `Verified` evidence construction, and retained a Terminal-free production Inspection package. + +## Freeze inputs + +The complete 1.13 Inspection public manifest was generated from the exact Alpha-7 Staging package/API artifact, not reconstructed by hand. It contains 90 exported public types and has normalized-LF SHA-256: + +```text +fd827a25abafb8e9ff3915567f45f9f2ec51b332bfd8a82dd4e4bca20290e764 +``` + +The five normalized-LF schema fingerprints are recorded in `docs/1.13.0-RE08-FREEZE-FINGERPRINTS.txt`. Versions 1-4 retain their historical hashes; version 5 is: + +```text +a151c7915d8b637d8bb64ef9d649168a4c120d7b9b7104299b6a78dab1f0a394 +``` + +## RE08 test-first evidence + +Initial RE08 test head `37846bcf363f4e3b1392ee68fd40bafe32f36def` exposed one repository-formatting defect in the test itself. That test-only defect was corrected at: + +```text +b576965197788fc0e1f9c6b1b75b8ea05ee38df1 +``` + +Workflow #821 / run `34796368003` then produced the clean RED witness: Linux Build succeeded with 0 warnings / 0 errors; ordinary repository suites remained green; the Inspection suite failed exactly four closure assertions on every supported TFM because RE08 freeze records, whole-surface verifier lock, consolidated guide, and Alpha-8 release metadata were intentionally absent. + +## Alpha-8 closure candidate + +The RE08 freeze foundation and whole-surface verifier were committed at `cf6a89886cfebdb01ce3d20a8cbe8288340162ee`. Release-facing README, versioning, compatibility, and long-range-roadmap updates were then applied by exact anchored insertion and committed at `6d3f024caa14ba920b3091a57ab9ee9703f05366`; the one-shot helper removed itself in that same commit. No production runtime semantics or public surface changed during these closure edits. + +## Alpha-8 acceptance + +Exact Alpha-8 product head: + +```text +f236c33d8239e80379bf8cf0f1123abd6c93c3cb +``` + +passed exact-head qualification run `34797445315` with all 12 ordinary pull-request-equivalent jobs green. The qualification workflow pinned every checkout to that exact product head because a stale cancelled PR-run concurrency slot prevented the ordinary workflow from acquiring a runner. The matrix itself was unchanged in substance: Windows/Linux/macOS Build+Test, Windows whole-1.13 plus reconstructed historical Inspection compatibility, coordinated package verification, the isolated DA07 consumer, all three installed-tool smokes, and all six archive RID smokes. + +The Windows verifier proved exact whole-surface 1.13 SHA-256 `fd827a25abafb8e9ff3915567f45f9f2ec51b332bfd8a82dd4e4bca20290e764`, then reconstructed exact 1.12 SHA-256 `f71501dcd27a530051c1a02083325144ced2b6173b6b967b9571c620815198f0`, exact 1.11 SHA-256 `69c7350d5d44d502ecf1698c8fe1c1336f03d38eb1a36e36219f50ac33585a86`, and the frozen 1.10 baseline. + +## Stable promotion validation + +Stable promotion changed coordinated version identity from `1.13.0-Alpha-8` to `1.13.0` and updated current-facing release documentation/tests only. It introduced no feature semantics, public API, schema, production dependency, target-framework, command, package-consumer-topology, or archive-RID change. + +The first stable qualification attempt correctly exposed one stale historical completion-gate assertion which still required root README install commands to use `1.12.0`. That test ownership was advanced to the current stable line without weakening its package-version or policy checks. + +Exact corrected stable product head: + +```text +6e9217b16c3023fb10fa34dbaa74afe48448d858 +``` + +passed exact-head qualification run `34799272472` with all 12 jobs green: Windows/Linux/macOS Build+Test, Windows exact whole-1.13 and historical Inspection compatibility, coordinated package packing and verification, isolated package consumption, all three installed-tool smokes, and all six matching archive RID smokes. + +This release-audit/documentation closure changes no product API, schema, dependency, target framework, command behavior, package-consumer topology, or archive contents. Its resulting exact head must receive one final full qualification before PR #43 may be considered merge-ready. + +## Stable promotion rule + +After Alpha-8 acceptance, stable `1.13.0` may change coordinated version identity and stable-facing release status only. It may not alter feature semantics, public API, schema, production dependencies, target frameworks, command behavior, package-consumer topology, or archive RIDs. Stable promotion requires a fresh full matrix. This audit does not authorize merge, tag creation, registry publication, or release publication. diff --git a/docs/COMPATIBILITY.md b/docs/COMPATIBILITY.md index 3c6883a6c..f4221d445 100644 --- a/docs/COMPATIBILITY.md +++ b/docs/COMPATIBILITY.md @@ -6,6 +6,30 @@ This document defines the supported 1.x compatibility boundary for `Icod.TermInfo.Inspection` package, and beginning with 1.6 the optional `Icod.TermInfo.Termcap` package. +## 1.13 compatibility freeze + +Version 1.13 is additive above the stable 1.12 boundary. RE08 freezes the complete +1.13 Inspection reflection manifest at 90 exported public types with normalized-LF +SHA-256 +`fd827a25abafb8e9ff3915567f45f9f2ec51b332bfd8a82dd4e4bca20290e764` +and requires equivalent public API across `net8.0`, `net9.0`, and `net10.0`. +The verifier first proves that complete 1.13 surface, then removes exactly the +reviewed RE06 renderer-member delta and cumulative `PersistentRasterRuntime*` +type delta to reconstruct frozen 1.12 SHA-256 +`f71501dcd27a530051c1a02083325144ced2b6173b6b967b9571c620815198f0`. +The established 1.12 -> 1.11 -> 1.10 reconstruction chain remains unchanged. + +Version 1.13 adds only protocol-neutral caller-owned runtime observation and +evidence-integration semantics to Inspection. It does not add live probing, +backend ranking, protocol negotiation, raw protocol responses, terminal session +or resource identity, or a production dependency on `Icod.Terminal`. + +JSON schema versions 1 through 4 remain immutable historical contracts. Version +5 is additive and contains exactly `persistentRasterRuntimeObservationSet` and +`persistentRasterRuntimeIntegration`. Stable 1.13 promotion may not change any +frozen schema, the exact 1.13 public surface, package dependency direction, +target frameworks, command semantics, package-consumer topology, or archive RIDs. + ## 1.12 compatibility freeze Version 1.12 is additive above the stable 1.11 boundary. PG08 freezes the diff --git a/docs/Icod.TermInfo.Inspection.schema.v5.json b/docs/Icod.TermInfo.Inspection.schema.v5.json new file mode 100644 index 000000000..e36c54654 --- /dev/null +++ b/docs/Icod.TermInfo.Inspection.schema.v5.json @@ -0,0 +1,427 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "urn:icod:terminfo:inspection:json:5", + "title": "Icod.TermInfo.Inspection persistent raster runtime JSON version 5", + "oneOf": [ + { + "$ref": "#/$defs/persistentRasterRuntimeObservationSetDocument" + }, + { + "$ref": "#/$defs/persistentRasterRuntimeIntegrationDocument" + } + ], + "$defs": { + "runtimeOutcome": { + "type": "string", + "enum": [ + "supported", + "unsupported", + "inconclusive" + ] + }, + "supportStatus": { + "type": "string", + "enum": [ + "unknown", + "supported", + "unsupported", + "contradicted" + ] + }, + "lifecycleSubject": { + "type": "string", + "enum": [ + "rasterDisplay", + "persistentUpload", + "acknowledgedUpload", + "placementCreation", + "multiplePlacements", + "placementUpdate", + "placementDeletion", + "resourceDeletion" + ] + }, + "placementSubject": { + "type": "string", + "enum": [ + "sourceRectangle", + "signedZOrder" + ] + }, + "sourceLabel": { + "type": "string", + "minLength": 1, + "maxLength": 256 + }, + "sourceOrdinal": { + "type": "integer", + "minimum": 0, + "maximum": 2147483647 + }, + "lifecycleObservation": { + "type": "object", + "additionalProperties": false, + "required": [ + "subject", + "outcome", + "sourceLabel", + "sourceOrdinal" + ], + "properties": { + "subject": { + "$ref": "#/$defs/lifecycleSubject" + }, + "outcome": { + "$ref": "#/$defs/runtimeOutcome" + }, + "sourceLabel": { + "$ref": "#/$defs/sourceLabel" + }, + "sourceOrdinal": { + "$ref": "#/$defs/sourceOrdinal" + } + } + }, + "placementObservation": { + "type": "object", + "additionalProperties": false, + "required": [ + "subject", + "outcome", + "sourceLabel", + "sourceOrdinal" + ], + "properties": { + "subject": { + "$ref": "#/$defs/placementSubject" + }, + "outcome": { + "$ref": "#/$defs/runtimeOutcome" + }, + "sourceLabel": { + "$ref": "#/$defs/sourceLabel" + }, + "sourceOrdinal": { + "$ref": "#/$defs/sourceOrdinal" + } + } + }, + "lifecycleEvidence": { + "type": "object", + "additionalProperties": false, + "required": [ + "subject", + "isPositive", + "kind", + "sourceLabel", + "sourceOrdinal" + ], + "properties": { + "subject": { + "$ref": "#/$defs/lifecycleSubject" + }, + "isPositive": { + "type": "boolean" + }, + "kind": { + "const": "verified" + }, + "sourceLabel": { + "$ref": "#/$defs/sourceLabel" + }, + "sourceOrdinal": { + "$ref": "#/$defs/sourceOrdinal" + } + } + }, + "placementEvidence": { + "type": "object", + "additionalProperties": false, + "required": [ + "subject", + "isPositive", + "kind", + "sourceLabel", + "sourceOrdinal" + ], + "properties": { + "subject": { + "$ref": "#/$defs/placementSubject" + }, + "isPositive": { + "type": "boolean" + }, + "kind": { + "const": "verified" + }, + "sourceLabel": { + "$ref": "#/$defs/sourceLabel" + }, + "sourceOrdinal": { + "$ref": "#/$defs/sourceOrdinal" + } + } + }, + "integrationIssue": { + "type": "object", + "additionalProperties": false, + "required": [ + "kind", + "existingEvidenceCount", + "requestedImportCount" + ], + "properties": { + "kind": { + "type": "string", + "enum": [ + "lifecycleEvidenceCapacityExhausted", + "placementEvidenceCapacityExhausted", + "lifecycleOrdinalSpaceExhausted", + "placementOrdinalSpaceExhausted" + ] + }, + "existingEvidenceCount": { + "type": "integer", + "minimum": 0, + "maximum": 4096 + }, + "requestedImportCount": { + "type": "integer", + "minimum": 0, + "maximum": 4096 + } + } + }, + "lifecycleStates": { + "type": "object", + "additionalProperties": false, + "required": [ + "rasterDisplay", + "persistentUpload", + "acknowledgedUpload", + "placementCreation", + "multiplePlacements", + "placementUpdate", + "placementDeletion", + "resourceDeletion" + ], + "properties": { + "rasterDisplay": { + "$ref": "#/$defs/supportStatus" + }, + "persistentUpload": { + "$ref": "#/$defs/supportStatus" + }, + "acknowledgedUpload": { + "$ref": "#/$defs/supportStatus" + }, + "placementCreation": { + "$ref": "#/$defs/supportStatus" + }, + "multiplePlacements": { + "$ref": "#/$defs/supportStatus" + }, + "placementUpdate": { + "$ref": "#/$defs/supportStatus" + }, + "placementDeletion": { + "$ref": "#/$defs/supportStatus" + }, + "resourceDeletion": { + "$ref": "#/$defs/supportStatus" + } + } + }, + "placementStates": { + "type": "object", + "additionalProperties": false, + "required": [ + "sourceRectangle", + "signedZOrder" + ], + "properties": { + "sourceRectangle": { + "$ref": "#/$defs/supportStatus" + }, + "signedZOrder": { + "$ref": "#/$defs/supportStatus" + } + } + }, + "persistentRasterRuntimeObservationSetDocument": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema", + "schemaVersion", + "documentKind", + "data" + ], + "properties": { + "schema": { + "const": "urn:icod:terminfo:inspection:json:5" + }, + "schemaVersion": { + "const": 5 + }, + "documentKind": { + "const": "persistentRasterRuntimeObservationSet" + }, + "data": { + "type": "object", + "additionalProperties": false, + "required": [ + "observationCount", + "lifecycleObservationCount", + "placementObservationCount", + "lifecycleObservations", + "placementObservations" + ], + "properties": { + "observationCount": { + "type": "integer", + "minimum": 0, + "maximum": 4096 + }, + "lifecycleObservationCount": { + "type": "integer", + "minimum": 0, + "maximum": 4096 + }, + "placementObservationCount": { + "type": "integer", + "minimum": 0, + "maximum": 4096 + }, + "lifecycleObservations": { + "type": "array", + "maxItems": 4096, + "items": { + "$ref": "#/$defs/lifecycleObservation" + } + }, + "placementObservations": { + "type": "array", + "maxItems": 4096, + "items": { + "$ref": "#/$defs/placementObservation" + } + } + } + } + } + }, + "persistentRasterRuntimeIntegrationDocument": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema", + "schemaVersion", + "documentKind", + "data" + ], + "properties": { + "schema": { + "const": "urn:icod:terminfo:inspection:json:5" + }, + "schemaVersion": { + "const": 5 + }, + "documentKind": { + "const": "persistentRasterRuntimeIntegration" + }, + "data": { + "type": "object", + "additionalProperties": false, + "required": [ + "succeeded", + "observationCount", + "observations", + "importedLifecycleEvidence", + "importedPlacementEvidence", + "inconclusiveLifecycleObservations", + "inconclusivePlacementObservations", + "issues", + "lifecycleStates", + "placementStates" + ], + "properties": { + "succeeded": { + "type": "boolean" + }, + "observationCount": { + "type": "integer", + "minimum": 0, + "maximum": 4096 + }, + "observations": { + "type": "object", + "additionalProperties": false, + "required": [ + "lifecycle", + "placement" + ], + "properties": { + "lifecycle": { + "type": "array", + "maxItems": 4096, + "items": { + "$ref": "#/$defs/lifecycleObservation" + } + }, + "placement": { + "type": "array", + "maxItems": 4096, + "items": { + "$ref": "#/$defs/placementObservation" + } + } + } + }, + "importedLifecycleEvidence": { + "type": "array", + "maxItems": 4096, + "items": { + "$ref": "#/$defs/lifecycleEvidence" + } + }, + "importedPlacementEvidence": { + "type": "array", + "maxItems": 4096, + "items": { + "$ref": "#/$defs/placementEvidence" + } + }, + "inconclusiveLifecycleObservations": { + "type": "array", + "maxItems": 4096, + "items": { + "$ref": "#/$defs/lifecycleObservation" + } + }, + "inconclusivePlacementObservations": { + "type": "array", + "maxItems": 4096, + "items": { + "$ref": "#/$defs/placementObservation" + } + }, + "issues": { + "type": "array", + "maxItems": 2, + "items": { + "$ref": "#/$defs/integrationIssue" + } + }, + "lifecycleStates": { + "$ref": "#/$defs/lifecycleStates" + }, + "placementStates": { + "$ref": "#/$defs/placementStates" + } + } + } + } + } + } +} diff --git a/docs/VERSIONING.md b/docs/VERSIONING.md index 1075b3528..2746bce33 100644 --- a/docs/VERSIONING.md +++ b/docs/VERSIONING.md @@ -3,6 +3,33 @@ The `Icod.TermInfo` package family follows Semantic Versioning for its public package contracts. +## 1.13 release line + +The RE01-RE08 development sequence is `1.13.0-Alpha-1` through +`1.13.0-Alpha-8`. Version 1.13 adds compatible public API only to +`Icod.TermInfo.Inspection` for caller-owned persistent-raster runtime observations, +deterministic integration into the frozen 1.11 lifecycle and 1.12 placement +evidence models, planner-delegating replanning, and additive version-5 JSON. +Runtime, Source, Compiler, and Termcap public APIs remain frozen. + +RE08 freezes the complete 1.13 Inspection reflection manifest at 90 exported +public types with normalized-LF SHA-256 +`fd827a25abafb8e9ff3915567f45f9f2ec51b332bfd8a82dd4e4bca20290e764`. +Release verification first requires that exact current 1.13 surface, then removes +only the reviewed six RE06 renderer members and nine cumulative +`PersistentRasterRuntime*` type blocks to reproduce frozen 1.12 SHA-256 +`f71501dcd27a530051c1a02083325144ced2b6173b6b967b9571c620815198f0`. +JSON versions 1 through 4 remain immutable; version 5 contains exactly the +runtime observation-set and integration-audit document kinds. + +After the exact Alpha-8 head passes the complete Staging package, historical and +RE07 package-consumer/sample, installed-tool, and six-RID archive gates, stable +`1.13.0` is a promotion-only transition. Promotion may change coordinated +release identity and stable-facing documentation only; it may not introduce +feature semantics, public API, schema fields, production dependencies, target +frameworks, command behavior, or archive RIDs, and it requires its own fresh full +validation. + ## 1.12 release line The PG01-PG08 development sequence is `1.12.0-Alpha-1` through diff --git a/docs/superpowers/plans/2026-09-13-1.13.0-runtime-evidence-interchange.md b/docs/superpowers/plans/2026-09-13-1.13.0-runtime-evidence-interchange.md new file mode 100644 index 000000000..63dc2d2cb --- /dev/null +++ b/docs/superpowers/plans/2026-09-13-1.13.0-runtime-evidence-interchange.md @@ -0,0 +1,528 @@ +# Icod.TermInfo 1.13.0 Runtime Evidence Interchange Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add bounded protocol-neutral persistent-raster runtime observations and deterministic integration into the frozen 1.11 lifecycle and 1.12 placement evidence/classification/planning models, then expose the handoff through additive JSON v5 and package-only Terminal qualification. + +**Architecture:** External consumers remain responsible for live verification. `Icod.TermInfo.Inspection` accepts immutable runtime observations, maps conclusive observations to the existing `Verified` evidence kinds, assigns safe final evidence ordinals without rewriting existing evidence, delegates classification and planning to the frozen 1.11/1.12 engines, and retains inconclusive/failed integration outcomes as deterministic audit evidence. All new 1.13 public types use the `PersistentRasterRuntime*` prefix so compatibility verification can remove the reviewed 1.13 delta and reconstruct the exact frozen 1.12 public API. + +**Tech Stack:** C# 13, .NET 8/9/10, xUnit 2.9, `Icod.TermInfo.Inspection`, existing bounded/deterministic JSON infrastructure, PowerShell 5.1-compatible release verification, NuGet package-only consumers, GitHub Actions Windows/Linux/macOS plus six archive RIDs. + +**Spec:** `docs/superpowers/specs/2026-09-13-1.13.0-runtime-evidence-interchange-design.md` + +## Global Constraints + +- Development sequence is `1.13.0-Alpha-1` through `1.13.0-Alpha-8`. +- Primary implementation package is `Icod.TermInfo.Inspection`. +- Baseline is stable `1.12.0` with complete Inspection API SHA-256 `f71501dcd27a530051c1a02083325144ced2b6173b6b967b9571c620815198f0` and 81 exported public types. +- Runtime, Source, Compiler, Termcap, and Inspection contracts through 1.12 remain source/binary compatible except unavoidable defect corrections. +- `PersistentRasterLifecycleEvidenceSubject` remains exactly eight values `0..7`; `PersistentRasterPlacementSubject` remains exactly `SourceRectangle = 0`, `SignedZOrder = 1`. +- `PersistentRasterLifecycleEvidenceKind` and `PersistentRasterPlacementEvidenceKind` remain exactly `CapabilityDerived = 0`, `Declared = 1`, `Verified = 2`. +- Frozen lifecycle and placement classifiers/planners remain authoritative; 1.13 adds no second precedence algorithm and no replacement planner/status family. +- JSON v1-v4 remain immutable with frozen schema hashes recorded in `docs/1.12.0-PG08-FREEZE-FINGERPRINTS.txt`; 1.13 adds JSON v5 only. +- JSON v5 identifier is `urn:icod:terminfo:inspection:json:5` and it contains exactly `persistentRasterRuntimeObservationSet` and `persistentRasterRuntimeIntegration`. +- Production `Icod.TermInfo` and `Icod.TermInfo.Inspection` SHALL NOT reference `Icod.Terminal`. +- No protocol/backend identifiers, terminal brand heuristics, session/resource/placement identities, endpoint availability, timestamps, raw probe responses, ranking, or negotiation enter the 1.13 production model. +- Observation count defaults to 256 and may be configured up to 4096, matching existing lifecycle/placement evidence ceilings. +- Runtime observation source labels are bounded to 256 UTF-16 code units; null/empty/whitespace, longer labels, negative local ordinals, undefined enums, null elements, and over-bound observation sets are argument errors. +- Final evidence integration uses the existing maximum supported family count of 4096; capacity and final-ordinal exhaustion are represented integration issues, not truncation and not partial family append. +- C# code follows repository 1TBS conventions and existing nullable/XML-doc/package-validation policy. +- Every tranche uses an exact RED test-only witness before production implementation and an exact-head ordinary PR matrix before acceptance. + +--- + +## File Structure + +New production files are split by one responsibility each: + +- `Icod.TermInfo.Inspection/src/PersistentRasterRuntimeObservationOutcome.cs` — frozen three-state runtime outcome vocabulary. +- `Icod.TermInfo.Inspection/src/PersistentRasterRuntimeObservationOptions.cs` — observation count/source-label limits. +- `Icod.TermInfo.Inspection/src/PersistentRasterRuntimeLifecycleObservation.cs` — one immutable lifecycle runtime observation. +- `Icod.TermInfo.Inspection/src/PersistentRasterRuntimePlacementObservation.cs` — one immutable placement runtime observation. +- `Icod.TermInfo.Inspection/src/PersistentRasterRuntimeObservationSet.cs` — immutable canonical bounded aggregate. +- `Icod.TermInfo.Inspection/src/PersistentRasterRuntimeIntegrationIssueKind.cs` — represented integration limitation vocabulary. +- `Icod.TermInfo.Inspection/src/PersistentRasterRuntimeIntegrationIssue.cs` — one immutable structured integration issue. +- `Icod.TermInfo.Inspection/src/PersistentRasterRuntimeIntegrationResult.cs` — immutable observation/evidence/profile audit result and RE05 planner conveniences. +- `Icod.TermInfo.Inspection/src/PersistentRasterRuntimeEvidenceIntegrator.cs` — atomic-per-family evidence mapping, safe ordinal assignment, and classifier delegation. +- `Icod.TermInfo.Inspection/src/TermInfoJsonRenderer.PersistentRasterRuntime.cs` — additive JSON v5 rendering. +- `docs/Icod.TermInfo.Inspection.schema.v5.json` — exactly the two v5 document kinds. +- `tests/Icod.TermInfo.Inspection.Tests/src/RE*.cs` — tranche-scoped TDD contracts. +- `docs/1.13.0-RE*.md` — tranche contract/qualification records. +- `docs/1.13.0-RE01-INSPECTION-PUBLIC-API-ADDITIONS.txt` — cumulative reviewed `PersistentRasterRuntime*` public types. +- `docs/1.13.0-RE06-INSPECTION-PUBLIC-API-ADDITIVE-MEMBERS.txt` — exact v5 `TermInfoJsonRenderer` public manifest lines. + +Existing compatibility, package, sample, and documentation files change only in the tranche that owns the change. + +--- + +### Task 1: RE01 architecture, vocabulary, bounds, and public API regret gate + +**Files:** +- Create: `tests/Icod.TermInfo.Inspection.Tests/src/RE01PersistentRasterRuntimeEvidenceContractTests.cs` +- Create: `Icod.TermInfo.Inspection/src/PersistentRasterRuntimeObservationOutcome.cs` +- Create: `Icod.TermInfo.Inspection/src/PersistentRasterRuntimeObservationOptions.cs` +- Create: `docs/1.13.0-RE01-INSPECTION-PUBLIC-API-ADDITIONS.txt` +- Modify: `.github/scripts/verify-inspection-compatibility.ps1` +- Modify: `Directory.Build.props` +- Create: `docs/1.13.0-RE01-RUNTIME-EVIDENCE-CONTRACT-AND-PUBLIC-API-REGRET-GATE.md` + +**Interfaces:** +- Consumes: frozen 1.12 API hash, 1.11 lifecycle subject/evidence-kind numerics, 1.12 placement subject/evidence-kind numerics, JSON v4 identity. +- Produces: + +```csharp +public enum PersistentRasterRuntimeObservationOutcome { + Supported = 0, + Unsupported = 1, + Inconclusive = 2, +} +``` + +- Produces `PersistentRasterRuntimeObservationOptions` with `DefaultMaximumObservationCount = 256`, `MaximumSupportedObservationCount = 4096`, `MaximumSourceLabelLength = 256`, and validated `MaximumObservationCount`. +- Does not create a `PersistentRasterRuntimeSubject` union enum. + +- [ ] **Step 1: Add the test-only RED contract** + +Create tests that compile against the two planned RE01 types and freeze existing boundaries. The core assertions must include: + +```csharp +[Fact] +public void RuntimeOutcomeMembershipAndNumericsAreFrozen() { + Assert.Equal( + new[] { + PersistentRasterRuntimeObservationOutcome.Supported, + PersistentRasterRuntimeObservationOutcome.Unsupported, + PersistentRasterRuntimeObservationOutcome.Inconclusive, + }, + Enum.GetValues() + ); + Assert.Equal( 0, (int)PersistentRasterRuntimeObservationOutcome.Supported ); + Assert.Equal( 1, (int)PersistentRasterRuntimeObservationOutcome.Unsupported ); + Assert.Equal( 2, (int)PersistentRasterRuntimeObservationOutcome.Inconclusive ); +} + +[Fact] +public void RuntimeObservationBoundsAreFrozen() { + PersistentRasterRuntimeObservationOptions defaults = new(); + Assert.Equal( 256, PersistentRasterRuntimeObservationOptions.DefaultMaximumObservationCount ); + Assert.Equal( 4096, PersistentRasterRuntimeObservationOptions.MaximumSupportedObservationCount ); + Assert.Equal( 256, PersistentRasterRuntimeObservationOptions.MaximumSourceLabelLength ); + Assert.Equal( 256, defaults.MaximumObservationCount ); + Assert.Throws( + () => new PersistentRasterRuntimeObservationOptions( 0 ) + ); + Assert.Throws( + () => new PersistentRasterRuntimeObservationOptions( 4097 ) + ); +} +``` + +Also reassert exact frozen lifecycle subjects/evidence-kind numerics, exact placement subjects/evidence-kind numerics, v4 schema identifier/version, and XML-project proof that production Inspection has no `Icod.Terminal` package/project reference. Assert by reflection that there is no exported type named `Icod.TermInfo.Inspection.PersistentRasterRuntimeSubject`. + +- [ ] **Step 2: Push only the RE01 test file and verify RED** + +Expected Build failure on all Inspection TFMs: missing `PersistentRasterRuntimeObservationOutcome` and `PersistentRasterRuntimeObservationOptions`. No production file is added before this witness. + +- [ ] **Step 3: Implement the minimum RE01 vocabulary and options** + +Add documented public enum exactly as above. Add options modeled on existing evidence options: + +```csharp +public sealed class PersistentRasterRuntimeObservationOptions { + public const int DefaultMaximumObservationCount = 256; + public const int MaximumSupportedObservationCount = 4096; + public const int MaximumSourceLabelLength = 256; + + public PersistentRasterRuntimeObservationOptions() + : this( DefaultMaximumObservationCount ) { + } + + public PersistentRasterRuntimeObservationOptions( + int maximumObservationCount + ) { + if ( + maximumObservationCount < 1 + || maximumObservationCount > MaximumSupportedObservationCount + ) { + throw new ArgumentOutOfRangeException( nameof( maximumObservationCount ) ); + } + MaximumObservationCount = maximumObservationCount; + } + + public int MaximumObservationCount { get; } +} +``` + +Use normal repository XML documentation and expanded property formatting rather than the compact property form in this plan snippet. + +- [ ] **Step 4: Establish the cumulative 1.13 API ledger and reconstruct 1.12** + +Create `docs/1.13.0-RE01-INSPECTION-PUBLIC-API-ADDITIONS.txt` with exactly the two RE01 types. Extend `verify-inspection-compatibility.ps1` so current 1.13 assemblies are no longer required to equal the 1.12 whole-surface hash before filtering. Instead: + +1. read approved 1.13 types using required prefix `Icod.TermInfo.Inspection.PersistentRasterRuntime`; +2. remove those reviewed type blocks from the current manifest; +3. require the resulting manifest SHA-256 to equal frozen 1.12 `f71501...198f0`; +4. run the established 1.12 -> 1.11 -> 1.10 reconstruction against that reconstructed 1.12 manifest. + +Do not weaken the existing Windows PowerShell 5.1-compatible hashing/string APIs. + +- [ ] **Step 5: Advance coordinated version and record RE01** + +Set: + +```xml +1.13.0-Alpha-1 +``` + +Document exact outcome numerics, bounds, prefix rule, frozen old enums/v4, no-unified-subject decision, dependency boundary, explicit exclusions, RED witness, and RE02 handoff. + +- [ ] **Step 6: Verify GREEN on exact Alpha-1 head** + +Require Windows/Linux/macOS Build+Test, Windows PowerShell compatibility reconstruction, package/API verification, installed-tool smokes, and all six archive RIDs green before RE01 is accepted. + +--- + +### Task 2: RE02 immutable runtime observations and canonical observation set + +**Files:** +- Create: `tests/Icod.TermInfo.Inspection.Tests/src/RE02PersistentRasterRuntimeObservationTests.cs` +- Create: `Icod.TermInfo.Inspection/src/PersistentRasterRuntimeLifecycleObservation.cs` +- Create: `Icod.TermInfo.Inspection/src/PersistentRasterRuntimePlacementObservation.cs` +- Create: `Icod.TermInfo.Inspection/src/PersistentRasterRuntimeObservationSet.cs` +- Modify: `docs/1.13.0-RE01-INSPECTION-PUBLIC-API-ADDITIONS.txt` +- Modify: `Directory.Build.props` +- Create: `docs/1.13.0-RE02-IMMUTABLE-RUNTIME-OBSERVATIONS.md` + +**Interfaces:** +- Lifecycle observation constructor: + +```csharp +PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject subject, + PersistentRasterRuntimeObservationOutcome outcome, + string sourceLabel, + int sourceOrdinal +) +``` + +- Placement observation constructor mirrors it with `PersistentRasterPlacementSubject`. +- Observation set constructor: + +```csharp +PersistentRasterRuntimeObservationSet( + IEnumerable lifecycleObservations, + IEnumerable placementObservations, + PersistentRasterRuntimeObservationOptions? options = null +) +``` + +- [ ] **Step 1: Write failing constructor/validation/snapshot tests** + +Cover exact field preservation, undefined subject/outcome rejection, negative ordinal rejection, source-label lengths 1 and 256 accepted, 257 rejected, whitespace rejected, null collection/null element rejection, empty set allowed, maximum total count 4096 accepted under explicit options, and 4097 rejected. + +- [ ] **Step 2: Write failing canonical-order/culture tests** + +For lifecycle and placement collections independently, require ordering by subject numeric, source label with `StringComparer.Ordinal`, source-local ordinal, then outcome numeric. Reverse/shuffle input and switch cultures (`tr-TR`, `fr-FR`) without changing snapshots. + +- [ ] **Step 3: Verify RED because all three observation types are absent** + +Commit tests only and capture exact CI missing-type failures. + +- [ ] **Step 4: Implement immutable observation values and set** + +Each observation validates its own fields. `PersistentRasterRuntimeObservationSet` snapshots each input exactly once, enforces the configured **combined** count (`lifecycle + placement`), canonicalizes each family, and exposes immutable `LifecycleObservations`, `PlacementObservations`, and `Count`. + +- [ ] **Step 5: Advance cumulative API ledger and version** + +Add the three RE02 types to the 1.13 ledger; advance to `1.13.0-Alpha-2`; record the exact ordering/bounds contract. + +- [ ] **Step 6: Require exact-head full matrix green** + +The 1.12 reconstructed API SHA must remain exact after removing the cumulative 1.13 type ledger. + +--- + +### Task 3: RE03 deterministic atomic evidence integration + +**Files:** +- Create: `tests/Icod.TermInfo.Inspection.Tests/src/RE03PersistentRasterRuntimeEvidenceIntegrationTests.cs` +- Create: `Icod.TermInfo.Inspection/src/PersistentRasterRuntimeIntegrationIssueKind.cs` +- Create: `Icod.TermInfo.Inspection/src/PersistentRasterRuntimeIntegrationIssue.cs` +- Create: `Icod.TermInfo.Inspection/src/PersistentRasterRuntimeIntegrationResult.cs` +- Create: `Icod.TermInfo.Inspection/src/PersistentRasterRuntimeEvidenceIntegrator.cs` +- Modify: `docs/1.13.0-RE01-INSPECTION-PUBLIC-API-ADDITIONS.txt` +- Modify: `Directory.Build.props` +- Create: `docs/1.13.0-RE03-DETERMINISTIC-EVIDENCE-INTEGRATION.md` + +**Interfaces:** + +```csharp +public enum PersistentRasterRuntimeIntegrationIssueKind { + LifecycleEvidenceCapacityExhausted = 0, + PlacementEvidenceCapacityExhausted = 1, + LifecycleOrdinalSpaceExhausted = 2, + PlacementOrdinalSpaceExhausted = 3, +} + +public static PersistentRasterRuntimeIntegrationResult Integrate( + PersistentRasterLifecycleProfile lifecycleProfile, + PersistentRasterPlacementProfile placementProfile, + PersistentRasterRuntimeObservationSet observations +) +``` + +Issue values expose `Kind`, `ExistingEvidenceCount`, and `RequestedImportCount`. Result exposes the exact original observation set, imported evidence collections, inconclusive observation collections, resulting lifecycle/placement profiles, issues, and `Succeeded`. + +- [ ] **Step 1: Write failing mapping tests** + +Supported lifecycle/placement observations must map to positive existing `Verified` evidence; Unsupported maps to negative existing `Verified`; Inconclusive maps to no classifier evidence and remains in the appropriate inconclusive collection. Source labels copy exactly. + +- [ ] **Step 2: Write failing safe ordinal tests** + +With existing evidence ordinal 10 and two conclusive canonical imports, require final imported ordinals 11 and 12. Existing evidence objects/ordinals remain unchanged. Empty existing evidence starts imports at 0. + +- [ ] **Step 3: Write failing capacity/ordinal atomicity tests** + +Use existing family evidence at 4096 to trigger family capacity issue; use existing `int.MaxValue` ordinal plus a conclusive import to trigger family ordinal issue. Require zero imported evidence for the failed family and an unchanged classified family profile. The other family may still integrate successfully. + +- [ ] **Step 4: Verify RED because integration/result/issue types are absent** + +Commit tests only. + +- [ ] **Step 5: Implement minimal atomic-per-family integrator** + +Precompute conclusive counts, check `existing.Count + imports <= 4096`, check enough consecutive `int` ordinal space, build all mapped evidence only after checks succeed, concatenate without renumbering existing evidence, and invoke the existing classifiers with explicit maximum-supported evidence options. Do not implement support precedence inside the integrator. + +- [ ] **Step 6: Update ledger/version/docs and verify GREEN** + +Add four new public types to the cumulative ledger, advance to `1.13.0-Alpha-3`, document atomicity, then require full exact-head matrix green. + +--- + +### Task 4: RE04 classifier/contradiction behavior through runtime integration + +**Files:** +- Create: `tests/Icod.TermInfo.Inspection.Tests/src/RE04PersistentRasterRuntimeClassificationIntegrationTests.cs` +- Modify only `PersistentRasterRuntimeEvidenceIntegrator.cs`/result internals if tests expose a defect; no new public type is planned. +- Modify: `Directory.Build.props` +- Create: `docs/1.13.0-RE04-CLASSIFICATION-AND-CONTRADICTION-INTEGRATION.md` + +**Interfaces:** +- Consumes: RE03 integration and the frozen classifiers. +- Produces no new classifier/status vocabulary. + +- [ ] **Step 1: Write tests for precedence delegation** + +Cover static CapabilityDerived positive + runtime Unsupported => final Unsupported; static Declared negative + runtime Supported => Supported; existing Verified positive + imported Verified negative => Contradicted; existing Verified negative + imported Verified positive => Contradicted; repeated compatible runtime observations remain Supported/Unsupported. + +- [ ] **Step 2: Write inconclusive-only and mixed-family tests** + +Inconclusive-only imports must preserve original support states while remaining visible. A failed lifecycle family plus successful placement family and the inverse must retain independent family results/issues. + +- [ ] **Step 3: Verify the new tests fail for any missing RE03 behavior, then make the smallest implementation correction** + +Do not copy classifier precedence into the integrator; corrections must keep delegation to `PersistentRasterLifecycleClassifier.Classify` and `PersistentRasterPlacementClassifier.Classify`. + +- [ ] **Step 4: Advance to `1.13.0-Alpha-4`, record RE04, and require full exact-head matrix green** + +The 1.13 API ledger should remain unchanged unless a genuine public API requirement is discovered; adding public surface requires a separate regret review before proceeding. + +--- + +### Task 5: RE05 replanning conveniences on the integration audit result + +**Files:** +- Create: `tests/Icod.TermInfo.Inspection.Tests/src/RE05PersistentRasterRuntimeReplanningTests.cs` +- Modify: `Icod.TermInfo.Inspection/src/PersistentRasterRuntimeIntegrationResult.cs` +- Modify: `Directory.Build.props` +- Create: `docs/1.13.0-RE05-REPLANNING-COMPOSITION.md` + +**Interfaces:** + +```csharp +public PersistentRasterLifecyclePlan CreateLifecyclePlan( + PersistentRasterLifecycleRequest request +) + +public PersistentRasterPlacementPlan CreatePlacementPlan( + PersistentRasterLifecycleRequest lifecycleRequest, + PersistentRasterPlacementRequest placementRequest +) +``` + +Because these members live on a 1.13-owned type whose whole block is removed when reconstructing 1.12, they do not require a separate historical-member ledger. + +- [ ] **Step 1: Write failing lifecycle replanning tests** + +Start from Unknown static lifecycle evidence, integrate runtime Supported observations for required subjects, call `CreateLifecyclePlan`, and require the same result shape as direct frozen planner invocation. Null request throws. + +- [ ] **Step 2: Write failing placement replanning tests** + +Require `CreatePlacementPlan` to first build the lifecycle plan from the strengthened lifecycle profile and supplied lifecycle request, then call the frozen placement planner with the strengthened placement profile. Test Satisfied, RequiresRuntimeVerification, Indeterminate, and Impossible paths against direct planner composition. + +- [ ] **Step 3: Implement only the two delegation methods** + +No new combined plan/result/status type. The methods return the frozen plan instances produced by existing planners. + +- [ ] **Step 4: Advance to `1.13.0-Alpha-5`, document the orchestration boundary, and require full exact-head matrix green** + +--- + +### Task 6: RE06 JSON v5 runtime observation/integration automation + +**Files:** +- Create: `tests/Icod.TermInfo.Inspection.Tests/src/RE06PersistentRasterRuntimeJsonTests.cs` +- Create: `Icod.TermInfo.Inspection/src/TermInfoJsonRenderer.PersistentRasterRuntime.cs` +- Create: `docs/Icod.TermInfo.Inspection.schema.v5.json` +- Create: `docs/1.13.0-RE06-INSPECTION-PUBLIC-API-ADDITIVE-MEMBERS.txt` +- Modify: `Icod.TermInfo.Inspection/Icod.TermInfo.Inspection.csproj` +- Modify: `.github/scripts/verify-inspection-compatibility.ps1` +- Modify package schema verification scripts as required by existing schema checks. +- Modify: `Directory.Build.props` +- Create: `docs/1.13.0-RE06-RUNTIME-EVIDENCE-AUTOMATION.md` + +**Interfaces:** + +```csharp +public const string PersistentRasterRuntimeSchemaIdentifier = + "urn:icod:terminfo:inspection:json:5"; +public const int PersistentRasterRuntimeSchemaVersion = 5; + +public static string Render( PersistentRasterRuntimeObservationSet observations ); +public static string Render( + PersistentRasterRuntimeObservationSet observations, + TermInfoJsonRendererOptions options, + CancellationToken cancellationToken = default +); +public static string Render( PersistentRasterRuntimeIntegrationResult integration ); +public static string Render( + PersistentRasterRuntimeIntegrationResult integration, + TermInfoJsonRendererOptions options, + CancellationToken cancellationToken = default +); +``` + +- [ ] **Step 1: Write exact normalized v5 RED vectors** + +Freeze observation-set property order and integration audit property order. The integration document includes canonical observations, imported evidence, inconclusive observations, issue records, and resulting subject support states, but does not nest complete historical profile JSON documents. + +- [ ] **Step 2: Freeze v1-v4 regression fingerprints and v5 schema identity** + +Require the four frozen hashes above unchanged and v5 `$id` exactly `urn:icod:terminfo:inspection:json:5`, with exactly two `oneOf` document references. + +- [ ] **Step 3: Implement v5 renderer using existing bounded infrastructure** + +Reuse `BoundedJsonOutput`, `DeterministicJsonWriter`, `TermInfoJsonRendererOptions`, cancellation boundaries, and existing semantic-name helpers where appropriate. Add no parser/deserializer. + +- [ ] **Step 4: Add deterministic/bound/culture/cancellation/package tests** + +Exact byte limit succeeds; one-byte-less fails closed; canceled token throws; repeated/tr-TR/fr-FR renders match; schema is packed into Inspection. + +- [ ] **Step 5: Extend compatibility reconstruction correctly** + +Record exact six v5 renderer manifest lines in the additive-member ledger. During 1.13 development, reconstruct 1.12 by first removing reviewed v5 renderer members from `TermInfoJsonRenderer`, then removing cumulative `PersistentRasterRuntime*` type blocks, then requiring exact frozen 1.12 SHA. Preserve the established 1.12 -> 1.11 -> 1.10 chain. + +- [ ] **Step 6: Advance to `1.13.0-Alpha-6`, record RE06, and require complete package/schema/full CI green** + +--- + +### Task 7: RE07 Icod.Terminal package-only interoperability and executable samples + +**Files:** +- Create: `tools/runtime-evidence-package-smoke/Icod.TermInfo.RuntimeEvidence.PackageSmoke.csproj` +- Create: `tools/runtime-evidence-package-smoke/Program.cs` +- Create: `.github/scripts/smoke-re07-runtime-evidence-interop.ps1` +- Create: `.github/scripts/package-smoke-re07.NuGet.Config` +- Create: `samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample.csproj` +- Create: `samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/Program.cs` +- Create: `samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/README.md` +- Modify: `samples/Icod.TermInfo.PersistentRasterLifecycle.Sample/Program.cs` +- Modify: `samples/Icod.TermInfo.PersistentRasterPlacement.Sample/Program.cs` +- Modify: `samples/README.md` +- Modify: `packaging/VerifyPackageArtifact.ps1` +- Create: `tests/Icod.TermInfo.Inspection.Tests/src/RE07PersistentRasterRuntimePackageQualificationTests.cs` +- Modify: `Directory.Build.props` +- Create: `docs/1.13.0-RE07-TERMINAL-INTEROPERABILITY-AND-PACKAGE-QUALIFICATION.md` + +**Interfaces:** +- Qualification code may reference stable `Icod.Terminal 1.12.0`; production TermInfo may not. +- Adapter maps externally obtained semantic results to runtime observations. It does not pass `TerminalSession`, `TerminalCapabilityStatus`, endpoint availability, backend identity, or protocol response objects into TermInfo. + +- [ ] **Step 1: Write topology RED tests** + +Require a new package-only consumer and sample, no Terminal dependency in production Inspection project XML, and package verifier wiring. Require lifecycle/placement samples to stop directly constructing final `Verified` evidence for the post-verification transition where the new API supersedes that boilerplate. + +- [ ] **Step 2: Add package-only adapter consumer** + +Use the published stable Terminal package and freshly packed Inspection package. Demonstrate caller mapping from Terminal semantic status into `PersistentRasterRuntime*Observation`, integration, reclassification, and existing planner use on net8/net9/net10. Keep any coarse Terminal-to-lifecycle subject expansion explicit in consumer code. + +- [ ] **Step 3: Add focused runtime-integration sample** + +Demonstrate static plan -> runtime verification required -> caller-owned runtime observations -> integrator -> strengthened profiles -> frozen planners -> concrete Terminal-owned execution. The sample must not calculate final classifier evidence ordinals or append `Verified` evidence manually. + +- [ ] **Step 4: Update existing lifecycle/placement samples to use 1.13 integration where they demonstrate runtime strengthening** + +Preserve their educational purpose and multi-TFM execution. + +- [ ] **Step 5: Wire exact package verification** + +Restore the fresh package with a dedicated source mapping, execute consumer/sample on net8/net9/net10, retain RL07 and PG07 historical consumers unchanged, and keep existing installed-tool/six-RID validation. + +- [ ] **Step 6: Advance to `1.13.0-Alpha-7`, record qualification, and require all 12 PR jobs green** + +--- + +### Task 8: RE08 hardening, whole-surface freeze, documentation, and release closure + +**Files:** +- Create: `tests/Icod.TermInfo.Inspection.Tests/src/RE08ReleaseClosureTests.cs` +- Create: `docs/1.13.0-INSPECTION-PUBLIC-API-FREEZE.md` +- Create: `docs/1.13.0-RE08-FREEZE-FINGERPRINTS.txt` +- Create: `docs/1.13.0-PERSISTENT-RASTER-RUNTIME-EVIDENCE-GUIDE.md` +- Create: `docs/1.13.0-RE08-RELEASE-HARDENING-AND-FREEZE.md` +- Create: `docs/1.13.0-RELEASE-AUDIT.md` +- Modify: `.github/scripts/verify-inspection-compatibility.ps1` +- Modify: `README.md` +- Modify: `Icod.TermInfo.Inspection/README.md` +- Modify: `Icod.TermInfo.Inspection/Icod.TermInfo.Inspection.csproj` +- Modify: `docs/VERSIONING.md` +- Modify: `docs/COMPATIBILITY.md` +- Modify: `Icod.TermInfo-Post-1.0-Development-Roadmap.md` +- Modify: `Directory.Build.props` + +**Interfaces:** +- Freezes complete 1.13 public reflection manifest while continuing to reconstruct exact 1.12 SHA `f71501...198f0` by removing only reviewed 1.13 types/member lines. +- Freezes JSON v1-v5 schema fingerprints and keeps v1-v4 byte-identical. + +- [ ] **Step 1: Add adversarial/freeze RED closure tests** + +Cover maximum observation count 4096, one-past rejection, source-label 256/257 boundary, `int.MaxValue` local ordinal, final classifier ordinal exhaustion, family capacity exhaustion, contradictory runtime observations, repeated/culture independence, immutable snapshots, and production dependency topology. + +- [ ] **Step 2: Generate whole 1.13 API manifest from an exact qualified Alpha-7 artifact** + +Record exported type count and normalized-LF SHA in the freeze doc/fingerprint ledger. Do not derive the release hash by hand from source. + +- [ ] **Step 3: Freeze v1-v5 schema hashes and exact compatibility decomposition** + +The verifier first proves the exact current 1.13 whole surface, then strips exact reviewed v5 renderer members and cumulative `PersistentRasterRuntime*` types to prove frozen 1.12, then performs the existing 1.11/1.10 reconstructions. + +- [ ] **Step 4: Complete consumer documentation and package release metadata** + +Document the runtime-observation versus static-evidence distinction, the adapter boundary, represented issues, planner delegation, JSON v5, package/sample usage, exclusions, and future negotiation handoff. Keep stable install commands at the current stable version until stable promotion. + +- [ ] **Step 5: Advance to `1.13.0-Alpha-8` and run exact full release qualification** + +Require Windows/Linux/macOS Build+Test, Windows PowerShell 5.1 compatibility verification, exact package/API/schema verification, historical plus RE07 package consumers, all samples on net8/net9/net10, installed-tool smokes, and six archive RIDs. + +- [ ] **Step 6: Stable promotion** + +After exact Alpha-8 acceptance, stable `1.13.0` may change coordinated release identity/current-facing metadata only. Run a fresh full matrix, record the exact stable witness, make any status-only documentation closure, run one final full matrix, and leave the PR unmerged until explicitly authorized. + +--- + +## Self-Review Result + +- **Spec coverage:** Every design section is owned by RE01–RE08: vocabulary/bounds (RE01), immutable observations/order (RE02), mapping/ordinal/capacity atomicity (RE03), frozen-classifier behavior (RE04), replanning (RE05), JSON v5 (RE06), Terminal/package integration (RE07), hardening/freeze/release closure (RE08). +- **Placeholder scan:** No `TBD`, `TODO`, implicit “similar to” implementation steps, or unnamed error-handling work remains. +- **Type consistency:** All 1.13-owned public types use `PersistentRasterRuntime*`; observation type names, integrator signature, result properties, RE05 methods, JSON renderer overloads, and compatibility-ledger names are consistent across tasks. +- **Scope:** Multi-protocol ranking/negotiation and live probing remain explicitly deferred; the plan is one coherent Inspection subsystem with downstream qualification, not multiple independent projects. diff --git a/docs/superpowers/specs/2026-09-13-1.13.0-runtime-evidence-interchange-design.md b/docs/superpowers/specs/2026-09-13-1.13.0-runtime-evidence-interchange-design.md new file mode 100644 index 000000000..f9a2e3f1f --- /dev/null +++ b/docs/superpowers/specs/2026-09-13-1.13.0-runtime-evidence-interchange-design.md @@ -0,0 +1,507 @@ +# Icod.TermInfo 1.13.0 — Persistent-Raster Runtime Evidence Interchange Design + +## Context + +Stable `Icod.TermInfo 1.12.0` completed the semantic persistent-raster planning stack in two orthogonal layers: + +- 1.11 lifecycle evidence, classification, and planning; and +- 1.12 advanced placement evidence, classification, and planning. + +Those layers intentionally stop before live terminal verification. A caller such as `Icod.Terminal` may determine a live semantic result, but today the caller must manually translate that result into TermInfo evidence, calculate final `SourceOrdinal` values, merge it into the existing evidence snapshot, reclassify, and replan. + +Version 1.13 removes that mechanical integration burden without moving live verification or protocol policy into TermInfo. + +## Goal + +Introduce a protocol-neutral runtime-observation and evidence-integration layer in `Icod.TermInfo.Inspection` that accepts caller-owned runtime facts, converts conclusive facts into existing `Verified` lifecycle/placement evidence, safely preserves/integrates existing evidence, and returns strengthened profiles plus a deterministic audit trail. + +The design must make the static-plan → live-verification → strengthened-evidence → replan flow straightforward while preserving all 1.11/1.12 semantic contracts. + +## Non-goals + +1.13 does not add live terminal I/O, probing, session ownership, endpoint availability, backend ranking, protocol preference, multi-protocol negotiation, scene/layout policy, image transport, or generic JSON deserialization. + +No production TermInfo assembly may reference `Icod.Terminal`. + +## Architecture + +The runtime interchange layer sits between an external verifier and the existing classifiers. + +```text +static TerminalDescription / caller evidence + | + v +existing lifecycle + placement profiles + | + v +existing planners + | + +-- runtime verification required --> external verifier + | + v + runtime observation set + | + v + evidence integrator + | + +---------------------+---------------------+ + | | + v v + strengthened lifecycle profile strengthened placement profile + | | + +---------------------+---------------------+ + | + v + existing planners +``` + +The integrator is an adapter into the existing semantic model, not a competing semantic engine. + +## Public model + +### Runtime outcome + +Introduce an enum responsibility-equivalent to: + +```csharp +public enum PersistentRasterRuntimeObservationOutcome { + Supported = 0, + Unsupported = 1, + Inconclusive = 2, +} +``` + +Numeric identities SHALL be explicit and frozen in RE01. + +`Supported` and `Unsupported` are conclusive runtime facts. `Inconclusive` is a retained audit result but is not evidence of either support polarity. + +### Lifecycle runtime observation + +Introduce an immutable value responsibility-equivalent to: + +```text +PersistentRasterLifecycleRuntimeObservation + Subject : PersistentRasterLifecycleEvidenceSubject + Outcome : PersistentRasterRuntimeObservationOutcome + SourceLabel : string + SourceOrdinal : int +``` + +`SourceOrdinal` is local to the imported runtime-observation stream. It is not the final `PersistentRasterLifecycleEvidence.SourceOrdinal` used by the frozen classifier. + +### Placement runtime observation + +Introduce an immutable value responsibility-equivalent to: + +```text +PersistentRasterPlacementRuntimeObservation + Subject : PersistentRasterPlacementSubject + Outcome : PersistentRasterRuntimeObservationOutcome + SourceLabel : string + SourceOrdinal : int +``` + +The lifecycle and placement types remain separate so the public model reuses the frozen 1.11/1.12 subject vocabularies rather than introducing another union enumeration. + +### Runtime observation set + +Introduce an immutable bounded aggregate responsibility-equivalent to: + +```text +PersistentRasterRuntimeObservationSet + LifecycleObservations + PlacementObservations +``` + +The constructor SHALL snapshot caller collections once. + +The aggregate SHALL preserve every observation, including repeated subjects, compatible duplicates, contradictions, and inconclusive results. + +No deduplication or conflict resolution occurs at construction time. + +### Integration issue model + +Introduce a bounded structured issue model responsibility-equivalent to: + +```text +PersistentRasterRuntimeIntegrationIssueKind +PersistentRasterRuntimeIntegrationIssue +``` + +Issue kinds SHALL be reserved for normal integration outcomes that cannot be represented by the frozen evidence model, especially: + +- lifecycle evidence capacity exhaustion; +- placement evidence capacity exhaustion; +- lifecycle ordinal-space exhaustion; +- placement ordinal-space exhaustion. + +Programming errors such as invalid enum values, malformed arguments, negative local ordinals, or null collections remain argument exceptions and SHALL NOT be converted into integration issues. + +### Integration result + +Introduce an immutable result responsibility-equivalent to: + +```text +PersistentRasterRuntimeIntegrationResult + Observations + ImportedLifecycleEvidence + ImportedPlacementEvidence + InconclusiveLifecycleObservations + InconclusivePlacementObservations + LifecycleProfile + PlacementProfile + Issues + Succeeded +``` + +The result SHALL retain enough information to explain exactly what was imported and what remained inconclusive. + +`Succeeded` means all conclusive observations were safely mapped and classified. It does not mean any downstream lifecycle or placement plan necessarily succeeds. + +## Observation validation + +Each observation SHALL validate: + +- subject enum is defined; +- outcome enum is defined; +- `SourceLabel` is non-null, non-empty, non-whitespace, and within a frozen bounded UTF-16 length; +- `SourceOrdinal` is non-negative. + +RE01 SHALL freeze the source-label maximum after comparing existing Inspection provenance bounds. The chosen maximum SHALL be shared by lifecycle and placement observations and must be large enough for stable machine-generated source identities without enabling unbounded provenance allocation. + +The observation set SHALL reject null elements and enforce a fixed maximum observation count. The default maximum should reuse an existing Inspection-wide cardinality bound when it serves the same denial-of-service purpose. + +## Canonical ordering + +Runtime observation sets SHALL expose deterministic canonical ordering independent of caller collection implementation and current culture. + +Canonical sort keys SHALL be: + +1. observation family (`Lifecycle` before `Placement` only for cross-family rendering; each public typed collection remains family-specific); +2. semantic subject numeric identity; +3. source label using ordinal string comparison; +4. source-local ordinal; +5. outcome numeric identity. + +The integrator SHALL use this canonical observation order when assigning final evidence ordinals. Input enumeration order therefore cannot change the integrated classifier result. + +## Evidence mapping + +### Conclusive lifecycle observations + +A lifecycle observation maps as follows: + +```text +Supported -> PersistentRasterLifecycleEvidence(IsPositive=true, Kind=Verified) +Unsupported -> PersistentRasterLifecycleEvidence(IsPositive=false, Kind=Verified) +Inconclusive -> no lifecycle classifier evidence +``` + +The mapped evidence `SourceLabel` is copied exactly from the validated runtime observation. + +### Conclusive placement observations + +A placement observation maps as follows: + +```text +Supported -> PersistentRasterPlacementEvidence(IsPositive=true, Kind=Verified) +Unsupported -> PersistentRasterPlacementEvidence(IsPositive=false, Kind=Verified) +Inconclusive -> no placement classifier evidence +``` + +No new evidence kind is introduced. + +## Final ordinal assignment + +Runtime observation local ordinals are provenance ordering only. + +For each evidence family independently: + +1. snapshot the existing profile evidence; +2. compute the next available classifier ordinal as `0` for an empty snapshot or `max(SourceOrdinal) + 1` under checked arithmetic; +3. canonicalize conclusive imported observations; +4. assign consecutive final evidence ordinals while preserving canonical imported order; +5. fail the corresponding family integration deterministically if ordinal space is exhausted. + +The integrator SHALL NOT renumber existing evidence. + +If either family cannot safely append all conclusive observations because of ordinal or capacity exhaustion, that family SHALL produce no partially appended evidence. Integration must be atomic per evidence family. + +A lifecycle failure does not require discarding an independently valid placement integration, and vice versa. The result SHALL expose issues and the resulting independently classified profiles clearly. + +## Capacity handling + +Existing frozen evidence classifiers have bounded evidence counts. The integrator SHALL check final `existing + imported` counts before constructing classifier input. + +Capacity exhaustion is a represented integration issue, not a thrown runtime-semantic error. + +The integrator SHALL not truncate observations or silently drop evidence to fit bounds. + +## Classification + +The integrator SHALL delegate final support classification to the frozen classifiers: + +```text +PersistentRasterLifecycleClassifier.Classify(...) +PersistentRasterPlacementClassifier.Classify(...) +``` + +This preserves the existing verified/declared/capability-derived precedence and contradiction behavior. + +The integration layer SHALL contain no parallel support-precedence algorithm. + +## Replanning + +The core integrator SHALL stop at strengthened profiles. + +RE05 MAY add a convenience orchestration helper that accepts existing lifecycle/placement requests and returns the existing planner outputs, but any such helper SHALL invoke: + +```text +PersistentRasterLifecyclePlanner.Plan(...) +PersistentRasterPlacementPlanner.Plan(...) +``` + +and SHALL return those frozen plan types unchanged. + +No new combined planner, combined support status, or replacement plan status shall be introduced. + +## JSON v5 + +JSON v1-v4 remain byte/semantic frozen. + +Version 5 SHALL add exactly two document kinds: + +```text +persistentRasterRuntimeObservationSet +persistentRasterRuntimeIntegration +``` + +The schema identifier SHALL follow the existing sequence: + +```text +urn:icod:terminfo:inspection:json:5 +``` + +### Observation-set document + +The payload SHALL contain: + +- deterministic lifecycle observation array; +- deterministic placement observation array; +- each observation's subject, outcome, source label, and source-local ordinal. + +### Integration document + +The payload SHALL contain: + +- original canonical observations; +- mapped lifecycle evidence; +- mapped placement evidence; +- inconclusive observations; +- integration issues; +- resulting lifecycle subject support states; +- resulting placement subject support states. + +The document SHALL not duplicate complete historical profile JSON documents as nested blobs. It should expose only the integration-specific audit result needed to understand the strengthened state. + +Rendering SHALL use the existing bounded UTF-8/cancellation/determinism machinery. + +No JSON parser/deserializer is introduced in 1.13. + +## Database-set composition + +Runtime observations are caller-owned post-inspection facts. They SHALL be applied only after the frozen database-set effective-definition boundary has selected or failed to select an effective description/profile. + +1.13 SHALL NOT introduce a new database precedence algorithm or allow runtime observations to manufacture an effective database winner when the database-set result is indeterminate. + +## Terminal qualification + +Production TermInfo remains independent from Terminal. + +A package-only qualification consumer SHALL adapt Terminal's semantic live status into the new runtime observation model. + +The adapter belongs in qualification/sample code and SHALL be explicit enough that downstream consumers can reproduce it without depending on repository internals. + +The preferred integration sample progression is: + +```text +TermInfo static plan + -> RequiresRuntimeVerification +Terminal VerifyCapabilityAsync(...) + -> caller adapter creates runtime observations +TermInfo integrator + -> strengthened profiles +existing planners + -> final semantic plan +Terminal + -> concrete execution +``` + +The sample SHALL remove the current application-specific `GetNextSourceOrdinal` and evidence-factory boilerplate where the new API supersedes it. + +## Error handling + +### Exceptions + +Use argument exceptions for invalid API use: + +- null required values; +- undefined enums; +- negative local ordinals; +- invalid source label; +- observation collection above its configured construction limit. + +### Represented outcomes + +Use integration issues/results for ordinary integration limitations: + +- final evidence capacity exhausted; +- final ordinal space exhausted; +- inconclusive observations. + +Inconclusive is not itself an error issue unless an orchestration API specifically needs to explain why a requested plan remained unverifiable. + +## Bounds + +RE01 SHALL freeze concrete limits after auditing existing 1.12 Inspection bounds. + +The design requires explicit bounds for: + +- source-label length; +- lifecycle observation count; +- placement observation count; +- total observation count if separately useful; +- final evidence counts through the existing classifier bounds; +- JSON output bytes through the existing renderer option. + +No arbitrary metadata dictionary or recursively nested provenance model is permitted. + +## Compatibility + +1.13 is additive to `Icod.TermInfo.Inspection`. + +It SHALL NOT alter: + +- Runtime/Source/Compiler/Termcap public APIs; +- existing Inspection members through 1.12; +- 1.11 lifecycle enum values or classifier/planner behavior; +- 1.12 placement enum values or classifier/planner behavior; +- JSON schemas v1-v4; +- database-set precedence; +- production dependency graph toward Terminal. + +The release verifier SHALL reconstruct the exact frozen 1.12 Inspection API from the current 1.13 assembly by removing only reviewed 1.13 additive types/members, following the same compatibility strategy used for previous minor releases. + +## Testing strategy + +### RE01 contract tests + +Freeze before production implementation: + +- exact runtime outcome values/numerics; +- subject reuse rather than a new unified subject enum; +- no production Terminal dependency; +- no mutation of 1.11/1.12 enums; +- no mutation of JSON v4; +- invalid enum/local ordinal/source-label validation; +- immutable snapshot expectations. + +### Observation tests + +Cover: + +- empty sets; +- maximum cardinality; +- one-past-bound rejection; +- repeated subjects; +- contradictory outcomes; +- inconclusive observations; +- culture independence; +- input-order independence; +- maximum valid local ordinal. + +### Integration tests + +Cover: + +- empty existing evidence; +- non-empty existing evidence; +- safe ordinal append; +- `int.MaxValue` exhaustion; +- classifier evidence capacity exhaustion; +- no partial family append; +- independent lifecycle/placement family success/failure; +- static positive + verified negative; +- static negative + verified positive; +- verified positive + verified negative; +- inconclusive-only imports. + +### JSON tests + +Cover: + +- exact v5 normalized vectors; +- v1-v4 fingerprints unchanged; +- culture independence; +- repeated rendering; +- cancellation; +- exact UTF-8 output bounds; +- schema validation; +- package inclusion. + +### Package/downstream qualification + +Cover: + +- net8.0; +- net9.0; +- net10.0; +- stable published Terminal package integration; +- no production TermInfo → Terminal dependency; +- Windows/Linux/macOS; +- existing installed-tool and six-RID archive smoke unaffected. + +## Explicit exclusions + +1.13 excludes: + +- live probing by TermInfo; +- protocol/backend identifiers; +- backend ranking/preference; +- multi-protocol negotiation; +- terminal-brand heuristics; +- current endpoint availability as TermInfo state; +- session/generation identity; +- resource/placement identity; +- raw probe responses; +- timestamps/host/process provenance; +- relative placement graphs; +- animation/frame lifecycle; +- Unicode placeholders; +- scene graphs; +- image/raster transport; +- generic JSON deserialization; +- changes to database-set precedence. + +## Release sequencing + +The release uses eight tranches: + +```text +RE01 Architecture, vocabulary, API regret gate +RE02 Immutable runtime observations +RE03 Deterministic evidence integration +RE04 Classification and contradiction integration +RE05 Replanning composition and audit result +RE06 JSON v5 runtime-evidence automation +RE07 Terminal interoperability and sample qualification +RE08 Hardening, exact freeze, docs, release closure +``` + +Stable `1.13.0` promotes the validated `1.13.0-Alpha-8` contract without semantic changes. + +## Post-1.13 direction + +1.13 deliberately prepares—but does not implement—the later multi-protocol preference/negotiation track. + +Only after runtime evidence is a clean semantic input and more than one meaningful execution backend exists for the same operation should TermInfo consider representing preference/ranking policy. diff --git a/packaging/VerifyPackageArtifact.ps1 b/packaging/VerifyPackageArtifact.ps1 index 105fa2250..b93b4da91 100644 --- a/packaging/VerifyPackageArtifact.ps1 +++ b/packaging/VerifyPackageArtifact.ps1 @@ -41,6 +41,13 @@ try { throw "PG07 package-only placement interoperability consumer exited with status $LASTEXITCODE." } + & ./.github/scripts/smoke-re07-runtime-evidence-interop.ps1 ` + -ArtifactDirectory $ArtifactDirectory ` + -Configuration $Configuration + if (0 -ne $LASTEXITCODE) { + throw "RE07 package-only runtime-evidence interoperability consumer exited with status $LASTEXITCODE." + } + $lifecycleSampleProject = Join-Path ` $repositoryRoot ` 'samples/Icod.TermInfo.PersistentRasterLifecycle.Sample/Icod.TermInfo.PersistentRasterLifecycle.Sample.csproj' @@ -79,6 +86,25 @@ try { } } + $runtimeIntegrationSampleProject = Join-Path ` + $repositoryRoot ` + 'samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample.csproj' + & dotnet restore $runtimeIntegrationSampleProject + if (0 -ne $LASTEXITCODE) { + throw 'RE07 persistent-raster runtime-integration sample restore failed.' + } + + foreach ($framework in @('net8.0', 'net9.0', 'net10.0')) { + & dotnet run ` + --project $runtimeIntegrationSampleProject ` + -c $Configuration ` + -f $framework ` + --no-restore + if (0 -ne $LASTEXITCODE) { + throw "RE07 persistent-raster runtime-integration sample failed on $framework." + } + } + $inspectionApiManifest = Join-Path ` $ArtifactDirectory ` 'Icod.TermInfo.Inspection.current-public-api.txt' diff --git a/samples/Icod.TermInfo.PersistentRasterLifecycle.Sample/Program.cs b/samples/Icod.TermInfo.PersistentRasterLifecycle.Sample/Program.cs index c19aec009..2cde0bc22 100644 --- a/samples/Icod.TermInfo.PersistentRasterLifecycle.Sample/Program.cs +++ b/samples/Icod.TermInfo.PersistentRasterLifecycle.Sample/Program.cs @@ -46,44 +46,48 @@ public static int Main() { "Unknown persistent support must require consumer-owned runtime verification." ); - List strengthenedEvidence = - staticProfile.Evidence.ToList(); - strengthenedEvidence.Add( - new PersistentRasterLifecycleEvidence( - PersistentRasterLifecycleEvidenceSubject.PersistentUpload, - isPositive: true, - PersistentRasterLifecycleEvidenceKind.Verified, - "consumer-runtime-verification", - sourceOrdinal: 0 - ) - ); - strengthenedEvidence.Add( - new PersistentRasterLifecycleEvidence( - PersistentRasterLifecycleEvidenceSubject.PlacementCreation, - isPositive: true, - PersistentRasterLifecycleEvidenceKind.Verified, - "consumer-runtime-verification", - sourceOrdinal: 1 - ) + PersistentRasterRuntimeObservationSet observations = new( + new[] { + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PersistentUpload, + PersistentRasterRuntimeObservationOutcome.Supported, + "consumer-runtime-verification", + 0 + ), + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PlacementCreation, + PersistentRasterRuntimeObservationOutcome.Supported, + "consumer-runtime-verification", + 1 + ), + }, + Array.Empty() ); - - PersistentRasterLifecycleProfile strengthenedProfile = - PersistentRasterLifecycleClassifier.Classify( - strengthenedEvidence + PersistentRasterPlacementProfile placementProfile = + PersistentRasterPlacementClassifier.Classify( + Array.Empty() ); - PersistentRasterLifecyclePlan strengthenedPlan = - PersistentRasterLifecyclePlanner.Plan( - strengthenedProfile, - request + PersistentRasterRuntimeIntegrationResult integration = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + staticProfile, + placementProfile, + observations ); + PersistentRasterLifecyclePlan strengthenedPlan = + integration.CreateLifecyclePlan( request ); + + Require( + integration.Succeeded, + "Conclusive runtime observations must integrate successfully." + ); Require( strengthenedPlan.Status == PersistentRasterLifecyclePlanStatus.Success, - "Verified consumer evidence must strengthen the lifecycle plan to success." + "Verified consumer observations must strengthen the lifecycle plan to success." ); Require( !strengthenedPlan.RequiresRuntimeVerification, - "Verified consumer evidence must remove runtime-verification requirements." + "Verified consumer observations must remove runtime-verification requirements." ); Require( strengthenedPlan.Steps.Count == 2, @@ -102,7 +106,7 @@ strengthenedPlan.Steps[ 1 ].Operation Console.WriteLine( TermInfoJsonRenderer.Render( - strengthenedProfile + integration ) ); Console.WriteLine( diff --git a/samples/Icod.TermInfo.PersistentRasterLifecycle.Sample/README.md b/samples/Icod.TermInfo.PersistentRasterLifecycle.Sample/README.md index 78373a510..cad158242 100644 --- a/samples/Icod.TermInfo.PersistentRasterLifecycle.Sample/README.md +++ b/samples/Icod.TermInfo.PersistentRasterLifecycle.Sample/README.md @@ -1,6 +1,6 @@ # Icod.TermInfo.PersistentRasterLifecycle.Sample -This sample demonstrates the protocol-neutral persistent-raster lifecycle flow added by `Icod.TermInfo.Inspection` 1.11. +This sample demonstrates the protocol-neutral persistent-raster lifecycle flow introduced by `Icod.TermInfo.Inspection` 1.11 and the caller-owned runtime-evidence integration path added in 1.13. It intentionally keeps terminal execution outside TermInfo. The sample does not probe a live terminal, transmit Kitty or Sixel payloads, allocate terminal-side resource or placement identifiers, or perform cleanup. Those responsibilities belong to the consuming terminal-session layer. @@ -10,11 +10,12 @@ The sample instead demonstrates the reusable semantic boundary: 2. inspect it with `PersistentRasterLifecycleInspector`; 3. observe that Sixel alone leaves persistent upload and placement support `Unknown`; 4. request upload plus one placement and receive an `Indeterminate` plan requiring runtime verification; -5. simulate a consumer-owned live verification result by adding explicit `Verified` evidence for persistent upload and placement creation; -6. reclassify the evidence and obtain a successful protocol-neutral plan; -7. render the strengthened profile and plan through the version-3 lifecycle JSON contract. +5. represent the consumer-owned runtime result as immutable `PersistentRasterRuntimeLifecycleObservation` values; +6. pass those observations through `PersistentRasterRuntimeEvidenceIntegrator`, which maps conclusive results to existing `Verified` evidence, assigns safe final source ordinals, and reclassifies the lifecycle profile; +7. call `CreateLifecyclePlan(...)` on the integration result to delegate replanning to the frozen lifecycle planner; and +8. render the runtime integration audit through JSON version 5 and the strengthened lifecycle plan through the frozen version-3 plan contract. -The project references only `Icod.TermInfo.Inspection`; it has no dependency on `Icod.Terminal`. +The project references only `Icod.TermInfo.Inspection`; it has no dependency on `Icod.Terminal`. The observations in this example stand in for results acquired by the consumer's own live/runtime verification mechanism. TermInfo integrates, classifies, and plans from those observations; it does not perform the verification itself. Run the sample with any supported target framework: @@ -30,4 +31,4 @@ dotnet run --project samples/Icod.TermInfo.PersistentRasterLifecycle.Sample/Icod dotnet run --project samples/Icod.TermInfo.PersistentRasterLifecycle.Sample/Icod.TermInfo.PersistentRasterLifecycle.Sample.csproj -f net10.0 ``` -The `Verified` evidence in this example stands in for evidence acquired by the consumer's own live/runtime verification mechanism. TermInfo classifies and plans from that evidence; it does not perform the verification itself. +For a sample that performs the sibling `Icod.Terminal` semantic verification call before creating runtime observations, see `../Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/README.md`. diff --git a/samples/Icod.TermInfo.PersistentRasterPlacement.Sample/Program.cs b/samples/Icod.TermInfo.PersistentRasterPlacement.Sample/Program.cs index 84686c310..c6de12c08 100644 --- a/samples/Icod.TermInfo.PersistentRasterPlacement.Sample/Program.cs +++ b/samples/Icod.TermInfo.PersistentRasterPlacement.Sample/Program.cs @@ -1,42 +1,49 @@ using Icod.Terminal; using Icod.TermInfo.Inspection; -PersistentRasterLifecycleProfile lifecycleProfile = +PersistentRasterLifecycleProfile initialLifecycleProfile = PersistentRasterLifecycleClassifier.Classify( - new[] { - new PersistentRasterLifecycleEvidence( - PersistentRasterLifecycleEvidenceSubject.PlacementCreation, - true, - PersistentRasterLifecycleEvidenceKind.Verified, - "sample lifecycle", - 0 - ), - } + Array.Empty() ); -PersistentRasterLifecyclePlan lifecyclePlan = - PersistentRasterLifecyclePlanner.Plan( - lifecycleProfile, - new PersistentRasterLifecycleRequest( placementCount: 1 ) - ); -PersistentRasterPlacementRequest placementRequest = - new( - requireSourceRectangle: true, - requireSignedZOrder: true - ); - PersistentRasterPlacementProfile initialPlacementProfile = PersistentRasterPlacementClassifier.Classify( Array.Empty() ); +PersistentRasterLifecycleRequest lifecycleRequest = + new( placementCount: 1 ); +PersistentRasterPlacementRequest placementRequest = new( + requireSourceRectangle: true, + requireSignedZOrder: true +); + +PersistentRasterRuntimeObservationSet lifecycleObservations = new( + new[] { + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PlacementCreation, + PersistentRasterRuntimeObservationOutcome.Supported, + "sample lifecycle runtime verification", + 0 + ), + }, + Array.Empty() +); +PersistentRasterRuntimeIntegrationResult lifecycleIntegration = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + initialLifecycleProfile, + initialPlacementProfile, + lifecycleObservations + ); +PersistentRasterLifecyclePlan lifecyclePlan = + lifecycleIntegration.CreateLifecyclePlan( lifecycleRequest ); PersistentRasterPlacementPlan initialPlacementPlan = PersistentRasterPlacementPlanner.Plan( lifecyclePlan, - initialPlacementProfile, + lifecycleIntegration.PlacementProfile, placementRequest ); -Console.WriteLine( "initial placement profile:" ); -Console.WriteLine( TermInfoJsonRenderer.Render( initialPlacementProfile ) ); +Console.WriteLine( "initial runtime integration:" ); +Console.WriteLine( TermInfoJsonRenderer.Render( lifecycleIntegration ) ); Console.WriteLine( "initial placement plan:" ); Console.WriteLine( TermInfoJsonRenderer.Render( initialPlacementPlan ) ); if ( @@ -46,34 +53,37 @@ return 1; } -PersistentRasterPlacementProfile verifiedPlacementProfile = - PersistentRasterPlacementClassifier.Classify( - new[] { - new PersistentRasterPlacementEvidence( - PersistentRasterPlacementSubject.SourceRectangle, - true, - PersistentRasterPlacementEvidenceKind.Verified, - "consumer runtime verification", - 0 - ), - new PersistentRasterPlacementEvidence( - PersistentRasterPlacementSubject.SignedZOrder, - true, - PersistentRasterPlacementEvidenceKind.Verified, - "consumer runtime verification", - 1 - ), - } +PersistentRasterRuntimeObservationSet placementObservations = new( + Array.Empty(), + new[] { + new PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Supported, + "consumer runtime verification", + 0 + ), + new PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject.SignedZOrder, + PersistentRasterRuntimeObservationOutcome.Supported, + "consumer runtime verification", + 1 + ), + } +); +PersistentRasterRuntimeIntegrationResult verifiedIntegration = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + lifecycleIntegration.LifecycleProfile, + lifecycleIntegration.PlacementProfile, + placementObservations ); PersistentRasterPlacementPlan verifiedPlacementPlan = - PersistentRasterPlacementPlanner.Plan( - lifecyclePlan, - verifiedPlacementProfile, + verifiedIntegration.CreatePlacementPlan( + lifecycleRequest, placementRequest ); -Console.WriteLine( "verified placement profile:" ); -Console.WriteLine( TermInfoJsonRenderer.Render( verifiedPlacementProfile ) ); +Console.WriteLine( "verified runtime integration:" ); +Console.WriteLine( TermInfoJsonRenderer.Render( verifiedIntegration ) ); Console.WriteLine( "verified placement plan:" ); Console.WriteLine( TermInfoJsonRenderer.Render( verifiedPlacementPlan ) ); if ( verifiedPlacementPlan.Status != PersistentRasterPlacementPlanStatus.Satisfied ) { diff --git a/samples/Icod.TermInfo.PersistentRasterPlacement.Sample/README.md b/samples/Icod.TermInfo.PersistentRasterPlacement.Sample/README.md index 30bb1f5f1..a8f88e1d2 100644 --- a/samples/Icod.TermInfo.PersistentRasterPlacement.Sample/README.md +++ b/samples/Icod.TermInfo.PersistentRasterPlacement.Sample/README.md @@ -1,17 +1,19 @@ # Icod.TermInfo.PersistentRasterPlacement.Sample -This 1.12 sample demonstrates the boundary between protocol-neutral TermInfo placement planning and consumer-owned Terminal execution values. +This sample demonstrates the boundary between protocol-neutral TermInfo placement planning and consumer-owned Terminal execution values, using the 1.13 runtime-observation/integration path rather than manual final evidence construction. -The sample begins with a successful 1.11 lifecycle plan but no advanced-placement evidence. Both 1.12 placement subjects therefore remain `Unknown`: +The sample begins with empty lifecycle and placement evidence. A caller-owned lifecycle runtime observation first establishes `PlacementCreation`, allowing the frozen lifecycle planner to produce a successful one-placement lifecycle plan. The advanced-placement subjects still remain `Unknown`: - pixel-space source rectangles; and - signed z-order. -A placement request requiring both semantics initially produces `RequiresRuntimeVerification`. The sample renders both the version-4 placement profile and placement plan so consumers can see the machine-readable unknown state and the planner's verification requirement. +A placement request requiring both semantics therefore initially produces `RequiresRuntimeVerification`. -The sample then adds caller-owned `Verified` evidence representing a result obtained by the consuming application's own live/runtime verification layer. Reclassification produces a supported placement profile, replanning produces `Satisfied`, and the verified profile and plan are rendered again through the version-4 JSON contract. +The consumer then supplies two immutable `PersistentRasterRuntimePlacementObservation` values for `SourceRectangle` and `SignedZOrder`. `PersistentRasterRuntimeEvidenceIntegrator` maps the conclusive observations into the existing frozen placement evidence model, assigns safe final source ordinals, and reclassifies the placement profile. `CreatePlacementPlan(...)` delegates the strengthened lifecycle and placement state back through the frozen planners and produces `Satisfied`. -Only after the semantic plan is satisfied does the consumer construct concrete Terminal execution values: +The sample renders both runtime integration audits and the before/after placement plans so consumers can see the machine-readable transition from unknown runtime state to a satisfied semantic plan. + +Only after semantic planning succeeds does the consumer construct concrete Terminal execution values: ```csharp TerminalRasterSourceRectangle sourceRectangle = new( @@ -26,7 +28,7 @@ TerminalRasterPlacementOptions executionOptions = new() { }; ``` -The coordinates and z-order value are intentionally absent from every TermInfo plan. TermInfo owns semantic evidence, classification, and planning; actual rectangle coordinates, signed stacking values, live verification, and protocol execution belong to the consuming application and `Icod.Terminal`. +The coordinates and z-order value are intentionally absent from every TermInfo plan. TermInfo owns protocol-neutral observations, evidence integration, classification, and planning; actual rectangle coordinates, signed stacking values, live verification, and protocol execution belong to the consuming application and `Icod.Terminal`. The sample targets `net8.0`, `net9.0`, and `net10.0`. For example: @@ -35,3 +37,5 @@ dotnet run --project samples/Icod.TermInfo.PersistentRasterPlacement.Sample/Icod ``` The sample references the in-repository `Icod.TermInfo.Inspection` project and stable `Icod.Terminal 1.12.0`. That Terminal dependency is qualification/sample-only; no production TermInfo project depends on `Icod.Terminal`. + +For the focused caller adapter that performs `Icod.Terminal` semantic capability verification and maps it into TermInfo runtime observations, see `../Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/README.md`. diff --git a/samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample.csproj b/samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample.csproj new file mode 100644 index 000000000..cc4a38f40 --- /dev/null +++ b/samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample.csproj @@ -0,0 +1,13 @@ + + + Exe + net8.0;net9.0;net10.0 + enable + enable + + + + + + + diff --git a/samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/Program.cs b/samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/Program.cs new file mode 100644 index 000000000..8bb86fd3f --- /dev/null +++ b/samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/Program.cs @@ -0,0 +1,145 @@ +using Icod.Terminal; +using Icod.TermInfo; +using Icod.TermInfo.Inspection; + +const string runtimeSourceLabel = + "Icod.Terminal 1.12.0 PersistentRasterGraphics live verification"; + +TerminalDescription description = + new TerminalDescriptionBuilder( "persistent-raster-runtime-integration-sample" ) + .SetDescription( "Persistent raster runtime integration sample" ) + .SetExtendedBoolean( "Sixel" ) + .Build(); +PersistentRasterLifecycleProfile staticProfile = + PersistentRasterLifecycleInspector.Inspect( description ); +PersistentRasterLifecycleRequest request = new( + uploadResource: true, + placementCount: 1, + updatePlacement: true, + deletePlacement: true, + deleteResource: true, + requireAcknowledgedUpload: true +); +PersistentRasterLifecyclePlan staticPlan = + PersistentRasterLifecyclePlanner.Plan( + staticProfile, + request + ); + +Console.WriteLine( $"Static plan: {staticPlan.Status}" ); + +PersistentRasterRuntimeObservationOutcome outcome; +if ( args.Contains( "--live", StringComparer.Ordinal ) ) { + await using TerminalSession session = await TerminalSession.OpenAsync( + new TerminalSessionOptions { + InputMode = TerminalInputMode.CBreak, + EchoInput = false + } + ); + TerminalCapabilityStatus status = await session.VerifyCapabilityAsync( + TerminalCapability.PersistentRasterGraphics + ); + outcome = MapPersistentRasterStatus( status ); + Console.WriteLine( + $"Live Terminal capability result: {status.Support}; mapped TermInfo outcome: {outcome}." + ); +} else { + outcome = MapPersistentRasterSupport( + TerminalCapabilitySupport.Verified + ); + Console.WriteLine( + "Dry-run mode: using the published Icod.Terminal 1.12 Verified support value. Pass --live on an interactive terminal to call VerifyCapabilityAsync." + ); +} + +PersistentRasterRuntimeObservationSet observations = new( + CreatePersistentRasterLifecycleObservations( + outcome, + runtimeSourceLabel + ), + Array.Empty() +); +PersistentRasterPlacementProfile placementProfile = + PersistentRasterPlacementClassifier.Classify( + Array.Empty() + ); +PersistentRasterRuntimeIntegrationResult integration = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + staticProfile, + placementProfile, + observations + ); +PersistentRasterLifecyclePlan strengthenedPlan = + integration.CreateLifecyclePlan( request ); + +Console.WriteLine( "Runtime integration audit:" ); +Console.WriteLine( TermInfoJsonRenderer.Render( integration ) ); +Console.WriteLine( "Plan after runtime evidence:" ); +Console.WriteLine( TermInfoJsonRenderer.Render( strengthenedPlan ) ); + +return + ( strengthenedPlan.Status == PersistentRasterLifecyclePlanStatus.Success ) + ? 0 + : 2 +; + +static PersistentRasterRuntimeObservationOutcome MapPersistentRasterStatus( + TerminalCapabilityStatus status +) { + if ( status.Capability != TerminalCapability.PersistentRasterGraphics ) { + throw new ArgumentException( + "The live status must describe persistent raster graphics.", + nameof( status ) + ); + } + if ( status.EvidenceKind != TerminalCapabilityEvidenceKind.LiveObservation ) { + return PersistentRasterRuntimeObservationOutcome.Inconclusive; + } + return MapPersistentRasterSupport( status.Support ); +} + +static PersistentRasterRuntimeObservationOutcome MapPersistentRasterSupport( + TerminalCapabilitySupport support +) => + support switch { + TerminalCapabilitySupport.Verified => + PersistentRasterRuntimeObservationOutcome.Supported, + TerminalCapabilitySupport.Unsupported => + PersistentRasterRuntimeObservationOutcome.Unsupported, + TerminalCapabilitySupport.Unknown + or TerminalCapabilitySupport.Advertised => + PersistentRasterRuntimeObservationOutcome.Inconclusive, + _ => throw new ArgumentOutOfRangeException( + nameof( support ), + support, + "The Terminal capability support state must be defined." + ), + }; + +static IReadOnlyList + CreatePersistentRasterLifecycleObservations( + PersistentRasterRuntimeObservationOutcome outcome, + string sourceLabel + ) { + ArgumentException.ThrowIfNullOrWhiteSpace( sourceLabel ); + PersistentRasterLifecycleEvidenceSubject[] subjects = [ + PersistentRasterLifecycleEvidenceSubject.PersistentUpload, + PersistentRasterLifecycleEvidenceSubject.AcknowledgedUpload, + PersistentRasterLifecycleEvidenceSubject.PlacementCreation, + PersistentRasterLifecycleEvidenceSubject.MultiplePlacements, + PersistentRasterLifecycleEvidenceSubject.PlacementUpdate, + PersistentRasterLifecycleEvidenceSubject.PlacementDeletion, + PersistentRasterLifecycleEvidenceSubject.ResourceDeletion, + ]; + PersistentRasterRuntimeLifecycleObservation[] observations = + new PersistentRasterRuntimeLifecycleObservation[ subjects.Length ]; + for ( int index = 0; index < subjects.Length; index++ ) { + observations[ index ] = new PersistentRasterRuntimeLifecycleObservation( + subjects[ index ], + outcome, + sourceLabel, + index + ); + } + return observations; +} diff --git a/samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/README.md b/samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/README.md new file mode 100644 index 000000000..b143e618c --- /dev/null +++ b/samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/README.md @@ -0,0 +1,52 @@ +# Persistent Raster Runtime Integration Sample + +This sample demonstrates the `Icod.TermInfo 1.13` boundary between static semantic planning and caller-owned live terminal verification. + +The flow is deliberately explicit: + +```text +TermInfo static inspection and plan + -> runtime verification required +Icod.Terminal 1.12 VerifyCapabilityAsync(...) + -> caller maps TerminalCapabilityStatus to runtime observations +TermInfo PersistentRasterRuntimeEvidenceIntegrator + -> strengthened lifecycle profile +existing TermInfo planner + -> final lifecycle plan +``` + +`Icod.TermInfo.Inspection` does not reference `Icod.Terminal`. The sample is the consumer-owned adapter boundary and pins the published `Icod.Terminal 1.12.0` package used for the 1.13 qualification contract. + +## Dry run + +The default mode is safe for CI and non-interactive environments. It uses the published Terminal `Verified` support value as a representative conclusive caller result, maps it into the runtime-observation model, integrates it, and replans: + +```console +dotnet run --project samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample -f net10.0 +``` + +## Live verification + +On an interactive terminal, pass `--live` to open an `Icod.Terminal` session and perform the actual bounded semantic verification call: + +```console +dotnet run --project samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample -f net10.0 -- --live +``` + +The caller adapter intentionally maps Terminal's semantic result into TermInfo's protocol-neutral runtime-outcome vocabulary as follows: + +| `Icod.Terminal` result | TermInfo runtime outcome | +| --- | --- | +| `LiveObservation` + `Verified` | `Supported` | +| `LiveObservation` + `Unsupported` | `Unsupported` | +| `LiveObservation` + `Unknown` | `Inconclusive` | +| `LiveObservation` + `Advertised` | `Inconclusive` | +| any non-live evidence kind | `Inconclusive` | + +The mapping is deliberately conservative. Static advertisement alone is not promoted to runtime support, and TermInfo does not interpret Terminal-specific evidence kinds itself. + +Terminal 1.12's `PersistentRasterGraphics` capability is intentionally coarser than TermInfo's lifecycle vocabulary. This sample therefore makes the second piece of consumer policy visible: a conclusive coarse result is expanded to the persistent-upload, acknowledged-upload, placement-create/multiple/update/delete, and resource-delete lifecycle subjects. TermInfo itself does not infer that expansion. + +The resulting `PersistentRasterRuntimeObservationSet` is handed to `PersistentRasterRuntimeEvidenceIntegrator`. Conclusive observations become existing `Verified` evidence with safe final source ordinals; inconclusive observations remain visible in the integration audit. `CreateLifecyclePlan(...)` then delegates to the existing frozen lifecycle planner. + +The sample does not execute image transport or persistent-raster operations. Its purpose is the interchange contract: static plan → live semantic result → runtime observations → integration → replan. diff --git a/samples/README.md b/samples/README.md index d06a167b9..85a7b0ab9 100644 --- a/samples/README.md +++ b/samples/README.md @@ -1,27 +1,35 @@ # Icod.TermInfo Samples -The repository contains six executable API samples and one command-suite +The repository contains seven executable API samples and one command-suite walkthrough. The API samples remain separate so acquisition, terminal-control, -toolchain, multi-database, persistent-raster lifecycle, and advanced-placement -examples stay easy to copy without mixing unrelated concerns. - -The 1.12 addition is `Icod.TermInfo.PersistentRasterPlacement.Sample`. It shows -the deliberate boundary between protocol-neutral TermInfo planning and -consumer-owned `Icod.Terminal 1.12.0` execution values. With no advanced-placement -evidence, source rectangles and signed z-order remain `Unknown` and planning -requires runtime verification. The consumer then supplies its own `Verified` -evidence, replans to `Satisfied`, and only then constructs an actual -`TerminalRasterSourceRectangle` and `ZIndex`. - -The 1.11 addition is `Icod.TermInfo.PersistentRasterLifecycle.Sample`, an -executable public-API walkthrough for protocol-neutral persistent-raster -evidence, classification, planning, consumer-owned runtime verification, and the -version-3 lifecycle JSON documents. It intentionally performs no terminal I/O -and has no dependency on `Icod.Terminal`. +toolchain, multi-database, persistent-raster lifecycle, advanced-placement, and +runtime-evidence integration examples stay easy to copy without mixing unrelated +concerns. + +The 1.13 addition is +`Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample`. It shows the complete +static-plan -> caller-owned Terminal verification -> runtime observation -> +evidence integration -> replan boundary. The sample pins the published +`Icod.Terminal 1.12.0` qualification package, keeps the adapter in consumer code, +and makes the coarse `PersistentRasterGraphics`-to-lifecycle-subject expansion +explicit. Its default dry-run mode is deterministic and CI-safe; `--live` invokes +`VerifyCapabilityAsync(...)` on an interactive terminal. + +The 1.12 `Icod.TermInfo.PersistentRasterPlacement.Sample` now uses the 1.13 +runtime-observation/integration API instead of manually constructing final +`Verified` evidence. After TermInfo confirms the required placement semantics, +the consumer still owns the concrete `TerminalRasterSourceRectangle` and signed +`ZIndex` execution values. + +The 1.11 `Icod.TermInfo.PersistentRasterLifecycle.Sample` likewise now models +consumer-owned verification as runtime observations and delegates evidence +mapping, final source ordinals, reclassification, and replanning to the 1.13 +integration API. It intentionally performs no terminal I/O and has no dependency +on `Icod.Terminal`. Version 1.11 deliberately adds no persistent-raster lifecycle command-line form. That feature remains a reusable `Icod.TermInfo.Inspection` API and is demonstrated -by the dedicated lifecycle sample rather than by `ToolSuite`. +by the dedicated lifecycle samples rather than by `ToolSuite`. The 1.10 addition is `Icod.TermInfo.DatabaseSet.Sample`, an executable public-API walkthrough for ordered explicit database sets, precedence, semantic shadow and @@ -36,8 +44,8 @@ the coordinated five-command suite: `tic`, `infocmp`, `toe`, `captoinfo`, and `infotocap`, including both the frozen 1.9 version-1 JSON forms and the additive 1.10 database-set automation forms. -All six executable API sample projects target `net8.0`, `net9.0`, and `net10.0`. -Every `dotnet run` example therefore specifies a framework; substitute +All seven executable API sample projects target `net8.0`, `net9.0`, and +`net10.0`. Every `dotnet run` example therefore specifies a framework; substitute `-f net8.0` or `-f net9.0` when exercising those consumer targets. ## Icod.TermInfo.Sample @@ -58,7 +66,7 @@ See `Icod.TermInfo.Sample/README.md`. ## Icod.TermInfo.Acquisition.Sample `Icod.TermInfo.Acquisition.Sample` is the focused compiled-database acquisition -demonstration introduced in 0.9 and retained through 1.11. It never emits +demonstration introduced in 0.9 and retained through 1.13. It never emits terminal-control strings. Commands: @@ -130,16 +138,17 @@ See `Icod.TermInfo.DatabaseSet.Sample/README.md` and ## Icod.TermInfo.PersistentRasterLifecycle.Sample -`Icod.TermInfo.PersistentRasterLifecycle.Sample` is the focused 1.11 reusable -Inspection example. It begins with a controlled Sixel description and proves -that ordinary raster-display evidence does not imply persistent upload or -placement support. The initial lifecycle request is therefore indeterminate and -requires runtime verification. +`Icod.TermInfo.PersistentRasterLifecycle.Sample` is the focused lifecycle +Inspection example. It begins with a controlled Sixel description and proves that +ordinary raster-display evidence does not imply persistent upload or placement +support. The initial lifecycle request is therefore indeterminate and requires +runtime verification. -The sample then adds explicit `Verified` evidence representing a result obtained -by the consumer's own live/runtime verification layer. Reclassification turns -that evidence into a successful protocol-neutral upload-plus-placement plan, -which is rendered using the version-3 lifecycle JSON contract. +The consumer represents a conclusive runtime result as two immutable lifecycle +observations. `PersistentRasterRuntimeEvidenceIntegrator` maps them to existing +`Verified` evidence, assigns safe final ordinals, reclassifies, and returns the +strengthened profile. `CreateLifecyclePlan(...)` then delegates to the frozen +lifecycle planner and produces the successful upload-plus-placement plan. The project references only `Icod.TermInfo.Inspection`. It does not perform live probing, transmit graphics, own terminal resource or placement identifiers, or @@ -155,18 +164,16 @@ See `Icod.TermInfo.PersistentRasterLifecycle.Sample/README.md`. ## Icod.TermInfo.PersistentRasterPlacement.Sample -`Icod.TermInfo.PersistentRasterPlacement.Sample` is the focused 1.12 downstream -integration example. It first constructs a successful lifecycle plan with no -advanced-placement evidence. Both `SourceRectangle` and `SignedZOrder` therefore -remain `Unknown`, and a request requiring both semantics produces -`RequiresRuntimeVerification`. +`Icod.TermInfo.PersistentRasterPlacement.Sample` is the focused advanced-placement +example. A lifecycle observation first makes placement creation admissible while +source rectangles and signed z-order remain `Unknown`, so placement planning +requires runtime verification. -The consumer then adds caller-owned `Verified` evidence, reclassifies, and -replans to `Satisfied`. The sample renders both version-4 placement profile and -plan documents before constructing concrete `Icod.Terminal 1.12.0` execution -values: a `TerminalRasterSourceRectangle` and a signed -`TerminalRasterPlacementOptions.ZIndex`. This demonstrates that TermInfo never -owns the actual crop coordinates or z-order integer. +The consumer then supplies two placement runtime observations. The integrator maps +those facts into the frozen placement evidence model, and `CreatePlacementPlan(...)` +replans to `Satisfied`. Only after TermInfo has finished its semantic work does the +sample construct concrete `Icod.Terminal 1.12.0` execution values: a +`TerminalRasterSourceRectangle` and signed `TerminalRasterPlacementOptions.ZIndex`. Run it with: @@ -176,6 +183,28 @@ dotnet run --project samples/Icod.TermInfo.PersistentRasterPlacement.Sample/Icod See `Icod.TermInfo.PersistentRasterPlacement.Sample/README.md`. +## Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample + +`Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample` is the focused 1.13 +interchange example. It preserves the architectural separation between TermInfo +semantic planning and caller-owned live verification while removing manual +evidence/ordinal boilerplate. + +The default invocation is deterministic and performs no terminal I/O: + +```text +dotnet run --project samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample.csproj -f net10.0 +``` + +On an interactive terminal, `--live` opens an `Icod.Terminal 1.12.0` session and +uses its semantic capability verifier before integration: + +```text +dotnet run --project samples/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample.csproj -f net10.0 -- --live +``` + +See `Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample/README.md`. + ## ToolSuite `ToolSuite` is a data-and-command walkthrough for the managed command suite. It @@ -190,8 +219,9 @@ validation, semantic comparison, conventional database enumeration, forward/reverse `use=` dependency reports, termcap-to-terminfo conversion, terminfo-to-termcap round trips, all four frozen version-1 JSON document kinds, and the three additive 1.10 database-set JSON document kinds. Persistent-raster -lifecycle and advanced-placement planning are intentionally absent here because -1.11/1.12 expose them through the reusable Inspection API. +lifecycle, advanced-placement, and runtime-evidence planning are intentionally +absent here because those concerns are exposed through the reusable Inspection +API and dedicated samples. See `ToolSuite/README.md`. diff --git a/tests/Icod.TermInfo.InfoCmp.Tests/src/CommandTests.cs b/tests/Icod.TermInfo.InfoCmp.Tests/src/CommandTests.cs index fa5c96342..e1e9ac146 100644 --- a/tests/Icod.TermInfo.InfoCmp.Tests/src/CommandTests.cs +++ b/tests/Icod.TermInfo.InfoCmp.Tests/src/CommandTests.cs @@ -40,7 +40,7 @@ public async Task VersionReportsCoordinatedDevelopmentVersion() { ); Assert.Equal( CommandExitCodes.Success, status ); - Assert.Contains( "1.12.0", ReadText( stdout ) ); + Assert.Contains( "1.13.0", ReadText( stdout ) ); Assert.Empty( ReadText( stderr ) ); } diff --git a/tests/Icod.TermInfo.Inspection.Tests/src/PG08ReleaseClosureTests.cs b/tests/Icod.TermInfo.Inspection.Tests/src/PG08ReleaseClosureTests.cs index a1101b7b1..665483265 100644 --- a/tests/Icod.TermInfo.Inspection.Tests/src/PG08ReleaseClosureTests.cs +++ b/tests/Icod.TermInfo.Inspection.Tests/src/PG08ReleaseClosureTests.cs @@ -31,14 +31,52 @@ public void ExactOneTwelveInspectionSurfaceIsFrozen() { string additiveMembers = ReadRepositoryFile( "docs/1.12.0-PG06-INSPECTION-PUBLIC-API-ADDITIVE-MEMBERS.txt" ); + string oneThirteenAdditions = ReadRepositoryFile( + "docs/1.13.0-RE01-INSPECTION-PUBLIC-API-ADDITIONS.txt" + ); + HashSet approvedOneThirteenTypes = oneThirteenAdditions + .Split( '\n' ) + .Select( line => line.Trim() ) + .Where( + line => + line.Length > 0 + && !line.StartsWith( "#", StringComparison.Ordinal ) + ) + .ToHashSet( StringComparer.Ordinal ); + Type[] currentTypes = + typeof( PersistentRasterPlacementProfile ).Assembly.GetExportedTypes(); + Type[] reconstructedOneTwelveTypes = currentTypes + .Where( + type => + type.FullName is null + || !approvedOneThirteenTypes.Contains( type.FullName ) + ) + .ToArray(); string compatibility = ReadRepositoryFile( ".github/scripts/verify-inspection-compatibility.ps1" ); + Assert.Equal( 81, reconstructedOneTwelveTypes.Length ); Assert.Equal( - 81, - typeof( PersistentRasterPlacementProfile ).Assembly.GetExportedTypes().Length + approvedOneThirteenTypes.Count, + currentTypes.Count( + type => + type.FullName?.StartsWith( + "Icod.TermInfo.Inspection.PersistentRasterRuntime", + StringComparison.Ordinal + ) == true + ) ); + foreach ( string approvedType in approvedOneThirteenTypes ) { + Assert.Contains( + currentTypes, + type => string.Equals( + type.FullName, + approvedType, + StringComparison.Ordinal + ) + ); + } Assert.Contains( InspectionApiSha256, freeze, StringComparison.Ordinal ); Assert.Contains( InspectionApiSha256, fingerprints, StringComparison.Ordinal ); Assert.Contains( InspectionApiSha256, compatibility, StringComparison.Ordinal ); @@ -103,10 +141,6 @@ public void ReleaseFacingMetadataDescribesStableOneTwelve() { string audit = ReadRepositoryFile( "docs/1.12.0-RELEASE-AUDIT.md" ); - string inspectionProject = ReadRepositoryFile( - "Icod.TermInfo.Inspection/Icod.TermInfo.Inspection.csproj" - ); - string buildProperties = ReadRepositoryFile( "Directory.Build.props" ); Assert.Contains( "1.12", rootReadme, StringComparison.Ordinal ); Assert.Contains( "1.12", inspectionReadme, StringComparison.Ordinal ); @@ -116,13 +150,8 @@ public void ReleaseFacingMetadataDescribesStableOneTwelve() { Assert.Contains( "1.12.0-Alpha-8", hardening, StringComparison.Ordinal ); Assert.Contains( "1.12.0-Alpha-8", audit, StringComparison.Ordinal ); Assert.Contains( - "1.12.0", - inspectionProject, - StringComparison.Ordinal - ); - Assert.Contains( - "1.12.0", - buildProperties, + "The coordinated version is `1.12.0`", + audit, StringComparison.Ordinal ); } diff --git a/tests/Icod.TermInfo.Inspection.Tests/src/RE01PersistentRasterRuntimeEvidenceContractTests.cs b/tests/Icod.TermInfo.Inspection.Tests/src/RE01PersistentRasterRuntimeEvidenceContractTests.cs new file mode 100644 index 000000000..cb2dacd8e --- /dev/null +++ b/tests/Icod.TermInfo.Inspection.Tests/src/RE01PersistentRasterRuntimeEvidenceContractTests.cs @@ -0,0 +1,171 @@ +using System.Xml.Linq; +using Icod.TermInfo.Inspection; +using Xunit; + +namespace Icod.TermInfo.Inspection.Tests; + +public sealed class RE01PersistentRasterRuntimeEvidenceContractTests { + [Fact] + public void RuntimeOutcomeMembershipAndNumericsAreFrozen() { + Assert.Equal( + new[] { + PersistentRasterRuntimeObservationOutcome.Supported, + PersistentRasterRuntimeObservationOutcome.Unsupported, + PersistentRasterRuntimeObservationOutcome.Inconclusive, + }, + Enum.GetValues() + ); + Assert.Equal( 0, (int)PersistentRasterRuntimeObservationOutcome.Supported ); + Assert.Equal( 1, (int)PersistentRasterRuntimeObservationOutcome.Unsupported ); + Assert.Equal( 2, (int)PersistentRasterRuntimeObservationOutcome.Inconclusive ); + } + + [Fact] + public void RuntimeObservationBoundsAreFrozen() { + PersistentRasterRuntimeObservationOptions defaults = new(); + + Assert.Equal( + 256, + PersistentRasterRuntimeObservationOptions.DefaultMaximumObservationCount + ); + Assert.Equal( + 4096, + PersistentRasterRuntimeObservationOptions.MaximumSupportedObservationCount + ); + Assert.Equal( + 256, + PersistentRasterRuntimeObservationOptions.MaximumSourceLabelLength + ); + Assert.Equal( + PersistentRasterRuntimeObservationOptions.DefaultMaximumObservationCount, + defaults.MaximumObservationCount + ); + Assert.Throws( + () => new PersistentRasterRuntimeObservationOptions( 0 ) + ); + Assert.Throws( + () => new PersistentRasterRuntimeObservationOptions( 4097 ) + ); + } + + [Fact] + public void FrozenLifecycleVocabularyRemainsUnchanged() { + Assert.Equal( + new[] { + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterLifecycleEvidenceSubject.PersistentUpload, + PersistentRasterLifecycleEvidenceSubject.AcknowledgedUpload, + PersistentRasterLifecycleEvidenceSubject.PlacementCreation, + PersistentRasterLifecycleEvidenceSubject.MultiplePlacements, + PersistentRasterLifecycleEvidenceSubject.PlacementUpdate, + PersistentRasterLifecycleEvidenceSubject.PlacementDeletion, + PersistentRasterLifecycleEvidenceSubject.ResourceDeletion, + }, + Enum.GetValues() + ); + Assert.Equal( + Enumerable.Range( 0, 8 ).ToArray(), + Enum.GetValues() + .Select( value => (int)value ) + .ToArray() + ); + Assert.Equal( + new[] { + PersistentRasterLifecycleEvidenceKind.CapabilityDerived, + PersistentRasterLifecycleEvidenceKind.Declared, + PersistentRasterLifecycleEvidenceKind.Verified, + }, + Enum.GetValues() + ); + Assert.Equal( + new[] { 0, 1, 2 }, + Enum.GetValues() + .Select( value => (int)value ) + .ToArray() + ); + } + + [Fact] + public void FrozenPlacementVocabularyRemainsUnchanged() { + Assert.Equal( + new[] { + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterPlacementSubject.SignedZOrder, + }, + Enum.GetValues() + ); + Assert.Equal( 0, (int)PersistentRasterPlacementSubject.SourceRectangle ); + Assert.Equal( 1, (int)PersistentRasterPlacementSubject.SignedZOrder ); + Assert.Equal( + new[] { + PersistentRasterPlacementEvidenceKind.CapabilityDerived, + PersistentRasterPlacementEvidenceKind.Declared, + PersistentRasterPlacementEvidenceKind.Verified, + }, + Enum.GetValues() + ); + Assert.Equal( + new[] { 0, 1, 2 }, + Enum.GetValues() + .Select( value => (int)value ) + .ToArray() + ); + } + + [Fact] + public void JsonVersionFourIdentityRemainsFrozen() { + Assert.Equal( + "urn:icod:terminfo:inspection:json:4", + TermInfoJsonRenderer.PersistentRasterPlacementSchemaIdentifier + ); + Assert.Equal( + 4, + TermInfoJsonRenderer.PersistentRasterPlacementSchemaVersion + ); + } + + [Fact] + public void RuntimeInterchangeDoesNotIntroduceUnifiedSubjectOrTerminalDependency() { + Assert.DoesNotContain( + typeof( PersistentRasterLifecycleProfile ).Assembly.GetExportedTypes(), + type => string.Equals( + type.FullName, + "Icod.TermInfo.Inspection.PersistentRasterRuntimeSubject", + StringComparison.Ordinal + ) + ); + + string root = FindRepositoryRoot(); + XDocument inspectionProject = XDocument.Load( + Path.Combine( + root, + "Icod.TermInfo.Inspection", + "Icod.TermInfo.Inspection.csproj" + ) + ); + Assert.DoesNotContain( + inspectionProject.Descendants(), + element => + (element.Name.LocalName == "PackageReference" + || element.Name.LocalName == "ProjectReference") + && string.Equals( + element.Attribute( "Include" )?.Value, + "Icod.Terminal", + StringComparison.Ordinal + ) + ); + } + + private static string FindRepositoryRoot() { + DirectoryInfo? current = new( AppContext.BaseDirectory ); + while ( current is not null ) { + if ( File.Exists( Path.Combine( current.FullName, "Icod.TermInfo.sln" ) ) ) { + return current.FullName; + } + current = current.Parent; + } + throw new DirectoryNotFoundException( + "Could not locate the repository root." + ); + } +} diff --git a/tests/Icod.TermInfo.Inspection.Tests/src/RE02PersistentRasterRuntimeObservationTests.cs b/tests/Icod.TermInfo.Inspection.Tests/src/RE02PersistentRasterRuntimeObservationTests.cs new file mode 100644 index 000000000..24ffb2a2c --- /dev/null +++ b/tests/Icod.TermInfo.Inspection.Tests/src/RE02PersistentRasterRuntimeObservationTests.cs @@ -0,0 +1,590 @@ +using System.Collections; +using System.Globalization; +using Icod.TermInfo.Inspection; +using Xunit; + +namespace Icod.TermInfo.Inspection.Tests; + +public sealed class RE02PersistentRasterRuntimeObservationTests { + [Fact] + public void LifecycleObservationPreservesValidatedRuntimeFact() { + PersistentRasterRuntimeLifecycleObservation observation = new( + PersistentRasterLifecycleEvidenceSubject.AcknowledgedUpload, + PersistentRasterRuntimeObservationOutcome.Inconclusive, + " caller:probe ", + int.MaxValue + ); + + Assert.Equal( + PersistentRasterLifecycleEvidenceSubject.AcknowledgedUpload, + observation.Subject + ); + Assert.Equal( + PersistentRasterRuntimeObservationOutcome.Inconclusive, + observation.Outcome + ); + Assert.Equal( " caller:probe ", observation.SourceLabel ); + Assert.Equal( int.MaxValue, observation.SourceOrdinal ); + } + + [Fact] + public void PlacementObservationPreservesValidatedRuntimeFact() { + PersistentRasterRuntimePlacementObservation observation = new( + PersistentRasterPlacementSubject.SignedZOrder, + PersistentRasterRuntimeObservationOutcome.Unsupported, + " caller:probe ", + int.MaxValue + ); + + Assert.Equal( + PersistentRasterPlacementSubject.SignedZOrder, + observation.Subject + ); + Assert.Equal( + PersistentRasterRuntimeObservationOutcome.Unsupported, + observation.Outcome + ); + Assert.Equal( " caller:probe ", observation.SourceLabel ); + Assert.Equal( int.MaxValue, observation.SourceOrdinal ); + } + + [Fact] + public void LifecycleObservationValidatesSubjectOutcomeLabelAndOrdinal() { + Assert.Throws( + () => new PersistentRasterRuntimeLifecycleObservation( + (PersistentRasterLifecycleEvidenceSubject)99, + PersistentRasterRuntimeObservationOutcome.Supported, + "probe", + 0 + ) + ); + Assert.Throws( + () => new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + (PersistentRasterRuntimeObservationOutcome)99, + "probe", + 0 + ) + ); + Assert.Throws( + () => new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Supported, + null!, + 0 + ) + ); + Assert.Throws( + () => new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Supported, + " ", + 0 + ) + ); + Assert.Throws( + () => new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Supported, + new string( 'x', 257 ), + 0 + ) + ); + Assert.Throws( + () => new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Supported, + "probe", + -1 + ) + ); + } + + [Fact] + public void PlacementObservationValidatesSubjectOutcomeLabelAndOrdinal() { + Assert.Throws( + () => new PersistentRasterRuntimePlacementObservation( + (PersistentRasterPlacementSubject)99, + PersistentRasterRuntimeObservationOutcome.Supported, + "probe", + 0 + ) + ); + Assert.Throws( + () => new PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject.SourceRectangle, + (PersistentRasterRuntimeObservationOutcome)99, + "probe", + 0 + ) + ); + Assert.Throws( + () => new PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Supported, + null!, + 0 + ) + ); + Assert.Throws( + () => new PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Supported, + " ", + 0 + ) + ); + Assert.Throws( + () => new PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Supported, + new string( 'x', 257 ), + 0 + ) + ); + Assert.Throws( + () => new PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Supported, + "probe", + -1 + ) + ); + } + + [Fact] + public void ObservationSourceLabelBoundaryIsAccepted() { + foreach ( string sourceLabel in new[] { "x", new string( 'x', 256 ) } ) { + PersistentRasterRuntimeLifecycleObservation lifecycle = new( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Supported, + sourceLabel, + 0 + ); + PersistentRasterRuntimePlacementObservation placement = new( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Supported, + sourceLabel, + 0 + ); + + Assert.Equal( sourceLabel, lifecycle.SourceLabel ); + Assert.Equal( sourceLabel, placement.SourceLabel ); + } + } + + [Fact] + public void ObservationSetAllowsEmptyImmutableSnapshot() { + PersistentRasterRuntimeObservationSet observations = new( + Array.Empty(), + Array.Empty() + ); + + Assert.Empty( observations.LifecycleObservations ); + Assert.Empty( observations.PlacementObservations ); + Assert.Equal( 0, observations.Count ); + Assert.False( + observations.LifecycleObservations + is PersistentRasterRuntimeLifecycleObservation[] + ); + Assert.False( + observations.PlacementObservations + is PersistentRasterRuntimePlacementObservation[] + ); + if ( + observations.LifecycleObservations + is IList lifecycleList + ) { + Assert.True( lifecycleList.IsReadOnly ); + } + if ( + observations.PlacementObservations + is IList placementList + ) { + Assert.True( placementList.IsReadOnly ); + } + } + + [Fact] + public void ObservationSetCopiesAndRetainsDuplicatesContradictionsAndInconclusiveResults() { + PersistentRasterRuntimeLifecycleObservation supportedLifecycle = new( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Supported, + "probe", + 0 + ); + PersistentRasterRuntimeLifecycleObservation unsupportedLifecycle = new( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Unsupported, + "probe", + 0 + ); + PersistentRasterRuntimePlacementObservation inconclusivePlacement = new( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Inconclusive, + "probe", + 0 + ); + List lifecycle = [ + unsupportedLifecycle, + supportedLifecycle, + supportedLifecycle, + ]; + List placement = [ + inconclusivePlacement, + ]; + + PersistentRasterRuntimeObservationSet observations = new( + lifecycle, + placement + ); + lifecycle.Clear(); + placement.Clear(); + + Assert.Equal( + new[] { + supportedLifecycle, + supportedLifecycle, + unsupportedLifecycle, + }, + observations.LifecycleObservations + ); + Assert.Equal( + new[] { inconclusivePlacement }, + observations.PlacementObservations + ); + Assert.Equal( 4, observations.Count ); + } + + [Fact] + public void ObservationSetRejectsNullCollectionsAndNullElements() { + Assert.Throws( + () => new PersistentRasterRuntimeObservationSet( + null!, + Array.Empty() + ) + ); + Assert.Throws( + () => new PersistentRasterRuntimeObservationSet( + Array.Empty(), + null! + ) + ); + Assert.Throws( + () => new PersistentRasterRuntimeObservationSet( + new PersistentRasterRuntimeLifecycleObservation[] { + CreateLifecycle( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Supported, + "probe", + 0 + ), + null!, + }, + Array.Empty() + ) + ); + Assert.Throws( + () => new PersistentRasterRuntimeObservationSet( + Array.Empty(), + new PersistentRasterRuntimePlacementObservation[] { + CreatePlacement( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Supported, + "probe", + 0 + ), + null!, + } + ) + ); + } + + [Fact] + public void ObservationSetEnforcesCombinedConfiguredMaximum() { + PersistentRasterRuntimeLifecycleObservation lifecycle = CreateLifecycle( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Supported, + "probe", + 0 + ); + PersistentRasterRuntimePlacementObservation placement = CreatePlacement( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Supported, + "probe", + 0 + ); + PersistentRasterRuntimeObservationOptions options = new( 4096 ); + + PersistentRasterRuntimeObservationSet maximum = new( + Enumerable.Repeat( lifecycle, 4095 ), + new[] { placement }, + options + ); + + Assert.Equal( 4096, maximum.Count ); + Assert.Throws( + () => new PersistentRasterRuntimeObservationSet( + Enumerable.Repeat( lifecycle, 4096 ), + new[] { placement }, + options + ) + ); + } + + [Fact] + public void ObservationSetEnumeratesEachCallerSequenceExactlyOnce() { + SingleUseEnumerable lifecycle = new( + new[] { + CreateLifecycle( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Supported, + "probe", + 0 + ), + } + ); + SingleUseEnumerable placement = new( + new[] { + CreatePlacement( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Inconclusive, + "probe", + 0 + ), + } + ); + + PersistentRasterRuntimeObservationSet observations = new( + lifecycle, + placement + ); + + Assert.Equal( 2, observations.Count ); + Assert.Equal( 1, lifecycle.EnumerationCount ); + Assert.Equal( 1, placement.EnumerationCount ); + } + + [Fact] + public void LifecycleCanonicalOrderingIsInputOrderAndCultureIndependent() { + PersistentRasterRuntimeLifecycleObservation first = CreateLifecycle( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Supported, + "I", + 0 + ); + PersistentRasterRuntimeLifecycleObservation outcomeLater = CreateLifecycle( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Unsupported, + "I", + 0 + ); + PersistentRasterRuntimeLifecycleObservation ordinalLater = CreateLifecycle( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Supported, + "I", + 1 + ); + PersistentRasterRuntimeLifecycleObservation lowerAsciiLater = CreateLifecycle( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Supported, + "i", + 0 + ); + PersistentRasterRuntimeLifecycleObservation dottedILater = CreateLifecycle( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Supported, + "İ", + 0 + ); + PersistentRasterRuntimeLifecycleObservation subjectLater = CreateLifecycle( + PersistentRasterLifecycleEvidenceSubject.PersistentUpload, + PersistentRasterRuntimeObservationOutcome.Inconclusive, + "A", + 0 + ); + PersistentRasterRuntimeLifecycleObservation[] expected = [ + first, + outcomeLater, + ordinalLater, + lowerAsciiLater, + dottedILater, + subjectLater, + ]; + PersistentRasterRuntimeLifecycleObservation[] input = [ + subjectLater, + dottedILater, + lowerAsciiLater, + ordinalLater, + outcomeLater, + first, + ]; + + Assert.Equal( + expected, + SnapshotLifecycleUnderCulture( input, "tr-TR" ) + ); + Assert.Equal( + expected, + SnapshotLifecycleUnderCulture( input.Reverse(), "fr-FR" ) + ); + } + + [Fact] + public void PlacementCanonicalOrderingIsInputOrderAndCultureIndependent() { + PersistentRasterRuntimePlacementObservation first = CreatePlacement( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Supported, + "I", + 0 + ); + PersistentRasterRuntimePlacementObservation outcomeLater = CreatePlacement( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Inconclusive, + "I", + 0 + ); + PersistentRasterRuntimePlacementObservation ordinalLater = CreatePlacement( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Supported, + "I", + 1 + ); + PersistentRasterRuntimePlacementObservation lowerAsciiLater = CreatePlacement( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Supported, + "i", + 0 + ); + PersistentRasterRuntimePlacementObservation dottedILater = CreatePlacement( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Supported, + "İ", + 0 + ); + PersistentRasterRuntimePlacementObservation subjectLater = CreatePlacement( + PersistentRasterPlacementSubject.SignedZOrder, + PersistentRasterRuntimeObservationOutcome.Unsupported, + "A", + 0 + ); + PersistentRasterRuntimePlacementObservation[] expected = [ + first, + outcomeLater, + ordinalLater, + lowerAsciiLater, + dottedILater, + subjectLater, + ]; + PersistentRasterRuntimePlacementObservation[] input = [ + subjectLater, + dottedILater, + lowerAsciiLater, + ordinalLater, + outcomeLater, + first, + ]; + + Assert.Equal( + expected, + SnapshotPlacementUnderCulture( input, "tr-TR" ) + ); + Assert.Equal( + expected, + SnapshotPlacementUnderCulture( input.Reverse(), "fr-FR" ) + ); + } + + private static PersistentRasterRuntimeLifecycleObservation CreateLifecycle( + PersistentRasterLifecycleEvidenceSubject subject, + PersistentRasterRuntimeObservationOutcome outcome, + string sourceLabel, + int sourceOrdinal + ) => new( + subject, + outcome, + sourceLabel, + sourceOrdinal + ); + + private static PersistentRasterRuntimePlacementObservation CreatePlacement( + PersistentRasterPlacementSubject subject, + PersistentRasterRuntimeObservationOutcome outcome, + string sourceLabel, + int sourceOrdinal + ) => new( + subject, + outcome, + sourceLabel, + sourceOrdinal + ); + + private static IReadOnlyList + SnapshotLifecycleUnderCulture( + IEnumerable observations, + string cultureName + ) { + CultureInfo originalCulture = CultureInfo.CurrentCulture; + CultureInfo originalUiCulture = CultureInfo.CurrentUICulture; + try { + CultureInfo culture = CultureInfo.GetCultureInfo( cultureName ); + CultureInfo.CurrentCulture = culture; + CultureInfo.CurrentUICulture = culture; + return new PersistentRasterRuntimeObservationSet( + observations, + Array.Empty() + ).LifecycleObservations; + } finally { + CultureInfo.CurrentCulture = originalCulture; + CultureInfo.CurrentUICulture = originalUiCulture; + } + } + + private static IReadOnlyList + SnapshotPlacementUnderCulture( + IEnumerable observations, + string cultureName + ) { + CultureInfo originalCulture = CultureInfo.CurrentCulture; + CultureInfo originalUiCulture = CultureInfo.CurrentUICulture; + try { + CultureInfo culture = CultureInfo.GetCultureInfo( cultureName ); + CultureInfo.CurrentCulture = culture; + CultureInfo.CurrentUICulture = culture; + return new PersistentRasterRuntimeObservationSet( + Array.Empty(), + observations + ).PlacementObservations; + } finally { + CultureInfo.CurrentCulture = originalCulture; + CultureInfo.CurrentUICulture = originalUiCulture; + } + } + + private sealed class SingleUseEnumerable : IEnumerable { + private readonly IEnumerable _source; + + public SingleUseEnumerable( IEnumerable source ) { + ArgumentNullException.ThrowIfNull( source ); + _source = source; + } + + public int EnumerationCount { + get; + private set; + } + + public IEnumerator GetEnumerator() { + EnumerationCount++; + if ( EnumerationCount > 1 ) { + throw new InvalidOperationException( + "The test sequence was enumerated more than once." + ); + } + return _source.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + => GetEnumerator(); + } +} diff --git a/tests/Icod.TermInfo.Inspection.Tests/src/RE03PersistentRasterRuntimeEvidenceIntegrationTests.cs b/tests/Icod.TermInfo.Inspection.Tests/src/RE03PersistentRasterRuntimeEvidenceIntegrationTests.cs new file mode 100644 index 000000000..57bfb4b91 --- /dev/null +++ b/tests/Icod.TermInfo.Inspection.Tests/src/RE03PersistentRasterRuntimeEvidenceIntegrationTests.cs @@ -0,0 +1,610 @@ +using Icod.TermInfo.Inspection; +using Xunit; + +namespace Icod.TermInfo.Inspection.Tests; + +public sealed class RE03PersistentRasterRuntimeEvidenceIntegrationTests { + [Fact] + public void IntegrationIssueKindMembershipAndNumericsAreFrozen() { + Assert.Equal( + new[] { + PersistentRasterRuntimeIntegrationIssueKind.LifecycleEvidenceCapacityExhausted, + PersistentRasterRuntimeIntegrationIssueKind.PlacementEvidenceCapacityExhausted, + PersistentRasterRuntimeIntegrationIssueKind.LifecycleOrdinalSpaceExhausted, + PersistentRasterRuntimeIntegrationIssueKind.PlacementOrdinalSpaceExhausted, + }, + Enum.GetValues() + ); + Assert.Equal( + 0, + (int)PersistentRasterRuntimeIntegrationIssueKind.LifecycleEvidenceCapacityExhausted + ); + Assert.Equal( + 1, + (int)PersistentRasterRuntimeIntegrationIssueKind.PlacementEvidenceCapacityExhausted + ); + Assert.Equal( + 2, + (int)PersistentRasterRuntimeIntegrationIssueKind.LifecycleOrdinalSpaceExhausted + ); + Assert.Equal( + 3, + (int)PersistentRasterRuntimeIntegrationIssueKind.PlacementOrdinalSpaceExhausted + ); + } + + [Fact] + public void ConclusiveObservationsMapToVerifiedEvidenceAndInconclusiveRemainVisible() { + PersistentRasterRuntimeLifecycleObservation lifecycleSupported = new( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Supported, + " lifecycle-supported ", + 7 + ); + PersistentRasterRuntimeLifecycleObservation lifecycleUnsupported = new( + PersistentRasterLifecycleEvidenceSubject.PersistentUpload, + PersistentRasterRuntimeObservationOutcome.Unsupported, + "lifecycle-unsupported", + 8 + ); + PersistentRasterRuntimeLifecycleObservation lifecycleInconclusive = new( + PersistentRasterLifecycleEvidenceSubject.AcknowledgedUpload, + PersistentRasterRuntimeObservationOutcome.Inconclusive, + "lifecycle-inconclusive", + 9 + ); + PersistentRasterRuntimePlacementObservation placementSupported = new( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Supported, + " placement-supported ", + 10 + ); + PersistentRasterRuntimePlacementObservation placementInconclusive = new( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Inconclusive, + "placement-inconclusive", + 11 + ); + PersistentRasterRuntimePlacementObservation placementUnsupported = new( + PersistentRasterPlacementSubject.SignedZOrder, + PersistentRasterRuntimeObservationOutcome.Unsupported, + "placement-unsupported", + 12 + ); + PersistentRasterRuntimeObservationSet observations = new( + new[] { + lifecycleInconclusive, + lifecycleUnsupported, + lifecycleSupported, + }, + new[] { + placementUnsupported, + placementInconclusive, + placementSupported, + } + ); + + PersistentRasterRuntimeIntegrationResult result = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + CreateEmptyLifecycleProfile(), + CreateEmptyPlacementProfile(), + observations + ); + + Assert.Same( observations, result.Observations ); + Assert.True( result.Succeeded ); + Assert.Empty( result.Issues ); + Assert.Equal( 2, result.ImportedLifecycleEvidence.Count ); + Assert.Equal( 2, result.ImportedPlacementEvidence.Count ); + Assert.Equal( + new[] { lifecycleInconclusive }, + result.InconclusiveLifecycleObservations + ); + Assert.Equal( + new[] { placementInconclusive }, + result.InconclusivePlacementObservations + ); + + PersistentRasterLifecycleEvidence importedLifecycleSupported = + Assert.Single( + result.ImportedLifecycleEvidence, + item => item.Subject + == PersistentRasterLifecycleEvidenceSubject.RasterDisplay + ); + Assert.True( importedLifecycleSupported.IsPositive ); + Assert.Equal( + PersistentRasterLifecycleEvidenceKind.Verified, + importedLifecycleSupported.Kind + ); + Assert.Equal( + " lifecycle-supported ", + importedLifecycleSupported.SourceLabel + ); + + PersistentRasterLifecycleEvidence importedLifecycleUnsupported = + Assert.Single( + result.ImportedLifecycleEvidence, + item => item.Subject + == PersistentRasterLifecycleEvidenceSubject.PersistentUpload + ); + Assert.False( importedLifecycleUnsupported.IsPositive ); + Assert.Equal( + PersistentRasterLifecycleEvidenceKind.Verified, + importedLifecycleUnsupported.Kind + ); + Assert.Equal( + "lifecycle-unsupported", + importedLifecycleUnsupported.SourceLabel + ); + + PersistentRasterPlacementEvidence importedPlacementSupported = + Assert.Single( + result.ImportedPlacementEvidence, + item => item.Subject == PersistentRasterPlacementSubject.SourceRectangle + ); + Assert.True( importedPlacementSupported.IsPositive ); + Assert.Equal( + PersistentRasterPlacementEvidenceKind.Verified, + importedPlacementSupported.Kind + ); + Assert.Equal( + " placement-supported ", + importedPlacementSupported.SourceLabel + ); + + PersistentRasterPlacementEvidence importedPlacementUnsupported = + Assert.Single( + result.ImportedPlacementEvidence, + item => item.Subject == PersistentRasterPlacementSubject.SignedZOrder + ); + Assert.False( importedPlacementUnsupported.IsPositive ); + Assert.Equal( + PersistentRasterPlacementEvidenceKind.Verified, + importedPlacementUnsupported.Kind + ); + Assert.Equal( + "placement-unsupported", + importedPlacementUnsupported.SourceLabel + ); + + Assert.Equal( + PersistentRasterLifecycleSupportStatus.Supported, + result.LifecycleProfile.RasterDisplay + ); + Assert.Equal( + PersistentRasterLifecycleSupportStatus.Unsupported, + result.LifecycleProfile.PersistentUpload + ); + Assert.Equal( + PersistentRasterLifecycleSupportStatus.Unknown, + result.LifecycleProfile.AcknowledgedUpload + ); + Assert.Equal( + PersistentRasterLifecycleSupportStatus.Supported, + result.PlacementProfile.SourceRectangle + ); + Assert.Equal( + PersistentRasterLifecycleSupportStatus.Unsupported, + result.PlacementProfile.SignedZOrder + ); + } + + [Fact] + public void SafeFinalOrdinalsAppendAfterExistingEvidenceWithoutRenumbering() { + PersistentRasterLifecycleEvidence existingLifecycle = new( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + true, + PersistentRasterLifecycleEvidenceKind.Declared, + "existing-lifecycle", + 10 + ); + PersistentRasterPlacementEvidence existingPlacement = new( + PersistentRasterPlacementSubject.SourceRectangle, + true, + PersistentRasterPlacementEvidenceKind.Declared, + "existing-placement", + 20 + ); + PersistentRasterLifecycleProfile lifecycleProfile = + PersistentRasterLifecycleClassifier.Classify( + new[] { existingLifecycle } + ); + PersistentRasterPlacementProfile placementProfile = + PersistentRasterPlacementClassifier.Classify( + new[] { existingPlacement } + ); + PersistentRasterRuntimeObservationSet observations = new( + new[] { + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PlacementCreation, + PersistentRasterRuntimeObservationOutcome.Supported, + "zeta", + 9 + ), + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PersistentUpload, + PersistentRasterRuntimeObservationOutcome.Unsupported, + "alpha", + 99 + ), + }, + new[] { + new PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject.SignedZOrder, + PersistentRasterRuntimeObservationOutcome.Unsupported, + "zeta", + 3 + ), + new PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Supported, + "alpha", + 77 + ), + } + ); + + PersistentRasterRuntimeIntegrationResult result = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + lifecycleProfile, + placementProfile, + observations + ); + + Assert.True( result.Succeeded ); + Assert.Equal( + new[] { 11, 12 }, + result.ImportedLifecycleEvidence + .Select( item => item.SourceOrdinal ) + .ToArray() + ); + Assert.Equal( + new[] { 21, 22 }, + result.ImportedPlacementEvidence + .Select( item => item.SourceOrdinal ) + .ToArray() + ); + Assert.Equal( 10, existingLifecycle.SourceOrdinal ); + Assert.Equal( 20, existingPlacement.SourceOrdinal ); + Assert.Contains( existingLifecycle, result.LifecycleProfile.Evidence ); + Assert.Contains( existingPlacement, result.PlacementProfile.Evidence ); + Assert.Equal( 3, result.LifecycleProfile.Evidence.Count ); + Assert.Equal( 3, result.PlacementProfile.Evidence.Count ); + } + + [Fact] + public void EmptyExistingEvidenceStartsImportedOrdinalsAtZero() { + PersistentRasterRuntimeObservationSet observations = new( + new[] { + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Supported, + "lifecycle", + int.MaxValue + ), + }, + new[] { + new PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Unsupported, + "placement", + int.MaxValue + ), + } + ); + + PersistentRasterRuntimeIntegrationResult result = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + CreateEmptyLifecycleProfile(), + CreateEmptyPlacementProfile(), + observations + ); + + Assert.Equal( 0, Assert.Single( result.ImportedLifecycleEvidence ).SourceOrdinal ); + Assert.Equal( 0, Assert.Single( result.ImportedPlacementEvidence ).SourceOrdinal ); + } + + [Fact] + public void LifecycleCapacityFailureIsAtomicWhilePlacementStillIntegrates() { + PersistentRasterLifecycleProfile lifecycleProfile = + CreateLifecycleProfileAtCapacity(); + PersistentRasterPlacementProfile placementProfile = + CreateEmptyPlacementProfile(); + PersistentRasterRuntimeObservationSet observations = new( + new[] { + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Supported, + "runtime-lifecycle", + 0 + ), + }, + new[] { + new PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Supported, + "runtime-placement", + 0 + ), + } + ); + + PersistentRasterRuntimeIntegrationResult result = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + lifecycleProfile, + placementProfile, + observations + ); + + Assert.False( result.Succeeded ); + Assert.Empty( result.ImportedLifecycleEvidence ); + Assert.Equal( lifecycleProfile.Evidence, result.LifecycleProfile.Evidence ); + Assert.Equal( 4096, result.LifecycleProfile.Evidence.Count ); + PersistentRasterRuntimeIntegrationIssue issue = Assert.Single( + result.Issues, + item => item.Kind + == PersistentRasterRuntimeIntegrationIssueKind.LifecycleEvidenceCapacityExhausted + ); + Assert.Equal( 4096, issue.ExistingEvidenceCount ); + Assert.Equal( 1, issue.RequestedImportCount ); + Assert.Single( result.ImportedPlacementEvidence ); + Assert.Equal( + PersistentRasterLifecycleSupportStatus.Supported, + result.PlacementProfile.SourceRectangle + ); + } + + [Fact] + public void PlacementCapacityFailureIsAtomicWhileLifecycleStillIntegrates() { + PersistentRasterLifecycleProfile lifecycleProfile = + CreateEmptyLifecycleProfile(); + PersistentRasterPlacementProfile placementProfile = + CreatePlacementProfileAtCapacity(); + PersistentRasterRuntimeObservationSet observations = new( + new[] { + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Supported, + "runtime-lifecycle", + 0 + ), + }, + new[] { + new PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Supported, + "runtime-placement", + 0 + ), + } + ); + + PersistentRasterRuntimeIntegrationResult result = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + lifecycleProfile, + placementProfile, + observations + ); + + Assert.False( result.Succeeded ); + Assert.Empty( result.ImportedPlacementEvidence ); + Assert.Equal( placementProfile.Evidence, result.PlacementProfile.Evidence ); + Assert.Equal( 4096, result.PlacementProfile.Evidence.Count ); + PersistentRasterRuntimeIntegrationIssue issue = Assert.Single( + result.Issues, + item => item.Kind + == PersistentRasterRuntimeIntegrationIssueKind.PlacementEvidenceCapacityExhausted + ); + Assert.Equal( 4096, issue.ExistingEvidenceCount ); + Assert.Equal( 1, issue.RequestedImportCount ); + Assert.Single( result.ImportedLifecycleEvidence ); + Assert.Equal( + PersistentRasterLifecycleSupportStatus.Supported, + result.LifecycleProfile.RasterDisplay + ); + } + + [Fact] + public void LifecycleOrdinalFailureIsAtomicWhilePlacementStillIntegrates() { + PersistentRasterLifecycleEvidence existingLifecycle = new( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + true, + PersistentRasterLifecycleEvidenceKind.Verified, + "existing-lifecycle", + int.MaxValue + ); + PersistentRasterLifecycleProfile lifecycleProfile = + PersistentRasterLifecycleClassifier.Classify( + new[] { existingLifecycle } + ); + PersistentRasterRuntimeObservationSet observations = new( + new[] { + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PersistentUpload, + PersistentRasterRuntimeObservationOutcome.Supported, + "runtime-lifecycle", + 0 + ), + }, + new[] { + new PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Unsupported, + "runtime-placement", + 0 + ), + } + ); + + PersistentRasterRuntimeIntegrationResult result = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + lifecycleProfile, + CreateEmptyPlacementProfile(), + observations + ); + + Assert.False( result.Succeeded ); + Assert.Empty( result.ImportedLifecycleEvidence ); + Assert.Equal( lifecycleProfile.Evidence, result.LifecycleProfile.Evidence ); + PersistentRasterRuntimeIntegrationIssue issue = Assert.Single( + result.Issues, + item => item.Kind + == PersistentRasterRuntimeIntegrationIssueKind.LifecycleOrdinalSpaceExhausted + ); + Assert.Equal( 1, issue.ExistingEvidenceCount ); + Assert.Equal( 1, issue.RequestedImportCount ); + Assert.Single( result.ImportedPlacementEvidence ); + Assert.Equal( + PersistentRasterLifecycleSupportStatus.Unsupported, + result.PlacementProfile.SourceRectangle + ); + } + + [Fact] + public void PlacementOrdinalFailureIsAtomicWhileLifecycleStillIntegrates() { + PersistentRasterPlacementEvidence existingPlacement = new( + PersistentRasterPlacementSubject.SourceRectangle, + true, + PersistentRasterPlacementEvidenceKind.Verified, + "existing-placement", + int.MaxValue + ); + PersistentRasterPlacementProfile placementProfile = + PersistentRasterPlacementClassifier.Classify( + new[] { existingPlacement } + ); + PersistentRasterRuntimeObservationSet observations = new( + new[] { + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Unsupported, + "runtime-lifecycle", + 0 + ), + }, + new[] { + new PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject.SignedZOrder, + PersistentRasterRuntimeObservationOutcome.Supported, + "runtime-placement", + 0 + ), + } + ); + + PersistentRasterRuntimeIntegrationResult result = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + CreateEmptyLifecycleProfile(), + placementProfile, + observations + ); + + Assert.False( result.Succeeded ); + Assert.Empty( result.ImportedPlacementEvidence ); + Assert.Equal( placementProfile.Evidence, result.PlacementProfile.Evidence ); + PersistentRasterRuntimeIntegrationIssue issue = Assert.Single( + result.Issues, + item => item.Kind + == PersistentRasterRuntimeIntegrationIssueKind.PlacementOrdinalSpaceExhausted + ); + Assert.Equal( 1, issue.ExistingEvidenceCount ); + Assert.Equal( 1, issue.RequestedImportCount ); + Assert.Single( result.ImportedLifecycleEvidence ); + Assert.Equal( + PersistentRasterLifecycleSupportStatus.Unsupported, + result.LifecycleProfile.RasterDisplay + ); + } + + [Fact] + public void InconclusiveObservationsDoNotConsumeEvidenceCapacity() { + PersistentRasterLifecycleProfile lifecycleProfile = + CreateLifecycleProfileAtCapacity(); + PersistentRasterPlacementProfile placementProfile = + CreatePlacementProfileAtCapacity(); + PersistentRasterRuntimeLifecycleObservation lifecycleInconclusive = new( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Inconclusive, + "lifecycle-inconclusive", + 0 + ); + PersistentRasterRuntimePlacementObservation placementInconclusive = new( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Inconclusive, + "placement-inconclusive", + 0 + ); + PersistentRasterRuntimeObservationSet observations = new( + new[] { lifecycleInconclusive }, + new[] { placementInconclusive } + ); + + PersistentRasterRuntimeIntegrationResult result = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + lifecycleProfile, + placementProfile, + observations + ); + + Assert.True( result.Succeeded ); + Assert.Empty( result.Issues ); + Assert.Empty( result.ImportedLifecycleEvidence ); + Assert.Empty( result.ImportedPlacementEvidence ); + Assert.Equal( + new[] { lifecycleInconclusive }, + result.InconclusiveLifecycleObservations + ); + Assert.Equal( + new[] { placementInconclusive }, + result.InconclusivePlacementObservations + ); + Assert.Equal( lifecycleProfile.Evidence, result.LifecycleProfile.Evidence ); + Assert.Equal( placementProfile.Evidence, result.PlacementProfile.Evidence ); + } + + private static PersistentRasterLifecycleProfile CreateEmptyLifecycleProfile() + => PersistentRasterLifecycleClassifier.Classify( + Array.Empty() + ); + + private static PersistentRasterPlacementProfile CreateEmptyPlacementProfile() + => PersistentRasterPlacementClassifier.Classify( + Array.Empty() + ); + + private static PersistentRasterLifecycleProfile CreateLifecycleProfileAtCapacity() { + PersistentRasterLifecycleEvidence[] evidence = Enumerable.Range( 0, 4096 ) + .Select( + index => new PersistentRasterLifecycleEvidence( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + true, + PersistentRasterLifecycleEvidenceKind.CapabilityDerived, + $"existing-{index:D4}", + index + ) + ) + .ToArray(); + return PersistentRasterLifecycleClassifier.Classify( + evidence, + new PersistentRasterLifecycleEvidenceOptions( 4096 ) + ); + } + + private static PersistentRasterPlacementProfile CreatePlacementProfileAtCapacity() { + PersistentRasterPlacementEvidence[] evidence = Enumerable.Range( 0, 4096 ) + .Select( + index => new PersistentRasterPlacementEvidence( + PersistentRasterPlacementSubject.SourceRectangle, + true, + PersistentRasterPlacementEvidenceKind.CapabilityDerived, + $"existing-{index:D4}", + index + ) + ) + .ToArray(); + return PersistentRasterPlacementClassifier.Classify( + evidence, + new PersistentRasterPlacementEvidenceOptions( 4096 ) + ); + } +} diff --git a/tests/Icod.TermInfo.Inspection.Tests/src/RE04PersistentRasterRuntimeClassificationIntegrationTests.cs b/tests/Icod.TermInfo.Inspection.Tests/src/RE04PersistentRasterRuntimeClassificationIntegrationTests.cs new file mode 100644 index 000000000..aace4f1c7 --- /dev/null +++ b/tests/Icod.TermInfo.Inspection.Tests/src/RE04PersistentRasterRuntimeClassificationIntegrationTests.cs @@ -0,0 +1,396 @@ +using Icod.TermInfo.Inspection; +using Xunit; + +namespace Icod.TermInfo.Inspection.Tests; + +public sealed class RE04PersistentRasterRuntimeClassificationIntegrationTests { + [Fact] + public void RuntimeUnsupportedOverridesCapabilityDerivedPositive() { + PersistentRasterLifecycleProfile lifecycleProfile = + PersistentRasterLifecycleClassifier.Classify( + new[] { + new PersistentRasterLifecycleEvidence( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + true, + PersistentRasterLifecycleEvidenceKind.CapabilityDerived, + "static-capability", + 0 + ), + } + ); + PersistentRasterRuntimeIntegrationResult result = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + lifecycleProfile, + CreateEmptyPlacementProfile(), + new PersistentRasterRuntimeObservationSet( + new[] { + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Unsupported, + "runtime", + 0 + ), + }, + Array.Empty() + ) + ); + + Assert.True( result.Succeeded ); + Assert.Equal( + PersistentRasterLifecycleSupportStatus.Unsupported, + result.LifecycleProfile.RasterDisplay + ); + PersistentRasterLifecycleEvidence imported = + Assert.Single( result.ImportedLifecycleEvidence ); + Assert.Equal( PersistentRasterLifecycleEvidenceKind.Verified, imported.Kind ); + Assert.False( imported.IsPositive ); + } + + [Fact] + public void RuntimeSupportedOverridesDeclaredNegative() { + PersistentRasterLifecycleProfile lifecycleProfile = + PersistentRasterLifecycleClassifier.Classify( + new[] { + new PersistentRasterLifecycleEvidence( + PersistentRasterLifecycleEvidenceSubject.PersistentUpload, + false, + PersistentRasterLifecycleEvidenceKind.Declared, + "declared-negative", + 0 + ), + } + ); + PersistentRasterRuntimeIntegrationResult result = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + lifecycleProfile, + CreateEmptyPlacementProfile(), + new PersistentRasterRuntimeObservationSet( + new[] { + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PersistentUpload, + PersistentRasterRuntimeObservationOutcome.Supported, + "runtime", + 0 + ), + }, + Array.Empty() + ) + ); + + Assert.True( result.Succeeded ); + Assert.Equal( + PersistentRasterLifecycleSupportStatus.Supported, + result.LifecycleProfile.PersistentUpload + ); + } + + [Fact] + public void ExistingVerifiedPositiveAndRuntimeNegativeBecomeContradicted() { + PersistentRasterLifecycleProfile lifecycleProfile = + PersistentRasterLifecycleClassifier.Classify( + new[] { + new PersistentRasterLifecycleEvidence( + PersistentRasterLifecycleEvidenceSubject.PlacementCreation, + true, + PersistentRasterLifecycleEvidenceKind.Verified, + "existing-verified-positive", + 0 + ), + } + ); + PersistentRasterRuntimeIntegrationResult result = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + lifecycleProfile, + CreateEmptyPlacementProfile(), + new PersistentRasterRuntimeObservationSet( + new[] { + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PlacementCreation, + PersistentRasterRuntimeObservationOutcome.Unsupported, + "runtime-negative", + 0 + ), + }, + Array.Empty() + ) + ); + + Assert.Equal( + PersistentRasterLifecycleSupportStatus.Contradicted, + result.LifecycleProfile.PlacementCreation + ); + } + + [Fact] + public void ExistingVerifiedNegativeAndRuntimePositiveBecomeContradicted() { + PersistentRasterPlacementProfile placementProfile = + PersistentRasterPlacementClassifier.Classify( + new[] { + new PersistentRasterPlacementEvidence( + PersistentRasterPlacementSubject.SourceRectangle, + false, + PersistentRasterPlacementEvidenceKind.Verified, + "existing-verified-negative", + 0 + ), + } + ); + PersistentRasterRuntimeIntegrationResult result = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + CreateEmptyLifecycleProfile(), + placementProfile, + new PersistentRasterRuntimeObservationSet( + Array.Empty(), + new[] { + new PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Supported, + "runtime-positive", + 0 + ), + } + ) + ); + + Assert.Equal( + PersistentRasterLifecycleSupportStatus.Contradicted, + result.PlacementProfile.SourceRectangle + ); + } + + [Fact] + public void RepeatedCompatibleRuntimeObservationsRemainConclusive() { + PersistentRasterRuntimeIntegrationResult result = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + CreateEmptyLifecycleProfile(), + CreateEmptyPlacementProfile(), + new PersistentRasterRuntimeObservationSet( + new[] { + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.ResourceDeletion, + PersistentRasterRuntimeObservationOutcome.Supported, + "runtime-a", + 0 + ), + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.ResourceDeletion, + PersistentRasterRuntimeObservationOutcome.Supported, + "runtime-b", + 1 + ), + }, + new[] { + new PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject.SignedZOrder, + PersistentRasterRuntimeObservationOutcome.Unsupported, + "runtime-a", + 2 + ), + new PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject.SignedZOrder, + PersistentRasterRuntimeObservationOutcome.Unsupported, + "runtime-b", + 3 + ), + } + ) + ); + + Assert.Equal( 2, result.ImportedLifecycleEvidence.Count ); + Assert.Equal( 2, result.ImportedPlacementEvidence.Count ); + Assert.Equal( + PersistentRasterLifecycleSupportStatus.Supported, + result.LifecycleProfile.ResourceDeletion + ); + Assert.Equal( + PersistentRasterLifecycleSupportStatus.Unsupported, + result.PlacementProfile.SignedZOrder + ); + } + + [Fact] + public void InconclusiveOnlyObservationsPreserveExistingSupportStates() { + PersistentRasterLifecycleProfile lifecycleProfile = + PersistentRasterLifecycleClassifier.Classify( + new[] { + new PersistentRasterLifecycleEvidence( + PersistentRasterLifecycleEvidenceSubject.MultiplePlacements, + true, + PersistentRasterLifecycleEvidenceKind.Declared, + "declared-positive", + 0 + ), + } + ); + PersistentRasterPlacementProfile placementProfile = + PersistentRasterPlacementClassifier.Classify( + new[] { + new PersistentRasterPlacementEvidence( + PersistentRasterPlacementSubject.SignedZOrder, + false, + PersistentRasterPlacementEvidenceKind.Declared, + "declared-negative", + 0 + ), + } + ); + PersistentRasterRuntimeLifecycleObservation lifecycleObservation = new( + PersistentRasterLifecycleEvidenceSubject.MultiplePlacements, + PersistentRasterRuntimeObservationOutcome.Inconclusive, + "runtime-lifecycle-inconclusive", + 0 + ); + PersistentRasterRuntimePlacementObservation placementObservation = new( + PersistentRasterPlacementSubject.SignedZOrder, + PersistentRasterRuntimeObservationOutcome.Inconclusive, + "runtime-placement-inconclusive", + 1 + ); + + PersistentRasterRuntimeIntegrationResult result = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + lifecycleProfile, + placementProfile, + new PersistentRasterRuntimeObservationSet( + new[] { lifecycleObservation }, + new[] { placementObservation } + ) + ); + + Assert.True( result.Succeeded ); + Assert.Empty( result.ImportedLifecycleEvidence ); + Assert.Empty( result.ImportedPlacementEvidence ); + Assert.Equal( + new[] { lifecycleObservation }, + result.InconclusiveLifecycleObservations + ); + Assert.Equal( + new[] { placementObservation }, + result.InconclusivePlacementObservations + ); + Assert.Equal( + PersistentRasterLifecycleSupportStatus.Supported, + result.LifecycleProfile.MultiplePlacements + ); + Assert.Equal( + PersistentRasterLifecycleSupportStatus.Unsupported, + result.PlacementProfile.SignedZOrder + ); + } + + [Fact] + public void LifecycleFailureDoesNotPreventPlacementClassification() { + PersistentRasterLifecycleProfile lifecycleProfile = + PersistentRasterLifecycleClassifier.Classify( + new[] { + new PersistentRasterLifecycleEvidence( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + true, + PersistentRasterLifecycleEvidenceKind.Verified, + "ordinal-exhausted", + int.MaxValue + ), + } + ); + PersistentRasterRuntimeIntegrationResult result = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + lifecycleProfile, + CreateEmptyPlacementProfile(), + new PersistentRasterRuntimeObservationSet( + new[] { + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PersistentUpload, + PersistentRasterRuntimeObservationOutcome.Supported, + "cannot-import", + 0 + ), + }, + new[] { + new PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Supported, + "placement-import", + 1 + ), + } + ) + ); + + Assert.False( result.Succeeded ); + Assert.Empty( result.ImportedLifecycleEvidence ); + Assert.Same( lifecycleProfile, result.LifecycleProfile ); + Assert.Single( + result.Issues, + item => item.Kind + == PersistentRasterRuntimeIntegrationIssueKind.LifecycleOrdinalSpaceExhausted + ); + Assert.Equal( + PersistentRasterLifecycleSupportStatus.Supported, + result.PlacementProfile.SourceRectangle + ); + } + + [Fact] + public void PlacementFailureDoesNotPreventLifecycleClassification() { + PersistentRasterPlacementProfile placementProfile = + PersistentRasterPlacementClassifier.Classify( + new[] { + new PersistentRasterPlacementEvidence( + PersistentRasterPlacementSubject.SourceRectangle, + false, + PersistentRasterPlacementEvidenceKind.Verified, + "ordinal-exhausted", + int.MaxValue + ), + } + ); + PersistentRasterRuntimeIntegrationResult result = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + CreateEmptyLifecycleProfile(), + placementProfile, + new PersistentRasterRuntimeObservationSet( + new[] { + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Unsupported, + "lifecycle-import", + 0 + ), + }, + new[] { + new PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject.SignedZOrder, + PersistentRasterRuntimeObservationOutcome.Supported, + "cannot-import", + 1 + ), + } + ) + ); + + Assert.False( result.Succeeded ); + Assert.Empty( result.ImportedPlacementEvidence ); + Assert.Same( placementProfile, result.PlacementProfile ); + Assert.Single( + result.Issues, + item => item.Kind + == PersistentRasterRuntimeIntegrationIssueKind.PlacementOrdinalSpaceExhausted + ); + Assert.Equal( + PersistentRasterLifecycleSupportStatus.Unsupported, + result.LifecycleProfile.RasterDisplay + ); + } + + private static PersistentRasterLifecycleProfile CreateEmptyLifecycleProfile() { + return PersistentRasterLifecycleClassifier.Classify( + Array.Empty() + ); + } + + private static PersistentRasterPlacementProfile CreateEmptyPlacementProfile() { + return PersistentRasterPlacementClassifier.Classify( + Array.Empty() + ); + } +} diff --git a/tests/Icod.TermInfo.Inspection.Tests/src/RE05PersistentRasterRuntimeReplanningTests.cs b/tests/Icod.TermInfo.Inspection.Tests/src/RE05PersistentRasterRuntimeReplanningTests.cs new file mode 100644 index 000000000..399a3e025 --- /dev/null +++ b/tests/Icod.TermInfo.Inspection.Tests/src/RE05PersistentRasterRuntimeReplanningTests.cs @@ -0,0 +1,418 @@ +using Icod.TermInfo.Inspection; +using Xunit; + +namespace Icod.TermInfo.Inspection.Tests; + +public sealed class RE05PersistentRasterRuntimeReplanningTests { + [Fact] + public void CreateLifecyclePlanDelegatesUsingStrengthenedLifecycleProfile() { + PersistentRasterRuntimeIntegrationResult integration = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + CreateEmptyLifecycleProfile(), + CreateEmptyPlacementProfile(), + new PersistentRasterRuntimeObservationSet( + new[] { + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PersistentUpload, + PersistentRasterRuntimeObservationOutcome.Supported, + "runtime-upload", + 0 + ), + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.AcknowledgedUpload, + PersistentRasterRuntimeObservationOutcome.Supported, + "runtime-acknowledged-upload", + 1 + ), + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PlacementCreation, + PersistentRasterRuntimeObservationOutcome.Supported, + "runtime-placement-creation", + 2 + ), + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.MultiplePlacements, + PersistentRasterRuntimeObservationOutcome.Supported, + "runtime-multiple-placements", + 3 + ), + }, + Array.Empty() + ) + ); + PersistentRasterLifecycleRequest request = new( + uploadResource: true, + placementCount: 2, + requireAcknowledgedUpload: true + ); + PersistentRasterLifecyclePlan expected = + PersistentRasterLifecyclePlanner.Plan( + integration.LifecycleProfile, + request + ); + + PersistentRasterLifecyclePlan actual = + integration.CreateLifecyclePlan( request ); + + Assert.Equal( PersistentRasterLifecyclePlanStatus.Success, actual.Status ); + AssertLifecyclePlansEquivalent( expected, actual ); + } + + [Fact] + public void CreateLifecyclePlanRejectsNullRequest() { + PersistentRasterRuntimeIntegrationResult integration = + CreateEmptyIntegrationResult(); + + Assert.Throws( + () => integration.CreateLifecyclePlan( null! ) + ); + } + + [Fact] + public void CreatePlacementPlanProducesSatisfiedResultByDirectComposition() { + PersistentRasterRuntimeIntegrationResult integration = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + CreateEmptyLifecycleProfile(), + CreateEmptyPlacementProfile(), + new PersistentRasterRuntimeObservationSet( + new[] { + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PlacementCreation, + PersistentRasterRuntimeObservationOutcome.Supported, + "runtime-lifecycle", + 0 + ), + }, + new[] { + new PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Supported, + "runtime-placement", + 1 + ), + } + ) + ); + PersistentRasterLifecycleRequest lifecycleRequest = new( + placementCount: 1 + ); + PersistentRasterPlacementRequest placementRequest = new( + requireSourceRectangle: true + ); + + PersistentRasterPlacementPlan expected = DirectPlacementPlan( + integration, + lifecycleRequest, + placementRequest + ); + PersistentRasterPlacementPlan actual = integration.CreatePlacementPlan( + lifecycleRequest, + placementRequest + ); + + Assert.Equal( PersistentRasterPlacementPlanStatus.Satisfied, actual.Status ); + AssertPlacementPlansEquivalent( expected, actual ); + } + + [Fact] + public void CreatePlacementPlanProducesRequiresRuntimeVerificationByDirectComposition() { + PersistentRasterLifecycleProfile lifecycleProfile = + PersistentRasterLifecycleClassifier.Classify( + new[] { + new PersistentRasterLifecycleEvidence( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + true, + PersistentRasterLifecycleEvidenceKind.Verified, + "verified-display", + 0 + ), + } + ); + PersistentRasterRuntimeIntegrationResult integration = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + lifecycleProfile, + CreateEmptyPlacementProfile(), + new PersistentRasterRuntimeObservationSet( + Array.Empty(), + Array.Empty() + ) + ); + PersistentRasterLifecycleRequest lifecycleRequest = new( + displayEphemeral: true + ); + PersistentRasterPlacementRequest placementRequest = new( + requireSourceRectangle: true + ); + + PersistentRasterPlacementPlan expected = DirectPlacementPlan( + integration, + lifecycleRequest, + placementRequest + ); + PersistentRasterPlacementPlan actual = integration.CreatePlacementPlan( + lifecycleRequest, + placementRequest + ); + + Assert.Equal( + PersistentRasterPlacementPlanStatus.RequiresRuntimeVerification, + actual.Status + ); + AssertPlacementPlansEquivalent( expected, actual ); + } + + [Fact] + public void CreatePlacementPlanProducesIndeterminateByDirectComposition() { + PersistentRasterPlacementProfile placementProfile = + PersistentRasterPlacementClassifier.Classify( + new[] { + new PersistentRasterPlacementEvidence( + PersistentRasterPlacementSubject.SignedZOrder, + true, + PersistentRasterPlacementEvidenceKind.Verified, + "verified-z-order", + 0 + ), + } + ); + PersistentRasterRuntimeIntegrationResult integration = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + CreateEmptyLifecycleProfile(), + placementProfile, + new PersistentRasterRuntimeObservationSet( + Array.Empty(), + Array.Empty() + ) + ); + PersistentRasterLifecycleRequest lifecycleRequest = new( + displayEphemeral: true + ); + PersistentRasterPlacementRequest placementRequest = new( + requireSignedZOrder: true + ); + + PersistentRasterPlacementPlan expected = DirectPlacementPlan( + integration, + lifecycleRequest, + placementRequest + ); + PersistentRasterPlacementPlan actual = integration.CreatePlacementPlan( + lifecycleRequest, + placementRequest + ); + + Assert.Equal( + PersistentRasterPlacementPlanStatus.Indeterminate, + actual.Status + ); + AssertPlacementPlansEquivalent( expected, actual ); + } + + [Fact] + public void CreatePlacementPlanProducesImpossibleByDirectComposition() { + PersistentRasterLifecycleProfile lifecycleProfile = + PersistentRasterLifecycleClassifier.Classify( + new[] { + new PersistentRasterLifecycleEvidence( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + false, + PersistentRasterLifecycleEvidenceKind.Verified, + "verified-display-negative", + 0 + ), + } + ); + PersistentRasterPlacementProfile placementProfile = + PersistentRasterPlacementClassifier.Classify( + new[] { + new PersistentRasterPlacementEvidence( + PersistentRasterPlacementSubject.SourceRectangle, + true, + PersistentRasterPlacementEvidenceKind.Verified, + "verified-source-rectangle", + 0 + ), + } + ); + PersistentRasterRuntimeIntegrationResult integration = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + lifecycleProfile, + placementProfile, + new PersistentRasterRuntimeObservationSet( + Array.Empty(), + Array.Empty() + ) + ); + PersistentRasterLifecycleRequest lifecycleRequest = new( + displayEphemeral: true + ); + PersistentRasterPlacementRequest placementRequest = new( + requireSourceRectangle: true + ); + + PersistentRasterPlacementPlan expected = DirectPlacementPlan( + integration, + lifecycleRequest, + placementRequest + ); + PersistentRasterPlacementPlan actual = integration.CreatePlacementPlan( + lifecycleRequest, + placementRequest + ); + + Assert.Equal( PersistentRasterPlacementPlanStatus.Impossible, actual.Status ); + AssertPlacementPlansEquivalent( expected, actual ); + } + + [Fact] + public void CreatePlacementPlanRejectsNullRequests() { + PersistentRasterRuntimeIntegrationResult integration = + CreateEmptyIntegrationResult(); + PersistentRasterLifecycleRequest lifecycleRequest = new( + displayEphemeral: true + ); + PersistentRasterPlacementRequest placementRequest = new( + requireSourceRectangle: true + ); + + Assert.Throws( + () => integration.CreatePlacementPlan( null!, placementRequest ) + ); + Assert.Throws( + () => integration.CreatePlacementPlan( lifecycleRequest, null! ) + ); + } + + private static PersistentRasterRuntimeIntegrationResult CreateEmptyIntegrationResult() { + return PersistentRasterRuntimeEvidenceIntegrator.Integrate( + CreateEmptyLifecycleProfile(), + CreateEmptyPlacementProfile(), + new PersistentRasterRuntimeObservationSet( + Array.Empty(), + Array.Empty() + ) + ); + } + + private static PersistentRasterLifecycleProfile CreateEmptyLifecycleProfile() { + return PersistentRasterLifecycleClassifier.Classify( + Array.Empty() + ); + } + + private static PersistentRasterPlacementProfile CreateEmptyPlacementProfile() { + return PersistentRasterPlacementClassifier.Classify( + Array.Empty() + ); + } + + private static PersistentRasterPlacementPlan DirectPlacementPlan( + PersistentRasterRuntimeIntegrationResult integration, + PersistentRasterLifecycleRequest lifecycleRequest, + PersistentRasterPlacementRequest placementRequest + ) { + PersistentRasterLifecyclePlan lifecyclePlan = + PersistentRasterLifecyclePlanner.Plan( + integration.LifecycleProfile, + lifecycleRequest + ); + return PersistentRasterPlacementPlanner.Plan( + lifecyclePlan, + integration.PlacementProfile, + placementRequest + ); + } + + private static void AssertLifecyclePlansEquivalent( + PersistentRasterLifecyclePlan expected, + PersistentRasterLifecyclePlan actual + ) { + Assert.Equal( expected.Status, actual.Status ); + Assert.Equal( + expected.RequiresRuntimeVerification, + actual.RequiresRuntimeVerification + ); + Assert.Equal( + expected.Steps + .Select( + item => ( + item.SequenceIndex, + item.Operation, + item.RequiresRuntimeVerification + ) + ), + actual.Steps + .Select( + item => ( + item.SequenceIndex, + item.Operation, + item.RequiresRuntimeVerification + ) + ) + ); + Assert.Equal( + expected.Issues + .Select( + item => ( + item.Operation, + item.Subject, + item.SupportStatus, + item.RequiresRuntimeVerification + ) + ), + actual.Issues + .Select( + item => ( + item.Operation, + item.Subject, + item.SupportStatus, + item.RequiresRuntimeVerification + ) + ) + ); + } + + private static void AssertPlacementPlansEquivalent( + PersistentRasterPlacementPlan expected, + PersistentRasterPlacementPlan actual + ) { + Assert.Equal( expected.Status, actual.Status ); + Assert.Equal( expected.LifecycleStatus, actual.LifecycleStatus ); + Assert.Equal( + expected.Requirements + .Select( + item => ( + item.Subject, + item.SupportStatus, + item.RequiresRuntimeVerification + ) + ), + actual.Requirements + .Select( + item => ( + item.Subject, + item.SupportStatus, + item.RequiresRuntimeVerification + ) + ) + ); + Assert.Equal( + expected.Issues + .Select( + item => ( + item.Subject, + item.SupportStatus, + item.RequiresRuntimeVerification + ) + ), + actual.Issues + .Select( + item => ( + item.Subject, + item.SupportStatus, + item.RequiresRuntimeVerification + ) + ) + ); + } +} diff --git a/tests/Icod.TermInfo.Inspection.Tests/src/RE06PersistentRasterRuntimeJsonTests.cs b/tests/Icod.TermInfo.Inspection.Tests/src/RE06PersistentRasterRuntimeJsonTests.cs new file mode 100644 index 000000000..9bbcbfe38 --- /dev/null +++ b/tests/Icod.TermInfo.Inspection.Tests/src/RE06PersistentRasterRuntimeJsonTests.cs @@ -0,0 +1,357 @@ +using System.Globalization; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; +using Xunit; + +namespace Icod.TermInfo.Inspection.Tests; + +public sealed class RE06PersistentRasterRuntimeJsonTests { + [Fact] + public void RuntimeObservationSetRendersExactVersionFiveDocument() { + PersistentRasterRuntimeObservationSet observations = + CreateObservationSetFixture(); + + string json = TermInfoJsonRenderer.Render( observations ); + + Assert.Equal( + "{\"schema\":\"urn:icod:terminfo:inspection:json:5\",\"schemaVersion\":5,\"documentKind\":\"persistentRasterRuntimeObservationSet\",\"data\":{\"observationCount\":3,\"lifecycleObservationCount\":2,\"placementObservationCount\":1,\"lifecycleObservations\":[{\"subject\":\"persistentUpload\",\"outcome\":\"supported\",\"sourceLabel\":\"runtime-a\",\"sourceOrdinal\":4},{\"subject\":\"resourceDeletion\",\"outcome\":\"inconclusive\",\"sourceLabel\":\"runtime-b\",\"sourceOrdinal\":8}],\"placementObservations\":[{\"subject\":\"signedZOrder\",\"outcome\":\"unsupported\",\"sourceLabel\":\"runtime-z\",\"sourceOrdinal\":2}]}}", + json + ); + } + + [Fact] + public void RuntimeIntegrationRendersExactVersionFiveAuditDocument() { + PersistentRasterRuntimeIntegrationResult integration = + CreateIntegrationFixture(); + + string json = TermInfoJsonRenderer.Render( integration ); + + Assert.Equal( + "{\"schema\":\"urn:icod:terminfo:inspection:json:5\",\"schemaVersion\":5,\"documentKind\":\"persistentRasterRuntimeIntegration\",\"data\":{\"succeeded\":true,\"observationCount\":4,\"observations\":{\"lifecycle\":[{\"subject\":\"persistentUpload\",\"outcome\":\"supported\",\"sourceLabel\":\"lifecycle-ok\",\"sourceOrdinal\":1},{\"subject\":\"resourceDeletion\",\"outcome\":\"inconclusive\",\"sourceLabel\":\"lifecycle-question\",\"sourceOrdinal\":2}],\"placement\":[{\"subject\":\"sourceRectangle\",\"outcome\":\"unsupported\",\"sourceLabel\":\"placement-no\",\"sourceOrdinal\":0},{\"subject\":\"signedZOrder\",\"outcome\":\"inconclusive\",\"sourceLabel\":\"placement-question\",\"sourceOrdinal\":3}]},\"importedLifecycleEvidence\":[{\"subject\":\"persistentUpload\",\"isPositive\":true,\"kind\":\"verified\",\"sourceLabel\":\"lifecycle-ok\",\"sourceOrdinal\":0}],\"importedPlacementEvidence\":[{\"subject\":\"sourceRectangle\",\"isPositive\":false,\"kind\":\"verified\",\"sourceLabel\":\"placement-no\",\"sourceOrdinal\":0}],\"inconclusiveLifecycleObservations\":[{\"subject\":\"resourceDeletion\",\"outcome\":\"inconclusive\",\"sourceLabel\":\"lifecycle-question\",\"sourceOrdinal\":2}],\"inconclusivePlacementObservations\":[{\"subject\":\"signedZOrder\",\"outcome\":\"inconclusive\",\"sourceLabel\":\"placement-question\",\"sourceOrdinal\":3}],\"issues\":[],\"lifecycleStates\":{\"rasterDisplay\":\"unknown\",\"persistentUpload\":\"supported\",\"acknowledgedUpload\":\"unknown\",\"placementCreation\":\"unknown\",\"multiplePlacements\":\"unknown\",\"placementUpdate\":\"unknown\",\"placementDeletion\":\"unknown\",\"resourceDeletion\":\"unknown\"},\"placementStates\":{\"sourceRectangle\":\"unsupported\",\"signedZOrder\":\"unknown\"}}}", + json + ); + } + + [Fact] + public void RuntimeIntegrationIssuesRenderStructuredDeterministicRecords() { + PersistentRasterLifecycleProfile lifecycleProfile = + PersistentRasterLifecycleClassifier.Classify( + new[] { + new PersistentRasterLifecycleEvidence( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + true, + PersistentRasterLifecycleEvidenceKind.Verified, + "max-ordinal", + int.MaxValue + ), + } + ); + PersistentRasterRuntimeIntegrationResult integration = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + lifecycleProfile, + CreateEmptyPlacementProfile(), + new PersistentRasterRuntimeObservationSet( + new[] { + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PersistentUpload, + PersistentRasterRuntimeObservationOutcome.Supported, + "cannot-import", + 0 + ), + }, + Array.Empty() + ) + ); + + using JsonDocument document = JsonDocument.Parse( + TermInfoJsonRenderer.Render( integration ) + ); + JsonElement issue = document.RootElement + .GetProperty( "data" ) + .GetProperty( "issues" )[ 0 ]; + Assert.Equal( + "lifecycleOrdinalSpaceExhausted", + issue.GetProperty( "kind" ).GetString() + ); + Assert.Equal( 1, issue.GetProperty( "existingEvidenceCount" ).GetInt32() ); + Assert.Equal( 1, issue.GetProperty( "requestedImportCount" ).GetInt32() ); + } + + [Fact] + public void RuntimeJsonVersionIdentityIsFrozen() { + Assert.Equal( + "urn:icod:terminfo:inspection:json:5", + TermInfoJsonRenderer.PersistentRasterRuntimeSchemaIdentifier + ); + Assert.Equal( + 5, + TermInfoJsonRenderer.PersistentRasterRuntimeSchemaVersion + ); + } + + [Fact] + public void RuntimeObservationRenderingIsDeterministicBoundedCancelableAndCultureIndependent() { + PersistentRasterRuntimeObservationSet observations = + CreateObservationSetFixture(); + string invariant = TermInfoJsonRenderer.Render( observations ); + Assert.Equal( invariant, TermInfoJsonRenderer.Render( observations ) ); + + AssertCultureIndependent( + invariant, + () => TermInfoJsonRenderer.Render( observations ) + ); + + int exactBytes = Encoding.UTF8.GetByteCount( invariant ); + Assert.Equal( + invariant, + TermInfoJsonRenderer.Render( + observations, + new TermInfoJsonRendererOptions( exactBytes ) + ) + ); + Assert.Throws( + () => TermInfoJsonRenderer.Render( + observations, + new TermInfoJsonRendererOptions( exactBytes - 1 ) + ) + ); + + using CancellationTokenSource cancellation = new(); + cancellation.Cancel(); + Assert.Throws( + () => TermInfoJsonRenderer.Render( + observations, + new TermInfoJsonRendererOptions(), + cancellation.Token + ) + ); + } + + [Fact] + public void RuntimeIntegrationRenderingIsDeterministicBoundedCancelableAndCultureIndependent() { + PersistentRasterRuntimeIntegrationResult integration = + CreateIntegrationFixture(); + string invariant = TermInfoJsonRenderer.Render( integration ); + Assert.Equal( invariant, TermInfoJsonRenderer.Render( integration ) ); + + AssertCultureIndependent( + invariant, + () => TermInfoJsonRenderer.Render( integration ) + ); + + int exactBytes = Encoding.UTF8.GetByteCount( invariant ); + Assert.Equal( + invariant, + TermInfoJsonRenderer.Render( + integration, + new TermInfoJsonRendererOptions( exactBytes ) + ) + ); + Assert.Throws( + () => TermInfoJsonRenderer.Render( + integration, + new TermInfoJsonRendererOptions( exactBytes - 1 ) + ) + ); + + using CancellationTokenSource cancellation = new(); + cancellation.Cancel(); + Assert.Throws( + () => TermInfoJsonRenderer.Render( + integration, + new TermInfoJsonRendererOptions(), + cancellation.Token + ) + ); + } + + [Fact] + public void VersionFiveSchemaAndPackageWiringAreFrozen() { + string root = FindRepositoryRoot(); + string schemaPath = Path.Combine( + root, + "docs", + "Icod.TermInfo.Inspection.schema.v5.json" + ); + using JsonDocument schema = JsonDocument.Parse( + File.ReadAllText( schemaPath ) + ); + Assert.Equal( + "urn:icod:terminfo:inspection:json:5", + schema.RootElement.GetProperty( "$id" ).GetString() + ); + string[] documentReferences = schema.RootElement + .GetProperty( "oneOf" ) + .EnumerateArray() + .Select( branch => branch.GetProperty( "$ref" ).GetString() ) + .Cast() + .ToArray(); + Assert.Equal( + new[] { + "#/$defs/persistentRasterRuntimeObservationSetDocument", + "#/$defs/persistentRasterRuntimeIntegrationDocument", + }, + documentReferences + ); + + string projectText = File.ReadAllText( + Path.Combine( + root, + "Icod.TermInfo.Inspection", + "Icod.TermInfo.Inspection.csproj" + ) + ); + Assert.Contains( + "Icod.TermInfo.Inspection.schema.v5.json", + projectText, + StringComparison.Ordinal + ); + } + + [Fact] + public void PreviousSchemaFingerprintsRemainFrozen() { + string root = FindRepositoryRoot(); + Assert.Equal( + "76578f421b254802d24453af6868edaf8c23c4b78a87c7e8ef86b233ff0e8500", + NormalizedLfSha256( + Path.Combine( root, "docs", "Icod.TermInfo.Inspection.schema.json" ) + ) + ); + Assert.Equal( + "ae4d53608881344e902f02303c71e2d432500969e60cfb005d70feea607499d0", + NormalizedLfSha256( + Path.Combine( root, "docs", "Icod.TermInfo.Inspection.schema.v2.json" ) + ) + ); + Assert.Equal( + "33ca95aee120f84d0d160ac189f8ddb4db183361b7bd83885c99c1c8ed355a97", + NormalizedLfSha256( + Path.Combine( root, "docs", "Icod.TermInfo.Inspection.schema.v3.json" ) + ) + ); + Assert.Equal( + "6383052f389d903683a9e24d55b73c97eb165db89c6ab5f26dbcb5a3c7fdda87", + NormalizedLfSha256( + Path.Combine( root, "docs", "Icod.TermInfo.Inspection.schema.v4.json" ) + ) + ); + } + + private static PersistentRasterRuntimeObservationSet CreateObservationSetFixture() { + return new PersistentRasterRuntimeObservationSet( + new[] { + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.ResourceDeletion, + PersistentRasterRuntimeObservationOutcome.Inconclusive, + "runtime-b", + 8 + ), + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PersistentUpload, + PersistentRasterRuntimeObservationOutcome.Supported, + "runtime-a", + 4 + ), + }, + new[] { + new PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject.SignedZOrder, + PersistentRasterRuntimeObservationOutcome.Unsupported, + "runtime-z", + 2 + ), + } + ); + } + + private static PersistentRasterRuntimeIntegrationResult CreateIntegrationFixture() { + PersistentRasterRuntimeObservationSet observations = + new PersistentRasterRuntimeObservationSet( + new[] { + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.ResourceDeletion, + PersistentRasterRuntimeObservationOutcome.Inconclusive, + "lifecycle-question", + 2 + ), + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PersistentUpload, + PersistentRasterRuntimeObservationOutcome.Supported, + "lifecycle-ok", + 1 + ), + }, + new[] { + new PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject.SignedZOrder, + PersistentRasterRuntimeObservationOutcome.Inconclusive, + "placement-question", + 3 + ), + new PersistentRasterRuntimePlacementObservation( + PersistentRasterPlacementSubject.SourceRectangle, + PersistentRasterRuntimeObservationOutcome.Unsupported, + "placement-no", + 0 + ), + } + ); + return PersistentRasterRuntimeEvidenceIntegrator.Integrate( + PersistentRasterLifecycleClassifier.Classify( + Array.Empty() + ), + CreateEmptyPlacementProfile(), + observations + ); + } + + private static PersistentRasterPlacementProfile CreateEmptyPlacementProfile() { + return PersistentRasterPlacementClassifier.Classify( + Array.Empty() + ); + } + + private static void AssertCultureIndependent( + string expected, + Func render + ) { + CultureInfo previousCulture = CultureInfo.CurrentCulture; + CultureInfo previousUiCulture = CultureInfo.CurrentUICulture; + try { + CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo( "tr-TR" ); + CultureInfo.CurrentUICulture = CultureInfo.GetCultureInfo( "tr-TR" ); + Assert.Equal( expected, render() ); + CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo( "fr-FR" ); + CultureInfo.CurrentUICulture = CultureInfo.GetCultureInfo( "fr-FR" ); + Assert.Equal( expected, render() ); + } finally { + CultureInfo.CurrentCulture = previousCulture; + CultureInfo.CurrentUICulture = previousUiCulture; + } + } + + private static string NormalizedLfSha256( + string path + ) { + string text = File.ReadAllText( path ) + .Replace( "\r\n", "\n", StringComparison.Ordinal ) + .Replace( '\r', '\n' ); + return Convert.ToHexString( + SHA256.HashData( + Encoding.UTF8.GetBytes( text ) + ) + ).ToLowerInvariant(); + } + + private static string FindRepositoryRoot() { + DirectoryInfo? current = new( AppContext.BaseDirectory ); + while ( current is not null ) { + if ( File.Exists( Path.Combine( current.FullName, "Icod.TermInfo.csproj" ) ) ) { + return current.FullName; + } + current = current.Parent; + } + throw new InvalidOperationException( "Repository root not found." ); + } +} diff --git a/tests/Icod.TermInfo.Inspection.Tests/src/RE07PersistentRasterRuntimePackageQualificationTests.cs b/tests/Icod.TermInfo.Inspection.Tests/src/RE07PersistentRasterRuntimePackageQualificationTests.cs new file mode 100644 index 000000000..3aabf47d1 --- /dev/null +++ b/tests/Icod.TermInfo.Inspection.Tests/src/RE07PersistentRasterRuntimePackageQualificationTests.cs @@ -0,0 +1,337 @@ +using System.Xml.Linq; +using Xunit; + +namespace Icod.TermInfo.Inspection.Tests; + +public sealed class RE07PersistentRasterRuntimePackageQualificationTests { + [Fact] + public void RuntimeEvidencePackageOnlyQualificationPinsStableTerminalOneTwelve() { + string repositoryRoot = GetRepositoryRoot(); + string projectPath = Path.Combine( + repositoryRoot, + "tools", + "runtime-evidence-package-smoke", + "Icod.TermInfo.RuntimeEvidence.PackageSmoke.csproj" + ); + Assert.True( File.Exists( projectPath ) ); + + XDocument project = XDocument.Load( projectPath ); + Assert.Equal( + "net8.0;net9.0;net10.0", + Assert.Single( + project.Descendants(), + element => element.Name.LocalName == "TargetFrameworks" + ).Value + ); + XElement[] packageReferences = project + .Descendants() + .Where( element => element.Name.LocalName == "PackageReference" ) + .ToArray(); + Assert.Equal( 2, packageReferences.Length ); + + XElement inspectionReference = Assert.Single( + packageReferences, + element => element.Attribute( "Include" )?.Value + == "Icod.TermInfo.Inspection" + ); + Assert.Equal( + "$(IcodTermInfoInspectionPackageVersion)", + inspectionReference.Attribute( "Version" )?.Value + ); + XElement terminalReference = Assert.Single( + packageReferences, + element => element.Attribute( "Include" )?.Value == "Icod.Terminal" + ); + Assert.Equal( + "1.12.0", + terminalReference.Attribute( "Version" )?.Value + ); + Assert.DoesNotContain( + project.Descendants(), + element => element.Name.LocalName == "ProjectReference" + ); + } + + [Fact] + public void RuntimeEvidenceConsumerMapsTerminalStatusThroughObservationIntegration() { + string repositoryRoot = GetRepositoryRoot(); + string sourcePath = Path.Combine( + repositoryRoot, + "tools", + "runtime-evidence-package-smoke", + "Program.cs" + ); + Assert.True( File.Exists( sourcePath ) ); + + string source = File.ReadAllText( sourcePath ); + foreach ( string requiredToken in new[] { + "TerminalCapabilityStatus", + "TerminalCapability.PersistentRasterGraphics", + "TerminalCapabilitySupport.Verified", + "TerminalCapabilitySupport.Unsupported", + "PersistentRasterRuntimeLifecycleObservation", + "PersistentRasterRuntimeObservationOutcome", + "PersistentRasterRuntimeObservationSet", + "PersistentRasterRuntimeEvidenceIntegrator.Integrate", + "CreateLifecyclePlan", + "PersistentRasterLifecycleEvidenceSubject.PersistentUpload", + "PersistentRasterLifecycleEvidenceSubject.AcknowledgedUpload", + "PersistentRasterLifecycleEvidenceSubject.PlacementCreation", + "PersistentRasterLifecycleEvidenceSubject.MultiplePlacements", + "PersistentRasterLifecycleEvidenceSubject.PlacementUpdate", + "PersistentRasterLifecycleEvidenceSubject.PlacementDeletion", + "PersistentRasterLifecycleEvidenceSubject.ResourceDeletion", + } ) { + Assert.Contains( requiredToken, source, StringComparison.Ordinal ); + } + Assert.DoesNotContain( + "new PersistentRasterLifecycleEvidence(", + source, + StringComparison.Ordinal + ); + Assert.DoesNotContain( + "GetNextSourceOrdinal", + source, + StringComparison.Ordinal + ); + } + + [Fact] + public void ProductionInspectionProjectStillHasNoTerminalDependency() { + string repositoryRoot = GetRepositoryRoot(); + XDocument inspectionProject = XDocument.Load( + Path.Combine( + repositoryRoot, + "Icod.TermInfo.Inspection", + "Icod.TermInfo.Inspection.csproj" + ) + ); + + Assert.DoesNotContain( + inspectionProject.Descendants().Where( + element => + element.Name.LocalName == "PackageReference" + || element.Name.LocalName == "ProjectReference" + ), + element => + (element.Attribute( "Include" )?.Value ?? string.Empty).Contains( + "Icod.Terminal", + StringComparison.Ordinal + ) + ); + } + + [Fact] + public void RuntimeIntegrationSampleShowsStaticVerifyIntegrateReplanBoundary() { + string repositoryRoot = GetRepositoryRoot(); + string sampleDirectory = Path.Combine( + repositoryRoot, + "samples", + "Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample" + ); + string projectPath = Path.Combine( + sampleDirectory, + "Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample.csproj" + ); + string sourcePath = Path.Combine( sampleDirectory, "Program.cs" ); + string readmePath = Path.Combine( sampleDirectory, "README.md" ); + Assert.True( File.Exists( projectPath ) ); + Assert.True( File.Exists( sourcePath ) ); + Assert.True( File.Exists( readmePath ) ); + + XDocument project = XDocument.Load( projectPath ); + Assert.Equal( + "net8.0;net9.0;net10.0", + Assert.Single( + project.Descendants(), + element => element.Name.LocalName == "TargetFrameworks" + ).Value + ); + XElement inspectionReference = Assert.Single( + project.Descendants(), + element => element.Name.LocalName == "ProjectReference" + ); + Assert.EndsWith( + "Icod.TermInfo.Inspection.csproj", + inspectionReference.Attribute( "Include" )?.Value, + StringComparison.Ordinal + ); + XElement terminalReference = Assert.Single( + project.Descendants(), + element => + element.Name.LocalName == "PackageReference" + && element.Attribute( "Include" )?.Value == "Icod.Terminal" + ); + Assert.Equal( + "1.12.0", + terminalReference.Attribute( "Version" )?.Value + ); + + string source = File.ReadAllText( sourcePath ); + foreach ( string requiredToken in new[] { + "PersistentRasterLifecycleInspector.Inspect", + "PersistentRasterLifecyclePlanner.Plan", + "VerifyCapabilityAsync", + "TerminalCapability.PersistentRasterGraphics", + "PersistentRasterRuntimeLifecycleObservation", + "PersistentRasterRuntimeObservationSet", + "PersistentRasterRuntimeEvidenceIntegrator.Integrate", + "CreateLifecyclePlan", + } ) { + Assert.Contains( requiredToken, source, StringComparison.Ordinal ); + } + Assert.DoesNotContain( + "new PersistentRasterLifecycleEvidence(", + source, + StringComparison.Ordinal + ); + Assert.DoesNotContain( + "GetNextSourceOrdinal", + source, + StringComparison.Ordinal + ); + } + + [Fact] + public void ExistingLifecycleSampleUsesRuntimeObservationIntegrationInsteadOfManualVerifiedEvidence() { + string repositoryRoot = GetRepositoryRoot(); + string source = File.ReadAllText( + Path.Combine( + repositoryRoot, + "samples", + "Icod.TermInfo.PersistentRasterLifecycle.Sample", + "Program.cs" + ) + ); + + Assert.Contains( + "PersistentRasterRuntimeLifecycleObservation", + source, + StringComparison.Ordinal + ); + Assert.Contains( + "PersistentRasterRuntimeEvidenceIntegrator.Integrate", + source, + StringComparison.Ordinal + ); + Assert.Contains( + "CreateLifecyclePlan", + source, + StringComparison.Ordinal + ); + Assert.DoesNotContain( + "PersistentRasterLifecycleEvidenceKind.Verified", + source, + StringComparison.Ordinal + ); + } + + [Fact] + public void ExistingPlacementSampleUsesRuntimeObservationIntegrationAndRetainsTerminalExecutionValues() { + string repositoryRoot = GetRepositoryRoot(); + string source = File.ReadAllText( + Path.Combine( + repositoryRoot, + "samples", + "Icod.TermInfo.PersistentRasterPlacement.Sample", + "Program.cs" + ) + ); + + Assert.Contains( + "PersistentRasterRuntimeLifecycleObservation", + source, + StringComparison.Ordinal + ); + Assert.Contains( + "PersistentRasterRuntimePlacementObservation", + source, + StringComparison.Ordinal + ); + Assert.Contains( + "PersistentRasterRuntimeEvidenceIntegrator.Integrate", + source, + StringComparison.Ordinal + ); + Assert.Contains( + "CreatePlacementPlan", + source, + StringComparison.Ordinal + ); + Assert.DoesNotContain( + "PersistentRasterLifecycleEvidenceKind.Verified", + source, + StringComparison.Ordinal + ); + Assert.DoesNotContain( + "PersistentRasterPlacementEvidenceKind.Verified", + source, + StringComparison.Ordinal + ); + Assert.Contains( + "TerminalRasterSourceRectangle", + source, + StringComparison.Ordinal + ); + Assert.Contains( "ZIndex", source, StringComparison.Ordinal ); + } + + [Fact] + public void RuntimeEvidenceQualificationIsWiredIntoPackageVerification() { + string repositoryRoot = GetRepositoryRoot(); + string verification = File.ReadAllText( + Path.Combine( + repositoryRoot, + "packaging", + "VerifyPackageArtifact.ps1" + ) + ); + Assert.Contains( + "smoke-re07-runtime-evidence-interop.ps1", + verification, + StringComparison.Ordinal + ); + Assert.Contains( + "Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample", + verification, + StringComparison.Ordinal + ); + + string smokeScriptPath = Path.Combine( + repositoryRoot, + ".github", + "scripts", + "smoke-re07-runtime-evidence-interop.ps1" + ); + string nugetConfigPath = Path.Combine( + repositoryRoot, + ".github", + "scripts", + "package-smoke-re07.NuGet.Config" + ); + Assert.True( File.Exists( smokeScriptPath ) ); + Assert.True( File.Exists( nugetConfigPath ) ); + + XDocument nugetConfig = XDocument.Load( nugetConfigPath ); + string[] nugetPatterns = nugetConfig + .Descendants() + .Where( element => element.Name.LocalName == "package" ) + .Select( element => element.Attribute( "pattern" )?.Value ?? string.Empty ) + .ToArray(); + Assert.Contains( "Icod.TermInfo*", nugetPatterns ); + Assert.Contains( "Icod.Terminal", nugetPatterns ); + Assert.Contains( "Icod.Timing", nugetPatterns ); + } + + private static string GetRepositoryRoot() { + DirectoryInfo? directory = new( AppContext.BaseDirectory ); + while ( directory is not null ) { + if ( File.Exists( Path.Combine( directory.FullName, "Directory.Build.props" ) ) ) { + return directory.FullName; + } + directory = directory.Parent; + } + + throw new DirectoryNotFoundException( "Could not locate the repository root." ); + } +} diff --git a/tests/Icod.TermInfo.Inspection.Tests/src/RE08ReleaseClosureTests.cs b/tests/Icod.TermInfo.Inspection.Tests/src/RE08ReleaseClosureTests.cs new file mode 100644 index 000000000..96c264fc4 --- /dev/null +++ b/tests/Icod.TermInfo.Inspection.Tests/src/RE08ReleaseClosureTests.cs @@ -0,0 +1,545 @@ +using System.Globalization; +using System.Security.Cryptography; +using System.Text; +using System.Xml.Linq; +using Icod.TermInfo.Inspection; +using Xunit; + +namespace Icod.TermInfo.Inspection.Tests; + +public sealed class RE08ReleaseClosureTests { + private const string OneTwelveInspectionApiSha256 = + "f71501dcd27a530051c1a02083325144ced2b6173b6b967b9571c620815198f0"; + private const string OneThirteenInspectionApiSha256 = + "fd827a25abafb8e9ff3915567f45f9f2ec51b332bfd8a82dd4e4bca20290e764"; + private const string JsonV1SchemaSha256 = + "76578f421b254802d24453af6868edaf8c23c4b78a87c7e8ef86b233ff0e8500"; + private const string JsonV2SchemaSha256 = + "ae4d53608881344e902f02303c71e2d432500969e60cfb005d70feea607499d0"; + private const string JsonV3SchemaSha256 = + "33ca95aee120f84d0d160ac189f8ddb4db183361b7bd83885c99c1c8ed355a97"; + private const string JsonV4SchemaSha256 = + "6383052f389d903683a9e24d55b73c97eb165db89c6ab5f26dbcb5a3c7fdda87"; + private const string JsonV5SchemaSha256 = + "a151c7915d8b637d8bb64ef9d649168a4c120d7b9b7104299b6a78dab1f0a394"; + + [Fact] + public void ExactOneThirteenInspectionSurfaceHasFreezeInputs() { + Type[] exportedTypes = + typeof( PersistentRasterRuntimeObservationSet ).Assembly.GetExportedTypes(); + Assert.Equal( 90, exportedTypes.Length ); + Assert.Equal( + 9, + exportedTypes.Count( + type => type.FullName?.StartsWith( + "Icod.TermInfo.Inspection.PersistentRasterRuntime", + StringComparison.Ordinal + ) == true + ) + ); + + string freeze = ReadRequiredRepositoryFile( + "docs/1.13.0-INSPECTION-PUBLIC-API-FREEZE.md" + ); + string fingerprints = ReadRequiredRepositoryFile( + "docs/1.13.0-RE08-FREEZE-FINGERPRINTS.txt" + ); + Assert.Contains( + OneThirteenInspectionApiSha256, + freeze, + StringComparison.Ordinal + ); + Assert.Contains( "90 exported public types", freeze, StringComparison.Ordinal ); + Assert.Contains( + OneThirteenInspectionApiSha256, + fingerprints, + StringComparison.Ordinal + ); + } + + [Fact] + public void AllFiveJsonSchemasHaveExactFrozenFingerprints() { + string root = FindRepositoryRoot(); + (string Path, string Sha256)[] schemas = [ + ( + Path.Combine( root, "docs", "Icod.TermInfo.Inspection.schema.json" ), + JsonV1SchemaSha256 + ), + ( + Path.Combine( root, "docs", "Icod.TermInfo.Inspection.schema.v2.json" ), + JsonV2SchemaSha256 + ), + ( + Path.Combine( root, "docs", "Icod.TermInfo.Inspection.schema.v3.json" ), + JsonV3SchemaSha256 + ), + ( + Path.Combine( root, "docs", "Icod.TermInfo.Inspection.schema.v4.json" ), + JsonV4SchemaSha256 + ), + ( + Path.Combine( root, "docs", "Icod.TermInfo.Inspection.schema.v5.json" ), + JsonV5SchemaSha256 + ), + ]; + + foreach ( (string path, string sha256) in schemas ) { + Assert.Equal( sha256, NormalizedLfSha256( path ) ); + } + + string fingerprints = ReadRequiredRepositoryFile( + "docs/1.13.0-RE08-FREEZE-FINGERPRINTS.txt" + ); + foreach ( (string _, string sha256) in schemas ) { + Assert.Contains( sha256, fingerprints, StringComparison.Ordinal ); + } + } + + [Fact] + public void RuntimeObservationBoundsOrdinalsAndSnapshotsRemainFrozen() { + string maximumLabel = new( + 'x', + PersistentRasterRuntimeObservationOptions.MaximumSourceLabelLength + ); + List source = + Enumerable.Range( + 0, + PersistentRasterRuntimeObservationOptions.MaximumSupportedObservationCount + ) + .Select( + index => new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Supported, + maximumLabel, + ( + index + == PersistentRasterRuntimeObservationOptions.MaximumSupportedObservationCount - 1 + ) + ? int.MaxValue + : index + ) + ) + .ToList(); + PersistentRasterRuntimeObservationSet snapshot = new( + source, + Array.Empty(), + new PersistentRasterRuntimeObservationOptions( + PersistentRasterRuntimeObservationOptions.MaximumSupportedObservationCount + ) + ); + source.Clear(); + + Assert.Equal( + PersistentRasterRuntimeObservationOptions.MaximumSupportedObservationCount, + snapshot.Count + ); + Assert.Equal( + PersistentRasterRuntimeObservationOptions.MaximumSupportedObservationCount, + snapshot.LifecycleObservations.Count + ); + Assert.Equal( 0, snapshot.LifecycleObservations[ 0 ].SourceOrdinal ); + Assert.Equal( int.MaxValue, snapshot.LifecycleObservations[ ^1 ].SourceOrdinal ); + Assert.Equal( maximumLabel, snapshot.LifecycleObservations[ 0 ].SourceLabel ); + + Assert.Throws( + () => new PersistentRasterRuntimeObservationSet( + Enumerable.Range( + 0, + PersistentRasterRuntimeObservationOptions.MaximumSupportedObservationCount + 1 + ) + .Select( + index => new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PersistentUpload, + PersistentRasterRuntimeObservationOutcome.Inconclusive, + "one-past", + index + ) + ), + Array.Empty(), + new PersistentRasterRuntimeObservationOptions( + PersistentRasterRuntimeObservationOptions.MaximumSupportedObservationCount + ) + ) + ); + Assert.Throws( + () => new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + PersistentRasterRuntimeObservationOutcome.Supported, + new string( + 'x', + PersistentRasterRuntimeObservationOptions.MaximumSourceLabelLength + 1 + ), + 0 + ) + ); + } + + [Fact] + public void RuntimeIntegrationCapacityOrdinalAndContradictionRemainConservative() { + PersistentRasterLifecycleEvidence[] atCapacity = + Enumerable.Range( + 0, + PersistentRasterLifecycleEvidenceOptions.MaximumSupportedEvidenceCount + ) + .Select( + index => new PersistentRasterLifecycleEvidence( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + isPositive: true, + PersistentRasterLifecycleEvidenceKind.Declared, + $"re08-capacity-{index:D4}", + index + ) + ) + .ToArray(); + PersistentRasterLifecycleProfile capacityProfile = + PersistentRasterLifecycleClassifier.Classify( + atCapacity, + new PersistentRasterLifecycleEvidenceOptions( + PersistentRasterLifecycleEvidenceOptions.MaximumSupportedEvidenceCount + ) + ); + PersistentRasterRuntimeIntegrationResult capacityResult = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + capacityProfile, + CreateEmptyPlacementProfile(), + new PersistentRasterRuntimeObservationSet( + new[] { + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PersistentUpload, + PersistentRasterRuntimeObservationOutcome.Supported, + "capacity-import", + 0 + ), + }, + Array.Empty() + ) + ); + Assert.False( capacityResult.Succeeded ); + Assert.Empty( capacityResult.ImportedLifecycleEvidence ); + Assert.Equal( capacityProfile.Evidence, capacityResult.LifecycleProfile.Evidence ); + Assert.Contains( + capacityResult.Issues, + issue => issue.Kind + == PersistentRasterRuntimeIntegrationIssueKind.LifecycleEvidenceCapacityExhausted + ); + + PersistentRasterLifecycleProfile ordinalProfile = + PersistentRasterLifecycleClassifier.Classify( + new[] { + new PersistentRasterLifecycleEvidence( + PersistentRasterLifecycleEvidenceSubject.RasterDisplay, + isPositive: true, + PersistentRasterLifecycleEvidenceKind.Verified, + "max-ordinal", + int.MaxValue + ), + } + ); + PersistentRasterRuntimeIntegrationResult ordinalResult = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + ordinalProfile, + CreateEmptyPlacementProfile(), + new PersistentRasterRuntimeObservationSet( + new[] { + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PersistentUpload, + PersistentRasterRuntimeObservationOutcome.Supported, + "ordinal-import", + int.MaxValue + ), + }, + Array.Empty() + ) + ); + Assert.False( ordinalResult.Succeeded ); + Assert.Empty( ordinalResult.ImportedLifecycleEvidence ); + Assert.Contains( + ordinalResult.Issues, + issue => issue.Kind + == PersistentRasterRuntimeIntegrationIssueKind.LifecycleOrdinalSpaceExhausted + ); + + PersistentRasterRuntimeIntegrationResult contradiction = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + CreateEmptyLifecycleProfile(), + CreateEmptyPlacementProfile(), + new PersistentRasterRuntimeObservationSet( + new[] { + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PlacementCreation, + PersistentRasterRuntimeObservationOutcome.Supported, + "positive", + 0 + ), + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PlacementCreation, + PersistentRasterRuntimeObservationOutcome.Unsupported, + "negative", + 1 + ), + }, + Array.Empty() + ) + ); + Assert.True( contradiction.Succeeded ); + Assert.Equal( 2, contradiction.ImportedLifecycleEvidence.Count ); + Assert.Equal( + PersistentRasterLifecycleSupportStatus.Contradicted, + contradiction.LifecycleProfile.PlacementCreation + ); + } + + [Fact] + public void RuntimeObservationRenderingIsRepeatedAndCultureIndependent() { + PersistentRasterRuntimeObservationSet observations = new( + new[] { + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PersistentUpload, + PersistentRasterRuntimeObservationOutcome.Supported, + "i", + 1 + ), + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PersistentUpload, + PersistentRasterRuntimeObservationOutcome.Supported, + "I", + 0 + ), + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PersistentUpload, + PersistentRasterRuntimeObservationOutcome.Supported, + "I", + 0 + ), + new PersistentRasterRuntimeLifecycleObservation( + PersistentRasterLifecycleEvidenceSubject.PersistentUpload, + PersistentRasterRuntimeObservationOutcome.Inconclusive, + "İ", + 2 + ), + }, + Array.Empty() + ); + string invariant = TermInfoJsonRenderer.Render( observations ); + Assert.Equal( invariant, TermInfoJsonRenderer.Render( observations ) ); + + CultureInfo originalCulture = CultureInfo.CurrentCulture; + CultureInfo originalUiCulture = CultureInfo.CurrentUICulture; + try { + foreach ( string cultureName in new[] { "tr-TR", "fr-FR" } ) { + CultureInfo culture = CultureInfo.GetCultureInfo( cultureName ); + CultureInfo.CurrentCulture = culture; + CultureInfo.CurrentUICulture = culture; + Assert.Equal( invariant, TermInfoJsonRenderer.Render( observations ) ); + } + } finally { + CultureInfo.CurrentCulture = originalCulture; + CultureInfo.CurrentUICulture = originalUiCulture; + } + } + + [Fact] + public void ProductionDependencyAndRe07QualificationTopologyRemainFrozen() { + string root = FindRepositoryRoot(); + XDocument inspectionProject = XDocument.Load( + Path.Combine( + root, + "Icod.TermInfo.Inspection", + "Icod.TermInfo.Inspection.csproj" + ) + ); + Assert.DoesNotContain( + inspectionProject.Descendants(), + element => + (element.Name.LocalName == "PackageReference" + || element.Name.LocalName == "ProjectReference") + && (element.Attribute( "Include" )?.Value ?? string.Empty).Contains( + "Icod.Terminal", + StringComparison.Ordinal + ) + ); + + XDocument runtimeSmoke = XDocument.Load( + Path.Combine( + root, + "tools", + "runtime-evidence-package-smoke", + "Icod.TermInfo.RuntimeEvidence.PackageSmoke.csproj" + ) + ); + XElement[] packageReferences = runtimeSmoke + .Descendants() + .Where( element => element.Name.LocalName == "PackageReference" ) + .ToArray(); + Assert.Equal( 2, packageReferences.Length ); + Assert.Contains( + packageReferences, + element => + element.Attribute( "Include" )?.Value == "Icod.TermInfo.Inspection" + && element.Attribute( "Version" )?.Value + == "$(IcodTermInfoInspectionPackageVersion)" + ); + Assert.Contains( + packageReferences, + element => + element.Attribute( "Include" )?.Value == "Icod.Terminal" + && element.Attribute( "Version" )?.Value == "1.12.0" + ); + Assert.DoesNotContain( + runtimeSmoke.Descendants(), + element => element.Name.LocalName == "ProjectReference" + ); + + string verification = ReadRequiredRepositoryFile( + "packaging/VerifyPackageArtifact.ps1" + ); + Assert.Contains( + "smoke-re07-runtime-evidence-interop.ps1", + verification, + StringComparison.Ordinal + ); + Assert.Contains( + "Icod.TermInfo.PersistentRasterRuntimeIntegration.Sample.csproj", + verification, + StringComparison.Ordinal + ); + } + + [Fact] + public void CompatibilityVerifierFreezesWholeOneThirteenBeforeReconstruction() { + string verifier = ReadRequiredRepositoryFile( + ".github/scripts/verify-inspection-compatibility.ps1" + ); + Assert.Contains( + OneThirteenInspectionApiSha256, + verifier, + StringComparison.Ordinal + ); + Assert.Contains( + OneTwelveInspectionApiSha256, + verifier, + StringComparison.Ordinal + ); + Assert.Contains( + "1.13.0-INSPECTION-PUBLIC-API-FREEZE.md", + verifier, + StringComparison.Ordinal + ); + Assert.Contains( + "Verified exact 1.13 Inspection public API SHA-256", + verifier, + StringComparison.Ordinal + ); + } + + [Fact] + public void ReleaseDocumentationDescribesStableOneThirteen() { + string buildProperties = ReadRequiredRepositoryFile( "Directory.Build.props" ); + string rootReadme = ReadRequiredRepositoryFile( "README.md" ); + string inspectionReadme = ReadRequiredRepositoryFile( + "Icod.TermInfo.Inspection/README.md" + ); + string inspectionProject = ReadRequiredRepositoryFile( + "Icod.TermInfo.Inspection/Icod.TermInfo.Inspection.csproj" + ); + string versioning = ReadRequiredRepositoryFile( "docs/VERSIONING.md" ); + string compatibility = ReadRequiredRepositoryFile( "docs/COMPATIBILITY.md" ); + string roadmap = ReadRequiredRepositoryFile( + "Icod.TermInfo-Post-1.0-Development-Roadmap.md" + ); + string guide = ReadRequiredRepositoryFile( + "docs/1.13.0-PERSISTENT-RASTER-RUNTIME-EVIDENCE-GUIDE.md" + ); + string hardening = ReadRequiredRepositoryFile( + "docs/1.13.0-RE08-RELEASE-HARDENING-AND-FREEZE.md" + ); + string audit = ReadRequiredRepositoryFile( + "docs/1.13.0-RELEASE-AUDIT.md" + ); + + Assert.Contains( + "1.13.0", + buildProperties, + StringComparison.Ordinal + ); + Assert.Contains( "1.13", rootReadme, StringComparison.Ordinal ); + Assert.Contains( "1.13", inspectionReadme, StringComparison.Ordinal ); + Assert.Contains( + "dotnet add package Icod.TermInfo.Inspection --version 1.13.0", + rootReadme, + StringComparison.Ordinal + ); + Assert.Contains( + "1.13.0", + inspectionProject, + StringComparison.Ordinal + ); + Assert.Contains( "## 1.13 release line", versioning, StringComparison.Ordinal ); + Assert.Contains( + "## 1.13 compatibility freeze", + compatibility, + StringComparison.Ordinal + ); + Assert.Contains( "1.13", roadmap, StringComparison.Ordinal ); + Assert.Contains( "PersistentRasterRuntime", guide, StringComparison.Ordinal ); + Assert.Contains( "1.13.0-Alpha-8", hardening, StringComparison.Ordinal ); + Assert.Contains( "1.13.0-Alpha-8", audit, StringComparison.Ordinal ); + } + + private static PersistentRasterLifecycleProfile CreateEmptyLifecycleProfile() { + return PersistentRasterLifecycleClassifier.Classify( + Array.Empty() + ); + } + + private static PersistentRasterPlacementProfile CreateEmptyPlacementProfile() { + return PersistentRasterPlacementClassifier.Classify( + Array.Empty() + ); + } + + private static string NormalizedLfSha256( + string path + ) { + string text = + File.ReadAllText( path ) + .Replace( "\r\n", "\n", StringComparison.Ordinal ) + .Replace( '\r', '\n' ); + return Convert.ToHexString( + SHA256.HashData( + Encoding.UTF8.GetBytes( text ) + ) + ).ToLowerInvariant(); + } + + private static string ReadRequiredRepositoryFile( + string relativePath + ) { + ArgumentException.ThrowIfNullOrWhiteSpace( relativePath ); + string path = Path.Combine( + FindRepositoryRoot(), + relativePath.Replace( + '/', + Path.DirectorySeparatorChar + ) + ); + Assert.True( + File.Exists( path ), + $"Required RE08 release-closure file is missing: {relativePath}" + ); + return File.ReadAllText( path ); + } + + private static string FindRepositoryRoot() { + DirectoryInfo? current = new( AppContext.BaseDirectory ); + while ( current is not null ) { + if ( File.Exists( Path.Combine( current.FullName, "Icod.TermInfo.sln" ) ) ) { + return current.FullName; + } + current = current.Parent; + } + throw new DirectoryNotFoundException( + "Could not locate repository root." + ); + } +} diff --git a/tests/Icod.TermInfo.Inspection.Tests/src/RL07PackageQualificationTests.cs b/tests/Icod.TermInfo.Inspection.Tests/src/RL07PackageQualificationTests.cs index 3cc57eb85..97717b086 100644 --- a/tests/Icod.TermInfo.Inspection.Tests/src/RL07PackageQualificationTests.cs +++ b/tests/Icod.TermInfo.Inspection.Tests/src/RL07PackageQualificationTests.cs @@ -170,12 +170,22 @@ public void LifecycleSampleUsesOnlyInspectionAndIsWiredIntoArtifactVerification( StringComparison.Ordinal ); Assert.Contains( - "PersistentRasterLifecycleEvidenceKind.Verified", + "PersistentRasterRuntimeLifecycleObservation", source, StringComparison.Ordinal ); Assert.Contains( - "PersistentRasterLifecyclePlanner.Plan", + "PersistentRasterRuntimeEvidenceIntegrator.Integrate", + source, + StringComparison.Ordinal + ); + Assert.Contains( + "CreateLifecyclePlan", + source, + StringComparison.Ordinal + ); + Assert.DoesNotContain( + "PersistentRasterLifecycleEvidenceKind.Verified", source, StringComparison.Ordinal ); diff --git a/tests/Icod.TermInfo.Inspection.Tests/src/RL08ReleaseClosureTests.cs b/tests/Icod.TermInfo.Inspection.Tests/src/RL08ReleaseClosureTests.cs index 0237e79c5..e79623fdf 100644 --- a/tests/Icod.TermInfo.Inspection.Tests/src/RL08ReleaseClosureTests.cs +++ b/tests/Icod.TermInfo.Inspection.Tests/src/RL08ReleaseClosureTests.cs @@ -31,6 +31,9 @@ public void ExactOneElevenInspectionSurfaceIsFrozen() { string oneTwelveAdditions = ReadRepositoryFile( "docs/1.12.0-PG01-INSPECTION-PUBLIC-API-ADDITIONS.txt" ); + string oneThirteenAdditions = ReadRepositoryFile( + "docs/1.13.0-RE01-INSPECTION-PUBLIC-API-ADDITIONS.txt" + ); HashSet approvedOneTwelveTypes = oneTwelveAdditions .Split( '\n' ) .Select( line => line.Trim() ) @@ -40,9 +43,25 @@ public void ExactOneElevenInspectionSurfaceIsFrozen() { && !line.StartsWith( "#", StringComparison.Ordinal ) ) .ToHashSet( StringComparer.Ordinal ); + HashSet approvedOneThirteenTypes = oneThirteenAdditions + .Split( '\n' ) + .Select( line => line.Trim() ) + .Where( + line => + line.Length > 0 + && !line.StartsWith( "#", StringComparison.Ordinal ) + ) + .ToHashSet( StringComparer.Ordinal ); Type[] currentTypes = typeof( PersistentRasterLifecycleProfile ).Assembly.GetExportedTypes(); - Type[] reconstructedOneElevenTypes = currentTypes + Type[] reconstructedOneTwelveTypes = currentTypes + .Where( + type => + type.FullName is null + || !approvedOneThirteenTypes.Contains( type.FullName ) + ) + .ToArray(); + Type[] reconstructedOneElevenTypes = reconstructedOneTwelveTypes .Where( type => type.FullName is null @@ -52,8 +71,18 @@ type.FullName is null Assert.Equal( 67, reconstructedOneElevenTypes.Length ); Assert.Equal( - approvedOneTwelveTypes.Count, + approvedOneThirteenTypes.Count, currentTypes.Count( + type => + type.FullName?.StartsWith( + "Icod.TermInfo.Inspection.PersistentRasterRuntime", + StringComparison.Ordinal + ) == true + ) + ); + Assert.Equal( + approvedOneTwelveTypes.Count, + reconstructedOneTwelveTypes.Count( type => type.FullName?.StartsWith( "Icod.TermInfo.Inspection.PersistentRasterPlacement", @@ -62,6 +91,16 @@ type.FullName is null ) ); foreach ( string approvedType in approvedOneTwelveTypes ) { + Assert.Contains( + reconstructedOneTwelveTypes, + type => string.Equals( + type.FullName, + approvedType, + StringComparison.Ordinal + ) + ); + } + foreach ( string approvedType in approvedOneThirteenTypes ) { Assert.Contains( currentTypes, type => string.Equals( @@ -154,6 +193,11 @@ public void ReleaseVerifierRequiresExactOneElevenAndRetainsOneTenCompatibility() compatibility, StringComparison.Ordinal ); + Assert.Contains( + "1.13.0-RE01-INSPECTION-PUBLIC-API-ADDITIONS.txt", + compatibility, + StringComparison.Ordinal + ); } [Fact] diff --git a/tests/Icod.TermInfo.Inspection.Tests/src/RP08ReleaseClosureTests.cs b/tests/Icod.TermInfo.Inspection.Tests/src/RP08ReleaseClosureTests.cs index 4246723bd..d1fc0703d 100644 --- a/tests/Icod.TermInfo.Inspection.Tests/src/RP08ReleaseClosureTests.cs +++ b/tests/Icod.TermInfo.Inspection.Tests/src/RP08ReleaseClosureTests.cs @@ -6,7 +6,7 @@ namespace Icod.TermInfo.Inspection.Tests; public sealed class RP08ReleaseClosureTests { - private const string DevelopmentVersion = "1.12.0"; + private const string DevelopmentVersion = "1.13.0"; private const string HistoricalDevelopmentVersion = "1.8.0-Alpha-8"; private const string Rp07Head = "a88237d0d2f0ecdf74a7d96f6ff1cb9a2e8e647d"; @@ -259,7 +259,7 @@ public void CoordinatedMetadataPreservesStableClosureAndIdentifiesCurrentDevelop StringComparison.Ordinal ); Assert.Contains( - "1.12.0", + "1.13.0", activeRoadmap, StringComparison.Ordinal ); diff --git a/tests/Icod.TermInfo.Inspection.Tests/src/RS08ContractTests.cs b/tests/Icod.TermInfo.Inspection.Tests/src/RS08ContractTests.cs index 20450d3fd..43377d8f2 100644 --- a/tests/Icod.TermInfo.Inspection.Tests/src/RS08ContractTests.cs +++ b/tests/Icod.TermInfo.Inspection.Tests/src/RS08ContractTests.cs @@ -6,7 +6,7 @@ namespace Icod.TermInfo.Inspection.Tests; public sealed class RS08ContractTests { private const string ReleaseVersion = "1.7.0"; - private const string CurrentDevelopmentVersion = "1.12.0"; + private const string CurrentDevelopmentVersion = "1.13.0"; [Fact] public void CurrentDevelopmentRetainsRs08ReleaseRecords() { diff --git a/tests/Icod.TermInfo.Router.Tests/src/CommandTests.cs b/tests/Icod.TermInfo.Router.Tests/src/CommandTests.cs index 9d5e6a96c..c0fb95032 100644 --- a/tests/Icod.TermInfo.Router.Tests/src/CommandTests.cs +++ b/tests/Icod.TermInfo.Router.Tests/src/CommandTests.cs @@ -48,7 +48,7 @@ string option ); Assert.Equal( 0, status ); - Assert.Contains( "1.12.0", ReadText( stdout ) ); + Assert.Contains( "1.13.0", ReadText( stdout ) ); Assert.Empty( ReadText( stderr ) ); } @@ -73,7 +73,7 @@ string commandName ); Assert.Equal( 0, status ); - Assert.Contains( "1.12.0", ReadText( stdout ) ); + Assert.Contains( "1.13.0", ReadText( stdout ) ); Assert.Empty( ReadText( stderr ) ); } diff --git a/tests/Icod.TermInfo.Router.Tests/src/ContractTests.cs b/tests/Icod.TermInfo.Router.Tests/src/ContractTests.cs index 84ea54fef..ad1c6da9a 100644 --- a/tests/Icod.TermInfo.Router.Tests/src/ContractTests.cs +++ b/tests/Icod.TermInfo.Router.Tests/src/ContractTests.cs @@ -18,7 +18,7 @@ public void CoordinatedProjectsConsumeCentralVersionAuthority() { "Directory.Build.props" ); Assert.StartsWith( - "1.12.0", + "1.13.0", ReadRequiredProperty( buildProperties, "IcodTermInfoSuiteVersion" diff --git a/tests/Icod.TermInfo.Termcap.Tests/src/TC08ContractTests.cs b/tests/Icod.TermInfo.Termcap.Tests/src/TC08ContractTests.cs index cc00a64f3..4697ed777 100644 --- a/tests/Icod.TermInfo.Termcap.Tests/src/TC08ContractTests.cs +++ b/tests/Icod.TermInfo.Termcap.Tests/src/TC08ContractTests.cs @@ -4,7 +4,7 @@ namespace Icod.TermInfo.Termcap.Tests; public sealed class TC08ContractTests { - private const string CurrentDevelopmentVersion = "1.12.0"; + private const string CurrentDevelopmentVersion = "1.13.0"; private const string HistoricalTc08Version = "1.6.0-Alpha-8"; private const string HistoricalTc07Version = "1.6.0-Alpha-7"; private const string TermcapApiSnapshotSha256 = diff --git a/tests/Icod.TermInfo.Tests/src/T45CompletionGateTests.cs b/tests/Icod.TermInfo.Tests/src/T45CompletionGateTests.cs index 323cfd955..77f5c22b3 100644 --- a/tests/Icod.TermInfo.Tests/src/T45CompletionGateTests.cs +++ b/tests/Icod.TermInfo.Tests/src/T45CompletionGateTests.cs @@ -33,7 +33,7 @@ public void AssemblyRetainsStableIdentityDuringCurrentDevelopment() { )[ 0 ]; Assert.StartsWith( - "1.12.0", + "1.13.0", semanticVersion, StringComparison.Ordinal ); @@ -61,7 +61,7 @@ public void ProjectMetadataIdentifiesCurrentDevelopmentAndStableAssembly() { ); Assert.StartsWith( - "1.12.0", + "1.13.0", ReadRequiredProperty( buildProperties, "IcodTermInfoSuiteVersion" @@ -111,27 +111,27 @@ public void FinalReadmeUsesStablePackageVersionAndPolicies() { ); Assert.Contains( - "dotnet add package Icod.TermInfo --version 1.12.0", + "dotnet add package Icod.TermInfo --version 1.13.0", readme ); Assert.Contains( - "dotnet add package Icod.TermInfo.Source --version 1.12.0", + "dotnet add package Icod.TermInfo.Source --version 1.13.0", readme ); Assert.Contains( - "dotnet add package Icod.TermInfo.Termcap --version 1.12.0", + "dotnet add package Icod.TermInfo.Termcap --version 1.13.0", readme ); Assert.Contains( - "dotnet add package Icod.TermInfo.Compiler --version 1.12.0", + "dotnet add package Icod.TermInfo.Compiler --version 1.13.0", readme ); Assert.Contains( - "dotnet add package Icod.TermInfo.Inspection --version 1.12.0", + "dotnet add package Icod.TermInfo.Inspection --version 1.13.0", readme ); Assert.Contains( - "dotnet tool install --global Icod.TermInfo.Tools --version 1.12.0", + "dotnet tool install --global Icod.TermInfo.Tools --version 1.13.0", readme ); Assert.DoesNotContain( @@ -166,6 +166,10 @@ public void FinalReadmeUsesStablePackageVersionAndPolicies() { "docs/1.12.0-RELEASE-AUDIT.md", readme ); + Assert.Contains( + "docs/1.13.0-RELEASE-AUDIT.md", + readme + ); } [Fact] diff --git a/tests/Icod.TermInfo.Tic.Tests/src/CommandTests.cs b/tests/Icod.TermInfo.Tic.Tests/src/CommandTests.cs index bc20987db..8fc717b62 100644 --- a/tests/Icod.TermInfo.Tic.Tests/src/CommandTests.cs +++ b/tests/Icod.TermInfo.Tic.Tests/src/CommandTests.cs @@ -54,7 +54,7 @@ string argument ); Assert.Equal( CommandExitCodes.Success, status ); - Assert.Contains( "1.12.0", ReadText( stdout ) ); + Assert.Contains( "1.13.0", ReadText( stdout ) ); Assert.Empty( ReadText( stderr ) ); } diff --git a/tests/Icod.TermInfo.Tic.Tests/src/ReleaseClosureTests.cs b/tests/Icod.TermInfo.Tic.Tests/src/ReleaseClosureTests.cs index b9f37ef4a..2e929fb5a 100644 --- a/tests/Icod.TermInfo.Tic.Tests/src/ReleaseClosureTests.cs +++ b/tests/Icod.TermInfo.Tic.Tests/src/ReleaseClosureTests.cs @@ -5,7 +5,7 @@ namespace Icod.TermInfo.Tic.Tests; public sealed class ReleaseClosureTests { private const string StableReleaseVersion = "1.9.0"; - private const string DevelopmentVersion = "1.12.0"; + private const string DevelopmentVersion = "1.13.0"; private const string VersionReference = "$(IcodTermInfoSuiteVersion)"; private const string StableAssemblyVersion = "1.0.0.0"; diff --git a/tests/Icod.TermInfo.Toe.Tests/src/CommandTests.cs b/tests/Icod.TermInfo.Toe.Tests/src/CommandTests.cs index 0cb0783ac..e88f0657f 100644 --- a/tests/Icod.TermInfo.Toe.Tests/src/CommandTests.cs +++ b/tests/Icod.TermInfo.Toe.Tests/src/CommandTests.cs @@ -48,7 +48,7 @@ public async Task VersionReportsCoordinatedDevelopmentVersion( string option ) { ); Assert.Equal( CommandExitCodes.Success, status ); - Assert.Contains( "1.12.0", ReadText( stdout ) ); + Assert.Contains( "1.13.0", ReadText( stdout ) ); Assert.Empty( ReadText( stderr ) ); } diff --git a/tools/runtime-evidence-package-smoke/Icod.TermInfo.RuntimeEvidence.PackageSmoke.csproj b/tools/runtime-evidence-package-smoke/Icod.TermInfo.RuntimeEvidence.PackageSmoke.csproj new file mode 100644 index 000000000..2ff018f44 --- /dev/null +++ b/tools/runtime-evidence-package-smoke/Icod.TermInfo.RuntimeEvidence.PackageSmoke.csproj @@ -0,0 +1,14 @@ + + + Exe + net8.0;net9.0;net10.0 + enable + enable + false + 0.0.0-local-validation-required + + + + + + diff --git a/tools/runtime-evidence-package-smoke/Program.cs b/tools/runtime-evidence-package-smoke/Program.cs new file mode 100644 index 000000000..e1e373cee --- /dev/null +++ b/tools/runtime-evidence-package-smoke/Program.cs @@ -0,0 +1,120 @@ +using Icod.Terminal; +using Icod.TermInfo.Inspection; + +PersistentRasterRuntimeObservationOutcome verifiedOutcome = + MapPersistentRasterSupport( TerminalCapabilitySupport.Verified ); +PersistentRasterRuntimeObservationOutcome unsupportedOutcome = + MapPersistentRasterSupport( TerminalCapabilitySupport.Unsupported ); +if ( + verifiedOutcome != PersistentRasterRuntimeObservationOutcome.Supported + || unsupportedOutcome != PersistentRasterRuntimeObservationOutcome.Unsupported +) { + throw new InvalidOperationException( + "The Terminal 1.12 support vocabulary did not map to TermInfo runtime outcomes." + ); +} + +PersistentRasterLifecycleProfile lifecycleProfile = + PersistentRasterLifecycleClassifier.Classify( + Array.Empty() + ); +PersistentRasterPlacementProfile placementProfile = + PersistentRasterPlacementClassifier.Classify( + Array.Empty() + ); +PersistentRasterRuntimeObservationSet observations = new( + CreatePersistentRasterLifecycleObservations( + verifiedOutcome, + "Icod.Terminal 1.12.0 PersistentRasterGraphics" + ), + Array.Empty() +); +PersistentRasterRuntimeIntegrationResult integration = + PersistentRasterRuntimeEvidenceIntegrator.Integrate( + lifecycleProfile, + placementProfile, + observations + ); +if ( !integration.Succeeded || integration.ImportedLifecycleEvidence.Count != 7 ) { + throw new InvalidOperationException( + "The package-only Terminal status adapter did not import the complete coarse lifecycle observation set." + ); +} + +PersistentRasterLifecyclePlan plan = integration.CreateLifecyclePlan( + new PersistentRasterLifecycleRequest( + uploadResource: true, + placementCount: 2, + updatePlacement: true, + deletePlacement: true, + deleteResource: true, + requireAcknowledgedUpload: true + ) +); +if ( plan.Status != PersistentRasterLifecyclePlanStatus.Success ) { + throw new InvalidOperationException( + "The package-only runtime observations did not strengthen the lifecycle plan to success." + ); +} + +Console.WriteLine( + "RE07 package-only Icod.Terminal 1.12 runtime-evidence interoperability passed." +); + +static PersistentRasterRuntimeObservationOutcome MapPersistentRasterStatus( + TerminalCapabilityStatus status +) { + if ( status.Capability != TerminalCapability.PersistentRasterGraphics ) { + throw new ArgumentException( + "The status must describe TerminalCapability.PersistentRasterGraphics.", + nameof( status ) + ); + } + return MapPersistentRasterSupport( status.Support ); +} + +static PersistentRasterRuntimeObservationOutcome MapPersistentRasterSupport( + TerminalCapabilitySupport support +) => + support switch { + TerminalCapabilitySupport.Verified => + PersistentRasterRuntimeObservationOutcome.Supported, + TerminalCapabilitySupport.Unsupported => + PersistentRasterRuntimeObservationOutcome.Unsupported, + TerminalCapabilitySupport.Unknown + or TerminalCapabilitySupport.Advertised => + PersistentRasterRuntimeObservationOutcome.Inconclusive, + _ => throw new ArgumentOutOfRangeException( + nameof( support ), + support, + "The Terminal capability support state must be defined." + ), + }; + +static IReadOnlyList + CreatePersistentRasterLifecycleObservations( + PersistentRasterRuntimeObservationOutcome outcome, + string sourceLabel + ) { + ArgumentException.ThrowIfNullOrWhiteSpace( sourceLabel ); + PersistentRasterLifecycleEvidenceSubject[] subjects = [ + PersistentRasterLifecycleEvidenceSubject.PersistentUpload, + PersistentRasterLifecycleEvidenceSubject.AcknowledgedUpload, + PersistentRasterLifecycleEvidenceSubject.PlacementCreation, + PersistentRasterLifecycleEvidenceSubject.MultiplePlacements, + PersistentRasterLifecycleEvidenceSubject.PlacementUpdate, + PersistentRasterLifecycleEvidenceSubject.PlacementDeletion, + PersistentRasterLifecycleEvidenceSubject.ResourceDeletion, + ]; + PersistentRasterRuntimeLifecycleObservation[] observations = + new PersistentRasterRuntimeLifecycleObservation[ subjects.Length ]; + for ( int index = 0; index < subjects.Length; index++ ) { + observations[ index ] = new PersistentRasterRuntimeLifecycleObservation( + subjects[ index ], + outcome, + sourceLabel, + index + ); + } + return observations; +}