Skip to content
go-libfossil Timeline
Login

Timeline

50 descendants of 6d8ccec5da3de55a

2026-10-22
17:21
Merge branch 'wip/horizon-render' into feat/horizon-cones

History views render a labelled horizon boundary as the horizon (draft-fossil-horizon-00 §5.4): Repo.Ancestry ends there without an error, Repo.Timeline and LogEntry report it in HorizonParents, and repo timeline prints it as (horizon). An unlabelled absent parent renders as before. check-in: d0c44dbc25 user: [email protected] tags: feat/horizon-cones, trunk

17:21
feat(history): render a horizon boundary as the horizon

A parent the origin labelled below a structural horizon is the horizon itself (draft-fossil-horizon-00 5.4), but the history views showed it as a bare parent hash, and the ancestry walk tried to read an event for it.

LogEntry and TimelineEntry gain HorizonParents, the subset of Parents that carries a boundary label; Ancestry ends at such a parent without an error; repo timeline prints it as "parent: <hash> (horizon)". An absent parent that is not labelled is transit and renders as before. check-in: ee3ccc7c67 user: [email protected] tags: feat/horizon-cones, trunk

17:02
docs: the structural horizon in the changelog and the sparse page check-in: 43d6d7f88b user: [email protected] tags: feat/horizon-cones, trunk
17:01
Merge branch 'wip/horizon-structural' into feat/horizon-cones

The structural horizon (draft-fossil-horizon-00 Appendix A step 5): a store at structural depth elides manifests and nodes below the horizon and keeps boundary labels; the client sends horizon-structural and horizon-seed and holds the seed it is sent; the origin restricts manifests to the held set, labels the boundary, mints and sends the seed, and serves structural only when the seed is current. Interoperates with zit at 9bf7ebfe0f in both directions. check-in: 78ed31bf91 user: [email protected] tags: feat/horizon-cones, trunk

16:46
fix(sync): decide a structural store's depth only on a pull's reply

A push alone carries no horizon pragma, so its reply says nothing about how the horizon is served, and a structural store pushing to its origin fell back to the content horizon. The first reply decides only when the request pulled. Preconditions of the structural serving and receiving paths are asserted. check-in: 1abe24cd2b user: [email protected] tags: feat/horizon-cones, trunk

16:30
fix(sync): ask for what a peer announces to a structural store

A structural store elides a phantom nothing it holds names, and an igot makes exactly such a phantom, so a structural store serving a push never asked for the pushed check-in and a pull relied on the session alone to want it. A name the peer announced in the exchange is now asked for when nothing held names it, on both sides; a labelled boundary or a tag's below-horizon target stays elided. check-in: 26aff82ccc user: [email protected] tags: feat/horizon-cones, trunk

16:28
fix(content): take the nodes a structural origin withholds outside the cone

Composed with path members, a structural horizon keeps K's trees, but an origin may withhold their nodes outside the cone, as zit's does since 2b635ea5ce. Those nodes classified transit, so a clone from zit stalled waiting on them. A node the path members exclude is now elided under a structural horizon, as a path cone elides it; zit interop under cone src proves it both ways. check-in: 74ac2ea4c1 user: [email protected] tags: feat/horizon-cones, trunk

16:25
test(conformance): cover the structural horizon's requirements

Cases for the ordinary commit from a structural store (§5.7), the leaf verdict over one (§3.1), verification and crossing (§5.4, §5.5), and the requirements table updated to what the structural horizon now covers; what remains, with the Appendix A step that covers it, stays in the notes. check-in: d9e74912e8 user: [email protected] tags: feat/horizon-cones, trunk

16:25
feat: expose the structural horizon and name it where history ends

Horizon gains Structural, whose zero value is the content horizon, so a Horizon built before it means what it meant; clone and sync results say how many replies served the structural level and whether the store fell back. repo clone and repo cone store take --structural with --horizon.

A structural store's history ends at its horizon, which the commands now say rather than leave bare phantoms (draft-fossil-horizon-00 §5.4, §5.5): repo info names the horizon and the check-ins below it, repo verify counts them apart from damage, and checkout, diff and annotate of a check-in below C fail naming the horizon and the deepen that reaches it. check-in: c61a17d9ab user: [email protected] tags: feat/horizon-cones, trunk

16:14
test(conformance): the structural horizon between go-libfossil and zit

zit clones and pulls at a structural horizon from go-libfossil's origin, and go-libfossil clones and pulls at one from zit serve. Each origin withholds the manifests below the horizon, labels the boundary and sends the seed; each client stays structural and derives the branch, leaf and tag state of the held set as a dense clone of the same origin does, including after a tag below the horizon moves the state at an entry and re-seeds it. zit holds the very seed go-libfossil mints. check-in: b76863738f user: [email protected] tags: feat/horizon-cones, trunk

16:12
feat(sync): serve and take the structural horizon

The origin now answers pragma horizon-structural (draft-fossil-horizon-00 §6.1, §6.2): it keeps only the manifests and nodes the held set reaches, labels every boundary hash at the head of each reply (§6.2.2), mints the seed for C and sends it, pragma ahead of its file card, when the client offers another name (§6.2.3), and advertises the structural level only then; a request it cannot place or seed is served as content. It refuses a seed offered to it and withholds a gimme'd manifest below the horizon.

A structural store's requests carry horizon-structural and the seed it holds. It routes the announced card to seed.Hold after checking its name and shape, never storing or announcing it, records the labels, and applies the seed after each reply and after the closing link. A first reply that does not serve structural with a seed, or a seed failing provenance, makes it a content-horizon store for good, reported in the result, and the manifests below the horizon are fetched (§5.1, §5.2.4, §6.3). check-in: dc33bca3a6 user: [email protected] tags: feat/horizon-cones, trunk

16:12
fix(manifest): link a structural store's check-ins against absent parents

A sparse store defers a check-in whose parent it lacks, so that mlinks wait on a complete parent tree. Under a structural horizon (draft -fossil-horizon-00 §5.1) the parents below the horizon never arrive, so C and every entry of K stayed unlinked. An absent source no longer defers there; the check-in links against the phantom as a dense store links it. check-in: 79ec72f88e user: [email protected] tags: feat/horizon-cones, trunk

16:12
fix(horizon/seed): apply the seed only to linked check-ins

The seed's rows name their target as their source, and the crosslink treats a blob that sources a tagxref row as already linked. A sync stores an entry before it links it, so a seed applied in between left the entry unlinked for good: no event, no plink, no leaf. Apply now skips a target the store has not linked; the sync layer applies again after the round that links it. check-in: c7c0c130bd user: [email protected] tags: feat/horizon-cones, trunk

15:49
feat(content): hold and classify a structural horizon store

A structural-horizon clone (draft-fossil-horizon-00 §5.1) keeps only the manifests and nodes its held set reaches, so its phantoms need classes the content horizon never had: a parent the origin labelled below the horizon is elided, an unlabelled one stays transit because it may only be late (§5.3), and a phantom only a tag or cherry-pick names is a manifest below the horizon nothing in K reaches. SetStoreCone stops refusing the depth, and keeps the boundary labels beside the store cone, dropping them when the horizon moves since they name what lies below one C. check-in: 31b140507a user: [email protected] tags: feat/horizon-cones, trunk

15:33
Merge branch 'wip/horizon-docs' into feat/horizon-cones

A docs page for sparse cones and horizons: concepts, the library API, the CLI, interop with zit and fossil 2.28, and the limits as they stand. check-in: 441604df52 user: [email protected] tags: feat/horizon-cones, trunk

15:33
docs(site): state the leaves rule as the draft now defines it

The page described the leaves rule as each childless check-in's ancestry back to its meeting point with C, the rule before zit#265. The rule at the pinned draft holds every descendant of each meeting point, which is why it holds nearly all of fossil-scm.org's history: old unmerged branches fork near the root. check-in: 097153487d user: [email protected] tags: feat/horizon-cones, trunk

15:33
docs(site): add sparse cones and horizons page check-in: 8be82ed25d user: [email protected] tags: feat/horizon-cones, trunk
15:27
docs(changelog): the horizon seed check-in: 53f9af98e3 user: [email protected] tags: feat/horizon-cones, trunk
15:26
Merge branch 'wip/horizon-seed' into feat/horizon-cones

The structural horizon's seed (draft-fossil-horizon-00 §5.2): an origin mints a control artifact carrying the tags in force at every entry of the held set, a clone holds it outside the artifact store where no sync, clone or rebuild can reach it, and the derivation applies it so every check-in in K derives as it does densely. Byte-identical to zit's seed when the latest summarized event is on a whole second. Conflicts in the horizon suite and table kept both sides; the seed's zit test now probes for a horizon-capable zit like the other horizon interop tests. check-in: 12dcbdeed6 user: [email protected] tags: feat/horizon-cones, trunk

15:20
feat(horizon): write the seed's D card to the second, as zit does

deck.Marshal writes D to the millisecond, so a go-libfossil seed could never match zit's bytes or name even over identical state. Mint now writes D as YYYY-MM-DDTHH:MM:SS, keeping the round-up to the next whole second that Section 5.2.2's "no earlier than the latest tag event" needs, and recomputes Z over the body. Over whole-second tag events the seed is now byte-identical to zit's, which the interop test asserts for the full seed and its name. check-in: 038e50befb user: [email protected] tags: feat/horizon-cones, trunk

15:08
docs(changelog): the horizon cursor check-in: 303e4c28b7 user: [email protected] tags: feat/horizon-cones, trunk
15:08
fix(sync): key the horizon cursor by the HTTP transport's URL

The cursor slice keeps a client's horizon cursor per origin, named by the internal SyncOpts.Origin, but the public Repo.Sync never set it, so a library or CLI sync kept no cursor and every request announced in full. The HTTP transport now names its peer by its URL with any password removed, as last-sync-url keeps it, and Sync passes that through. Other transports name no origin and keep announcing in full, which §6.2.6 allows. The test pulls with a credential-bearing URL and finds the cursor under the password-free one, and no config row holding the password. check-in: 331b540f1c user: [email protected] tags: feat/horizon-cones, trunk

15:06
test(conformance): probe zit for horizon support in the cursor interop check-in: 8135205010 user: [email protected] tags: feat/horizon-cones, trunk
15:06
Merge branch 'wip/horizon-cursor' into feat/horizon-cones

The horizon cursor (draft-fossil-horizon-00 §6.2.6): an origin that classified a request answers pragma horizon-cursor with its position, a converged client records it per origin and cone and sends it back, and neither side announces again what the other already has. Interoperates with zit both ways. check-in: f704c6d58b user: [email protected] tags: feat/horizon-cones, trunk

15:06
Merge branch 'feat/horizon-cones' into wip/horizon-cursor check-in: ec8b4c770b user: [email protected] tags: feat/horizon-cones, trunk
15:05
feat(sync): horizon cursor, origin and client

A horizon pull announced the client's whole holding with igot on every request, and the origin answered with its whole kept inventory: one card per artifact, both ways, on every pull. draft-fossil-horizon-00 §6.2.6 replaces both with a position.

The origin values pragma horizon-cursor right after horizon-served in every reply to a horizon pull, as zit places it. Its position is rid order (the clone SEQNO sense), stopped short of the lowest phantom: a filled phantom keeps its old rid, so a cursor covering it would hide the arrival. Receiving a cursor, any reply that pulls announces nothing at or below it. A filled phantom that a check-in from an earlier receipt already named as parent is recorded as the reclassification position; a cursor below it is answered as if absent. A cursor beyond the origin's own position is ignored.

The client keeps, per origin and cone, the last reply's cursor and the rid count the origin holds through, sends the cursor on every request, announces nothing on a pull-only request and only what it appended on one that sends. Sessions name their origin with SyncOpts.Origin; without it nothing is kept and every request announces in full. check-in: b61bd3475c user: [email protected] tags: feat/horizon-cones, trunk

14:48
docs(changelog): the public horizon API check-in: 76d780fde1 user: [email protected] tags: feat/horizon-cones, trunk
14:48
test(conformance): skip horizon interop on a zit that predates horizons

TestZitHorizonInterop took whatever zit was on PATH. A zit release from trunk reports the same version as the horizon-rfc build but has no --horizon flag, so the test failed in zit's argument parser. The new zitHorizonBinary probes the binary for horizon support, as zitBinary already probes for send-submanifests, and skips it, or fails the run under REQUIRE_ZIT_BIN=1, with the reason. check-in: 27b85364f2 user: [email protected] tags: feat/horizon-cones, trunk

14:48
Merge branch 'wip/horizon-api' into feat/horizon-cones

The public horizon API in the shape approved: a horizon is part of Cone (NewHorizonCone, Cone.Horizon, Cone.WithHorizon), store cones carry it through SetStoreCone and SetStoreConeContext and the new CloneOpts.StoreCone, checkout cones refuse it with ErrHorizonCheckout, and the CLI takes --horizon and --leaves on repo clone and repo cone store and --no-horizon on repo cone store. check-in: 964b91f682 user: [email protected] tags: feat/horizon-cones, trunk

14:47
feat(horizon): expose content horizons in the public API and CLI

A store cone could carry a horizon only through internal/cone, so library users and the CLI had no way to clone into or edit a content-horizon store. Add Horizon, HorizonRule, NewHorizonCone, Cone.Horizon and Cone.WithHorizon over the internal cone (always the content cut), carry it through SetStoreCone/SetStoreConeContext (deepening fetches what the older check-in keeps) and CloneOpts.StoreCone, and add --horizon, --leaves, --no-horizon and --cone to repo clone and repo cone store.

A horizon is a property of the store, not of a checkout (draft-fossil-horizon-00 3.2), so checkout cones carrying one are refused with ErrHorizonCheckout, and widening the store from a checkout cone keeps the store's horizon. check-in: e5904fc85b user: [email protected] tags: feat/horizon-cones, trunk

14:33
docs(changelog): horizon serving check-in: b59ba60848 user: [email protected] tags: feat/horizon-cones, trunk
14:33
Merge branch 'wip/horizon-serve' into feat/horizon-cones

Appendix A step 3 without the cursor: the client sends pragma horizon, the origin computes the held set over its dense graph, sends only the blobs above the horizon, inflates crossing deltas, sends no cluster, answers horizon-served content and refuses what it cannot cover (§6.4). Against a horizon-ignorant origin the client requests what its clusters name, as transit (§4.5 after zit#266). Interoperates with zit both ways. check-in: 84be5ad824 user: [email protected] tags: feat/horizon-cones, trunk

14:33
feat(horizon): mint, hold and apply the structural-horizon seed

A structural-horizon clone lacks C's ancestry, so tag propagation has nowhere to start: every check-in in K would derive no branch, and closed markers and names set below C would vanish. The seed of draft-fossil-horizon-00 Section 5.2 restates the dense tag state at each entry of K so the unmodified derivation comes out exact.

internal/horizon/seed mints it at an origin (T cards per row in force at C and each check-in of K whose primary parent is outside K; D the latest summarized event rounded up to a whole second; U the origin's login), and holds it at a clone in a table of its own, outside the artifact store, so nothing that enumerates or serves artifacts can send it. It is applied when held, by Rebuild, and by seed.Apply, which the sync layer calls after each round. Replacing or discarding a seed re-derives the store, since only a re-derivation separates the old seed's rows from those real artifacts wrote. A Verifier hook carries the Section 5.2.4 provenance policy; no signing is implemented.

The crosslink-table clearing moves from verify into manifest.Rederive so Rebuild and the seed share one owner of re-derivation. The cards match zit's seed byte for byte in T and U, and each implementation derives from the other's seed what it derives from its own. check-in: 45fb2c7ed4 user: [email protected] tags: feat/horizon-cones, trunk

13:14
Merge branch 'wip/horizon-measure' into feat/horizon-cones

The Appendix A step 4 measurement: an opt-in corpus test reporting what a structural horizon saves over a content horizon at several depths. check-in: 5430186930 user: [email protected] tags: feat/horizon-cones, trunk

13:14
test(horizon): measure whether a structural horizon pays on a real repository

Opt-in corpus test (LIBFOSSIL_CORPUS) for draft-fossil-horizon-00 Appendix A step 4: for C at 10, 100 and 1000 check-ins back along trunk's primary-parent chain and at the check-in nearest a year before the tip, compare a content horizon's transfer with a structural horizon's under both held-set rules. It works on a copy of the repository. The seed is estimated as one control artifact with one T card per tag in force at each entry of K, uncompressed.

fossil-scm.org clone: 67894 artifacts, 37.2 MiB stored, 8425.2 MiB expanded (21008 manifests, 8.9 MiB stored, 1099.8 MiB expanded; no sub-manifest nodes; 6263 other structure, 691 clusters). Runtime 58s, peak heap 161 MiB. Share = (content - structural) / content.

descendants rule K below (n / MiB exp) content MiB exp structural MiB exp share exp share stored 10 back 12 20996 / 1098.9 1136.5 37.6 96.7% 49.0% 100 back 121 20887 / 1089.9 1262.0 172.1 86.4% 48.5% 1000 back 1746 19262 / 962.2 2341.0 1379.0 41.1% 40.0% 1 year (426 back) 796 20212 / 1036.1 1846.3 810.3 56.1% 44.5%

leaves rule: K = 20907 of 21008 at every depth; share 0.0% expanded, -7.7% stored (the seed outweighs the 101 manifests below). check-in: c6ef4af29f user: [email protected] tags: feat/horizon-cones, trunk

12:05
fix(sync): chase cluster names only from horizon-ignorant origins

The re-pinned §4.5 (zit#266) splits the cluster rule by origin. An origin that says horizon-served announces what it keeps directly, so a horizon store must not request a name only a cluster gives it. A horizon-ignorant origin, fossil(1) among them, folds 100 or more unclustered artifacts into a cluster as it answers a pull and announces only the cluster, so a new check-in may be named nowhere else: the store must request what the cluster names, as transit, and classify what arrives. A horizon store acted on no cluster from either, and so never received such a check-in from fossil.

CrosslinkCluster now mints member phantoms in every store, as fossil does; the received-file filter already drops the cluster and the blobs below the horizon at the end of the transfer. Whether to act on a cluster is decided where the origin is known: once a reply of the session says horizon-served content, the sync client asks for no cluster catalog and the sync and clone clients store no cluster file card, so no cluster-only phantom is minted from such an origin.

A real fossil(1) case pulls a leaf announced only through the cluster fossil makes, and holds it and its blob without holding the cluster. check-in: 588d5b264e user: [email protected] tags: feat/horizon-cones, trunk

12:02
Merge branch 'feat/horizon-cones' into wip/horizon-serve check-in: c6feb4349a user: [email protected] tags: feat/horizon-cones, trunk
12:02
feat(sync): serve the content horizon and send its pragma

A horizon store asked every peer for the whole history and threw away what fell below C on arrival, so a horizon clone cost a dense clone's bandwidth. draft-fossil-horizon-00 §6 lets the request carry the horizon and the origin restrict what it sends.

The client sends `pragma horizon HASH RULE` after its path-cone pragmas on every clone and pull, never horizon-structural, and counts the replies that say `pragma horizon-served content` (HorizonServed on the clone and sync results, internal and public). It still classifies every reply locally, so a horizon-ignorant peer stays correct (§6.3).

The handler reads the pragma into the request's cone and serves it through the existing keep machinery rather than a parallel path: the pull inventory's keep set drops the blobs below the horizon (via ConeReferences.HorizonBelow) and every cluster; the clone walk walks the tree of a held-set check-in only, counts a parent as sent only inside the held set, and sends every sub-manifest node at its row while sending no cluster. A crossing delta is inflated through the same sourceKept test the path cone uses, charged to the round's batch. req-clusters under a horizon answers nothing, and every reply says horizon-served content.

A horizon store refuses, through refuseClone, a dense clone and a horizon whose held set is not inside its own, both computed over the store with horizon.Place. That is the spec's subset test; zit refuses any other C or rule. A path-only store now also refuses a horizon-only request, which keeps every path.

Interop with zit at horizon-rfc bbd1435674 holds both ways: zit clones and pulls at a horizon from the handler, the client from `zit serve`, and each side's horizon store refuses the other's dense clone. check-in: 352a0d9e0b user: [email protected] tags: feat/horizon-cones, trunk

11:32
conform(horizon): pin at horizon-rfc 9bf7ebfe0f, the zit#266 correction

zit#266 reported that a horizon clone pulling from stock Fossil never learns a check-in Fossil announced only inside a cluster: xfer.c clusters 100 or more unclustered artifacts and then announces only the cluster. §4.5 at 9bf7ebfe0f binds the cluster prohibition to an origin whose replies carry horizon-served, and has the clone request a horizon-ignorant origin's cluster members as transit and classify what arrives. HZ-4.5-1 is restated; implementing it waits for horizon-served, which the horizon pragmas bring. The sync draft on that branch differs from the trunk copy pinned here only by §9's editorial list of extension pragma names. check-in: d5926b325c user: [email protected] tags: feat/horizon-cones, trunk

11:20
docs(changelog): the content horizon in stores and sync check-in: bf99482411 user: [email protected] tags: feat/horizon-cones, trunk
11:19
Merge branch 'wip/horizon-sync' into feat/horizon-cones

A content-horizon store against any peer: below-horizon blobs it is sent are recorded as elided rather than stored, it holds and requests no cluster, and a below-horizon blob an operation needs is fetched on demand. check-in: 27b8439777 user: [email protected] tags: feat/horizon-cones, trunk

11:18
feat(sync): classify horizon stores locally against dense peers

No peer speaks the horizon pragmas yet, so a content-horizon store receives whatever a dense peer sends (draft-fossil-horizon-00 §6.3). The received-content filter now applies the horizon's classification, read from internal/content rather than re-derived: blobs below the horizon become elided phantoms and are counted in the result's Elided. Blobs no held manifest names yet stay as unreferenced holdings, and retained delta bases stay transit (§4.2).

A horizon store also acts on no cluster (§4.5). Crosslinking a cluster applies its tag but makes no member phantoms. The filter drops the cluster's content, and classification elides any phantom that carries the cluster tag. The client stops sending req-clusters. Path-cone-only stores keep today's cluster behaviour.

Conformance cases run against go-libfossil's handler and fossil(1): clone and pull, deepen, demand fetch through UpdateContext, transit deltas, and cluster rules. check-in: f1f4240f40 user: [email protected] tags: feat/horizon-cones, trunk

02:59
docs(changelog): sparse classification at real-repository scale check-in: 6e6281148f user: [email protected] tags: feat/horizon-cones, trunk
02:59
Merge branch 'wip/sparse-scale' into feat/horizon-cones

Sparse-store classification sized to what it proves rather than to the store: file content is never read, completely linked check-ins answer from the crosslink tables, and a content horizon reads only its held set's trees. A path-cone store of fossil-scm.org classifies in about 2 s at a 65 MiB peak, where v0.14.0 failed with a byte-budget error. check-in: cd1b25e114 user: [email protected] tags: feat/horizon-cones, trunk

02:57
fix(content): classify sparse stores from crosslink tables, not the whole store

Sparse classification read, inflated and delta-expanded every held blob, file content included, under a whole-store byte budget. On a clone of the Fossil SCM repository (67,894 blobs, 8.4 GB expanded) LoadAbsences failed at once with "byte budget exceeded", so no path-cone or content-horizon store of a real repository could classify its phantoms, and pull, verify and compaction failed with it.

Reference facts now come from the tables wherever they provably stand in for raw cards. A blob the crosslinker recorded as no artifact and mlink names as a file version is never read. A completely linked check-in (event row, no orphan row, a primary parent edge) whose primary parent and baseline trees are themselves reproduced contributes its mlink rows as F cards and its plink, cherrypick and tagxref rows as references; by induction over the primary chain those rows give the same (name, file) pairs as its raw cards. Everything else is still read raw: unlinked and orphaned check-ins, roots, check-ins whose parent never arrived, other artifacts, and every check-in once the store has seen a sub-manifest, since trees are not crosslinked. Budgets are charged per round for the selected blobs and their delta ancestors only, and a base's bytes are released once its last delta is applied.

A content horizon now reads only its held set's manifests and the baselines and nodes they reach, on demand, with one visited set and a byte budget scaled to the held set.

On the Fossil SCM clone, cone {src} classifies in about 2 s at a 65 MiB peak heap, reading 8,655 of 67,894 blobs; the inside/outside sets equal the all-raw result, and Outside/Required agree on every artifact. check-in: b28c06506b user: [email protected] tags: feat/horizon-cones, trunk

00:12
Merge branch 'wip/horizon-content' into feat/horizon-cones

The content horizon's store classification: a blob is elided when no check-in in the held set names it through its full tree, a kept blob is in both keep sets under a path cone, structure is never elided under a horizon, and a horizon the store cannot place elides nothing, as zit does. check-in: bc54a61b2c user: [email protected] tags: feat/horizon-cones, trunk

00:05
Merge branch 'feat/horizon-cones' into wip/horizon-content

# Conflicts: # conformance/horizon_test.go # conformance/requirements/horizon.tsv check-in: 80c8df6a74 user: [email protected] tags: feat/horizon-cones, trunk

00:04
feat(content): classify blobs below a content horizon as elided

A store cone may now carry a content horizon (draft-fossil-horizon-00 §3.2, §4.1-§4.3). Classification computes the held set K from the store's own check-in graph and walks K's full trees -- F cards directly, through B baselines and through S sub-manifest nodes, with one visited set over structural names -- because mlink records a file version only where it changed, and a blob introduced below C and unchanged through C is named by C's tree alone. Nothing about K is stored: every classification recomputes it, so moving C or losing the cone is a reload, never a migration.

Blobs no K check-in names are elided; with path members as well a blob is kept only when it is in both keep sets. Under a horizon nothing structural is elided, by the horizon or by path members, and delta bases of held artifacts and shunned names keep their classes. A C the store does not hold elides nothing, as zit's classification does: eliding everything would leave the pull that brings a deepened C nothing to ask for. A structural horizon is refused by SetStoreCone until it is implemented.

internal/content now imports internal/horizon, so the held-set tests, which commit through internal/manifest, move to an external test package to avoid an import cycle. check-in: 2ea6479ba9 user: [email protected] tags: feat/horizon-cones, trunk

00:03
conform(horizon): pin at horizon-rfc bbd1435674; the leaves rule over P cards

zit's author called horizon-rfc bbd1435674 ready to implement against, and zit#265 settled the leaves rule there against fossil's leaf: a childless check-in is one no P card names, open or closed, and K under the leaves rule is C's descendants plus every descendant of each check-in where a childless check-in's ancestry meets C's. Both rules now yield a set closed under descendants, so leaves and branch tips derive exactly.

HeldSet follows zit's computation at that check-in (src/sparse.zig, ClassifyState.extend): meeting points are the members of C's ancestry a childless check-in's parent walk reaches without passing one, and a childless check-in is skipped only when it descends from C. The tests that encoded the old rule are restated: a merged-but-open branch tip adds nothing, a closed branch's tip does, the fork's descendants on C's side are held, and an unrelated root adds nothing.

The draft copy, the README pin and the table follow: HZ-3.1-1, HZ-5.2-1 (the seed applies to every entry of K) and HZ-6.2-11 (announce only in reply to a request that pulls) are restated, and HZ-3.1-4 (closure under descendants) is new, with a case checking that no child of a held check-in falls outside K under either rule. check-in: a68f89e414 user: [email protected] tags: feat/horizon-cones, trunk

2026-10-21
22:21
docs(changelog): the horizon held set check-in: 707fa08656 user: [email protected] tags: feat/horizon-cones, trunk
22:21
refactor(horizon): name the held-set rule with the cone's HorizonRule

Both slices defined the descendants/leaves rule: cone.HorizonRule for the serialized member and horizon.Rule for the walk. One concept gets one type; the walk now takes the value the cone carries. check-in: 6d8ccec5da user: [email protected] tags: feat/horizon-cones, trunk