Artifact 9dd2547cd125e0dd08098ca05f89e45667c90d842536fbb3616304f4343fba96:
- File
internal/manifest/rederive.go
— part of check-in
[45fb2c7ed4]
at
2026-10-22 14:33:04
on branch feat/horizon-cones
— 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. (user: [email protected], size: 2099) [annotate] [blame] [check-ins using] [more...]
package manifest import ( "fmt" "github.com/danmestas/go-libfossil/db" ) // rederivedTables are the derived tables a crosslink sweep owns. Rederive // empties them so every blob is a candidate again, then lets CrosslinkRebuild // refill them exactly as a clone that received every artifact would. This is // the same set the repository-state corpus test empties before its sweep, // which matches `fossil rebuild` in event, plink, mlink, tagxref, leaf and // phantom. orphan is among them because the sweep files a delta manifest whose // baseline is absent there; `fossil rebuild` drops and recreates it the same // way. var rederivedTables = []string{ "event", "plink", "leaf", "mlink", "tagxref", "forumpost", "attachment", "backlink", "cherrypick", "orphan", } // RederivedTables returns the tables Rederive empties and refills. func RederivedTables() []string { return append([]string{}, rederivedTables...) } // Rederive empties the crosslink-derived tables and crosslinks every artifact // again, entirely within tx (CrosslinkRebuild), returning how many it linked. // The sweep writes event/plink/mlink/tagxref, creates phantoms for absent // targets, parents and file content, reparents, then repairs leaf and // tag-propagation state over the whole plink graph -- the order `fossil // rebuild` uses. A table may be absent: a repository created by fossil(1) // without the feature that owns it (forumpost until the first forum post, say) // has no such table until the sweep creates it, and there is nothing to clear. func Rederive(tx *db.Tx) (int, error) { if tx == nil { panic("manifest.Rederive: tx must not be nil") } for _, table := range rederivedTables { var n int err := tx.QueryRow("SELECT count(*) FROM sqlite_master WHERE type='table' AND name=?", table).Scan(&n) if err != nil { return 0, fmt.Errorf("manifest.Rederive: table %s: %w", table, err) } if n == 0 { continue } if _, err := tx.Exec("DELETE FROM " + table); err != nil { return 0, fmt.Errorf("manifest.Rederive: clear %s: %w", table, err) } } return CrosslinkRebuild(tx) }