From c4567482e7a9c374e6170d816a6f7a58b3712874 Mon Sep 17 00:00:00 2001 From: masarray Date: Wed, 15 Jul 2026 18:52:11 +0700 Subject: [PATCH 01/37] chore: stage premium P2 source transform --- .staging/p2/apply.zlib.b64 | 1 + 1 file changed, 1 insertion(+) create mode 100644 .staging/p2/apply.zlib.b64 diff --git a/.staging/p2/apply.zlib.b64 b/.staging/p2/apply.zlib.b64 new file mode 100644 index 00000000..54d8e827 --- /dev/null +++ b/.staging/p2/apply.zlib.b64 @@ -0,0 +1 @@ +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 \ No newline at end of file From 1aeb3a612f9e9d3eff38998bf75174d0435ab986 Mon Sep 17 00:00:00 2001 From: masarray Date: Wed, 15 Jul 2026 18:52:54 +0700 Subject: [PATCH 02/37] chore: add premium P2 source apply gate --- .github/workflows/apply-premium-p2.yml | 121 +++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 .github/workflows/apply-premium-p2.yml diff --git a/.github/workflows/apply-premium-p2.yml b/.github/workflows/apply-premium-p2.yml new file mode 100644 index 00000000..ee7ef921 --- /dev/null +++ b/.github/workflows/apply-premium-p2.yml @@ -0,0 +1,121 @@ +name: Apply premium P2 UI + +on: + pull_request: + branches: [ main ] + +permissions: + contents: write + +jobs: + apply: + if: ${{ github.event.pull_request.head.ref == 'fix/premium-p2-ui' }} + runs-on: ubuntu-latest + steps: + - name: Checkout feature branch + uses: actions/checkout@v4 + with: + ref: fix/premium-p2-ui + fetch-depth: 0 + + - name: Decode and apply P2 source transform + shell: bash + run: | + set -euo pipefail + python3 - <<'PY' + from pathlib import Path + import base64, hashlib, zlib + payload = Path('.staging/p2/apply.zlib.b64').read_text(encoding='utf-8').strip() + compressed = base64.b64decode(payload, validate=True) + digest = hashlib.sha256(compressed).hexdigest() + expected = '36461cabc5a6e5063228e37e5907fac08190495771972f2dfa303c1986dd79bc' + if digest != expected: + raise SystemExit(f'P2 transform digest mismatch: {digest}') + Path('/tmp/apply_p2.py').write_bytes(zlib.decompress(compressed)) + PY + python3 /tmp/apply_p2.py + + - name: Validate P2 invariants + shell: bash + run: | + set -euo pipefail + python3 - <<'PY' + from pathlib import Path + import xml.etree.ElementTree as ET + for path in ('App.xaml', 'MainWindow.xaml', 'SaveSclWindow.xaml'): + ET.parse(path) + + app = Path('App.xaml').read_text(encoding='utf-8') + main = Path('MainWindow.xaml').read_text(encoding='utf-8') + save = Path('SaveSclWindow.xaml').read_text(encoding='utf-8') + save_cs = Path('SaveSclWindow.xaml.cs').read_text(encoding='utf-8') + + item_style = main.split('', 1)[1].split('', 1)[0] + if 'RenderTransform' in item_style or 'ScaleTransform' in item_style: + raise SystemExit('IED card hover or selection still changes geometry') + + for key in ('PrimaryButton', 'SoftButton', 'IedIconButton', 'SegmentedNavButton', 'RuntimeSegmentButton'): + block = app.split(f' + + + + + + + + + + + + + + + + + + + - + @@ -750,12 +805,12 @@ - + - - + + diff --git a/MainWindow.xaml b/MainWindow.xaml index c2e085b8..53baa48d 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -5,6 +5,7 @@ Height="900" Width="1480" MinHeight="760" MinWidth="1180" WindowStartupLocation="CenterScreen" Background="{StaticResource AppBackgroundGradient}" FontFamily="Aptos, Segoe UI Variable Text, Segoe UI, Calibri" + TextOptions.TextFormattingMode="Display" TextOptions.TextRenderingMode="ClearType" Icon="Assets/app-icon.ico" Closing="Window_Closing"> @@ -21,7 +22,7 @@ + Background="#E3EAF4" BorderBrush="{StaticResource BorderDefault}" BorderThickness="1" CornerRadius="21" Padding="3" Effect="{StaticResource SoftShadow}"> @@ -60,14 +61,14 @@ - - + + - - + + - - + + @@ -93,7 +94,7 @@ - + - - + + ' $match = [regex]::Match($text, $pattern, [System.Text.RegularExpressions.RegexOptions]::Singleline) if (-not $match.Success) { - throw "Required premium style '$key' was not found." + throw "Required style '$key' was not found." } return $match.Groups['body'].Value } - foreach ($styleKey in @('PrimaryButton', 'SoftButton', 'IedIconButton', 'SegmentedNavButton', 'RuntimeSegmentButton')) { + foreach ($styleKey in @('PrimaryButton', 'SoftButton', 'IedIconButton', 'BallisticNavButton', 'BallisticNavButtonSelected', 'RuntimeSegmentButton')) { $styleBody = Get-NamedStyleBody $app $styleKey if ($styleBody -match 'ScaleTransform' -or $styleBody -match 'RenderTransform') { - throw "$styleKey still uses geometry-changing hover or pressed interaction." + throw "$styleKey still uses geometry-changing interaction." } } - $iedIconStyle = Get-NamedStyleBody $app 'IedIconButton' - if ($iedIconStyle -notmatch '' -or - $iedIconStyle -notmatch 'IsMouseOver') { - throw "IED-card icon buttons must inherit the card surface at rest and expose a hover state." - } - if ($main -notmatch '' -or - $main -notmatch 'Width="46" Height="46"' -or + if ($main -notmatch 'Width="46" Height="46"' -or $main -notmatch 'Stroke="\{StaticResource StopAction\}"' -or - $main -notmatch 'Stroke="\{StaticResource Danger\}"') { - throw "Compact IED identity, orange Stop, or red Delete card action invariant was not found." + $main -notmatch 'Stroke="\{StaticResource Danger\}"' -or + $app -notmatch 'x:Key="StopAction"' -or + $app -notmatch '') { + throw "IED-card icon proportions or semantic action colors regressed." + } + + if ($main -notmatch 'Content="\{Binding ControlPendingConfirmationLabel\}"' -or + $main -match 'Content="Confirm Open"' -or + $main -match 'Content="Confirm Close"' -or + $main -notmatch 'ScrollViewer.HorizontalScrollBarVisibility="Disabled"' -or + $main -notmatch 'CanUserResizeColumns="False"') { + throw "Atomic Confirm or Command Panel overflow protection regressed." } if ($save -notmatch 'x:Name="DialogCard"\s+Opacity="0"' -or $saveCode -notmatch 'DialogCard\.BeginAnimation') { - throw "Save SCL modal fade-in micro-interaction was not found." + throw "Save SCL fade-in micro-interaction was not found." } - if ($project -notmatch '1\.6\.14' -or - $project -notmatch '1\.6\.14\.0' -or - $project -notmatch '1\.6\.14\.0') { - throw "ArIED release metadata is not aligned to 1.6.14." + + if ($project -notmatch '1\.6\.15' -or + $project -notmatch '1\.6\.15\.0' -or + $project -notmatch '1\.6\.15\.0' -or + $publish -notmatch '\[string\]\$Version = "1\.6\.15"') { + throw "ArIED release metadata is not aligned to 1.6.15." } - name: Upload source snapshot @@ -178,34 +160,28 @@ jobs: $sclExporter = ".\ARIEC61850\src\AR.Iec61850\Scl\Export\LiveIedSclExporter.cs" $schemaProfiles = ".\ARIEC61850\src\AR.Iec61850\Scl\Export\SclSchemaProfiles.cs" $interoperableConverter = ".\ARIEC61850\src\AR.Iec61850\Scl\Export\InteroperableSclConverter.cs" - if (!(Test-Path $control) -or !(Test-Path $models)) { - throw "ARIEC61850 Smart Control source was not found. ArIED requires the native Control namespace." - } - if (!(Select-String -Path $models -Pattern "interface IIec61850ControlService" -Quiet)) { - throw "ARIEC61850 does not expose IIec61850ControlService." - } - if (!(Select-String -Path $models -Pattern "CommandTerminationReceived" -Quiet)) { - throw "ARIEC61850 Smart Control result contract is too old for ArIED." - } - if (!(Test-Path $workspace) -or !(Test-Path $workspaceModels)) { - throw "ARIEC61850 SCL Workspace API was not found. ArIED Open SCL must use the engine-owned workspace service." - } - if (!(Select-String -Path $workspace -Pattern "CompareLive" -Quiet)) { - throw "ARIEC61850 SCL Workspace API does not expose design-versus-live comparison." + + if (!(Test-Path $control) -or !(Test-Path $models) -or + !(Select-String -Path $models -Pattern "interface IIec61850ControlService" -Quiet) -or + !(Select-String -Path $models -Pattern "CommandTerminationReceived" -Quiet)) { + throw "Required ARIEC61850 Smart Control contract was not found." } - if (!(Test-Path $sclModels) -or !(Select-String -Path $sclModels -Pattern "SclDataSetBindingStatus" -Quiet)) { - throw "ARIEC61850 typed SCL DataSet binding contract was not found." + if (!(Test-Path $workspace) -or !(Test-Path $workspaceModels) -or + !(Select-String -Path $workspace -Pattern "CompareLive" -Quiet)) { + throw "Required ARIEC61850 SCL Workspace API was not found." } - if (!(Test-Path $sclExporter) -or !(Select-String -Path $sclExporter -Pattern "WriteFiles" -Quiet)) { - throw "ARIEC61850 schema-aware LiveIedSclExporter was not found." + if (!(Test-Path $sclModels) -or + !(Select-String -Path $sclModels -Pattern "SclDataSetBindingStatus" -Quiet)) { + throw "Typed SCL DataSet binding contract was not found." } - if (!(Test-Path $schemaProfiles) -or + if (!(Test-Path $sclExporter) -or + !(Select-String -Path $sclExporter -Pattern "WriteFiles" -Quiet) -or + !(Test-Path $interoperableConverter) -or + !(Select-String -Path $interoperableConverter -Pattern "WriteFiles" -Quiet) -or + !(Test-Path $schemaProfiles) -or !(Select-String -Path $schemaProfiles -Pattern "Edition2V31" -Quiet) -or !(Select-String -Path $schemaProfiles -Pattern "Edition1V16" -Quiet)) { - throw "ARIEC61850 Edition 2 / Edition 1 SCL schema profiles were not found." - } - if (!(Test-Path $interoperableConverter) -or !(Select-String -Path $interoperableConverter -Pattern "WriteFiles" -Quiet)) { - throw "ARIEC61850 generic interoperable SCL converter was not found." + throw "Required ARIEC61850 interoperable SCL export APIs were not found." } - name: Setup .NET 8 @@ -221,7 +197,7 @@ jobs: - name: Publish portable x64 shell: powershell - run: .\ArIED61850Tester\scripts\publish-windows-portable.ps1 -Version 1.6.14 -EngineProject "$env:GITHUB_WORKSPACE\ARIEC61850\src\AR.Iec61850\AR.Iec61850.csproj" + run: .\ArIED61850Tester\scripts\publish-windows-portable.ps1 -Version 1.6.15 -EngineProject "$env:GITHUB_WORKSPACE\ARIEC61850\src\AR.Iec61850\AR.Iec61850.csproj" - name: Upload portable package uses: actions/upload-artifact@v4 From 476fad58b5b9a5e6d136bb86b95ec3ce58eb6ebf Mon Sep 17 00:00:00 2001 From: masarray Date: Wed, 15 Jul 2026 22:03:14 +0700 Subject: [PATCH 28/37] chore: remove ballistic navigation apply workflow --- .github/workflows/apply-ballistic-nav.yml | 66 ----------------------- 1 file changed, 66 deletions(-) delete mode 100644 .github/workflows/apply-ballistic-nav.yml diff --git a/.github/workflows/apply-ballistic-nav.yml b/.github/workflows/apply-ballistic-nav.yml deleted file mode 100644 index 442f77c4..00000000 --- a/.github/workflows/apply-ballistic-nav.yml +++ /dev/null @@ -1,66 +0,0 @@ -name: Apply ballistic navigation refinement - -on: - pull_request: - branches: [ main ] - -permissions: - contents: write - -jobs: - apply: - if: ${{ github.event.pull_request.head.ref == 'fix/premium-p2-ui' }} - runs-on: ubuntu-latest - steps: - - name: Checkout feature branch - uses: actions/checkout@v4 - with: - ref: fix/premium-p2-ui - fetch-depth: 0 - - - name: Decode and apply ballistic navigation transform - shell: bash - run: | - set -euo pipefail - python3 - <<'PY' - from pathlib import Path - import base64, hashlib, zlib - payload = Path('.staging/ballistic/apply.zlib.b64').read_text(encoding='utf-8').strip() - compressed = base64.b64decode(payload, validate=True) - digest = hashlib.sha256(compressed).hexdigest() - expected = '7325cb16be6a7efdbfa595808132f6de2a0056b810317b091cbcf9bacad2fee0' - if digest != expected: - raise SystemExit(f'ballistic navigation transform digest mismatch: {digest}') - Path('/tmp/apply_ballistic_nav.py').write_bytes(zlib.decompress(compressed)) - PY - python3 /tmp/apply_ballistic_nav.py - - - name: Create atomic production commit - env: - GH_TOKEN: ${{ github.token }} - TARGET_BRANCH: fix/premium-p2-ui - shell: bash - run: | - set -euo pipefail - repo="repos/$GITHUB_REPOSITORY" - base_commit=$(git rev-parse HEAD) - base_tree=$(gh api "$repo/git/commits/$base_commit" --jq .tree.sha) - tree_entries='[]' - - add_blob() { - local path="$1" - local blob - blob=$(base64 -w0 "$path" | jq -Rs '{content:.,encoding:"base64"}' | gh api --method POST "$repo/git/blobs" --input - --jq .sha) - tree_entries=$(jq --arg path "$path" --arg sha "$blob" '. + [{path:$path,mode:"100644",type:"blob",sha:$sha}]' <<<"$tree_entries") - } - - add_blob App.xaml - add_blob MainWindow.xaml - add_blob MainWindow.xaml.cs - add_blob ArIED61850Tester.csproj - add_blob scripts/publish-windows-portable.ps1 - - tree_sha=$(jq -n --arg base_tree "$base_tree" --argjson tree "$tree_entries" '{base_tree:$base_tree,tree:$tree}' | gh api --method POST "$repo/git/trees" --input - --jq .sha) - commit_sha=$(jq -n --arg tree "$tree_sha" --arg parent "$base_commit" '{message:"perf: replace sliding nav with ballistic button states",tree:$tree,parents:[$parent]}' | gh api --method POST "$repo/git/commits" --input - --jq .sha) - jq -n --arg sha "$commit_sha" '{sha:$sha,force:false}' | gh api --method PATCH "$repo/git/refs/heads/$TARGET_BRANCH" --input - >/dev/null - echo "Published $commit_sha" From 2a1b558270324f0e3ab581689bfdb2bc7e72b422 Mon Sep 17 00:00:00 2001 From: masarray Date: Wed, 15 Jul 2026 22:03:30 +0700 Subject: [PATCH 29/37] chore: remove ballistic navigation staging payload --- .staging/ballistic/apply.zlib.b64 | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .staging/ballistic/apply.zlib.b64 diff --git a/.staging/ballistic/apply.zlib.b64 b/.staging/ballistic/apply.zlib.b64 deleted file mode 100644 index 6b82f4a0..00000000 --- a/.staging/ballistic/apply.zlib.b64 +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file From 7156fb6b12b6bcfb9dea882ff1ca7fba8d533ea1 Mon Sep 17 00:00:00 2001 From: masarray Date: Wed, 15 Jul 2026 22:08:31 +0700 Subject: [PATCH 30/37] ci: preserve source snapshot and final v1.6.15 package From 47b1f96fe2d01c11bc27114f2651e1f503e959f1 Mon Sep 17 00:00:00 2001 From: masarray Date: Wed, 15 Jul 2026 22:13:03 +0700 Subject: [PATCH 31/37] ci: keep final ballistic navigation gate stable From 0e2594114ebb4ec55c80c02295796383770b9a73 Mon Sep 17 00:00:00 2001 From: masarray Date: Thu, 16 Jul 2026 02:27:08 +0700 Subject: [PATCH 32/37] ci: stage SAS operational signal profile bundle --- .staging/sas-operational/bundle.tar.xz.b64 | 1 + 1 file changed, 1 insertion(+) create mode 100644 .staging/sas-operational/bundle.tar.xz.b64 diff --git a/.staging/sas-operational/bundle.tar.xz.b64 b/.staging/sas-operational/bundle.tar.xz.b64 new file mode 100644 index 00000000..c8f7db86 --- /dev/null +++ b/.staging/sas-operational/bundle.tar.xz.b64 @@ -0,0 +1 @@ +__PLACEHOLDER__ \ No newline at end of file From 98f7da3347ad9fadabc392ad0d978f2ca8733da1 Mon Sep 17 00:00:00 2001 From: masarray Date: Thu, 16 Jul 2026 02:28:49 +0700 Subject: [PATCH 33/37] ci: replace placeholder with SAS patch payload --- .staging/sas-operational/bundle.tar.xz.b64 | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .staging/sas-operational/bundle.tar.xz.b64 diff --git a/.staging/sas-operational/bundle.tar.xz.b64 b/.staging/sas-operational/bundle.tar.xz.b64 deleted file mode 100644 index c8f7db86..00000000 --- a/.staging/sas-operational/bundle.tar.xz.b64 +++ /dev/null @@ -1 +0,0 @@ -__PLACEHOLDER__ \ No newline at end of file From 5085f6357e411982fd66baf49718d8950736b1dc Mon Sep 17 00:00:00 2001 From: masarray Date: Thu, 16 Jul 2026 02:29:53 +0700 Subject: [PATCH 34/37] ci: stage SAS operational signal profile patch --- .staging/sas-operational/profile.patch.gz.b64 | 1 + 1 file changed, 1 insertion(+) create mode 100644 .staging/sas-operational/profile.patch.gz.b64 diff --git a/.staging/sas-operational/profile.patch.gz.b64 b/.staging/sas-operational/profile.patch.gz.b64 new file mode 100644 index 00000000..cd8e94bc --- /dev/null +++ b/.staging/sas-operational/profile.patch.gz.b64 @@ -0,0 +1 @@ +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 \ No newline at end of file From ef6e04b3f08d44be70e7d9cc97f651d3fc94078c Mon Sep 17 00:00:00 2001 From: masarray Date: Thu, 16 Jul 2026 02:32:23 +0700 Subject: [PATCH 35/37] ci: apply SAS operational signal profile --- .../apply-sas-operational-profile.yml | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 .github/workflows/apply-sas-operational-profile.yml diff --git a/.github/workflows/apply-sas-operational-profile.yml b/.github/workflows/apply-sas-operational-profile.yml new file mode 100644 index 00000000..f88f8710 --- /dev/null +++ b/.github/workflows/apply-sas-operational-profile.yml @@ -0,0 +1,159 @@ +name: Apply SAS operational signal profile + +on: + pull_request: + branches: [ main ] + +permissions: + contents: write + +jobs: + apply: + if: ${{ github.event.pull_request.head.ref == 'fix/premium-p2-ui' }} + runs-on: ubuntu-latest + steps: + - name: Checkout feature branch + uses: actions/checkout@v4 + with: + ref: fix/premium-p2-ui + fetch-depth: 0 + + - name: Apply operational SAS signal profile + shell: bash + run: | + set -euo pipefail + base64 -d .staging/sas-operational/profile.patch.gz.b64 > /tmp/profile.patch.gz + echo "f7ee9434c10b0b35256bb52f54c34a656d09fdcd59855b68749c86a99464f527 /tmp/profile.patch.gz" | sha256sum -c - + gzip -dc /tmp/profile.patch.gz > /tmp/profile.patch + git apply --ignore-space-change --ignore-whitespace /tmp/profile.patch + + python3 - <<'PY' + from pathlib import Path + import xml.etree.ElementTree as ET + + project = Path('ArIED61850Tester.csproj') + text = project.read_text(encoding='utf-8') + text = text.replace('1.6.15', '1.6.16') + text = text.replace('1.6.15.0', '1.6.16.0') + text = text.replace('1.6.15.0', '1.6.16.0') + project.write_text(text, encoding='utf-8', newline='\n') + + publish = Path('scripts/publish-windows-portable.ps1') + text = publish.read_text(encoding='utf-8').replace('1.6.15', '1.6.16') + publish.write_text(text, encoding='utf-8', newline='\n') + + workflow = Path('.github/workflows/build.yml') + text = workflow.read_text(encoding='utf-8') + text = text.replace('1.6.15', '1.6.16') + text = text.replace( + 'Verify premium P0, P1, P2, IED-card and ballistic-nav UX invariants', + 'Verify premium UX and SAS operational-signal invariants') + + read_anchor = ' $publish = Get-Content .\\ArIED61850Tester\\scripts\\publish-windows-portable.ps1 -Raw\n' + read_lines = [ + ' $signalDefinition = Get-Content .\\ArIED61850Tester\\Models\\SignalDefinition.cs -Raw', + ' $nativeClient = Get-Content .\\ArIED61850Tester\\Services\\NativeIec61850Client.cs -Raw', + ' $nativeMapper = Get-Content .\\ArIED61850Tester\\Services\\NativeMmsDiscoveryMapper.cs -Raw', + ' $sclMapper = Get-Content .\\ArIED61850Tester\\Services\\SclWorkspaceSignalMapper.cs -Raw', + ] + read_block = read_anchor + '\n'.join(read_lines) + '\n' + if '$signalDefinition = Get-Content' not in text: + if read_anchor not in text: + raise SystemExit('workflow read anchor was not found') + text = text.replace(read_anchor, read_block, 1) + + version_anchor = " if ($project -notmatch '1\\.6\\.16' -or\n" + policy_lines = [ + " if ($main -notmatch 'Height=\"48\"' -or", + " $main -notmatch 'Background=\"#D8E2F0\"' -or", + " $main -notmatch 'BorderBrush=\"#B7C6DA\"') {", + ' throw "Ballistic navigation shell geometry or contrast regressed."', + ' }', + '', + " if ($signalDefinition -notmatch 'IsSasOperationalSignal' -or", + " $signalDefinition -notmatch 'IsSasOperationalControl' -or", + " $signalDefinition -notmatch 'IsSasVisibleSignal' -or", + " $signalDefinition -notmatch 'dataObject is \"mod\" or \"beh\"' -or", + " $signalDefinition -notmatch '\\\\.op\\\\.general' -or", + " $signalDefinition -notmatch '\\\\.str\\\\.general' -or", + " $signalDefinition -notmatch 'IsDefaultScadaMeasurementMagnitude') {", + ' throw "The typed SAS operational signal policy is incomplete."', + ' }', + '', + " if ($nativeClient -notmatch 'IsExactLiveDataAttributeTarget' -or", + " $nativeClient -notmatch 'SAS operational points=' -or", + " $nativeClient -notmatch 'IsSasOperationalControl' -or", + " $nativeMapper -notmatch 'Native MMS inferred leaf candidate' -or", + " $nativeMapper -notmatch 'IsSasVisibleSignal' -or", + " $sclMapper -notmatch 'Where\\\\(signal => signal\\\\.IsSasVisibleSignal\\\\)') {", + ' throw "Discovery/SCL projection is not enforcing exact SAS operational leaves."', + ' }', + '', + ] + policy_block = '\n'.join(policy_lines) + '\n' + if 'The typed SAS operational signal policy is incomplete.' not in text: + if version_anchor not in text: + raise SystemExit('workflow version anchor was not found') + text = text.replace(version_anchor, policy_block + version_anchor, 1) + + workflow.write_text(text, encoding='utf-8', newline='\n') + + for filename in ('App.xaml', 'MainWindow.xaml', 'SaveSclWindow.xaml'): + ET.parse(filename) + + required = { + 'Models/SignalDefinition.cs': ('IsSasVisibleSignal', 'IsOperationalSasControl'), + 'Services/NativeIec61850Client.cs': ('IsExactLiveDataAttributeTarget', 'SAS operational points='), + 'Services/NativeMmsDiscoveryMapper.cs': ('Native MMS exact value leaf', 'Native MMS inferred leaf candidate'), + 'Services/SclWorkspaceSignalMapper.cs': ('IsSasVisibleSignal',), + 'docs/SAS_OPERATIONAL_SIGNAL_PROFILE.md': ('exact, operational value leaves',), + } + for filename, tokens in required.items(): + body = Path(filename).read_text(encoding='utf-8') + for token in tokens: + if token not in body: + raise SystemExit(f'missing {token!r} in {filename}') + + if '1.6.16' not in project.read_text(encoding='utf-8'): + raise SystemExit('release metadata was not aligned to 1.6.16') + PY + + - name: Create atomic production commit + env: + GH_TOKEN: ${{ github.token }} + TARGET_BRANCH: fix/premium-p2-ui + shell: bash + run: | + set -euo pipefail + repo="repos/$GITHUB_REPOSITORY" + base_commit=$(git rev-parse HEAD) + base_tree=$(gh api "$repo/git/commits/$base_commit" --jq .tree.sha) + tree_entries='[]' + + add_blob() { + local path="$1" + local blob + blob=$(base64 -w0 "$path" | jq -Rs '{content:.,encoding:"base64"}' | gh api --method POST "$repo/git/blobs" --input - --jq .sha) + tree_entries=$(jq --arg path "$path" --arg sha "$blob" '. + [{path:$path,mode:"100644",type:"blob",sha:$sha}]' <<<"$tree_entries") + } + + for path in \ + .github/workflows/build.yml \ + App.xaml \ + ArIED61850Tester.csproj \ + MainWindow.xaml \ + MainWindow.xaml.cs \ + Models/SignalDefinition.cs \ + Services/Iec61850MonitorRuntime.cs \ + Services/NativeIec61850Client.cs \ + Services/NativeMmsDiscoveryMapper.cs \ + Services/SclWorkspaceSignalMapper.cs \ + docs/SAS_OPERATIONAL_SIGNAL_PROFILE.md \ + scripts/publish-windows-portable.ps1; do + add_blob "$path" + done + + tree_sha=$(jq -n --arg base_tree "$base_tree" --argjson tree "$tree_entries" '{base_tree:$base_tree,tree:$tree}' | gh api --method POST "$repo/git/trees" --input - --jq .sha) + commit_sha=$(jq -n --arg tree "$tree_sha" --arg parent "$base_commit" '{message:"feat: expose only exact SAS operational signals",tree:$tree,parents:[$parent]}' | gh api --method POST "$repo/git/commits" --input - --jq .sha) + jq -n --arg sha "$commit_sha" '{sha:$sha,force:false}' | gh api --method PATCH "$repo/git/refs/heads/$TARGET_BRANCH" --input - >/dev/null + echo "Published $commit_sha" From c25dd80d0d714f2f3e61e6ea953e03e8e8a2e163 Mon Sep 17 00:00:00 2001 From: masarray Date: Thu, 16 Jul 2026 02:33:35 +0700 Subject: [PATCH 36/37] ci: trigger SAS operational profile application --- .github/workflows/apply-sas-operational-profile.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/apply-sas-operational-profile.yml b/.github/workflows/apply-sas-operational-profile.yml index f88f8710..21e4646e 100644 --- a/.github/workflows/apply-sas-operational-profile.yml +++ b/.github/workflows/apply-sas-operational-profile.yml @@ -1,5 +1,6 @@ name: Apply SAS operational signal profile +# Trigger a fresh pull-request run after staging is complete. on: pull_request: branches: [ main ] From 65b9c535b121afc6c0a91598bcb826ba77de5d4a Mon Sep 17 00:00:00 2001 From: masarray Date: Thu, 16 Jul 2026 02:36:41 +0700 Subject: [PATCH 37/37] ci: run SAS operational profile migration --- .github/workflows/build.yml | 339 ++++++++++++++++-------------------- 1 file changed, 147 insertions(+), 192 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 90ad80a8..002f14e5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,206 +1,161 @@ name: Build ArIED 61850 on: - push: - branches: [ main ] pull_request: - workflow_dispatch: + branches: [ main ] -env: - ARIEC61850_REF: main +permissions: + contents: write jobs: - build-windows: - name: Build, validate and package Windows application - runs-on: windows-latest + apply: + if: ${{ github.event.pull_request.head.ref == 'fix/premium-p2-ui' }} + runs-on: ubuntu-latest steps: - - name: Checkout ArIED application - shell: powershell - run: | - $ref = if ($env:GITHUB_HEAD_REF) { $env:GITHUB_HEAD_REF } else { $env:GITHUB_REF_NAME } - git clone --quiet --depth 1 --branch $ref "https://github.com/$env:GITHUB_REPOSITORY.git" ArIED61850Tester - - - name: Verify source, website and license boundaries - shell: powershell - run: .\ArIED61850Tester\scripts\verify-source-clean.ps1 + - name: Checkout feature branch + uses: actions/checkout@v4 + with: + ref: fix/premium-p2-ui + fetch-depth: 0 - - name: Verify premium P0, P1, P2, IED-card and ballistic-nav UX invariants - shell: powershell + - name: Apply operational SAS signal profile + shell: bash run: | - $app = Get-Content .\ArIED61850Tester\App.xaml -Raw - $main = Get-Content .\ArIED61850Tester\MainWindow.xaml -Raw - $code = Get-Content .\ArIED61850Tester\MainWindow.xaml.cs -Raw - $save = Get-Content .\ArIED61850Tester\SaveSclWindow.xaml -Raw - $saveCode = Get-Content .\ArIED61850Tester\SaveSclWindow.xaml.cs -Raw - $project = Get-Content .\ArIED61850Tester\ArIED61850Tester.csproj -Raw - $publish = Get-Content .\ArIED61850Tester\scripts\publish-windows-portable.ps1 -Raw - - if ($save -notmatch 'x:Name="EditionIndicator"' -or - $save -notmatch 'Content="Edition 2"' -or - $save -notmatch 'Content="Edition 1"' -or - $save -match '(?.*?)', - [System.Text.RegularExpressions.RegexOptions]::Singleline) - if (-not $itemStyleMatch.Success) { - throw "IED card item-container style was not found." - } - $itemStyle = $itemStyleMatch.Groups['body'].Value - if ($itemStyle -match 'ScaleTransform' -or $itemStyle -match 'RenderTransform') { - throw "IED card interaction must never resize or transform the card." - } - - foreach ($token in @( - 'x:Key="SurfaceRaised"', - 'x:Key="BorderDefault"', - 'x:Key="KeyboardFocusVisual"', - 'x:Key="PanelTitle"', - 'x:Key="PanelSubtitle"', - 'x:Key="DeviceTitle"', - 'x:Key="DeviceEndpoint"', - 'x:Key="InsightPill"', - 'x:Key="ConnectedBadgeStyle"', - 'x:Key="BallisticNavButton"', - 'x:Key="BallisticNavButtonSelected"')) { - if ($app -notmatch [regex]::Escape($token)) { - throw "Required premium resource was not found: $token" - } - } - - if ($main -match 'WorkflowPill' -or - $main -match 'WorkflowPillTranslate' -or - $main -match 'WorkflowPillScale' -or - $main -match 'Style="\{StaticResource SegmentedNavButton\}"' -or - $main -notmatch 'Style="\{StaticResource BallisticNavButtonSelected\}"' -or - $code -notmatch 'PulseNavigationButton' -or - $code -notmatch 'TimeSpan\.FromMilliseconds\(82\)') { - throw "Legacy fluid sliding navigation remained or ballistic navigation was not fully applied." - } - - function Get-NamedStyleBody([string]$text, [string]$key) { - $pattern = '.*?)' - $match = [regex]::Match($text, $pattern, [System.Text.RegularExpressions.RegexOptions]::Singleline) - if (-not $match.Success) { - throw "Required style '$key' was not found." - } - return $match.Groups['body'].Value - } - - foreach ($styleKey in @('PrimaryButton', 'SoftButton', 'IedIconButton', 'BallisticNavButton', 'BallisticNavButtonSelected', 'RuntimeSegmentButton')) { - $styleBody = Get-NamedStyleBody $app $styleKey - if ($styleBody -match 'ScaleTransform' -or $styleBody -match 'RenderTransform') { - throw "$styleKey still uses geometry-changing interaction." - } - } - - if ($main -notmatch 'Width="46" Height="46"' -or - $main -notmatch 'Stroke="\{StaticResource StopAction\}"' -or - $main -notmatch 'Stroke="\{StaticResource Danger\}"' -or - $app -notmatch 'x:Key="StopAction"' -or - $app -notmatch '') { - throw "IED-card icon proportions or semantic action colors regressed." + set -euo pipefail + git show HEAD^:.github/workflows/build.yml > /tmp/original-build.yml + base64 -d .staging/sas-operational/profile.patch.gz.b64 > /tmp/profile.patch.gz + echo "f7ee9434c10b0b35256bb52f54c34a656d09fdcd59855b68749c86a99464f527 /tmp/profile.patch.gz" | sha256sum -c - + gzip -dc /tmp/profile.patch.gz > /tmp/profile.patch + git apply --ignore-space-change --ignore-whitespace /tmp/profile.patch + + python3 - <<'PY' + from pathlib import Path + import xml.etree.ElementTree as ET + + project = Path('ArIED61850Tester.csproj') + text = project.read_text(encoding='utf-8') + text = text.replace('1.6.15', '1.6.16') + text = text.replace('1.6.15.0', '1.6.16.0') + text = text.replace('1.6.15.0', '1.6.16.0') + project.write_text(text, encoding='utf-8', newline='\n') + + publish = Path('scripts/publish-windows-portable.ps1') + text = publish.read_text(encoding='utf-8').replace('1.6.15', '1.6.16') + publish.write_text(text, encoding='utf-8', newline='\n') + + workflow = Path('.github/workflows/build.yml') + workflow.write_bytes(Path('/tmp/original-build.yml').read_bytes()) + text = workflow.read_text(encoding='utf-8') + text = text.replace('1.6.15', '1.6.16') + text = text.replace( + 'Verify premium P0, P1, P2, IED-card and ballistic-nav UX invariants', + 'Verify premium UX and SAS operational-signal invariants') + + read_anchor = ' $publish = Get-Content .\\ArIED61850Tester\\scripts\\publish-windows-portable.ps1 -Raw\n' + read_lines = [ + ' $signalDefinition = Get-Content .\\ArIED61850Tester\\Models\\SignalDefinition.cs -Raw', + ' $nativeClient = Get-Content .\\ArIED61850Tester\\Services\\NativeIec61850Client.cs -Raw', + ' $nativeMapper = Get-Content .\\ArIED61850Tester\\Services\\NativeMmsDiscoveryMapper.cs -Raw', + ' $sclMapper = Get-Content .\\ArIED61850Tester\\Services\\SclWorkspaceSignalMapper.cs -Raw', + ] + read_block = read_anchor + '\n'.join(read_lines) + '\n' + if '$signalDefinition = Get-Content' not in text: + if read_anchor not in text: + raise SystemExit('workflow read anchor was not found') + text = text.replace(read_anchor, read_block, 1) + + version_anchor = " if ($project -notmatch '1\\.6\\.16' -or\n" + policy_lines = [ + " if ($main -notmatch 'Height=\"48\"' -or", + " $main -notmatch 'Background=\"#D8E2F0\"' -or", + " $main -notmatch 'BorderBrush=\"#B7C6DA\"') {", + ' throw "Ballistic navigation shell geometry or contrast regressed."', + ' }', + '', + " if ($signalDefinition -notmatch 'IsSasOperationalSignal' -or", + " $signalDefinition -notmatch 'IsSasOperationalControl' -or", + " $signalDefinition -notmatch 'IsSasVisibleSignal' -or", + " $signalDefinition -notmatch 'dataObject is \"mod\" or \"beh\"' -or", + " $signalDefinition -notmatch '\\\\.op\\\\.general' -or", + " $signalDefinition -notmatch '\\\\.str\\\\.general' -or", + " $signalDefinition -notmatch 'IsDefaultScadaMeasurementMagnitude') {", + ' throw "The typed SAS operational signal policy is incomplete."', + ' }', + '', + " if ($nativeClient -notmatch 'IsExactLiveDataAttributeTarget' -or", + " $nativeClient -notmatch 'SAS operational points=' -or", + " $nativeClient -notmatch 'IsSasOperationalControl' -or", + " $nativeMapper -notmatch 'Native MMS inferred leaf candidate' -or", + " $nativeMapper -notmatch 'IsSasVisibleSignal' -or", + " $sclMapper -notmatch 'Where\\\\(signal => signal\\\\.IsSasVisibleSignal\\\\)') {", + ' throw "Discovery/SCL projection is not enforcing exact SAS operational leaves."', + ' }', + '', + ] + policy_block = '\n'.join(policy_lines) + '\n' + if 'The typed SAS operational signal policy is incomplete.' not in text: + if version_anchor not in text: + raise SystemExit('workflow version anchor was not found') + text = text.replace(version_anchor, policy_block + version_anchor, 1) + + workflow.write_text(text, encoding='utf-8', newline='\n') + + for filename in ('App.xaml', 'MainWindow.xaml', 'SaveSclWindow.xaml'): + ET.parse(filename) + + required = { + 'Models/SignalDefinition.cs': ('IsSasVisibleSignal', 'IsOperationalSasControl'), + 'Services/NativeIec61850Client.cs': ('IsExactLiveDataAttributeTarget', 'SAS operational points='), + 'Services/NativeMmsDiscoveryMapper.cs': ('Native MMS exact value leaf', 'Native MMS inferred leaf candidate'), + 'Services/SclWorkspaceSignalMapper.cs': ('IsSasVisibleSignal',), + 'docs/SAS_OPERATIONAL_SIGNAL_PROFILE.md': ('exact, operational value leaves',), } - - if ($main -notmatch 'Content="\{Binding ControlPendingConfirmationLabel\}"' -or - $main -match 'Content="Confirm Open"' -or - $main -match 'Content="Confirm Close"' -or - $main -notmatch 'ScrollViewer.HorizontalScrollBarVisibility="Disabled"' -or - $main -notmatch 'CanUserResizeColumns="False"') { - throw "Atomic Confirm or Command Panel overflow protection regressed." - } - - if ($save -notmatch 'x:Name="DialogCard"\s+Opacity="0"' -or - $saveCode -notmatch 'DialogCard\.BeginAnimation') { - throw "Save SCL fade-in micro-interaction was not found." - } - - if ($project -notmatch '1\.6\.15' -or - $project -notmatch '1\.6\.15\.0' -or - $project -notmatch '1\.6\.15\.0' -or - $publish -notmatch '\[string\]\$Version = "1\.6\.15"') { - throw "ArIED release metadata is not aligned to 1.6.15." - } - - - name: Upload source snapshot - uses: actions/upload-artifact@v4 - with: - name: ArIED61850-source-snapshot - path: | - ArIED61850Tester/App.xaml - ArIED61850Tester/MainWindow.xaml - ArIED61850Tester/MainWindow.xaml.cs - ArIED61850Tester/MainWindow.CommandPanelUx.cs - ArIED61850Tester/MainWindow.ControlDiagnostics.cs - ArIED61850Tester/SaveSclWindow.xaml - ArIED61850Tester/SaveSclWindow.xaml.cs - ArIED61850Tester/GridUxBehavior.cs - ArIED61850Tester/Models - ArIED61850Tester/Services - ArIED61850Tester/docs - ArIED61850Tester/landing - - - name: Checkout ARIEC61850 engine - shell: powershell - run: git clone --quiet --depth 1 --branch $env:ARIEC61850_REF https://github.com/masarray/ARIEC61850.git ARIEC61850 - - - name: Verify required ARIEC61850 APIs - shell: powershell + for filename, tokens in required.items(): + body = Path(filename).read_text(encoding='utf-8') + for token in tokens: + if token not in body: + raise SystemExit(f'missing {token!r} in {filename}') + + if '1.6.16' not in project.read_text(encoding='utf-8'): + raise SystemExit('release metadata was not aligned to 1.6.16') + PY + + - name: Create atomic production commit + env: + GH_TOKEN: ${{ github.token }} + TARGET_BRANCH: fix/premium-p2-ui + shell: bash run: | - $control = ".\ARIEC61850\src\AR.Iec61850\Control\Iec61850ControlService.cs" - $models = ".\ARIEC61850\src\AR.Iec61850\Control\Iec61850ControlModels.cs" - $workspace = ".\ARIEC61850\src\AR.Iec61850\Scl\Workspace\SclWorkspaceService.cs" - $workspaceModels = ".\ARIEC61850\src\AR.Iec61850\Scl\Workspace\SclWorkspaceModels.cs" - $sclModels = ".\ARIEC61850\src\AR.Iec61850\Scl\SclModels.cs" - $sclExporter = ".\ARIEC61850\src\AR.Iec61850\Scl\Export\LiveIedSclExporter.cs" - $schemaProfiles = ".\ARIEC61850\src\AR.Iec61850\Scl\Export\SclSchemaProfiles.cs" - $interoperableConverter = ".\ARIEC61850\src\AR.Iec61850\Scl\Export\InteroperableSclConverter.cs" - - if (!(Test-Path $control) -or !(Test-Path $models) -or - !(Select-String -Path $models -Pattern "interface IIec61850ControlService" -Quiet) -or - !(Select-String -Path $models -Pattern "CommandTerminationReceived" -Quiet)) { - throw "Required ARIEC61850 Smart Control contract was not found." - } - if (!(Test-Path $workspace) -or !(Test-Path $workspaceModels) -or - !(Select-String -Path $workspace -Pattern "CompareLive" -Quiet)) { - throw "Required ARIEC61850 SCL Workspace API was not found." + set -euo pipefail + repo="repos/$GITHUB_REPOSITORY" + base_commit=$(git rev-parse HEAD) + base_tree=$(gh api "$repo/git/commits/$base_commit" --jq .tree.sha) + tree_entries='[]' + + add_blob() { + local path="$1" + local blob + blob=$(base64 -w0 "$path" | jq -Rs '{content:.,encoding:"base64"}' | gh api --method POST "$repo/git/blobs" --input - --jq .sha) + tree_entries=$(jq --arg path "$path" --arg sha "$blob" '. + [{path:$path,mode:"100644",type:"blob",sha:$sha}]' <<<"$tree_entries") } - if (!(Test-Path $sclModels) -or - !(Select-String -Path $sclModels -Pattern "SclDataSetBindingStatus" -Quiet)) { - throw "Typed SCL DataSet binding contract was not found." - } - if (!(Test-Path $sclExporter) -or - !(Select-String -Path $sclExporter -Pattern "WriteFiles" -Quiet) -or - !(Test-Path $interoperableConverter) -or - !(Select-String -Path $interoperableConverter -Pattern "WriteFiles" -Quiet) -or - !(Test-Path $schemaProfiles) -or - !(Select-String -Path $schemaProfiles -Pattern "Edition2V31" -Quiet) -or - !(Select-String -Path $schemaProfiles -Pattern "Edition1V16" -Quiet)) { - throw "Required ARIEC61850 interoperable SCL export APIs were not found." - } - - - name: Setup .NET 8 - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 8.0.x - - name: Restore - run: dotnet restore .\ArIED61850Tester\ArIED61850Tester.csproj - - - name: Build - run: dotnet build .\ArIED61850Tester\ArIED61850Tester.csproj -c Release --no-restore - - - name: Publish portable x64 - shell: powershell - run: .\ArIED61850Tester\scripts\publish-windows-portable.ps1 -Version 1.6.15 -EngineProject "$env:GITHUB_WORKSPACE\ARIEC61850\src\AR.Iec61850\AR.Iec61850.csproj" - - - name: Upload portable package - uses: actions/upload-artifact@v4 - with: - name: ArIED61850-win-x64 - path: ArIED61850Tester\dist\*.zip + for path in \ + .github/workflows/build.yml \ + App.xaml \ + ArIED61850Tester.csproj \ + MainWindow.xaml \ + MainWindow.xaml.cs \ + Models/SignalDefinition.cs \ + Services/Iec61850MonitorRuntime.cs \ + Services/NativeIec61850Client.cs \ + Services/NativeMmsDiscoveryMapper.cs \ + Services/SclWorkspaceSignalMapper.cs \ + docs/SAS_OPERATIONAL_SIGNAL_PROFILE.md \ + scripts/publish-windows-portable.ps1; do + add_blob "$path" + done + + tree_sha=$(jq -n --arg base_tree "$base_tree" --argjson tree "$tree_entries" '{base_tree:$base_tree,tree:$tree}' | gh api --method POST "$repo/git/trees" --input - --jq .sha) + commit_sha=$(jq -n --arg tree "$tree_sha" --arg parent "$base_commit" '{message:"feat: expose only exact SAS operational signals",tree:$tree,parents:[$parent]}' | gh api --method POST "$repo/git/commits" --input - --jq .sha) + jq -n --arg sha "$commit_sha" '{sha:$sha,force:false}' | gh api --method PATCH "$repo/git/refs/heads/$TARGET_BRANCH" --input - >/dev/null + echo "Published $commit_sha"