Artifact 8148fdb210067ab5eaa93734ee4f01c17ac386d62cead954255d37f7334cc892:
- File
internal/content/cone_reference_facts.go
— part of check-in
[2ea6479ba9]
at
2026-10-22 00:04:54
on branch feat/horizon-cones
— 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. (user: [email protected], size: 3034) [annotate] [blame] [check-ins using] [more...]
package content import ( "context" "github.com/danmestas/go-libfossil/db" "github.com/danmestas/go-libfossil/internal/cone" "github.com/danmestas/go-libfossil/internal/deck" libfossil "github.com/danmestas/go-libfossil/internal/fsltype" ) // ConeReferenceFacts holds immutable raw-artifact facts for one database // snapshot, without a cone key. The owner must validate that snapshot before // using these facts against a database that may have changed. // // Loading reads and inflates the whole store, so it serves whole-store work // (a pull's inventory, compaction, verification, crosslink), never a clone // round. Retained decks are classification projections, not complete // artifacts suitable for marshaling. type ConeReferenceFacts struct { names map[libfossil.FslID]string held map[string]libfossil.FslID nodes map[libfossil.FslID]*deck.Deck byUUID map[string]*deck.Deck manifests map[string]*deck.Deck // check-ins, for a horizon's tree walk records []coneReferenceRecord roots []*deck.Deck unreadable []string } type coneReferenceRecord struct { uuid string deck *deck.Deck } func LoadConeReferenceFacts(ctx context.Context, q db.Querier) (*ConeReferenceFacts, error) { // Discovery is guarded before allocation. Counting only check-ins/trees // does not bound wiki, forum, cluster or file storage. Oversized // snapshots fail rather than allocate first and discover their budget // after parsing. facts, _, err := loadConeReferenceFactsBounded(ctx, q, ReferenceBudget{ Artifacts: coneReferenceNodes, StoredBytes: 64 << 20, InflatedBytes: 128 << 20, ExpandedBytes: 128 << 20, Cards: 1 << 20, }, true) return facts, err } // Classify creates request-local membership; neither the facts nor previous // classifications are changed. Maps of raw identities and decks are read-only. func (facts *ConeReferenceFacts) Classify( ctx context.Context, c *cone.Cone, ) (*ConeReferences, error) { if facts == nil || ctx == nil { panic("content.ConeReferenceFacts.Classify: nil facts or context") } p := &ConeReferences{ names: facts.names, held: facts.held, nodes: facts.nodes, outside: map[string]bool{}, inside: map[string]bool{}, required: map[string]bool{}, tree: map[string]bool{}, } if err := ctx.Err(); err != nil { return nil, err } if c == nil { return p, nil } for _, uuid := range facts.unreadable { p.required[uuid] = true } for _, record := range facts.records { if err := ctx.Err(); err != nil { return nil, err } d := record.deck if d.Type != deck.Submanifest { p.required[record.uuid] = true } for _, f := range d.F { if f.UUID == "" { continue } if c.Covers(f.Name) { p.inside[f.UUID] = true } else { p.outside[f.UUID] = true } } p.structural(d) } w := coneReferenceWalk{ctx: ctx, cone: c, refs: p, nodes: facts.byUUID, path: map[string]bool{}} for _, root := range facts.roots { if err := w.walk(root.S, "", 1, true); err != nil { return nil, err } } return p, nil }