Documentation
¶
Overview ¶
Package snapshot captures per-commit structural state (modules, files, symbols, scalar metrics) and persists it under `.krit/snapshots/` for later timeline and diff queries.
Index ¶
- Constants
- Variables
- func BlobPath(root, sha string) (string, error)
- func CaptureManifest(root string, res *Result, repoRoot, kritVersion string) (string, error)
- func EnsureGitignoreEntry(repoRoot, pattern string) (added bool, err error)
- func FindingsPath(root, sha string) (string, error)
- func HookPath(repoRoot string) string
- func InstallHook(repoRoot string, force bool) (string, error)
- func RedactBlob(b *Blob)
- func RedactFindings(f *Findings)
- func ResolveCommitSHA(repoRoot, ref string) (string, error)
- func ResolveParentSHAs(repoRoot, sha string) ([]string, error)
- func Save(root string, b *Blob) (string, error)
- func SaveFindings(root string, f *Findings) (string, error)
- func SaveManifest(root string, m *Manifest) (string, error)
- func SaveMetrics(root string, m *Metrics) (string, error)
- func SaveResult(root string, res *Result, repoRoot, kritVersion string) (string, error)
- func SnapshotsDir(repoRoot string) string
- func UninstallHook(repoRoot string) (string, error)
- type BackfillEvent
- type BackfillOptions
- type BackfillResult
- type Blob
- type CaptureOptions
- type DiffResult
- type DiffSide
- type Entry
- type File
- type FileMetrics
- type FileRef
- type Findings
- type FindingsRunOptions
- type GateConstraint
- type GateOptions
- type GateResult
- type GateThreshold
- type GateViolation
- type Index
- type Manifest
- type ManifestCounts
- type MetricDelta
- type Metrics
- type Module
- type ModuleDep
- type ModuleEdge
- type ModuleMetrics
- type PruneEntry
- type PruneOptions
- type PruneResult
- type Reachability
- type Result
- type SimulateEvent
- type SimulateOptions
- type SimulatePoint
- type SimulateResult
- type SimulateSource
- type Symbol
- type SymbolRef
- type TimelinePoint
- type TimelineQuery
- type TimelineScope
Constants ¶
const DefaultKeepFeatureAge = 30 * 24 * time.Hour
DefaultKeepFeatureAge is the retention window for snapshots only reachable from feature branches.
const DefaultKeepOrphanAge = 7 * 24 * time.Hour
DefaultKeepOrphanAge is the retention window for unreachable snapshots — shorter than feature retention because force-pushes / rebases produce these and they're typically the user wanting them gone.
const FindingsSchemaVersion = 1
FindingsSchemaVersion versions the findings sidecar wire format. Bump when ByRule/ByRuleFile semantics change in a way that breaks decode of previously written sidecars.
const HookMarker = "# krit-snapshot-hook"
HookMarker is the line InstallHook injects after the shebang so future InstallHook / UninstallHook calls can recognise their own output without overwriting a user's custom hook.
const IndexSchemaVersion = 1
IndexSchemaVersion versions the index.json layout. Bumped when an older reader couldn't tolerate a new field; readers that see a higher version fall back to the per-manifest scan to stay correct across rolling deploys.
const ManifestSchemaVersion = 2
ManifestSchemaVersion versions the JSON manifest layout.
v1 — flat Files / Symbols / Modules count fields at the top level.
v2 — counts moved into a nested Counts struct so future additions
(LinesOfCode, FailedFiles, FindingsTotal) don't widen the
top-level shape. v1 readers still see the flat fields: writers
populate both, and the v1->v2 migrator copies them in on read
so older payloads compare correctly.
const MetricsSchemaVersion = 1
MetricsSchemaVersion versions Metrics independently of Blob, so adding a new scalar to the rollup does not invalidate graph blobs.
const RedactionMarker = "krit-redacted:"
RedactionMarker is the prefix prepended to every hashed identifier so a reader can tell at a glance that a value was redacted (vs. a legitimate identifier that happens to look like hex). Diff readers that join across snapshots key on the full string so the marker is part of the join key — two redacted snapshots with the same source FQN still match.
const SchemaVersion = 1
SchemaVersion is bumped when Blob's wire format changes in a way that breaks decode of existing on-disk snapshots. Cross-version diffs compare this before reading the rest.
const SnapshotsGitignorePattern = ".krit/snapshots/"
SnapshotsGitignorePattern is the entry EnsureGitignoreEntry adds to the repo's root .gitignore so users don't accidentally commit captured snapshot blobs and manifests.
Variables ¶
var DefaultPermanentBranches = []string{"main", "master"}
DefaultPermanentBranches is the fallback set of always-keep branches when PruneOptions.PermanentBranches is empty.
var PostCommitHook string
Functions ¶
func CaptureManifest ¶
CaptureManifest builds a Manifest from a captured Result and writes it next to the blob and metrics files. repoRoot is used for the git parent lookup; pass "" to skip the lookup.
func EnsureGitignoreEntry ¶
EnsureGitignoreEntry appends pattern to the repo root .gitignore if it is not already present (as an exact line match, ignoring surrounding whitespace). It is best-effort: if the .gitignore can't be read or written, the error is returned but callers may choose to ignore it.
Returns added=true when the pattern was newly written, and false if it was already present (or if pattern was empty).
func FindingsPath ¶
FindingsPath returns the on-disk location for a snapshot's findings sidecar. Mirrors BlobPath / MetricsPath layout (sharded by sha[:2]).
func InstallHook ¶
InstallHook writes the embedded post-commit hook. With force=false it refuses to overwrite an existing non-krit hook so a user's hand-rolled hook is preserved.
func RedactBlob ¶
func RedactBlob(b *Blob)
RedactBlob replaces every source-identifier field on b with a stable one-way hash and flips b.Redacted. Idempotent: a blob that is already Redacted is returned unchanged so a CaptureOptions.Redact caller that runs RedactBlob defensively doesn't double-hash.
Fields redacted:
- RepoRoot — absolute path on the capture machine, leaks the user's directory layout. Cleared (not hashed) because it is not a join key.
- File.Path — repo-relative path. Each path segment is hashed individually so the directory shape (depth, fan-out) survives while names do not.
- Symbol.Name / FQN / Owner / Package / Signature / File — all source identifiers. Hashing preserves stable join keys for diff while making reverse lookup infeasible.
Fields kept clear: SchemaVersion, KritVersion, CommitSHA, CapturedAt, Symbol.Kind / Visibility / Line / Language / IsOverride / IsTest, File.Language / Lines / Bytes, Module.Path / Dir / Dependencies / Consumers. Module paths are gradle coordinates — public by convention and not a code-secrecy concern; if a downstream user needs them hashed too they can extend this function.
func RedactFindings ¶
func RedactFindings(f *Findings)
RedactFindings rewrites Findings.ByRuleFile so the per-file path keys are hashed using the same scheme as RedactBlob. Rule IDs in ByRule and ByRuleFile stay in clear text — they are public krit identifiers and contain no caller-controlled content. Idempotent.
Callers that pass the result to Diff must redact both snapshots consistently; mixing redacted and raw findings panics the diff guard on Findings.Redacted (see Diff).
func ResolveCommitSHA ¶
ResolveCommitSHA returns `git rev-parse <ref>` inside repoRoot. ref defaults to "HEAD".
func ResolveParentSHAs ¶
ResolveParentSHAs returns the parent commit shas for sha. A merge returns >1; the initial commit returns nil. Errors propagate to the caller; manifest capture swallows them so a missing git stays non-fatal.
func Save ¶
Save writes b atomically to its content-addressed location under root and returns the path written.
func SaveResult ¶
SaveResult persists the graph blob, metrics rollup, and a freshly-built manifest for a captured Result. Returns the blob path.
func SnapshotsDir ¶
SnapshotsDir returns the snapshots root inside repoRoot.
func UninstallHook ¶
UninstallHook removes the post-commit hook iff it carries HookMarker; a hand-rolled hook is left untouched.
Types ¶
type BackfillEvent ¶
BackfillEvent is one commit's outcome. Kind is "captured", "skipped", or "failed".
type BackfillOptions ¶
type BackfillOptions struct {
RepoRoot string
Branch string
Since time.Duration
MaxCommits int
Workers int
Force bool
KritVersion string
Reporter func(BackfillEvent)
}
BackfillOptions controls a Backfill invocation. Snapshots land in the primary repo's `.krit/snapshots/`; commits are checked out into temporary worktrees that are torn down after capture.
type BackfillResult ¶
func Backfill ¶
func Backfill(opts BackfillOptions) (BackfillResult, error)
type Blob ¶
type Blob struct {
SchemaVersion int
KritVersion string
CommitSHA string
CapturedAt int64
RepoRoot string
Modules []Module
Files []File
Symbols []Symbol
// Redacted is true when the blob's symbol names, owner/package
// strings, signatures, and file paths have been replaced with
// stable one-way hashes via Redact. Snapshots captured with
// CaptureOptions.Redact = true (or post-processed through
// RedactBlob) flip this flag. Diff guards against comparing
// redacted vs non-redacted blobs because the join keys
// (FQN / Signature / Path) live in different namespaces.
Redacted bool
}
Blob is the captured structural snapshot of a project at one commit, persisted as zstd(gob(Blob)).
Module.Dir, File.Path, and Symbol.File are repo-relative; RepoRoot is the absolute path on the capture machine and is not portable.
func MigrateBlob ¶
MigrateBlob walks blob's schema up to the current SchemaVersion. Returns blob unchanged when already current; refuses to operate on nil, on a future-schema blob (this binary is older than the data), or when the per-step migration table doesn't cover the gap.
type CaptureOptions ¶
type CaptureOptions struct {
RepoRoot string
CommitSHA string
KritVersion string
Workers int
// Now allows tests to inject a deterministic capture time.
Now func() time.Time
// WithFindings, when true, runs krit's rule pipeline against the
// worktree and attaches a per-rule findings rollup to Result. The
// scan dominates capture wall-time, so this is opt-in.
WithFindings bool
// Redact, when true, post-processes the captured blob (and the
// findings rollup, if WithFindings) through RedactBlob /
// RedactFindings before returning. Use this for repos with
// strict-secrecy requirements: snapshots persist Symbol.FQN /
// Owner / Package / Signature plus File.Path, all of which can
// embed proprietary identifiers. Redact replaces them with
// stable one-way hashes so the snapshot still supports diff /
// timeline / metrics but cannot be reverse-engineered to a
// source identifier. The flag is captured into Blob.Redacted /
// Findings.Redacted / Manifest.Redacted so Diff can guard
// against comparing redacted vs raw.
Redact bool
}
CaptureOptions controls a single Capture invocation.
type DiffResult ¶
type DiffResult struct {
From DiffSide `json:"from"`
To DiffSide `json:"to"`
AddedFiles []FileRef `json:"added_files,omitempty"`
RemovedFiles []FileRef `json:"removed_files,omitempty"`
AddedSymbols []SymbolRef `json:"added_symbols,omitempty"`
RemovedSymbols []SymbolRef `json:"removed_symbols,omitempty"`
AddedModules []string `json:"added_modules,omitempty"`
RemovedModules []string `json:"removed_modules,omitempty"`
AddedEdges []ModuleEdge `json:"added_edges,omitempty"`
RemovedEdges []ModuleEdge `json:"removed_edges,omitempty"`
RepoMetrics map[string]MetricDelta `json:"repo_metrics,omitempty"`
ModuleMetrics map[string]map[string]MetricDelta `json:"module_metrics,omitempty"`
// FindingsByRule is populated only when both snapshots carry a
// findings sidecar AND share a RuleSetHash. Cross-rule-set
// comparisons stay nil so callers don't mistake an apples-to-oranges
// delta for a real drift signal.
FindingsByRule map[string]MetricDelta `json:"findings_by_rule,omitempty"`
// FindingsRuleSetMismatch is set when both sides have findings
// sidecars whose RuleSetHash differs. Consumers should refuse to
// report a findings delta in that case.
FindingsRuleSetMismatch bool `json:"findings_rule_set_mismatch,omitempty"`
}
DiffResult is the structural delta between two captured snapshots.
func Diff ¶
func Diff(root, fromSHA, toSHA string) (*DiffResult, error)
Diff loads the two captured snapshots and returns the structural delta. Both blobs and metrics are read; either side may have a metrics rollup absent without aborting the diff.
type DiffSide ¶
type DiffSide struct {
CommitSHA string `json:"commit_sha"`
CapturedAt int64 `json:"captured_at"`
KritVersion string `json:"krit_version,omitempty"`
BlobSchema int `json:"blob_schema"`
MetricsSchema int `json:"metrics_schema"`
}
DiffSide identifies one end of a diff and tags it with the krit and blob schema versions so consumers can guard against incomparable reads.
type FileMetrics ¶
type FileRef ¶
type FileRef struct {
Path string `json:"path"`
Module string `json:"module,omitempty"`
Language string `json:"language,omitempty"`
}
FileRef names a file in the diff. Module is the gradle path, "" when the file is outside any discovered module.
type Findings ¶
type Findings struct {
SchemaVersion int
CommitSHA string
RuleSetHash string
// ByRule maps rule ID -> total finding count across the snapshot.
ByRule map[string]int
// ByRuleFile maps rule ID -> (repo-relative file path -> count).
// Optional; consumers that only need per-rule totals can ignore it.
ByRuleFile map[string]map[string]int
// Redacted is true when ByRuleFile's path keys have been replaced
// with stable one-way hashes by RedactFindings. Rule IDs stay in
// clear text because they are public krit identifiers. Mirrors
// Blob.Redacted so a snapshot is wholly redacted or wholly raw.
Redacted bool
}
Findings is the per-commit per-rule findings rollup persisted next to the structural blob. simulate reads it as a Timeline; diff uses RuleSetHash to refuse cross-rule-set comparisons.
func LoadFindings ¶
func RunFindings ¶
func RunFindings(ctx context.Context, repoRoot, commitSHA string, opts FindingsRunOptions) (*Findings, error)
RunFindings runs the full rule pipeline against repoRoot once and returns a populated Findings ready to persist alongside the structural blob. The function exists so internal/snapshot can keep the heavy pipeline import contained and so Capture can opt in via a single call.
type FindingsRunOptions ¶
type FindingsRunOptions struct {
// Config is the resolved krit.yml; nil falls back to defaults.
Config *config.Config
// ActiveRules is the rule set dispatched against the worktree. When
// empty, RunFindings selects the default active set.
ActiveRules []*api.Rule
// Workers overrides per-phase worker counts.
Workers int
// IncludeGenerated forwards through to ParsePhase.
IncludeGenerated bool
// RepoRelativeTo, when non-empty, rewrites the absolute file paths
// returned in ByRuleFile to be repo-relative. Empty leaves paths
// untouched.
RepoRelativeTo string
}
FindingsRunOptions tunes RunFindings without forcing every caller to build a full pipeline.ProjectArgs.
type GateConstraint ¶
type GateConstraint string
GateConstraint identifies which limit a GateThreshold imposed.
const ( ConstraintMaxAbsolute GateConstraint = "max_absolute" ConstraintMaxIncrease GateConstraint = "max_increase" ConstraintMaxIncreasePct GateConstraint = "max_increase_pct" )
type GateOptions ¶
type GateOptions struct {
Root string
FromSHA string
ToSHA string
Thresholds []GateThreshold
}
type GateResult ¶
type GateResult struct {
From string `json:"from"`
To string `json:"to"`
Violations []GateViolation `json:"violations,omitempty"`
}
func Gate ¶
func Gate(opts GateOptions) (*GateResult, error)
type GateThreshold ¶
type GateThreshold struct {
Module string
Metric string
MaxAbsolute *float64
MaxIncrease *float64
MaxIncreasePct *float64
}
GateThreshold expresses one constraint on a metric. Empty Module targets the repo-scope reading; a non-empty Module targets that module's reading from DiffResult.ModuleMetrics. At least one of MaxAbsolute / MaxIncrease / MaxIncreasePct must be set.
type GateViolation ¶
type GateViolation struct {
// Module is empty for repo-scope thresholds; otherwise the module
// path the violation applies to (e.g. ":feature:checkout").
Module string `json:"module,omitempty"`
Metric string `json:"metric"`
Constraint GateConstraint `json:"constraint"`
Limit float64 `json:"limit"`
Got float64 `json:"got"`
From float64 `json:"from"`
To float64 `json:"to"`
}
type Index ¶
Index is the rollup of every captured manifest, persisted as a single JSON file at <root>/index.json so `snapshot status` and the MCP `status` op are O(1) reads instead of O(N) directory walks.
Missing or unreadable index files are not fatal: callers fall back to LoadManifests' per-sha scan, so older repos and partially- captured snapshot trees keep working.
type Manifest ¶
type Manifest struct {
SchemaVersion int `json:"schema_version"`
CommitSHA string `json:"commit_sha"`
ParentSHAs []string `json:"parent_shas,omitempty"`
CapturedAt int64 `json:"captured_at"`
KritVersion string `json:"krit_version"`
BlobSchema int `json:"blob_schema"`
MetricsSchema int `json:"metrics_schema"`
// Files / Symbols / Modules are kept at the top level for v1
// reader compatibility. Prefer Counts in new code; the two move
// in lockstep.
Files int `json:"files"`
Symbols int `json:"symbols"`
Modules int `json:"modules"`
Counts ManifestCounts `json:"counts"`
RuleSetHash string `json:"rule_set_hash,omitempty"`
// Redacted mirrors Blob.Redacted (and Findings.Redacted, which
// must agree). When true the snapshot's symbol names, FQNs,
// owners, packages, signatures, and file paths are one-way
// hashes rather than source identifiers. Diff refuses to compare
// a redacted manifest against a raw one.
Redacted bool `json:"redacted,omitempty"`
}
Manifest is the JSON sidecar describing a captured snapshot. Greppable without krit; lets callers answer "what shas have we captured, at what krit version, with what parents" without decoding the binary blobs.
RuleSetHash is reserved for the findings-rollup phase: when non-empty it identifies the rule registry + config used to compute findings, so cross-version diffs can refuse to compare incomparable counts.
The flat Files / Symbols / Modules fields and the nested Counts struct carry the same numbers — writers populate both so v1 readers (and grep/jq pipelines) keep working through the transition. New code should prefer Counts.
func LoadManifest ¶
func LoadManifests ¶
LoadManifests returns every captured sha's manifest under root, sorted by sha. Missing or malformed manifests are skipped silently.
Prefers the index.json rollup (O(1) read) when present; falls back to the legacy per-sha scan when the rollup is missing (older captures), unreadable (corruption), or carries a newer schema.
func MigrateManifest ¶
MigrateManifest is the *Manifest counterpart to MigrateBlob.
type ManifestCounts ¶
type ManifestCounts struct {
Files int `json:"files"`
Symbols int `json:"symbols"`
Modules int `json:"modules"`
}
ManifestCounts is the per-snapshot count rollup nested under Manifest at v2. New count fields land here without touching the top-level shape.
type MetricDelta ¶
type MetricDelta struct {
From float64 `json:"from"`
To float64 `json:"to"`
Delta float64 `json:"delta"`
}
MetricDelta carries before/after scalars and their difference.
type Metrics ¶
type Metrics struct {
SchemaVersion int
CommitSHA string
CapturedAt int64
Files []FileMetrics
Modules []ModuleMetrics
}
Metrics is the scalar rollup persisted next to a Blob. Timeline queries load these directly without decoding the graph.
func LoadMetrics ¶
func MigrateMetrics ¶
MigrateMetrics is the *Metrics counterpart to MigrateBlob.
type ModuleEdge ¶
type ModuleEdge struct {
From string `json:"from"`
To string `json:"to"`
Configuration string `json:"configuration"`
}
ModuleEdge is one outgoing dependency edge (`from` depends on `to`).
type ModuleMetrics ¶
type PruneEntry ¶
type PruneEntry struct {
CommitSHA string
CapturedAt time.Time
Reach Reachability
// Pruned is true when Prune deleted (or, in DryRun, would have
// deleted) this snapshot.
Pruned bool
// Reason is a short human-readable note: "kept (permanent)",
// "kept (feature, age 12d < 30d)", "pruned (orphan, age 9d > 7d)".
Reason string
}
PruneEntry is one captured snapshot's prune classification.
type PruneOptions ¶
type PruneOptions struct {
// Root is the snapshots directory (i.e. SnapshotsDir(repoRoot)).
Root string
// RepoRoot is the working-tree root passed to git.
RepoRoot string
// PermanentBranches lists the branch names treated as
// always-keep. Empty means use DefaultPermanentBranches.
PermanentBranches []string
// KeepFeatureAge is the retention window for feature-branch-only
// snapshots. Zero means use DefaultKeepFeatureAge.
KeepFeatureAge time.Duration
// KeepOrphanAge is the retention window for unreachable
// snapshots. Zero means use DefaultKeepOrphanAge.
KeepOrphanAge time.Duration
// DryRun reports what would be pruned without removing anything.
DryRun bool
// Now is the reference clock for age comparisons. Zero means
// time.Now(). Test seam.
Now time.Time
// ReachableSHAs returns the set of commit shas reachable from any
// of the given refs. Nil means "use the production git-backed
// implementation". Test seam.
ReachableSHAs func(repoRoot string, refs []string) (map[string]bool, error)
// AllRefSHAs returns the set of commit shas reachable from any
// ref in the repository. Nil means "use the production git-backed
// implementation". Test seam.
AllRefSHAs func(repoRoot string) (map[string]bool, error)
}
PruneOptions configures Prune. Reachability is resolved via the ReachableSHAs / AllRefSHAs hooks, which are normally backed by git in production and stubbed in tests.
type PruneResult ¶
type PruneResult struct {
Entries []PruneEntry
// Pruned counts entries actually removed (or, in DryRun, that
// would have been removed).
Pruned int
// Errors collects per-sha removal failures so the caller can
// surface them; the run continues past individual errors.
Errors []error
}
PruneResult summarises a Prune run.
func Prune ¶
func Prune(opts PruneOptions) (*PruneResult, error)
Prune walks the snapshots tree and applies retention rules. Errors are accumulated per-sha rather than aborting the run so a single broken entry doesn't prevent cleanup of the rest.
type Reachability ¶
type Reachability string
Reachability classifies a captured snapshot for retention purposes.
const ( // ReachPermanent — sha is reachable from a permanent branch // (default main / master). Always kept. ReachPermanent Reachability = "permanent" // ReachFeature — sha is reachable from at least one ref but none // of the permanent branches. Kept while younger than KeepFeatureAge. ReachFeature Reachability = "feature" // ReachOrphan — sha is not reachable from any current ref. Kept // while younger than KeepOrphanAge. ReachOrphan Reachability = "orphan" )
type Result ¶
Result pairs the structural blob with the metrics rollup derived from the same parse. Findings is populated only when CaptureOptions.WithFindings is set.
func Capture ¶
func Capture(opts CaptureOptions) (*Result, error)
type SimulateEvent ¶
type SimulateEvent struct {
Kind string
CommitSHA string
Findings int
Duration time.Duration
Error error
}
SimulateEvent is one commit's outcome. Kind is "scored" or "failed".
type SimulateOptions ¶
type SimulateOptions struct {
RepoRoot string
Rule string
Branch string
Since time.Duration
MaxCommits int
Workers int
// KritBin is the krit executable to invoke against each captured
// worktree. Empty defaults to argv[0]; tests inject a path.
KritBin string
// NoSidecar forces the shell-out path even when persisted findings
// sidecars are available. Tests use this to exercise the legacy
// path explicitly; production callers should leave it false.
NoSidecar bool
Reporter func(SimulateEvent)
}
type SimulatePoint ¶
type SimulatePoint struct {
CommitSHA string `json:"commit_sha"`
CommittedAt int64 `json:"committed_at"`
Findings int `json:"findings"`
Source SimulateSource `json:"source,omitempty"`
}
type SimulateResult ¶
type SimulateResult struct {
Rule string `json:"rule"`
Points []SimulatePoint `json:"points"`
Failed []string `json:"failed,omitempty"`
}
func Simulate ¶
func Simulate(opts SimulateOptions) (*SimulateResult, error)
Simulate answers "if rule X had been active for the last <since>, how many findings would each commit have carried?" by walking history with detached worktrees and shelling out to krit per commit. Slow per call (one full analyse per commit) — the use case is one-off rule tuning.
type SimulateSource ¶
type SimulateSource string
SimulateSource identifies how a SimulatePoint was scored.
const ( // SimulateSourceSidecar marks counts read from a persisted findings.gob.zst. SimulateSourceSidecar SimulateSource = "sidecar" // SimulateSourceScan marks counts produced by the shell-out fallback. SimulateSourceScan SimulateSource = "scan" )
type SymbolRef ¶
type SymbolRef struct {
FQN string `json:"fqn"`
Signature string `json:"signature,omitempty"`
Kind string `json:"kind,omitempty"`
File string `json:"file,omitempty"`
}
SymbolRef names a symbol in the diff. FQN + Signature is the join key; symbols with the same FQN but different overloads diff independently.
type TimelinePoint ¶
TimelinePoint is one (commit, value) reading. Sorted by CapturedAt ascending in the result of Timeline.
func Timeline ¶
func Timeline(root string, q TimelineQuery) ([]TimelinePoint, error)
Timeline loads every metrics rollup under root, projects the requested scalar, and returns the points sorted by capture time. Snapshots that have no matching target (e.g. a module that did not exist at that sha) are skipped — callers see a sparse series rather than zero-filled readings, mirroring git history.
type TimelineQuery ¶
type TimelineQuery struct {
Scope TimelineScope
Target string // module path (":app") or repo-relative file path; ignored for ScopeRepo
Metric string // see metricByName
}
TimelineQuery describes a single timeline read across all captured snapshots under a repo's snapshots root.
type TimelineScope ¶
type TimelineScope string
TimelineScope selects which slice of a Metrics rollup contributes a value at each captured commit.
const ( ScopeRepo TimelineScope = "repo" ScopeModule TimelineScope = "module" ScopeFile TimelineScope = "file" )