Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
187 commits
Select commit Hold shift + click to select a range
cf27ee5
docs: plan v2.5.22 runtime reliability
naipi11 Aug 27, 2026
30cf6dc
fix: bind setup activation to immutable payload
naipi11 Aug 27, 2026
aec0eeb
fix: harden immutable setup payload verification
naipi11 Aug 27, 2026
b9e7465
docs: refine stable rebind acceptance
naipi11 Aug 27, 2026
493245e
fix: close setup payload reparse write windows
naipi11 Aug 27, 2026
6dcb725
fix: inventory all setup destination directories
naipi11 Aug 27, 2026
8611e73
fix: require one Inno Files section
naipi11 Aug 27, 2026
8ac6106
fix: enforce Inno inventory include boundary
naipi11 Aug 27, 2026
c81987e
docs: replace Inno include parsing
naipi11 Aug 27, 2026
ec4194f
docs: number include boundary task
naipi11 Aug 27, 2026
31323b5
fix: compile include-free setup inventory
naipi11 Aug 27, 2026
67b9939
docs: constrain setup preprocessor boundary
naipi11 Aug 27, 2026
d174711
fix: reject unsafe Inno preprocessor directives
naipi11 Aug 27, 2026
05d3646
fix: enforce exact Inno preprocessor constructs
naipi11 Aug 27, 2026
b34b0d1
fix: stabilize verified Electron process trees
naipi11 Aug 27, 2026
fe34fdf
docs: record Task 3 verification
naipi11 Aug 27, 2026
b312174
test: cover tray action revision correlation
naipi11 Aug 27, 2026
6554d28
docs: record Task 4 verification
naipi11 Aug 27, 2026
94ca92f
fix: gate tray feedback on durable diagnostics
naipi11 Aug 27, 2026
1c8a645
docs: record Task 4 fix round 1
naipi11 Aug 27, 2026
3bdd575
fix: release tray actions after diagnostic failure
naipi11 Aug 27, 2026
424f9c8
docs: record Task 4 fix round 2
naipi11 Aug 27, 2026
9c1e4df
fix: harden tray terminal diagnostics
naipi11 Aug 27, 2026
4aad2a7
docs: record Task 4 fix round 3
naipi11 Aug 27, 2026
a7292f6
docs: plan async tray receipt reliability
naipi11 Aug 28, 2026
365fde2
fix: pin tray terminal receipt storage
naipi11 Aug 28, 2026
592ef5a
docs: record async tray receipt Task 1
naipi11 Aug 28, 2026
debe093
docs: refine tray receipt compatibility boundary
naipi11 Aug 28, 2026
f207496
fix: preserve tray receipt parent compatibility
naipi11 Aug 28, 2026
6cdbcdb
docs: record tray receipt compatibility fix
naipi11 Aug 28, 2026
7549dde
fix: isolate tray terminal receipt writes
naipi11 Aug 28, 2026
1f1ecc5
fix: hide provisional tray receipt UI
naipi11 Aug 28, 2026
1212579
fix: drop failed tray receipt reposts
naipi11 Aug 28, 2026
3315c33
docs: record async tray receipt Task 2
naipi11 Aug 28, 2026
2f028bb
docs: clarify async tray receipt callback boundary
naipi11 Aug 28, 2026
8588289
fix: seal tray receipt dispatch races
naipi11 Aug 28, 2026
924a97a
docs: record tray receipt race fix
naipi11 Aug 28, 2026
31a74c6
test: trace authenticated tray terminal receipts
naipi11 Aug 28, 2026
939f2fa
docs: record authenticated tray receipt trace
naipi11 Aug 28, 2026
efd2f90
fix: require tray child natural exit after ack
naipi11 Aug 28, 2026
1743de6
test: gate release on tray receipt trace
naipi11 Aug 28, 2026
2e0b49a
docs: record release trace gate validation
naipi11 Aug 28, 2026
f0e388e
fix: reject bypassed tray trace gates
naipi11 Aug 28, 2026
1d6bd1f
docs: record tray trace gate bypass fix
naipi11 Aug 28, 2026
49f88e2
release: prepare CodexRemote-fix v2.5.22
naipi11 Aug 28, 2026
9799d4f
docs: record v2.5.22 release validation
naipi11 Aug 28, 2026
6204a68
fix: verify portable release artifacts
naipi11 Aug 28, 2026
f437d50
test: normalize release notes fixture line endings
naipi11 Aug 28, 2026
31fd365
test: update v2.5.22 release branding contract
naipi11 Aug 28, 2026
cb36d0b
test: expose activation payload byte swap
naipi11 Aug 28, 2026
ed782ec
fix: seal activation bytes and reject linked leaves
naipi11 Aug 28, 2026
ca5a5f7
fix: bind setup artifact provenance
naipi11 Aug 28, 2026
1f4259f
fix: validate setup provenance across hosts
naipi11 Aug 28, 2026
5293182
docs: record final security fix evidence
naipi11 Aug 28, 2026
f1ee9b7
test: expose linked whole-install deletion
naipi11 Aug 28, 2026
1a4abec
fix: preflight whole-install tree deletion
naipi11 Aug 28, 2026
b145108
docs: record whole-install delete fix
naipi11 Aug 28, 2026
2633509
test: expose activation stage substitution
naipi11 Aug 28, 2026
39e838f
fix: pin activation stage write boundary
naipi11 Aug 28, 2026
7bdd78c
test: expose setup write TOCTOU and provenance drift
naipi11 Aug 28, 2026
f5021b0
fix: pin setup writes and verify provenance inputs
naipi11 Aug 28, 2026
8102bde
docs: record final TOCTOU remediation
naipi11 Aug 28, 2026
144fc23
docs: define transactional install release design
naipi11 Aug 28, 2026
0ecf0a5
docs: plan transactional install release
naipi11 Aug 29, 2026
dc52697
feat: add pinned install file transaction
naipi11 Aug 29, 2026
a431dfb
fix: harden pinned install transaction capabilities
naipi11 Aug 29, 2026
6c8a70a
fix: make owned tree cleanup all-or-nothing
naipi11 Aug 29, 2026
713fb0b
docs: retire nonempty transaction trees safely
naipi11 Aug 29, 2026
0c2cf64
fix: retire owned install trees atomically
naipi11 Aug 29, 2026
03de616
docs: plan immutable generation release model
naipi11 Aug 29, 2026
e3ad4f7
fix: use immutable generation file primitives
naipi11 Aug 29, 2026
2baf9df
docs: narrow immutable generation cleanup contract
naipi11 Aug 29, 2026
90d9d8d
fix: close immutable generation review gaps
naipi11 Aug 29, 2026
e74cfdf
fix: seal temporary leaves before publication
naipi11 Aug 29, 2026
f86d439
fix: finalize retirement and close cleanup
naipi11 Aug 29, 2026
5cb83a7
docs: allow retained generation compensation
naipi11 Aug 29, 2026
c1e3ef0
feat: add retained generation file capabilities
naipi11 Aug 29, 2026
14178fc
fix: close retained generation capability gaps
naipi11 Aug 29, 2026
e80b829
fix: reject duplicate retained manifest identities
naipi11 Aug 29, 2026
2101485
fix: commit lifecycle through immutable generations
naipi11 Aug 31, 2026
bd5b690
fix: complete generation bootstrap lifecycle
naipi11 Aug 31, 2026
f10f725
fix: recover append-only lifecycle finalization
naipi11 Sep 1, 2026
8eb6920
fix: fail closed selector fallback
naipi11 Sep 1, 2026
d3ec629
fix: prove selector absence safely
naipi11 Sep 1, 2026
46692e7
fix: permit proven legacy selector fallback
naipi11 Sep 1, 2026
9e1c41c
fix: bootstrap sealed installer package
naipi11 Sep 1, 2026
51def5c
fix: harden sealed setup evidence
naipi11 Sep 1, 2026
728b627
docs: record sealed setup evidence
naipi11 Sep 1, 2026
1cb9162
fix: bind portable sealed execution
naipi11 Sep 1, 2026
a5d5958
docs: record portable sealed evidence
naipi11 Sep 1, 2026
58bd760
test: exercise portable sealed race end to end
naipi11 Sep 1, 2026
9e971d1
docs: record portable entrypoint race
naipi11 Sep 1, 2026
141ab71
test: exercise portable copy barrier race
naipi11 Sep 1, 2026
70bc231
docs: record portable copy barrier evidence
naipi11 Sep 1, 2026
e43d9ce
feat: register product only after readiness
naipi11 Sep 1, 2026
97cf17d
docs: record post-readiness registration evidence
naipi11 Sep 1, 2026
2e76698
fix: complete matched product lifecycle
naipi11 Sep 1, 2026
0fdc392
docs: record matched product lifecycle evidence
naipi11 Sep 1, 2026
283ecbe
fix: bind matched product finalization
naipi11 Sep 1, 2026
2f8ff2f
docs: record Ready-bound product evidence
naipi11 Sep 1, 2026
67c79d8
fix: reject invalid Ready evidence before cleanup
naipi11 Sep 1, 2026
7c333e0
docs: record pre-cleanup Ready gate evidence
naipi11 Sep 1, 2026
1ff17bb
fix: enforce strict retained product authority
naipi11 Sep 1, 2026
a83840b
docs: record strict retained authority evidence
naipi11 Sep 1, 2026
3cd8912
fix: prove durable Ready product authority
naipi11 Sep 1, 2026
a60759b
docs: record durable Ready authority evidence
naipi11 Sep 1, 2026
a9900a4
fix: serialize strict product authority
naipi11 Sep 1, 2026
03b611b
docs: record canonical product authority evidence
naipi11 Sep 1, 2026
2e86de1
fix: retain failed product transaction cleanup
naipi11 Sep 2, 2026
320b954
docs: record product cleanup repair evidence
naipi11 Sep 2, 2026
25eedda
docs: plan durable product cleanup
naipi11 Sep 2, 2026
271a6e5
fix: fence durable product cleanup
naipi11 Sep 2, 2026
ffb5dd0
docs: record durable product cleanup evidence
naipi11 Sep 2, 2026
79b4825
fix: partition durable cleanup history
naipi11 Sep 2, 2026
a4c632c
docs: record cleanup fence fix evidence
naipi11 Sep 2, 2026
da72b91
docs: record durable cleanup fence evidence
naipi11 Sep 2, 2026
b1aec46
test: reject stale Ready product authority
naipi11 Sep 2, 2026
9e00ccf
fix: close durable cleanup recovery gaps
naipi11 Sep 2, 2026
f895ebb
docs: record final durable cleanup fix evidence
naipi11 Sep 2, 2026
3ec2a3d
docs: plan rollback lease ordering
naipi11 Sep 2, 2026
a178f01
fix: release rollback cleanup lease
naipi11 Sep 2, 2026
80392ad
docs: record rollback lease ordering evidence
naipi11 Sep 2, 2026
2aef5ff
docs: plan legacy upgrade and reclamation
naipi11 Sep 2, 2026
5114a09
fix: bridge legacy lifecycle upgrade
naipi11 Sep 2, 2026
812b34e
docs: record legacy lifecycle upgrade evidence
naipi11 Sep 2, 2026
52d3962
fix: resume failed legacy migration
naipi11 Sep 2, 2026
4504c1a
docs: record legacy migration retry evidence
naipi11 Sep 2, 2026
6be1025
fix: migrate exact legacy registration profiles
naipi11 Sep 2, 2026
be47fad
docs: record legacy registration profile evidence
naipi11 Sep 2, 2026
69c762d
fix: make legacy registry compensation create-only
naipi11 Sep 2, 2026
3682b81
docs: record registry compensation fix evidence
naipi11 Sep 2, 2026
1d57736
fix: reclaim generation through pinned handles
naipi11 Sep 2, 2026
6b9bc7e
docs: record generation reclamation evidence
naipi11 Sep 2, 2026
58db6fd
fix: close legacy upgrade recovery gaps
naipi11 Sep 2, 2026
ea86451
docs: record legacy final fix evidence
naipi11 Sep 2, 2026
a5ffe4a
docs: plan cross-process retry closure
naipi11 Sep 2, 2026
9973bfd
fix: recover installed TaskRemoved across sessions
naipi11 Sep 2, 2026
bf4d215
docs: record cross-session TaskRemoved recovery
naipi11 Sep 2, 2026
ac209f6
docs: plan TaskRemoved retry review fixes
naipi11 Sep 2, 2026
bf939c6
fix: harden TaskRemoved replacement recovery
naipi11 Sep 2, 2026
6107151
docs: record TaskRemoved fix round evidence
naipi11 Sep 2, 2026
bc0441d
docs: accept TaskRemoved retry fix
naipi11 Sep 2, 2026
148f6ee
fix: persist legacy product migration plan
naipi11 Sep 3, 2026
66a91aa
docs: record durable legacy migration evidence
naipi11 Sep 3, 2026
3d40763
docs: plan durable migration review fixes
naipi11 Sep 3, 2026
a0c8d71
fix: harden durable legacy plan recovery
naipi11 Sep 3, 2026
f38ddff
docs: record durable plan fix round evidence
naipi11 Sep 3, 2026
01ebdba
docs: accept durable migration fix
naipi11 Sep 3, 2026
ec8dac0
docs: plan Runtime V7 ABI closure
naipi11 Sep 3, 2026
9d7cc90
fix: advance install runtime ABI to V7
naipi11 Sep 3, 2026
69ee7ce
docs: record Runtime V7 closure evidence
naipi11 Sep 3, 2026
3eea951
docs: accept Runtime V7 closure
naipi11 Sep 3, 2026
1109854
docs: start exact release evidence task
naipi11 Sep 3, 2026
fb163b7
feat: require dual Defender release evidence
naipi11 Sep 3, 2026
2b01753
docs: record exact release evidence task
naipi11 Sep 3, 2026
ec97b8c
docs: plan Defender authority review fixes
naipi11 Sep 3, 2026
9f8a9e7
fix: close Defender authority review gaps
naipi11 Sep 3, 2026
bc4eda5
docs: record Defender authority fix evidence
naipi11 Sep 3, 2026
291a953
docs: add Defender authority fix report
naipi11 Sep 3, 2026
6d9bafa
fix: stage releases behind draft promotion gates
naipi11 Sep 4, 2026
db0cd79
docs: record Task 6 release gate verification
naipi11 Sep 4, 2026
325985d
fix: restore public release tool module wrappers
naipi11 Sep 4, 2026
94994af
docs: record Task 6 module-boundary correction
naipi11 Sep 4, 2026
19c8962
[verified] Harden Windows runtime lifecycle reliability
naipi11 Sep 10, 2026
79b6aa0
fix: harden runtime payload containment
naipi11 Sep 11, 2026
0124dfb
test: remove uninstall wrapper timing race
naipi11 Sep 11, 2026
7d383c2
test: avoid platform-specific uninstall timing assertion
naipi11 Sep 11, 2026
ea27909
fix: harden v2.5.22 install lifecycle reliability
naipi11 Sep 12, 2026
c80502c
test: harden temporary residue cleanup
naipi11 Sep 13, 2026
2269f06
test: harden state store teardown
naipi11 Sep 13, 2026
dbb4ee3
test: harden static probe teardown
naipi11 Sep 13, 2026
17526cf
test: use safe root for full residue fixture
naipi11 Sep 13, 2026
f9dc476
test: harden uninstall bootstrap teardown
naipi11 Sep 14, 2026
2e8a23b
test: pin temp roots and child stdin to canonical utf-8
Sep 19, 2026
544315e
test: make the ci gates pass on hosted windows runners
Sep 19, 2026
4dbbd56
test: keep child stderr assertions self-explanatory
Sep 19, 2026
d863a34
diag: probe the build script on the runner
Sep 19, 2026
c40a2dc
diag: compare build under pwsh and powershell
Sep 19, 2026
b58f63a
diag: probe scriptblock scoping across invocation styles
Sep 19, 2026
4a01e52
fix: keep the temporary setup action a plain scriptblock
Sep 19, 2026
0418d60
diag: probe portable launcher provenance
Sep 19, 2026
f533ac3
fix: keep json provenance timestamps as text under powershell 7
Sep 19, 2026
53eb732
fix: accept json integer shapes that powershell 7 produces
Sep 19, 2026
b42b44d
fix: define the setup artifact integer helper at module scope
Sep 19, 2026
a9c84b8
test: use compliant stage names in the build action probe
Sep 19, 2026
dccce2f
chore: drop the temporary diagnostic workflows
Sep 19, 2026
0a7a37c
chore: drop the temporary build probes
Sep 19, 2026
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
14 changes: 11 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@ jobs:
validate:
name: Windows validation
runs-on: windows-latest
timeout-minutes: 20
# The full repository validation runs 43 persistence suites sequentially in
# Windows PowerShell child processes. On a hosted runner that takes about 33
# minutes, so the job limit must cover it with margin for the lifecycle,
# release-contract and TrayHost steps that follow.
timeout-minutes: 90
steps:
- name: Check out repository
uses: actions/checkout@v4
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262

- name: Set up Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
with:
node-version: 22

Expand All @@ -49,3 +53,7 @@ jobs:
- name: Run production TrayHost smoke test
shell: pwsh
run: ./tests/trayhost/Invoke-TrayHostSelfTest.ps1 -ProductionOnly

- name: Run authenticated TrayHost production trace
shell: pwsh
run: ./tests/trayhost/Invoke-TrayHostSelfTest.ps1 -ProductionTraceOnly
219 changes: 112 additions & 107 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ on:
required: true
type: string

concurrency:
group: ccod-draft-${{ github.event.inputs.tag || github.ref_name }}
cancel-in-progress: false

permissions:
contents: read

Expand All @@ -19,6 +23,7 @@ jobs:
runs-on: windows-latest
outputs:
tag: ${{ steps.release.outputs.tag }}
commit: ${{ steps.commit.outputs.commit }}
steps:
- name: Validate release tag
id: release
Expand All @@ -27,25 +32,76 @@ jobs:
CCOD_RELEASE_TAG_INPUT: ${{ inputs.tag }}
run: |
$tag = if ($env:GITHUB_EVENT_NAME -eq 'workflow_dispatch') { [string]$env:CCOD_RELEASE_TAG_INPUT } else { [string]$env:GITHUB_REF_NAME }
$tag = $tag.Trim()
if ($tag -cnotmatch '^v\d+\.\d+\.\d+$') {
if ($tag -cnotmatch '^v\d+\.\d+\.\d+\z') {
throw "Release tag must be canonical vX.Y.Z; received '$tag'."
}
"tag=$tag" >> $env:GITHUB_OUTPUT

build:
- name: Check out the validated release tag
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262
with:
ref: ${{ steps.release.outputs.tag }}
fetch-depth: 1

- name: Resolve immutable release commit
id: commit
shell: pwsh
run: |
$commit = ([string](& git rev-parse HEAD)).Trim().ToLowerInvariant()
if ($LASTEXITCODE -ne 0 -or $commit -cnotmatch '^[0-9a-f]{40}$') {
throw "Release tag did not resolve to a canonical commit: '$commit'."
}
"commit=$commit" >> $env:GITHUB_OUTPUT

preflight:
needs: prepare
outputs:
tag: ${{ needs.prepare.outputs.tag }}
commit: ${{ needs.prepare.outputs.commit }}
runs-on: windows-latest
steps:
- name: Check out repository
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262
with:
ref: ${{ needs.prepare.outputs.commit }}

- name: Run clean release runner
shell: pwsh
env:
CCOD_RELEASE_TAG: ${{ needs.prepare.outputs.tag }}
run: |
$tag = $env:CCOD_RELEASE_TAG
if ($tag -cnotmatch '^v\d+\.\d+\.\d+\z') {
throw "Release tag output is invalid: '$tag'."
}
$version = $tag.Substring(1)
Import-Module (Join-Path $PWD 'tools\Test-CleanReleaseRunner.psm1') -Force -DisableNameChecking
$result = Test-CcodCleanReleaseRunner -RepositoryRoot $PWD -ExpectedVersion $version
$artifact = Join-Path $PWD 'build\preflight\clean-preflight.json'
New-Item -ItemType Directory -Path (Split-Path $artifact -Parent) -Force | Out-Null
Copy-Item -LiteralPath $result.PreflightPath -Destination $artifact -Force

- name: Upload immutable clean preflight evidence
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: CodexRemote-fix clean preflight
path: build/preflight/clean-preflight.json
if-no-files-found: error

build:
needs: preflight
outputs:
tag: ${{ needs.preflight.outputs.tag }}
commit: ${{ needs.preflight.outputs.commit }}
runs-on: windows-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262
with:
ref: ${{ needs.prepare.outputs.tag }}
ref: ${{ needs.preflight.outputs.commit }}

- name: Set up Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
with:
node-version: 22

Expand All @@ -65,160 +121,109 @@ jobs:
shell: pwsh
run: ./tests/trayhost/Invoke-TrayHostSelfTest.ps1 -ProductionOnly

- name: Run authenticated TrayHost production trace
shell: pwsh
run: ./tests/trayhost/Invoke-TrayHostSelfTest.ps1 -ProductionTraceOnly

- name: Build CodexRemote-fix portable bundle
shell: pwsh
env:
CCOD_RELEASE_TAG: ${{ needs.prepare.outputs.tag }}
CCOD_RELEASE_TAG: ${{ needs.preflight.outputs.tag }}
run: |
$version = $env:CCOD_RELEASE_TAG.Substring(1)
./build/build.ps1 -Version $version

- name: Upload immutable CodexRemote-fix portable bundle
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: CodexRemote-fix portable bundle
path: build/dist/*
path: build/dist
if-no-files-found: error

publish:
stage:
needs: build
runs-on: windows-latest
permissions:
contents: write
steps:
- name: Check out repository
uses: actions/checkout@v4
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262
with:
ref: ${{ needs.build.outputs.tag }}
ref: ${{ needs.build.outputs.commit }}

- name: Set up Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
with:
node-version: 22

- name: Download immutable CodexRemote-fix portable bundle
uses: actions/download-artifact@v4
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
name: CodexRemote-fix portable bundle
path: build/dist

- name: Verify downloaded release candidate contract
- name: Download immutable clean preflight evidence
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
name: CodexRemote-fix clean preflight
path: build/preflight

- name: Materialize transferred clean preflight evidence
shell: pwsh
env:
CCOD_RELEASE_TAG: ${{ needs.build.outputs.tag }}
run: |
npm run test:release-contract
$tag = $env:CCOD_RELEASE_TAG
if ($tag -cnotmatch '^v\d+\.\d+\.\d+$') {
throw "Release tag output is invalid: '$tag'."
}
$version = $tag.Substring(1)
. ./tools/Test-ReleaseDefender.ps1 -Library
$manifestPath = Join-Path $PWD ("build\dist\CodexRemote-fix-$version-release-manifest.json")
Test-CcodReleaseAssetManifest -ManifestPath $manifestPath -AssetDirectory (Join-Path $PWD 'build\dist') -ExpectedVersion $version | Out-Null
$setupManifestPath = Join-Path $PWD ("build\dist\CodexRemote-fix-$version-setup-release-manifest.json")
Test-CcodReleaseAssetManifest -ManifestPath $setupManifestPath -AssetDirectory (Join-Path $PWD 'build\dist') -ExpectedVersion $version | Out-Null
$source = Join-Path $PWD 'build\preflight\clean-preflight.json'
$evidence = Join-Path $env:RUNNER_TEMP 'ccod-draft-evidence'
New-Item -ItemType Directory -Path $evidence -Force | Out-Null
Copy-Item -LiteralPath $source -Destination (Join-Path $evidence ("CodexRemote-fix-$version-clean-preflight.json")) -Force

- name: Publish GitHub release
- name: Verify downloaded release candidate contract
shell: pwsh
env:
GH_TOKEN: ${{ github.token }}
CCOD_RELEASE_TAG: ${{ needs.build.outputs.tag }}
CCOD_RELEASE_COMMIT: ${{ needs.build.outputs.commit }}
run: |
npm run test:release-contract
$tag = $env:CCOD_RELEASE_TAG
if ($tag -cnotmatch '^v\d+\.\d+\.\d+$') {
if ($tag -cnotmatch '^v\d+\.\d+\.\d+\z') {
throw "Release tag output is invalid: '$tag'."
}
$version = $tag.Substring(1)
$changelog = Get-Content -LiteralPath (Join-Path $PWD 'CHANGELOG.md') -Raw -Encoding UTF8
$releaseSection = [regex]::Match($changelog, '(?ms)^## ' + [regex]::Escape($tag) + '\r?\n(.*?)(?=^## |\z)')
if (-not $releaseSection.Success) {
throw "CHANGELOG.md has no release section for $tag"
}
$englishSection = [regex]::Match($releaseSection.Groups[1].Value, '(?ms)^### English\r?\n(.*?)(?=^### |\z)')
if (-not $englishSection.Success) {
throw "CHANGELOG.md has no English release section for $tag"
}
$notesPath = Join-Path $env:RUNNER_TEMP ("CodexRemote-fix-$tag-release-notes.md")
Set-Content -LiteralPath $notesPath -Value ("# CodexRemote-fix $version`n`n" + $englishSection.Groups[1].Value.Trim() + "`n") -Encoding UTF8
& gh release view $tag --repo $env:GITHUB_REPOSITORY --json assets 2>$null | Out-Null
$exists = $LASTEXITCODE -eq 0
$existingAssets = @()
if ($exists) {
$existingAssets = @(& gh release view $tag --repo $env:GITHUB_REPOSITORY --json assets --jq '.assets[].name')
if ($LASTEXITCODE -ne 0) {
throw "Could not inspect existing release assets for $tag."
}
}
$assets = @(Get-ChildItem -LiteralPath (Join-Path $PWD 'build\dist') -File | Where-Object { $_.Extension -in @('.exe', '.zip', '.txt', '.json') })
$assetNames = @($assets | ForEach-Object { $_.Name })
$unexpected = @($existingAssets | Where-Object { $assetNames -cnotcontains $_ })
if ($unexpected.Count -ne 0) {
throw "Existing release $tag contains unexpected assets and cannot be safely updated: $($unexpected -join ', ')"
}
foreach ($asset in $assets) {
if ($existingAssets -ccontains $asset.Name) {
$downloadRoot = Join-Path $env:RUNNER_TEMP ('ccod-existing-asset-' + [guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $downloadRoot -Force | Out-Null
try {
& gh release download $tag --repo $env:GITHUB_REPOSITORY --pattern $asset.Name --dir $downloadRoot
if ($LASTEXITCODE -ne 0) {
throw "Could not download existing release asset $($asset.Name)."
}
$existingAsset = Join-Path $downloadRoot $asset.Name
if (-not (Test-Path -LiteralPath $existingAsset -PathType Leaf) -or
(Get-FileHash -LiteralPath $existingAsset -Algorithm SHA256).Hash.ToLowerInvariant() -cne (Get-FileHash -LiteralPath $asset.FullName -Algorithm SHA256).Hash.ToLowerInvariant()) {
throw "Existing release asset $($asset.Name) differs from the immutable candidate."
}
} finally {
if (Test-Path -LiteralPath $downloadRoot) { Remove-Item -LiteralPath $downloadRoot -Recurse -Force -ErrorAction SilentlyContinue }
}
}
Import-Module (Join-Path $PWD 'tools\ReleaseAssetContract.psm1') -Force
$names = @(Get-CcodExpectedReleaseAssetNames -Version $version)
$candidate = Test-CcodExactReleaseAssetSet -AssetDirectory (Join-Path $PWD 'build/dist') -Version $version
if ($env:CCOD_RELEASE_COMMIT -cnotmatch '^[0-9a-f]{40}\z' -or $candidate.GitCommit -cne $env:CCOD_RELEASE_COMMIT) {
throw 'CCOD_RELEASE_BUILD_COMMIT_MISMATCH'
}
if (-not $exists) {
gh release create $tag --repo $env:GITHUB_REPOSITORY --title "CodexRemote-fix $version" --notes-file $notesPath
} else {
gh release edit $tag --repo $env:GITHUB_REPOSITORY --title "CodexRemote-fix $version" --notes-file $notesPath
}
foreach ($asset in $assets) {
if ($existingAssets -cnotcontains $asset.Name) {
& gh release upload $tag --repo $env:GITHUB_REPOSITORY $asset.FullName
if ($LASTEXITCODE -ne 0) {
throw "Could not upload release asset $($asset.Name)."
}
}
$manifestPath = Join-Path $PWD ('build\dist\CodexRemote-fix-' + $version + '-release-manifest.json')
Test-CcodReleaseAssetManifest -ManifestPath $manifestPath -AssetDirectory (Join-Path $PWD 'build\dist') -ExpectedVersion $version | Out-Null
$setupManifestPath = Join-Path $PWD ('build\dist\CodexRemote-fix-' + $version + '-setup-release-manifest.json')
Test-CcodReleaseAssetManifest -ManifestPath $setupManifestPath -AssetDirectory (Join-Path $PWD 'build\dist') -ExpectedVersion $version | Out-Null
if ($names.Count -ne 11) {
throw 'Release candidate does not expose the exact 11 public names.'
}

- name: Read back published GitHub release assets
- name: Stage private GitHub draft
shell: pwsh
env:
GH_TOKEN: ${{ github.token }}
CCOD_RELEASE_TAG: ${{ needs.build.outputs.tag }}
run: |
$tag = $env:CCOD_RELEASE_TAG
if ($tag -cnotmatch '^v\d+\.\d+\.\d+$') {
if ($tag -cnotmatch '^v\d+\.\d+\.\d+\z') {
throw "Release tag output is invalid: '$tag'."
}
$assets = @(Get-ChildItem -LiteralPath (Join-Path $PWD 'build\dist') -File | Where-Object { $_.Extension -in @('.exe', '.zip', '.txt', '.json') })
$assetNames = @($assets | ForEach-Object { $_.Name })
$publishedNames = @(& gh release view $tag --repo $env:GITHUB_REPOSITORY --json assets --jq '.assets[].name')
if ($LASTEXITCODE -ne 0 -or ((@($publishedNames | Sort-Object) -join '|') -cne (@($assetNames | Sort-Object) -join '|'))) {
throw "Published release $tag does not expose the exact expected asset set for read-back."
}
$readBackRoot = Join-Path $env:RUNNER_TEMP ('ccod-release-readback-' + [guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $readBackRoot -Force | Out-Null
try {
foreach ($asset in $assets) {
& gh release download $tag --repo $env:GITHUB_REPOSITORY --pattern $asset.Name --dir $readBackRoot
if ($LASTEXITCODE -ne 0) {
throw "Could not read back published release asset $($asset.Name)."
}
$readBackAsset = Join-Path $readBackRoot $asset.Name
if (-not (Test-Path -LiteralPath $readBackAsset -PathType Leaf) -or
(Get-FileHash -LiteralPath $readBackAsset -Algorithm SHA256).Hash.ToLowerInvariant() -cne (Get-FileHash -LiteralPath $asset.FullName -Algorithm SHA256).Hash.ToLowerInvariant()) {
throw "Published release asset $($asset.Name) does not match the immutable candidate bytes."
}
}
} finally {
if (Test-Path -LiteralPath $readBackRoot) { Remove-Item -LiteralPath $readBackRoot -Recurse -Force -ErrorAction SilentlyContinue }
$version = $tag.Substring(1)
$notesPath = Join-Path $env:RUNNER_TEMP ("CodexRemote-fix-$tag-release-notes.md")
& (Join-Path $PWD 'tools\New-GitHubReleaseNotes.ps1') -ChangelogPath (Join-Path $PWD 'CHANGELOG.md') -Tag $tag -OutputPath $notesPath | Out-Null
$evidence = Join-Path $env:RUNNER_TEMP 'ccod-draft-evidence'
$preflight = Join-Path $evidence ("CodexRemote-fix-$version-clean-preflight.json")
if (-not (Test-Path -LiteralPath $preflight -PathType Leaf)) {
throw "Transferred clean preflight evidence is missing: '$preflight'."
}
Import-Module (Join-Path $PWD 'tools\Invoke-GitHubDraftRelease.psm1') -Force -DisableNameChecking
Invoke-CcodGitHubDraftRelease -Mode Stage -Tag $tag -AssetDirectory (Join-Path $PWD 'build\dist') -EvidenceDirectory $evidence -NotesPath $notesPath | Out-Null
Invoke-CcodGitHubDraftRelease -Mode Verify -Tag $tag -AssetDirectory (Join-Path $PWD 'build\dist') -EvidenceDirectory $evidence | Out-Null
Loading
Loading