Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 157 additions & 0 deletions .github/workflows/m3h2-rc1-public-candidate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
name: Publish M3.H.2 RC1 Verified Windows Engineering Preview

on:
pull_request:
paths:
- '.github/workflows/m3h2-rc1-public-candidate.yml'
- 'docs/releases/M3H2_RC1_PUBLIC_CANDIDATE_2026-10-25.md'
push:
branches: [main]
paths:
- '.github/workflows/m3h2-rc1-public-candidate.yml'

permissions:
contents: read
actions: read

concurrency:
group: publish-m3h2-rc1-once
cancel-in-progress: false

env:
RC_TAG: processbus-sim-m3h2-rc1-20261025
RC_HEAD: fb81c247fb24cd6b5ac6aad6b3fb27486d620b96
RC_RUN: '40145602361'
RC_ARTIFACT: arstack-studio-native-preview-win-x64-328-fb81c247fb24cd6b5ac6aad6b3fb27486d620b96
PREVIOUS_STABLE_ID: '422727082'

jobs:
evidence:
name: Verify preview evidence, main ancestry and frozen stable release
runs-on: ubuntu-latest
timeout-minutes: 7
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check exact provenance; do not promote physical RX evidence
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
git merge-base --is-ancestor "$RC_HEAD" HEAD
test "$(gh api "repos/$GITHUB_REPOSITORY/pulls/328" --jq '.merged')" = true
test "$(gh api "repos/$GITHUB_REPOSITORY/actions/runs/$RC_RUN" --jq '.head_sha')" = "$RC_HEAD"
test "$(gh api "repos/$GITHUB_REPOSITORY/actions/runs/$RC_RUN" --jq '.conclusion')" = success
test "$(gh api "repos/$GITHUB_REPOSITORY/releases/latest" --jq '.id')" = "$PREVIOUS_STABLE_ID"
test -s docs/releases/M3H2_RC1_PUBLIC_CANDIDATE_2026-10-25.md
grep -q 'prerelease, NOT Public Stable' docs/releases/M3H2_RC1_PUBLIC_CANDIDATE_2026-10-25.md
test "$(gh api "repos/$GITHUB_REPOSITORY/issues/324" --jq '.state')" = open
if gh release view "$RC_TAG" >/dev/null 2>&1; then
echo 'Refusing to overwrite or duplicate M3.H.2 RC1 release' >&2
exit 1
fi
echo 'PASS: exact PR-head Windows+firmware CI, ancestry and immutable stable release'

publish:
name: Publish checksummed public prerelease only
needs: evidence
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
timeout-minutes: 25
permissions:
contents: write
actions: read
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Download exact successful Windows preview artifact
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
mkdir -p candidate extracted dist
gh run download "$RC_RUN" --repo "$GITHUB_REPOSITORY" \
--name "$RC_ARTIFACT" --dir candidate
test -s candidate/ARStack-Studio-Preview-win-x64-setup.exe
test -s candidate/ARStack-Studio-Preview-win-x64-portable.zip
test -s candidate/SHA256SUMS.txt
(cd candidate && sha256sum -c SHA256SUMS.txt)
unzip -t candidate/ARStack-Studio-Preview-win-x64-portable.zip | tail -n 1
unzip -q candidate/ARStack-Studio-Preview-win-x64-portable.zip -d extracted
test -s extracted/PREVIEW-PROVENANCE.json
test -s extracted/firmware/firmware-manifest.json
python3 - <<'PY'
import hashlib, json, pathlib, os
root=pathlib.Path('extracted')
m=json.loads((root/'firmware/firmware-manifest.json').read_text(encoding='utf-8-sig'))
p=json.loads((root/'PREVIEW-PROVENANCE.json').read_text(encoding='utf-8-sig'))
sha=os.environ['RC_HEAD']
assert p['guiCommit']==sha and p['firmwareSourceCommit']==sha
assert p['guiVersion']=='0.1.1-preview'
assert m['sourceCommit']==sha and m['chip']=='esp32p4'
assert m['phaseSyncPreview'] is True
assert m['globalGmTraceabilityPhysicallyProven'] is False
firmware=root/'firmware'/m['image']
assert firmware.is_file() and firmware.stat().st_size>100000
assert hashlib.sha256(firmware.read_bytes()).hexdigest()==m['sha256']
assert len(m['image'])<128 and '/' not in m['image'] and '\\' not in m['image']
assert (root/'ARStackStudio.exe').is_file()
print('PASS: preview GUI/source SHA equals bundled firmware SHA; firmware checksum valid')
PY
cp candidate/ARStack-Studio-Preview-win-x64-setup.exe dist/
cp candidate/ARStack-Studio-Preview-win-x64-portable.zip dist/
cp extracted/firmware/firmware-manifest.json dist/
firmware_name="$(python3 -c 'import json; print(json.load(open("extracted/firmware/firmware-manifest.json"))["image"])')"
cp "extracted/firmware/$firmware_name" "dist/$firmware_name"
(cd dist && sha256sum ./*.exe ./*.zip ./*.bin ./firmware-manifest.json > SHA256SUMS.txt && sha256sum -c SHA256SUMS.txt)

- name: Publish new prerelease without changing latest stable
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
set -euo pipefail
test "$GITHUB_REF_NAME" = main
git merge-base --is-ancestor "$RC_HEAD" HEAD
test "$(gh api "repos/$GITHUB_REPOSITORY/releases/latest" --jq '.id')" = "$PREVIOUS_STABLE_ID"
if gh release view "$RC_TAG" >/dev/null 2>&1; then
echo 'RC tag/release already exists; no --clobber permitted' >&2
exit 1
fi
gh release create "$RC_TAG" dist/* \
--target "$GITHUB_SHA" \
--title 'ARStack M3.H.2 RC1 — Windows Engineering Preview (not stable)' \
--prerelease \
--notes-file docs/releases/M3H2_RC1_PUBLIC_CANDIDATE_2026-10-25.md \
--verify-tag=false

- name: Verify published public preview and stable preservation
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
set -euo pipefail
gh api "repos/$GITHUB_REPOSITORY/releases/tags/$RC_TAG" > /tmp/rc.json
gh api "repos/$GITHUB_REPOSITORY/releases/latest" > /tmp/latest.json
python3 - <<'PY'
import json,pathlib,hashlib,os
rc=json.load(open('/tmp/rc.json'))
stable=json.load(open('/tmp/latest.json'))
assert rc['tag_name']==os.environ['RC_TAG']
assert rc['prerelease'] is True and rc['draft'] is False
assert stable['id']==int(os.environ['PREVIOUS_STABLE_ID']), 'Stable latest was inadvertently replaced'
expected={f.name for f in pathlib.Path('dist').iterdir() if f.is_file()}
actual={a['name'] for a in rc['assets']}
assert actual==expected, f'Published asset list mismatch: {actual ^ expected}'
for asset in rc['assets']:
source=pathlib.Path('dist')/asset['name']
assert source.stat().st_size==asset['size']
digest=asset.get('digest')
if digest:
assert digest=='sha256:'+hashlib.sha256(source.read_bytes()).hexdigest()
print('PASS: M3.H.2 RC1 public prerelease, all exact files and immutable latest stable')
PY
41 changes: 41 additions & 0 deletions docs/releases/M3H2_RC1_PUBLIC_CANDIDATE_2026-10-25.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# ARStack Process Bus Simulator M3.H.2 RC1 — Public Engineering Preview

**25 October 2026 — prerelease, NOT Public Stable.** This release candidate is intended for isolated, non-production IEC 61850 SV/PTP laboratory experiments. The latest tested stable M3.H.1 continues to be [processbus-sim-m3h1-20261024](https://github.com/masarray/arstack61850/releases/tag/processbus-sim-m3h1-20261024), without any changed assets or firmware.

## What's improved

- **Current-session SV STOP alarm:** board-retained `SVLIFE stop_cause=RUNTIME_FAULT` history no longer generates a frightening error on ordinary Studio startup. A red unexpected-STOP alarm requires an observed transition from verified RUNNING to STOPPED during the current session. New fault evidence remains diagnosable.
- **Compact hardware status LED:** DISCONNECTED (grey), CONNECTING (blue), PREPARING (amber), READY/RUNNING (green) and ERROR (red), sourced from the active serial/identity/supervisor state.
- **Canonical SV TX diagnostics:** first/last ESP-IDF Ethernet transmit errors, link evidence and contiguous-burst/stop history are retained across operator restart. The fail-closed **400 consecutive TX failure** threshold has **not** been bypassed.
- Exact Windows GUI and ESP32-P4 firmware pairing is verified in the source-head CI and installer lifecycle tests.

## Exactly which binaries

| Evidence | Immutable source |
| --- | --- |
| Studio + paired firmware source | PR [#328](https://github.com/masarray/arstack61850/pull/328) reviewed head `fb81c247fb24cd6b5ac6aad6b3fb27486d620b96` |
| Windows setup + portable build | [GitHub Actions run 40145602361](https://github.com/masarray/arstack61850/actions/runs/40145602361), artifact `arstack-studio-native-preview-win-x64-328-fb81c247fb24cd6b5ac6aad6b3fb27486d620b96` |
| Firmware built from same head | [Run 40145602361](https://github.com/masarray/arstack61850/actions/runs/40145602361), firmware `sourceCommit=fb81c247fb24cd6b5ac6aad6b3fb27486d620b96` |
| PR CI | 10 success, 1 conditional skip, 0 failures; Windows native, Qt headless/HiDPI, firmware, cleanup and installer migration |
| Merge to main | `c8d351f1fe80ca4cf8f3e58cf6727566bec7d1f6` (packaged runtime is PR head above; subsequent changes are release paperwork) |

Download the Windows **Setup** or **Portable ZIP**, confirm the included `SHA256SUMS.txt`, and use the **firmware bundled with that package**. Do not mix preview and old stable firmware. The executable still reports the internal `0.1.1-preview` engineering version; **M3.H.2 RC1 is the package/release-candidate label, not a claim of a new stable semantic version or hardware-certified product**.

The separate Linux pull-request AppImage artifact was a GUI/test build and did not include a cross-platform release-tag-matched firmware package. **It is not promoted as a firmware-paired Linux release in RC1.** Public Stable M3.H.1 remains the Linux choice.

## Actual user-observed result

Two operator screenshots on COM15 show: connected idle **READY**, active **RUNNING**, and a displayed snapshot of approximately **3,999 fps, MISSED 0, TX FAIL 0**. These prove useful operator-state UX and that a board claims RUNNING at that moment. They **do not** establish a 1-hour endurance pass, independent NIC capture, reception by DigSubAnalyzer/ARSAS, authenticated PTP grandmaster synchronization, or error-free long-term Ethernet transmission.

## Stable promotion — blocked until these are completed

- [ ] Physical **3600 s** retained run on this exact candidate, complete start/stop cycling and no missed frames/transport faults; attach test conditions and trustworthy raw logs.
- [ ] Recipient NIC unfiltered PCAPNG captures actual 0x88BA packets, VLAN/tag state, APPID, svID and sample-counter continuity; note NIC/offload/switch topology.
- [ ] **DigSubAnalyzer** receives and decodes the live stream with values and sample counter.
- [ ] **ARSAS** receives and decodes the same real stream independently, with filters/NIC identity recorded.
- [ ] Verify full package SHA-256, firmware manifest/source pairing, installer migration and Windows/Linux cross-platform distribution for the new stable release tag. Do not relabel these preview binaries stable.
- [ ] Activate and independently verify [main branch protection](https://github.com/masarray/arstack61850/issues/322) under authorized repository administration before calling governance complete.

Field incident: [#324](https://github.com/masarray/arstack61850/issues/324). Release gate/runbook: [independent SV wire capture](../P0_SMV_WIRE_CAPTURE_ACCEPTANCE.md) and [RC2 physical acceptance](../SMV_STUDIO_RC2_ACCEPTANCE.md).

This candidate is strictly **prerelease=true**. The workflow refuses to overwrite or downgrade the existing Public Stable release, and its artifacts are pinned and checksum-checked before publication.
Loading