Skip to content

Close P1 regression and ownership evidence gates #35

Close P1 regression and ownership evidence gates

Close P1 regression and ownership evidence gates #35

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