snapshot

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 11, 2026 License: MIT Imports: 29 Imported by: 0

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

View Source
const DefaultKeepFeatureAge = 30 * 24 * time.Hour

DefaultKeepFeatureAge is the retention window for snapshots only reachable from feature branches.

View Source
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.

View Source
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.

View Source
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.

View Source
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.

View Source
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.
View Source
const MetricsSchemaVersion = 1

MetricsSchemaVersion versions Metrics independently of Blob, so adding a new scalar to the rollup does not invalidate graph blobs.

View Source
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.

View Source
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.

View Source
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

View Source
var DefaultPermanentBranches = []string{"main", "master"}

DefaultPermanentBranches is the fallback set of always-keep branches when PruneOptions.PermanentBranches is empty.

View Source
var PostCommitHook string

Functions

func BlobPath

func BlobPath(root, sha string) (string, error)

func CaptureManifest

func CaptureManifest(root string, res *Result, repoRoot, kritVersion string) (string, error)

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

func EnsureGitignoreEntry(repoRoot, pattern string) (added bool, err error)

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

func FindingsPath(root, sha string) (string, error)

FindingsPath returns the on-disk location for a snapshot's findings sidecar. Mirrors BlobPath / MetricsPath layout (sharded by sha[:2]).

func HookPath

func HookPath(repoRoot string) string

func InstallHook

func InstallHook(repoRoot string, force bool) (string, error)

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

func ResolveCommitSHA(repoRoot, ref string) (string, error)

ResolveCommitSHA returns `git rev-parse <ref>` inside repoRoot. ref defaults to "HEAD".

func ResolveParentSHAs

func ResolveParentSHAs(repoRoot, sha string) ([]string, error)

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

func Save(root string, b *Blob) (string, error)

Save writes b atomically to its content-addressed location under root and returns the path written.

func SaveFindings

func SaveFindings(root string, f *Findings) (string, error)

func SaveManifest

func SaveManifest(root string, m *Manifest) (string, error)

func SaveMetrics

func SaveMetrics(root string, m *Metrics) (string, error)

func SaveResult

func SaveResult(root string, res *Result, repoRoot, kritVersion string) (string, error)

SaveResult persists the graph blob, metrics rollup, and a freshly-built manifest for a captured Result. Returns the blob path.

func SnapshotsDir

func SnapshotsDir(repoRoot string) string

SnapshotsDir returns the snapshots root inside repoRoot.

func UninstallHook

func UninstallHook(repoRoot string) (string, error)

UninstallHook removes the post-commit hook iff it carries HookMarker; a hand-rolled hook is left untouched.

Types

type BackfillEvent

type BackfillEvent struct {
	Kind      string
	CommitSHA string
	Error     error
	Duration  time.Duration
}

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

type BackfillResult struct {
	Captured int
	Skipped  int
	Failed   int
}

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 Load

func Load(root, sha string) (*Blob, error)

func MigrateBlob

func MigrateBlob(blob *Blob) (*Blob, error)

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 Entry

type Entry struct {
	CommitSHA string
	Path      string
	Bytes     int64
}

Entry is one captured snapshot, returned by List.

func List

func List(root string) ([]Entry, error)

List returns every captured snapshot under root, sorted by sha. Entries that fail to stat are skipped silently.

type File

type File struct {
	Path     string
	Module   string
	Language string
	Lines    int
	Bytes    int
}

type FileMetrics

type FileMetrics struct {
	Path          string
	Module        string
	Language      string
	LOC           int
	Bytes         int
	Symbols       int
	PublicSymbols int
	Cyclomatic    int
}

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 LoadFindings(root, sha string) (*Findings, error)

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

type Index struct {
	SchemaVersion int        `json:"schema_version"`
	Entries       []Manifest `json:"entries"`
}

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.

func LoadIndex

func LoadIndex(root string) (*Index, error)

LoadIndex reads the rollup at <root>/index.json. Returns (nil, nil) when the file does not exist or carries a higher schema version than this binary understands — callers fall back to LoadManifests.

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 LoadManifest(root, sha string) (*Manifest, error)

func LoadManifests

func LoadManifests(root string) ([]Manifest, error)

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

func MigrateManifest(m *Manifest) (*Manifest, error)

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 LoadMetrics(root, sha string) (*Metrics, error)

func MigrateMetrics

func MigrateMetrics(m *Metrics) (*Metrics, error)

MigrateMetrics is the *Metrics counterpart to MigrateBlob.

type Module

type Module struct {
	Path         string
	Dir          string
	Dependencies []ModuleDep
	Consumers    []string
}

type ModuleDep

type ModuleDep struct {
	Path          string
	Configuration string
}

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 ModuleMetrics struct {
	Path          string
	Files         int
	LOC           int
	Symbols       int
	PublicSymbols int
	Cyclomatic    int
	FanIn         int
	FanOut        int
}

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

type Result struct {
	Blob     *Blob
	Metrics  *Metrics
	Findings *Findings
}

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 Symbol

type Symbol struct {
	Name       string
	Kind       string
	Visibility string
	File       string
	Line       int
	Language   string
	Package    string
	FQN        string
	Owner      string
	Signature  string
	IsOverride bool
	IsTest     bool
}

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

type TimelinePoint struct {
	CommitSHA  string
	CapturedAt int64
	Value      float64
}

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"
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL