carlos / platform Public

host: the real writer's publication under every claim state folds through the real edge fold as the mixed-history statement says

lifetimes on and off crossed with region HA off and a positive, absent,
failed and undecided claim read: the pair appears only with lifetimes, the
stamp only with a positive claim, and every retained publication folds
against every fresh one, in both file orders, through edge.FoldedHome.
The unstamped claims-on publication against a retained claims-off pair is
spelled out on its own.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev f1e13d9ee2892a98de4edb31ac116ca7bab3dfaf parent 9d054e4
1 file changed, +189 −0
  • internal/host/edgesfile_test.go +189 −0
diff --git a/internal/host/edgesfile_test.go b/internal/host/edgesfile_test.go
index bb2154778..f094a810f 100644
--- a/internal/host/edgesfile_test.go
+++ b/internal/host/edgesfile_test.go
@@ -20,6 +20,7 @@ import (
"time"
"github.com/carlosframework/platform/internal/appconfig"
+ "github.com/carlosframework/platform/internal/edge"
"github.com/carlosframework/platform/internal/edgesfile"
"github.com/carlosframework/platform/internal/hostclaim"
"github.com/carlosframework/platform/internal/moves"
@@ -1751,6 +1752,194 @@ func TestLifetimesOffWriterPublishesNoPair(t *testing.T) {
}
}
+// claimRead is how one pass's claim read for the host went.
+type claimRead int
+
+const (
+ readHAOff claimRead = iota // region HA off: no claim reader at all
+ readPositive // the claim names this box: stamped
+ readAbsent // no claim object
+ readFailed // the read errored, and never succeeded before
+ readUndecided // the pass's deadline came before the read
+)
+
+func (r claimRead) String() string {
+ return [...]string{"ha-off", "positive", "absent", "failed", "undecided"}[r]
+}
+
+// claimsFor builds the region HA claim reader for one claim state, with
+// box as this box and stamp as its claim's holder stamp.
+func claimsFor(r claimRead, box, host string, stamp uint64) (*edgesClaims, *claimsBook) {
+ book := &claimsBook{claims: map[string]*hostclaim.Claim{}}
+ switch r {
+ case readHAOff:
+ return nil, book
+ case readPositive, readUndecided:
+ book.claims[host] = serving(box, stamp)
+ case readFailed:
+ book.errs = map[string]bool{host: true}
+ }
+ c := newEdgesClaims(box, book.read)
+ c.logf = func(string, ...any) {} // a failed read logs; not this test's business
+ if r == readUndecided {
+ c.passDeadline = 0 // expired before the first read: no decision
+ }
+ return c, book
+}
+
+// publication is one writer configuration and the stamp and pair it must
+// publish for the host.
+type publication struct {
+ lifetimes bool
+ read claimRead
+ row registry.Pair
+ // claimStamp is the holder stamp of the claim this box would read
+ // (positive and undecided reads); stamp is what it must publish.
+ claimStamp uint64
+ stamp int64
+ pair registry.Pair
+}
+
+func (p publication) String() string {
+ return fmt.Sprintf("lifetimes=%v read=%s row=(%d, %d) claim stamp %d", p.lifetimes, p.read, p.row.Incarnation, p.row.ServingSeq, p.claimStamp)
+}
+
+func pairLess(a, b registry.Pair) bool {
+ if a.Incarnation != b.Incarnation {
+ return a.Incarnation < b.Incarnation
+ }
+ return a.ServingSeq < b.ServingSeq
+}
+
+// mixedHistory is the brief's mixed-history statement: whether the
+// earlier file's publication keeps the dial, and the clause that says so.
+func mixedHistory(first, second publication) (bool, string) {
+ switch {
+ case first.stamp > 0 && second.stamp > 0:
+ return first.stamp >= second.stamp, "two stamped publications fold as today"
+ case first.stamp > 0 || second.stamp > 0:
+ return first.stamp > 0, "a stamped publication beats any unstamped entry whatever its pair"
+ case first.pair == second.pair:
+ return true, "equal pairs (two unstamped claims-on (0, 0) among them) fold as today"
+ case first.pair == (registry.Pair{}) || second.pair == (registry.Pair{}):
+ return second.pair == (registry.Pair{}), "an unstamped (0, 0) loses to a retained publication carrying a pair"
+ default:
+ return pairLess(second.pair, first.pair), "the higher pair wins (the design's moves)"
+ }
+}
+
+func TestPublicationUnderEveryClaimState(t *testing.T) {
+ const host = "shop.example.com"
+ reads := []claimRead{readHAOff, readPositive, readAbsent, readFailed, readUndecided}
+ regs := map[registry.Pair]*registry.Registry{}
+ regFor := func(p registry.Pair) *registry.Registry {
+ if regs[p] == nil {
+ regs[p] = edgesTestRegistry(t)
+ lifetimeRow(t, regs[p], host, p.Incarnation, p.ServingSeq)
+ }
+ return regs[p]
+ }
+ // publish runs the real writer as box under p and checks what it
+ // published against p's stamp and pair.
+ publish := func(p publication, box string) []byte {
+ t.Helper()
+ claims, book := claimsFor(p.read, box, host, p.claimStamp)
+ body, hosts := publishLifetimes(t, regFor(p.row), box, claims, p.lifetimes)
+ h, ok := hosts[host]
+ if !ok {
+ t.Fatalf("%s as %s: host not published", p, box)
+ }
+ if h.Stamp != p.stamp || h.Incarnation != p.pair.Incarnation || h.ServingSeq != p.pair.ServingSeq {
+ t.Errorf("%s as %s: published stamp %d pair (%d, %d), want stamp %d pair (%d, %d)",
+ p, box, h.Stamp, h.Incarnation, h.ServingSeq, p.stamp, p.pair.Incarnation, p.pair.ServingSeq)
+ }
+ if p.read == readUndecided && len(book.reads) != 0 {
+ t.Errorf("%s: an undecided pass read the claim", p)
+ }
+ return body
+ }
+ states := func(row registry.Pair, stamp int64) []publication {
+ var out []publication
+ for _, lifetimes := range []bool{false, true} {
+ for _, r := range reads {
+ p := publication{lifetimes: lifetimes, read: r, row: row, claimStamp: uint64(stamp)}
+ if r == readPositive {
+ p.stamp = stamp // the stamp only with a positive claim
+ }
+ if lifetimes {
+ p.pair = row // the pair only with lifetimes
+ }
+ out = append(out, p)
+ }
+ }
+ return out
+ }
+ retained := states(registry.Pair{Incarnation: 1, ServingSeq: 3}, 41)
+ var fresh []publication
+ for _, seq := range []uint64{2, 3, 4} {
+ fresh = append(fresh, states(registry.Pair{Incarnation: 1, ServingSeq: seq}, 57)...)
+ }
+ bodies := map[string][]byte{} // publication and box -> body
+ bodyOf := func(p publication, box string) []byte {
+ k := p.String() + " " + box
+ if bodies[k] == nil {
+ bodies[k] = publish(p, box)
+ }
+ return bodies[k]
+ }
+ clauses := map[string]int{}
+ for _, r := range retained {
+ for _, f := range fresh {
+ for _, order := range [][2]publication{{r, f}, {f, r}} {
+ files := map[string][]byte{
+ edgesFileKey("annie"): bodyOf(order[0], "annie"),
+ edgesFileKey("betty"): bodyOf(order[1], "betty"),
+ }
+ box, ok, err := edge.FoldedHome(files, "carol", host)
+ if err != nil || !ok {
+ t.Fatalf("fold %s | %s: %q %v %v", order[0], order[1], box, ok, err)
+ }
+ firstWins, clause := mixedHistory(order[0], order[1])
+ clauses[clause]++
+ want := "betty"
+ if firstWins {
+ want = "annie"
+ }
+ if box != want {
+ t.Errorf("annie %s | betty %s: fold chose %s, want %s (%s)", order[0], order[1], box, want, clause)
+ }
+ }
+ }
+ }
+ // Every clause of the statement was exercised, in both orders.
+ for _, clause := range []string{
+ "two stamped publications fold as today",
+ "a stamped publication beats any unstamped entry whatever its pair",
+ "equal pairs (two unstamped claims-on (0, 0) among them) fold as today",
+ "an unstamped (0, 0) loses to a retained publication carrying a pair",
+ "the higher pair wins (the design's moves)",
+ } {
+ if clauses[clause] == 0 {
+ t.Errorf("no fold exercised %q", clause)
+ }
+ }
+ // The statement's named case, spelled out: an unstamped claims-on
+ // publication against a retained claims-off one carrying (1, 3) loses
+ // to it in both orders, whatever its claim read was.
+ for _, rr := range []claimRead{readHAOff, readAbsent, readFailed, readUndecided} {
+ for _, fr := range []claimRead{readHAOff, readAbsent, readFailed, readUndecided} {
+ ret := publication{lifetimes: true, read: rr, row: registry.Pair{Incarnation: 1, ServingSeq: 3}, claimStamp: 41, pair: registry.Pair{Incarnation: 1, ServingSeq: 3}}
+ on := publication{lifetimes: false, read: fr, row: registry.Pair{Incarnation: 1, ServingSeq: 4}, claimStamp: 57}
+ for _, boxes := range [][2]string{{"annie", "betty"}, {"betty", "annie"}} {
+ files := map[string][]byte{edgesFileKey(boxes[0]): bodyOf(ret, boxes[0]), edgesFileKey(boxes[1]): bodyOf(on, boxes[1])}
+ if box, _, _ := edge.FoldedHome(files, "carol", host); box != boxes[0] {
+ t.Errorf("retained %s on %s against unstamped claims-on %s: fold chose %s", ret, boxes[0], on, box)
+ }
+ }
+ }
+ }
+}
+
// The box-side lifetimes switch is the one the box judge is built from
// (controller ruling 1): runAgent derives it from boxJudge, once, and hands
// it to the edges file loop, which hands it to the writer.