Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

111 lines
3.6 KiB
Go

package indexer
import (
"go.uber.org/zap"
"github.com/zzet/gortex/internal/artifacts"
"github.com/zzet/gortex/internal/graph"
)
// contentLinkMinEdgeBudget is the floor on EdgeMotivates the doc->code linker
// may add. Above it the budget scales to 10% of the live edge count, so the
// derived why-layer can never approach doubling the graph.
const contentLinkMinEdgeBudget = 2000
// contentLinkEdgeBudget is the single authority bounding how many EdgeMotivates
// edges the content->code linker emits: max(2000, 10% of live edges).
func contentLinkEdgeBudget(edgeCount int) int {
if scaled := edgeCount / 10; scaled > contentLinkMinEdgeBudget {
return scaled
}
return contentLinkMinEdgeBudget
}
// linkContentToCode mints EdgeMotivates edges from content chunks (pdf / office
// / text KindDoc tagged data_class=content) to the code symbols their text
// names, reusing the artifact reference scanner (whole-token match, 4-char
// floor, 200 refs/chunk, 1 MiB scan cap). Same-repo edges are emitted here;
// because EdgeMotivates is registered in BaseKindsForCrossRepo, the
// DetectCrossRepoEdges pass that runs next mints the cross_repo_motivates
// parallel for any edge spanning a repo boundary. Bounded by a single edge
// budget so the why-layer cannot bloat the graph; over budget, it stops and
// logs rather than silently truncating.
func (idx *Indexer) linkContentToCode() {
g := idx.graph
if g == nil {
return
}
nameIndex := artifacts.SymbolNameIndex(g, "") // union across all repos
if len(nameIndex) == 0 {
return
}
budget := contentLinkEdgeBudget(g.EdgeCount())
var edges []*graph.Edge
added := 0
truncated := false
// emit scans one chunk's FULL body for symbol references and mints the
// EdgeMotivates edges, honouring the single edge budget. Returns false
// when the budget is exhausted so the caller stops the scan.
emit := func(chunkID, filePath, text string) bool {
if text == "" {
return true
}
signal := mineDocSignal(text)
for _, sym := range artifacts.ScanSymbolRefs([]byte(text), nameIndex) {
if sym == chunkID {
continue
}
if added >= budget {
truncated = true
return false
}
edges = append(edges, &graph.Edge{
From: chunkID, To: sym, Kind: graph.EdgeMotivates,
FilePath: filePath, Origin: graph.OriginTextMatched,
Meta: map[string]any{"signal": signal},
})
added++
}
return true
}
if cs := idx.contentSearcher(); cs != nil {
// Disk path: read FULL section bodies from the content index — the
// graph node keeps only a snippet — streamed so a content-heavy
// repo's hundreds of thousands of sections never materialise here.
// contentSearcher() resolves the disk sink even while idx.graph is
// the in-memory shadow (where the content rows were streamed to disk).
_ = cs.ScanContent(idx.repoPrefix, func(nodeID, filePath, body string) bool {
return emit(nodeID, filePath, body)
})
} else {
// In-memory fallback: with no content index the full text is still on
// the nodes (streamContentSections leans only when a content searcher
// exists), so scan the graph nodes directly.
for _, chunk := range g.AllNodes() {
if !graph.IsContentNode(chunk) {
continue
}
text, _ := chunk.Meta["section_text"].(string)
if !emit(chunk.ID, chunk.FilePath, text) {
break
}
}
}
if len(edges) > 0 {
g.AddBatch(nil, edges)
}
if idx.logger == nil {
return
}
if truncated {
idx.logger.Warn("indexer: content->code linking hit the edge budget; remaining links dropped",
zap.Int("budget", budget), zap.Int("emitted", added))
} else if added > 0 {
idx.logger.Info("indexer: content->code why-links emitted (global)", zap.Int("edges", added))
}
}