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
130 changes: 60 additions & 70 deletions .github/workflows/m3h-source-main-reconciliation.yml
Original file line number Diff line number Diff line change
@@ -1,97 +1,87 @@
name: M3.H Reconcile Tested Source into Main (Safe Staging)
name: M3.H Stable Release and Main Provenance Guard (Read Only)

on:
push:
branches: [main]
paths: ['.github/workflows/m3h-source-main-reconciliation.yml']
paths:
- '.github/workflows/m3h-source-main-reconciliation.yml'
pull_request:
branches: [main]
paths:
- '.github/workflows/m3h-source-main-reconciliation.yml'
- 'docs/integration/M3H_MAINLINE_RELEASE_SOURCE_CLOSURE_2026-10-24.md'
workflow_dispatch:

permissions:
contents: write
contents: read

jobs:
reconcile-exact-source:
verify-immutable-m3h:
if: github.repository == 'masarray/arstack61850'
runs-on: ubuntu-latest
timeout-minutes: 18
timeout-minutes: 8
env:
GH_TOKEN: ${{ github.token }}
VERIFIED_SOURCE_SHA: a922e9bdba4e57a4e88406eb83344455dd8ff142
INTEGRATION_BRANCH: integration/m3h-verified-source-to-main-20261024
RELEASE_TAG: processbus-sim-m3h-20261024
TAG_COMMIT: a4b6e53d0dbbcababc55ba8a4ff97ade6f2a6200
steps:
- name: Checkout complete history of current main
- name: Checkout full history with release tag (no mutable writes)
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
persist-credentials: true
- name: Reconcile, preserve main updates, and stage a traceable two-parent merge
persist-credentials: false

- name: Confirm tested source and official release tag both are in mainline ancestry
shell: bash
run: |
set -euo pipefail
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git fetch origin "$VERIFIED_SOURCE_SHA"
MAIN_SHA="$(git rev-parse HEAD)"
BASE_SHA="$(git merge-base "$MAIN_SHA" "$VERIFIED_SOURCE_SHA")"
echo "MAIN_BASE=$MAIN_SHA" >> "$GITHUB_ENV"
echo "Common ancestor: $BASE_SHA"
echo "Current main: $MAIN_SHA"
echo "Verified M3.H source: $VERIFIED_SOURCE_SHA"
git merge-base --is-ancestor "$BASE_SHA" "$MAIN_SHA"
git merge-base --is-ancestor "$BASE_SHA" "$VERIFIED_SOURCE_SHA"
echo 'Main updates since common ancestor:'
git diff --name-status "$BASE_SHA" "$MAIN_SHA"
echo 'Main/source overlapping changed paths:'
comm -12 <(git diff --name-only "$BASE_SHA" "$MAIN_SHA" | sort) <(git diff --name-only "$BASE_SHA" "$VERIFIED_SOURCE_SHA" | sort) || true
git switch -c "$INTEGRATION_BRANCH"
if ! git merge --no-ff --no-edit -m 'Merge verified M3.H Process Bus SV/PTP source into main history' "$VERIFIED_SOURCE_SHA"; then
echo 'STOP: merge conflicts require manual triage, never discard conflicts automatically' >&2
git status --short
exit 1
fi
test "$(git rev-list --parents -n 1 HEAD | wc -w)" -eq 3
git fetch --force --tags origin
test "$(git rev-parse "$RELEASE_TAG^{commit}")" = "$TAG_COMMIT"
git merge-base --is-ancestor "$VERIFIED_SOURCE_SHA" HEAD
git merge-base --is-ancestor "$MAIN_SHA" HEAD
git merge-base --is-ancestor "$TAG_COMMIT" HEAD
echo "PASS: immutable source and official tag are ancestors of this mainline candidate."

# EXACT verified firmware, Studio and core source: no implementation drift.
- name: Confirm release-closure mainline has no silent firmware or Studio implementation drift
shell: bash
run: |
set -euo pipefail
python3 - <<'PY'
import subprocess
source = 'a922e9bdba4e57a4e88406eb83344455dd8ff142'
paths = ['CMakeLists.txt','apps/arstack_studio/','embedded/esp32p4_smv_injector/','include/','src/','tests/','scripts/','tools/']
changed=subprocess.check_output(['git','diff','--name-only',source,'HEAD','--',*paths],text=True).splitlines()
exceptions={'apps/arstack_studio/README.md','embedded/esp32p4_smv_injector/README.md'}
code=[p for p in changed if p not in exceptions]
if code:
raise SystemExit('ABORT: integrated implementation differs from tested source:\\n'+'\\n'.join(code))
print('PASS: firmware/Studio/core implementation byte-identical to verified M3.H source.')
protected = ('CMakeLists.txt','apps/arstack_studio/','embedded/esp32p4_smv_injector/',
'include/','src/','tests/','scripts/','tools/')
readme_exceptions = {'apps/arstack_studio/README.md','embedded/esp32p4_smv_injector/README.md'}
changed = subprocess.check_output(['git','diff','--name-only',source,'HEAD'],text=True).splitlines()
drift = [p for p in changed if (p == 'CMakeLists.txt' or
any(p.startswith(prefix) for prefix in protected if prefix.endswith('/')))
and p not in readme_exceptions]
if drift:
raise SystemExit('M3.H closure rejected: changes to tested runtime source: '+repr(drift))
print('PASS: firmware, Studio, runtime, tests and build code match shipped tested source.')
PY

# Preserve ALL existing main contributions, not just markdown.
git diff --name-only "$BASE_SHA" "$MAIN_SHA" > /tmp/main-changed.txt
while IFS= read -r path; do
[ -n "$path" ] || continue
main_blob="$(git rev-parse "$MAIN_SHA:$path" 2>/dev/null || true)"
integrated_blob="$(git rev-parse "HEAD:$path" 2>/dev/null || true)"
if [ "$main_blob" != "$integrated_blob" ]; then
echo "ABORT: current main contribution overwritten: $path" >&2
exit 1
fi
done < /tmp/main-changed.txt
echo 'PASS: every file modified on main after fork retains identical Git blob.'

# CI logic modified by M3.H remains exactly identical.
while IFS= read -r path; do
[ -n "$path" ] || continue
src_blob="$(git rev-parse "$VERIFIED_SOURCE_SHA:$path" 2>/dev/null || true)"
merged_blob="$(git rev-parse "HEAD:$path" 2>/dev/null || true)"
if [ "$src_blob" != "$merged_blob" ]; then
echo "ABORT: source-related CI workflow drift: $path" >&2
exit 1
fi
done < <(git diff --name-only "$BASE_SHA" "$VERIFIED_SOURCE_SHA" -- .github/workflows)
echo 'PASS: tested-source CI workflow definitions preserved.'
echo "Candidate integration SHA: $(git rev-parse HEAD)"
git show --no-patch --format='parents=%P%nsubject=%s' HEAD
# Never update main here. Only push a separate immutable-review candidate.
git push origin "HEAD:refs/heads/$INTEGRATION_BRANCH"
echo "Branch candidate pushed: $INTEGRATION_BRANCH"
- name: Query GitHub release metadata and immutable asset identities (read only)
shell: bash
run: |
set -euo pipefail
gh api "repos/$GITHUB_REPOSITORY/releases/tags/$RELEASE_TAG" > release.json
python3 - <<'PY'
import json
from pathlib import Path
release=json.loads(Path('release.json').read_text())
assert release['id']==422544367
assert release['tag_name']=='processbus-sim-m3h-20261024'
assert release['target_commitish']=='a4b6e53d0dbbcababc55ba8a4ff97ade6f2a6200'
assert release['draft'] is False and release['prerelease'] is False
expected={
'ARStack-ProcessBus-M3H-win-x64-setup.exe': (671406892,31059935,'4902e8bf44e78fc55a139ce84a1273f0768ef6fe10000f5d0f85d75d2f6c736e'),
'ARStack-ProcessBus-M3H-win-x64-portable.zip': (671406890,48335087,'ca01424e4102aa65e353737024ebf3258b4d2f614fa60d932c95c353ee228d8f'),
'ARStack-ESP32P4-ProcessBus-M3H-firmware.bin': (671406887,542096,'61c508e9bf1357eb964d2b817b260b6a0e985446ddd68de1d45d9467e25c4607'),
'firmware-manifest.json': (671406893,942,'b2878148a1cda30f11a4c93a200bcba07bb63548aa571de8d05260c2ef630f9a'),
'SHA256SUMS.txt': (671406889,416,'c7674d1a97b77b66adda12216f7d5e460f86da5cb07fd1d1f02bf3011b99c876'),
}
actual={a['name']:(a['id'],a['size'],a['digest'].removeprefix('sha256:')) for a in release['assets']}
assert actual==expected, 'M3.H immutable GitHub assets changed'
print('PASS: Existing Public Stable release, original five asset IDs, sizes and SHA-256 verified.')
PY
34 changes: 18 additions & 16 deletions docs/integration/M3H_MAINLINE_RELEASE_SOURCE_CLOSURE_2026-10-24.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,27 @@

**Do not delete frozen historical branches or alter the published release/tag.** A closed PR does not imply it was independently merged via its button; Git ancestry and GitHub merge metadata are explicitly distinguished.

## Separate OPEN / divergent source PRs — not shipped as M3.H
## Divergent SMV/PTP PR cleanup — CLOSED as historical, not merged (DONE)

Nine remaining SMV/PTP-adjacent PRs are old divergent tracks with unique commits not in verified release HEAD. **Do not auto-merge or label them equivalent.** Unique-commit counts are from head-of-PR vs shipped `a922e9bd...` before integration; refresh before any future targeted port.
All nine legacy source branches were previously **OPEN/DRAFT**. They have now been **CLOSED / NOT MERGED** after exact-head review, branch-vs-main comparison and a per-PR archival comment explaining residual requirements. All original commits and discussions remain accessible. **No experimental firmware code was imported into the accepted M3.H binary or main.** This is transparent archive hygiene, not a claim that untested features work.

| PR | Unique branch commits (relative to shipped M3.H head) | Classification |
| Historical PR | Prior unique commits vs released source | Final disposition / canonical unresolved acceptance |
| --- | ---: | --- |
| [#284](https://github.com/masarray/arstack61850/pull/284) | 17 | Alternate PTP SOURCE fast acquisition / external GM AUTO_FAILOVER; external receiver/relay FAT still unverified |
| [#272](https://github.com/masarray/arstack61850/pull/272) | 7 | Alternative Studio operator fault toast / UX |
| [#266](https://github.com/masarray/arstack61850/pull/266) | 16 | Earlier PTP stale role / clipboard variant |
| [#263](https://github.com/masarray/arstack61850/pull/263) | 23 | Earlier firmware identity / PTP START UX |
| [#259](https://github.com/masarray/arstack61850/pull/259) | 13 | Earlier USB identity / packet capture UX |
| [#256](https://github.com/masarray/arstack61850/pull/256) | 2 | External-GM RECEIVER phase/MAC candidate |
| [#253](https://github.com/masarray/arstack61850/pull/253) | 25 | Earlier running 4I+4V atomic changes; targeted verification required |
| [#239](https://github.com/masarray/arstack61850/pull/239) | 17 | Earlier GUI/wire SSOT variant |
| [#166](https://github.com/masarray/arstack61850/pull/166) | 94 | Broad legacy M1/M2/M3 timing stack, not the accepted on-wire M3.H line |

**Follow-up hygiene verified on 2026-10-24:** All nine divergent SMV/PTP-adjacent PRs above are now **OPEN / DRAFT**, including the eight previously non-Draft PRs #284, #272, #266, #263, #259, #256, #253 and #239. Each of the nine PRs received a canonical reconciliation comment referencing this ledger. These branches retain unique work and are not included in the M3.H shipped firmware baseline; therefore they are deliberately not marked merged or closed without a reviewed feature-by-feature disposition. New contributions must branch from the current `main` and pass targeted gates.

The other eight open PRs concern Workbench, Browser, and IED Simulator lanes; do not close or merge them as part of SMV M3.H source consolidation.
| [#284](https://github.com/masarray/arstack61850/pull/284) | 17 | Archived SOURCE-fast acquisition / AUTO_FAILOVER candidate; physical external-GM/RECEIVER and IED evidence remain #162/#154 |
| [#272](https://github.com/masarray/arstack61850/pull/272) | 7 | Archived alternative operator alerts; missing center-screen PHY/PTP feedback retained in [#300](https://github.com/masarray/arstack61850/issues/300) |
| [#266](https://github.com/masarray/arstack61850/pull/266) | 16 | Archived stale-role recovery/clipboard variant; guarded real board STOP/readback and UX retained #300/#287 |
| [#263](https://github.com/masarray/arstack61850/pull/263) | 23 | Archived earlier firmware identity/PTP START behavior; only genuinely missing current-main behavior retained #300 |
| [#259](https://github.com/masarray/arstack61850/pull/259) | 13 | Archived old USB-flash/capture recovery variant (older firmware); targeted missing identity/telemetry UX retained #300 |
| [#256](https://github.com/masarray/arstack61850/pull/256) | 2 | Archived unverified external-GM RECEIVER MAC/phase qualification; acceptance retained #162/#154 |
| [#253](https://github.com/masarray/arstack61850/pull/253) | 25 | Archived atomic live 4I+4V transaction implementation; acceptance retained [#42](https://github.com/masarray/arstack61850/issues/42) |
| [#239](https://github.com/masarray/arstack61850/pull/239) | 17 | Archived old Studio GUI/wire SSOT alternative; missing UX and partial-update concerns retained #300/#42 |
| [#166](https://github.com/masarray/arstack61850/pull/166) | 94 | Archived broad earlier M1/M2/M3 experimental tree; physical RECEIVER/PTP scope retained #162/#154 and extended timing #21 |

Comparison counts are historical (against shipped `a922e9bd...`), not a current merge suitability statement. Each branch also diverges from current `main`; do not resurrect a whole stacked patchset. Source modifications should be separately re-implemented or selectively ported from fresh main with explicit negative/positive regression tests.

**Current PR queue:** exactly **8 unrelated Workbench, Browser, IED Simulator PRs** remain open. They belong to different product tracks and were not altered by M3.H SMV cleanup.

**Safety CI:** the old automatic write-capable M3.H source-reconciliation workflow must be retired after merge. A read-only provenance guard checks source/tag ancestry, runtime parity and all five immutable release asset digests; it must never create a merge branch or publish binaries.

## Issues kept OPEN on purpose — different acceptance targets

Expand Down
Loading