carlos / platform Public

host: C8 proves origins through claim Plan 15's batch ClaimOriginProof (Plan 15 switch)

chainOriginProof (the retained admission chain) is gone. movesOriginProof
is now the batch contract Plan 15's ClaimOriginProof implements:
Proven(ctx, observation, admission, generations) (map, error), which
authenticates the bound observation against the stored claim, walks the
claim's recorded admissions once from its committed head, stops as soon
as every wanted generation is proven, bounds each node read at 4096
bytes, caches per store, and takes home aliases only through the
verified roster binding. The chain starts empty: nothing is seeded from
retained admissions, which are never evidence, and a claim with no
recorded head proves nothing, so every existing host refuses until the
#250 files backfill has run. That is intended.

proveOrigins is the one route: one Proven(set) call per establishment
over the deduplicated referenced origins plus the admitted generation
(Task 6b's set), no per-origin calls; a false answer is ErrOrigin, an
error is the error, and no proof wired refuses. The production source
binds ClaimOriginProof over the deployment store with the actor's home
alias and this box's engine region. Nothing else changes: the
src_gen bridge, the prepared replay, the carried set and the no-mint
rule stand.

Tests: the Establish fixtures hold a claims store under both switches,
observe hand-built claims as stored observations, and record admitted
origins into the head as the claim's CAS and the #250 backfill do
(recordOrigins); the probe and recovery fixtures run the real proof
over their claim stores, their real admissions recording as they go,
with src recorded for the clean kinds. A pruned or retained-only origin
refuses; origins the claim records pass; a never-recorded origin
refuses; a free-standing generation the observation does not record
refuses at the proof and by Establish; a counting witness shows one
Proven(set) call per Establish over the referenced origins and the
admitted generation; the real-C8 recovery, restart and no-mint tests
stay green. Plan 15's test-only singleton bridge is retired.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 7ccb691acd637ab3dd53b0f603c6b78f5e24e93d parent 01edf90
6 files changed, +297 −193
  • internal/host/claimfiles.go +7 −3
  • internal/host/movesfiles.go +62 −86
  • internal/host/movesfiles_test.go +210 −83
  • internal/host/movesprobeowners_test.go +10 −1
  • internal/host/movesrecoveryowners_test.go +5 −0
  • internal/host/plan15_originproof_test.go +3 −20
diff --git a/internal/host/claimfiles.go b/internal/host/claimfiles.go
index 153ddc682..eb007e325 100644
--- a/internal/host/claimfiles.go
+++ b/internal/host/claimfiles.go
@@ -116,16 +116,20 @@ func movesFilesDirFor(dataDir string) func(host string) (string, string) {
}
}
-func (s *movesFilesSource) Open(ctx context.Context, _ release.Store, data lease.VersionedStore, actor hostbox.Actor) (ClaimFilesManifest, error) {
+func (s *movesFilesSource) Open(ctx context.Context, claims release.Store, data lease.VersionedStore, actor hostbox.Actor) (ClaimFilesManifest, error) {
region := park.PlacementRegion(actor.Region)
- if s == nil || s.DataDir == "" || s.newFiles == nil || data == nil || actor.Bucket == "" || actor.Box == "" || region == "" || actor.Cloud.Region == "" {
+ if s == nil || s.DataDir == "" || s.newFiles == nil || claims == nil || data == nil || actor.Bucket == "" || actor.Box == "" || region == "" || actor.Cloud.Region == "" {
return nil, hostwrite.ErrUnavailable
}
files, err := s.newFiles(ctx, actor.Bucket, actor.Cloud.Region)
if err != nil {
return nil, err
}
- return &movesFiles{Files: files, Data: data, Bucket: actor.Bucket, HomeRegion: actor.HomeRegion, Region: region, Box: actor.Box, DataDir: s.DataDir, FilesDir: movesFilesDirFor(s.DataDir)}, nil
+ // The origin proof is claim Plan 15's, over the deployment store the
+ // claims live in, with the home alias from the verified roster binding
+ // (actor.HomeRegion) and this box's engine region.
+ proof := ClaimOriginProof{Claims: claims, HomeRegion: actor.HomeRegion, Region: region}
+ return &movesFiles{Files: files, Data: data, Bucket: actor.Bucket, HomeRegion: actor.HomeRegion, Region: region, Box: actor.Box, DataDir: s.DataDir, FilesDir: movesFilesDirFor(s.DataDir), Proof: proof}, nil
}
func (s *movesFilesSource) Close(context.Context) error { return nil }
diff --git a/internal/host/movesfiles.go b/internal/host/movesfiles.go
index d694da7b3..e05f9f0e8 100644
--- a/internal/host/movesfiles.go
+++ b/internal/host/movesfiles.go
@@ -13,7 +13,6 @@ import (
"os"
"path/filepath"
"reflect"
- "slices"
"sort"
"strconv"
"strings"
@@ -78,10 +77,11 @@ type movesFiles struct {
// FilesDir is the host's files directory and env file on this box
// (moveCfg.FilesDir).
FilesDir func(host string) (dir, envPath string)
- // Proof proves an origin against admissions the claim has recorded
- // (platform-1e's ruling, fix round 3): nil means chainOriginProof, the
- // retained admission chain, until claim Plan 15's durable
- // per-incarnation record slots in behind movesOriginProof.
+ // Proof proves origins against admissions the claim has recorded
+ // (platform-1e's ruling, fix round 3; claim Plan 15): in production
+ // ClaimOriginProof over the deployment store and the verified home
+ // alias, one batch call per establishment. nil refuses: there is no
+ // other route, and retained admissions are never evidence.
Proof movesOriginProof
// Evidence reads the claim pin's files evidence for a host, when the
// claim carries a pin: region HA's ReadPin over the op journal's pin
@@ -450,74 +450,60 @@ func (f *movesFiles) newestManifest(ctx context.Context, o hostclaim.Observed, i
return hostfiles.Manifest{}, nil, false, nil
}
-// movesOriginProof proves that a generation of the holder's incarnation
-// was admitted, by the claim's own records (platform-1e's ruling, fix
-// round 3): provenance comes from the claim, never from a manifest, whose
-// writer holds the bucket credentials that could write its Origins too.
-// Today's implementation is chainOriginProof, the retained admission
-// chain; claim Plan 15's durable per-incarnation record (platform-1e's,
-// with a read API) slots in behind this interface. Anything unprovable
-// refuses: a host past the pruning window refuses until Plan 15 lands,
-// and that is accepted.
+// movesOriginProof proves that generations of the holder's incarnation
+// were admitted, by the claim's own records (platform-1e's ruling, fix
+// round 3; claim Plan 15): provenance comes from the claim, never from a
+// manifest, whose writer holds the bucket credentials that could write
+// its Origins too. The implementation is Plan 15's ClaimOriginProof: a
+// batch Proven over the whole wanted set, which authenticates the bound
+// observation against the stored claim (the CAS result the caller holds,
+// never a bare Admission.NewGeneration), walks the claim's recorded
+// admissions once from its committed head, stops as soon as every wanted
+// generation is proven, bounds each node read, and takes home aliases only
+// through the verified roster binding. The chain starts empty: nothing is
+// seeded from retained admissions, which are never evidence, and a claim
+// with no recorded head proves nothing, so every existing host refuses
+// until the #250 files backfill has run. That is intended. A false answer
+// is ErrOrigin; an error is the error.
type movesOriginProof interface {
- Proven(ctx context.Context, o hostclaim.Observed, in hostclaim.Admission, generation string) (bool, error)
+ Proven(ctx context.Context, o hostclaim.Observed, in hostclaim.Admission, generations []string) (map[string]bool, error)
}
-// chainOriginProof proves a generation by the retained admission chain of
-// the bound observation alone (movesFilesChain: the observed holder's
-// generation and each cold admission's predecessor in this region). It
-// never accepts a bare Admission.NewGeneration supplied on its own
-// (platform-1e's condition, fix round 4): the generation an admission
-// opens is proven only once the observation the caller holds records it,
-// which is the observation Materialize is given (the CAS result
-// Writer.Admit returns, BindWakeRetrySession's result, or the resumed
-// recovery's current observation checked against its journal), where the
-// holder's generation is that one. A free-standing generation with no such
-// observation is not proven.
-type chainOriginProof struct{ home, region string }
-
-func (p chainOriginProof) Proven(_ context.Context, o hostclaim.Observed, _ hostclaim.Admission, generation string) (bool, error) {
- if generation == "" || o.Absent || o.Claim == nil || o.Claim.Holder == nil {
- return false, nil
- }
- if slices.Contains(movesFilesChain(o.Claim.Holder, p.home, p.region), generation) {
- return true, nil
- }
- // A recovery's source generation (item 3): the claim journalled it at
- // the flip, and the recovery restores from it; the chain through its
- // own admissions follows.
- if src, ok := recoverySourceGeneration(o.Claim); ok && src != "" {
- return slices.Contains(movesFilesChainFrom(o.Claim.Holder, src, p.home, p.region), generation), nil
- }
- return false, nil
-}
-
-func (f *movesFiles) proof() movesOriginProof {
- if f.Proof != nil {
- return f.Proof
+// proveOrigins is the one route to origin proof (fix round 4; Plan 15):
+// one Proven call over the deduplicated set of generations, and a
+// generation's files prefix under location enters the allowed set only
+// when that call proves it. A generation the proof does not prove is
+// ErrOrigin, naming the first; an error is the error; no proof wired
+// refuses. No per-origin calls (Plan 15's review: one walk per
+// establishment).
+func (f *movesFiles) proveOrigins(ctx context.Context, o hostclaim.Observed, in hostclaim.Admission, location string, generations []string) ([]string, error) {
+ if f.Proof == nil {
+ return nil, fmt.Errorf("%w: no origin proof is wired", hostwrite.ErrUnavailable)
}
- return chainOriginProof{home: f.HomeRegion, region: f.Region}
-}
-
-// proveOrigin is the one route to origin proof (fix round 4): the
-// generation's files prefix under location enters an allowed set only
-// when movesOriginProof proves the generation. A proof that answers false
-// is ErrOrigin; an error is the error. Nothing enters a set any other way,
-// so a different proof (claim Plan 15's) has the final say over the
-// chain's generations and the prepared generation as over every other.
-func (f *movesFiles) proveOrigin(ctx context.Context, o hostclaim.Observed, in hostclaim.Admission, location, gen string) (string, error) {
- if !movesFilesGeneration(gen) {
- return "", fmt.Errorf("%w: %q is not a generation name", hostfiles.ErrOrigin, gen)
+ set := make([]string, 0, len(generations))
+ seen := map[string]bool{}
+ for _, gen := range generations {
+ if !movesFilesGeneration(gen) {
+ return nil, fmt.Errorf("%w: %q is not a generation name", hostfiles.ErrOrigin, gen)
+ }
+ if !seen[gen] {
+ seen[gen] = true
+ set = append(set, gen)
+ }
}
- origin := movesFilesPrefix(location, gen)
- proven, err := f.proof().Proven(ctx, o, in, gen)
+ sort.Strings(set)
+ proven, err := f.Proof.Proven(ctx, o, in, set)
if err != nil {
- return "", fmt.Errorf("prove the origin %s: %w", origin, err)
+ return nil, fmt.Errorf("prove the origins of %s: %w", location, err)
}
- if !proven {
- return "", fmt.Errorf("%w: %s is not proven by the claim's recorded admissions", hostfiles.ErrOrigin, origin)
+ origins := make([]string, 0, len(set))
+ for _, gen := range set {
+ if !proven[gen] {
+ return nil, fmt.Errorf("%w: %s is not proven by the claim's recorded admissions", hostfiles.ErrOrigin, movesFilesPrefix(location, gen))
+ }
+ origins = append(origins, movesFilesPrefix(location, gen))
}
- return origin, nil
+ return origins, nil
}
// movesFilesAllowed is the origin set an admission allows for m, the
@@ -537,36 +523,26 @@ func (f *movesFiles) proveOrigin(ctx context.Context, o hostclaim.Observed, in h
// before; only unreferenced hints and unreferenced chain generations, which
// no entry depends on, are no longer asked about.
func (f *movesFiles) movesFilesAllowed(ctx context.Context, o hostclaim.Observed, in hostclaim.Admission, location, admitted string, m hostfiles.Manifest) ([]string, error) {
- set := map[string]bool{}
- own, err := f.proveOrigin(ctx, o, in, location, admitted)
- if err != nil {
- return nil, err
- }
- set[own] = true
named, err := movesFilesOrigins(location, "", m)
if err != nil {
return nil, err
}
base := strings.TrimSuffix(location, "/") + "/files/g-"
+ generations := []string{admitted}
for _, origin := range named {
- if set[origin] {
- continue
- }
rest, under := strings.CutPrefix(origin, base)
gen, slash := strings.CutSuffix(rest, "/")
if !under || !slash || !movesFilesGeneration(gen) {
return nil, fmt.Errorf("%w: %s is not a generation's files prefix of %s", hostfiles.ErrOrigin, origin, location)
}
- if _, err := f.proveOrigin(ctx, o, in, location, gen); err != nil {
- return nil, err
- }
- set[origin] = true
+ generations = append(generations, gen)
}
- allowed := make([]string, 0, len(set))
- for origin := range set {
- allowed = append(allowed, origin)
+ // One Proven call over the deduplicated referenced origins plus the
+ // admitted generation (Plan 15).
+ allowed, err := f.proveOrigins(ctx, o, in, location, generations)
+ if err != nil {
+ return nil, err
}
- sort.Strings(allowed)
if err := hostfiles.ValidateManifest(m, allowed); err != nil {
return nil, err
}
@@ -898,7 +874,7 @@ func (f *movesFiles) Establish(ctx context.Context, o hostclaim.Observed, in Cla
return err
}
case in.Empty:
- own, err := f.proveOrigin(ctx, o, in.Admission, h.Location, gen)
+ own, err := f.proveOrigins(ctx, o, in.Admission, h.Location, []string{gen})
if err != nil {
return err
}
@@ -906,7 +882,7 @@ func (f *movesFiles) Establish(ctx context.Context, o hostclaim.Observed, in Cla
if err != nil {
return err
}
- p.SetOrigins([]string{own})
+ p.SetOrigins(own)
default:
// The chain is the holder's: the newest authorized manifest through
// it wins, a recovery's own included (the prepared rollback
@@ -940,11 +916,11 @@ func (f *movesFiles) Establish(ctx context.Context, o hostclaim.Observed, in Cla
if !never {
return fmt.Errorf("%w: no files manifest in the admitted predecessor chain of %s (%s)", hostfiles.ErrNoEvidence, o.Host, h.Generation)
}
- own, err := f.proveOrigin(ctx, o, in.Admission, h.Location, gen)
+ own, err := f.proveOrigins(ctx, o, in.Admission, h.Location, []string{gen})
if err != nil {
return err
}
- base, origins = emptyManifest(), []string{own}
+ base, origins = emptyManifest(), own
}
p, err = hostfiles.NewFromCopy(files, prefix, base, origins)
if err != nil {
diff --git a/internal/host/movesfiles_test.go b/internal/host/movesfiles_test.go
index 1086feb07..067e1cae9 100644
--- a/internal/host/movesfiles_test.go
+++ b/internal/host/movesfiles_test.go
@@ -10,7 +10,6 @@ import (
"os"
"path/filepath"
"reflect"
- "sort"
"strings"
"sync"
"testing"
@@ -95,6 +94,7 @@ func (r *recordingReader) GetVersion(ctx context.Context, bucket, key, version s
type filesFixture struct {
t *testing.T
+ claims release.Store
bucket *leasetest.VersionedBucket
data *recordingReader
files casBucketStore
@@ -111,9 +111,21 @@ type filesFixture struct {
workErrAt int
}
-const (
- filesHost = "shop.example.com"
- filesLoc = "instances/" + filesHost + "/" + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+const filesHost = "shop.example.com"
+
+// filesCreated and filesInc are the fixture host's creation and incarnation
+// (hostclaim.Incarnation), so its location is a valid origin tuple
+// (instances/<host>/<incarnation>) for claim Plan 15's proof.
+var (
+ filesCreated = time.Date(2026, 10, 1, 0, 0, 0, 0, time.UTC)
+ filesInc = func() string {
+ inc, err := hostclaim.Incarnation("bab", "jam", filesHost, filesCreated)
+ if err != nil {
+ panic(err)
+ }
+ return inc
+ }()
+ filesLoc = "instances/" + filesHost + "/" + filesInc
)
func (x *filesFixture) With(_ context.Context, _ hostwrite.Identity, fn func() error) error {
@@ -128,10 +140,14 @@ func (x *filesFixture) With(_ context.Context, _ hostwrite.Identity, fn func() e
func newFilesFixture(t *testing.T) *filesFixture {
t.Helper()
+ // Claim Plan 15: the proof reads the claim's recorded admissions from
+ // the claims store under both switches.
+ t.Setenv("CARLOS_HOST_CLAIM", "1")
+ t.Setenv("CARLOS_HOST_CLAIM_ORIGINS", "1")
b := leasetest.NewVersionedBucket()
b.AddBucket("data")
- x := &filesFixture{t: t, bucket: b, data: &recordingReader{VersionedStore: b}, files: casBucketStore{b: b, bucket: "data"}, dir: t.TempDir()}
- x.f = &movesFiles{Files: x.files, Data: x.data, Bucket: "data", HomeRegion: "ie", Region: "uk", Box: "uk-box", DataDir: x.dir,
+ x := &filesFixture{t: t, claims: release.NewDirStore(t.TempDir()), bucket: b, data: &recordingReader{VersionedStore: b}, files: casBucketStore{b: b, bucket: "data"}, dir: t.TempDir()}
+ x.f = &movesFiles{Files: x.files, Data: x.data, Bucket: "data", HomeRegion: "ie", Region: "uk", Box: "uk-box", DataDir: x.dir, Proof: ClaimOriginProof{Claims: x.claims, HomeRegion: "ie", Region: "uk"},
FilesDir: func(host string) (string, string) {
return filepath.Join(x.dir, host), filepath.Join(x.dir, host+".env")
}}
@@ -175,16 +191,123 @@ func (x *filesFixture) manifest(gen string) (hostfiles.Manifest, bool) {
return m, found
}
-// serving is a serving claim on uk-box whose holder is at gen with the
-// cold admission chain given, newest last: each entry is
-// "<generation>:<region>:<predecessor>:<predecessor region>".
+// servingObserved is a serving claim on uk-box whose holder is at gen with
+// the cold admission chain given, newest last: each entry is
+// "<generation>:<region>:<predecessor>:<predecessor region>". It is a
+// hand-built claim: nothing in it is proven until observe stores it with
+// recorded admissions (recordOrigins).
func servingObserved(gen string, chain ...string) hostclaim.Observed {
- h := &hostclaim.Holder{Family: "instance", Key: "k", Account: "bab", App: "jam", Incarnation: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", Location: filesLoc, Generation: gen, Session: 3, WriterBox: "uk-box", Stamp: 7}
- for _, c := range chain {
+ h := &hostclaim.Holder{Family: "instance", Key: "apps/bab/jam/instances/" + filesHost + ".json", Account: "bab", App: "jam", CreatedAt: filesCreated, Incarnation: filesInc, Location: filesLoc, Generation: gen, Session: 3, WriterBox: "uk-box", Stamp: 7}
+ for i, c := range chain {
p := strings.Split(c, ":")
- h.Admissions = append(h.Admissions, hostclaim.AdmissionRecord{Generation: p[0], Region: p[1], Predecessor: p[2], PredecessorRegion: p[3], Mode: "cold", Session: 3, Box: "uk-box"})
+ h.Admissions = append(h.Admissions, hostclaim.AdmissionRecord{Generation: p[0], Region: p[1], Predecessor: p[2], PredecessorRegion: p[3], Mode: "cold", Session: uint64(3 + i), Box: "uk-box"})
+ }
+ if n := len(h.Admissions); n > 0 {
+ h.Session = h.Admissions[n-1].Session
+ }
+ return hostclaim.Observed{Host: filesHost, ETag: "e1", Claim: &hostclaim.Claim{V: 2, Host: filesHost, Gen: 9, Holder: h, Op: hostclaim.Op{Kind: hostclaim.KindServing, Exec: "uk-box"}}}
+}
+
+// recordOrigins records tuples as the claim's admitted origins, as the
+// claim's CAS records an admission and the #250 files backfill records a
+// live origin into an admitted prefix: one immutable node per tuple under
+// control/host-origins/<host>/, chained from the claim's current head, and
+// the head written on the stored claim. The claim must already be stored.
+func recordOrigins(t *testing.T, claims release.Store, host string, tuples ...hostclaim.OriginTuple) {
+ t.Helper()
+ ctx := context.Background()
+ o, err := hostclaim.Read(ctx, claims, host)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if o.Absent {
+ t.Fatalf("recordOrigins: %s has no stored claim", host)
+ }
+ c := o.Claim
+ head := hostclaim.OriginHead{}
+ if c.Origins != nil {
+ head = *c.Origins
+ }
+ type node struct {
+ V int `json:"v"`
+ Host string `json:"host"`
+ Count uint64 `json:"count"`
+ Previous string `json:"previous,omitempty"`
+ Origin hostclaim.OriginTuple `json:"origin"`
+ }
+ for _, tuple := range tuples {
+ body, err := json.Marshal(node{V: 1, Host: host, Count: head.Count + 1, Previous: head.Digest, Origin: tuple})
+ if err != nil {
+ t.Fatal(err)
+ }
+ sum := sha256.New()
+ sum.Write([]byte("carlos-admitted-origin-v1\x00"))
+ sum.Write(body)
+ digest := hex.EncodeToString(sum.Sum(nil))
+ key := "control/host-origins/" + host + "/" + digest + ".json"
+ if err := hostclaim.ValidateOriginObject(key, body); err != nil {
+ t.Fatalf("recordOrigins: %v", err)
+ }
+ if _, err := claims.Put(ctx, key, body, ""); err != nil && !errors.Is(err, release.ErrCASConflict) {
+ t.Fatal(err)
+ }
+ head = hostclaim.OriginHead{Digest: digest, Count: head.Count + 1}
+ }
+ c.Origins = &head
+ raw, err := json.Marshal(c)
+ if err != nil {
+ t.Fatal(err)
+ }
+ key, err := hostclaim.Key(host)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if _, err := claims.Put(ctx, key, raw, o.ETag); err != nil {
+ t.Fatal(err)
}
- return hostclaim.Observed{Host: filesHost, ETag: "e1", Claim: &hostclaim.Claim{V: 1, Host: filesHost, Gen: 9, Holder: h, Op: hostclaim.Op{Kind: hostclaim.KindServing, Exec: "uk-box"}}}
+}
+
+// observe stores the hand-built observation's claim in the fixture's claims
+// store, records the named generations as admitted origins of the holder's
+// incarnation and location in this region (the admitted generation among
+// them, as its admission records it), and returns the stored observation,
+// which is what Materialize is given and what the proof authenticates.
+func (x *filesFixture) observe(o hostclaim.Observed, recorded ...string) hostclaim.Observed {
+ x.t.Helper()
+ ctx := context.Background()
+ c := o.Claim
+ if err := c.Validate(); err != nil {
+ x.t.Fatalf("observe: %v", err)
+ }
+ key, err := hostclaim.Key(o.Host)
+ if err != nil {
+ x.t.Fatal(err)
+ }
+ etag := ""
+ if stored, err := hostclaim.Read(ctx, x.claims, o.Host); err == nil && !stored.Absent {
+ // A re-observation keeps the chain the store already records: the
+ // head is the claim's, never a hand-built claim's.
+ etag, c.Origins = stored.ETag, stored.Claim.Origins
+ }
+ raw, err := json.Marshal(c)
+ if err != nil {
+ x.t.Fatal(err)
+ }
+ if _, err := x.claims.Put(ctx, key, raw, etag); err != nil {
+ x.t.Fatal(err)
+ }
+ tuples := make([]hostclaim.OriginTuple, 0, len(recorded))
+ for _, gen := range recorded {
+ tuples = append(tuples, hostclaim.OriginTuple{Incarnation: c.Holder.Incarnation, Location: c.Holder.Location, Region: "uk", Generation: gen})
+ }
+ if len(tuples) > 0 {
+ recordOrigins(x.t, x.claims, o.Host, tuples...)
+ }
+ stored, err := hostclaim.Read(ctx, x.claims, o.Host)
+ if err != nil {
+ x.t.Fatal(err)
+ }
+ return stored
}
func launchInputs(newGen string) ClaimLaunchInputs {
@@ -450,7 +573,7 @@ func TestEstablishCopiesTheNewestValidManifestByExactVersion(t *testing.T) {
if g1.Seq != 3 {
t.Fatalf("g1 at seq %d", g1.Seq)
}
- o := servingObserved("g3", "g1:uk:@root:uk", "g2:uk:g1:uk", "g3:uk:g2:uk")
+ o := x.observe(servingObserved("g3", "g1:uk:@root:uk", "g2:uk:g1:uk", "g3:uk:g2:uk"), "g1", "g3")
if err := x.f.Establish(ctx, o, launchInputs("g3"), x); err != nil {
t.Fatal(err)
}
@@ -533,23 +656,24 @@ func TestEstablishKeepsOriginsPastAdmissionPruning(t *testing.T) {
for i := 4; i <= 11; i++ {
chain = append(chain, fmt.Sprintf("g%d:uk:g%d:uk", i, i-1))
}
- o := servingObserved("g11", chain...)
- if len(o.Claim.Holder.Admissions) != 8 {
- t.Fatalf("%d admissions", len(o.Claim.Holder.Admissions))
- }
- // Provenance comes from the claim (platform-1e's ruling, fix round 3):
- // the carried set is a hint, and every origin must be proven against
- // admissions the claim has recorded. Today that is the retained chain,
- // which no longer names g1, so the establishment refuses; it passes
- // only with a proof that records g1, as claim Plan 15's durable
- // per-incarnation record will.
+ c := servingObserved("g11", chain...)
+ if len(c.Claim.Holder.Admissions) != 8 {
+ t.Fatalf("%d admissions", len(c.Claim.Holder.Admissions))
+ }
+ // Provenance comes from the claim (platform-1e's ruling, fix round 3;
+ // claim Plan 15): the carried set is a hint, retained admissions are
+ // never evidence, and every origin must be proven by the claim's
+ // recorded admissions. g1 is retained-only (pruned from the chain and
+ // never recorded), so the establishment refuses; once the claim records
+ // g1 (its admission, or the #250 files backfill), it passes.
+ o := x.observe(c, "g11")
if err := x.f.Establish(ctx, o, launchInputs("g11"), x); !errors.Is(err, hostfiles.ErrOrigin) {
- t.Fatalf("past pruning with the retained chain alone: %v", err)
+ t.Fatalf("past pruning, g1 retained only: %v", err)
}
if _, found := x.manifest("g11"); found {
t.Fatal("a manifest was written over an unproven ancestor")
}
- x.f.Proof = recordedOrigins{"g1": true, "g2": true, "g3": true, "g4": true, "g5": true, "g6": true, "g7": true, "g8": true, "g9": true, "g10": true, "g11": true}
+ o = x.observe(c, "g1")
if err := x.f.Establish(ctx, o, launchInputs("g11"), x); err != nil {
t.Fatalf("establish past pruning with the claim's record: %v", err)
}
@@ -623,7 +747,8 @@ func preparedFixture(t *testing.T) (*filesFixture, preparedManifest, hostclaim.O
x.journal("mv1", pm)
x.writeEnv("K=1\nCARLOS_INSTANCE_KEY=abc\n")
o := servingObserved("mv1", "cur:ie:@root:ie", "mv1:uk:cur:ie")
- o.Claim.Op = hostclaim.Op{Kind: hostclaim.KindLanding, ID: "mv1", Phase: "probing", Exec: "uk-box"}
+ o.Claim.Op = hostclaim.Op{Kind: hostclaim.KindLanding, ID: "mv1", Phase: "probing", Exec: "uk-box", Participants: []string{"ie-box", "uk-box"}, Intent: map[string]string{"to": "uk", "source": "ie-box", "target": "uk-box"}}
+ o = x.observe(o, "mv1")
in := launchInputs("mv1")
in.Prepared = true
return x, pm, o, in
@@ -776,7 +901,7 @@ func TestEstablishPublishesOutsideTheStartsWork(t *testing.T) {
})
st := &stallingStore{Store: x.files, key: hostfiles.ManifestKey(movesFilesPrefix(filesLoc, "g2")), stalled: make(chan struct{}), release: make(chan struct{})}
x.f.Files = st
- o := servingObserved("g2", "g1:uk:@root:uk", "g2:uk:g1:uk")
+ o := x.observe(servingObserved("g2", "g1:uk:@root:uk", "g2:uk:g1:uk"), "g1", "g2")
done := make(chan error, 1)
go func() { done <- x.f.Establish(ctx, o, launchInputs("g2"), x) }()
select {
@@ -834,7 +959,7 @@ func TestEstablishRefusesANeverAdmittedOrigin(t *testing.T) {
t.Fatal(err)
}
})
- o := servingObserved("g2", "g1:uk:@root:uk", "g2:uk:g1:uk")
+ o := x.observe(servingObserved("g2", "g1:uk:@root:uk", "g2:uk:g1:uk"), "g1", "g2")
never := movesFilesPrefix(filesLoc, "never") + "objects/0000000000000001-" + sha([]byte("N"))
x.bucket.Put("data", never, []byte("N"))
for name, origins := range map[string][]string{"no carried set": nil, "a carried set without it": {movesFilesPrefix(filesLoc, "g1")}} {
@@ -880,7 +1005,7 @@ func TestEstablishRefusesANeverAdmittedOrigin(t *testing.T) {
hint := m
hint.Origins = []string{movesFilesPrefix(filesLoc, "g1"), movesFilesPrefix(filesLoc, "g2"), movesFilesPrefix(filesLoc, "never")}
x.bucket.Put("data", hostfiles.ManifestKey(movesFilesPrefix(filesLoc, "g2")), mustJSON(t, hint))
- o3 := servingObserved("g3", "g1:uk:@root:uk", "g2:uk:g1:uk", "g3:uk:g2:uk")
+ o3 := x.observe(servingObserved("g3", "g1:uk:@root:uk", "g2:uk:g1:uk", "g3:uk:g2:uk"), "g3")
if err := x.f.Establish(ctx, o3, launchInputs("g3"), x); err != nil {
t.Fatalf("carried only: %v", err)
}
@@ -917,6 +1042,7 @@ func TestEstablishSeedsALegacyLocationsOriginSet(t *testing.T) {
}
o := servingObserved("g2", "g1:uk:@root:uk", "g2:uk:g1:uk")
o.Claim.Holder.Location = legacy
+ o = x.observe(o, "g1", "g2")
if err := x.f.Establish(ctx, o, launchInputs("g2"), x); err != nil {
t.Fatal(err)
}
@@ -933,20 +1059,12 @@ func TestEstablishSeedsALegacyLocationsOriginSet(t *testing.T) {
x.bucket.Put("data", hostfiles.ManifestKey(loc("g2")), mustJSON(t, bad))
o3 := servingObserved("g3", "g1:uk:@root:uk", "g2:uk:g1:uk", "g3:uk:g2:uk")
o3.Claim.Holder.Location = legacy
+ o3 = x.observe(o3, "g3")
if err := x.f.Establish(ctx, o3, launchInputs("g3"), x); !errors.Is(err, hostfiles.ErrOrigin) {
t.Fatalf("never admitted under a legacy location: %v", err)
}
}
-// recordedOrigins is a claim-backed origin proof that records the named
-// generations: what claim Plan 15's durable per-incarnation record will
-// answer.
-type recordedOrigins map[string]bool
-
-func (r recordedOrigins) Proven(_ context.Context, _ hostclaim.Observed, _ hostclaim.Admission, generation string) (bool, error) {
- return r[generation], nil
-}
-
// answeringProof is a claim-backed origin proof with an answer per
// generation: proven, not proven, or an error; a generation it does not
// name is not proven.
@@ -955,11 +1073,15 @@ type answeringProof struct {
errs map[string]error
}
-func (a answeringProof) Proven(_ context.Context, _ hostclaim.Observed, _ hostclaim.Admission, generation string) (bool, error) {
- if err := a.errs[generation]; err != nil {
- return false, err
+func (a answeringProof) Proven(_ context.Context, _ hostclaim.Observed, _ hostclaim.Admission, generations []string) (map[string]bool, error) {
+ out := map[string]bool{}
+ for _, generation := range generations {
+ if err := a.errs[generation]; err != nil {
+ return nil, err
+ }
+ out[generation] = a.proven[generation]
}
- return a.proven[generation], nil
+ return out, nil
}
// movesOriginProof is the only route to origin proof (fix round 4): the
@@ -1025,30 +1147,15 @@ func TestEstablishProvesEveryOriginThroughTheProof(t *testing.T) {
}
}
-// chainOriginProof never accepts a bare Admission.NewGeneration supplied
-// on its own (platform-1e's condition, fix round 4): a generation is
-// proven only when the bound observation's retained chain records it, as
-// the post-admission observation Materialize is given does. A
-// free-standing generation, named by the admission but by no observation,
-// is refused at the proof and by Establish, and nothing is written.
-func TestChainProofRefusesAFreeStandingGeneration(t *testing.T) {
+// The proof never accepts a bare Admission.NewGeneration supplied on its
+// own (platform-1e's condition, fix round 4; claim Plan 15): a generation
+// is proven only when the bound observation's claim records its
+// admission, as the post-admission observation Materialize is given does.
+// A free-standing generation, named by the admission but recorded by no
+// observation, is refused at the proof and by Establish, and nothing is
+// written; an observation of no claim is an error.
+func TestClaimProofRefusesAFreeStandingGeneration(t *testing.T) {
ctx := context.Background()
- proof := chainOriginProof{home: "ie", region: "uk"}
- before := servingObserved("g1", "g1:uk:@root:uk") // the observation before g2's admission
- in := hostclaim.Admission{Box: "uk-box", Mode: "cold", NewGeneration: "g2", Region: "uk"}
- if ok, err := proof.Proven(ctx, before, in, "g2"); ok || err != nil {
- t.Fatalf("a free-standing generation proven: %v %v", ok, err)
- }
- if ok, err := proof.Proven(ctx, hostclaim.Observed{Host: filesHost, Absent: true}, in, "g2"); ok || err != nil {
- t.Fatalf("proven with no claim: %v %v", ok, err)
- }
- if ok, _ := proof.Proven(ctx, before, in, "g1"); !ok {
- t.Fatal("the observed holder's generation is not proven")
- }
- after := servingObserved("g2", "g1:uk:@root:uk", "g2:uk:g1:uk") // the CAS result the writer's admission returns
- if ok, _ := proof.Proven(ctx, after, in, "g2"); !ok {
- t.Fatal("the admitted generation the observation records is not proven")
- }
x := newFilesFixture(t)
x.writeEnv("K=1\n")
x.publish("g1", func(p *hostfiles.Publisher) {
@@ -1056,27 +1163,41 @@ func TestChainProofRefusesAFreeStandingGeneration(t *testing.T) {
t.Fatal(err)
}
})
+ proof := ClaimOriginProof{Claims: x.claims, HomeRegion: "ie", Region: "uk"}
+ before := x.observe(servingObserved("g1", "g1:uk:@root:uk"), "g1") // the observation before g2's admission
+ in := hostclaim.Admission{Box: "uk-box", Mode: "cold", NewGeneration: "g2", Region: "uk"}
+ if got, err := proof.Proven(ctx, before, in, []string{"g1", "g2"}); err != nil || !got["g1"] || got["g2"] {
+ t.Fatalf("a free-standing generation: %v %v", got, err)
+ }
+ if _, err := proof.Proven(ctx, hostclaim.Observed{Host: filesHost, Absent: true}, in, []string{"g2"}); err == nil {
+ t.Fatal("proven with no claim")
+ }
if err := x.f.Establish(ctx, before, launchInputs("g2"), x); !errors.Is(err, hostfiles.ErrOrigin) {
t.Fatalf("establish with an observation that does not record the admission: %v", err)
}
if _, found := x.manifest("g2"); found {
t.Fatal("a manifest was written for a free-standing generation")
}
+ after := x.observe(servingObserved("g2", "g1:uk:@root:uk", "g2:uk:g1:uk"), "g2") // the CAS result the writer's admission returns
if err := x.f.Establish(ctx, after, launchInputs("g2"), x); err != nil {
t.Fatalf("establish with the bound observation: %v", err)
}
}
-// countingProof records every generation it is asked about, in order,
+// countingProof records every Proven call and the set it was asked about,
// answering from proven.
type countingProof struct {
proven map[string]bool
- asked []string
+ calls [][]string
}
-func (c *countingProof) Proven(_ context.Context, _ hostclaim.Observed, _ hostclaim.Admission, generation string) (bool, error) {
- c.asked = append(c.asked, generation)
- return c.proven[generation], nil
+func (c *countingProof) Proven(_ context.Context, _ hostclaim.Observed, _ hostclaim.Admission, generations []string) (map[string]bool, error) {
+ c.calls = append(c.calls, append([]string(nil), generations...))
+ out := map[string]bool{}
+ for _, generation := range generations {
+ out[generation] = c.proven[generation]
+ }
+ return out, nil
}
// Only referenced origins are proven and carried (Task 6b, the moves side
@@ -1117,9 +1238,10 @@ func TestEstablishProvesAndCarriesOnlyReferencedOrigins(t *testing.T) {
if !found || !reflect.DeepEqual(m.Origins, want) {
t.Fatalf("g2 origins %v, want %v", m.Origins, want)
}
- sort.Strings(proof.asked)
- if !reflect.DeepEqual(proof.asked, []string{"g1", "g2", "g5"}) {
- t.Fatalf("Proven asked %v, want each referenced origin and the admitted generation once", proof.asked)
+ // One Proven(set) call per Establish (Plan 15): the deduplicated
+ // referenced origins plus the admitted generation, in one batch.
+ if len(proof.calls) != 1 || !reflect.DeepEqual(proof.calls[0], []string{"g1", "g2", "g5"}) {
+ t.Fatalf("Proven calls %v, want one call over the referenced origins and the admitted generation", proof.calls)
}
// A referenced origin the proof refuses still refuses the whole.
x.f.Proof = &countingProof{proven: map[string]bool{"g1": true, "g2": true, "g3": true}}
@@ -1164,8 +1286,8 @@ func TestRecoveryEstablishesFromTheRecordedSourceGeneration(t *testing.T) {
for name, shape := range map[string]func(o *hostclaim.Observed){
"a clean return": func(*hostclaim.Observed) {},
"a declared-loss return": func(o *hostclaim.Observed) {
- o.Claim.Op.Journal["loss.l1"] = "1"
- o.Claim.LossRefs = []string{"control/loss-records/l1.json"}
+ o.Claim.Op.Journal["loss.l1"] = `{"kind":"move-target-lost","target":"ie-box"}`
+ o.Claim.LossRefs, o.Claim.Lost = []string{"l1"}, []string{"ie-box"}
},
"a fenced return": func(o *hostclaim.Observed) {
o.Claim.Op.Journal["fence.ie-box"] = `{"enrollment":"enr","key":"control/box-fences/enr.json","sha256":"ab"}`
@@ -1190,6 +1312,7 @@ func TestRecoveryEstablishesFromTheRecordedSourceGeneration(t *testing.T) {
})
o := recoveryObserved(hostclaim.KindRestoring, "src", "mv1", "r1")
shape(&o)
+ o = x.observe(o, "src", "r1")
if err := x.f.Establish(ctx, o, launchInputs("r1"), x); err != nil {
t.Fatalf("establish from src_gen: %v", err)
}
@@ -1263,7 +1386,7 @@ func TestRecoveryRestartTakesTheNewestRecoveryManifest(t *testing.T) {
publish(x, "src", "A")
}
rb := publish(x, "rb1", "B")
- o := recoveryRestartObserved(hostclaim.KindRestoring, "rb1", "r2")
+ o := x.observe(recoveryRestartObserved(hostclaim.KindRestoring, "rb1", "r2"), "rb1", "r2")
if err := x.f.Establish(ctx, o, launchInputs("r2"), x); err != nil {
t.Fatalf("the restart: %v", err)
}
@@ -1280,7 +1403,7 @@ func TestRecoveryRestartTakesTheNewestRecoveryManifest(t *testing.T) {
x.writeEnv("K=1\n")
publish(x, "src", "A")
newer := publish(x, "r1", "C") // the earlier recovery admission's publication
- o := recoveryRestartObserved(kind, "r1", "r2")
+ o := x.observe(recoveryRestartObserved(kind, "r1", "r2"), "r1", "r2")
if err := x.f.Establish(ctx, o, launchInputs("r2"), x); err != nil {
t.Fatalf("the restart: %v", err)
}
@@ -1299,6 +1422,7 @@ func TestRecoveryRestartTakesTheNewestRecoveryManifest(t *testing.T) {
rb := publish(x, "rb1", "B")
o := recoveryRestartObserved(hostclaim.KindRestoring, "rb1", "r2")
o.Claim.Op.Journal[moves.JournalReturnSeal] = "sealed"
+ o = x.observe(o, "rb1", "r2")
if err := x.f.Establish(ctx, o, launchInputs("r2"), x); err != nil {
t.Fatalf("the second admission: %v", err)
}
@@ -1308,6 +1432,7 @@ func TestRecoveryRestartTakesTheNewestRecoveryManifest(t *testing.T) {
r2 := publish(x, "r2", "D") // the second admission's own publication
o3 := servingObserved("r3", "src:uk:@root:uk", "mv1:ie:src:uk", "rb1:uk:mv1:ie", "r2:uk:rb1:uk", "r3:uk:r2:uk")
o3.Claim.Op = o.Claim.Op
+ o3 = x.observe(o3, "r3")
if err := x.f.Establish(ctx, o3, launchInputs("r3"), x); err != nil {
t.Fatalf("the third admission: %v", err)
}
@@ -1324,6 +1449,7 @@ func TestRecoveryRestartTakesTheNewestRecoveryManifest(t *testing.T) {
publish(x, "src", "A")
o := recoveryRestartObserved(hostclaim.KindRestoring, "rb1", "r2")
o.Claim.Op.Journal[moves.JournalReturnSeal] = "sealed"
+ o = x.observe(o, "src", "r2")
if err := x.f.Establish(ctx, o, launchInputs("r2"), x); !errors.Is(err, hostfiles.ErrNoEvidence) {
t.Fatalf("a dirty return without its rollback manifest: %v", err)
}
@@ -1335,7 +1461,7 @@ func TestRecoveryRestartTakesTheNewestRecoveryManifest(t *testing.T) {
x := newFilesFixture(t)
x.writeEnv("K=1\n")
src := publish(x, "src", "A")
- o := recoveryObserved(hostclaim.KindRestoring, "src", "mv1", "r1")
+ o := x.observe(recoveryObserved(hostclaim.KindRestoring, "src", "mv1", "r1"), "src", "r1")
if err := x.f.Establish(ctx, o, launchInputs("r1"), x); err != nil {
t.Fatalf("the first admission: %v", err)
}
@@ -1366,7 +1492,7 @@ func TestEstablishCarriesTheEnvBytesAndDropsTheAdoptionItem(t *testing.T) {
if err := os.WriteFile(adoption, []byte(`{"v":2}`), 0o600); err != nil {
t.Fatal(err)
}
- o := servingObserved("g2", "g1:uk:@root:uk", "g2:uk:g1:uk")
+ o := x.observe(servingObserved("g2", "g1:uk:@root:uk", "g2:uk:g1:uk"), "g1", "g2")
x.workErr = hostbox.ErrChanged
if err := x.f.Establish(ctx, o, launchInputs("g2"), x); !errors.Is(err, hostbox.ErrChanged) {
t.Fatalf("refused work: %v", err)
@@ -1483,7 +1609,7 @@ func TestEstablishHostThatNeverPublishedFiles(t *testing.T) {
x.f.Evidence = func(context.Context, hostclaim.Observed) (hostfiles.PinnedFiles, bool, error) {
return hostfiles.PinnedFiles{}, true, nil
}
- o := servingObserved("g3", "g1:uk:@root:uk", "g2:uk:g1:uk", "g3:uk:g2:uk")
+ o := x.observe(servingObserved("g3", "g1:uk:@root:uk", "g2:uk:g1:uk", "g3:uk:g2:uk"), "g3")
if err := x.f.Establish(ctx, o, launchInputs("g3"), x); err != nil {
t.Fatal(err)
}
@@ -1494,7 +1620,7 @@ func TestEstablishHostThatNeverPublishedFiles(t *testing.T) {
// The claim's own empty bootstrap: a created incarnation before its
// first writer, admitted after EmptyAuthority.
x.f.Evidence = nil
- empty := servingObserved("g1", "g1:uk:@root:uk") // admitted: the holder at g1 after EmptyAuthority
+ empty := x.observe(servingObserved("g1", "g1:uk:@root:uk"), "g1") // admitted: the holder at g1 after EmptyAuthority
in := launchInputs("g1")
in.Empty = true
if err := x.f.Establish(ctx, empty, in, x); err != nil {
@@ -1561,7 +1687,8 @@ func TestEstablishOnThePreparedGenerationContinuesFromSequenceZero(t *testing.T)
x.journal("mv1", pm)
x.writeEnv("K=1\nCARLOS_INSTANCE_KEY=abc\n")
o := servingObserved("mv1", "cur:ie:@root:ie", "mv1:uk:cur:ie")
- o.Claim.Op = hostclaim.Op{Kind: hostclaim.KindLanding, ID: "mv1", Phase: "probing", Exec: "uk-box"}
+ o.Claim.Op = hostclaim.Op{Kind: hostclaim.KindLanding, ID: "mv1", Phase: "probing", Exec: "uk-box", Participants: []string{"ie-box", "uk-box"}, Intent: map[string]string{"to": "uk", "source": "ie-box", "target": "uk-box"}}
+ o = x.observe(o, "mv1")
in := launchInputs("mv1")
in.Prepared = true
// The first attempt crashes after the commit, before the local effect:
diff --git a/internal/host/movesprobeowners_test.go b/internal/host/movesprobeowners_test.go
index 97eb4d60f..d72f19b56 100644
--- a/internal/host/movesprobeowners_test.go
+++ b/internal/host/movesprobeowners_test.go
@@ -91,6 +91,10 @@ func (f *poFixture) poFlipClaim() *hostclaim.Claim {
func newPOFixture(t *testing.T) *poFixture {
t.Helper()
+ // Claim Plan 15: admissions through the real CAS record their origin
+ // under both switches; the real C8's proof reads that record.
+ t.Setenv("CARLOS_HOST_CLAIM", "1")
+ t.Setenv("CARLOS_HOST_CLAIM_ORIGINS", "1")
at := time.Date(2026, 10, 1, 0, 0, 0, 0, time.UTC)
inc, err := hostclaim.Incarnation("bab", "jam", poHost, at)
if err != nil {
@@ -349,7 +353,12 @@ func TestProbeIsAColdAdmissionIntoThePreparedGeneration(t *testing.T) {
// directory and files paths, as the claim assembly binds it: the probe
// owners' Files is this, not a fake (whole-branch fixes, item 1).
func (f *poFixture) realC8() *movesFiles {
- return &movesFiles{Files: casBucketStore{b: f.bucket, bucket: "data"}, Data: f.bucket, Bucket: "data", HomeRegion: "ie", Region: f.owners.Region, Box: f.owners.Box, DataDir: f.dataDir, FilesDir: f.owners.FilesDir}
+ return &movesFiles{Files: casBucketStore{b: f.bucket, bucket: "data"}, Data: f.bucket, Bucket: "data", HomeRegion: "ie", Region: f.owners.Region, Box: f.owners.Box, DataDir: f.dataDir, FilesDir: f.owners.FilesDir,
+ // Plan 15's proof over the fixture's claim store: the admissions the
+ // fixture makes through the real CAS are recorded (both switches
+ // on), and recordOrigins records what a hand-built claim or the
+ // #250 backfill would.
+ Proof: ClaimOriginProof{Claims: f.claims, HomeRegion: "ie", Region: f.owners.Region}}
}
// preparedManifestJournalled builds the prepared manifest local writes for
diff --git a/internal/host/movesrecoveryowners_test.go b/internal/host/movesrecoveryowners_test.go
index 004ff67e2..b7f30518f 100644
--- a/internal/host/movesrecoveryowners_test.go
+++ b/internal/host/movesrecoveryowners_test.go
@@ -172,6 +172,11 @@ func newRCFixture(t *testing.T, kind string) *rcFixture {
t.Fatal(err)
}
f.putClaim(c)
+ // Plan 15: the source generation the clean kinds bridge from is proven
+ // only when the claim records it; record it as the #250 backfill does.
+ if kind != rcLegacy {
+ recordOrigins(t, f.claims, poHost, hostclaim.OriginTuple{Incarnation: f.inc, Location: "instances/" + poHost, Region: "ie", Generation: "src"})
+ }
return f
}
diff --git a/internal/host/plan15_originproof_test.go b/internal/host/plan15_originproof_test.go
index 62858930f..1cbf81fca 100644
--- a/internal/host/plan15_originproof_test.go
+++ b/internal/host/plan15_originproof_test.go
@@ -1,7 +1,6 @@
package host
import (
- "context"
"testing"
"time"
@@ -9,20 +8,9 @@ import (
"github.com/carlosframework/platform/internal/release"
)
-var _ interface {
- Proven(context.Context, hostclaim.Observed, hostclaim.Admission, []string) (map[string]bool, error)
-} = ClaimOriginProof{}
-
-// The moves session will adopt the batch signature after Plan 15 lands.
-// This test-only bridge checks compatibility with its current singleton API.
-type plan15SingleOriginAdapter struct{ ClaimOriginProof }
-
-var _ movesOriginProof = plan15SingleOriginAdapter{}
-
-func (p plan15SingleOriginAdapter) Proven(ctx context.Context, o hostclaim.Observed, in hostclaim.Admission, generation string) (bool, error) {
- proven, err := p.ClaimOriginProof.Proven(ctx, o, in, []string{generation})
- return proven[generation], err
-}
+// ClaimOriginProof is the moves provider's proof (movesOriginProof's batch
+// contract), adopted with the Plan 15 switch.
+var _ movesOriginProof = ClaimOriginProof{}
func TestPlan15MovesProof(t *testing.T) {
t.Setenv("CARLOS_HOST_CLAIM", "1")
@@ -58,10 +46,5 @@ func TestPlan15MovesProof(t *testing.T) {
if got[tc.generation] != tc.want || err != nil {
t.Fatalf("%+v: %v %v", tc, got, err)
}
- var adapter movesOriginProof = plan15SingleOriginAdapter{p}
- one, err := adapter.Proven(t.Context(), o, hostclaim.Admission{NewGeneration: tc.generation}, tc.generation)
- if one != tc.want || err != nil {
- t.Fatalf("singleton adapter %+v: %v %v", tc, one, err)
- }
}
}