Artifact 1f931c66bd33b50fcc5d53f1cce45dceb81c586c3da51c7209238e368b0a0ae6:
- File
internal/verify/verify.go
— part of check-in
[c61a17d9ab]
at
2026-10-22 16:25:41
on branch feat/horizon-cones
— 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. (user: [email protected], size: 3815) [annotate] [blame] [check-ins using] [more...]
// Package verify provides comprehensive repository verification and rebuild. package verify import ( "fmt" "time" "github.com/danmestas/go-libfossil/internal/content" libfossil "github.com/danmestas/go-libfossil/internal/fsltype" "github.com/danmestas/go-libfossil/internal/repo" ) // IssueKind categorizes the type of verification issue found. type IssueKind int const ( IssueHashMismatch IssueKind = iota IssueBlobCorrupt IssueDeltaDangling IssuePhantomOrphan IssueEventMissing IssueEventMismatch IssueMlinkMissing IssuePlinkMissing IssueTagxrefMissing IssueFilenameMissing IssueLeafIncorrect IssueMissingReference ) // Issue represents a single verification problem found in the repository. type Issue struct { Kind IssueKind RID libfossil.FslID UUID string Table string Message string } // Report contains the results of a repository verification pass. type Report struct { Issues []Issue BlobsChecked int BlobsOK int BlobsFailed int BlobsSkipped int MissingRefs int // Phantoms counts the artifacts the store names but lacks and still // wants: transit phantoms, which synchronization requests. Shunned and // elided artifacts are not among them. Phantoms int // Elided counts the artifacts deliberately absent under the store cone // (draft-fossil-sparse-cone-00 §5.2). Elision is healthy: it is never an // Issue, so a store complete within its cone verifies OK, and it is // counted apart from damage and from Phantoms so that neither hides the // other. It is zero for a dense store. Elided int // BelowHorizon counts, among Elided, the check-ins below a structural // horizon (draft-fossil-horizon-00 §5.4): its boundary and the manifests // only a tag names. They are the horizon, not missing parents. BelowHorizon int TablesRebuilt []string Duration time.Duration } // OK returns true if no issues were found during verification. func (r *Report) OK() bool { return len(r.Issues) == 0 } // addIssue appends a new issue to the report. func (r *Report) addIssue(issue Issue) { r.Issues = append(r.Issues, issue) } // Verify performs comprehensive repository verification. // It checks blob integrity, delta chains, phantom records, and derived tables, // and counts what the store lacks by absence class. // Returns a report of all issues found. Never stops early - reports all problems. func Verify(r *repo.Repo) (*Report, error) { if r == nil { panic("verify: nil repo") } start := time.Now() report := &Report{} // Phase 1: Blob integrity (content hash verification) if err := checkBlobs(r, report); err != nil { return nil, err } // Phase 2: Structural integrity (delta chains, phantoms) if err := checkDeltaChains(r, report); err != nil { return nil, err } if err := checkPhantoms(r, report); err != nil { return nil, err } // Phase 3: Derived tables (event, mlink, plink, tagxref, filename, leaf) if err := checkDerived(r, report); err != nil { return nil, err } // Phase 4: what the store lacks, by absence class. if err := countAbsences(r, report); err != nil { return nil, err } report.Duration = time.Since(start) return report, nil } // countAbsences fills Phantoms and Elided from the store's phantoms, // classified against its store cone. It runs after the derived-table checks // because elision is read from mlink, a derived table. func countAbsences(r *repo.Repo, report *Report) error { if r == nil { panic("verify.countAbsences: nil *repo.Repo") } if report == nil { panic("verify.countAbsences: nil *Report") } a, err := content.LoadAbsences(r.DB()) if err != nil { return fmt.Errorf("verify: %w", err) } report.Phantoms = a.Count(content.Transit) report.Elided = a.Count(content.Elided) report.BelowHorizon = a.BelowHorizon() return nil }