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

159 lines
4.6 KiB
Go

package resolver
import (
"context"
"sort"
"strings"
"time"
"go.uber.org/zap"
"github.com/zzet/gortex/internal/graph"
)
// RemoteDeclarationProber is satisfied by the daemon's Federator. It asks
// each enabled remote whether it owns a declaration matching name,
// passing the caller's import/module hint so a bare-name probe is never
// issued. Returns the first positive hit or ok=false. The
// implementation lives in internal/daemon (keeps internal/resolver pure —
// no HTTP here); it bounds its own per-remote deadline (ProxyToolCtx).
type RemoteDeclarationProber interface {
ProbeDeclaration(ctx context.Context, name, importHint string) (RemoteDecl, bool)
}
// RemoteDecl is a remote daemon's confirmed declaration of a symbol.
type RemoteDecl struct {
Slug string // owning remote roster slug
RemoteID string // <prefix>/<file>::<sym> on the remote
Kind graph.NodeKind
RepoPrefix string
WorkspaceID string
File string
Line int
}
// EnableRemoteStitch wires the proxy-edge mint path: a prober and the
// proxy-node heap bound. Called by the daemon entry point only when
// federation.edges.enabled. A nil prober (or budget <= 0 with no prober)
// leaves the resolver in its default read-only fan-out behaviour.
func (cr *CrossRepoResolver) EnableRemoteStitch(prober RemoteDeclarationProber, proxyBudget int) {
cr.prober = prober
cr.proxyBudget = proxyBudget
cr.edgesEnabled = prober != nil
}
// tryRemoteStitch is the gated proxy-edge mint. It runs only after local
// resolution fails (the caller checks e.To == oldTo). On a confirmed
// remote declaration it mints an origin-namespaced proxy node and
// rewrites the edge to it with honest provenance (text_matched, never
// lsp_resolved). Returns true when it stitched.
func (cr *CrossRepoResolver) tryRemoteStitch(e *graph.Edge, name string, stats *CrossRepoStats) bool {
// 1. EVIDENCE GATE: never mint on a bare name. The caller
// file must import something for a remote target to be plausible.
importHint := cr.importHintFor(e)
if importHint == "" {
return false
}
decl, ok := cr.prober.ProbeDeclaration(context.Background(), name, importHint)
if !ok {
return false
}
// 2. MINT the proxy node (origin-namespaced so it can never alias a
// local id), bounded by the heap budget.
pid := graph.ProxyNodeID(decl.Slug, decl.RemoteID)
if cr.graph.GetNode(pid) == nil {
if cr.proxyBudgetExceeded() {
if cr.logger != nil {
cr.logger.Warn("federation: proxy-node budget exceeded; mint refused",
zap.Int("budget", cr.proxyBudget), zap.String("name", name))
}
return false
}
cr.graph.AddNode(&graph.Node{
ID: pid,
Kind: decl.Kind,
Name: name,
FilePath: decl.File,
StartLine: decl.Line,
RepoPrefix: decl.RepoPrefix,
WorkspaceID: decl.WorkspaceID,
Origin: "remote:" + decl.Slug,
Stub: true,
FetchedAt: time.Now(),
})
}
// 3. REWRITE the edge to the proxy with honest provenance.
e.To = pid
e.CrossRepo = true
e.Origin = graph.OriginTextMatched
// resolveFunctionCall counted this edge as Unresolved; it is now a
// cross-repo edge to the proxy.
if stats.Unresolved > 0 {
stats.Unresolved--
}
stats.CrossRepoEdges++
return true
}
// importHintFor returns a comma-joined list of the caller file's import
// targets, or "" when the file imports nothing. A non-empty hint is the
// positive evidence the mint path requires; "" disables the probe.
func (cr *CrossRepoResolver) importHintFor(e *graph.Edge) string {
fileID := cr.callerFileID(e)
if fileID == "" {
return ""
}
var hints []string
seen := map[string]struct{}{}
for _, ie := range cr.graph.GetOutEdges(fileID) {
if ie.Kind != graph.EdgeImports {
continue
}
h := importHintName(ie.To)
if h == "" {
continue
}
if _, dup := seen[h]; dup {
continue
}
seen[h] = struct{}{}
hints = append(hints, h)
}
if len(hints) == 0 {
return ""
}
sort.Strings(hints)
return strings.Join(hints, ",")
}
// importHintName normalises an import edge's target into a module/path
// hint, stripping the unresolved + import:: markers.
func importHintName(to string) string {
n := graph.UnresolvedName(to)
if n == "" {
n = to
}
return strings.TrimPrefix(n, "import::")
}
// proxyBudgetExceeded reports whether the graph already holds the maximum
// number of proxy nodes. Counts on demand; mints are rare
// (gated behind the evidence rule + the off-by-default flag).
func (cr *CrossRepoResolver) proxyBudgetExceeded() bool {
if cr.proxyBudget <= 0 {
return false
}
count := 0
for _, n := range cr.graph.AllNodes() {
if graph.IsProxyNode(n) {
count++
if count >= cr.proxyBudget {
return true
}
}
}
return false
}