diff --git a/docs/RELEASE-RUNBOOK.md b/docs/RELEASE-RUNBOOK.md index 7fc3fb3..d9b1d3d 100644 --- a/docs/RELEASE-RUNBOOK.md +++ b/docs/RELEASE-RUNBOOK.md @@ -1,53 +1,60 @@ # Release runbook `omp-spt` has one release topology: this repository builds one multi-platform `adapter.spt`, and the GitHub release for `BigscreenVR/omp-spt` publishes that file under the default asset name `adapter.spt`. The archive contains the complete adapter: ```text manifest.toml strings/omp-spt.mjs +strings/package.json +strings/skills/commune/SKILL.md +strings/skills/role/SKILL.md +strings/skills/setup/SKILL.md +strings/skills/signoff/SKILL.md x86_64-pc-windows-msvc/omp-spt.exe x86_64-unknown-linux-gnu/omp-spt +x86_64-unknown-linux-musl/omp-spt ``` spt-core keeps the shared root and flattens the current node's recognized target directory into the installed adapter directory. There are no per-platform release assets. Installing this one asset completes adapter installation; there is no secondary publication target or session reload action. -## v0.2.0 release-target limitations +## Release-target limitations -The v0.2.0 asset supports exactly **x86_64 Windows** (`x86_64-pc-windows-msvc`) and **x86_64 Linux** (`x86_64-unknown-linux-gnu`). It contains no macOS, Arm64, or other target payload. OMP itself may run on additional platforms, but that does not make the `omp-spt` release install usable there. Adding any target requires a real helper build, a recognized archive target, deterministic package validation, and tagged native acceptance before it can be advertised. + +The asset supports exactly three helper targets: **x86_64 Windows** (`x86_64-pc-windows-msvc`), **x86_64 GNU Linux** (`x86_64-unknown-linux-gnu`), and a static **x86_64 musl Linux** compatibility tier (`x86_64-unknown-linux-musl`). It contains no macOS, Arm64, or other target payload. The musl member exists for OMP-capable x86_64 Linux hosts where the GNU helper is incompatible; it does not claim that OMP runs on Alpine generally. Adding any target requires a real helper build, a recognized archive target, deterministic package validation, and tagged native acceptance before it can be advertised. User commands are: ```sh spt adapter add --release BigscreenVR/omp-spt spt adapter update omp-spt ``` ## Release prerequisites The release operator needs: - write access to `BigscreenVR/omp-spt` and an authenticated `gh` CLI; - Rust and Cargo for the `tools/omp-spt` crate; - Node.js for the native extension tests; -- Python with TOML and `jsonschema` support for manifest validation and stdlib `tarfile` support for authoring portable archive modes; +- Python with TOML and `jsonschema` support for manifest validation and stdlib `tarfile` support for target validation and reproducible portable archive authoring; - `traceable-reqs` for the required coverage gate; -- Zig plus `cargo-zigbuild` when cross-building the Linux binary from Windows; and -- disposable acceptance hosts for both `x86_64-pc-windows-msvc` and `x86_64-unknown-linux-gnu`, each with compatible `spt` and real `omp` installations. +- Zig plus `cargo-zigbuild` when cross-building the GNU and musl Linux helpers from Windows; and +- disposable native acceptance hosts for `x86_64-pc-windows-msvc`, `x86_64-unknown-linux-gnu`, and `x86_64-unknown-linux-musl`, each with compatible real `spt` and `omp` installations. The musl host must be representative of that target; a GNU host that happens to run the static helper does not qualify. -The release commit must contain the intended `adapter/omp-spt.toml`, `adapter/strings/omp-spt.mjs`, helper source, tests, and changelog. The tag tree is the release source of truth. +The release commit must contain the intended `adapter/omp-spt.toml`, exact `adapter/strings/` plugin payload, helper source, tests, and changelog. The tag tree is the release source of truth. ## 1. Set the version and release notes Set `[adapter].version` in `adapter/omp-spt.toml` to `X.Y.Z`. The Git tag and GitHub release tag must be `vX.Y.Z`; spt-core's `gh_release` updater resolves this conventional tag form. Add `## [X.Y.Z] - YYYY-MM-DD` at the top of the current `omp-spt` section in `CHANGELOG.md`. Release notes must describe user-visible changes, required spt-core or OMP version changes, breaking behavior, and recovery steps. Do not include requirement ids, source paths, test-machine details, commit hashes, or incident forensics. Each release-note paragraph, blockquote, and bullet should be one physical line so GitHub performs display wrapping. If needed, the repository includes the verified normalizer: ```sh python ci/publish/unwrap-changelog.py CHANGELOG.md ``` @@ -73,97 +80,128 @@ Use one shell for the remaining release commands. Pin the canonical tag name, co ```sh RELEASE_TAG=vX.Y.Z RELEASE_COMMIT=$(git rev-parse --verify HEAD^{commit}) || exit 1 RELEASE_TREE=$(git rev-parse --verify "$RELEASE_COMMIT^{tree}") || exit 1 test -z "$(git status --porcelain=v1)" || { echo "release tree is not clean and committed" >&2 exit 1 } ``` Record `RELEASE_COMMIT` and `RELEASE_TREE` with the release evidence. Do not build from a different checkout, amend the commit, or change `HEAD` after this point. Every later identity guard must pass in the same shell; otherwise discard the artifacts and restart from this section. -## 4. Build both platform binaries +## 4. Build all three target binaries -The verified packer requires exactly one Windows binary and one Linux binary. +The verified packer requires exactly one helper for each release target. Immediately before building, reassert the frozen clean source: ```sh test "$(git rev-parse --verify HEAD^{commit})" = "$RELEASE_COMMIT" test "$(git rev-parse --verify HEAD^{tree})" = "$RELEASE_TREE" test -z "$(git status --porcelain=v1)" ``` -Build and test the native Windows release binary: +Build and test the native Windows release helper: ```sh sh ci/digest/build.sh ``` This writes: ```text tools/omp-spt/target/release/omp-spt.exe ``` -Build the Linux release binary. On Windows, use Zig as the cross-linker: +Install both Linux Rust targets and the cross-linker once: ```sh rustup target add x86_64-unknown-linux-gnu +rustup target add x86_64-unknown-linux-musl cargo install cargo-zigbuild +``` + +Build the GNU helper: + +```sh cargo zigbuild --release \ --target x86_64-unknown-linux-gnu \ --manifest-path tools/omp-spt/Cargo.toml ``` -This writes: +Build the static musl helper from the same frozen source: + +```sh +cargo zigbuild --release \ + --target x86_64-unknown-linux-musl \ + --manifest-path tools/omp-spt/Cargo.toml +``` + +The Linux outputs are: ```text tools/omp-spt/target/x86_64-unknown-linux-gnu/release/omp-spt +tools/omp-spt/target/x86_64-unknown-linux-musl/release/omp-spt +``` + +Zig itself must be on `PATH`. Native Linux builders may produce the same target paths with a suitable Cargo linker. Do not copy or rename the GNU output into the musl path. + +Validate each built file before packaging: + +```sh +python ci/publish/validate-release-binary.py \ + x86_64-pc-windows-msvc \ + tools/omp-spt/target/release/omp-spt.exe +python ci/publish/validate-release-binary.py \ + x86_64-unknown-linux-gnu \ + tools/omp-spt/target/x86_64-unknown-linux-gnu/release/omp-spt +python ci/publish/validate-release-binary.py \ + x86_64-unknown-linux-musl \ + tools/omp-spt/target/x86_64-unknown-linux-musl/release/omp-spt ``` -`cargo install cargo-zigbuild` is one-time setup. Zig itself must also be on `PATH`. A native Linux builder may produce the same target path with its normal Cargo toolchain. +The validator requires PE32+ x86-64 for Windows, ELF64 x86-64 with the glibc x86-64 program interpreter for GNU, and ELF64 x86-64 with neither `PT_INTERP` nor `DT_NEEDED` for the static musl tier. A file with only `MZ` or ELF magic, a wrong architecture, a dynamic binary mislabeled as musl, or a renamed target output fails. ## 5. Pack the single release asset The repository packer is dry-run by default: Before either the dry-run or applied pack, repeat the `HEAD`, tree, and clean-status guards from section 4. A failed guard invalidates the input binaries; rebuild them after freezing a new release commit. ```sh sh ci/publish/package-adapter.sh ``` Review the plan, then create the artifact: ```sh sh ci/publish/package-adapter.sh --apply ``` The applied command writes only: ```text dist/adapter.spt ``` -`ci/publish/package-adapter.sh` validates the manifest, enforces a fat-archive-compatible spt-core floor, and requires exactly `adapter/strings/omp-spt.mjs` plus both platform binaries. It rejects a Windows payload without PE magic, a Linux payload without ELF magic, failed staging copies, a non-executable archived Linux member, and any archive member outside the exact manifest, extension, Windows binary, and Linux binary contract. Its Python archive writer authors the Linux member as mode `0755` even when packaging on MSYS/NTFS, then the packer validates that mode from the completed archive. It renames `adapter/omp-spt.toml` to root-level `manifest.toml`; it does not copy arbitrary files from `adapter/strings/`. Do not add another target directory or publish an alternate `.spt` asset without changing the spt-core archive contract first. +`ci/publish/package-adapter.sh` validates the manifest, enforces a fat-archive-compatible spt-core floor, and requires exactly the shared plugin files plus all three validated target helpers. It rejects missing, mislabeled, malformed, wrong-architecture, or dynamically linked musl input; a non-executable archived Linux member; any byte difference between source and archived payload; and any archive member outside the exact public contract. Its Python archive writer gives both Linux members mode `0755` even on MSYS/NTFS and fixes archive metadata from `SOURCE_DATE_EPOCH` (default `0`) for reproducible output. The packer renames `adapter/omp-spt.toml` to root-level `manifest.toml` and copies only the named `adapter/strings/` plugin files. For an independent listing check: ```sh tar -tzf dist/adapter.spt ``` -The listing must contain the four release files shown at the top of this runbook and no other files; normal directory entries emitted by `tar` are allowed. Retain the packer's validation output and the SHA-256 hashes of `dist/adapter.spt` and both input executables. +After normalizing directory-entry trailing slashes, the listing must contain exactly the ten release files shown at the top of this runbook and their required parent directories. Retain the packer's validation output and the SHA-256 hashes of `dist/adapter.spt` and all three input helpers. ## 6. Tag and stage the GitHub release Before tagging, prove that the working checkout is still the clean frozen source, create the canonical tag at that exact commit, and verify its identity: ```sh test "$(git rev-parse --verify HEAD^{commit})" = "$RELEASE_COMMIT" test "$(git rev-parse --verify HEAD^{tree})" = "$RELEASE_TREE" test -z "$(git status --porcelain=v1)" git tag "$RELEASE_TAG" "$RELEASE_COMMIT" test "$(git rev-parse --verify "$RELEASE_TAG^{commit}")" = "$RELEASE_COMMIT" test "$(git rev-parse --verify "$RELEASE_TAG^{tree}")" = "$RELEASE_TREE" @@ -171,66 +209,70 @@ git push origin "$RELEASE_TAG" ``` Create a tagged prerelease so the exact hosted asset can be acceptance-tested without making it the latest stable release. Immediately before upload, repeat the commit, tree, clean-status, and tag-identity guards; any failure means the asset must not be uploaded: ```sh test "$(git rev-parse --verify HEAD^{commit})" = "$RELEASE_COMMIT" test "$(git rev-parse --verify HEAD^{tree})" = "$RELEASE_TREE" test -z "$(git status --porcelain=v1)" test "$(git rev-parse --verify "$RELEASE_TAG^{commit}")" = "$RELEASE_COMMIT" test "$(git rev-parse --verify "$RELEASE_TAG^{tree}")" = "$RELEASE_TREE" sha256sum dist/adapter.spt \ tools/omp-spt/target/release/omp-spt.exe \ - tools/omp-spt/target/x86_64-unknown-linux-gnu/release/omp-spt + tools/omp-spt/target/x86_64-unknown-linux-gnu/release/omp-spt \ + tools/omp-spt/target/x86_64-unknown-linux-musl/release/omp-spt gh release create "$RELEASE_TAG" dist/adapter.spt \ --repo BigscreenVR/omp-spt \ --verify-tag \ --prerelease \ --latest=false \ --title "omp-spt vX.Y.Z" \ --notes-file ``` These guards tie the uploaded artifact's build session to the clean commit and tree named by `vX.Y.Z`. The uploaded file name must remain exactly `adapter.spt`. Do not upload per-platform variants or rebuild between hashing and upload. ## 7. Verify acquisition and native acceptance -Repeat this entire section independently on disposable `x86_64-pc-windows-msvc` and `x86_64-unknown-linux-gnu` acceptance hosts. First prove tagged acquisition, the exact installed version, and installed strings with the repository's opt-in integration: +Repeat this entire section independently on disposable native `x86_64-pc-windows-msvc`, `x86_64-unknown-linux-gnu`, and representative `x86_64-unknown-linux-musl` acceptance hosts. First prove tagged acquisition, the exact installed version, installed strings, and the target key used by the release record: ```sh OMP_SPT_ACCEPTANCE=1 \ +OMP_SPT_ACCEPTANCE_TARGET= \ OMP_SPT_RELEASE_TAG=vX.Y.Z \ OMP_SPT_EXPECTED_VERSION=X.Y.Z \ sh ci/publish/release-acquire-int.sh ``` -Both release variables are mandatory once acquisition is opted in. The integration rejects a tag other than `v$OMP_SPT_EXPECTED_VERSION`, downloads from `BigscreenVR/omp-spt`, verifies that `omp-spt` registers active from that repository's GitHub release directory, asserts `spt adapter version omp-spt` is exactly `X.Y.Z`, and resolves the packaged adapter label. Missing tools or GitHub authentication are failures after opt-in, not skips. The script temporarily removes the `omp-spt` registration, so do not run it on a node hosting an endpoint you need to preserve. +Use exactly one of the three target triples listed above. All three release variables are mandatory once acquisition is opted in. The integration rejects a tag other than `v$OMP_SPT_EXPECTED_VERSION`, downloads from `BigscreenVR/omp-spt`, verifies that `omp-spt` registers active from that repository's GitHub release directory, asserts `spt adapter version omp-spt` is exactly `X.Y.Z`, resolves the packaged adapter label, and prints the acceptance target into its success result. The target value is an evidence key, not host auto-detection: the operator remains responsible for running on the matching native host. Missing tools or GitHub authentication are failures after opt-in, not skips. The script temporarily removes the `omp-spt` registration, so do not run it on a node hosting an endpoint you need to preserve. + +On the musl acceptance host, first retain native `spt --version` and `omp --version` output and prove the real OMP application starts there. Do not use a GNU host merely because it can execute the static helper, and do not substitute an Alpine container where OMP itself is absent. The release claim is the static helper's representative-target compatibility, not generic Alpine support. Reinstall the tagged candidate on that same host, assert its version again, then complete every native endpoint check in `docs/CI.md`: ```sh spt adapter add --release BigscreenVR/omp-spt --tag vX.Y.Z spt adapter version omp-spt spt endpoint run --adapter omp-spt --id omp-spt-accept-fresh --create ``` -The version command must print exactly `X.Y.Z`. Release acceptance on each target requires evidence for tagged acquisition, fresh native bringup, attached OMP TUI, same-node message-to-turn-to-correlated-reply flow, idle/busy/idle state, blocked in-TUI session switching, native launch-time resume, graceful shutdown, and both ReadyAgent and LiveAgent behavior. Neither target can substitute for the other. +The version command must print exactly `X.Y.Z`. Release acceptance on each target requires evidence for tagged acquisition, fresh native bringup, attached OMP TUI, same-node message-to-turn-to-correlated-reply flow, idle/busy/idle lifecycle, blocked in-TUI session switching, native launch-time resume, graceful shutdown, and both ReadyAgent and LiveAgent behavior. No target can substitute for another. Cross-node transport is not an `omp-spt` release gate. Do not delay the adapter release on a subnet routing check after same-node delivery has proved the adapter boundary. -If any acquisition, version, or native acceptance item fails on either target, leave the release marked as a prerelease, fix the source, and cut a new version. Do not replace a tested tag or asset in place. +If any acquisition, target identification, version, or native acceptance item fails on any target, leave the release marked as a prerelease, fix the source, and cut a new version. Do not replace a tested tag or asset in place. ## 8. Promote and verify the default install path -Only after all tagged acquisition and native acceptance evidence passes on both supported x86_64 targets, promote the release: +Only after all tagged acquisition and native acceptance evidence passes on all three supported helper targets, promote the release: ```sh gh release edit "vX.Y.Z" \ --repo BigscreenVR/omp-spt \ --prerelease=false \ --latest ``` Verify that the latest release is the intended stable tag and has one uploaded release asset named `adapter.spt`: ```sh gh release view --repo BigscreenVR/omp-spt \ @@ -257,22 +299,35 @@ spt adapter version omp-spt The update must install `X.Y.Z` from the same default `adapter.spt`. A running endpoint keeps the extension and helper it loaded at bringup; restart it to exercise the new release: ```sh spt endpoint stop spt endpoint run --adapter omp-spt --id ``` The second command attaches the native OMP TUI by default. No additional installation source or reload command is part of the update. ## Release record +Author one JSON record per target using the contract in `docs-site/src/reference/release-evidence.md`, and keep its referenced acceptance logs beside it. Validate all three records together so the validator can reject duplicate targets or mixed source identities: + +```sh +python ci/release/validate-release-evidence.py \ + release/evidence-v1.schema.json adapter/omp-spt.toml \ + dist/evidence/release-evidence-vX.Y.Z-x86_64-pc-windows-msvc.json \ + dist/evidence/release-evidence-vX.Y.Z-x86_64-unknown-linux-gnu.json \ + dist/evidence/release-evidence-vX.Y.Z-x86_64-unknown-linux-musl.json +``` + +The command must print `RELEASE-EVIDENCE OK`. Schema success is necessary but does not manufacture evidence: populate each record only after that target's native acquisition and endpoint checks pass, then attach all three records and their referenced logs to the same GitHub release as `adapter.spt`. + Retain: - frozen release commit/tree and canonical `vX.Y.Z` tag identity; - deterministic gate outputs with no release-blocking skips; -- hashes for `dist/adapter.spt` and both input binaries; +- hashes for `dist/adapter.spt` and all three input helpers; - `gh release view` output showing stable tag `vX.Y.Z` and asset `adapter.spt`; -- tagged acquisition results with exact `X.Y.Z` version assertions on both supported x86_64 targets; -- the full per-target native acceptance evidence required by `docs/CI.md`; +- three machine-readable target records, one each for `x86_64-pc-windows-msvc`, `x86_64-unknown-linux-gnu`, and `x86_64-unknown-linux-musl`; +- tagged acquisition results with exact `X.Y.Z` version and exact-target assertions in each record; +- the full per-target native acceptance evidence required by `docs/CI.md`, including proof that the musl record came from a representative OMP-capable musl host; - the unpinned acquisition result; and - the `spt adapter update omp-spt` result and final reported version. [raw output: artifact://349]