Documentation
¶
Index ¶
- func ExtractGoSignatures(projectRoot, relPath string) (map[int]map[string]string, error)
- func ExtractGoSignaturesWithLocals(projectRoot, relPath string, facts TypeFacts) (map[int]map[string]string, error)
- func FormatCoverageResponse(report *CoverageReport) string
- func FormatSymbolDrift(drifts []SymbolDrift) string
- func InferLocalVarTypes(fn *ast.FuncDecl, base map[string]string, facts TypeFacts) map[string]string
- func IsExcludedDir(name string) bool
- func NodeID(filePath, name string, startLine int) string
- func RenderRepoMap(rm *RepoMap, maxTokens int) string
- func Satisfies(typeMethods map[string]bool, iface InterfaceDef) bool
- func SliceBody(content []byte, sym CodeSymbol) ([]byte, bool)
- func TypeMethodSets(syms []CodeSymbol) map[string]map[string]bool
- func VerifyBody(content []byte, sym CodeSymbol) (body []byte, fresh bool)
- type CCppLang
- type CallSite
- type CodeEdge
- func ResolveConcreteMethodCallEdges(filePath string, fileSymbols []CodeSymbol, callSites []CallSite, ...) []CodeEdge
- func ResolveCrossFileCallEdges(filePath string, fileSymbols []CodeSymbol, callSites []CallSite, ...) []CodeEdge
- func ResolveEmbedsEdges(relPath string, fileSyms, pkgSyms []CodeSymbol, embeds []TypeEmbed) []CodeEdge
- func ResolveImplementsEdges(relPath string, fileSyms, pkgSyms []CodeSymbol, ...) []CodeEdge
- func ResolveInterfaceDispatchEdges(filePath string, fileSymbols []CodeSymbol, callSites []CallSite, ...) []CodeEdge
- func ResolveIntraPackageCallEdges(filePath string, fileSymbols, pkgSymbols []CodeSymbol, callSites []CallSite) []CodeEdge
- type CodeSymbol
- type CoverageReport
- type CoverageStatus
- type EdgeKind
- type EdgeResolver
- type EdgeStore
- func (e *EdgeStore) AllEdges(ctx context.Context) ([]CodeEdge, error)
- func (e *EdgeStore) EnsureSchema(ctx context.Context) error
- func (e *EdgeStore) InEdges(ctx context.Context, dstID string) ([]CodeEdge, error)
- func (e *EdgeStore) OutEdges(ctx context.Context, srcID string) ([]CodeEdge, error)
- func (e *EdgeStore) ReplaceFileEdges(ctx context.Context, filePath string, edges []CodeEdge) error
- type FileSymbols
- type GoImport
- type GoLang
- func (g *GoLang) DetectModules(projectRoot string) ([]Module, error)
- func (g *GoLang) Extensions() []string
- func (g *GoLang) Language() string
- func (g *GoLang) ParseImports(filePath string, projectRoot string) ([]ImportEdge, error)
- func (g *GoLang) ResolveFileEdges(ctx context.Context, projectRoot, relPath string, symbols SymbolView) ([]CodeEdge, error)
- type IgnoreChecker
- type ImportEdge
- type ImportParser
- type InterfaceDef
- type JSTSLang
- func (j *JSTSLang) DetectModules(projectRoot string) ([]Module, error)
- func (j *JSTSLang) Extensions() []string
- func (j *JSTSLang) Language() string
- func (j *JSTSLang) ParseImports(filePath string, projectRoot string) ([]ImportEdge, error)
- func (j *JSTSLang) ResolveFileEdges(ctx context.Context, projectRoot, relPath string, symbols SymbolView) ([]CodeEdge, error)
- type MethodSig
- type Module
- type ModuleCoverage
- type ModuleDetector
- type ModuleGovernanceGap
- type ModuleImpactInfo
- type Provenance
- type PythonLang
- func (p *PythonLang) DetectModules(projectRoot string) ([]Module, error)
- func (p *PythonLang) Extensions() []string
- func (p *PythonLang) Language() string
- func (p *PythonLang) ParseImports(filePath string, projectRoot string) ([]ImportEdge, error)
- func (p *PythonLang) ResolveFileEdges(ctx context.Context, projectRoot, relPath string, symbols SymbolView) ([]CodeEdge, error)
- type Registry
- type RepoMap
- type RustLang
- type Scanner
- func (s *Scanner) EnsureIndexMetaSchema(ctx context.Context) error
- func (s *Scanner) GetDependents(ctx context.Context, moduleID string) ([]string, error)
- func (s *Scanner) GetModules(ctx context.Context) ([]Module, error)
- func (s *Scanner) ModulesLastScanned(ctx context.Context) time.Time
- func (s *Scanner) ResolveFileToModule(ctx context.Context, filePath string) (string, error)
- func (s *Scanner) ScanDependencies(ctx context.Context, projectRoot string) ([]ImportEdge, error)
- func (s *Scanner) ScanEdges(ctx context.Context, projectRoot string) (int, error)
- func (s *Scanner) ScanModules(ctx context.Context, projectRoot string) ([]Module, error)
- func (s *Scanner) ScanSymbols(ctx context.Context, projectRoot string) (int, error)
- func (s *Scanner) SetFingerprint(ctx context.Context, fp string) error
- func (s *Scanner) SourceFingerprint(projectRoot string) (string, error)
- func (s *Scanner) StoredFingerprint(ctx context.Context) (string, error)
- type Symbol
- type SymbolDrift
- type SymbolRef
- type SymbolSnapshot
- type SymbolStore
- func (s *SymbolStore) AllSymbolRefs(ctx context.Context) ([]SymbolRef, error)
- func (s *SymbolStore) EnsureSchema(ctx context.Context) error
- func (s *SymbolStore) FileSymbolsStale(ctx context.Context, projectRoot, relPath string) (bool, error)
- func (s *SymbolStore) GetByDir(ctx context.Context, dir string) ([]CodeSymbol, error)
- func (s *SymbolStore) GetByFile(ctx context.Context, filePath string) ([]CodeSymbol, error)
- func (s *SymbolStore) GetByID(ctx context.Context, id string) (CodeSymbol, bool, error)
- func (s *SymbolStore) GetByIdentity(ctx context.Context, file, name string, startLine int) (CodeSymbol, bool, error)
- func (s *SymbolStore) GetByName(ctx context.Context, name string) ([]CodeSymbol, error)
- func (s *SymbolStore) IndexFileSymbols(ctx context.Context, projectRoot, relPath string) error
- func (s *SymbolStore) ReplaceFileSymbols(ctx context.Context, filePath string, syms []CodeSymbol) error
- func (s *SymbolStore) SearchSymbols(ctx context.Context, q string, limit int) ([]CodeSymbol, error)
- type SymbolView
- type TypeEmbed
- type TypeFacts
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ExtractGoSignatures ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 (*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 ¶
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 ¶
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 ¶
NewEdgeStore creates an edge store over an existing DB connection.
func (*EdgeStore) AllEdges ¶
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 ¶
EnsureSchema creates the code_edges table + indexes if absent (idempotent).
func (*EdgeStore) InEdges ¶
InEdges returns edges where dstID is the target (its callers / dispatchers).
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 ¶
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 ¶
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 ¶
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 (*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 ¶
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 ¶
DetectModules discovers JS/TS packages by looking for package.json files. Handles monorepo workspaces.
func (*JSTSLang) Extensions ¶
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 ¶
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.
type RustLang ¶
type RustLang struct{}
RustLang implements ModuleDetector and ImportParser for Rust projects.
func (*RustLang) DetectModules ¶
DetectModules discovers Rust crates/modules by looking for Cargo.toml and mod.rs/lib.rs.
func (*RustLang) Extensions ¶
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 (*Scanner) EnsureIndexMetaSchema ¶
EnsureIndexMetaSchema creates the single-row index-meta table (idempotent).
func (*Scanner) GetDependents ¶
GetDependents returns modules that depend on the given module (1-hop).
func (*Scanner) GetModules ¶
GetModules returns all stored modules.
func (*Scanner) ModulesLastScanned ¶
ModulesLastScanned returns the time of the last module scan, or zero if never scanned.
func (*Scanner) ResolveFileToModule ¶
ResolveFileToModule finds the most specific module for a file path (longest prefix match).
func (*Scanner) ScanDependencies ¶
ScanDependencies parses imports across all modules and builds the dependency graph.
func (*Scanner) ScanEdges ¶
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 ¶
ScanModules detects all modules in the project and stores them in the DB. Respects .gitignore, global git ignore, and .haftignore.
func (*Scanner) ScanSymbols ¶
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 ¶
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 ¶
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.
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 ¶
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 ¶
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 ¶
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 ¶
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.