Close P1 regression and ownership evidence gates #35
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Linux x64 Native AOT | |
| on: | |
| push: | |
| branches: [master] | |
| pull_request: | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| jobs: | |
| linux-x64-native-aot: | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up .NET 10.0.400 | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: 10.0.400 | |
| - name: Install Native AOT toolchain | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install --no-install-recommends -y clang zlib1g-dev sqlite3 | |
| - name: Install rustc 1.98 oracle | |
| run: rustup toolchain install 1.98.0 --profile minimal | |
| - name: Restore | |
| run: dotnet restore RustSharp.slnx | |
| - name: Build | |
| run: dotnet build RustSharp.slnx -c Release --no-restore | |
| - name: Validate Linux probe shell | |
| run: bash -n eng/Invoke-LinuxNativeAotProbe.sh | |
| - name: Run executable harness | |
| run: | | |
| set -o pipefail | |
| mkdir -p artifacts/p0 | |
| dotnet run --project tests/RustSharp.Tests/RustSharp.Tests.csproj -c Release --no-build --no-restore | tee artifacts/p0/test-harness.log | |
| - name: Restore IL verifier | |
| run: dotnet tool restore --tool-manifest .config/dotnet-tools.json | |
| - name: Compile vertical sample | |
| run: dotnet run --project src/RustSharp.Cli -c Release --no-build --no-restore -- compile samples/hello.rs --output artifacts/p0/hello.dll | |
| - name: Verify IL | |
| run: pwsh -NoProfile -File eng/Invoke-ILVerify.ps1 -AssemblyPath artifacts/p0/hello.dll -EvidencePath artifacts/p0/hello.ilverify.json | |
| - name: Run rustc differential harness | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| set +e | |
| dotnet run --project tools/RustSharp.Conformance -c Release --no-build --no-restore -- --profile vertical-slice-v1 --oracle rustc-1.98 | |
| harness_exit=$? | |
| set -e | |
| report="artifacts/conformance/vertical-slice-v1.json" | |
| if [[ ! -f "$report" ]]; then | |
| echo "Conformance harness did not write $report" >&2 | |
| exit 1 | |
| fi | |
| read_json_status() { | |
| local report_path="$1" | |
| local report_scope="$2" | |
| REPORT_PATH="$report_path" REPORT_SCOPE="$report_scope" pwsh -NoLogo -NoProfile -NonInteractive -Command '$ErrorActionPreference = "Stop"; $report = Get-Content -Raw -LiteralPath $env:REPORT_PATH | ConvertFrom-Json; $value = if ($env:REPORT_SCOPE -eq "summary") { $report.Summary.Status } else { $report.Status }; if ([string]::IsNullOrWhiteSpace([string]$value)) { throw "Report status is missing." }; [Console]::Write([string]$value)' | |
| } | |
| if ! report_status="$(read_json_status "$report" summary)"; then | |
| echo "Could not parse conformance report status from $report" >&2 | |
| exit 1 | |
| fi | |
| if (( harness_exit == 2 )) && [[ "$report_status" == "blocked" ]]; then | |
| echo "::warning::Conformance harness is explicitly blocked; see $report" | |
| exit "$harness_exit" | |
| fi | |
| if (( harness_exit != 0 )); then | |
| exit "$harness_exit" | |
| fi | |
| if [[ "$report_status" != "passed" ]]; then | |
| echo "Conformance harness returned success without a passed summary report (status=$report_status)" >&2 | |
| exit 1 | |
| fi | |
| - name: Run safe-core lexing corpus | |
| shell: pwsh | |
| run: | | |
| $ErrorActionPreference = 'Stop' | |
| $reportPath = Join-Path $PWD 'artifacts/conformance/safe-core-lexing.json' | |
| & dotnet run --project tools/RustSharp.Conformance -c Release --no-build --no-restore -- --profile safe-core-lexing | |
| $harnessExitCode = $LASTEXITCODE | |
| if (-not (Test-Path -LiteralPath $reportPath -PathType Leaf)) { | |
| throw "Safe-core lexing harness did not write '$reportPath'." | |
| } | |
| if ($harnessExitCode -ne 0) { | |
| exit $harnessExitCode | |
| } | |
| $report = Get-Content -Raw -LiteralPath $reportPath | ConvertFrom-Json | |
| if ($report.PSObject.Properties.Match('SchemaVersion').Count -ne 1 -or | |
| $report.PSObject.Properties.Match('Cases').Count -ne 1 -or | |
| $report.PSObject.Properties.Match('HarnessError').Count -ne 1 -or | |
| $report.Scope.PSObject.Properties.Match('RustcConformance').Count -ne 1 -or | |
| $report.Scope.PSObject.Properties.Match('RuntimeConformance').Count -ne 1 -or | |
| $report.Scope.PSObject.Properties.Match('Statement').Count -ne 1 -or | |
| $report.Manifest.PSObject.Properties.Match('Validated').Count -ne 1 -or | |
| $report.Manifest.PSObject.Properties.Match('Path').Count -ne 1 -or | |
| $report.Manifest.PSObject.Properties.Match('Sha256').Count -ne 1 -or | |
| $report.Manifest.PSObject.Properties.Match('Version').Count -ne 1 -or | |
| $report.Manifest.PSObject.Properties.Match('Lexer').Count -ne 1 -or | |
| $report.Manifest.PSObject.Properties.Match('Denominator').Count -ne 1 -or | |
| $report.Manifest.PSObject.Properties.Match('CaseCount').Count -ne 1 -or | |
| $report.Manifest.PSObject.Properties.Match('Error').Count -ne 1 -or | |
| $report.Summary.PSObject.Properties.Match('Status').Count -ne 1 -or | |
| $report.Summary.PSObject.Properties.Match('Denominator').Count -ne 1 -or | |
| $report.Summary.PSObject.Properties.Match('Executed').Count -ne 1 -or | |
| $report.Summary.PSObject.Properties.Match('Passed').Count -ne 1 -or | |
| $report.Summary.PSObject.Properties.Match('Failed').Count -ne 1 -or | |
| $report.Summary.PSObject.Properties.Match('Errors').Count -ne 1 -or | |
| $report.Summary.PSObject.Properties.Match('Skipped').Count -ne 1 -or | |
| $report.Summary.PSObject.Properties.Match('ExitCode').Count -ne 1 -or | |
| $report.Execution.PSObject.Properties.Match('DeadlineExpired').Count -ne 1) { | |
| throw 'Safe-core lexing evidence scope or manifest metadata is missing.' | |
| } | |
| if ($report.SchemaVersion -isnot [long] -or | |
| $report.Manifest.Version -isnot [long] -or | |
| $report.Manifest.Denominator -isnot [long] -or | |
| $report.Manifest.CaseCount -isnot [long] -or | |
| $report.Summary.Denominator -isnot [long] -or | |
| $report.Summary.Executed -isnot [long] -or | |
| $report.Summary.Passed -isnot [long] -or | |
| $report.Summary.Failed -isnot [long] -or | |
| $report.Summary.Errors -isnot [long] -or | |
| $report.Summary.Skipped -isnot [long] -or | |
| $report.Summary.ExitCode -isnot [long]) { | |
| throw 'Safe-core lexing evidence integer metadata is missing or is not a JSON integer.' | |
| } | |
| if ($report.Scope.RustcConformance -isnot [bool] -or | |
| $report.Scope.RuntimeConformance -isnot [bool] -or | |
| $report.Manifest.Validated -isnot [bool] -or | |
| $report.Execution.DeadlineExpired -isnot [bool]) { | |
| throw 'Safe-core lexing evidence boolean metadata is missing or is not a JSON boolean.' | |
| } | |
| $manifestPath = Join-Path $PWD 'tools/RustSharp.Conformance/fixtures/safe-core-lexing-manifest.json' | |
| $manifest = Get-Content -Raw -LiteralPath $manifestPath | ConvertFrom-Json | |
| $expectedDenominator = $manifest.denominator | |
| $expectedManifestSha256 = (Get-FileHash -LiteralPath $manifestPath -Algorithm SHA256).Hash | |
| if ($expectedDenominator -ne 24 -or @($manifest.cases).Count -ne $expectedDenominator -or | |
| ($manifest.coverage | ConvertTo-Json -Depth 8 -Compress) -cne ($report.Manifest.Coverage | ConvertTo-Json -Depth 8 -Compress) -or | |
| (@($manifest.cases.id | Sort-Object) -join ',') -cne (@($report.Cases.Id | Sort-Object) -join ',')) { | |
| throw 'Lexical category or case identity evidence differs from the versioned corpus.' | |
| } | |
| $denominator = $report.Summary.Denominator | |
| $invalidCases = @($report.Cases | Where-Object { | |
| $_.PSObject.Properties.Match('Status').Count -ne 1 -or | |
| $_.PSObject.Properties.Match('LexerInvoked').Count -ne 1 -or | |
| $_.PSObject.Properties.Match('ExpectationsMatched').Count -ne 1 -or | |
| $_.PSObject.Properties.Match('IsTruncated').Count -ne 1 -or | |
| $_.PSObject.Properties.Match('SourceRoundTrips').Count -ne 1 -or | |
| $_.PSObject.Properties.Match('LexicalCoverageExact').Count -ne 1 -or | |
| $_.PSObject.Properties.Match('SpansExact').Count -ne 1 -or | |
| $_.LexerInvoked -isnot [bool] -or | |
| $_.ExpectationsMatched -isnot [bool] -or | |
| $_.IsTruncated -isnot [bool] -or | |
| $_.SourceRoundTrips -isnot [bool] -or | |
| $_.LexicalCoverageExact -isnot [bool] -or | |
| $_.SpansExact -isnot [bool] -or | |
| [string]::IsNullOrWhiteSpace([string] $_.Id) -or | |
| [string]::IsNullOrWhiteSpace([string] $_.Source) -or | |
| [string]::IsNullOrWhiteSpace([string] $_.SourceSha256) -or | |
| [string] $_.Status -ne 'passed' -or | |
| $_.LexerInvoked -ne $true -or | |
| $_.ExpectationsMatched -ne $true -or | |
| $_.IsTruncated -ne $false -or | |
| $_.SourceRoundTrips -ne $true -or | |
| $_.LexicalCoverageExact -ne $true -or | |
| $_.SpansExact -ne $true | |
| }) | |
| if ($report.SchemaVersion -ne 1 -or | |
| [string] $report.Profile -ne 'safe-core-lexing' -or | |
| [string] $report.EvidenceKind -ne 'lexer-acceptance' -or | |
| [string] $report.Summary.Status -ne 'passed' -or | |
| $denominator -ne $expectedDenominator -or | |
| $report.Summary.Executed -ne $denominator -or | |
| $report.Summary.Passed -ne $denominator -or | |
| $report.Summary.Failed -ne 0 -or | |
| $report.Summary.Errors -ne 0 -or | |
| $report.Summary.Skipped -ne 0 -or | |
| $report.Summary.ExitCode -ne 0 -or | |
| $report.Scope.RustcConformance -ne $false -or | |
| $report.Scope.RuntimeConformance -ne $false -or | |
| [string] $report.Scope.Statement -ne 'This report measures only RustSharp safe-core lexer acceptance; it is not rustc differential or runtime conformance evidence.' -or | |
| $report.Manifest.Validated -ne $true -or | |
| [string] $report.Manifest.Path -ne 'tools/RustSharp.Conformance/fixtures/safe-core-lexing-manifest.json' -or | |
| [string] $report.Manifest.Sha256 -ne $expectedManifestSha256 -or | |
| $report.Manifest.Version -ne 2 -or | |
| $report.Manifest.RustVersion -ne '1.98.0' -or | |
| $report.Manifest.Edition -ne '2024' -or | |
| $report.Manifest.UnicodeVersion -ne '17.0.0' -or | |
| @($report.Manifest.Coverage.PSObject.Properties).Count -ne 22 -or | |
| [string] $report.Manifest.Lexer -ne 'RustSharp.Syntax.RustLexer.Lex' -or | |
| $report.Manifest.Denominator -ne $denominator -or | |
| $report.Manifest.CaseCount -ne $denominator -or | |
| @($report.Cases).Count -ne $denominator -or | |
| $invalidCases.Count -ne 0 -or | |
| $report.Execution.DeadlineExpired -ne $false -or | |
| -not [string]::IsNullOrWhiteSpace([string] $report.Manifest.Error) -or | |
| -not [string]::IsNullOrWhiteSpace([string] $report.HarnessError)) { | |
| throw 'Safe-core lexing harness returned success without complete manifest-driven lexer-acceptance evidence.' | |
| } | |
| - name: Run safe-core syntax corpus | |
| shell: pwsh | |
| run: | | |
| $ErrorActionPreference = 'Stop' | |
| & ./eng/Invoke-BoundedProcess.ps1 -FilePath dotnet -ArgumentList @('run', '--project', 'tools/RustSharp.Conformance', '-c', 'Release', '--no-build', '--no-restore', '--', '--profile', 'safe-core-syntax') -TimeoutSeconds 240 | |
| & ./eng/Test-SyntaxEvidence.ps1 | |
| - name: Run safe-core name-resolution corpus | |
| shell: pwsh | |
| timeout-minutes: 5 | |
| run: | | |
| $ErrorActionPreference = 'Stop' | |
| & ./eng/Invoke-BoundedProcess.ps1 -FilePath dotnet -ArgumentList @('run', '--project', 'tools/RustSharp.Conformance', '-c', 'Release', '--no-build', '--no-restore', '--', '--profile', 'safe-core-name-resolution') -TimeoutSeconds 240 | |
| & ./eng/Test-NameResolutionEvidence.ps1 | |
| - name: Run Linux x64 Native AOT probe | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| set +e | |
| bash eng/Invoke-LinuxNativeAotProbe.sh samples/hello.rs artifacts/p0/linux-x64 300 | |
| probe_exit=$? | |
| set -e | |
| evidence="artifacts/p0/linux-x64/linux-aot-evidence.json" | |
| read_json_status() { | |
| local report_path="$1" | |
| REPORT_PATH="$report_path" pwsh -NoLogo -NoProfile -NonInteractive -Command '$ErrorActionPreference = "Stop"; $report = Get-Content -Raw -LiteralPath $env:REPORT_PATH | ConvertFrom-Json; $value = $report.Status; if ([string]::IsNullOrWhiteSpace([string]$value)) { throw "Report status is missing." }; [Console]::Write([string]$value)' | |
| } | |
| if [[ ! -f "$evidence" ]]; then | |
| echo "Linux Native AOT probe did not write $evidence" >&2 | |
| exit 1 | |
| fi | |
| if ! evidence_status="$(read_json_status "$evidence")"; then | |
| echo "Could not parse Linux Native AOT evidence status from $evidence" >&2 | |
| exit 1 | |
| fi | |
| if (( probe_exit == 77 )) && [[ "$evidence_status" == "skipped" ]]; then | |
| echo "::warning::Linux Native AOT probe is explicitly blocked; see $evidence" | |
| exit "$probe_exit" | |
| fi | |
| if (( probe_exit != 0 )); then | |
| exit "$probe_exit" | |
| fi | |
| if [[ "$evidence_status" != "passed" ]]; then | |
| echo "Linux Native AOT probe returned success without passed evidence (status=$evidence_status)" >&2 | |
| exit 1 | |
| fi | |
| - name: Run typed MIR Linux x64 Native AOT probe | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| set +e | |
| bash eng/Invoke-LinuxNativeAotProbe.sh samples/safe-core.rs artifacts/p1-10/linux-x64-v2 300 safe-core-mir-p1-v2 $'Safe core on .NET\n42\ntrue\n' | |
| probe_exit=$? | |
| set -e | |
| evidence="artifacts/p1-10/linux-x64-v2/linux-aot-evidence.json" | |
| if [[ ! -f "$evidence" ]]; then | |
| echo "Typed MIR Linux Native AOT probe did not write $evidence" >&2 | |
| exit 1 | |
| fi | |
| read_json_status() { | |
| local report_path="$1" | |
| REPORT_PATH="$report_path" pwsh -NoLogo -NoProfile -NonInteractive -Command '$ErrorActionPreference = "Stop"; $report = Get-Content -Raw -LiteralPath $env:REPORT_PATH | ConvertFrom-Json; $value = $report.status; if ([string]::IsNullOrWhiteSpace([string]$value)) { throw "Report status is missing." }; [Console]::Write([string]$value)' | |
| } | |
| if ! evidence_status="$(read_json_status "$evidence")"; then | |
| echo "Could not parse typed MIR Linux Native AOT evidence status from $evidence" >&2 | |
| exit 1 | |
| fi | |
| if (( probe_exit == 77 )) && [[ "$evidence_status" == "skipped" ]]; then | |
| echo "::warning::Typed MIR Linux Native AOT probe is explicitly blocked; see $evidence" | |
| exit "$probe_exit" | |
| fi | |
| if (( probe_exit != 0 )); then | |
| exit "$probe_exit" | |
| fi | |
| if [[ "$evidence_status" != "passed" ]]; then | |
| echo "Typed MIR Linux Native AOT probe returned success without passed evidence (status=$evidence_status)" >&2 | |
| exit 1 | |
| fi | |
| - name: Run P0 I/O smoke probes | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| set +e | |
| dotnet run --project tools/RustSharp.Smoke -c Release --no-build --no-restore -- --profile p0-io | |
| smoke_exit=$? | |
| set -e | |
| report="artifacts/smoke/p0-io.json" | |
| if [[ ! -f "$report" ]]; then | |
| echo "I/O smoke probes did not write $report" >&2 | |
| exit 1 | |
| fi | |
| read_json_status() { | |
| local report_path="$1" | |
| REPORT_PATH="$report_path" pwsh -NoLogo -NoProfile -NonInteractive -Command '$ErrorActionPreference = "Stop"; $report = Get-Content -Raw -LiteralPath $env:REPORT_PATH | ConvertFrom-Json; $value = $report.Summary.Status; if ([string]::IsNullOrWhiteSpace([string]$value)) { throw "Report summary status is missing." }; [Console]::Write([string]$value)' | |
| } | |
| if ! report_status="$(read_json_status "$report")"; then | |
| echo "Could not parse smoke report summary status from $report" >&2 | |
| exit 1 | |
| fi | |
| if (( smoke_exit == 2 )) && [[ "$report_status" == "blocked" ]]; then | |
| echo "::warning::I/O smoke probes are explicitly blocked; see $report" | |
| exit "$smoke_exit" | |
| fi | |
| if (( smoke_exit != 0 )); then | |
| exit "$smoke_exit" | |
| fi | |
| if [[ "$report_status" != "passed" ]]; then | |
| echo "I/O smoke probes returned success without a passed summary report (status=$report_status)" >&2 | |
| exit 1 | |
| fi | |
| - name: Verify primitive safe-core profile | |
| shell: pwsh | |
| timeout-minutes: 5 | |
| env: | |
| DOTNET_CLI_USE_MSBUILD_SERVER: '0' | |
| MSBUILDDISABLENODEREUSE: '1' | |
| run: | | |
| $ErrorActionPreference = 'Stop' | |
| & ./eng/Invoke-BoundedProcess.ps1 -FilePath dotnet -ArgumentList @('tools/RustSharp.Conformance/bin/Release/net10.0/RustSharp.Conformance.dll', '--profile', 'safe-core-primitives-v1', '--oracle', 'rustc-1.98', '--report', 'artifacts/p1/safe-core-primitives-v1.json') -TimeoutSeconds 200 | |
| & ./eng/Invoke-BoundedProcess.ps1 -FilePath dotnet -ArgumentList @('src/RustSharp.Cli/bin/Release/net10.0/rsc.dll', 'compile', 'samples/safe-core.rs', '--profile', 'safe-core-primitives-v1', '--output', 'artifacts/p1/safe-core.dll') -TimeoutSeconds 30 | |
| & ./eng/Invoke-ILVerify.ps1 -AssemblyPath artifacts/p1/safe-core.dll -EvidencePath artifacts/p1/safe-core.ilverify.json | |
| - name: Verify generic executable profile | |
| shell: pwsh | |
| timeout-minutes: 5 | |
| env: | |
| DOTNET_CLI_USE_MSBUILD_SERVER: '0' | |
| MSBUILDDISABLENODEREUSE: '1' | |
| run: | | |
| $ErrorActionPreference = 'Stop' | |
| & ./eng/Invoke-BoundedProcess.ps1 -FilePath dotnet -ArgumentList @('tools/RustSharp.Conformance/bin/Release/net10.0/RustSharp.Conformance.dll', '--profile', 'safe-core-generics-v1', '--oracle', 'rustc-1.98', '--report', 'artifacts/p1-05/safe-core-generics-v1.json') -TimeoutSeconds 200 | |
| & ./eng/Invoke-BoundedProcess.ps1 -FilePath dotnet -ArgumentList @('src/RustSharp.Cli/bin/Release/net10.0/rsc.dll', 'compile', 'samples/generics.rs', '--profile', 'safe-core-generics-v1', '--output', 'artifacts/p1-05/generics.dll') -TimeoutSeconds 30 | |
| & ./eng/Test-GenericEvidence.ps1 | |
| & ./eng/Invoke-ILVerify.ps1 -AssemblyPath artifacts/p1-05/generics.dll -EvidencePath artifacts/p1-05/generics.ilverify.json | |
| & ./eng/Invoke-BoundedProcess.ps1 -FilePath dotnet -ArgumentList @('src/RustSharp.Cli/bin/Release/net10.0/rsc.dll', 'compile', 'tests/workspaces/generics/Cargo.toml', '--profile', 'safe-core-generics-v1', '--output', 'artifacts/p1-05/generic-packages.dll') -TimeoutSeconds 30 | |
| & ./eng/Invoke-BoundedProcess.ps1 -FilePath dotnet -ArgumentList @('artifacts/p1-05/generic-packages.dll') -TimeoutSeconds 10 | |
| & ./eng/Invoke-ILVerify.ps1 -AssemblyPath artifacts/p1-05/generic-packages.dll -EvidencePath artifacts/p1-05/generic-packages.ilverify.json | |
| - name: Run P1 v2 borrow and Drop differential gate | |
| shell: pwsh | |
| timeout-minutes: 20 | |
| env: | |
| DOTNET_CLI_USE_MSBUILD_SERVER: '0' | |
| MSBUILDDISABLENODEREUSE: '1' | |
| run: | | |
| $ErrorActionPreference = 'Stop' | |
| & ./eng/Invoke-BoundedProcess.ps1 -FilePath dotnet -ArgumentList @('tools/RustSharp.Conformance/bin/Release/net10.0/RustSharp.Conformance.dll', '--profile', 'p1-differential-v2', '--oracle', 'rustc-1.98', '--report', 'artifacts/p1-10/p1-differential-v2.json') -TimeoutSeconds 900 -CapturePrefix 'artifacts/p1-10/p1-differential-v2' | |
| $report = Get-Content -Raw -LiteralPath 'artifacts/p1-10/p1-differential-v2.json' | ConvertFrom-Json | |
| if ([string]$report.summary.status -ne 'passed' -or $report.summary.denominator -ne 16 -or $report.summary.executed -ne 16 -or $report.summary.passed -ne 16 -or $report.summary.failed -ne 0 -or $report.summary.skipped -ne 0 -or $report.summary.blocked -ne 0) { | |
| throw 'P1 v2 differential gate did not produce 16/16 with zero failed, skipped, or blocked cases.' | |
| } | |
| - name: Upload Linux evidence | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: linux-x64-native-aot-evidence | |
| path: artifacts | |
| if-no-files-found: warn |