Skip to content

fix: validate Gateway upgrades during release dispatch - #178

Merged
RomneyDa merged 3 commits into
mainfrom
fix/dispatch-msix-upgrade-proof
Oct 20, 2026
Merged

RomneyDa merged 3 commits into
mainfrom
fix/dispatch-msix-upgrade-proof

Conversation

@RomneyDa

@RomneyDa RomneyDa commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

What Problem This Solves

Fixes: release dispatches can authorize a Gateway MSIX without installed-upgrade proof for the candidate being shipped.

User Impact

Release owners can run a credential-free test preflight, and store/official dispatches must pass their own installed-upgrade matrix before authorization. The packaging revision is reset to 0 for the first package of the new upstream stable version; current stable 2026.10.5 derives v2026.10.5-msix.0 and package version 2026.10.500.0.

No installed launcher behavior, package identity, publisher, runtime pin, or immutable upgrade baseline changes. A candidate that cannot upgrade the Store-installed version intentionally stops before release authorization.

Why This Change Was Made

Manual test, store, and official candidates use the existing immutable source owner and complete upgrade matrix. Test mode captures the same stable release identity constraints without gaining publication or Azure credentials. Push and unsigned dispatches retain development versions and skip installed upgrades.

The October 20 update integrates main 403e5ba393857b653e5427bbe9fb80e88d0f5cf1 and resolves all three conflicts. It preserves main's reusable trusted/read-only builds, early source-admission checks, cache capability boundaries, and selected-build validation. The dispatch aggregate requirement now extends Assert-WorkflowJobResults.ps1, rather than restoring the retired inline gate or source-string assertions. Release authorization directly depends on successful upgrade results.

Evidence

Current signed head: 801e886a54805f56e3a1554f1a0b275450ccbbac. Independent Codex Auto Review completed against the full candidate relative to main 403e5ba, with no actionable P0-P2 findings.

Passed locally in PowerShell 7:

  • scripts/Test-WorkflowSigningConfiguration.ps1: current selected-build contracts and five actual workflow-classifier scenarios; 0.57 s wall time.
  • scripts/Test-WorkflowJobResults.Tests.ps1: existing PR gates plus successful candidate dispatches and rejection of skipped, failed, or cancelled upgrades for all three candidate modes, even with a false classification flag; 0.39 s.
  • scripts/Test-OpenClawSource.Tests.ps1: 75 cases, including test-mode identity, source overrides, offline retry replay, and retained early admission; 1.38 s.
  • scripts/Test-SigningInputs.Tests.ps1.
  • scripts/Test-MSIXReleaseIdentity.Tests.ps1 and direct derivation from the updated policy.
  • scripts/Test-WorkflowPackageVersion.Tests.ps1.
  • scripts/Test-MSIXUpgradeMatrix.Tests.ps1.
  • scripts/Test-PackagingRelevance.Tests.ps1.
  • scripts/Test-DocReferences.ps1: zero findings; git diff --check; all ten changed files retain LF.

On macOS, the signing and packaging-relevance fixtures initially stopped before their cases because TEMP was unset. Their unchanged suites passed with process-local TEMP set to .NET's temporary directory. No product compatibility path or test assertion was changed for that prerequisite.

Exact-head PR CI is in progress. Windows, NativeAOT, package composition, and all 13 installed-upgrade scenarios are not covered by the local checks; no current-head installed-upgrade success is claimed. GitHub reports the updated head conflict-free (MERGEABLE), not merge-ready while CI remains unfinished. Per-suite CI seconds are not yet available.

Historical evidence, not current-head qualification

The prior manual test dispatch passed both architectures, bundles, all 13 installed transitions, and the final gate for OpenClaw 2026.10.4 at 8b58e12. Retained transition details include the authentic Store in-place update and LocalState retention. Credential-free production-DAG evidence covers the earlier workflow's allowed and denied authorization paths. Those receipts do not qualify the new assembled head or the new upstream version.

No merge, manual dispatch, official signing, or release publication is part of this update. Real installed recovery/sign-in and MXC-session retention proof remain separate outstanding release work; package-transition CI does not prove those flows.

@clawsweeper

clawsweeper Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@clawsweeper clawsweeper Bot added P2 Normal priority bug or improvement with limited blast radius. merge-risk: 🚨 automation 🚨 Merging this PR could break CI, automerge, proof capture, label sync, or automation. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Oct 16, 2026
@clawsweeper

clawsweeper Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codex review: needs maintainer review before merge.

What this changes

This PR runs installed-upgrade validation for release-candidate dispatches and requires success before release authorization.

Example: Dispatch test mode for OpenClaw 2026.10.4.

  • Before: The dispatch uses a development package version and skips installed-upgrade validation.
  • After: The dispatch builds candidate 2026.10.400.0 and exercises all 13 installed transitions without release authorization.

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) A focused, correct repair with real dispatch and installed-upgrade proof plus isolated authorization-boundary execution; live release effects are intentionally substituted at the external boundary.
Proof confidence 🐚 platinum hermit (4/6) Sufficient (live_output): The completed real workflow dispatch exercises candidate composition and all 13 installed transitions, with release effects skipped on the non-main test path; the full credential-free production-DAG reproducer and observed output cover allowed main official/store candidates and denied failed/skipped upgrades and non-release principals. Both previous rank-up requests are addressed, and the current head preserves all exercised behavior. Live Azure signing and official/store publication were intentionally unrun; artifact bytes could not be independently downloaded through this host's allowlist.
Patch quality 🦞 diamond lobster (5/6) No actionable review findings were identified.

Product

Kind: Bug fix · Worth it: Yes · Fix scope: Complete
User problem: Release owners cannot obtain same-candidate installed-upgrade validation through release dispatch, so advancing stable requires a validation-only pull request.
Reason: The maintainer-authored direction removes that operational detour and requires candidate-specific proof before release authorization.

Merge readiness

✅ Ready for maintainer review

This maintainer-authored PR fixes a release-validation gap still present on main. No actionable patch defects remain; current-head required CI is still running.

Priority: P2
Reviewed head: 76f86aa00b097f75d507ff504c09119cb4fa5614

Before merge

None.

Findings

None.

Agent review details

How this fits together

The packaging workflow turns an immutable upstream source selection into architecture packages and bundles, validates installed upgrades, and gates release authorization, signing, and publication.

flowchart LR
  A[Manual dispatch] --> B[Immutable source selection]
  B --> C[Architecture packages and bundles]
  C --> D[Installed upgrade matrix]
  D --> E[Release authorization]
  E --> F[Signing and publication]
  E --> G[Authorized Store retention]
Loading

Technical review

Best possible solution:

Land the existing-owner dispatch gate after current-head required CI succeeds, retaining same-run upgrade evidence with each release.

Do we have a high-confidence way to reproduce the issue?

Main's event conditions directly show that manual releases skip installed upgrades, and the supplied old/new DAG execution demonstrates authorization after failed upgrade proof on the original workflow.

Is this the best way to solve the issue?

The change addresses the orchestration owner directly, reuses immutable source selection and the existing transition matrix, and adds focused behavior tests without creating another release-policy owner.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against 19d7cc0aab65.

Provenance checked

  • Dispatch classification and aggregate gate changes intended behavior with a stated reason (306df11: Skip unnecessary PR packaging while retaining an aggregate workflow gate; later versioning logic left non-PR events outside upgrade validation.)
  • Installed-upgrade matrix keeps the original intent (7d7acad: Parallelize the existing transition scenarios while preserving immutable baselines and evidence.)
  • Test-mode source selection changes intended behavior with a stated reason (54e718d: Store and official modes use release source constraints; the later override contract preserves stable release identity separately from payload selection.)
  • Release source and identity documentation keeps the original intent (b678f5d: Permit explicit upstream refs while keeping stable policy as the release-identity owner and replaying immutable selections.)

Testing

Proof path: shipped entry point.

Security

None.

Evidence

What I checked:

Likely related people:

  • Dallin Romney: Raw commit 7d7acad adds .github/workflows/gateway-msix.yml:822 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: high; commits: 7d7acad6db64; files: .github/workflows/gateway-msix.yml)
  • Paul Campbell: Raw commit 306df11 adds .github/workflows/gateway-msix.yml:58 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: high; commits: 306df11af13a; files: .github/workflows/gateway-msix.yml)
  • Linus Huang: Raw commit 9f18180 adds .github/workflows/gateway-msix.yml:257 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: medium; commits: 9f18180d27ba; files: .github/workflows/gateway-msix.yml)

Review metrics

Metric Value Why it matters
Change distribution Workflow/source +37/-10; tests +181/-2; docs +46/-27 Most growth exercises the actual classifier and aggregate scripts across event and result scenarios; production changes reuse the existing matrix and source owner.

Labels

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🦞 diamond lobster.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR.
  • remove rating: 🦪 silver shellfish: Current PR rating is rating: 🐚 platinum hermit, so this older rating label is no longer current.
  • remove status: 📣 needs proof: Current PR status label is status: 👀 ready for maintainer look.

Label justifications:

  • P2: Closes a release-validation gap with limited direct runtime impact.
  • merge-risk: 🚨 automation: Changes release eligibility so failed or skipped upgrades stop authorization; the maintainer explicitly accepts that stop condition and supplies success/denial evidence.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🦞 diamond lobster.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR.
  • proof: sufficient: Contributor real behavior proof is sufficient.

Rating scale

6/6 🦀 challenger crab · 5/6 🦞 diamond lobster · 4/6 🐚 platinum hermit · 3/6 🦐 gold shrimp · 2/6 🦪 silver shellfish · 1/6 🧂 unranked krab. Overall follows the weaker of proof and patch quality; ✨ marks media proof (a screenshot, video, or linked artifact) that directly shows the changed behavior.

Workflow

ClawSweeper edits this one comment on every review. Comment @clawsweeper re-review for a fresh review only. Repair and merge execution lanes are retired.

History

Review history (1 earlier review cycle)
  • reviewed 2026-10-16T03:00:15.478Z sha 8b58e12 :: needs real behavior proof before merge. :: none

Reviewed October 15, 2026, 11:42 PM ET / October 16, 2026, 03:42 UTC (Revision 2).

@RomneyDa

RomneyDa commented Oct 16, 2026 •

Copy link
Copy Markdown
Member Author

ClawSweeper authority-boundary proof

The 13 credential-free release-boundary scenarios below passed against the production workflow at 8b58e12a885bd680ed2d53f9f3810895ba927211. The current head, 76f86aa00b097f75d507ff504c09119cb4fa5614, only corrects README wording: git diff --exit-code 8b58e12 HEAD -- .github/workflows/gateway-msix.yml scripts src tests passed. The workflow SHA-256 at both heads is 28532789c3e290314e33b0d38f1a533351bc4c1edd5fc21ed702547dabae9509.

This is executed emulator proof, not a live official/store dispatch or a claim that Azure was contacted. A disposable projection reads the real production job if and needs values, including the new upgrade prerequisite. It runs the real rejection and aggregate-gate scripts unchanged. Synthetic upstream jobs supply successful, failed, or skipped upgrade results; external work is replaced with local marker writes. No uses, release environment, OIDC permission, GitHub/Azure token, signing call, Store upload, or publication action is present in the projection. nektos/act v0.2.89 executes the job DAG; assertions inspect the resulting marker files and exit codes.

Here, signing means that the job containing Azure login was reachable, not that signing occurred. Store means the authorized-retention job was reachable, not an upload. The success-path marker substitutes for release-policy validation; that validator's real fixture suite is separately passing.

Candidate/event Upgrade fixture Authorization Signing Publication Store Final gate
main / official success reached reached reached reached pass
main / store success reached blocked blocked reached pass
main / official failure blocked blocked blocked blocked fail
main / official skipped blocked blocked blocked blocked fail
main / store failure blocked blocked blocked blocked fail
main / store skipped blocked blocked blocked blocked fail
non-main / official success blocked blocked blocked blocked fail
non-main / store success blocked blocked blocked blocked fail
main / test success blocked blocked blocked blocked pass
non-main / test success blocked blocked blocked blocked pass
main / unsigned not selected blocked blocked blocked blocked pass
pull request success blocked blocked blocked blocked pass
push / main not selected blocked blocked blocked blocked pass

Observed output:

main-official-success: passed
main-store-success: passed
main-official-failed-upgrade: passed
main-official-skipped-upgrade: passed
main-store-failed-upgrade: passed
main-store-skipped-upgrade: passed
branch-official-success: passed
branch-store-success: passed
main-test-success: passed
branch-test-success: passed
main-unsigned: passed
pull-request: passed
push-main: passed
All 13 production-DAG boundary scenarios passed.

The real manual test dispatch completed successfully: both architecture packages, both bundles, temporary test signing, all 13 actual installed transitions, and the aggregate gate passed. Every emitted transition JSON was downloaded and verified against candidate 2026.10.400.0, the matrix's baseline/delivery/mode, the policy package family, successful Windows status, and expected LocalState behavior. The authentic Store-installed baseline was 2026.9.900.0; its in-place update retained LocalState. Authorization, official signing, authorized Store retention, and publication all remained skipped on this real non-main test dispatch. The captured source is OpenClaw 2026.10.4 at 383c124cc32ffee3f8e56f549b88da94d269a4af, signing mode test, with publish_release=false. The new-head CI run is recovering an ARM64 .NET-installer CLR crash on a fresh hosted runner (attempt 2); its host and x64 lanes already passed. Live main official/store execution is intentionally not claimed before merge.

Regression confirmed: running the same isolated proof against the original 19d7cc0 workflow fails its denial assertion: main-official-failed-upgrade: Authorized expected=False actual=True. That workflow authorizes the main candidate without successful upgrade proof. The changed production workflow passes every success and denial assertion. This is execution of the old/new DAG, not a source-text assertion.

Reproducer used for the credential-free proof

The temporary proof harness below is evidence only; no production policy or test-only release bypass was added. Build the emulator into a disposable directory with go install github.com/nektos/act@v0.2.89 and set ActPath to that binary. Run this PowerShell 7 harness from the repository root with a fresh output directory. It refuses to overwrite an existing scenario and writes every projection, event fixture, emulator log, and report.json there. The local run had no user .actrc file.

[CmdletBinding()]
param(
    [string]$RepositoryRoot = (Get-Location).Path,
    [string]$WorkflowPath = '',
    [string]$ActPath = '/tmp/msix-authorization-proof-tools/act',
    [string]$OutputDirectory = '/tmp/msix-release-authorization-proof'
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

if ($WorkflowPath -eq '') {
    $WorkflowPath = Join-Path $RepositoryRoot '.github/workflows/gateway-msix.yml'
}
$workflowPath = $WorkflowPath
$workflow = [IO.File]::ReadAllText($workflowPath)
$workflowHash = (Get-FileHash -LiteralPath $workflowPath -Algorithm SHA256).Hash.ToLowerInvariant()
$commit = & git -C $RepositoryRoot rev-parse HEAD
if ($LASTEXITCODE -ne 0) { throw 'Cannot resolve proof commit.' }

# This is execution evidence, not a policy implementation. Copy the production
# DAG and its conditions unchanged; replace external work at the isolation
# boundary. No production step that can request credentials is copied.
$jobs = [ordered]@{}
$jobText = [regex]::Match($workflow, '(?ms)^jobs:\r?\n(?<jobs>.*)\z').Groups['jobs'].Value
foreach ($match in [regex]::Matches(
    $jobText,
    '(?ms)^  (?<id>[a-z][a-z0-9-]+):\r?\n(?<body>.*?)(?=^  [a-z][a-z0-9-]+:|\z)'
)) {
    $jobs[$match.Groups['id'].Value] = $match.Groups['body'].Value
}
if ($jobs.Count -ne 14) { throw "Unexpected workflow job count: $($jobs.Count)." }
$trigger = [regex]::Match($workflow, '(?ms)^on:\r?\n.*?(?=^concurrency:)').Value
if ($trigger -eq '') { throw 'Cannot locate workflow triggers.' }
$encoding = [Text.UTF8Encoding]::new($false)
$null = [IO.Directory]::CreateDirectory($OutputDirectory)

$scenarios = @(
    @{ Name='main-official-success'; Mode='official'; Ref='main'; Upgrade='success'; Authorized=$true; Signed=$true; Published=$true; Store=$true; Gate=$true },
    @{ Name='main-store-success'; Mode='store'; Ref='main'; Upgrade='success'; Authorized=$true; Store=$true; Gate=$true },
    @{ Name='main-official-failed-upgrade'; Mode='official'; Ref='main'; Upgrade='failure' },
    @{ Name='main-official-skipped-upgrade'; Mode='official'; Ref='main'; Upgrade='skipped' },
    @{ Name='main-store-failed-upgrade'; Mode='store'; Ref='main'; Upgrade='failure' },
    @{ Name='main-store-skipped-upgrade'; Mode='store'; Ref='main'; Upgrade='skipped' },
    @{ Name='branch-official-success'; Mode='official'; Ref='proof-branch'; Upgrade='success' },
    @{ Name='branch-store-success'; Mode='store'; Ref='proof-branch'; Upgrade='success' },
    @{ Name='main-test-success'; Mode='test'; Ref='main'; Upgrade='success'; Gate=$true },
    @{ Name='branch-test-success'; Mode='test'; Ref='proof-branch'; Upgrade='success'; Gate=$true },
    @{ Name='main-unsigned'; Mode='unsigned'; Ref='main'; Upgrade='success'; Versioning='false'; Gate=$true },
    @{ Name='pull-request'; Mode='official'; Ref='main'; Event='pull_request'; Upgrade='success'; Gate=$true },
    @{ Name='push-main'; Mode='official'; Ref='main'; Event='push'; Upgrade='success'; Versioning='false'; Gate=$true }
)

$results = [Collections.Generic.List[object]]::new()
foreach ($scenario in $scenarios) {
    $caseRoot = Join-Path $OutputDirectory $scenario.Name
    if (Test-Path -LiteralPath $caseRoot) { throw "Proof output exists: $caseRoot" }
    $markers = Join-Path $caseRoot 'markers'
    $null = [IO.Directory]::CreateDirectory($markers)
    & git -C $caseRoot init --quiet
    if ($LASTEXITCODE -ne 0) { throw 'Cannot initialize isolated workflow fixture.' }
    & git -C $caseRoot remote add origin https://github.com/fixture/proof.git
    if ($LASTEXITCODE -ne 0) { throw 'Cannot record synthetic fixture origin.' }
    $eventName = if ($scenario.ContainsKey('Event')) { $scenario.Event } else { 'workflow_dispatch' }
    $versioning = if ($scenario.ContainsKey('Versioning')) { $scenario.Versioning } else { 'true' }
    $lines = [Collections.Generic.List[string]]::new()
    $lines.Add('name: Credential-free production authorization DAG projection')
    $lines.Add($trigger.TrimEnd())
    $lines.Add('permissions: {}')
    $lines.Add('jobs:')
    foreach ($id in $jobs.Keys) {
        $body = $jobs[$id]
        if ($id -eq 'ci-gate') {
            # The real aggregate scripts are safe and provide the denied-case
            # observable result, including required-but-skipped upgrade proof.
            $lines.Add("  ${id}:")
            $lines.Add($body.TrimEnd())
            $marker = (Join-Path $markers $id).Replace("'", "''")
            $lines.Add('      - name: Record successful gate')
            $lines.Add('        shell: pwsh')
            $lines.Add("        run: '[IO.File]::WriteAllText(''$marker'', ''reached'')'")
            continue
        }
        $lines.Add("  ${id}:")
        $condition = [regex]::Match($body, '(?m)^    if: (?<expression>.*)$')
        if ($condition.Success) {
            $expression = $condition.Groups['expression'].Value.TrimEnd()
            if ($id -eq 'test-msix-upgrades' -and $scenario.Upgrade -eq 'skipped') {
                # Inject a skipped upstream fixture, not a changed authority gate.
                $expression = '${{ false }}'
            }
            $lines.Add("    if: $expression")
        }
        $needs = [regex]::Match($body, '(?m)^    needs:\r?\n(?:^      - [^\r\n]+\r?\n)+')
        if ($needs.Success) { $lines.Add($needs.Value.TrimEnd()) }
        else {
            $needs = [regex]::Match($body, '(?m)^    needs: [^\r\n]+')
            if ($needs.Success) { $lines.Add($needs.Value) }
        }
        $lines.Add('    runs-on: ubuntu-latest')
        if ($id -eq 'changes') {
            $lines.Add('    outputs:')
            foreach ($output in @('packaging', 'payload_artifact', 'bundle_build')) {
                $lines.Add("      ${output}: 'true'")
            }
            $lines.Add("      versioning: '$versioning'")
        }
        $lines.Add('    steps:')
        if ($id -eq 'reject-untrusted-official-signing') {
            $steps = [regex]::Match($body, '(?ms)^    steps:\r?\n(?<steps>.*)$').Groups['steps'].Value
            $lines.RemoveAt($lines.Count - 1)
            $lines.Add('    steps:')
            $lines.Add($steps.TrimEnd())
            continue
        }
        $marker = (Join-Path $markers $id).Replace("'", "''")
        $lines.Add('      - name: Record isolated job effect (no external actions)')
        $lines.Add('        shell: pwsh')
        $lines.Add('        run: |')
        $lines.Add("          [IO.File]::WriteAllText('$marker', 'reached')")
        if ($id -eq 'test-msix-upgrades' -and $scenario.Upgrade -eq 'failure') {
            $lines.Add('          exit 1')
        }
    }
    $projection = Join-Path $caseRoot 'proof.yml'
    $projectionText = ($lines -join "`n") + "`n"
    foreach ($forbidden in @('uses:', 'id-token:', 'environment:', 'AZURE_', 'secrets.')) {
        if ($projectionText.Contains($forbidden, [StringComparison]::Ordinal)) {
            throw "Unsafe projected execution contains $forbidden"
        }
    }
    [IO.File]::WriteAllText($projection, $projectionText, $encoding)
    $eventFile = Join-Path $caseRoot 'event.json'
    $eventData = @{
        ref = "refs/heads/$($scenario.Ref)"
        after = $commit.Trim()
        deleted = $false
        inputs = @{ signing_mode = $scenario.Mode; publish_release = 'true' }
        number = 1
        pull_request = @{ head = @{ ref = 'proof-branch' }; base = @{ ref = 'main' } }
        repository = @{ default_branch = 'main'; full_name = 'fixture/proof' }
    }
    [IO.File]::WriteAllText($eventFile, ($eventData | ConvertTo-Json -Depth 10), $encoding)
    $empty = Join-Path $caseRoot 'empty'
    [IO.File]::WriteAllText($empty, '', $encoding)
    $processInfo = [Diagnostics.ProcessStartInfo]::new($ActPath)
    $processInfo.WorkingDirectory = $caseRoot
    $processInfo.UseShellExecute = $false
    $processInfo.RedirectStandardOutput = $true
    $processInfo.RedirectStandardError = $true
    # Keep only OS execution prerequisites. No GitHub/Azure token or agent
    # environment reaches the emulator or its marker-only child processes.
    $processInfo.Environment.Clear()
    foreach ($name in @(
        'PATH', 'HOME', 'TMPDIR', 'TEMP', 'TMP', 'SystemRoot', 'COMSPEC',
        'DOTNET_ROOT', 'DOTNET_ROOT_ARM64', 'DOTNET_ROOT_X64', 'DOTNET_ROLL_FORWARD'
    )) {
        $value = [Environment]::GetEnvironmentVariable($name)
        if ($null -ne $value) { $processInfo.Environment[$name] = $value }
    }
    foreach ($argument in @(
        $eventName, '-W', $projection, '-e', $eventFile,
        '-P', 'ubuntu-latest=-self-hosted', '-P', 'windows-latest=-self-hosted',
        '--secret-file', $empty, '--var-file', $empty, '--env-file', $empty,
        '--input-file', $empty, '--json', '--concurrent-jobs', '1',
        '--container-architecture', 'linux/arm64', '--action-offline-mode',
        '--action-cache-path', (Join-Path $caseRoot 'action-cache'), '--no-cache-server'
    )) { $processInfo.ArgumentList.Add($argument) }
    $process = [Diagnostics.Process]::Start($processInfo)
    $stdout = $process.StandardOutput.ReadToEndAsync()
    $stderr = $process.StandardError.ReadToEndAsync()
    $process.WaitForExit()
    $log = $stdout.GetAwaiter().GetResult() + $stderr.GetAwaiter().GetResult()
    [IO.File]::WriteAllText((Join-Path $caseRoot 'act.log'), $log, $encoding)
    $effects = [ordered]@{
        Authorized = 'authorize-signing'
        Signed = 'sign-msix'
        Published = 'publish-release'
        Store = 'retain-store-submission'
        Gate = 'ci-gate'
    }
    $record = [ordered]@{ name = $scenario.Name; exitCode = $process.ExitCode }
    foreach ($effect in $effects.Keys) {
        $expected = $scenario.ContainsKey($effect) -and $scenario[$effect]
        $actual = Test-Path -LiteralPath (Join-Path $markers $effects[$effect])
        $record[$effect] = $actual
        if ($actual -ne $expected) {
            throw "$($scenario.Name): $effect expected=$expected actual=$actual. Inspect $caseRoot/act.log"
        }
    }
    $expectedExit = if ($scenario.ContainsKey('Gate') -and $scenario.Gate) { 0 } else { 1 }
    if ($process.ExitCode -ne $expectedExit) {
        throw "$($scenario.Name): expected exit $expectedExit, got $($process.ExitCode). Inspect $caseRoot/act.log"
    }
    $results.Add([pscustomobject]$record)
    Write-Host "$($scenario.Name): passed"
}
$report = [ordered]@{
    schemaVersion = 1
    evidenceKind = 'credential-free act execution of production if/needs with isolated marker effects'
    packagingCommit = $commit.Trim()
    workflowSha256 = $workflowHash
    emulator = 'nektos/act v0.2.89'
    scenarios = $results
}
[IO.File]::WriteAllText((Join-Path $OutputDirectory 'report.json'), ($report | ConvertTo-Json -Depth 10) + "`n", $encoding)
Write-Host "All $($results.Count) production-DAG boundary scenarios passed. Evidence: $OutputDirectory"

@RomneyDa

Copy link
Copy Markdown
Member Author

Completed candidate and installed-transition proof

Head: 76f86aa00b097f75d507ff504c09119cb4fa5614. The README-only follow-up leaves all workflow, scripts, source, and tests byte-identical to the executed head 8b58e12a885bd680ed2d53f9f3810895ba927211; the PR body records the exact diff/hash verification.

Manual release-candidate test dispatch 38841707866 completed successfully, including managed quality/Release tests, NativeAOT execution, every owning PowerShell suite, both architecture packages, both identity-channel bundles, temporary test signing, all 13 installed transitions, and the final aggregate gate.

The immutable source snapshot selects OpenClaw 2026.10.4 at 383c124cc32ffee3f8e56f549b88da94d269a4af, with the captured stable release identity at the same commit; mode test, publish_release=false. The candidate is 2026.10.400.0 in every JSON record. I downloaded and checked all 13 JSONs against the production matrix: exact baseline/delivery/mode, policy family, strictly newer candidate, Windows status Ok, and expected package-family/LocalState retention or isolation. The authentic Store baseline installed 2026.9.900.0 from product 9NV70LV3D6XC and retained its state through the update.

Scenario Baseline Candidate LocalState Native JSON artifact
reset-0-standalone 0.0.0.0 2026.10.400.0 isolated JSON
sideload-0-standalone 0.0.0.0 2026.10.400.0 retained JSON
reset-1-standalone 0.0.0.1 2026.10.400.0 isolated JSON
sideload-1-standalone 0.0.0.1 2026.10.400.0 retained JSON
reset-2-bundle 0.0.0.0 2026.10.400.0 isolated JSON
sideload-2-bundle 0.0.0.0 2026.10.400.0 retained JSON
reset-3-bundle 0.0.0.1 2026.10.400.0 isolated JSON
sideload-3-bundle 0.0.0.1 2026.10.400.0 retained JSON
reset-4-standalone 2026.9.800.0 2026.10.400.0 isolated JSON
sideload-4-standalone 2026.9.800.0 2026.10.400.0 retained JSON
reset-5-bundle 2026.9.800.0 2026.10.400.0 isolated JSON
sideload-5-bundle 2026.9.800.0 2026.10.400.0 retained JSON
store-bundle 2026.9.900.0 2026.10.400.0 retained JSON

The same real non-main test dispatch left authorize-signing, sign-msix, retain-store-submission, and publish-release skipped. The separate credential-free production-DAG execution proves successful main official/store reachability and denied failed/skipped/non-main/test paths using local marker doubles, with the complete reproducer, 13 observed passes, and a demonstrated failure on the original workflow. It does not claim live Azure access, signing, publication, or actual main official/store dispatch.

Current-head PR CI 38845312038 is recovering on attempt 2 from a hosted ARM64 actions/setup-dotnet@v6 crash (Internal CLR error 0x80131506, before repository restore/compilation). Its Windows host and x64 lanes passed; the same ARM64 setup and composition passed in the completed manual dispatch. The failed jobs were rerun on a fresh hosted runner without source/test/timeout changes. The PR is not claimed merge-ready until current-head CI and review settle.

The PR body is updated with the completed evidence, exact head, infrastructure failure/recovery, and live release lanes intentionally not run. No merge or release is requested here.

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

🦞👀
Exact review queued.

Re-review progress:

@clawsweeper clawsweeper Bot added proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Oct 16, 2026
@RomneyDa
RomneyDa merged commit 2062d05 into main Oct 20, 2026
37 checks passed
@RomneyDa
RomneyDa deleted the fix/dispatch-msix-upgrade-proof branch October 20, 2026 23:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 automation 🚨 Merging this PR could break CI, automerge, proof capture, label sync, or automation. P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant