codebase

package
v0.0.0-...-5abf6fe Latest Latest
Warning

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

Go to latest
Published: Jun 7, 2026 License: MIT Imports: 30 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ExtractGoSignatures

func ExtractGoSignatures(projectRoot, relPath string) (map[int]map[string]string, error)

ExtractGoSignatures maps each func/method's start line to its receiver+param variable names → declared type name (the simple identifier; *T and pkg.T reduce to T). Uses go/ast for accurate types. This is the receiver-type inference that bounds dispatch precision: a call x.M() links through an interface only when x's DECLARED type is a known interface.

func ExtractGoSignaturesWithLocals

func ExtractGoSignaturesWithLocals(projectRoot, relPath string, facts TypeFacts) (map[int]map[string]string, error)

ExtractGoSignaturesWithLocals additionally infers the types of LOCAL variables (`resolver := registry.ResolverForFile(path)`) via the package's TypeFacts, so interface dispatch resolves through `:=`-inferred receivers — not only the declared receiver/param ones. Same conservative discipline: an unresolvable local contributes no entry.

func FormatCoverageResponse

func FormatCoverageResponse(report *CoverageReport) string

FormatCoverageResponse formats the coverage report for MCP output.

func FormatSymbolDrift

func FormatSymbolDrift(drifts []SymbolDrift) string

FormatSymbolDrift renders drift report for display.

func InferLocalVarTypes

func InferLocalVarTypes(fn *ast.FuncDecl, base map[string]string, facts TypeFacts) map[string]string

InferLocalVarTypes returns the variable→type map for a function body: the base (receiver + params) extended with locals whose RHS type resolves through the package's TypeFacts. Pure given (fn, base, facts).

Soundness over precision: the result is a FLAT per-function table, but Go has lexical scope, so a name can denote different types at different lines (inner- block shadowing, or a base param shadowed by a local). A flat table cannot say which type holds at a given call site — so the moment a name is bound to two DIFFERENT types, it is dropped entirely (marked ambiguous). Absent is safe; a wrong type would become a wrong dispatch edge. Only `:=` and `var` bind here; `=` cannot change a Go variable's type, so it is ignored.

func IsExcludedDir

func IsExcludedDir(name string) bool

IsExcludedDir checks if a directory should be skipped during walking. Uses the IgnoreChecker if available, otherwise falls back to the dir name check.

func NodeID

func NodeID(filePath, name string, startLine int) string

NodeID is the deterministic identity hash for a symbol node — same identity (file, name, start line) always yields the same id across re-indexing, so edges stay valid through an idempotent rebuild of unchanged symbols.

func RenderRepoMap

func RenderRepoMap(rm *RepoMap, maxTokens int) string

RenderRepoMap formats the repo map for injection into the system prompt. Shows directory tree with exported symbols, kinds, and line counts. Files sorted by modification time (recent first) for relevance. Respects a token budget (approximate: 4 chars ≈ 1 token).

func Satisfies

func Satisfies(typeMethods map[string]bool, iface InterfaceDef) bool

Satisfies reports whether a concrete type's method-name set covers every method of the interface. Pure. Empty interfaces never satisfy (excluded).

func SliceBody

func SliceBody(content []byte, sym CodeSymbol) ([]byte, bool)

SliceBody returns the byte-exact body of a symbol from file content. Pure; returns ok=false if the offsets don't fit the content (stale — caller must re-index before trusting source).

func TypeMethodSets

func TypeMethodSets(syms []CodeSymbol) map[string]map[string]bool

TypeMethodSets groups concrete method names by receiver type, from symbols.

func VerifyBody

func VerifyBody(content []byte, sym CodeSymbol) (body []byte, fresh bool)

VerifyBody slices the symbol's body from freshly-read content AND re-hashes it against the stored hash — the freshness guarantee for P3. fresh=true means the stored byte range still points at the exact same source on disk; false means the file was edited (offsets stale or content changed) and the caller must re-index before trusting the slice. Pure: the hash comparison, not a stored-hash-vs-stored-hash check, is what catches an actively-edited file.

Types

type CCppLang

type CCppLang struct{}

CCppLang implements ModuleDetector and ImportParser for C/C++ projects.

func (*CCppLang) DetectModules

func (c *CCppLang) DetectModules(projectRoot string) ([]Module, error)

DetectModules discovers C/C++ modules by reading compile_commands.json, falling back to directory-based heuristics with Makefile/CMakeLists.txt markers.

func (*CCppLang) Extensions

func (c *CCppLang) Extensions() []string

func (*CCppLang) Language

func (c *CCppLang) Language() string

func (*CCppLang) ParseImports

func (c *CCppLang) ParseImports(filePath string, projectRoot string) ([]ImportEdge, error)

ParseImports extracts #include "..." edges from a C/C++ source file. System includes (#include <...>) are skipped since they're external.

type CallSite

type CallSite struct {
	Callee    string
	Qualifier string // "" = unqualified (intra-package candidate); non-"" = qualified (P1b)
	Line      int    // 1-based
}

CallSite is one extracted call expression: the called name, an optional qualifier (the selector operand — a package alias or receiver), and the line. The enclosing caller symbol is resolved later by line containment, not here.

func ExtractCallSites

func ExtractCallSites(projectRoot, relPath string) ([]CallSite, error)

ExtractCallSites walks a Go file's call expressions. Go only for P1a — other languages return nil (node extraction still works for them; edges don't yet). Pure relative to the file content; no DB.

type CodeEdge

type CodeEdge struct {
	SrcID      string
	DstID      string
	Kind       EdgeKind
	FilePath   string
	Line       int
	Provenance Provenance
}

CodeEdge is a resolved directed relationship between two symbol nodes. It exists ONLY when both endpoints resolved — an unresolved call is an absent edge, never a nullable one ("partial coverage worse than none", in the type).

func ResolveConcreteMethodCallEdges

func ResolveConcreteMethodCallEdges(filePath string, fileSymbols []CodeSymbol, callSites []CallSite, signatures map[int]map[string]string, facts TypeFacts, lookup func(name string) []CodeSymbol) []CodeEdge

ResolveConcreteMethodCallEdges resolves QUALIFIED calls whose receiver is a CONCRETE-typed variable or field — `store.Get(...)`, `s.scanner.ScanEdges(...)` — to the method's definition node, including cross-package (the method lives where its type is declared). This is the static counterpart to interface dispatch: the receiver type is resolved (via vars + struct-field facts), then the method node is found by (receiver, name) with the same exactly-1-or-drop guard — a bare-receiver-name collision across packages (two `Store` types) drops rather than emitting a wrong edge. Interface receivers naturally yield no match here (interfaces have no method-body nodes) and are left to dispatch.

func ResolveCrossFileCallEdges

func ResolveCrossFileCallEdges(filePath string, fileSymbols []CodeSymbol, callSites []CallSite, imports []GoImport, lookup func(name string) []CodeSymbol) []CodeEdge

ResolveCrossFileCallEdges resolves QUALIFIED call sites (pkg.Func) to exported package-level functions in the imported local package. Chain: qualifier → import alias → local dir → exported func node. Same exactly-1-or-drop guard: an external import, an unknown qualifier (a receiver variable, not a package), or an unresolved name yields NO edge. lookup is the by-name node accessor (injected so the resolver stays pure relative to the store).

func ResolveEmbedsEdges

func ResolveEmbedsEdges(relPath string, fileSyms, pkgSyms []CodeSymbol, embeds []TypeEmbed) []CodeEdge

ResolveEmbedsEdges emits `embeds` edges (type -> embedded type) for each embedding declared in relPath, resolving the embedded name to exactly one package-local type symbol. External or ambiguous embeds are dropped, never guessed (the no-wrong-edge invariant). Heuristic provenance: the embedding is explicit in source, but name resolution here is package-scoped without import analysis. Pure.

func ResolveImplementsEdges

func ResolveImplementsEdges(relPath string, fileSyms, pkgSyms []CodeSymbol, interfaces map[string]InterfaceDef) []CodeEdge

ResolveImplementsEdges synthesizes STATIC implements edges: for each concrete type DECLARED in relPath whose method-name set covers an interface's methods, emit type -> interface (answering "what implements this interface"). Same precision class as interface_dispatch — method-NAME coverage, so a name match with a different signature is a possible false positive; hence heuristic provenance, not static. Empty interfaces never match (Satisfies excludes them). Package-scoped via pkgSyms; reuses TypeMethodSets + Satisfies. Pure.

func ResolveInterfaceDispatchEdges

func ResolveInterfaceDispatchEdges(
	filePath string,
	fileSymbols []CodeSymbol,
	callSites []CallSite,
	signatures map[int]map[string]string,
	interfaces map[string]InterfaceDef,
	implSymbols []CodeSymbol,
) []CodeEdge

ResolveInterfaceDispatchEdges synthesizes interface_dispatch edges (heuristic provenance) from a call site through an interface to its concrete impls. The precision bound: it fires ONLY when the call's receiver variable has a DECLARED type (from signatures) that is a KNOWN interface declaring the called method. Where that doesn't hold, NO edge — the boundary stays "unresolved dispatch". Multiple satisfying impls produce multiple edges (the true dispatch fan, not ambiguity). Pure given its injected lookups. implSymbols MUST be scoped to a single package. Receiver names are bare (package-stripped), so a whole-repo impl lookup would collide unrelated types that happen to share a receiver name (e.g. db.Store vs artifact.Store) and emit a WRONG dispatch edge — the precision-cliff failure this scoping prevents.

func ResolveIntraPackageCallEdges

func ResolveIntraPackageCallEdges(filePath string, fileSymbols, pkgSymbols []CodeSymbol, callSites []CallSite) []CodeEdge

ResolveIntraPackageCallEdges resolves UNQUALIFIED call sites in a file to package-level definitions, against the package's symbol set. The over- resolution guard is exactly-1-or-drop: a callee with zero or multiple package candidates yields NO edge (an unresolved call is honest; a fanned-out wrong edge is corrosive). Qualified calls (receiver/package selectors) are left for P1b. Pure — symbols + call sites in, edges out.

type CodeSymbol

type CodeSymbol struct {
	ID        string
	FilePath  string
	Name      string
	Kind      string
	Receiver  string
	StartLine int
	EndLine   int
	StartByte int
	EndByte   int
	Hash      string
	Exported  bool
	Lang      string
}

CodeSymbol is a persisted symbol node. Identity is (FilePath, Name, StartLine) — so two same-name methods on different receivers (different start lines) are two distinct nodes, never one. ID is the deterministic surrogate derived from that identity, used as the stable handle that code_edges reference. Immutable value; the store is the only shell.

type CoverageReport

type CoverageReport struct {
	TotalModules int
	CoveredCount int
	PartialCount int
	BlindCount   int
	Modules      []ModuleCoverage
}

CoverageReport is the full coverage report for a project.

func ComputeCoverage

func ComputeCoverage(ctx context.Context, db *sql.DB) (*CoverageReport, error)

ComputeCoverage calculates decision coverage for all modules. It joins codebase_modules with affected_files via path prefix matching.

type CoverageStatus

type CoverageStatus string

CoverageStatus represents how well a module is governed by decisions.

const (
	CoverageCovered CoverageStatus = "covered" // ≥1 active decision covers files in this module
	CoveragePartial CoverageStatus = "partial" // has decisions but they're stale or low R_eff
	CoverageBlind   CoverageStatus = "blind"   // no decisions reference files in this module
)

type EdgeKind

type EdgeKind string

EdgeKind enumerates the code→code relationships walked by traversal. Reference edges are deliberately NOT here — they are far more numerous and resolve far weaker, so they never enter the traversal set (per the parity decision).

const (
	EdgeCall              EdgeKind = "call"
	EdgeInterfaceDispatch EdgeKind = "interface_dispatch"
	EdgeImplements        EdgeKind = "implements"
	EdgeExtends           EdgeKind = "extends"
	EdgeEmbeds            EdgeKind = "embeds"
	// EdgeCallback is a synthesized indirect-call edge: a named function passed
	// as a callback argument (`register(handler)`, `emitter.on("x", handler)`) is
	// invoked later through dynamic dispatch the AST cannot follow directly. The
	// edge wires the registration site to the handler so callback-only functions
	// are not falsely shown as having zero callers. Always heuristic provenance.
	EdgeCallback EdgeKind = "callback"
)

type EdgeResolver

type EdgeResolver interface {
	Language() string
	Extensions() []string
	ResolveFileEdges(ctx context.Context, projectRoot, relPath string, symbols SymbolView) ([]CodeEdge, error)
}

EdgeResolver extracts the code→code edges originating in ONE file. It is the per-language PORT: the code graph is NOT Go-specific. Go is the first adapter; Python / TypeScript / Rust / … each add an EdgeResolver implementation plus a registry entry — exactly like the existing ModuleDetector / ImportParser adapters. Orchestration (scan, traversal, tools) codes against this interface, never against a language. An unresolved call/dispatch yields no edge.

type EdgeStore

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

EdgeStore persists code edges. Shell over *sql.DB; the caller owns lifecycle.

func NewEdgeStore

func NewEdgeStore(db *sql.DB) *EdgeStore

NewEdgeStore creates an edge store over an existing DB connection.

func (*EdgeStore) AllEdges

func (e *EdgeStore) AllEdges(ctx context.Context) ([]CodeEdge, error)

AllEdges returns every code edge in one pass — the whole-graph enumeration the fused-graph ranker (graphrank, dec-20260604-3aaad199 phase 2) needs to build adjacency once instead of N per-node queries. Stable order = deterministic build.

func (*EdgeStore) EnsureSchema

func (e *EdgeStore) EnsureSchema(ctx context.Context) error

EnsureSchema creates the code_edges table + indexes if absent (idempotent).

func (*EdgeStore) InEdges

func (e *EdgeStore) InEdges(ctx context.Context, dstID string) ([]CodeEdge, error)

InEdges returns edges where dstID is the target (its callers / dispatchers).

func (*EdgeStore) OutEdges

func (e *EdgeStore) OutEdges(ctx context.Context, srcID string) ([]CodeEdge, error)

OutEdges returns edges where srcID is the source (its callees / dispatch targets).

func (*EdgeStore) ReplaceFileEdges

func (e *EdgeStore) ReplaceFileEdges(ctx context.Context, filePath string, edges []CodeEdge) error

ReplaceFileEdges idempotently rebuilds the edges originating in one file (delete-by-file then insert) so re-indexing a file is exact, not additive.

type FileSymbols

type FileSymbols struct {
	Path     string   // relative path from project root
	Language string   // "go", "python", "javascript", "typescript", "rust", "c", "cpp"
	Lines    int      // total line count
	Symbols  []Symbol // extracted symbols, sorted by line
	ModTime  int64    // modification time (unix nano) for recency sorting
}

FileSymbols holds symbols extracted from a single file.

type GoImport

type GoImport struct {
	Alias      string
	ImportPath string
	LocalDir   string
}

GoImport is one resolved import of a Go file: the alias the source uses to qualify calls, the import path, and the LOCAL directory it maps to (relative to projectRoot) — empty for external (non-module) dependencies, which never resolve to a node.

func ExtractGoImports

func ExtractGoImports(projectRoot, relPath string) ([]GoImport, error)

ExtractGoImports returns the import aliases of a Go file mapped to local directories, using the module path from go.mod. The alias is the explicit rename or, by convention, the import path's last segment (haft's packages follow dir==package, so this resolves correctly; a mismatch simply fails to resolve — an honest miss, not a wrong edge). External imports get LocalDir "".

type GoLang

type GoLang struct{}

GoLang implements ModuleDetector and ImportParser for Go projects.

func (*GoLang) DetectModules

func (g *GoLang) DetectModules(projectRoot string) ([]Module, error)

DetectModules discovers Go packages by walking directories for .go files. Each directory containing .go files (excluding _test.go-only dirs) is a module.

func (*GoLang) Extensions

func (g *GoLang) Extensions() []string

func (*GoLang) Language

func (g *GoLang) Language() string

func (*GoLang) ParseImports

func (g *GoLang) ParseImports(filePath string, projectRoot string) ([]ImportEdge, error)

ParseImports extracts import edges from a Go source file using go/parser.

func (*GoLang) ResolveFileEdges

func (g *GoLang) ResolveFileEdges(ctx context.Context, projectRoot, relPath string, symbols SymbolView) ([]CodeEdge, error)

ResolveFileEdges is the Go adapter's implementation of EdgeResolver — it composes the Go-specific extraction + resolution (call-site, intra-package, cross-file qualified, interface dispatch) behind the port. The Go-specific internals live in callsites.go / dispatch.go; nothing outside this method knows the language is Go.

type IgnoreChecker

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

IgnoreChecker determines if paths should be excluded from scanning. It respects .gitignore (local + global), .haftignore, and a minimal set of hardcoded dirs that should always be skipped (.git, .haft).

func GetIgnoreChecker

func GetIgnoreChecker(projectRoot string) *IgnoreChecker

GetIgnoreChecker returns a cached IgnoreChecker for the given project root.

func NewIgnoreChecker

func NewIgnoreChecker(projectRoot string) *IgnoreChecker

NewIgnoreChecker builds an IgnoreChecker for the given project root. Reads .gitignore, global git ignore files, and .haftignore.

func (*IgnoreChecker) IsIgnored

func (ic *IgnoreChecker) IsIgnored(relPath string) bool

IsIgnored returns true if the relative path should be excluded.

type ImportEdge

type ImportEdge struct {
	SourceModule string // module that imports
	TargetModule string // module being imported
	SourceFile   string // file containing the import
	ImportPath   string // raw import path as written in source
}

ImportEdge represents a dependency between two modules.

type ImportParser

type ImportParser interface {
	// ParseImports extracts import edges from a single source file.
	// Returns edges with raw import paths. Caller resolves to modules.
	ParseImports(filePath string, projectRoot string) ([]ImportEdge, error)

	// Extensions returns file extensions this parser handles.
	Extensions() []string
}

ImportParser extracts import/dependency edges from source files.

type InterfaceDef

type InterfaceDef struct {
	Name      string
	FilePath  string
	StartLine int
	Methods   []MethodSig
}

InterfaceDef is a Go interface and its method set, extracted structurally. Empty (marker) interfaces are excluded by the extractor — they satisfy everything and would only generate noise.

func ExtractGoInterfaces

func ExtractGoInterfaces(projectRoot, relPath string) ([]InterfaceDef, error)

ExtractGoInterfaces extracts each interface type's method set from a Go file. Pure relative to file content. Go only.

type JSTSLang

type JSTSLang struct{}

JSTSLang implements ModuleDetector and ImportParser for JavaScript/TypeScript.

func (*JSTSLang) DetectModules

func (j *JSTSLang) DetectModules(projectRoot string) ([]Module, error)

DetectModules discovers JS/TS packages by looking for package.json files. Handles monorepo workspaces.

func (*JSTSLang) Extensions

func (j *JSTSLang) Extensions() []string

func (*JSTSLang) Language

func (j *JSTSLang) Language() string

func (*JSTSLang) ParseImports

func (j *JSTSLang) ParseImports(filePath string, projectRoot string) ([]ImportEdge, error)

ParseImports extracts import/require edges from a JS/TS file.

func (*JSTSLang) ResolveFileEdges

func (j *JSTSLang) ResolveFileEdges(ctx context.Context, projectRoot, relPath string, symbols SymbolView) ([]CodeEdge, error)

ResolveFileEdges makes JSTSLang an EdgeResolver. It emits two edge families:

  • `extends` / `implements` edges from the explicit JS/TS heritage clauses, resolved directory-locally (heuristic provenance — no import analysis);
  • `call` edges, resolved through relative-import analysis (file-local defs, named imports `{Foo}`, and namespaced `ns.foo()` calls) with the same exactly-1-or-drop discipline (static provenance).

A base or call that does not resolve to exactly one symbol is dropped, never guessed. Default imports and instance-method calls (`obj.method()`) are left unresolved — their target cannot be named soundly from the AST alone.

type MethodSig

type MethodSig struct {
	Name string
}

MethodSig is a method's name for satisfaction matching. Matching is NAME- coverage only — no signature/arity comparison — so a same-name method with a different signature is a possible false positive, which is exactly why implements / interface_dispatch edges carry heuristic provenance. (Arity-based precision would also need the CONCRETE side's arity, which the symbol store does not extract; deferred rather than half-claimed.)

type Module

type Module struct {
	ID        string // auto-generated from path, e.g., "mod-internal-auth"
	Path      string // relative path from project root
	Name      string // human-readable name, e.g., "auth"
	Lang      string // go, js, ts, python, rust, mixed, unknown
	FileCount int    // number of source files
}

Module represents a detected module/package in the codebase.

type ModuleCoverage

type ModuleCoverage struct {
	Module        Module
	Status        CoverageStatus
	DecisionCount int
	DecisionIDs   []string

	// ImpactScore is the number of governed (Covered or Partial) modules
	// that depend on this module — i.e., how many decision-covered places
	// would be at risk if this module changes. Computed in ComputeCoverage
	// via Scanner.GetDependents reverse-graph traversal (dec-20260527-e4b86938).
	// Zero means no governed dependents (isolated utility, low priority).
	ImpactScore int
}

ModuleCoverage describes the decision coverage for a single module.

type ModuleDetector

type ModuleDetector interface {
	// DetectModules walks the project and returns discovered modules.
	DetectModules(projectRoot string) ([]Module, error)

	// Language returns the language this detector handles.
	Language() string
}

ModuleDetector discovers module/package boundaries in a project.

type ModuleGovernanceGap

type ModuleGovernanceGap struct {
	Module Module
	Files  []string
}

ModuleGovernanceGap reports that a module touched by a new decision has no prior active decision coverage.

func FindFirstDecisionModules

func FindFirstDecisionModules(ctx context.Context, db *sql.DB, affectedFiles []string) ([]ModuleGovernanceGap, error)

FindFirstDecisionModules returns touched modules that currently have no active decision coverage. The caller can use this to warn that a decision is establishing the first explicit architectural context for a module.

type ModuleImpactInfo

type ModuleImpactInfo struct {
	ModuleID    string
	ModulePath  string
	DecisionIDs []string
	IsBlind     bool
}

ModuleImpactInfo describes a module affected by dependency propagation.

func EnrichDriftWithImpact

func EnrichDriftWithImpact(ctx context.Context, db *sql.DB, driftFiles []string) ([]ModuleImpactInfo, error)

EnrichDriftWithImpact adds dependency propagation to drift reports. For each drifted file, resolves to a module, finds dependents, and looks up their decisions.

type Provenance

type Provenance string

Provenance records how an edge was established. static = resolved directly from the AST + symbol table; heuristic = synthesized (e.g. structural interface→impl matching), which the agent can treat with appropriate caution.

const (
	ProvenanceStatic    Provenance = "static"
	ProvenanceHeuristic Provenance = "heuristic"
)

type PythonLang

type PythonLang struct{}

PythonLang implements ModuleDetector and ImportParser for Python projects.

func (*PythonLang) DetectModules

func (p *PythonLang) DetectModules(projectRoot string) ([]Module, error)

DetectModules discovers Python packages by looking for __init__.py or pyproject.toml.

func (*PythonLang) Extensions

func (p *PythonLang) Extensions() []string

func (*PythonLang) Language

func (p *PythonLang) Language() string

func (*PythonLang) ParseImports

func (p *PythonLang) ParseImports(filePath string, projectRoot string) ([]ImportEdge, error)

ParseImports extracts import edges from a Python source file.

func (*PythonLang) ResolveFileEdges

func (p *PythonLang) ResolveFileEdges(ctx context.Context, projectRoot, relPath string, symbols SymbolView) ([]CodeEdge, error)

ResolveFileEdges makes PythonLang an EdgeResolver. It emits two edge families:

  • `extends` edges (subclass -> base) from class inheritance, resolved within the file's package (heuristic provenance — no import analysis on bases);
  • `call` edges, resolved through import analysis (file-local defs, names imported via `from M import N`, and module-qualified `m.foo()` calls) with the same exactly-1-or-drop discipline (static provenance).

Every unresolved base or call is an absent edge, never a guessed one. Dynamic instance-method dispatch (`obj.method()` where obj is not an imported module) is deliberately dropped — it cannot be typed soundly from the AST alone.

type Registry

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

Registry maps file extensions to language detectors, import parsers, and code-graph edge resolvers. Adding a language = one adapter type + entries here; orchestration codes against the interfaces, never a specific language.

func NewRegistry

func NewRegistry() *Registry

NewRegistry creates a registry with all supported languages.

func (*Registry) Detectors

func (r *Registry) Detectors() []ModuleDetector

Detectors returns all registered module detectors.

func (*Registry) ParserForFile

func (r *Registry) ParserForFile(path string) ImportParser

ParserForFile returns the import parser for a file, or nil if unsupported.

func (*Registry) ResolverForFile

func (r *Registry) ResolverForFile(path string) EdgeResolver

ResolverForFile returns the code-graph edge resolver for a file, or nil if no language adapter resolves edges for that extension (node extraction may still work — edges are a separate, incrementally-grown capability).

type RepoMap

type RepoMap struct {
	Files      []FileSymbols
	TotalFiles int
	TotalSyms  int
}

RepoMap is the complete symbol map for a repository.

func BuildRepoMap

func BuildRepoMap(projectRoot string, maxFiles int) (*RepoMap, error)

BuildRepoMap scans the project and extracts symbols from all supported files.

type RustLang

type RustLang struct{}

RustLang implements ModuleDetector and ImportParser for Rust projects.

func (*RustLang) DetectModules

func (r *RustLang) DetectModules(projectRoot string) ([]Module, error)

DetectModules discovers Rust crates/modules by looking for Cargo.toml and mod.rs/lib.rs.

func (*RustLang) Extensions

func (r *RustLang) Extensions() []string

func (*RustLang) Language

func (r *RustLang) Language() string

func (*RustLang) ParseImports

func (r *RustLang) ParseImports(filePath string, projectRoot string) ([]ImportEdge, error)

ParseImports extracts use/mod edges from a Rust source file.

type Scanner

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

Scanner detects modules and builds the dependency graph for a project.

func NewScanner

func NewScanner(db *sql.DB) *Scanner

NewScanner creates a new codebase scanner.

func (*Scanner) EnsureIndexMetaSchema

func (s *Scanner) EnsureIndexMetaSchema(ctx context.Context) error

EnsureIndexMetaSchema creates the single-row index-meta table (idempotent).

func (*Scanner) GetDependents

func (s *Scanner) GetDependents(ctx context.Context, moduleID string) ([]string, error)

GetDependents returns modules that depend on the given module (1-hop).

func (*Scanner) GetModules

func (s *Scanner) GetModules(ctx context.Context) ([]Module, error)

GetModules returns all stored modules.

func (*Scanner) ModulesLastScanned

func (s *Scanner) ModulesLastScanned(ctx context.Context) time.Time

ModulesLastScanned returns the time of the last module scan, or zero if never scanned.

func (*Scanner) ResolveFileToModule

func (s *Scanner) ResolveFileToModule(ctx context.Context, filePath string) (string, error)

ResolveFileToModule finds the most specific module for a file path (longest prefix match).

func (*Scanner) ScanDependencies

func (s *Scanner) ScanDependencies(ctx context.Context, projectRoot string) ([]ImportEdge, error)

ScanDependencies parses imports across all modules and builds the dependency graph.

func (*Scanner) ScanEdges

func (s *Scanner) ScanEdges(ctx context.Context, projectRoot string) (int, error)

ScanEdges builds the code_edges layer for every file with a registered EdgeResolver, via the language-agnostic port (Go today; other languages add an adapter). Must run AFTER ScanSymbols — cross-file/dispatch resolution reads the full node store. Idempotent per file; per-file failures skipped.

func (*Scanner) ScanModules

func (s *Scanner) ScanModules(ctx context.Context, projectRoot string) ([]Module, error)

ScanModules detects all modules in the project and stores them in the DB. Respects .gitignore, global git ignore, and .haftignore.

func (*Scanner) ScanSymbols

func (s *Scanner) ScanSymbols(ctx context.Context, projectRoot string) (int, error)

ScanSymbols extracts and stores code symbols for every supported source file, populating the code_symbols node layer of the code graph. Respects the same ignore/exclusion rules as ScanModules; idempotent per file (delete-then-insert). Per-file failures are skipped, not fatal. (Per-file transactions here — the P5 incremental layer narrows this to touched files; a full scan is the cold path.)

func (*Scanner) SetFingerprint

func (s *Scanner) SetFingerprint(ctx context.Context, fp string) error

SetFingerprint records the fingerprint of the tree the current index was built from, so a later query can tell whether the source has changed since.

func (*Scanner) SourceFingerprint

func (s *Scanner) SourceFingerprint(projectRoot string) (string, error)

SourceFingerprint computes a cheap fingerprint of the indexable source tree: a sha256 over sorted "relPath\x00size\x00mtime" lines for every file that ScanSymbols would index (same ignore + language filter). Stat-only — no file reads — so it is fast enough to check on each query. Any added/removed file, or a size/mtime change, flips the fingerprint; an unchanged tree reproduces it exactly. Shell (walk + stat); the hash is deterministic given the metadata.

func (*Scanner) StoredFingerprint

func (s *Scanner) StoredFingerprint(ctx context.Context) (string, error)

StoredFingerprint returns the fingerprint captured at the last index build, or "" if the index has never recorded one (treated as stale by the caller).

type Symbol

type Symbol struct {
	Name     string // symbol name
	Kind     string // "func", "type", "interface", "class", "method", "const"
	Line     int    // 1-based line number
	Exported bool   // starts with uppercase (Go) or is exported
}

Symbol represents an extracted code symbol (function, type, class, etc.)

type SymbolDrift

type SymbolDrift struct {
	FilePath   string `json:"file_path"`
	SymbolName string `json:"symbol_name"`
	SymbolKind string `json:"symbol_kind"`
	Status     string `json:"status"` // "unchanged", "modified", "added", "removed"
	OldLine    int    `json:"old_line,omitempty"`
	NewLine    int    `json:"new_line,omitempty"`
}

SymbolDrift describes how a single symbol changed between baseline and current.

func CompareSymbolSnapshots

func CompareSymbolSnapshots(baseline []SymbolSnapshot, current []SymbolSnapshot) []SymbolDrift

CompareSymbolSnapshots compares baseline snapshots against current state.

type SymbolRef

type SymbolRef struct {
	ID       string
	FilePath string
}

SymbolRef is a symbol's stable id paired with its file — the minimum the fused-graph ranker needs to bridge a symbol node to its file connector node (graphrank, dec-20260604-3aaad199 phase 2) without loading full symbol rows.

type SymbolSnapshot

type SymbolSnapshot struct {
	FilePath   string `json:"file_path"`
	SymbolName string `json:"symbol_name"`
	SymbolKind string `json:"symbol_kind"`        // func, type, class, interface, method
	Line       int    `json:"line"`               // 1-based start line
	EndLine    int    `json:"end_line"`           // 1-based end line
	Hash       string `json:"hash"`               // SHA256 of the symbol's source text
	StartByte  int    `json:"start_byte"`         // body start byte offset — for byte-exact source slicing
	EndByte    int    `json:"end_byte"`           // body end byte offset
	Receiver   string `json:"receiver,omitempty"` // method receiver type (Go), "" otherwise
	Exported   bool   `json:"exported"`           // first rune uppercase (Go export proxy)
}

SymbolSnapshot captures a symbol's identity and content hash at a point in time.

func ExtractSymbolSnapshots

func ExtractSymbolSnapshots(projectRoot, relPath string) ([]SymbolSnapshot, error)

ExtractSymbolSnapshots extracts symbol-level hashes from a file using tree-sitter. Returns one snapshot per symbol, each with a content hash of the symbol's source text.

type SymbolStore

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

SymbolStore persists code symbols (the node layer of the code graph). It does not own the DB connection — the caller manages lifecycle.

func NewSymbolStore

func NewSymbolStore(db *sql.DB) *SymbolStore

NewSymbolStore creates a symbol store over an existing DB connection.

func (*SymbolStore) AllSymbolRefs

func (s *SymbolStore) AllSymbolRefs(ctx context.Context) ([]SymbolRef, error)

AllSymbolRefs enumerates every symbol's (id, file) in one pass, stably ordered.

func (*SymbolStore) EnsureSchema

func (s *SymbolStore) EnsureSchema(ctx context.Context) error

EnsureSchema creates the code_symbols table + indexes if absent (idempotent).

func (*SymbolStore) FileSymbolsStale

func (s *SymbolStore) FileSymbolsStale(ctx context.Context, projectRoot, relPath string) (bool, error)

FileSymbolsStale reports whether the file on disk no longer matches the stored symbols — by node identity + body hash. The is-stale half of the freshness primitive; pair with IndexFileSymbols to rebuild on demand before slicing.

func (*SymbolStore) GetByDir

func (s *SymbolStore) GetByDir(ctx context.Context, dir string) ([]CodeSymbol, error)

GetByDir returns symbols whose file lives DIRECTLY in dir (a Go package = the files in one directory, not nested). Used to scope impl resolution to a single package so bare receiver names don't collide across packages.

func (*SymbolStore) GetByFile

func (s *SymbolStore) GetByFile(ctx context.Context, filePath string) ([]CodeSymbol, error)

GetByFile returns all symbols stored for a file, ordered by start line.

func (*SymbolStore) GetByID

func (s *SymbolStore) GetByID(ctx context.Context, id string) (CodeSymbol, bool, error)

GetByID returns the single node with the given surrogate id, if present. The inverse of NodeID — resolves a traversal hop back to its symbol for display.

func (*SymbolStore) GetByIdentity

func (s *SymbolStore) GetByIdentity(ctx context.Context, file, name string, startLine int) (CodeSymbol, bool, error)

GetByIdentity returns the single node at (file, name, start_line), if present.

func (*SymbolStore) GetByName

func (s *SymbolStore) GetByName(ctx context.Context, name string) ([]CodeSymbol, error)

GetByName returns all symbols with the given name across files (overloads incl.).

func (*SymbolStore) IndexFileSymbols

func (s *SymbolStore) IndexFileSymbols(ctx context.Context, projectRoot, relPath string) error

IndexFileSymbols extracts a file and replaces its symbol rows. The rebuild-on- demand half of the freshness primitive — calling it makes the store match disk.

func (*SymbolStore) ReplaceFileSymbols

func (s *SymbolStore) ReplaceFileSymbols(ctx context.Context, filePath string, syms []CodeSymbol) error

ReplaceFileSymbols idempotently rebuilds one file's symbol rows in a single transaction (delete-then-insert), so re-indexing a file is exact, not additive.

func (*SymbolStore) SearchSymbols

func (s *SymbolStore) SearchSymbols(ctx context.Context, q string, limit int) ([]CodeSymbol, error)

SearchSymbols returns symbols whose name CONTAINS q (case-insensitive), deterministically ranked — exact, prefix, substring, then fuzzy — and capped. The seed-resolution fallback when the exact name is not known. When substring finds nothing it falls back to a bounded edit-distance scan so a TYPO still resolves (autenticate -> authenticate), the tier the store previously lacked. Deterministic (LIKE / edit distance + a fixed Go sort); no embeddings, no second runtime. An empty/whitespace query matches nothing (never "everything").

type SymbolView

type SymbolView interface {
	GetByFile(ctx context.Context, filePath string) ([]CodeSymbol, error)
	GetByDir(ctx context.Context, dir string) ([]CodeSymbol, error)
	GetByName(ctx context.Context, name string) ([]CodeSymbol, error)
}

SymbolView is the read port over the symbol-node store that an EdgeResolver needs to resolve call targets to nodes (scoped per file / package / name). *SymbolStore satisfies it — resolvers depend on this abstraction, not the concrete store (hexagonal).

type TypeEmbed

type TypeEmbed struct {
	Type     string
	Embedded string
}

TypeEmbed is a Go type and one type it embeds — an anonymous struct field or an embedded interface (Go's composition / "extends"). Both names are bare (package-stripped).

func ExtractGoEmbeds

func ExtractGoEmbeds(projectRoot, relPath string) []TypeEmbed

ExtractGoEmbeds reads one Go file and records each struct/interface embedding (an anonymous field, or an embedded interface) as a (Type, Embedded) pair. Shell (read + parse); pure data out. Unparseable files yield nothing.

type TypeFacts

type TypeFacts struct {
	FuncReturns   map[string][]string          // funcName -> ordered bare result types
	MethodReturns map[string][]string          // recvType \x00 methodName -> ordered bare result types
	StructFields  map[string]map[string]string // structType -> fieldName -> bare type
}

TypeFacts is the package-scoped index needed to infer the types of LOCAL variables (`x := f()`, `x := recv.field.Method()`), so interface-dispatch resolution reaches `:=`-inferred receivers, not only declared ones.

Everything here is PACKAGE-SCOPED on purpose: within one Go package, func names, (receiver, method) pairs, and (struct, field) pairs are all unique, so aggregating across the package's files is collision-free. Cross-package facts are deliberately absent — an unresolvable RHS yields NO inferred type, never a guessed one (the precision-cliff discipline that keeps dispatch edges honest).

func ExtractGoTypeFacts

func ExtractGoTypeFacts(projectRoot, relPath string) (TypeFacts, error)

ExtractGoTypeFacts reads one Go file and records its func/method return types and struct field types. Shell (file read + parse); the recorded facts are pure data. Unparseable files yield empty facts, never an error that aborts a scan.

func NewTypeFacts

func NewTypeFacts() TypeFacts

NewTypeFacts returns an empty, ready-to-fill TypeFacts.

Jump to

Keyboard shortcuts

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