evidence

package
v0.14.0 Latest Latest
Warning

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

Go to latest
Published: Aug 21, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

@index Turns ranked search candidates into a list a reader can judge.

Index

Constants

View Source
const PageHitBudget = 50

PageHitBudget is the point past which one answer stops taking on more files.

It bounds a page without ever trimming a file: a file is added whole or left for the next page, and the first file of a page is always added. So a file holding sixty hits is still answered with sixty hits — it just travels alone. Half a file is worse evidence than a long one, because the reader cannot tell which half they got.

Variables

This section is empty.

Functions

This section is empty.

Types

type Coverage added in v0.13.1

type Coverage struct {
	WithReason   int `json:"with_reason"`
	Declarations int `json:"declarations"`
}

Coverage is how much of the searched repositories ever recorded a reason: how many declarations carry at least one @intent or @domainRule, out of how many declarations were indexed at all.

It is a fact about the index rather than about this query, and it is what separates the two empty answers that used to read alike. An empty answer in a repository with 1900 recorded reasons means the reasons do not cover this question; an empty answer in a repository with none means nobody has written anything down yet, and only the second one is answered by annotating.

WithReason counts declarations, never reasons. One reason is one indexed document, so counting documents would report a declaration whose author wrote three of them three times.

This is declared here, rather than reused from the intent package, for the same reason IntentHit is: a list has to be describable without the retrieval ports that filled it.

@domainRule WithReason never exceeds Declarations. @intent let an empty answer say whether anyone ever recorded a reason to search.

func (Coverage) Known added in v0.13.1

func (c Coverage) Known() bool

Known reports whether these numbers were measured at all.

A surface that never reached the recorded-reason index leaves the zero value here, and "0 of 0 declarations recorded a reason" reads as a finding when it is the absence of one. Nothing may state the fraction without asking this first. @intent keep an unmeasured coverage from being reported as a measured zero.

type File

type File struct {
	// Namespace is empty outside federated search. Two repositories can each
	// hold an internal/app/main.go, and those are two files, not one.
	Namespace string
	FilePath  string
	Hits      []Result
}

File is every hit one file answered the query with.

The file is the unit of a search answer because it is the unit of reading: a reader picks a file first and a declaration inside it second. Grouping also removes the old reason to hide hits — ten hits in one file cost one decision, not ten — so a shown file arrives whole.

@intent make the file, not the declaration, the thing a caller chooses between.

func (File) HitCount

func (f File) HitCount() int

HitCount is how many hits this file answered with. @intent let a caller weigh a file before reading any of its hits.

type IntentHit added in v0.13.0

type IntentHit struct {
	Reason string
	Terms  []string
}

IntentHit is what the intent index said about one candidate: the recorded reason it matched and the query terms written in it. @intent carry the intent query's evidence into the list without the list depending on the intent packages.

type List

type List struct {
	Files        []File
	WeakFiltered int
	// OverflowFiles is how many further files this page did not reach, whether
	// the Limit or the page budget stopped it. It separates "this is
	// everything" from "this is the first ten files of thirty".
	OverflowFiles int
	// PoolTruncated says the candidate pool this list was built from came back
	// full: the backend had at least as many candidates as the pool could hold
	// and may have had more. Build never sets it — it is a fact about the fetch,
	// which happens before this package sees anything — so whoever fetched the
	// pool sets it.
	//
	// It is deliberately not folded into OverflowFiles, which counts files and
	// only ever counts files. Read together they separate the two ways a page
	// can end: OverflowFiles == 0 with PoolTruncated false is the whole answer,
	// while OverflowFiles == 0 with PoolTruncated true is only the end of the
	// candidates that were fetched.
	PoolTruncated bool
	// NextOffset is the offset the page after this one starts at. It is set
	// whether or not another page exists; OverflowFiles and PoolTruncated are
	// what say whether asking for it is worth anything.
	//
	// It is carried rather than recomputed by the caller because "offset plus
	// the files on this page" is only right when the page is one contiguous run
	// of the answer. Whoever cut the page is the only one who knows that.
	NextOffset int
	// Coverage is how much of the searched repositories recorded a reason at all.
	// Build does not measure it — it is a fact about the index, which is read
	// before this package sees anything — so it arrives on Options and is carried
	// through unchanged.
	Coverage Coverage
	// Note is set only when Files is empty, and says which kind of empty it is:
	// nothing retrieved, nothing explainable, a page past the end, or a
	// repository where nobody ever recorded a reason to search.
	Note string
}

List is a whole search answer, including what it decided not to show. @intent make "nothing to show" a readable answer rather than an empty array.

func Build

func Build(query string, nodes []graph.Node, opts Options) List

Build turns the reranked candidate pool into a list whose every entry can be justified, and reports what it left out.

The order it is handed is the order it keeps. Ranking was measured against the golden set and its job turned out to be membership, not sequence: a reader who reads all ten lines is unaffected by which is third. So this changes who is in the list, never who is first — except that weak results kept by IncludeWeak go last, since they are there to be scrolled past.

@requires nodes arrive in the order rank.Rerank left them, and carry their loaded Annotation. @ensures the returned Files never exceed Options.Limit, and every returned file carries all of its justified hits. @ensures Note is non-empty exactly when Files is empty. @ensures a negative Offset is read as zero everywhere the page is cut, counted, and stepped from. @intent give a reader or an agent a file list where every line states why it is there.

func (List) Hits

func (l List) Hits() []Result

Hits flattens the answer back into one ranked sequence, a file at a time. @ensures hits of one file stay contiguous and keep the order Build was handed. @intent give renderers and measurements one sequence without losing the grouping.

func (List) Justified added in v0.13.1

func (l List) Justified() bool

Justified reports whether any hit on this page named a signal the query touched. It is false for an empty answer, and also for a page IncludeWeak filled with candidates that matched nothing nameable — to a caller those are the same situation: the answer holds nothing that can say why it is here.

The two are one method rather than two checks at the call site because a caller acts on them identically, and keeping them apart invited the reading that a page with rows on it must have answered something.

@ensures true only when at least one shown hit carries at least one Match. @intent tell a page that answered something apart from one that merely has rows on it.

type Match

type Match string

Match names one reason a candidate is in the list. @intent let a reader see which part of a result the query actually touched.

const (
	MatchName   Match = "name"
	MatchPath   Match = "path"
	MatchIntent Match = "intent"
)

type NodeRef added in v0.13.0

type NodeRef struct {
	Namespace string
	ID        uint
}

NodeRef names one node across repositories. Node ids are unique only within a namespace, so a federated answer needs both to address a node. @intent key per-node intent evidence so it cannot leak onto another repository's node.

type Options

type Options struct {
	Limit  int
	Offset int
	// PerNamespace makes Limit and Offset a budget every namespace gets on its
	// own rather than one budget the namespaces compete for. Federated search
	// sets it; a single-repository search has one namespace and cannot tell the
	// difference.
	//
	// Sharing one budget meant a limit smaller than the namespace count silenced
	// whole repositories, and the page ended up an arbitrary subset of the
	// answer rather than a run of it — so the offset that would resume it did
	// not exist.
	PerNamespace bool
	// IncludeWeak keeps candidates no signal explains, after the explainable
	// ones. Off by default, because a list padded with unexplainable results
	// reads as "here are ten answers" when there were two.
	IncludeWeak bool
	// Intent is what the intent query said about each node it returned. A node
	// with an entry here is justified by it — the terms are the proof — even
	// when its name, path, and @intent share no token with the query.
	Intent map[NodeRef]IntentHit
	// Coverage is how much of the searched repositories recorded a reason at all.
	// It is an input rather than something set on the returned list afterwards
	// because the note depends on it: which kind of empty an empty answer is
	// cannot be decided without knowing whether anybody wrote a reason down.
	Coverage Coverage
}

Options are the caller's choices about how wide the list may be. @domainRule Limit and Offset are both counted in files, never in hits, so paging never splits a file. @intent keep the bounds a caller controls — page size, page position, strictness — in one argument.

type Result

type Result struct {
	Node   graph.Node
	Intent string
	// Matched lists every signal the query touched, in a fixed order: name,
	// path, intent. It is empty only for a weak result kept by IncludeWeak.
	Matched []Match
	// Reason is the recorded reason the intent index matched — the node's
	// @intent, or its @domainRule when no @intent exists. It is set only for
	// hits the intent query returned, so an empty Reason means the hit earned
	// its place on name, path, or token overlap alone.
	Reason string
	// MatchedTerms are the terms of the query written in Reason, as the intent
	// scorer counted them. They are the proof behind MatchIntent when token
	// overlap alone cannot see the match.
	MatchedTerms []string
}

Result is one candidate and the case for it. @intent carry a search hit together with the evidence that justifies showing it.

Jump to

Keyboard shortcuts

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