carlos / platform Public

settings-2 task 3: a new head's promotion writes its signed record after authorization, before the pointer moves

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev fcaa86df37f923df447dd6867652260ba86fc627 parent fac0f53
3 files changed, +143 −0
  • internal/release/authorize_test.go +5 −0
  • internal/release/promote_record_test.go +120 −0
  • internal/release/release.go +18 −0
diff --git a/internal/release/authorize_test.go b/internal/release/authorize_test.go
index 5d5b78eba..faadaa20d 100644
--- a/internal/release/authorize_test.go
+++ b/internal/release/authorize_test.go
@@ -176,6 +176,11 @@ func TestAuthorizeSeesAPipelineInstalledBeforeTheAuthoritativeRead(t *testing.T)
if got := edgeVersion(t, ctx, inner, pub); got != "" {
t.Fatalf("edge = %q after a refusal", got)
}
+ // Astra I1 #1: the authoritative callback refused, so nothing may vouch
+ // for v1 either.
+ if _, err := ReadPromotionRecord(ctx, inner, pub, "bab", "myapp", "v1"); !errors.Is(err, ErrNoPromotionRecord) {
+ t.Fatalf("a refused promotion left a record: %v", err)
+ }
}
// Astra's round 2 counterexample: an owner promotes under a new pipeline
diff --git a/internal/release/promote_record_test.go b/internal/release/promote_record_test.go
new file mode 100644
index 000000000..cf6e55ae6
--- /dev/null
+++ b/internal/release/promote_record_test.go
@@ -0,0 +1,120 @@
+package release
+
+import (
+ "context"
+ "errors"
+ "strings"
+ "testing"
+ "time"
+)
+
+func TestPromoteWritesTheRecord(t *testing.T) {
+ s := NewDirStore(t.TempDir())
+ ctx := context.Background()
+ priv, pub := testKeys(t)
+ shipRelease(t, s, "bab", "km", "v1", adoptPlatform, []byte("one"))
+ if err := Promote(ctx, s, priv, "bab", "km", "stable", "v1", Promotion{Actor: "a@example.com"}); err != nil {
+ t.Fatal(err)
+ }
+ rec, err := ReadPromotionRecord(ctx, s, pub, "bab", "km", "v1")
+ if err != nil || rec.Channel != "stable" || rec.Actor != "a@example.com" || rec.ManifestDigest != manifestDigestOf(t, s, "bab", "km", "v1") {
+ t.Fatalf("record = %+v, %v", rec, err)
+ }
+}
+
+// Spec §8: a promoted build that has left the pointer's history is accepted
+// through its signed promotion record.
+func TestABuildOutOfHistoryIsStillAuthorized(t *testing.T) {
+ s := NewDirStore(t.TempDir())
+ ctx := context.Background()
+ priv, pub := testKeys(t)
+ for i := 0; i < historyMax+2; i++ {
+ v := "v" + string(rune('a'+i))
+ shipRelease(t, s, "bab", "km", v, adoptPlatform, []byte(v))
+ if err := Promote(ctx, s, priv, "bab", "km", "canary/x", v); err != nil {
+ t.Fatal(err)
+ }
+ }
+ p, _ := Channel(ctx, s, pub, "bab", "km", "canary/x")
+ for _, h := range append([]string{p.Version}, p.History...) {
+ if h == "va" {
+ t.Fatal("va should have fallen out of the pointer")
+ }
+ }
+ if _, err := AuthorizedManifest(ctx, s, pub, "bab", "km", "va"); err != nil {
+ t.Fatalf("va left the history and lost its authorization: %v", err)
+ }
+}
+
+// Review Focus 4.
+func TestOneRecordPerVersionAcrossChannels(t *testing.T) {
+ s := NewDirStore(t.TempDir())
+ ctx := context.Background()
+ priv, pub := testKeys(t)
+ shipRelease(t, s, "bab", "km", "v1", adoptPlatform, []byte("one"))
+ for _, ch := range []string{"canary/x", "stable"} {
+ if err := Promote(ctx, s, priv, "bab", "km", ch, "v1"); err != nil {
+ t.Fatal(err)
+ }
+ }
+ if rec, _ := ReadPromotionRecord(ctx, s, pub, "bab", "km", "v1"); rec.Channel != "canary/x" {
+ t.Fatalf("record channel %q; want the first promotion's", rec.Channel)
+ }
+}
+
+// Astra I1 #3: a repeat or a renewal of a legacy head never signs its
+// current bytes. Only `carlos releases authorize` does.
+func TestARepeatOrRetryNeverCreatesARecord(t *testing.T) {
+ s := NewDirStore(t.TempDir())
+ ctx := context.Background()
+ priv, pub := testKeys(t)
+ shipRelease(t, s, "bab", "km", "v1", adoptPlatform, []byte("one"))
+ if err := Promote(ctx, s, priv, "bab", "km", "canary/x", "v1"); err != nil {
+ t.Fatal(err)
+ }
+ if err := s.Delete(ctx, PromotionRecordKey("bab", "km", "v1")); err != nil { // a head promoted before records
+ t.Fatal(err)
+ }
+ res, err := PromoteOnLadder(ctx, s, priv, "bab", "km", "canary/x", "v1", PromoteOptions{})
+ if err != nil || !res.NoOp {
+ t.Fatalf("repeat = %+v, %v", res, err)
+ }
+ if _, err := ReadPromotionRecord(ctx, s, pub, "bab", "km", "v1"); !errors.Is(err, ErrNoPromotionRecord) {
+ t.Fatalf("a no-op repeat created a record: %v", err)
+ }
+ time.Sleep(1100 * time.Millisecond)
+ if _, err := PromoteOnLadder(ctx, s, priv, "bab", "km", "canary/x", "v1", PromoteOptions{Retry: true}); err != nil {
+ t.Fatal(err)
+ }
+ if _, err := ReadPromotionRecord(ctx, s, pub, "bab", "km", "v1"); !errors.Is(err, ErrNoPromotionRecord) {
+ t.Fatalf("a retry created a record: %v", err)
+ }
+}
+
+// refusingStore refuses writes under one prefix.
+type refusingStore struct {
+ Store
+ prefix string
+}
+
+func (r refusingStore) Put(ctx context.Context, key string, body []byte, ifMatch string) (string, error) {
+ if strings.HasPrefix(key, r.prefix) {
+ return "", errors.New("refused")
+ }
+ return r.Store.Put(ctx, key, body, ifMatch)
+}
+
+// Review Focus 1.
+func TestPromotionFailsBeforeThePointerWhenTheRecordCannotBeWritten(t *testing.T) {
+ base := NewDirStore(t.TempDir())
+ ctx := context.Background()
+ priv, pub := testKeys(t)
+ shipRelease(t, base, "bab", "km", "v1", adoptPlatform, []byte("one"))
+ s := refusingStore{Store: base, prefix: "releases/bab/km/promoted/"}
+ if err := Promote(ctx, s, priv, "bab", "km", "stable", "v1"); err == nil {
+ t.Fatal("the promotion succeeded without its record")
+ }
+ if _, err := Channel(ctx, base, pub, "bab", "km", "stable"); err == nil {
+ t.Fatal("the pointer moved although the record was not written")
+ }
+}
diff --git a/internal/release/release.go b/internal/release/release.go
index 97693744a..d77987961 100644
--- a/internal/release/release.go
+++ b/internal/release/release.go
@@ -876,6 +876,24 @@ func (sg Signer) claimRawPromote(ctx context.Context, s Store, acct, app, channe
return &AuthorizationError{Err: aerr}
}
}
+ // The durable record of this promotion (spec §4.1), from the bytes the
+ // publisher shipped: after every check that can refuse the write (the
+ // retry guard, the authoritative authorize callback), and BEFORE the
+ // pointer moves, so a refused promotion leaves nothing behind and a
+ // published head never lacks one. Only a NEW head is recorded: a renewal
+ // (--retry) keeps the head it found, and signing that head's current
+ // bytes here would be the automatic backfill §4.1 forbids. A legacy head
+ // is authorized only by `carlos releases authorize`.
+ if old.Version != version {
+ actor := ""
+ if len(rec) > 0 {
+ actor = rec[0].Actor
+ }
+ if err := sg.WritePromotionRecord(ctx, s, PromotionRecord{Account: acct, App: app, Version: version,
+ ManifestDigest: ManifestDigest(shippedRaw), Channel: channel, Actor: actor}); err != nil {
+ return fmt.Errorf("promote: write the promotion record for %s: %w", version, err)
+ }
+ }
next := &ChannelPointer{Account: acct, App: app, Channel: channel, Version: version,
History: old.History, Audit: old.Audit}
if old.Version != "" && old.Version != version {