memory

package
v0.6.1 Latest Latest
Warning

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

Go to latest
Published: Aug 13, 2026 License: MIT Imports: 4 Imported by: 0

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

View Source
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

func EstimateTokens(s string) int

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

func RenderLine(e storage.Entry) string

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.

func (*ErrQuery) Error

func (e *ErrQuery) Error() string

func (*ErrQuery) Unwrap

func (e *ErrQuery) Unwrap() error

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

type Pool struct {
	Entries []storage.Entry
	Matched []int64
}

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

func Slice(entries []storage.Entry, opts Options) Result

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.

type Source

type Source interface {
	List(f storage.ListFilter) ([]storage.Entry, error)
	Search(query string, limit int) ([]storage.Entry, error)
}

Source is the read surface Gather needs. *storage.Store satisfies it; a test can substitute a fake without a database.

Jump to

Keyboard shortcuts

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