Skip to content
Draft
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
312 commits
Select commit Hold shift + click to select a range
340b476
fix(release): bind updater manifest to receipt-authorized bytes
seonghobae Sep 14, 2026
4d6f7cb
fix(release): publish receipt-bound updater manifest
seonghobae Sep 14, 2026
64fbd14
fix(release): preserve exact updater signature text
seonghobae Sep 14, 2026
889554d
test(release): cover manifest publication boundary cleanly
seonghobae Sep 14, 2026
f0d6dd5
docs(release): trace receipt-bound updater manifest publication
seonghobae Sep 14, 2026
db4ad06
test(release): reproduce hosted asset re-verification gap
seonghobae Sep 14, 2026
e022794
fix(release): re-admit downloaded hosted release assets
seonghobae Sep 14, 2026
3c9221b
fix(release): re-verify draft and published release bytes
seonghobae Sep 14, 2026
3412a19
test(release): format hosted re-verification coverage
seonghobae Sep 14, 2026
4c1dfe9
docs(release): trace hosted release byte re-verification
seonghobae Sep 14, 2026
0f13ef0
test(release): require immutable release attestation gate
seonghobae Sep 14, 2026
b65f00c
fix(release): verify immutable release attestation
seonghobae Sep 14, 2026
d6cc359
docs(release): trace immutable release attestation gate
seonghobae Sep 14, 2026
57ced89
test(release): require updater manifest security metadata
seonghobae Sep 14, 2026
21ee4ff
fix(release): bind updater security metadata to receipts
seonghobae Sep 14, 2026
209f58e
docs(release): trace updater security metadata boundary
seonghobae Sep 14, 2026
857e1e9
test(release): cover updater policy metadata admission
seonghobae Sep 14, 2026
4467a9e
test(distribution): require Rust updater lifecycle core
seonghobae Sep 14, 2026
1339cfd
feat(distribution): add updater lifecycle Rust core crate
seonghobae Sep 14, 2026
0fbb2e8
build(distribution): lock updater lifecycle core
seonghobae Sep 14, 2026
42fdeed
fix(distribution): enforce updater anti-replay and rollback decisions
seonghobae Sep 14, 2026
cd25fab
docs(distribution): trace anti-replay rollback decision core
seonghobae Sep 14, 2026
d80a477
chore(distribution): deny warnings and missing rustdoc
seonghobae Sep 14, 2026
a0a9eec
docs(architecture): register Distribution update decision core
seonghobae Sep 14, 2026
9df9e61
docs(product): establish commercial technical gap baseline
seonghobae Sep 14, 2026
fa5690b
test(distribution): require durable highest-seen state suite
seonghobae Sep 14, 2026
f07f35e
feat(distribution): establish highest-seen state crate
seonghobae Sep 14, 2026
30159e0
build(distribution): lock highest-seen state graph
seonghobae Sep 14, 2026
95889b6
feat(distribution): persist replay authority as bounded append log
seonghobae Sep 14, 2026
05a8dc9
docs(distribution): trace durable updater freshness state
seonghobae Sep 14, 2026
9a481cc
docs(product): record durable updater-state progress
seonghobae Sep 14, 2026
fb1ac16
fix(distribution): disambiguate bounded state read
seonghobae Sep 14, 2026
6826c92
docs(architecture): register durable Distribution state owner
seonghobae Sep 14, 2026
e525aa1
test(distribution): require authenticated runtime admission suite
seonghobae Sep 14, 2026
5c95912
feat(distribution): add runtime admission crate manifest
seonghobae Sep 14, 2026
b9f72be
build(distribution): lock runtime path dependency graph
seonghobae Sep 14, 2026
85db601
feat(distribution): bound raw updater metadata as provisional input
seonghobae Sep 14, 2026
def74ef
refactor(distribution): keep provisional runtime admission stateless
seonghobae Sep 14, 2026
418c68d
build(distribution): minimize provisional runtime lock graph
seonghobae Sep 14, 2026
1c52911
docs(distribution): correct raw updater metadata trust boundary
seonghobae Sep 14, 2026
d0bdea0
docs(product): expose updater metadata-authentication gap
seonghobae Sep 14, 2026
8533403
docs(architecture): separate provisional updater metadata from authority
seonghobae Sep 14, 2026
59bc8c8
fix(updater): pin provisional release download namespace
seonghobae Sep 14, 2026
daad6e5
fix(updater): keep URL admission Rust-stable
seonghobae Sep 14, 2026
e25c3f1
docs(updater): trace pinned release URL admission
seonghobae Sep 14, 2026
1e1f1c2
test(distribution): require bounded updater download contract
seonghobae Sep 14, 2026
f183c0c
feat(distribution): add bounded updater artifact streaming
seonghobae Sep 14, 2026
19c5983
docs(distribution): trace bounded updater streaming boundary
seonghobae Sep 14, 2026
21f3bee
docs(product): make bounded updater download gap code-current
seonghobae Sep 14, 2026
539fbe2
docs(architecture): add bounded updater download owner
seonghobae Sep 14, 2026
dcc04b7
test(distribution): require crash-safe updater staging sink
seonghobae Sep 14, 2026
ed079fd
feat(distribution): add exclusive updater staging sink
seonghobae Sep 14, 2026
7620248
test(distribution): cover updater staging failure cleanup
seonghobae Sep 14, 2026
00825be
docs(distribution): trace exclusive updater staging lifecycle
seonghobae Sep 14, 2026
e485b3a
docs(product): record updater staging boundary
seonghobae Sep 14, 2026
a956bcf
test(distribution): remove unverified sealed artifacts on drop
seonghobae Sep 14, 2026
e76abdd
fix(distribution): clean sealed artifacts until trust promotion
seonghobae Sep 14, 2026
2909133
docs(distribution): trace sealed-artifact cleanup before trust promotion
seonghobae Sep 14, 2026
a065ceb
docs(product): keep sealed updater bytes provisional
seonghobae Sep 14, 2026
56aa746
test(distribution): require sealed read-only descriptor stream
seonghobae Sep 14, 2026
13ca9b7
test(distribution): stop depending on writable sealed handle
seonghobae Sep 14, 2026
6144302
fix(distribution): withhold writable sealed artifact handle
seonghobae Sep 14, 2026
fdd9705
docs(traceability): bind sealed updater reads to descriptor
seonghobae Sep 14, 2026
5568e22
docs(product): keep sealed updater descriptor read-only
seonghobae Sep 14, 2026
f6723cf
docs(architecture): withhold sealed updater write capability
seonghobae Sep 14, 2026
1c8d83d
docs(architecture): restore sources and narrow sealed reader capability
seonghobae Sep 14, 2026
5713689
docs(architecture): restore exact verification wording
seonghobae Sep 14, 2026
e1274be
test(distribution): RED bound sealed reads to admitted bytes
seonghobae Sep 14, 2026
c451096
fix(distribution): cap sealed verifier reads at admitted bytes
seonghobae Sep 14, 2026
e294147
test(distribution): reject sealed descriptor truncation
seonghobae Sep 14, 2026
f6b376a
docs(distribution): trace sealed reader byte-bound repair
seonghobae Sep 14, 2026
56d215e
docs(product): keep sealed verifier resource bound code-current
seonghobae Sep 14, 2026
9f7bddd
test(distribution): require admitted artifact transport binding
seonghobae Sep 15, 2026
663affc
fix(distribution): bind admitted artifact transport fields
seonghobae Sep 15, 2026
ed7426f
docs(distribution): trace admitted transport binding
seonghobae Sep 15, 2026
9c95c71
docs(product): align updater transport gap baseline
seonghobae Sep 15, 2026
46c4cba
docs(architecture): bind updater transport metadata owner
seonghobae Sep 15, 2026
9d1dc2f
test(release): reject runtime-incompatible release versions
seonghobae Sep 15, 2026
e268c9b
fix(release): align release version grammar with updater core
seonghobae Sep 15, 2026
97f9c2a
docs(traceability): bind stable release version grammar
seonghobae Sep 15, 2026
6cee7a6
docs(product): record stable release grammar invariant
seonghobae Sep 15, 2026
1cf9656
test(release): reject version components beyond u64
seonghobae Sep 15, 2026
1c44f25
fix(release): align stable version range with runtime
seonghobae Sep 15, 2026
c2fd0cd
docs(traceability): close stable version range drift
seonghobae Sep 15, 2026
ba426b4
docs(product): record stable version range invariant
seonghobae Sep 15, 2026
6e6ebe2
feat(distribution): add updater transport crate
seonghobae Sep 15, 2026
5f29863
build(distribution): lock transport owner graph
seonghobae Sep 15, 2026
2819481
feat(distribution): add transport response boundary
seonghobae Sep 15, 2026
8f39dfc
test(distribution): require admitted GitHub release redirect
seonghobae Sep 15, 2026
1b4f7a0
test(distribution): gate transport contract in root suite
seonghobae Sep 15, 2026
4964c3c
fix(distribution): admit one-hop GitHub release asset redirects
seonghobae Sep 15, 2026
d424dd8
docs(distribution): trace updater transport policy
seonghobae Sep 15, 2026
adc52ef
docs(product): align distribution transport gap baseline
seonghobae Sep 15, 2026
2e77173
docs(architecture): register updater transport boundary
seonghobae Sep 15, 2026
8313e9f
test(distribution): reject malformed Tauri signature envelopes
seonghobae Sep 15, 2026
6e5e42f
fix(distribution): validate Tauri signature envelope before network
seonghobae Sep 15, 2026
03c1314
test(release): reject non-base64 updater signatures
seonghobae Sep 15, 2026
2c7c772
fix(release): enforce Tauri signature envelope before publication
seonghobae Sep 15, 2026
fd906e2
docs(distribution): trace signature-envelope admission
seonghobae Sep 15, 2026
0e318ef
docs(product): align updater signature-envelope gap
seonghobae Sep 15, 2026
2f5af3e
docs(architecture): align updater signature-envelope ownership
seonghobae Sep 15, 2026
e376325
test(distribution): reject encoded updater response bodies
seonghobae Sep 15, 2026
606095e
fix(distribution): reject transformed updater response bodies
seonghobae Sep 15, 2026
f616421
docs(distribution): trace exact response-byte framing admission
seonghobae Sep 15, 2026
80fdc39
docs(product): record exact-byte updater response framing
seonghobae Sep 15, 2026
c3dbebe
test(distribution): redact redirect queries in diagnostics
seonghobae Sep 15, 2026
be91505
fix(distribution): redact redirect query data from diagnostics
seonghobae Sep 15, 2026
6b8cabb
docs(distribution): trace updater diagnostic redaction
seonghobae Sep 15, 2026
935266a
test(distribution): bound updater signature debug output
seonghobae Sep 15, 2026
1a4e8f5
fix(distribution): bound signature diagnostics
seonghobae Sep 15, 2026
b290bf0
docs(distribution): trace bounded signature diagnostics
seonghobae Sep 15, 2026
027ba14
test(distribution): reject provisional signature debug exposure
seonghobae Sep 15, 2026
90855cc
fix(distribution): redact provisional signature diagnostics
seonghobae Sep 15, 2026
bd17041
docs(distribution): record provisional diagnostic redaction
seonghobae Sep 15, 2026
93526ae
docs(gap): keep updater diagnostics buyer truth current
seonghobae Sep 15, 2026
5b0ddb5
test(distribution): require restart recovery for stale staging artifact
seonghobae Sep 15, 2026
b7a1839
fix(distribution): reclaim stale unverified staging artifact on restart
seonghobae Sep 15, 2026
e1b4a81
docs(distribution): trace stale staging restart recovery
seonghobae Sep 15, 2026
84d4a7b
docs(product): record updater staging restart recovery boundary
seonghobae Sep 15, 2026
dcd7a80
docs(distribution): align bounded staging with restart recovery
seonghobae Sep 15, 2026
74565b8
repair(distribution): return product baseline to canonical owner
seonghobae Sep 15, 2026
82843b4
test(distribution): reject reclaiming active staging attempts
seonghobae Sep 15, 2026
40cd754
fix(distribution): lease staging namespace before stale recovery
seonghobae Sep 15, 2026
ebf9428
test(distribution): account for persistent staging lease sentinel
seonghobae Sep 15, 2026
d2d1872
test(distribution): cover sealed lease and lease symlink boundary
seonghobae Sep 15, 2026
ebefa23
docs(distribution): trace active staging lease recovery
seonghobae Sep 15, 2026
9046e90
docs(distribution): make bounded staging lease code-current
seonghobae Sep 15, 2026
752b534
test(distribution): avoid platform-specific reads under staging lease
seonghobae Sep 15, 2026
c30167d
docs(distribution): record cross-platform staging lease fixture
seonghobae Sep 15, 2026
41afd2a
test(distribution): require staging lease contracts on shipped OS fam…
seonghobae Sep 15, 2026
cfb3ec1
ci(distribution): gate staging lease tests on Windows macOS and Linux
seonghobae Sep 15, 2026
137d162
docs(distribution): require cross-platform staging lease evidence
seonghobae Sep 15, 2026
44265a0
test(distribution): prove staging lease across real processes
seonghobae Sep 15, 2026
a999376
docs(distribution): trace real-process staging lease coverage
seonghobae Sep 15, 2026
af89337
test(release): red for descriptor-bound identity inputs
seonghobae Sep 15, 2026
42d34b5
fix(release): bind identity reads to stable descriptors
seonghobae Sep 15, 2026
0ef8912
docs(release): trace descriptor-bound identity admission
seonghobae Sep 15, 2026
68f1bb5
test(release): reject nonstandard JSON constants in identity inputs
seonghobae Sep 15, 2026
494aa0f
fix(release): require strict standard JSON identity metadata
seonghobae Sep 15, 2026
6808f29
docs(traceability): record strict JSON release identity admission
seonghobae Sep 15, 2026
8d56ab1
test(distribution): reject malformed nonselected signature envelope
seonghobae Sep 15, 2026
4e28d0c
fix(distribution): validate all updater signature envelopes
seonghobae Sep 15, 2026
9b691d7
refactor(distribution): keep signature syntax in metadata owner
seonghobae Sep 15, 2026
5b85e03
test(distribution): assert signature envelope owner boundary
seonghobae Sep 15, 2026
89f97a2
docs(distribution): make metadata owner authoritative for signature e…
seonghobae Sep 15, 2026
eb29922
test(distribution): cover signature envelope padding bounds
seonghobae Sep 15, 2026
a62b9ca
docs(architecture): align updater signature-envelope ownership
seonghobae Sep 15, 2026
26ff403
test(distribution): use admitted signature envelopes
seonghobae Sep 15, 2026
11a5a47
test(distribution): bind redirect decisions to policy identity
seonghobae Sep 15, 2026
35b851e
fix(distribution): bind redirect state to policy identity
seonghobae Sep 15, 2026
3f7daa8
docs(distribution): trace redirect policy binding repair
seonghobae Sep 15, 2026
a418640
test(release): reject same-size identity mutation during read
seonghobae Sep 15, 2026
438dca0
fix(release): verify stable identity bytes across descriptor reads
seonghobae Sep 15, 2026
24f8135
docs(release): trace stable descriptor snapshot admission
seonghobae Sep 15, 2026
f0b38c0
test(release): reject path replacement during identity read
seonghobae Sep 15, 2026
906b4f4
fix(release): revalidate identity path after stable read
seonghobae Sep 15, 2026
9671144
docs(traceability): bind release reads back to repository paths
seonghobae Sep 15, 2026
e25fd44
test(distribution): reject concurrent freshness-state writer
seonghobae Sep 15, 2026
dd2ae7f
fix(distribution): serialize highest-seen state access
seonghobae Sep 15, 2026
027963a
docs(distribution): trace highest-seen state lease
seonghobae Sep 15, 2026
e578644
test(distribution-state): reject impossible torn version prefixes
seonghobae Sep 15, 2026
e793d00
fix(distribution-state): reject impossible torn version prefixes
seonghobae Sep 15, 2026
e8a064c
docs(distribution-state): trace impossible torn-tail admission
seonghobae Sep 15, 2026
b633482
test(distribution): reject hard-linked freshness state
seonghobae Sep 15, 2026
4f8c933
fix(distribution): reject hard-linked freshness authority
seonghobae Sep 15, 2026
bd694de
docs(distribution): trace hard-link state admission
seonghobae Sep 15, 2026
8832e62
test(ci): require hosted coverage for Distribution owners
seonghobae Sep 15, 2026
c5a09d5
fix(ci): execute standalone Distribution owner tests
seonghobae Sep 15, 2026
835bfb5
docs(ci): bind Distribution owners to hosted platform tests
seonghobae Sep 15, 2026
4aca07e
test(distribution): reject replacement-path cleanup
seonghobae Sep 15, 2026
bc72745
fix(distribution): preserve replaced staging paths
seonghobae Sep 15, 2026
ecec997
docs(distribution): trace staging cleanup identity
seonghobae Sep 15, 2026
e07a50d
fix(distribution): publish Windows freshness state without alias muta…
seonghobae Sep 15, 2026
5303792
test(distribution): scope link-count rejection to Unix
seonghobae Sep 15, 2026
2f231d6
test(distribution): prove Windows state alias preservation
seonghobae Sep 15, 2026
c2b61b0
docs(distribution): correct Windows freshness-state authority
seonghobae Sep 15, 2026
e3313b9
docs(architecture): align updater state publication semantics
seonghobae Sep 15, 2026
7904f10
test(distribution): reject vulnerable HTTP TLS admission
seonghobae Sep 15, 2026
40496d5
fix(distribution): gate HTTP TLS dependency admission
seonghobae Sep 15, 2026
117962a
docs(distribution): trace HTTP TLS dependency admission
seonghobae Sep 15, 2026
3def1e1
fix(ci): run Distribution HTTP gate in quickcheck
seonghobae Sep 15, 2026
5f81143
test(distribution): reject vendored native TLS no-ALPN feature
seonghobae Sep 15, 2026
b26fc7d
fix(distribution): reject every native TLS reqwest feature
seonghobae Sep 15, 2026
fc22b46
docs(distribution): trace complete reqwest TLS feature admission
seonghobae Sep 15, 2026
621bac6
test(distribution): reject implicit reqwest decoding features
seonghobae Sep 15, 2026
43d7863
fix(distribution): allow-list reqwest transport features
seonghobae Sep 15, 2026
eb10409
test(distribution): cover reqwest feature allow-list
seonghobae Sep 15, 2026
372dd9d
docs(distribution): make reqwest feature admission explicit
seonghobae Sep 15, 2026
d36c4d6
test(distribution): reject target-specific reqwest bypass
seonghobae Sep 15, 2026
da209f1
fix(distribution): inspect target-scoped reqwest dependencies
seonghobae Sep 15, 2026
2b10fe9
docs(distribution): cover target-scoped dependency admission
seonghobae Sep 15, 2026
c58ed76
style(distribution): keep dependency admission formatter-safe
seonghobae Sep 15, 2026
7330af6
test(distribution): reject renamed reqwest admission bypass
seonghobae Sep 15, 2026
5356dcf
fix(distribution): admit renamed reqwest by package identity
seonghobae Sep 15, 2026
49b84eb
docs(distribution): trace Cargo reqwest alias admission
seonghobae Sep 15, 2026
cf6655d
test(distribution): reject workspace-inherited reqwest bypass
seonghobae Sep 15, 2026
8284ae6
fix(distribution): reject workspace-inherited reqwest ownership
seonghobae Sep 15, 2026
6958feb
docs(distribution): trace workspace reqwest inheritance
seonghobae Sep 15, 2026
a8d91b3
test(distribution): reject noncanonical reqwest sources
seonghobae Sep 15, 2026
914609c
fix(distribution): pin reqwest to canonical registry source
seonghobae Sep 15, 2026
e6a0663
test(distribution): reject noncanonical rustls source
seonghobae Sep 15, 2026
4fdc294
fix(distribution): require canonical rustls lock source
seonghobae Sep 15, 2026
2ff17b7
docs(distribution): trace HTTP dependency source provenance
seonghobae Sep 15, 2026
09852dd
test(distribution): reject unbounded reqwest requirements
seonghobae Sep 15, 2026
106d1f6
fix(distribution): bound reqwest version requirements
seonghobae Sep 15, 2026
bd359a3
docs(distribution): trace bounded reqwest requirements
seonghobae Sep 15, 2026
308f4a6
test(distribution): expose Windows staging replacement cleanup
seonghobae Sep 15, 2026
1d60331
fix(distribution): defer unprovable Windows staging unlink
seonghobae Sep 15, 2026
1a6ee09
test(distribution): prove deferred Windows staging reclaim
seonghobae Sep 15, 2026
0bae56f
docs(distribution): trace Windows staging cleanup authority
seonghobae Sep 15, 2026
c69e91d
docs(architecture): align Windows staging cleanup semantics
seonghobae Sep 15, 2026
7996cc2
test(distribution): align sealed cleanup with Windows contract
seonghobae Sep 15, 2026
2acb2f3
test(distribution): align transport staging cleanup by platform
seonghobae Sep 15, 2026
4c94b46
test(distribution): align staging lifecycle with Windows cleanup
seonghobae Sep 15, 2026
2e4c355
test(distribution): clean persistent lease fixtures explicitly
seonghobae Sep 15, 2026
6a85d35
test(distribution): account for persistent staging lease
seonghobae Sep 15, 2026
57385c1
docs(distribution): align bounded staging cleanup claims
seonghobae Sep 15, 2026
64fe0bc
docs(distribution): make restart recovery OS-cleanup current
seonghobae Sep 15, 2026
106e03a
test(distribution): reject unreviewed reqwest release lines
seonghobae Sep 15, 2026
13be264
fix(distribution): bind reqwest admission to reviewed release line
seonghobae Sep 15, 2026
7662556
docs(distribution): trace reviewed reqwest release line
seonghobae Sep 15, 2026
8564741
fix(distribution): expose in-flight staging path
seonghobae Sep 16, 2026
fd29aa1
test(distribution): bind redirects to full provisional identity
seonghobae Sep 16, 2026
6b0fe81
fix(distribution): bind redirect token to full candidate identity
seonghobae Sep 16, 2026
b79a250
docs(distribution): trace full redirect identity binding
seonghobae Sep 16, 2026
9901b2d
test(distribution): preserve candidate identity through sealing
seonghobae Sep 16, 2026
e42f6e6
fix(distribution): bind sealed artifacts to release identity
seonghobae Sep 16, 2026
3ef753f
test(distribution): read sealed artifact through owned descriptor
seonghobae Sep 16, 2026
50f072e
docs(distribution): trace sealed candidate identity binding
seonghobae Sep 16, 2026
dea8f7b
test(distribution): redact sealed transport diagnostics
seonghobae Sep 16, 2026
1136d34
docs(distribution): trace sealed transport diagnostics
seonghobae Sep 16, 2026
dbe7988
test(distribution): reject prerelease TLS boundary drift
seonghobae Sep 16, 2026
98a53d6
fix(distribution): compare TLS advisory prereleases correctly
seonghobae Sep 16, 2026
a3737a3
docs(distribution): trace prerelease advisory boundary repair
seonghobae Sep 16, 2026
269bd4a
test(distribution): align prerelease rejection receipt
seonghobae Sep 16, 2026
c85402f
docs(distribution): record prerelease regression verification
seonghobae Sep 16, 2026
340e7eb
test(distribution): reject transitive HTTP source substitution
seonghobae Sep 16, 2026
92162da
fix(distribution): bind full HTTP lock graph provenance
seonghobae Sep 16, 2026
6cded37
docs(distribution): trace full HTTP lock provenance
seonghobae Sep 16, 2026
5324118
test(distribution): cover lock provenance admission edges
seonghobae Sep 16, 2026
4920cf2
docs(distribution): record lock provenance edge evidence
seonghobae Sep 16, 2026
ed5be00
test(distribution): reject reqwest feature forwarding
seonghobae Sep 16, 2026
bc56faa
fix(distribution): block reqwest feature forwarding
seonghobae Sep 16, 2026
0f74972
test(distribution): cover weak reqwest feature forwarding
seonghobae Sep 16, 2026
ae98559
docs(distribution): trace reqwest feature forwarding gate
seonghobae Sep 16, 2026
da5c690
test(distribution): document integration test crates
seonghobae Sep 16, 2026
2cd906c
fix(release): revalidate publication asset list
seonghobae Sep 16, 2026
528ba04
docs(distribution): explain consumed staging cleanup
seonghobae Sep 16, 2026
070a463
test(release): specify explicit UMX-HQ acquisition contracts
seonghobae Sep 29, 2026
07daac9
feat(release): acquire UMX-HQ weights through explicit pinned tooling
seonghobae Sep 29, 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
17 changes: 17 additions & 0 deletions .github/workflows/build-baseline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,21 @@ env:
GIT_CONFIG_VALUE_0: develop

jobs:
release-identity:
name: release-identity
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Verify release identity
run: python3 scripts/checks/verify_release_identity.py

build-windows-native:
name: build / windows / amd64
needs: release-identity
runs-on: windows-2025
strategy:
fail-fast: false
Expand Down Expand Up @@ -122,6 +135,7 @@ jobs:

build-windows-arm64:
name: build / windows / arm64
needs: release-identity
runs-on: windows-11-arm
strategy:
fail-fast: false
Expand Down Expand Up @@ -232,6 +246,7 @@ jobs:

build-macos-native:
name: build / macos / amd64
needs: release-identity
runs-on: macos-15-intel
strategy:
fail-fast: false
Expand Down Expand Up @@ -294,6 +309,7 @@ jobs:

build-macos-arm64:
name: build / macos / arm64
needs: release-identity
runs-on: macos-15
strategy:
fail-fast: false
Expand Down Expand Up @@ -369,6 +385,7 @@ jobs:
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
needs:
- release-identity
- gate-windows
- gate-macos
permissions:
Expand Down
104 changes: 104 additions & 0 deletions scripts/checks/verify_release_identity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#!/usr/bin/env python3
"""Fail closed when BandScope release-version projections disagree."""

from __future__ import annotations

import json
import os
import sys
from pathlib import Path
from typing import Any

_REPOSITORY_ROOT = Path(__file__).resolve().parents[2]


def _read_json_object(metadata_path: Path) -> dict[str, Any]:
"""Read one release metadata document and require a JSON object root."""
try:
metadata_document = json.loads(metadata_path.read_text(encoding="utf-8"))
except (OSError, UnicodeError, json.JSONDecodeError) as metadata_error:
raise ValueError(
f"could not read release metadata: {metadata_path.name}"
) from metadata_error
if not isinstance(metadata_document, dict):
raise ValueError(f"release metadata must be an object: {metadata_path.name}")
return metadata_document


def _required_string(
metadata_document: dict[str, Any], field_name: str, source_name: str
) -> str:
"""Return a non-empty string field without coercing malformed metadata."""
field_value = metadata_document.get(field_name)
if (
not isinstance(field_value, str)
or not field_value.strip()
or field_value != field_value.strip()
):
raise ValueError(
f"{source_name} {field_name} must be a non-empty trimmed string"
)
return field_value


def verify_release_identity(
repository_root: Path, release_tag: str | None = None
) -> str:
"""Verify package, Tauri, and optional tag versions against ``VERSION``."""
try:
version_text = (repository_root / "VERSION").read_text(encoding="utf-8")
Comment thread
seonghobae marked this conversation as resolved.
Resolved
except (OSError, UnicodeError) as identity_error:
raise ValueError("could not read authoritative VERSION") from identity_error

version_lines = version_text.splitlines()
if (
len(version_lines) != 1
or not version_lines[0]
or version_lines[0] != version_lines[0].strip()
or version_text != f"{version_lines[0]}\n"
):
raise ValueError("VERSION must contain exactly one non-empty version line")
release_version = version_lines[0]

package_document = _read_json_object(repository_root / "package.json")
tauri_document = _read_json_object(
repository_root / "apps" / "desktop" / "src-tauri" / "tauri.conf.json"
)

package_version = _required_string(
package_document, "version", "package.json"
)
tauri_version = _required_string(
tauri_document, "version", "tauri.conf.json"
)
if package_version != release_version:
raise ValueError("package.json version does not match VERSION")
if tauri_version != release_version:
raise ValueError("tauri.conf.json version does not match VERSION")

if release_tag is not None and release_tag != f"v{release_version}":
raise ValueError("release tag does not match VERSION")

return release_version


def main() -> int:
"""Run the release identity gate for repository and tag-triggered workflows."""
release_tag = (
os.environ.get("GITHUB_REF_NAME")
if os.environ.get("GITHUB_REF_TYPE") == "tag"
else None
)
try:
release_version = verify_release_identity(
_REPOSITORY_ROOT, release_tag=release_tag
)
except ValueError as identity_error:
print(f"release identity check failed: {identity_error}", file=sys.stderr)
return 1
print(f"BandScope release identity verified: v{release_version}")
return 0


if __name__ == "__main__":
raise SystemExit(main())
1 change: 1 addition & 0 deletions scripts/harness/quickcheck.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ python3 scripts/checks/verify_security_notes.py
python3 scripts/checks/security_gates.py
python3 scripts/checks/verify_supply_chain.py
python3 scripts/checks/verify_github_bootstrap_policy.py
python3 scripts/checks/verify_release_identity.py
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
Resolved
npm run lint
npm run typecheck
npm run test
Expand Down
174 changes: 174 additions & 0 deletions services/analysis-engine/tests/test_release_version_identity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
"""Release identity contracts for the packaged BandScope desktop application."""

from __future__ import annotations

import importlib.util
import json
from pathlib import Path
from types import ModuleType

import pytest

_REPOSITORY_ROOT = Path(__file__).resolve().parents[3]
_GUARD_PATH = _REPOSITORY_ROOT / "scripts" / "checks" / "verify_release_identity.py"
_BUILD_BASELINE_PATH = _REPOSITORY_ROOT / ".github" / "workflows" / "build-baseline.yml"


def _load_guard() -> ModuleType:
"""Load the repository-owned release identity guard from its executable path."""
assert _GUARD_PATH.is_file(), "release preflight must own a version identity guard"
guard_module_spec = importlib.util.spec_from_file_location(
"verify_release_identity", _GUARD_PATH
)
assert guard_module_spec is not None and guard_module_spec.loader is not None
guard_module = importlib.util.module_from_spec(guard_module_spec)
guard_module_spec.loader.exec_module(guard_module)
return guard_module


def _write_release_metadata(repository_root: Path, release_version: str) -> None:
"""Write the minimum release metadata consumed by the identity guard."""
(repository_root / "apps" / "desktop" / "src-tauri").mkdir(parents=True)
(repository_root / "VERSION").write_text(
f"{release_version}\n", encoding="utf-8"
)
(repository_root / "package.json").write_text(
json.dumps({"name": "bandscope", "version": release_version}),
encoding="utf-8",
)
(
repository_root / "apps" / "desktop" / "src-tauri" / "tauri.conf.json"
).write_text(
json.dumps(
{
"productName": "BandScope",
"version": release_version,
"identifier": "com.bandscope.desktop",
}
),
encoding="utf-8",
)


def _workflow_job_block(workflow_text: str, job_name: str) -> str:
"""Return one top-level GitHub Actions job without requiring a YAML runtime dependency."""
job_marker = f" {job_name}:"
workflow_lines = workflow_text.splitlines()
try:
job_start_index = workflow_lines.index(job_marker)
except ValueError as lookup_error:
raise AssertionError(f"workflow job is missing: {job_name}") from lookup_error

job_end_index = len(workflow_lines)
for line_index in range(job_start_index + 1, len(workflow_lines)):
workflow_line = workflow_lines[line_index]
if (
workflow_line.startswith(" ")
and not workflow_line.startswith(" ")
and workflow_line.endswith(":")
):
job_end_index = line_index
break
return "\n".join(workflow_lines[job_start_index:job_end_index])


def test_release_preflight_executes_version_identity_guard() -> None:
"""Keep release preflight fail-closed when version projections drift."""
quickcheck_text = (
_REPOSITORY_ROOT / "scripts" / "harness" / "quickcheck.sh"
).read_text(encoding="utf-8")
release_workflow_text = (
_REPOSITORY_ROOT / ".github" / "workflows" / "release.yml"
).read_text(encoding="utf-8")

assert "python3 scripts/checks/verify_release_identity.py" in quickcheck_text
assert "./scripts/harness/quickcheck.sh" in release_workflow_text


def test_tag_build_and_publication_depend_on_release_identity_gate() -> None:
"""Block package construction and publication when release identity is invalid."""
build_workflow_text = _BUILD_BASELINE_PATH.read_text(encoding="utf-8")

identity_job = _workflow_job_block(build_workflow_text, "release-identity")
assert "run: python3 scripts/checks/verify_release_identity.py" in identity_job

for build_job_name in (
"build-windows-native",
"build-windows-arm64",
"build-macos-native",
"build-macos-arm64",
):
build_job = _workflow_job_block(build_workflow_text, build_job_name)
assert "needs: release-identity" in build_job

publication_job = _workflow_job_block(
build_workflow_text, "publish-immutable-release"
)
for required_job_name in ("release-identity", "gate-windows", "gate-macos"):
assert f" - {required_job_name}" in publication_job


def test_repository_release_version_matches_authoritative_version_file() -> None:
"""Verify checked-in projections without creating another version authority."""
release_guard = _load_guard()
version_text = (_REPOSITORY_ROOT / "VERSION").read_text(encoding="utf-8")
assert version_text.endswith("\n")
expected_version = version_text.removesuffix("\n")
assert "\n" not in expected_version
assert (
release_guard.verify_release_identity(_REPOSITORY_ROOT) == expected_version
)


def test_release_identity_guard_rejects_metadata_drift(tmp_path: Path) -> None:
"""Reject a package projection that diverges from the authoritative version."""
release_guard = _load_guard()
_write_release_metadata(tmp_path, "1.2.3")
package_document = json.loads(
(tmp_path / "package.json").read_text(encoding="utf-8")
)
package_document["version"] = "1.2.4"
(tmp_path / "package.json").write_text(
json.dumps(package_document), encoding="utf-8"
)

with pytest.raises(ValueError, match="package.json version does not match VERSION"):
release_guard.verify_release_identity(tmp_path)


def test_release_identity_guard_rejects_wrong_tag(tmp_path: Path) -> None:
"""Reject a version tag that does not identify the exact VERSION release."""
release_guard = _load_guard()
_write_release_metadata(tmp_path, "1.2.3")

with pytest.raises(ValueError, match="release tag does not match VERSION"):
release_guard.verify_release_identity(tmp_path, release_tag="v1.2.2")


def test_release_identity_guard_rejects_multiline_version_authority(tmp_path: Path) -> None:
"""Reject an ambiguous VERSION file even if projections repeat the same text."""
release_guard = _load_guard()
_write_release_metadata(tmp_path, "1.2.3")
ambiguous_version = "1.2.3\n2.0.0"
(tmp_path / "VERSION").write_text(
f"{ambiguous_version}\n", encoding="utf-8"
)
(tmp_path / "package.json").write_text(
json.dumps({"name": "bandscope", "version": ambiguous_version}),
encoding="utf-8",
)
(tmp_path / "apps" / "desktop" / "src-tauri" / "tauri.conf.json").write_text(
json.dumps(
{
"productName": "BandScope",
"version": ambiguous_version,
"identifier": "com.bandscope.desktop",
}
),
encoding="utf-8",
)

with pytest.raises(
ValueError, match="VERSION must contain exactly one non-empty version line"
):
release_guard.verify_release_identity(tmp_path)
Loading