Skip to content

M2.17-M P0: reconcile #618/#620; canonical Data Model focused hydration + Explorer read SSOT #802

M2.17-M P0: reconcile #618/#620; canonical Data Model focused hydration + Explorer read SSOT

M2.17-M P0: reconcile #618/#620; canonical Data Model focused hydration + Explorer read SSOT #802

name: SCL Interoperability R7 Build
on:
pull_request:
paths:
- "Services/NativeIec61850Client.CanonicalModel.cs"
- "Services/NativeIec61850Client.SmartDiscoveryCapture.cs"
- "Directory.Build.targets"
- "scripts/enable-smart-discovery-capture.ps1"
- "Services/NativeIec61850Client.SmartDiscoveryLifecycle.cs"
- "Services/NativeIec61850Client.SclAssisted.cs"
- "Services/SclAssistedConnectionPreparation.cs"
- "Models/MonitorModels.cs"
- "MainWindow.xaml.cs"
- "Services/CanonicalSclReloadValidator.cs"
- "engines/ARIEC61850.lock.json"
- "evidence/interoperability-reference-target.json"
- "docs/INTEROPERABILITY_REFERENCE_CONTRACT.md"
- "tests/ARSAS.Tests/CanonicalLiveSclExportRegressionTests.cs"
- "tests/ARSAS.Tests/CanonicalSclReloadValidatorTests.cs"
- ".github/workflows/scl-interoperability-r7.yml"
workflow_dispatch:
concurrency:
group: scl-interoperability-r7-${{ github.event_name == 'pull_request' && github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
jobs:
build-r7-scl-interoperability:
name: Build canonical discovery to interoperable SCL test package
runs-on: windows-latest
steps:
- name: Checkout exact ARSAS source revision
shell: pwsh
env:
ARSAS_SOURCE_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
run: |
if ($env:ARSAS_SOURCE_SHA -notmatch '^[0-9a-f]{40}$') {
throw "Invalid ARSAS source SHA: $env:ARSAS_SOURCE_SHA"
}
git clone --quiet --filter=blob:none --no-checkout "https://github.com/$env:GITHUB_REPOSITORY.git" ARSAS
git -C .\ARSAS fetch --quiet --depth 1 origin $env:ARSAS_SOURCE_SHA
git -C .\ARSAS checkout --quiet --detach $env:ARSAS_SOURCE_SHA
$arsasCommit = (git -C .\ARSAS rev-parse HEAD).Trim()
if ($arsasCommit -ne $env:ARSAS_SOURCE_SHA) {
throw "ARSAS SHA mismatch. Expected $env:ARSAS_SOURCE_SHA, got $arsasCommit."
}
"ARSAS_COMMIT=$arsasCommit" | Out-File $env:GITHUB_ENV -Encoding utf8 -Append
- name: Resolve current engine while preserving R7/R10 authority
shell: pwsh
run: |
$lock = Get-Content .\ARSAS\engines\ARIEC61850.lock.json -Raw | ConvertFrom-Json
$convergence = Get-Content .\ARSAS\evidence\interoperability-reference-target.json -Raw | ConvertFrom-Json
$association = $convergence.postBaselineAcceptance.sclAssociationInteroperability
$accepted = $lock.sclAssociationInteroperability
if ($lock.repository -ne 'masarray/ARIEC61850' -or
$lock.commit -notmatch '^[0-9a-f]{40}$') {
throw 'Invalid engine lock repository or commit SHA.'
}
if ($convergence.status -ne 'physical-retest-passed-merge-ready' -or
[int]$convergence.activeStack.enginePerformance.pullRequest -ne 134 -or
[int]$convergence.activeStack.engineModelAndScl.pullRequest -ne 135 -or
[int]$convergence.activeStack.consumerIntegration.pullRequest -ne 324 -or
[bool]$convergence.promotion.productionPromoted -or
-not [bool]$convergence.promotion.physicalRetestPassed -or
[bool]$convergence.promotion.mergeAllowedBeforePhysicalRetest) {
throw 'Immutable R7/R10 physical convergence evidence was weakened.'
}
if ($accepted.contractId -ne 'SCL-ASSOC-PARTIAL-ADDR-PHYSICAL' -or
[int]$accepted.sourcePullRequest -ne 143 -or
$accepted.testedEngineCommit -ne $association.engine.testedHead -or
$accepted.mergedEngineCommit -ne $association.engine.mergedMain -or
-not [bool]$accepted.preservesR10DiscoveryModelBaseline -or
-not [bool]$accepted.fullDiscoveryFallbackForbidden -or
-not [bool]$accepted.cyclicMmsProcessPollingForbidden -or
[int]$association.physicalAcceptance.failedReads -ne 0 -or
-not [bool]$association.physicalAcceptance.actualInformationReportObserved) {
throw 'Physical SCL-association/R10 provenance is not intact.'
}
. .\ARSAS\scripts\test-p10-9f-core-routing-trial.ps1
. .\ARSAS\scripts\test-m217k-typed-status-trial.ps1
$coreRoutingTrialValid = Test-P109FCoreRoutingTrial -Lock $lock
$m217KTrialValid = Test-M217KTypedStatusTrial -Lock $lock
$trial = $lock.smartInteroperabilityCodeTrial
if ($null -ne $trial -and $lock.commit -eq $trial.exactCommit) {
if ([int]$lock.sourcePullRequest -ne 148 -or
[int]$trial.integrationPullRequest -ne 148 -or
[int]$trial.ciRun -ne 759 -or
$trial.status -ne 'code-verified-not-physical' -or
-not [bool]$trial.physicalPromotionRequired -or
-not [bool]$trial.fullDiscoveryFallbackForbidden -or
$lock.purpose -notmatch 'CODE-VERIFIED INTEROPERABILITY TRIAL ONLY') {
throw 'Smart interoperability trial is not strictly CodeVerified and promotion-blocked.'
}
Write-Host 'Running stacked interoperability checks on a CodeVerified-only engine; no physical/mainline promotion is implied.'
}
elseif ($lock.commit -eq $lock.sclServerAtTrial.exactCommit) {
$serverAt = $lock.sclServerAtTrial
if ($lock.commit -ne '352c81e6a798635c6addcee0683235ca87ad416d' -or
$lock.ref -ne 'main' -or
[int]$lock.sourcePullRequest -ne 153 -or
[int]$serverAt.sourcePullRequest -ne 153 -or
$serverAt.baseEngineCommit -ne $trial.exactCommit -or
$trial.status -ne 'code-verified-not-physical' -or
$serverAt.status -ne 'code-verified-candidate-not-physical' -or
-not [bool]$serverAt.physicalQualificationRequired -or
-not [bool]$serverAt.noAutomaticMmsFailover -or
$lock.purpose -notmatch 'CODE-VERIFIED INTEROPERABILITY TRIAL ONLY') {
throw 'P6.2 ServerAt trial provenance is invalid or silently promoted.'
}
Write-Host 'Running exact P6.2 ServerAt trial on the P6.1 code-only ancestry; physical/main promotion remains blocked.'
}
elseif ($null -ne $lock.datasetFirstDiscoveryTrial -and
$lock.commit -eq $lock.datasetFirstDiscoveryTrial.exactCommit) {
$closure = $lock.datasetFirstDiscoveryTrial
if ($lock.ref -ne 'main' -or
[int]$lock.sourcePullRequest -ne 157 -or
[int]$closure.enginePullRequest -ne 157 -or
[int]$closure.consumerPullRequest -ne 497 -or
[int]$closure.issue -ne 495 -or
$closure.status -ne 'code-trial-not-physical' -or
$closure.baseEngineCommit -ne $lock.sclServerAtTrial.exactCommit -or
-not [bool]$closure.physicalQualificationRequired -or
-not [bool]$closure.fullNameListFallbackForbidden -or
-not [bool]$closure.cyclicMmsProcessPollingForbidden) {
throw 'P7.6G DataSet-first discovery experimental pin provenance is invalid.'
}
Write-Host 'DataSet-first closure trial qualified for CI only; no physical promotion.'
}
elseif ($null -ne $lock.p10IdentityTrial -and
$lock.commit -eq $lock.p10IdentityTrial.exactCommit) {
$p10 = $lock.p10IdentityTrial
$closure = $lock.datasetFirstDiscoveryTrial
if ($lock.ref -ne 'main' -or
[int]$lock.sourcePullRequest -ne 161 -or
[int]$p10.enginePullRequest -ne 161 -or
$p10.baseEngineCommit -ne $closure.exactCommit -or
$p10.exactCommit -ne '00ad2819b99ffe6b3f885e59aa24a6afb2b4d8e1' -or
$p10.status -ne 'code-trial-not-physical' -or
-not [bool]$p10.physicalQualificationRequired -or
-not [bool]$p10.fullNameListFallbackForbidden -or
-not [bool]$p10.cyclicMmsProcessPollingForbidden -or
$p10.identitySource -ne 'TrustedSclExactDomainMatch' -or
$closure.status -ne 'code-trial-not-physical') {
throw 'P10 identity trial is not pinned to exact qualified DataSet-first ancestry.'
}
Write-Host 'P10 trusted-SCL identity trial accepted for code CI ONLY; R7/R10 physical gate unchanged.'
}
elseif ($m217KTrialValid) {
if ($env:GITHUB_BASE_REF -eq 'main' -or $env:GITHUB_REF_NAME -eq 'main') {
throw 'M2.17-K is not physically accepted and cannot run as a mainline promotion.'
}
Write-Host 'M2.17-K exact typed C264 engine trial accepted for CI ONLY; R7/R10 physical authority remains historical.'
}
elseif ($coreRoutingTrialValid) {
Write-Host 'P10.9F routing core accepted for CodeVerified staging CI ONLY; R7/R10 physical authority unchanged.'
}
elseif ($lock.commit -eq $convergence.activeStack.engineModelAndScl.head) {
if ([int]$lock.sourcePullRequest -notin @(134, 135) -or
$lock.purpose -notmatch 'R7 physical SCL repair pin') {
throw 'Historical R7 engine pin authority mismatch.'
}
}
else {
throw 'Unknown engine pin; neither physical R7 nor exact CodeVerified trial.'
}
"ARIEC61850_REPOSITORY=$($lock.repository)" | Out-File $env:GITHUB_ENV -Encoding utf8 -Append
"ARIEC61850_COMMIT=$($lock.commit)" | Out-File $env:GITHUB_ENV -Encoding utf8 -Append
"ARIEC61850_SOURCE_PR=$($lock.sourcePullRequest)" | Out-File $env:GITHUB_ENV -Encoding utf8 -Append
$version = (Get-Content .\ARSAS\VERSION -Raw).Trim()
"ARSAS_VERSION=$version" | Out-File $env:GITHUB_ENV -Encoding utf8 -Append
- name: Checkout exact ARIEC61850 engine revision
shell: pwsh
run: |
git clone --quiet --filter=blob:none --no-checkout "https://github.com/$env:ARIEC61850_REPOSITORY.git" ARIEC61850
git -C .\ARIEC61850 fetch --quiet --depth 1 origin $env:ARIEC61850_COMMIT
git -C .\ARIEC61850 checkout --quiet --detach $env:ARIEC61850_COMMIT
$actual = (git -C .\ARIEC61850 rev-parse HEAD).Trim()
if ($actual -ne $env:ARIEC61850_COMMIT) {
throw "Engine SHA mismatch. Expected $env:ARIEC61850_COMMIT, got $actual."
}
- name: Verify canonical SCL interoperability contracts
shell: pwsh
run: |
$canonical = Get-Content .\ARIEC61850\src\AR.Iec61850\Discovery\LiveIedCanonicalModel.cs -Raw
$exporter = Get-Content .\ARIEC61850\src\AR.Iec61850\Scl\Export\CanonicalLiveIedSclExporter.cs -Raw
$association = Get-Content .\ARIEC61850\src\AR.Iec61850\Mms\MmsClientSession.AssociationEvidence.cs -Raw
$cotpClient = Get-Content .\ARIEC61850\src\AR.Iec61850\Osi\CotpClient.cs -Raw
$rcbProjector = Get-Content .\ARIEC61850\src\AR.Iec61850\Scl\Export\LiveRcbLogicalGroupProjector.cs -Raw
$smartDiscovery = Get-Content .\ARIEC61850\src\AR.Iec61850\Mms\MmsClientSession.SmartDiscovery.cs -Raw
$singleFlight = Get-Content .\ARIEC61850\src\AR.Iec61850\Mms\MmsClientSession.SmartDiscoverySingleFlight.cs -Raw
$smartGva = Get-Content .\ARIEC61850\src\AR.Iec61850\Mms\MmsClientSession.SmartVariableAccessAttributes.cs -Raw
$referenceParts = Get-Content .\ARIEC61850\src\AR.Iec61850\Discovery\Iec61850ReferenceParts.cs -Raw
$liveExporter = Get-Content .\ARIEC61850\src\AR.Iec61850\Scl\Export\LiveIedSclExporter.cs -Raw
$standardRegistry = Get-Content .\ARIEC61850\src\AR.Iec61850\Discovery\Iec61850StandardModelRegistry.cs -Raw
$cdcInference = Get-Content .\ARIEC61850\src\AR.Iec61850\Discovery\CdcInferenceEngine.cs -Raw
$consumer = Get-Content .\ARSAS\Services\NativeIec61850Client.CanonicalModel.cs -Raw
$capture = Get-Content .\ARSAS\Services\NativeIec61850Client.SmartDiscoveryCapture.cs -Raw
$convergence = Get-Content .\ARSAS\evidence\interoperability-reference-target.json -Raw | ConvertFrom-Json
$buildTargets = Get-Content .\ARSAS\Directory.Build.targets -Raw
$lifecycle = Get-Content .\ARSAS\Services\NativeIec61850Client.SmartDiscoveryLifecycle.cs -Raw
$sclClient = Get-Content .\ARSAS\Services\NativeIec61850Client.SclAssisted.cs -Raw
$sclPreparation = Get-Content .\ARSAS\Services\SclAssistedConnectionPreparation.cs -Raw
$save = Get-Content .\ARSAS\MainWindow.xaml.cs -Raw
$reloadValidator = Get-Content .\ARSAS\Services\CanonicalSclReloadValidator.cs -Raw
$regression = Get-Content .\ARSAS\tests\ARSAS.Tests\CanonicalLiveSclExportRegressionTests.cs -Raw
$reloadTests = Get-Content .\ARSAS\tests\ARSAS.Tests\CanonicalSclReloadValidatorTests.cs -Raw
foreach ($required in @(
'class LiveIedCanonicalModel',
'InstanceValues',
'LiveIedCommunicationEvidence')) {
if ($canonical -notmatch [regex]::Escape($required)) {
throw "Canonical engine contract missing: $required"
}
}
foreach ($required in @(
'ValidateCanonicalCommunication',
'PreserveRuntimeServiceCapacity',
'ApplyCanonicalInstanceValues',
'ValidateRoundTripAssociation')) {
if ($exporter -notmatch [regex]::Escape($required)) {
throw "Canonical SCL exporter contract missing: $required"
}
}
if ($association -notmatch 'GetAcceptedCommunicationEvidence') {
throw 'Accepted association evidence API is missing.'
}
if ($env:ARIEC61850_SOURCE_PR -in @('135', '143')) {
$wireReader = '.\ARIEC61850\src\AR.Iec61850\Acse\AcseAssociationRequestIdentityReader.cs'
if (!(Test-Path $wireReader) -or
$association -notmatch 'AcseAssociationRequestIdentityReader\.Read' -or
$association -notmatch '_cotp\.LastConnectParameters\?\.DestinationTsap' -or
$cotpClient -notmatch 'public CotpConnectParameters\? LastConnectParameters' -or
$cotpClient -notmatch 'LastConnectParameters = acceptedParameters' -or
$association -match 'ApTitle\s*=\s*"1,1,1,999,1"') {
throw 'PR #135 must derive canonical AP/AE/selectors from exact accepted ACSE and COTP session evidence, not duplicated profile constants.'
}
}
if ($smartDiscovery -notmatch 'DiscoverSmartAsync' -or
$singleFlight -notmatch 'DiscoverSmartSingleFlightAsync' -or
$smartGva -notmatch 'GetVariableAccessAttributesSmartAsync' -or
$referenceParts -notmatch 'instanceStart' -or
$referenceParts -notmatch 'IsFourLetterLnClass' -or
$liveExporter -notmatch 'TryResolveStandardSubDataObjectCdc' -or
$liveExporter -notmatch 'DataObjectReferencePaths' -or
$liveExporter -notmatch 'IsEdition2ServiceTrackingCdc' -or
$standardRegistry -notmatch 'Key\("TCTR", "ARtg"\)' -or
$standardRegistry -notmatch 'Key\("TVTR", "VRtg"\)' -or
$standardRegistry -notmatch 'Key\("LTIM", "TmChgDT"\)' -or
$standardRegistry -notmatch 'Key\("LTRK", "BrcbTrk"\)' -or
$standardRegistry -notmatch 'Key\("XCBR", "EEName"\)' -or
$standardRegistry -notmatch 'Key\("LLN0", "MltLev"\)' -or
$cdcInference -notmatch '"CST".*"BTS".*"UTS".*"STS".*"CTS"' -or
$capture -match 'TryBuildSupplementalGetNameListSnapshotAsync' -or
$capture -match 'DiscoverDomainVariableTypeTreeNamesAsync' -or
$capture -match 'AddAdaptiveLogicalNodeSiblingProbeSignalsAsync' -or
$capture -match 'EnrichEngineeringUnitsAsync') {
throw 'external IEC 61850 reference convergence source contract regressed: smart structure-first discovery or semantic SCL authority is missing, or a forbidden legacy browse path returned.'
}
if ([int]$convergence.physicalReference.externalReferenceCapture.associations -ne 1 -or
[int]$convergence.physicalReference.rejectedLegacyArsasCapture.associations -ne 2 -or
-not [bool]$convergence.discoveryAcceptance.exactlyOneAssociation -or
-not [bool]$convergence.discoveryAcceptance.supplementalLegacyAssociationForbidden -or
-not [bool]$convergence.sclAcceptance.reopenInArsasRequired -or
-not [bool]$convergence.sclAcceptance.physicalReconnectRequired) {
throw 'external IEC 61850 reference physical acceptance target was weakened.'
}
if ($consumer -notmatch 'LiveIedCanonicalModelBuilder\.Build\(model, communication, initialRead\)' -or
$capture -match 'InitialFcReadPlanner\.FromSclModel' -or
$capture -match 'ExecuteInitialFcReadPlanSmartAsync' -or
$capture -notmatch 'initialFcRoots=deferred' -or
$buildTargets -notmatch 'VerifyPhysicalProvenSmartDiscoveryRoute' -or
$buildTargets -notmatch '\-VerifyOnly' -or
$buildTargets -match 'GITHUB_WORKFLOW' -or
$buildTargets -match 'SmartDiscoveryProductionPromoted' -or
$lifecycle -notmatch 'PublishCanonicalModel\(model, initialRead\)' -or
$save -notmatch 'CanonicalLiveIedSclExporter\.WriteFiles' -or
$save -notmatch 'profile:\s*"full-model"' -or
$save -notmatch 'CanonicalSclReloadValidator\.Validate' -or
$sclPreparation -notmatch 'MmsLocalAssociationProfile\.ExistingRuntimeDefault' -or
$sclClient -notmatch 'IsSafeTrustedSclInitialReadFc' -or
$sclClient -notmatch 'ExecuteInitialFcReadPlanSmartAsync' -or
$reloadValidator -notmatch 'workspaceService\.Open' -or
$reloadValidator -notmatch 'result\.Profile.*full-model' -or
$reloadValidator -notmatch 'changed the proven calling-side runtime identity' -or
$reloadValidator -notmatch 'reproduce accepted association profile' -or
$regression -notmatch 'SmartDiscovery_DefersEagerFcValueReadsFromStructuralScan' -or
$reloadTests -notmatch 'ExportedCanonicalScl_ReopensThroughArsasWorkspaceWithoutStructuralDrift' -or
$reloadTests -notmatch 'BuildDefaultAssociationPayload' -or
$reloadTests -notmatch 'GoldenRcbShape_RoundTripsThirtyFourRuntimeAsThirtyTwoLogicalWithPhysicalCapacity' -or
$rcbProjector -match 'SameNumericText\(left\.IntegrityPeriodMs, right\.IntegrityPeriodMs\)' -or
$rcbProjector -match 'Same\(left\.OptionalFields, right\.OptionalFields\)') {
throw 'ARSAS canonical discovery -> full-model SCL -> exact native reconnect -> safe initial value-read contract is incomplete.'
}
- name: Setup .NET 8
uses: actions/setup-dotnet@v6
with:
dotnet-version: 8.0.x
- name: Restore ARSAS solution
run: dotnet restore .\ARSAS\ArIED61850Tester.sln
- name: Build ARSAS Release
run: dotnet build .\ARSAS\ArIED61850Tester.sln -c Release --no-restore
- name: Verify R7 smart discovery route was compiled
shell: pwsh
run: |
$consumer = Get-Content .\ARSAS\Services\NativeIec61850Client.cs -Raw
if ($consumer -notmatch 'DiscoverSignalsSmartForCaptureAsync\(cancellationToken, progress\)') {
throw 'R7 build compiled without the smart discovery route; refusing legacy-path artifact.'
}
if ($consumer -notmatch 'if \(SmartDiscoveryCaptureModeEnabled\)') {
throw 'R7 build did not install the smart discovery gate at DiscoverSignalsAsync.'
}
Write-Host 'R7 smart discovery route is installed in the compiled source.'
- name: Run ARSAS canonical-export regressions
run: dotnet test .\ARSAS\tests\ARSAS.Tests\ARSAS.Tests.csproj -c Release --no-build --no-restore --logger "trx;LogFileName=arsas-r7-scl-tests.trx" --results-directory .\ARSAS\TestResults
- name: Build engine and run engine tests
shell: pwsh
run: |
dotnet restore .\ARIEC61850\ARIEC61850.sln
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
dotnet build .\ARIEC61850\ARIEC61850.sln -c Release --no-restore
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
dotnet test .\ARIEC61850\ARIEC61850.sln -c Release --no-build --no-restore --logger "trx;LogFileName=ariec61850-r7-scl-tests.trx" --results-directory .\ARSAS\TestResults
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
- name: Publish R7 portable single EXE
shell: pwsh
run: |
.\ARSAS\scripts\publish-windows-portable.ps1 `
-Version $env:ARSAS_VERSION `
-Runtime win-x64 `
-SingleFile $true `
-SelfContained $true `
-EngineProject "$env:GITHUB_WORKSPACE\ARIEC61850\src\AR.Iec61850\AR.Iec61850.csproj" `
-NpcapProject "$env:GITHUB_WORKSPACE\ARIEC61850\src\AR.Iec61850.Transports.Npcap\AR.Iec61850.Transports.Npcap.csproj"
- name: Smoke test R7 portable executable
shell: pwsh
run: |
$exe = ".\ARSAS\dist\ARSAS-$env:ARSAS_VERSION-win-x64-portable.exe"
if (!(Test-Path $exe -PathType Leaf)) { throw "Portable EXE missing: $exe" }
$env:DOTNET_BUNDLE_EXTRACT_BASE_DIR = Join-Path $env:RUNNER_TEMP 'ARSAS-r7-scl-bundle-cache'
$process = Start-Process -FilePath $exe -ArgumentList @('--portable-smoke-test') -PassThru
if (-not $process.WaitForExit(30000)) {
Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue
throw 'Portable EXE smoke test timed out.'
}
if ($process.ExitCode -ne 0) {
throw "Portable EXE smoke test failed: $($process.ExitCode)"
}
$convergencePath = ".\ARSAS\evidence\interoperability-reference-target.json"
$convergenceHash = (Get-FileHash $convergencePath -Algorithm SHA256).Hash.ToLowerInvariant()
$convergence = Get-Content $convergencePath -Raw | ConvertFrom-Json
@(
'ARSAS R7 SCL interoperability field-test build',
"ARSAS commit: $env:ARSAS_COMMIT",
"ARIEC61850 commit: $env:ARIEC61850_COMMIT",
"Engine source PR: $env:ARIEC61850_SOURCE_PR",
"Convergence contract SHA256: $convergenceHash",
"Convergence status: $($convergence.status)",
'Active stack: ARIEC61850 #134 -> #135 -> ARSAS #324',
'',
'Acceptance target:',
'IED -> fast structural Smart Discovery -> full-model SCL Ed1/Ed2 -> ARSAS Workspace Reload -> exact native calling/called association -> bounded safe FC-root snapshot.',
'CI invariant: full-model export excludes no discovered attributes; SCL rebuilds the accepted native handshake; golden RCB shape is 34 runtime -> 32 logical with ConfReportControl max=34.',
'Physical gate: one MMS association; no supplemental legacy browse; no recursive per-leaf GVA storm; same-relay PCAP + Ed2 IID + Ed1 ICD + diagnostic required before promotion.',
'',
'This artifact is NOT production-promotion authority and does NOT replace P0-5e/P0-5f physical discovery-budget evidence.'
) | Set-Content .\ARSAS\dist\R7-SCL-INTEROP-BUILD.txt -Encoding utf8
- name: Upload R7 SCL interoperability build
uses: actions/upload-artifact@v7
with:
name: ARSAS-r7-scl-interoperability-win-x64
path: |
ARSAS\dist\ARSAS-*-win-x64-portable.exe
ARSAS\dist\R7-SCL-INTEROP-BUILD.txt
ARSAS\evidence\interoperability-reference-target.json
ARSAS\docs\INTEROPERABILITY_REFERENCE_CONTRACT.md
ARSAS\TestResults\*.trx
if-no-files-found: error
retention-days: 14