diff --git a/.github/workflows/m3h-source-main-reconciliation.yml b/.github/workflows/m3h-source-main-reconciliation.yml index df0f13f49..fc099b5b3 100644 --- a/.github/workflows/m3h-source-main-reconciliation.yml +++ b/.github/workflows/m3h-source-main-reconciliation.yml @@ -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 diff --git a/docs/integration/M3H_MAINLINE_RELEASE_SOURCE_CLOSURE_2026-10-24.md b/docs/integration/M3H_MAINLINE_RELEASE_SOURCE_CLOSURE_2026-10-24.md index a0d058584..a3394d210 100644 --- a/docs/integration/M3H_MAINLINE_RELEASE_SOURCE_CLOSURE_2026-10-24.md +++ b/docs/integration/M3H_MAINLINE_RELEASE_SOURCE_CLOSURE_2026-10-24.md @@ -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