From 9480480f52f5a1ce707ff97a21a14a11614c4c76 Mon Sep 17 00:00:00 2001 From: Calvin Secrest Date: Wed, 30 Sep 2026 13:45:11 -0400 Subject: [PATCH 1/4] Add files via upload Signed-off-by: Calvin Secrest --- CONFORMANCE.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CONFORMANCE.md b/CONFORMANCE.md index 762ccd3..323ecc7 100644 --- a/CONFORMANCE.md +++ b/CONFORMANCE.md @@ -32,6 +32,15 @@ Statuses are stated per the architecture's honesty rule. Built member-layer work | **Single participant identity** — one member, simultaneously contributor and consumer | Design-binding | No producer/consumer split exists in any schema or type; binds every member surface as it lands | | **Forkability** | Built | Consumes the coordination protocol at a published version tag; no coordinator is privileged | +## JFA registry bindings + +Tests that bind an ID from the Janus invariant registry (`jfa-conformance-suite.py` in NTARI-RAND/Janus) are named `TestInvariant__`, with any `-` in the ID written `_` (so `REC-six-holders` becomes `TestInvariant_REC_six_holders_…`). `go test -run TestInvariant_ -v ./...` lists every binding and its result. + +| Registry ID | Test | What it adds beyond the package's existing tests | +|---|---|---| +| **L1** — each exchange moves two balances netting to zero | `internal/economy` `TestInvariant_L1_ZeroSumBalance` | Concurrent payers across two ledgers sharing one store; per-record netting; replay reproduces every balance | +| **L8** — full distribution carried; no scalar score anywhere | `internal/covenant` `TestInvariant_L8_NoScalarAggregation` | Two members with equal means and different shapes stay distinguishable; a module-wide source scan for score-named or float-returning exported functions, score-like JSON tags, and float fields in reputation packages | + ## Stand-ins and open residuals - **Single-witness StandIn.** Every witnessed checkpoint today has fewer than two independent witnesses; the record layer computes and reports this label structurally. Live federation is the architecture's open problem 2 — the keystone build. From 9dbf1af3de9932b129871ee43da6d3e054c25cad Mon Sep 17 00:00:00 2001 From: Calvin Secrest Date: Wed, 30 Sep 2026 13:47:52 -0400 Subject: [PATCH 2/4] Add files via upload Signed-off-by: Calvin Secrest --- internal/economy/conformance_test_v1.go | 140 ++++++++++++++ internal/economy/conformance_test_v2.go | 246 ++++++++++++++++++++++++ 2 files changed, 386 insertions(+) create mode 100644 internal/economy/conformance_test_v1.go create mode 100644 internal/economy/conformance_test_v2.go diff --git a/internal/economy/conformance_test_v1.go b/internal/economy/conformance_test_v1.go new file mode 100644 index 0000000..bd01359 --- /dev/null +++ b/internal/economy/conformance_test_v1.go @@ -0,0 +1,140 @@ +package economy + +// JFA conformance bindings for the economy layer. +// +// Test names carry the Janus registry ID they bind (jfa-conformance-suite.py +// in NTARI-RAND/Janus), so a CI log parser can map a PASS/FAIL back to the +// invariant: TestInvariant__, with any "-" in the ID written "_". + +import ( + "errors" + "math/rand" + "sync" + "testing" +) + +// TestInvariant_L1_ZeroSumBalance binds registry ID L1: "each exchange moves +// two balances netting to zero." +// +// TestZeroSum already proves the end-state sum on one ledger, driven +// serially. This binding adds what L1 needs beyond that: +// +// 1. Concurrency: several payers post at once, through two Ledgers sharing +// one Store, so admission races and catch-up are exercised. +// 2. Per-exchange netting: every admitted record in the store moves exactly +// two accounts, by -amount and +amount — not just a sum that happens to +// come out zero at the end. +// 3. Determinism: balances are a function of the sealed record, so a fresh +// Open over the same store reproduces every balance and the zero sum. +func TestInvariant_L1_ZeroSumBalance(t *testing.T) { + const ( + debitCap = 300 + spendsPerPeer = 60 + ) + f := newFixture(t, ModeCredit, debitCap, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06) + l2, err := Open(f.genesis, f.dir, f.store) + if err != nil { + t.Fatalf("second Open over the shared store: %v", err) + } + ledgers := []*Ledger{f.ledger, l2} + + // One goroutine per payer, so each payer's nonces stay sequential while + // different payers race each other across both ledgers. + var wg sync.WaitGroup + admitted := make([]int, len(f.members)) + errs := make(chan error, len(f.members)) + for pi := range f.members { + wg.Add(1) + go func(pi int) { + defer wg.Done() + rng := rand.New(rand.NewSource(int64(pi) + 1)) + payer := f.members[pi] + var nonce uint64 + for i := 0; i < spendsPerPeer; i++ { + ti := (pi + 1 + rng.Intn(len(f.members)-1)) % len(f.members) + s := f.spend(payer, f.members[ti], Amount(rng.Intn(120)+1), nonce+1) + switch err := ledgers[i%2].Post(s); { + case err == nil: + nonce++ + admitted[pi]++ + case errors.Is(err, ErrLimit): + // The uniform cap is the only legitimate refusal here. + default: + errs <- err + return + } + } + }(pi) + } + wg.Wait() + close(errs) + for err := range errs { + t.Fatalf("unexpected rejection under concurrency: %v", err) + } + + total := 0 + for _, n := range admitted { + total += n + } + if total == 0 { + t.Fatal("no spends admitted; the binding exercised nothing") + } + + // (2) Every sealed exchange nets to zero on its own. + recs, err := f.store.All() + if err != nil { + t.Fatalf("store.All: %v", err) + } + if len(recs) != total { + t.Fatalf("store holds %d records, want %d admitted spends", len(recs), total) + } + fold := map[AccountID]int64{} + for i, r := range recs { + s, ok := r.(Spend) + if !ok { + t.Fatalf("record %d is %T, want Spend", i, r) + } + if s.From == s.To { + t.Fatalf("record %d moves one account, want two", i) + } + before := fold[s.From] + fold[s.To] + fold[s.From] -= int64(s.Amount) + fold[s.To] += int64(s.Amount) + if after := fold[s.From] + fold[s.To]; after != before { + t.Fatalf("record %d changed the pair's sum by %d, want 0", i, after-before) + } + } + + // End state: the sum over all accounts is exactly zero, on both live + // ledgers (after catch-up) and on a fresh replay. + reopened, err := Open(f.genesis, f.dir, f.store) + if err != nil { + t.Fatalf("reopen for replay: %v", err) + } + // The live ledgers may each lag the store (a ledger only catches up when + // it posts), so they are checked for a zero sum over their own applied + // prefix; the fresh replay is checked against the full fold. + var sum int64 + for _, m := range f.members { + b := int64(reopened.Balance(m.id)) + if b != fold[m.id] { + t.Fatalf("replayed balance %d != folded balance %d: balances must be a function of the record", b, fold[m.id]) + } + if b < -debitCap { + t.Fatalf("balance %d below the uniform cap -%d", b, debitCap) + } + sum += b + } + if sum != 0 { + t.Fatalf("sum of balances = %d after %d concurrent spends, want exactly 0", sum, total) + } + for li, l := range ledgers { + var s int64 + for _, m := range f.members { + s += int64(l.Balance(m.id)) + } + if s != 0 { + t.Fatalf("live ledger %d sum = %d, want exactly 0 at every applied prefix", li, s) + } + } +} diff --git a/internal/economy/conformance_test_v2.go b/internal/economy/conformance_test_v2.go new file mode 100644 index 0000000..7f2824e --- /dev/null +++ b/internal/economy/conformance_test_v2.go @@ -0,0 +1,246 @@ +package covenant + +// JFA conformance bindings for the covenant layer. +// +// Test names carry the Janus registry ID they bind (jfa-conformance-suite.py +// in NTARI-RAND/Janus), so a CI log parser can map a PASS/FAIL back to the +// invariant: TestInvariant__, with any "-" in the ID written "_". + +import ( + "go/ast" + "go/parser" + "go/token" + "os" + "path/filepath" + "regexp" + "strings" + "testing" + "time" +) + +// TestInvariant_L8_NoScalarAggregation binds registry ID L8: "full +// distribution carried; no scalar score anywhere." +// +// The package-local tripwires (TestNoCollapseTripwire, +// TestNoCollapseFunctionTripwire, TestStandingNotSerializable) hold the line +// inside internal/covenant. This binding adds the two things L8 says that +// those do not: +// +// - "full distribution carried": two members a mean cannot tell apart are +// told apart by Standing, level by level, and nothing is lost. +// - "anywhere": the whole module, not just this package, exposes no scalar +// summary of reputation. +func TestInvariant_L8_NoScalarAggregation(t *testing.T) { + t.Run("full distribution carried", testL8FullDistribution) + t.Run("no scalar anywhere in the module", testL8NoScalarInModule) +} + +// testL8FullDistribution records two subjects whose verdicts have the SAME +// arithmetic mean (+2) but different shapes, and requires Standing to keep +// every level's count, so the one who caused harm is never averaged into the +// same number as the one who never did. +func testL8FullDistribution(t *testing.T) { + steady, steadyPub, _ := testMember(0x30) // 2, 2, 2, 2 mean 2 + harmed, harmedPub, _ := testMember(0x40) // -1, 4, 4, 1 mean 2 + + dir := dirMap{steady: steadyPub, harmed: harmedPub} + seals := sealSet{} + b := testBook(t, dir, seals, NewMemStore()) + + cats := []string{"reliability", "usability", "performance", "support"} + type verdict struct { + subject MemberID + level Level + } + var verdicts []verdict + for _, l := range []Level{LevelBasicSatisfaction, LevelBasicSatisfaction, LevelBasicSatisfaction, LevelBasicSatisfaction} { + verdicts = append(verdicts, verdict{steady, l}) + } + for _, l := range []Level{LevelNoTrust, LevelDelight, LevelDelight, LevelBasicPromise} { + verdicts = append(verdicts, verdict{harmed, l}) + } + + for i, v := range verdicts { + assessor, pub, priv := testMember(byte(0x90 + i)) + dir[assessor] = pub + ex := ref(byte(0x40 + i)) + seals.seal(ex, assessor, v.subject) + a := Assessment{ + Assessor: assessor, + Subject: v.subject, + Exchange: ex, + Relation: RelationTrade, + Category: cats[i%len(cats)], + Level: v.level, + IssuedAt: time.Unix(1700001000+int64(i), 0).UTC(), + } + if v.level == LevelNoTrust { + a.CommentHash = commentHash(byte(0xD0 + i)) + } + a.Sign(priv) + if err := b.Record(a); err != nil { + t.Fatalf("Record #%d = %v", i, err) + } + } + + // Guard the premise: a mean would make these two identical. + mean := func(subject MemberID) float64 { + var sum, n int + for _, v := range verdicts { + if v.subject == subject { + sum += int(v.level) + n++ + } + } + return float64(sum) / float64(n) + } + if mean(steady) != mean(harmed) { + t.Fatalf("fixture broken: means %v and %v differ; the test must pit equal means against unequal shapes", mean(steady), mean(harmed)) + } + + want := map[MemberID]map[Level]int{ + steady: {LevelBasicSatisfaction: 4}, + harmed: {LevelNoTrust: 1, LevelDelight: 2, LevelBasicPromise: 1}, + } + got := map[MemberID]Distribution{} + for subject, wantCounts := range want { + s, err := b.Standing(subject) + if err != nil { + t.Fatalf("Standing = %v", err) + } + d := s.Relation(RelationTrade).Overall() + got[subject] = d + counted := 0 + for _, l := range Levels() { + if d.Count(l) != wantCounts[l] { + t.Errorf("count at %q = %d, want %d", l, d.Count(l), wantCounts[l]) + } + counted += d.Count(l) + } + if counted != d.Total() || d.Total() != 4 { + t.Errorf("per-level counts sum to %d, Total = %d, want both 4: the distribution must be lossless", counted, d.Total()) + } + if h := s.Relation(RelationTrade).Harm(); h != wantCounts[LevelNoTrust] { + t.Errorf("Harm = %d, want %d: the breach must never be diluted by volume", h, wantCounts[LevelNoTrust]) + } + } + + differ := false + for _, l := range Levels() { + if got[steady].Count(l) != got[harmed].Count(l) { + differ = true + } + } + if !differ { + t.Fatal("Standing cannot tell apart two members with different shapes: the distribution has been collapsed") + } +} + +// collapseName is the vocabulary of a scalar reputation summary. It is +// narrower than the package tripwire's collapsePattern because it scans the +// whole module, where "sum" (checksum) and "weight" (techtree citation +// breakdown, a struct of counts) are legitimate. +var collapseName = regexp.MustCompile(`(?i)(mean|avg|average|score|median|percentile|rank)`) + +// collapseJSON catches a scalar leaking onto the wire through a struct tag. +var collapseJSON = regexp.MustCompile(`json:"(?i:[a-z_]*(mean|avg|average|score|median|percentile|rank|rating)[a-z_]*)[",]`) + +// reputationDirs are the packages that hold or serve member reputation; any +// floating-point field there is a scalar waiting to happen. +var reputationDirs = map[string]bool{ + filepath.Join("internal", "covenant"): true, + filepath.Join("internal", "consumerapi"): true, +} + +// testL8NoScalarInModule parses every shipped Go file in the module and +// fails on: an exported function or method named like a collapse; an +// exported function or method returning a float; a struct tag that puts a +// score-like field on the wire; or a float field in a reputation package. +func testL8NoScalarInModule(t *testing.T) { + root := moduleRoot(t) + fset := token.NewFileSet() + scanned := 0 + err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + if n := info.Name(); n == ".git" || n == "vendor" || n == "testdata" { + return filepath.SkipDir + } + return nil + } + if !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") { + return nil + } + rel, _ := filepath.Rel(root, path) + f, err := parser.ParseFile(fset, path, nil, 0) + if err != nil { + t.Fatalf("parse %s: %v", rel, err) + } + scanned++ + inReputation := reputationDirs[filepath.Dir(rel)] + + ast.Inspect(f, func(n ast.Node) bool { + switch x := n.(type) { + case *ast.FuncDecl: + if !x.Name.IsExported() { + return true + } + if collapseName.MatchString(x.Name.Name) { + t.Errorf("%s: exported %q is named like a scalar reputation summary", rel, x.Name.Name) + } + if x.Type.Results != nil { + for _, r := range x.Type.Results.List { + if isFloat(r.Type) { + t.Errorf("%s: exported %q returns a float; a scalar is one step from a score", rel, x.Name.Name) + } + } + } + case *ast.StructType: + for _, fld := range x.Fields.List { + line := fset.Position(fld.Pos()).Line + if fld.Tag != nil && collapseJSON.MatchString(fld.Tag.Value) { + t.Errorf("%s:%d: struct tag %s puts a score-like field on the wire", rel, line, fld.Tag.Value) + } + if inReputation && isFloat(fld.Type) { + t.Errorf("%s:%d: float field in a reputation package", rel, line) + } + } + } + return true + }) + return nil + }) + if err != nil { + t.Fatalf("walk: %v", err) + } + if scanned < 20 { + t.Fatalf("scanned only %d source files from %s — the scan is not looking at the module", scanned, root) + } +} + +func isFloat(e ast.Expr) bool { + id, ok := e.(*ast.Ident) + return ok && (id.Name == "float32" || id.Name == "float64") +} + +// moduleRoot walks up from the package directory to the directory holding +// go.mod. +func moduleRoot(t *testing.T) string { + t.Helper() + dir, err := os.Getwd() + if err != nil { + t.Fatalf("Getwd: %v", err) + } + for { + if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil { + return dir + } + parent := filepath.Dir(dir) + if parent == dir { + t.Fatal("no go.mod above the covenant package") + } + dir = parent + } +} From 8d3f1df72f23c4c37769c096e4d2372bb71397d9 Mon Sep 17 00:00:00 2001 From: Calvin Secrest Date: Wed, 30 Sep 2026 13:55:28 -0400 Subject: [PATCH 3/4] Rename conformance_test_v1.go to conformance_test.go Signed-off-by: Calvin Secrest --- internal/economy/{conformance_test_v1.go => conformance_test.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename internal/economy/{conformance_test_v1.go => conformance_test.go} (100%) diff --git a/internal/economy/conformance_test_v1.go b/internal/economy/conformance_test.go similarity index 100% rename from internal/economy/conformance_test_v1.go rename to internal/economy/conformance_test.go From 8e10172e571fee57138cd99071d416c096b9c32e Mon Sep 17 00:00:00 2001 From: Calvin Secrest Date: Wed, 30 Sep 2026 13:56:48 -0400 Subject: [PATCH 4/4] Rename conformance_test_v2.go to conformance_test.go Signed-off-by: Calvin Secrest --- .../conformance_test_v2.go => covenant/conformance_test.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename internal/{economy/conformance_test_v2.go => covenant/conformance_test.go} (100%) diff --git a/internal/economy/conformance_test_v2.go b/internal/covenant/conformance_test.go similarity index 100% rename from internal/economy/conformance_test_v2.go rename to internal/covenant/conformance_test.go