Documentation
¶
Overview ¶
Package memory ranks a candidate pool of entries by DEC-043's blended reciprocal-rank fusion and trims the result to DEC-044's token budget — the corpus read back as working memory. The package is deliberately PURE: it reads no clock and no host state (the recency signal is ordinal, a rank rather than an age, so the ranking is time-invariant — DEC-043 sub-decision 3), and it imports nothing outside the standard library plus internal/storage (for the Entry type).
The token estimate this package computes sizes a single retrieval; it is never written to storage and never enters the caller-reported token-count tag DEC-027 reserves — see DEC-044's honesty clause.
Index ¶
Constants ¶
const ( FusionK = 60 WeightRecency = 1.0 WeightMatch = 1.0 WeightProject = 1.0 PoolLimit = 200 DefaultBudget = 2000 CharsPerToken = 4 )
Locked constants (DEC-043, DEC-044). Each is exported and golden-pinned: changing any one is a visible behavior change, not a silent one.
Variables ¶
This section is empty.
Functions ¶
func EstimateTokens ¶
EstimateTokens returns ceil(len(s)/4) over UTF-8 BYTES (not runes), DEC-044 sub-decision 2's documented chars-per-token heuristic. Byte counting degrades in the safe direction: it over-estimates the cost of multi-byte text rather than under-estimating it.
func RenderLine ¶
RenderLine renders e as DEC-044 sub-decision 5's one-line entry shape:
- <id> <YYYY-MM-DD> [<project>/<type>] <title> — <impact>
with "-" for an absent project or type, and the impact clause only when Impact is non-empty (no trailing em-dash artifact). The date is created_at's UTC date part only.
Types ¶
type ErrQuery ¶
type ErrQuery struct{ Err error }
ErrQuery marks a malformed query so callers can surface it as a USER error (a UserError on the CLI, a tool error over MCP) rather than an internal one. Everything else Gather returns is an infrastructure failure.
type GatherOptions ¶
type GatherOptions struct {
Query string // raw user query; "" skips the relevance read
Project string // "" skips the project read
}
GatherOptions selects which of the three reads run.
type Item ¶
type Item struct {
Entry storage.Entry
Line string
Tokens int
Score float64
Rank int // 1-based position within the full fused ranking
}
Item is one selected entry plus everything the renderers need. Line is the DEC-044 rendering and Tokens is its estimated cost, so the estimate and the render cannot disagree.
type Options ¶
type Options struct {
// Query is echoed into the envelope only; ranking uses Matched.
Query string
// Project, when non-empty, contributes a soft boost term — never a filter
// (DEC-043 sub-decision 4).
Project string
// Matched is the entry ids Store.Search returned, IN ITS RANK ORDER
// (FTS5 `ORDER BY rank, id DESC`). Nil/empty means "no query". Ids not in
// the candidate pool, and duplicates, are ignored. CALLER CONTRACT: the
// order is the signal — passing an arbitrary id set silently degrades the
// relevance term to whatever order you happened to build.
Matched []int64
// Budget is the token budget for the rendered entry lines. Must be > 0;
// the caller rejects 0/negative before calling (DEC-044 sub-decision 1).
Budget int
}
Options selects and bounds a memory slice. There is deliberately NO `now` field: the recency signal is ordinal (a rank, not an age), so the ranking is time-invariant (DEC-043 sub-decision 3). The renderer owns the wall clock, as in every other DEC-014 consumer.
type Pool ¶
Pool is Gather's result: the union of the reads, plus the bm25 id order that becomes Options.Matched. The order of Matched is load-bearing — it is the relevance ranking itself, not a set (DEC-043 sub-decision 1).
func Gather ¶
func Gather(src Source, opts GatherOptions) (Pool, error)
Gather performs DEC-043 sub-decision 5's one-bounded-read-per-list composition and returns the union pool plus the bm25 id order.
Each read is capped at PoolLimit. Dropping the project read makes old same-project history unreachable at any rank; dropping the match read makes an old top-relevance entry unreachable. Slice dedupes the union by id, so the overlap between reads costs nothing but rows.
type Result ¶
type Result struct {
Items []Item // non-nil, in rank order
Candidates int // deduped pool size
Included int
Skipped int
EstimatedTokens int
Budget int
}
Result is the slice plus its accounting. Included+Skipped == Candidates.
func Slice ¶
Slice ranks entries by DEC-043's reciprocal-rank fusion over three ordinal lists — recency (re-derived from entries, not from input order), relevance (opts.Matched, in its given order), and project membership (opts.Project, recency-ordered) — then trims the ranked list to opts.Budget by DEC-044's skip-and-continue policy: an entry that does not fit is skipped and the fill continues, so a later cheaper entry can still be included.
Slice is a pure function of its arguments. It dedupes the input by ID and is invariant to input order.