Documentation
¶
Overview ¶
Package retrieve holds FTS5 keyword recall: search, the linear-scan fallback, cross-project the cross-project search, and the anchor helpers (lineage walk + ranked anchor messages) that the view layer composes into bookend windows.
Index ¶
Constants ¶
const ( MethodBM25 = "bm25 only" MethodBM25Coverage = "bm25 + coverage re-rank" MethodSortOverlay = "sort overlay (recency)" )
Ranking-regime labels reported by ScoreExplain.Method — the honest name of the rule that decided a hit's position. These mirror the three branches in Search/MatchAnchors exactly (no invented blend):
- bm25 only: single-term relevance — pure FTS5 bm25 order (rank, m.id).
- bm25 + coverage re-rank: multi-term relevance — stable re-sort by the count of distinct query terms matched; bm25 is the tiebreak.
- sort overlay (newest/oldest): a recency sort REPLACES relevance entirely; bm25 and coverage do not influence position.
Variables ¶
This section is empty.
Functions ¶
func LineageRoot ¶
LineageRoot walks parent_id to the conversation root (capped at 64 hops).
func SearchExplained ¶
func SearchExplained(dbp, q string, limit int, p SearchParams) (out []Hit, explains []ScoreExplain)
SearchExplained runs the same ranking as Search and returns the top-`limit` Hits alongside a parallel, honest ScoreExplain per hit (explains[i] explains out[i]). It is the clean entrypoint the cli calls behind --debug-search; no extra query, no behavior change — the order is byte-identical to Search.
Types ¶
type AllHit ¶
AllHit is one cross-project cross-project result: a Hit plus the project label and the per-project hit count.
type Anchor ¶
type Anchor struct {
ID int
SessionID string
UUID string // source message uuid — the stable external read-ref handle
Role string // empty for a vector-only synthesized anchor
ISO string
Parent string
Snip string
Cov int
// OnlyCopySince is the session's only_copy_since watermark: >0 when the backing
// source file was deleted by the CLI but the row is retained (RawClaw is now the only copy).
// Surfaced so the only-copy state is clear. 0 = present upstream.
OnlyCopySince float64
// Attached by the fusion / discovery layers (zero until set):
Fused float64 // RRF score (semantic.Fuse)
Topic string // topic-layer label for this anchor's segment (Fuse / TopicForMessage)
Root string // lineage root session id (LineageRoot)
Project string // project label
DBP string // db path this anchor came from
Rank int // original keyword rank (tiebreak)
Routine bool // true when the session has an effective routine verdict
}
Anchor is one ranked anchor message from MatchAnchors — a candidate the view layer expands into a bookend window. Carries (id, session_id, role, iso, parent, snip, cov) plus the fields the view/discovery layers attach downstream.
func MatchAnchors ¶
MatchAnchors runs FTS5 recall and returns ranked Anchor messages (the OR/coverage logic of Search, returning message ids for the view layer). `fetch` is the overfetch ceiling.
type ExplainInputs ¶
type ExplainInputs struct {
Terms []string // distinct lowercased query terms (from lowerSet)
Multi bool // true when the query OR-expanded (>1 term) — gates coverage re-rank
Sort string // p.Sort: "" relevance, else "newest"/"oldest" overlay
Routines []bool // routine flag per hit, parallel to covs
}
ExplainInputs are the real ranking inputs an explainer needs, captured at the same point Search/MatchAnchors compute them. A caller that already ran the search passes the ordered Cov values it observed.
type Hit ¶
type Hit struct {
ISO string
SessionID string
Role string
IsSubagent bool
Parent string
Snippet string
Routine bool
}
Hit is one flat search result returned by Search / LinearFallback: (iso, sid, role, is_sub, parent, snip).
func LinearFallback ¶
func LinearFallback(transcriptDir, q string, limit int, p SearchParams) []Hit
LinearFallback is the FTS5-absent linear scan over a project's JSONL, honoring the same flags + phrase (substring/adjacency) semantics.
NOTE: modernc.org/sqlite always has FTS5, so this path is dead in practice — kept for parity with the FTS5 path.
type ScoreExplain ¶
type ScoreExplain struct {
BM25Rank int `json:"bm25_rank"` // ordinal in bm25 order; -1 when a sort overlay ordered the hit
Coverage int `json:"coverage"` // distinct query terms matched (the real re-rank key)
Recency float64 `json:"recency"` // 1 = a recency sort overlay set the order, else 0
Final int `json:"final"` // 0-based ordinal position in the returned results
Method string `json:"method"` // ranking regime: one of the Method* constants
Terms []string `json:"terms"` // the lowercased distinct query terms scored against
Tier string `json:"tier"` // sort tier: "normal" or "routine"
}
ScoreExplain is the LLM-free, honest breakdown of WHY one hit landed at its rank. It carries only what the real ranking actually uses — it does NOT fabricate a composite scalar score, because RawClaw has none.
Honesty notes (read these before trusting a field):
- BM25 is NOT selected as a scalar by the live query (the SQL orders by the opaque FTS5 `rank` and never reads its value). So BM25Rank is the hit's ORDINAL position in bm25 order, not the bm25 number. -1 means "bm25 did not order this result" (a recency sort overlay was in force).
- Coverage is the REAL integer the re-rank uses: distinct query terms found in the hit's coverage haystack. Always 1 for a single-term query.
- Recency is a BOOL-as-float flag, not a weight: 1 when a sort overlay set the order, 0 otherwise. There is no recency term blended into relevance.
- Final is the hit's 0-based ordinal in the returned slice (its actual position), NOT a computed score. Method names the rule that produced it.
func Explain ¶
func Explain(covs []int, in ExplainInputs) []ScoreExplain
Explain builds one honest ScoreExplain per (already-ordered) coverage value, in result order. `covs[i]` is the coverage the ranker computed for the hit at position i (Anchor.Cov, or scoredHit.cov). The returned breakdowns describe the REAL regime — no invented blend, no fake BM25 scalar.
Under a sort overlay, bm25 did not order the hits, so BM25Rank is -1 and Recency is 1. Under relevance, BM25Rank is the hit's bm25 ordinal: for the single-term (bm25-only) regime it equals the result position; for the multi-term regime it is unknown after the stable coverage re-sort, so it is reported as -1 (honest: we cannot recover the pre-resort bm25 ordinal here).
type SearchParams ¶
type SearchParams struct {
Role string // "" = any; else "user"/"assistant"
Sort string // "" = relevance; "newest"/"oldest"
IncludeTools bool
IncludeSubagents bool
Since string // "" = no bound; else YYYY-MM-DD inclusive
Before string // "" = no bound; else YYYY-MM-DD inclusive
RawMatch string // "" = plain path; else explicit FTS5 expr (boolean query)
MinMessages int // 0 = no minimum
// Scope narrowing for the one store, where project and source are columns
// rather than a choice of which database to open. Both empty searches the
// whole corpus. Projects is an exact-match list the caller has already
// resolved (a path pattern is matched in Go, never pushed into SQL).
Projects []string
SourceTool string
}
SearchParams groups the many optional filters shared by Search / MatchAnchors (keeps signatures under the 4-param guideline). A zero value = no filters.