pmacs/.github
Levi Neuwirth 84b1620e7e
feat(release): binaries on tag — Distribution Stage 1
.github/workflows/ had exactly one workflow and it was test-only: no
release job, no artifact upload, no tags-to-binaries path. Installing
pmacs meant `git clone` plus knowing the feature-flag matrix.
COHERENCE.md §17 grades this "missing — zero release machinery exists";
this moves it to Partial and completes journey step 1.

Scope is one stage: binaries when a `v*` tag is pushed, attached to a
GitHub Release. Channels, rollback, update-in-place, signing, RHEL 9 and
Intel macOS are out of scope and named in the framing's §5.

WHAT SHIPS: pmacs and pmacs-gpu, both at 1.1.0, CRDT-enabled, co-located
in one archive, with SHA256SUMS. pmacs-protocol stays at 1.0.0 — it is
the wire crate and versions on its own schedule.

THE VERSION BUMP EXPOSED A REAL DEFECT, and it is the reason this PR
touches src/ at all. `InstanceIdentity::for_running_process` is defined
in pmacs-protocol and expanded `env!("CARGO_PKG_VERSION")` THERE. `env!`
expands in the crate being compiled, so the field documented as "Pmacs
version string" carried the PROTOCOL crate's version. That identity
reaches Lua as `pmacs.instance.identity()` and goes on the wire in
`Hello`, so a 1.1.0 release would have told every attached frontend it
was 1.0.0.

Nothing could have caught it earlier. Three tests assert
`id.pmacs_version == env!("CARGO_PKG_VERSION")` evaluated in the pmacs
crate — the correct assertion — but while both crates read 1.0.0 they
compared the same number reached by two different paths and COULD NOT
FAIL. Deciding to hold pmacs-protocol at 1.0.0 while moving pmacs is
what made them discriminating; all three failed on the bump. The version
is now a parameter so `env!` expands in the caller's crate. A test can
be correct and still prove nothing when the two things it compares are
equal for a reason unrelated to the code under test.

TWO LAYERS OF BINARY EXCLUSION, and layer 2 is load-bearing —
demonstrated, not argued. Cargo auto-discovers src/bin/*.rs, so a
release build can produce five binaries and three must never ship
(pmacs-audit is a contributor tool; pmacs_fake_lsp and pmacs_fake_mcp
are test fixtures). Layer 1 names explicit --bin targets. Layer 2 stages
an explicit asset list, and building this branch produced exactly the
case it guards: after building ONLY --bin pmacs and -p pmacs-gpu,
target/release still held all three forbidden binaries, left by an
earlier `cargo test --release`. Swatinem/rust-cache restores that kind
of directory in CI. An implementation trusting layer 1 and archiving the
directory would have published a fake language server in the first
release.

The three archive assertions are bite-verified: a smuggled
pmacs_fake_lsp, a missing pmacs-gpu, and a cleared executable bit are
each caught, with the honest archive passing.

THE GLIBC FLOOR IS ASSERTED, NOT TRUSTED. Pinning ubuntu-22.04 sets the
floor at 2.35 (Ubuntu 22.04, Debian 12 — NOT RHEL 9 at 2.34, which needs
a container or cross-build and is parked). But a pinned runner proves
nothing about the artifact, and the failure surfaces as a bare
`GLIBC_2.39 not found` on a user's machine with no clue which commit
caused it. The build reads versioned-symbol requirements out of the
binary and fails above the floor, so switching to ubuntu-latest fails in
CI instead of shipping. Bite-verified both directions on a glibc 2.44
host. Both runners are pinned; macos-latest would drift the minimum
supported macOS with no commit to point at.

Preflight runs before any build: the tag must match the root crate
version (stripping a prerelease suffix, so v1.1.0-rc.1 and v1.1.0 both
match 1.1.0), and the tagged commit must be an ancestor of main. Both
catch mistakes that are cheap now and expensive once a public URL
exists. The suite is not re-run — CI already tested the commit — but
nothing otherwise enforced that a tag points at a tested one.

Verified: fmt, diff-check, clippy with and without crdt, --lib 1896,
--lib --features crdt 2081, pmacs-protocol 19, m4 149, required GPU 221,
and the full serialized crdt sweep at 3,715 passed / 0 failed / 30
ignored — identical to the pre-change baseline, so the protocol
signature change broke nothing. Archive staging, contents, executable
bits and both --version outputs were exercised against a real release
build locally.

No release is cut by this PR. Per the framing's §7 the RC is tagged
after merge, from the merge SHA.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 14:40:47 -04:00
..
workflows feat(release): binaries on tag — Distribution Stage 1 2026-08-01 14:40:47 -04:00