compare

package
v1.39.1 Latest Latest
Warning

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

Go to latest
Published: Jul 17, 2026 License: Apache-2.0 Imports: 50 Imported by: 0

Documentation

Overview

Package compare provides structural and semantic code comparison between repositories.

Comparison works at multiple levels:

  • Symbols: matched by name, signature, and semantic similarity
  • Metrics: quantitative measures of code quality and complexity
  • Architecture: LLM-powered insights into design patterns and gaps

Index

Constants

View Source
const HintApproxArchMetrics = "Architecture metrics are approximate (derived from in-memory call graph). " +
	"Run the `code_graph` tool with the same repo path for full analysis including call depth."

HintApproxArchMetrics is the caveat attached to ArchMetrics results derived from an in-memory call graph rather than the full Apache AGE graph. MaxCallDepth, InterfaceRatio, and CommunityCount are not computed by the fallback.

Variables

This section is empty.

Functions

func BuildCompareContext

func BuildCompareContext(matches []SymbolMatch, metricsA, metricsB RepoMetrics, query string) string

BuildCompareContext assembles structured text context for the LLM (no hotspots).

func BuildCompareContextV2

func BuildCompareContextV2(matches []SymbolMatch, metricsA, metricsB RepoMetrics, query string,
	hotspotsA, hotspotsB []HotspotFile, relStatsA, relStatsB *RelStats,
	freshnessA, freshnessB *FreshnessStats, dataflowA, dataflowB *DataflowStats,
	apiDiff *APIDiff, routeDiff *RouteDiff,
	archA, archB *ArchMetrics) string

BuildCompareContextV2 assembles structured text context for the LLM, including hotspot and type hierarchy data.

Sections:

  1. ## Query — the user's question
  2. ## Metrics — JSON comparison of aggregate quality metrics
  3. ## Maintenance Hotspots — high-churn x high-complexity files (if any)
  4. ## Type Hierarchy — relationship stats (extends/implements/embeds) per repo
  5. ## Matched Symbols (side-by-side) — non-gap pairs up to maxMatchedPairs
  6. ## Coverage Gaps — symbols absent from one side, up to maxGapSymbols

Content is truncated once the cumulative output exceeds maxContextChars.

func CollectChurn

func CollectChurn(ctx context.Context, root string, since time.Duration) (map[string]ChurnStats, error)

CollectChurn runs git log --numstat on the given repo root and returns churn statistics per file (relative paths).

`since` bounds the history window (e.g. 90*24*time.Hour); pass 0 for all history. Returns nil map and nil error for non-git directories.

func ComputeGrade

func ComputeGrade(m RepoMetrics) string

ComputeGrade returns a letter grade (A+ through F) for the given metrics.

func DetectFrameworks

func DetectFrameworks(imports []string, language string) []string

DetectFrameworks returns a sorted list of framework names detected from the given import paths for the specified language.

func ExtractRoutes

func ExtractRoutes(ctx context.Context, root string, snap *RepoSnapshot) []routes.Route

ExtractRoutes walks the snapshot files, reads each source file, and extracts HTTP routes using the registered route matchers. Only files whose language matches snap.Language are processed. The root parameter is the absolute path to the repository root used to build absolute file paths from RelPath. Context cancellation is respected between files.

func FileComplexityFromSnapshot

func FileComplexityFromSnapshot(snap *RepoSnapshot) map[string]float64

FileComplexityFromSnapshot computes average cyclomatic complexity per file from the snapshot's symbols. Keys are relative paths (root prefix stripped) to match git churn output from CollectChurn.

func GradeScore

func GradeScore(m RepoMetrics) float64

GradeScore computes a quality score in [0, 100] from RepoMetrics. Higher is better. Uses 14 sub-scores with weights summing to 1.0.

func IsCompiledArtifact

func IsCompiledArtifact(filePath string) bool

IsCompiledArtifact returns true when filePath looks like a build output that should be excluded from coupling and other source-level analyses.

The implementation lives in internal/artifactfilter (stdlib-only leaf) so that packages like internal/federate can import just that leaf without pulling in compare's heavy transitive closure.

func SubScoreSum

func SubScoreSum(m RepoMetrics) float64

SubScoreSum returns the total score from sub-scores (should equal GradeScore). Exported for drift-guard tests.

func ToGumTree

func ToGumTree(node *sitter.Node, source []byte) *gum.Tree

ToGumTree converts a tree-sitter node tree into a gum.Tree for AST diffing. Sets Value on all leaf named nodes (language-agnostic). Calls Refresh() making it ready for gum.Match().

Types

type APIDelta

type APIDelta struct {
	Name string
	Kind string
	SigA string
	SigB string
}

APIDelta captures a signature change for a symbol present in both repos.

type APIDiff

type APIDiff struct {
	Common     int
	OnlyACount int
	OnlyBCount int
	ChangedSig int
	OnlyA      []APISymbol
	OnlyB      []APISymbol
	Changed    []APIDelta
}

APIDiff is the result of comparing two API surfaces.

func ComputeAPIDiff

func ComputeAPIDiff(a, b []APISymbol) APIDiff

ComputeAPIDiff compares two API surfaces and returns a structured diff. Symbols are matched by (name, kind) key. Matched symbols with different signatures are counted as changed. Lists are truncated to maxAPIDiffItems.

type APISymbol

type APISymbol struct {
	Name      string
	Kind      string
	Signature string
	Package   string
	File      string
}

APISymbol represents a single exported symbol in a repository's public API surface.

func ExtractAPISurface

func ExtractAPISurface(symbols []*parser.Symbol, language string) []APISymbol

ExtractAPISurface filters a symbol list to exported symbols of relevant kinds. Package is set to the directory of the symbol's file.

type ArchMetrics

type ArchMetrics struct {
	PackageCount      int           `json:"packageCount"`
	CommunityCount    int           `json:"communityCount"`
	CrossPkgCallRatio float64       `json:"crossPkgCallRatio"`
	MaxCallDepth      int           `json:"maxCallDepth"`
	InterfaceRatio    float64       `json:"interfaceRatio"`
	GodPackages       []GodPackage  `json:"godPackages,omitempty"`
	CircularDeps      []CircularDep `json:"circularDeps,omitempty"`
	// NotIndexed is set when the code graph has no packages for this repo,
	// meaning code_graph tool was never called with this repo path. Call
	// code_graph with the same repo first to populate the graph.
	NotIndexed bool `json:"notIndexed,omitempty"`
	// Approximate is set when metrics were derived from an in-memory call graph
	// rather than the full Apache AGE graph. MaxCallDepth, InterfaceRatio, and
	// CommunityCount are not computed in the approximate path and must not be
	// presented as real measurements.
	Approximate bool   `json:"approximate,omitempty"`
	Hint        string `json:"hint,omitempty"`
}

ArchMetrics holds architectural metrics derived from the code graph.

func CollectArchMetrics

func CollectArchMetrics(ctx context.Context, store *codegraph.Store, root string) *ArchMetrics

CollectArchMetrics queries the Apache AGE code graph for architectural metrics. Each sub-query is non-fatal: failures are logged and skipped. When the graph is absent (NotIndexed), it falls back to FallbackArchMetrics to derive PackageCount and CrossPkgCallRatio from an in-memory call graph.

func FallbackArchMetrics added in v1.22.0

func FallbackArchMetrics(ctx context.Context, root string) *ArchMetrics

FallbackArchMetrics builds an in-memory call graph with a 10-second timeout and derives PackageCount and CrossPkgCallRatio from it. It is called when CollectArchMetrics returns NotIndexed (AGE graph absent or repo not yet indexed).

MaxCallDepth, InterfaceRatio, and CommunityCount are not computed here — they are too expensive without the persistent graph store. Callers must set HintApproxArchMetrics on the returned struct to indicate the metrics are approximate.

Returns nil when root is empty or when BuildFromRepo fails (caller should fall back to NotIndexed=true in that case).

type ArchitectureInsight

type ArchitectureInsight struct {
	Insight string `json:"insight"`
	Source  string `json:"source"`
	Example string `json:"example,omitempty"`
	Benefit string `json:"benefit"`
}

ArchitectureInsight captures a design pattern or architectural observation.

type ChurnStats

type ChurnStats struct {
	// Commits is the number of commits that touched this file.
	Commits int `json:"commits"`

	// Additions is the total lines added across all commits.
	Additions int `json:"additions"`

	// Deletions is the total lines deleted across all commits.
	Deletions int `json:"deletions"`
}

ChurnStats holds change frequency data for a single file.

func (ChurnStats) ChurnScore

func (c ChurnStats) ChurnScore() float64

ChurnScore returns a weighted churn score as defined by Omen: sum(1.0 + (additions + deletions) / 100.0) per commit.

type CircularDep

type CircularDep struct {
	PackageA string `json:"packageA"`
	PackageB string `json:"packageB"`
}

CircularDep represents a circular dependency between two packages.

type CompareInput

type CompareInput struct {
	RootA       string
	RootB       string
	Query       string
	Opts        SnapshotOpts
	OxCodes     *oxcodes.Client
	EmbedClient *embed.Client     // nil = skip semantic matching
	GraphStore  *codegraph.Store  // nil = skip architecture graph analysis
	ParseCache  *cache.ParseCache // nil = skip parse caching
}

CompareInput is the input for CompareRepos.

type CompareResult

type CompareResult struct {
	RepoA           string             `json:"repo_a"`
	RepoB           string             `json:"repo_b"`
	Query           string             `json:"query"`
	MetricsA        RepoMetrics        `json:"metrics_a"`
	MetricsB        RepoMetrics        `json:"metrics_b"`
	Analysis        LLMAnalysis        `json:"analysis"`
	MatchedSymbols  int                `json:"matched_symbols"`
	UnmatchedA      int                `json:"unmatched_a"`
	UnmatchedB      int                `json:"unmatched_b"`
	MatchBreakdown  MatchBreakdown     `json:"match_breakdown"`
	ImportDiff      ImportDiff         `json:"import_diff"`
	DiffStats       *DiffStats         `json:"diff_stats,omitempty"`
	HotspotsA       []HotspotFile      `json:"hotspots_a,omitempty"`
	HotspotsB       []HotspotFile      `json:"hotspots_b,omitempty"`
	RelStatsA       *RelStats          `json:"rel_stats_a,omitempty"`
	RelStatsB       *RelStats          `json:"rel_stats_b,omitempty"`
	QualityA        *QualityIndicators `json:"quality_a,omitempty"`
	QualityB        *QualityIndicators `json:"quality_b,omitempty"`
	FreshnessA      *FreshnessStats    `json:"freshness_a,omitempty"`
	FreshnessB      *FreshnessStats    `json:"freshness_b,omitempty"`
	DataflowA       *DataflowStats     `json:"dataflow_a,omitempty"`
	DataflowB       *DataflowStats     `json:"dataflow_b,omitempty"`
	APIDiffResult   *APIDiff           `json:"api_diff,omitempty"`
	RouteDiffResult *RouteDiff         `json:"route_diff,omitempty"`
	CouplingA       []CoupledPair      `json:"coupling_a,omitempty"`
	CouplingB       []CoupledPair      `json:"coupling_b,omitempty"`
	ArchMetricsA    *ArchMetrics       `json:"arch_metrics_a,omitempty"`
	ArchMetricsB    *ArchMetrics       `json:"arch_metrics_b,omitempty"`
	CrossLangReport *CrossLangReport   `json:"cross_lang_report,omitempty"`
}

CompareResult contains the full structured output of a code comparison.

func CompareRepos

func CompareRepos(ctx context.Context, input CompareInput, llmClient llm.Completer) (*CompareResult, error)

CompareRepos orchestrates a full comparison between two repositories. llmClient must be non-nil; pass llm.NoOp{} when LLM is not configured. NoOp.Complete returns ErrLLMUnavailable which runLLMAnalysis treats as a fallback (deterministic comparison still runs in full).

type CoupledPair

type CoupledPair struct {
	FileA     string  `json:"fileA"`
	FileB     string  `json:"fileB"`
	CoChanges int     `json:"coChanges"`
	Ratio     float64 `json:"ratio"` // coChanges / max(changesA, changesB)
}

CoupledPair represents two files that frequently change together in git history.

func CollectCoupling

func CollectCoupling(ctx context.Context, root string, minCoChanges int) []CoupledPair

CollectCoupling analyzes git log to find files that frequently change together. Returns pairs with at least minCoChanges co-occurrences, sorted by frequency desc. Looks at the last year of history. Returns nil if git is unavailable.

type CoverageGap

type CoverageGap struct {
	MissingIn  string `json:"missingIn"`
	Feature    string `json:"feature"`
	LocationB  string `json:"locationB,omitempty"`
	Importance string `json:"importance"`
}

CoverageGap identifies a feature present in one repo but absent in the other.

type CrossLangMatch

type CrossLangMatch struct {
	NameA      string  `json:"nameA"`
	NameB      string  `json:"nameB"`
	FileA      string  `json:"fileA"`
	FileB      string  `json:"fileB"`
	Similarity float64 `json:"similarity"`
}

CrossLangMatch describes a semantic match between symbols in different languages.

type CrossLangReport

type CrossLangReport struct {
	LanguageA       string           `json:"languageA"`
	LanguageB       string           `json:"languageB"`
	SemanticMatches int              `json:"semanticMatches"`
	TopMatches      []CrossLangMatch `json:"topMatches,omitempty"`
}

CrossLangReport summarizes cross-language comparison findings.

func BuildCrossLangReport

func BuildCrossLangReport(matches []SymbolMatch, langA, langB string) *CrossLangReport

BuildCrossLangReport creates a cross-language report from symbol matches. Returns nil if both repos use the same language.

type DataflowStats

type DataflowStats struct {
	DeadStores    int `json:"deadStores"`
	UnusedVars    int `json:"unusedVars"`
	TotalFindings int `json:"totalFindings"`
	FilesAnalyzed int `json:"filesAnalyzed"`
}

DataflowStats holds dead code and data flow findings from ox-codes.

func GatherDataflow

func GatherDataflow(ctx context.Context, client *oxcodes.Client, root, language string) *DataflowStats

GatherDataflow runs ox-codes dataflow analysis on a repo.

type DiffStats

type DiffStats struct {
	ModifiedWithDiff int `json:"modifiedWithDiff"`
	TotalInserts     int `json:"totalInserts"`
	TotalDeletes     int `json:"totalDeletes"`
	TotalUpdates     int `json:"totalUpdates"`
	TotalMoves       int `json:"totalMoves"`
}

DiffStats aggregates AST diff statistics across all modified matches.

type DiffSummary

type DiffSummary struct {
	// TotalChanges is the total number of edit actions in the diff script.
	TotalChanges int `json:"totalChanges"`

	// Inserts is the count of insert (single node) and insert-tree actions.
	Inserts int `json:"inserts"`

	// Deletes is the count of delete (single node) and delete-tree actions.
	Deletes int `json:"deletes"`

	// Updates is the count of update (value change) actions.
	Updates int `json:"updates"`

	// Moves is the count of move (reorder) actions.
	Moves int `json:"moves"`

	// Changes is a list of human-readable change descriptions (max maxDiffChanges).
	Changes []string `json:"changes,omitempty"`
}

DiffSummary holds the result of a structural AST diff between two symbol bodies.

func ComputeASTDiff

func ComputeASTDiff(bodyA, bodyB, language string) *DiffSummary

ComputeASTDiff parses two symbol bodies using tree-sitter for the given language, computes a GumTree edit script, and returns a DiffSummary. Returns nil if either body is empty, the language is unsupported, or bodies are too large.

type EmbeddingClassifier

type EmbeddingClassifier struct {
	// contains filtered or unexported fields
}

EmbeddingClassifier implements LLMClassifier using vector similarity.

func NewEmbeddingClassifier

func NewEmbeddingClassifier(ctx context.Context, client *embed.Client) *EmbeddingClassifier

NewEmbeddingClassifier creates a classifier using the given embedding client.

func (*EmbeddingClassifier) ClassifySymbols

func (c *EmbeddingClassifier) ClassifySymbols(a, b []*parser.Symbol) ([]SymbolMatch, error)

ClassifySymbols finds semantic matches between unmatched symbols using embeddings.

type FreshnessStats

type FreshnessStats struct {
	DepFreshnessRatio float64 `json:"depFreshnessRatio"`
	VulnSecurityRatio float64 `json:"vulnSecurityRatio"`
	TotalDeps         int     `json:"totalDeps"`
	OutdatedDeps      int     `json:"outdatedDeps"`
	VulnDeps          int     `json:"vulnDeps"`
}

FreshnessStats holds dependency freshness and vulnerability data.

func CollectFreshness

func CollectFreshness(ctx context.Context, root string) (*FreshnessStats, float64, float64)

CollectFreshness checks dependency freshness and vulnerabilities for a repo. Returns nil if no manifests found. depRatio and vulnRatio contain the ratios.

type GodPackage

type GodPackage struct {
	Name      string `json:"name"`
	Importers int    `json:"importers"`
}

GodPackage represents a package with many importers (high coupling).

type HotspotFile

type HotspotFile struct {
	File       string  `json:"file"`
	Score      float64 `json:"score"`
	Churn      int     `json:"churn"`
	Complexity float64 `json:"complexity"`
	Risk       string  `json:"risk"` // "critical", "high", "moderate"
}

HotspotFile is a file identified as a maintenance risk (high churn x high complexity).

func ComputeHotspots

func ComputeHotspots(churn map[string]ChurnStats, fileComplexity map[string]float64) []HotspotFile

ComputeHotspots combines churn data and per-file complexity to find maintenance hotspots. Uses the Omen formula: score = percentile(churn) x percentile(complexity).

type ImportCategory

type ImportCategory string

ImportCategory classifies an import path.

const (
	// ImportStdlib marks standard library imports.
	ImportStdlib ImportCategory = "stdlib"

	// ImportInternal marks project-relative (internal) imports.
	ImportInternal ImportCategory = "internal"

	// ImportExternal marks third-party/external imports.
	ImportExternal ImportCategory = "external"
)

func CategorizeImport

func CategorizeImport(imp, language string) ImportCategory

CategorizeImport classifies an import path as stdlib, internal, or external for the given language.

type ImportDiff

type ImportDiff struct {
	CommonCount int      `json:"commonCount"`
	OnlyACount  int      `json:"onlyACount"`
	OnlyBCount  int      `json:"onlyBCount"`
	OnlyA       []string `json:"onlyA,omitempty"`
	OnlyB       []string `json:"onlyB,omitempty"`
	FrameworksA []string `json:"frameworksA,omitempty"`
	FrameworksB []string `json:"frameworksB,omitempty"`
	StdlibA     int      `json:"stdlibA"`
	StdlibB     int      `json:"stdlibB"`
	ExternalA   int      `json:"externalA"`
	ExternalB   int      `json:"externalB"`
}

ImportDiff captures the difference between two import sets.

func ComputeImportDiff

func ComputeImportDiff(importsA, importsB []string, language string) ImportDiff

ComputeImportDiff computes the set difference between two import lists. The language parameter is used for categorization and framework detection.

type LLMAnalysis

type LLMAnalysis struct {
	Quality         []QualityAspect       `json:"quality"`
	Gaps            []CoverageGap         `json:"gaps"`
	Architecture    []ArchitectureInsight `json:"architecture"`
	Recommendations []string              `json:"recommendations"`
	Verdict         VerdictResult         `json:"verdict"`
}

LLMAnalysis holds the structured output from LLM-powered comparison.

type LLMClassifier

type LLMClassifier interface {
	ClassifySymbols(a, b []*parser.Symbol) ([]SymbolMatch, error)
}

LLMClassifier is a consumer-defined interface for semantic symbol pairing. It is called as a last resort after exact and fuzzy matching are exhausted.

type MagicNumberEntry

type MagicNumberEntry struct {
	Name     string
	File     string
	Line     int
	Count    int
	Language string
}

MagicNumberEntry describes a single function containing magic numbers.

func CollectMagicNumbers

func CollectMagicNumbers(snap *RepoSnapshot) []MagicNumberEntry

CollectMagicNumbers returns all functions/methods that contain magic numbers. Test files are excluded. Results are sorted by count descending.

type MatchBreakdown

type MatchBreakdown struct {
	Exact    int `json:"exact"`
	Modified int `json:"modified"`
	Fuzzy    int `json:"fuzzy"`
	Renamed  int `json:"renamed"`
	Semantic int `json:"semantic"`
	Moved    int `json:"moved"`
}

MatchBreakdown counts matches by type for structured reporting.

type MatchType

type MatchType string

MatchType describes how two symbols were matched.

const (
	// MatchExact means the symbols have the same name and kind and identical body.
	MatchExact MatchType = "exact"

	// MatchModified means the symbols have the same name and kind but different body.
	MatchModified MatchType = "modified"

	// MatchRenamed means the symbols have different names but same signature and/or body hash.
	MatchRenamed MatchType = "renamed"

	// MatchFuzzy means the symbols have similar names or signatures.
	MatchFuzzy MatchType = "fuzzy"

	// MatchSemantic means the symbols serve the same purpose (LLM-determined).
	MatchSemantic MatchType = "semantic"

	// MatchMoved means the symbol has the same name, kind, and body but resides
	// in a different file or package. Indicates code reorganization without logic change.
	MatchMoved MatchType = "moved"

	// MatchGap means the symbol has no counterpart in the other repository.
	MatchGap MatchType = "gap"
)

type OutlierFunc

type OutlierFunc struct {
	Name  string
	File  string
	Line  int
	Value int
}

OutlierFunc identifies a single function that holds a maximum metric value.

type Outliers

type Outliers struct {
	MaxCyclomatic   OutlierFunc
	MaxCognitive    OutlierFunc
	MaxFuncLines    OutlierFunc
	MaxNesting      OutlierFunc
	MaxMagicNumbers OutlierFunc
}

Outliers holds the worst-offending function for each key metric.

func CollectOutliers

func CollectOutliers(snap *RepoSnapshot) Outliers

CollectOutliers iterates snapshot symbols and records which function holds each maximum metric value. File paths are stripped of the root prefix to produce relative paths (same pattern as FileComplexityFromSnapshot).

type QualityAspect

type QualityAspect struct {
	Aspect   string `json:"aspect"`
	Winner   string `json:"winner"`
	Reason   string `json:"reason"`
	SnippetA string `json:"snippetA,omitempty"`
	SnippetB string `json:"snippetB,omitempty"`
}

QualityAspect describes a qualitative comparison point between two repos.

type QualityIndicators

type QualityIndicators struct {
	TodoCount     int `json:"todo_count"`
	ErrorPatterns int `json:"error_patterns"` // unhandled errors
	PanicCount    int `json:"panic_count"`    // panic() calls
	MagicNumbers  int `json:"magic_numbers"`
}

QualityIndicators summarizes code quality signals from ox-codes.

func GatherQualityIndicators

func GatherQualityIndicators(ctx context.Context, client *oxcodes.Client, root, language string) *QualityIndicators

GatherQualityIndicators runs ox-codes scoped searches to assess code quality. All searches run in parallel for speed.

type Recommendation

type Recommendation struct {
	Priority  int
	Potential int // estimated score points recoverable
	Area      string
	Message   string
}

Recommendation is a single actionable improvement suggestion.

func ComputeRecommendations

func ComputeRecommendations(m RepoMetrics, out Outliers, maxItems int) []Recommendation

ComputeRecommendations identifies the weakest scoring areas and returns actionable recommendations sorted by potential score impact (descending). maxItems limits the number of returned recommendations (0 = unlimited).

type RelStats

type RelStats struct {
	Total          int `json:"total"`
	Extends        int `json:"extends"`
	Implements     int `json:"implements"`
	Embeds         int `json:"embeds"`
	UniqueSubjects int `json:"uniqueSubjects"`
}

RelStats summarizes type relationship counts for a repository.

func ComputeRelStats

func ComputeRelStats(rels []parser.TypeRelationship) *RelStats

ComputeRelStats computes relationship statistics from extracted relationships. Returns nil if no relationships exist.

type RepoMetrics

type RepoMetrics struct {
	Files              int     `json:"files"`
	TotalLines         int     `json:"totalLines"`
	AvgFuncLines       float64 `json:"avgFuncLines"`
	MaxFuncLines       int     `json:"maxFuncLines"`
	AvgComplexity      float64 `json:"avgComplexity"`
	MaxComplexity      int     `json:"maxComplexity"`
	TestRatio          float64 `json:"testRatio"`
	DocRatio           float64 `json:"docRatio"`
	ErrorHandlingRatio float64 `json:"errorHandlingRatio"`
	Interfaces         int     `json:"interfaces"`
	ExternalDeps       int     `json:"externalDeps"`
	Grade              string  `json:"grade"`

	// New metrics for smarter scoring.
	AvgCognitiveComplexity float64 `json:"avgCognitiveComplexity"`
	MaxCognitiveComplexity int     `json:"maxCognitiveComplexity"`
	AvgNestingDepth        float64 `json:"avgNestingDepth"`
	MaxNestingDepth        int     `json:"maxNestingDepth"`
	LargeFileRatio         float64 `json:"largeFileRatio"`
	DuplicationRatio       float64 `json:"duplicationRatio"`
	MagicNumberRatio       float64 `json:"magicNumberRatio"`
	AvgParamCount          float64 `json:"avgParamCount"`
	MaxParamCount          int     `json:"maxParamCount"`
	Score                  float64 `json:"score"`
	SemanticDupRatio       float64 `json:"semanticDupRatio,omitempty"`  // fraction of functions in semantic dup groups
	DepFreshnessRatio      float64 `json:"depFreshnessRatio,omitempty"` // fraction of deps that are up-to-date
	VulnSecurityRatio      float64 `json:"vulnSecurityRatio,omitempty"` // fraction of deps with no known vulns
	TotalDeps              int     `json:"totalDeps,omitempty"`         // number of external dependencies scanned; 0 means no manifests found (N/A)
	// DepsScanned is true when a dependency freshness scan was attempted (regardless of
	// whether it found any deps). When false, the dep-freshness and vulnerability
	// sub-scores fall back to the legacy penalty path (same behaviour as before PR #250),
	// so lightweight paths like explore that skip the freshness scan are not affected.
	DepsScanned bool `json:"depsScanned,omitempty"`
	// DeadCodeCandidates is the count of functions with CE dead-code probability >= 0.25.
	// Populated from code_dead_code_scores when code_graph snapshot is available.
	DeadCodeCandidates int      `json:"deadCodeCandidates,omitempty"`
	DeadCodeTopNames   []string `json:"deadCodeTopNames,omitempty"` // up to 3 top candidates
}

RepoMetrics holds aggregate quality and complexity metrics for a repo.

func ComputeMetrics

func ComputeMetrics(snap *RepoSnapshot) RepoMetrics

ComputeMetrics derives aggregate quality metrics from a RepoSnapshot.

The snapshot must already have Symbols and Imports populated; FileCount and TotalLines are copied verbatim. Test-file detection is based on Symbol.File paths (unique files seen across all symbols).

type RepoSnapshot

type RepoSnapshot struct {
	// Name is the repository name or slug.
	Name string `json:"name"`

	// Root is the absolute local path to the repository.
	Root string `json:"root"`

	// FocusMode is "content" when the fallback content filter was used
	// instead of path-based focus. Empty means path-based or no focus.
	FocusMode string `json:"focusMode,omitempty"`

	// Language is the dominant programming language.
	Language string `json:"language"`

	// Symbols is all symbols extracted from the repository.
	Symbols []*parser.Symbol `json:"symbols,omitempty"`

	// Imports is the flat list of all unique import paths used.
	Imports []string `json:"imports,omitempty"`

	// Files holds per-file metadata.
	Files []SnapshotFile `json:"files,omitempty"`

	// FileCount is the total number of source files.
	FileCount int `json:"fileCount"`

	// TotalLines is the approximate total lines of code.
	TotalLines int `json:"totalLines"`

	// Rels holds type relationships extracted from the repository.
	Rels []parser.TypeRelationship `json:"rels,omitempty"`

	// Partial is true when one or more ingested files could not be read during
	// snapshotting (e.g. the source tree was deleted out from under the walk, or
	// a per-file context cancellation cut the parse short). When set, the derived
	// metrics (Files, TotalLines, TestRatio, Symbols) under-count the real repo
	// and MUST NOT be presented as a complete result. See DroppedReadError /
	// DroppedCtxCancel for the per-reason breakdown.
	Partial bool `json:"partial,omitempty"`

	// DroppedReadError counts files that were enumerated by ingest but failed
	// os.ReadFile at parse time (vanished file / permission flip / truncated
	// tree). This is the dominant signal of a use-after-delete race on a shared
	// clone dir.
	DroppedReadError int `json:"droppedReadError,omitempty"`

	// DroppedCtxCancel counts files skipped because the snapshot context was
	// cancelled before the parse worker reached them (timeout / caller abort).
	DroppedCtxCancel int `json:"droppedCtxCancel,omitempty"`
}

RepoSnapshot is a parsed, summarized view of a repository ready for comparison.

func BuildSnapshot

func BuildSnapshot(ctx context.Context, root string, opts SnapshotOpts) (*RepoSnapshot, error)

BuildSnapshot ingests and parses a repository, returning a RepoSnapshot suitable for structural comparison.

Steps:

  1. Ingest the repository tree filtered by opts.Focus / opts.Language.
  2. Parse all files in parallel (worker pool of runtime.NumCPU goroutines).
  3. Aggregate symbols, unique imports, per-file entries, and line counts.

type RouteDiff

type RouteDiff struct {
	Common     int            `json:"common"`
	OnlyACount int            `json:"onlyACount"`
	OnlyBCount int            `json:"onlyBCount"`
	OnlyA      []routes.Route `json:"onlyA,omitempty"`
	OnlyB      []routes.Route `json:"onlyB,omitempty"`
}

RouteDiff summarises the difference between two sets of HTTP routes.

func ComputeRouteDiff

func ComputeRouteDiff(a, b []routes.Route) RouteDiff

ComputeRouteDiff compares two route slices and returns a RouteDiff. Routes are matched by method + normalised path; handler names are ignored.

type SARIFArtifact

type SARIFArtifact struct {
	URI string `json:"uri"`
}

SARIFArtifact holds the URI of the artifact.

type SARIFDriver

type SARIFDriver struct {
	Name    string      `json:"name"`
	Version string      `json:"version"`
	Rules   []SARIFRule `json:"rules,omitempty"`
}

SARIFDriver is the tool driver metadata.

type SARIFLocation

type SARIFLocation struct {
	PhysicalLocation SARIFPhysical `json:"physicalLocation"`
}

SARIFLocation identifies where a result was found.

type SARIFMessage

type SARIFMessage struct {
	Text string `json:"text"`
}

SARIFMessage holds a plain-text message.

type SARIFPhysical

type SARIFPhysical struct {
	ArtifactLocation SARIFArtifact `json:"artifactLocation"`
	Region           *SARIFRegion  `json:"region,omitempty"`
}

SARIFPhysical identifies a file and optional region.

type SARIFRegion

type SARIFRegion struct {
	StartLine int `json:"startLine"`
}

SARIFRegion specifies a location within an artifact.

type SARIFReport

type SARIFReport struct {
	Schema  string     `json:"$schema"`
	Version string     `json:"version"`
	Runs    []SARIFRun `json:"runs"`
}

SARIFReport is a minimal SARIF v2.1.0 report.

func BuildSARIF

func BuildSARIF(
	repoName string,
	metrics RepoMetrics,
	quality *QualityIndicators,
	dataflow *DataflowStats,
	hotspots []HotspotFile,
	outliers Outliers,
) *SARIFReport

BuildSARIF converts code health findings into a SARIF v2.1.0 report.

type SARIFResult

type SARIFResult struct {
	RuleID    string          `json:"ruleId"`
	Level     string          `json:"level"` // "error", "warning", "note"
	Message   SARIFMessage    `json:"message"`
	Locations []SARIFLocation `json:"locations,omitempty"`
}

SARIFResult is a single finding produced by the tool.

type SARIFRule

type SARIFRule struct {
	ID               string           `json:"id"`
	ShortDescription SARIFMessage     `json:"shortDescription"`
	DefaultConfig    *SARIFRuleConfig `json:"defaultConfiguration,omitempty"`
}

SARIFRule describes a rule that can produce results.

type SARIFRuleConfig

type SARIFRuleConfig struct {
	Level string `json:"level"` // "error", "warning", "note"
}

SARIFRuleConfig holds the default severity for a rule.

type SARIFRun

type SARIFRun struct {
	Tool    SARIFTool     `json:"tool"`
	Results []SARIFResult `json:"results"`
}

SARIFRun is a single analysis run.

type SARIFTool

type SARIFTool struct {
	Driver SARIFDriver `json:"driver"`
}

SARIFTool describes the analysis tool.

type SnapshotFile

type SnapshotFile struct {
	RelPath  string           `json:"relPath"`
	Language string           `json:"language"`
	Lines    int              `json:"lines"`
	Symbols  []*parser.Symbol `json:"symbols,omitempty"`
	Imports  []string         `json:"imports,omitempty"`
}

SnapshotFile holds parsed metadata for a single source file within a repo snapshot.

type SnapshotOpts

type SnapshotOpts struct {
	// Focus limits ingestion to files under this subdirectory or matching this
	// glob pattern. Empty means process the entire repository.
	Focus string

	// Language limits ingestion to files of this programming language.
	// Empty means accept all supported languages.
	Language string

	// MaxFiles limits the number of files ingested and parsed.
	// 0 means use the default cap (10,000).
	MaxFiles int

	// ParseCache caches parsed files across BuildSnapshot calls. When non-nil,
	// unchanged files are skipped on repeated builds.
	ParseCache *cache.ParseCache
}

SnapshotOpts controls what gets ingested and parsed when building a snapshot.

type SymbolMatch

type SymbolMatch struct {
	SymbolA   *parser.Symbol `json:"symbolA,omitempty"`
	SymbolB   *parser.Symbol `json:"symbolB,omitempty"`
	MatchType MatchType      `json:"matchType"`
	Category  string         `json:"category"`
	Score     float64        `json:"score"`
	Diff      *DiffSummary   `json:"diff,omitempty"`
}

SymbolMatch pairs a symbol from repo A with its counterpart in repo B. If one side is nil, the symbol exists only in the other repository.

func MatchSymbols

func MatchSymbols(symbolsA, symbolsB []*parser.Symbol, classifier LLMClassifier) []SymbolMatch

MatchSymbols aligns symbols from two repositories using four passes:

  1. Exact: match by identical name and kind (score 1.0).
  2. Fuzzy: match by name similarity >= fuzzyThreshold, same kind (score = similarity).
  3. Signature: match by identical/similar signature + body hash (catches renames).
  4. Semantic: delegate remaining unmatched pairs to classifier (if non-nil).

Symbols with no counterpart become gap entries (SymbolA or SymbolB is nil).

func (SymbolMatch) IsGap

func (m SymbolMatch) IsGap() bool

IsGap returns true if the symbol is present in only one repository (or neither).

func (SymbolMatch) MissingIn

func (m SymbolMatch) MissingIn() string

MissingIn returns "repo_a" if the symbol is missing from repo A, "repo_b" if missing from repo B, or "" if the symbol exists in both (or neither).

type VerdictResult

type VerdictResult struct {
	CanReplace string   `json:"canReplace"` // "yes", "partial", "no"
	Reason     string   `json:"reason"`
	Blockers   []string `json:"blockers,omitempty"`
}

VerdictResult is the structured "can replace?" assessment.

Jump to

Keyboard shortcuts

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