Artifact 49f99d2a9930b4c07fc7f79fdfd4ffabc694758b1b5df54efbddf2e63be16197:
- File
conformance/horizon_seed_date_test.go
— part of check-in
[5d0ef566aa]
at
2026-10-24 14:34:00
on branch feat/horizon-dfloor-receive
— Merge feat/seed-receive-drop; re-pin horizon draft to zit caeef421ee
Re-pins draft-fossil-horizon-00 to zit trunk check-in caeef421ee (file artifact ab18fa7f89), which changes §5.2.2, §5.2.3 and §6.2.3 and adds two Appendix C bullets. This brings in the local feat/seed-receive-drop branch, which implements the receive rule, and resolves it against main (#477 chain-walk bounds, #478 seed binding).
§5.2.2, the seed's D (tracked in #479): - Mint dates a seed at max(tagxref.mtime) over every row in the repository, cancels and propagated rows included, as zit's latestTagEvent does. It was the latest of the rows the seed summarizes. - A cancel of the newest tag, or a reopened closed branch, therefore no longer mints a seed earlier than the one a clone holds, which the clone's D floor refused as a replay.
§5.2.3, the receive rule: - A pull, clone or sync drops a seed by this store's label or by a binding card when it can read it, and the link step refuses one completed later. - The draft also requires that a delta kept without its base, pushed or received, is not announced or served while it cannot be expanded. feat/seed-receive-drop only withheld pushed ones. The pending mark moves into storeDeltaContent, so a pull marks too, and a clone marks what is still unreadable when it ends (seedcheck.WithholdUnreadable). That avoids judging every dangling file delta of a clone a second time. - Since #478 minted seeds carry a binding card. The seed-shaped-ordinary cases therefore strip it, and a new test checks that a seed with the card is never linked.
Conformance rows: - HZ-5.2-2 takes the new D text. - HZ-5.2-4 loses the old ordinary-artifact sentence. - HZ-6.2-6 says the D is fixed by the repository's tag events. - New rows: HZ-5.2-14 (the receive-path drop; PARTIAL, since a seed completed in a later round is kept inert, and was counted when its delta arrived), HZ-5.2-15 (the dropped-hash record) and HZ-5.2-16 (withholding unexpandable deltas). - New cases: two D cases mirroring zit's "horizon seed date" regression, a cancel on C and a cancel at an ancestor below it, and five receive cases. - The deepen-floor case now replays an older seed, since an honest origin no longer mints one. (user: [email protected], size: 3315) [annotate] [blame] [check-ins using] [more...]
package conformance import ( "context" "testing" "github.com/danmestas/go-libfossil/internal/cone" libfossil "github.com/danmestas/go-libfossil/internal/fsltype" "github.com/danmestas/go-libfossil/internal/horizon/seed" "github.com/danmestas/go-libfossil/internal/repo" "github.com/danmestas/go-libfossil/internal/sync" "github.com/danmestas/go-libfossil/internal/tag" ) // A seed's D is the latest tag event in the repository, cancels included // (§5.2.2), so no change to tag state yields an earlier D and a clone's D // floor (§5.2.4) never refuses a re-seed as a replay. These mirror zit's // "horizon seed date" regression (src/horizon_e2e.zig). func horizonSeedDateCases() []Case { return []Case{horizonSeedDateCancel, horizonSeedDateAncestor} } var ( horizonSeedDateCancel = Case{ID: "horizon.5.2.2.seed-date-cancel", Req: "HZ-5.2-2"} horizonSeedDateAncestor = Case{ID: "horizon.5.2.2.seed-date-ancestor-cancel", Req: "HZ-5.2-2"} ) // hzDateStep is one tag event at the origin. type hzDateStep struct { target func(x *hzStruct) libfossil.FslID name string typ int } func TestHorizonSeedDate(t *testing.T) { type step = hzDateStep onC := func(x *hzStruct) libfossil.FslID { return x.s.c3 } onAncestor := func(x *hzStruct) libfossil.FslID { return x.s.c1 } check(t, horizonSeedDateCancel, func() error { return hzSeedDateSteps(t, []step{ {onC, "late", tag.TagSingleton}, {onC, "late", tag.TagCancel}, {onC, "closed", tag.TagSingleton}, {onC, "closed", tag.TagCancel}, }) }) // A tag propagated from c1, below C, becomes the newest row at C; // cancelling it at c1 deletes the row it propagated to C, the latest of // the rows the old D was dated from. The cancel's own row stays at c1. check(t, horizonSeedDateAncestor, func() error { return hzSeedDateSteps(t, []step{ {onAncestor, "tint", tag.TagPropagating}, {onAncestor, "tint", tag.TagCancel}, }) }) } // hzSeedDateSteps clones the structural store, then applies each tag event // at the origin, an hour after the last, pulling after each: the clone must // take every re-seed and stay structural. func hzSeedDateSteps(t *testing.T, steps []hzDateStep) error { x := newHZStruct(t) r, _, err := x.clone(t, x.peer()) if err != nil { return err } for i, s := range steps { x.s.tag(t, s.target(x), s.name, s.typ, "") before, err := seed.Held(r.DB()) if err != nil { return err } res, err := sync.Sync(context.Background(), r, x.peer(), sync.SyncOpts{Pull: true}) if err != nil { return expectf("step %d (%s %d): pull: %v", i, s.name, s.typ, err) } if len(res.Errors) != 0 || res.HorizonFallback { return expectf("step %d (%s %d): errors %q, fallback %v", i, s.name, s.typ, res.Errors, res.HorizonFallback) } if err := hzStillStructural(r); err != nil { return expectf("step %d (%s %d): %v", i, s.name, s.typ, err) } after, err := seed.Held(r.DB()) if err != nil || after == nil || string(after) == string(before) { return expectf("step %d (%s %d): the clone did not take the re-seed (%v)", i, s.name, s.typ, err) } } return nil } func hzStillStructural(r *repo.Repo) error { d, err := hzStructDepth(r) if err != nil { return err } if d != cone.DepthStructural { return expectf("the store fell back to depth %v", d) } return nil }