carlos / platform Public

release: StageHeldVerified, T7's stage under the claim without the publication (moves Task 7, Q2.1)

Paul's T7 ruling (2026-10-12): under the claim, T7 stages the pinned
release into the shared version store and verifies it, and does not
publish live/<host>. StageHeldVerified runs MaterializeHeld's checks short
of the swap: D3's admit, §4.1's authorization and the digest-bound stage,
then D3's bind on the staged bytes. Like MaterializeHeldUnder it stages
outside any selection and does not consult CARLOS_HOST_CLAIM.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev b3d423390c5f733c31c4b06e6df5a72dfdb06f6d parent 0141cae
2 files changed, +153 −0
  • internal/release/materializeheld.go +44 −0
  • internal/release/materializeheld_claim_test.go +109 −0
diff --git a/internal/release/materializeheld.go b/internal/release/materializeheld.go
index b8efb7454..90b2da387 100644
--- a/internal/release/materializeheld.go
+++ b/internal/release/materializeheld.go
@@ -317,6 +317,50 @@ func StageHeld(ctx context.Context, s Store, pub ed25519.PublicKey, r *DigestRat
return stageHeld(ctx, s, pub, r, optDir, rt, version)
}
+// StageHeldVerified is T7 under the host claim (moves design §7.3 and §9.2
+// as amended on 2026-10-12, Paul's T7 ruling): the pinned version is staged
+// into the shared version store and verified, and live/<host> is never
+// touched. Its publication is the landing's, at configured, under the
+// held-release selection (MaterializeHeldUnder). As adoption does under the
+// claim, the stage runs outside any selection: the version store is shared
+// and a stage is no host's write. So, like MaterializeHeldUnder, it does not
+// consult CARLOS_HOST_CLAIM.
+//
+// Verified is everything MaterializeHeld checks short of the swap: D3's
+// admit before anything is staged (heldProofAdmit), §4.1's authorization
+// and the stage bound to its digest (stageHeld), then D3's bind on the
+// staged bytes, which hashes the version's own file and so needs no link.
+// A refusal of either D3 check is ErrHeldNotProven, and bind's is recorded
+// in the move's held namespace as the publication records it; a build §4.1
+// does not authorize is ErrHeldNotAuthorized, before anything is
+// downloaded; a version store this process cannot write is ErrStageBlocked
+// (there is no root relay under the claim). A nil proof is refused.
+func StageHeldVerified(ctx context.Context, s Store, pub ed25519.PublicKey, r *DigestRatchet, optDir string, rt registry.Route, version string, proof *ProofGate) (*Manifest, error) {
+ if err := heldShape(rt, version); err != nil {
+ return nil, err
+ }
+ if proof == nil {
+ return nil, errHeldNoProofGate
+ }
+ proof.BeginPass()
+ ptr, v, err := heldProofAdmit(ctx, s, optDir, rt, version, proof)
+ if err != nil {
+ return nil, err
+ }
+ m, err := stageHeld(ctx, s, pub, r, optDir, rt, version)
+ if err != nil {
+ return nil, err
+ }
+ cur, err := LiveVersion(optDir, rt.Host)
+ if err != nil {
+ return nil, err
+ }
+ if !proof.bindRecording(optDir, rt, ptr, v, cur, proof.now(), heldRefusalWriter(proof.bakeDir, rt.HoldOp)) {
+ return nil, fmt.Errorf("%w: %s at %s: its staged bytes are not the proven ones", ErrHeldNotProven, rt.Host, version)
+ }
+ return m, nil
+}
+
// stageHeld is every held stage's one staging step (StageHeld, and
// materializeHeld for MaterializeHeld and MaterializeHeldUnder): §4.1 first
// (HeldAuthorized, under pub and the box's ratchet r), then the stage bound
diff --git a/internal/release/materializeheld_claim_test.go b/internal/release/materializeheld_claim_test.go
index 0601dcafe..b0ec5b9a1 100644
--- a/internal/release/materializeheld_claim_test.go
+++ b/internal/release/materializeheld_claim_test.go
@@ -5,6 +5,7 @@ import (
"errors"
"os"
"strings"
+ "syscall"
"testing"
"github.com/carlosframework/platform/internal/hostwrite"
@@ -143,3 +144,111 @@ func heldClaimNothingStaged(t *testing.T, rec *heldRecordingStore, work *heldCla
t.Fatalf("live/<host> names %q", got)
}
}
+
+// TestStageHeldVerifiedStagesWithoutPublishing (moves after claim Plan 12,
+// Task 7; Paul's T7 ruling of 2026-10-12): under the claim, T7 stages the
+// pinned release into the shared version store and verifies it, §4.1's
+// authorization and D3's admit before the stage and D3's bind on the staged
+// bytes after it, and never touches live/<host>: the publication is the
+// landing's, at configured, under its held-release selection.
+func TestStageHeldVerifiedStagesWithoutPublishing(t *testing.T) {
+ t.Run("staged, live untouched", func(t *testing.T) {
+ ctx, s, opt := heldFixture(t)
+ t.Setenv("CARLOS_HOST_CLAIM", "1")
+ m, err := StageHeldVerified(ctx, s, heldPub, nr(t), opt, heldRoute(), "v1", offGate(t))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if m == nil || m.Version != "v1" {
+ t.Fatalf("manifest %+v", m)
+ }
+ if _, err := os.Stat(VersionPath(opt, "bab", "v1", "console")); err != nil {
+ t.Fatalf("v1 was not staged: %v", err)
+ }
+ if _, err := os.Lstat(LiveLink(opt, heldTestHost)); !os.IsNotExist(err) {
+ t.Fatalf("live/<host> was written: %v", err)
+ }
+ // Again: the stage is idempotent, and still publishes nothing.
+ if _, err := StageHeldVerified(ctx, s, heldPub, nr(t), opt, heldRoute(), "v1", offGate(t)); err != nil {
+ t.Fatal(err)
+ }
+ if _, err := os.Lstat(LiveLink(opt, heldTestHost)); !os.IsNotExist(err) {
+ t.Fatalf("live/<host> was written: %v", err)
+ }
+ })
+ t.Run("proven, live left on the earlier build", func(t *testing.T) {
+ e := newHeldProofEnv(t)
+ t.Setenv("CARLOS_HOST_CLAIM", "1")
+ e.require(t, "console")
+ e.prove(t, e.pinnedSHA)
+ if _, err := StageHeldVerified(e.ctx, e.store, heldPub, nr(t), e.opt, heldRoute(), "v1", e.gate); err != nil {
+ t.Fatal(err)
+ }
+ if _, err := os.Stat(VersionPath(e.opt, "bab", "v1", "console")); err != nil || e.live() != "v0" {
+ t.Fatalf("staged %v, live %q; want v1 staged and v0 live", err, e.live())
+ }
+ })
+ t.Run("not proven", func(t *testing.T) {
+ e := newHeldProofEnv(t)
+ t.Setenv("CARLOS_HOST_CLAIM", "1")
+ e.require(t, "console")
+ if _, err := StageHeldVerified(e.ctx, e.store, heldPub, nr(t), e.opt, heldRoute(), "v1", e.gate); !errors.Is(err, ErrHeldNotProven) {
+ t.Fatalf("an unproven release: %v", err)
+ }
+ if _, err := os.Stat(VersionPath(e.opt, "bab", "v1", "console")); err == nil || e.live() != "v0" {
+ t.Fatalf("an unproven release was staged (%v) or live moved (%q)", err, e.live())
+ }
+ })
+ t.Run("the staged bytes are not the proven ones", func(t *testing.T) {
+ e := newHeldProofEnv(t)
+ t.Setenv("CARLOS_HOST_CLAIM", "1")
+ e.require(t, "console")
+ e.prove(t, e.pinnedSHA)
+ stale := []byte("not the shipped bytes")
+ down := e.staleCopyDown(t, stale)
+ if _, err := StageHeldVerified(e.ctx, down, heldPub, nr(t), e.opt, heldRoute(), "v1", e.gate); !errors.Is(err, ErrHeldNotProven) || e.live() != "v0" {
+ t.Fatalf("other staged bytes: %v, live %q", err, e.live())
+ }
+ r, _ := ReadRefusal(HeldRefusalBase(e.bake), heldTestHost)
+ if r == nil || r.Kind != RefusalNotProven || r.MoveOp != heldRoute().HoldOp || !strings.Contains(r.Reason, etagOf(stale)) {
+ t.Fatalf("refusal = %+v, want the move's not-proven record naming the staged hash", r)
+ }
+ })
+ t.Run("not authorized", func(t *testing.T) {
+ ctx, s, opt := heldFixture(t)
+ t.Setenv("CARLOS_HOST_CLAIM", "1")
+ shipRelease(t, s, "bab", "console", "v3", adoptPlatform, []byte("shipped, never promoted"))
+ if _, err := StageHeldVerified(ctx, s, heldPub, nr(t), opt, heldRoute(), "v3", offGate(t)); !errors.Is(err, ErrHeldNotAuthorized) {
+ t.Fatalf("a never-promoted build: %v", err)
+ }
+ if _, err := os.Stat(VersionPath(opt, "bab", "v3", "console")); err == nil {
+ t.Fatal("an unauthorized build was staged")
+ }
+ })
+ t.Run("blocked", func(t *testing.T) {
+ ctx, s, opt := heldFixture(t)
+ t.Setenv("CARLOS_HOST_CLAIM", "1")
+ orig := materializeHeldStage
+ t.Cleanup(func() { materializeHeldStage = orig })
+ materializeHeldStage = func(context.Context, Store, string, string, string, string, string, string) (*Manifest, error) {
+ return nil, &os.PathError{Op: "mkdir", Path: opt, Err: syscall.EROFS}
+ }
+ if _, err := StageHeldVerified(ctx, s, heldPub, nr(t), opt, heldRoute(), "v1", offGate(t)); !errors.Is(err, ErrStageBlocked) {
+ t.Fatalf("a read-only version store: %v", err)
+ }
+ })
+ t.Run("refusals", func(t *testing.T) {
+ ctx, s, opt := heldFixture(t)
+ if _, err := StageHeldVerified(ctx, s, heldPub, nr(t), opt, heldRoute(), "v1", nil); !errors.Is(err, errHeldNoProofGate) {
+ t.Fatalf("no gate: %v", err)
+ }
+ noOp := heldRoute()
+ noOp.HoldOp = ""
+ if _, err := StageHeldVerified(ctx, s, heldPub, nr(t), opt, noOp, "v1", offGate(t)); err == nil {
+ t.Fatal("a row no move holds was staged")
+ }
+ if _, err := os.Stat(VersionPath(opt, "bab", "v1", "console")); err == nil {
+ t.Fatal("a refused stage staged v1")
+ }
+ })
+}