Documentation
¶
Overview ¶
Package store owns the SQLite index. Knowledge-card search data can be rebuilt from files; runtime injection history is preserved by explicit schema migrations.
Index ¶
- Constants
- func GranLevel(g string) int
- type BM25Hit
- type CardStats
- type Effectiveness
- type EmbedTarget
- type FileState
- type InjectionAgg
- type InjectionRecord
- type InjectionRow
- type Store
- func (s *Store) AddFeedback(ctx context.Context, cardID, kind string, delta float64) error
- func (s *Store) AllCardStats(ctx context.Context, now time.Time) (map[string]CardStats, error)
- func (s *Store) AllCards(ctx context.Context) ([]StoredCard, error)
- func (s *Store) AllCardsMeta(ctx context.Context) ([]StoredCard, error)
- func (s *Store) CardByID(ctx context.Context, id string) (StoredCard, error)
- func (s *Store) CardWindowUsage(ctx context.Context) (map[string]WindowUsage, error)
- func (s *Store) CardsByRowid(ctx context.Context, rowids []int64) ([]StoredCard, error)
- func (s *Store) Close() error
- func (s *Store) DailyTokens(ctx context.Context, days int) ([]int, error)
- func (s *Store) DeleteCardByPath(ctx context.Context, path string) error
- func (s *Store) Embeddings(ctx context.Context, model string) (map[int64][]float32, error)
- func (s *Store) FileStates(ctx context.Context) (map[string]FileState, error)
- func (s *Store) InjectedCardIDs(ctx context.Context) (map[string]bool, error)
- func (s *Store) InjectedLevels(ctx context.Context, sessionID string) (map[string]int, error)
- func (s *Store) InjectionAggs(ctx context.Context) ([]InjectionAgg, error)
- func (s *Store) InjectionsSince(ctx context.Context, cutoff time.Time) ([]InjectionRow, error)
- func (s *Store) MetaGet(ctx context.Context, key string) (string, error)
- func (s *Store) MetaSet(ctx context.Context, key, value string) error
- func (s *Store) MissingEmbeddings(ctx context.Context, model string) ([]EmbedTarget, error)
- func (s *Store) PenalizeAbandonedPointers(ctx context.Context, sessionID string) error
- func (s *Store) PruneStale(ctx context.Context, retainDays int) error
- func (s *Store) Rebuild(ctx context.Context) error
- func (s *Store) RecentInjections(ctx context.Context, limit int) ([]InjectionRow, error)
- func (s *Store) RecordInjections(ctx context.Context, sessionID, event, promptHash, cwd string, ...) error
- func (s *Store) ResetSession(ctx context.Context, sessionID string) error
- func (s *Store) SearchBM25(ctx context.Context, matchExpr string, limit int) ([]BM25Hit, error)
- func (s *Store) SessionCount(ctx context.Context) (int, error)
- func (s *Store) SessionTokens(ctx context.Context, sessionID string) (int, error)
- func (s *Store) SwapLastPrompt(ctx context.Context, sessionID, prompt string) (string, error)
- func (s *Store) UpsertCard(ctx context.Context, c knowledge.Card, mtime, size int64) error
- func (s *Store) UpsertEmbedding(ctx context.Context, rowid int64, model string, vec []float32, ...) error
- func (s *Store) UtilityMultipliers(ctx context.Context) (map[string]float64, error)
- type StoredCard
- type WindowUsage
Constants ¶
const ( EffHelpful = "helpful" // pulled or referenced after injection EffNoisy = "noisy" // downvoted or repeatedly ignored as a pointer EffExpensive = "expensive" // heavy token spend, no observed positive signal EffUncertain = "uncertain" // too few observations to judge )
Effectiveness buckets — a per-card verdict derived from the decayed feedback counters plus the card's footprint in the injection-log retention window. Purely read-side: buckets never feed ranking (UtilityMultipliers does that); they exist so the operator can see which cards earn their tokens.
const ( GranHook = "hook" GranSummary = "summary" GranBody = "body" )
Granularity levels, ordered: hook < summary < body. Session dedup is monotonic — a card is only re-sent at a strictly higher level (14.43).
const ( FeedbackInjected = "injected" FeedbackExpanded = "expanded" // expand_card pull — strong positive FeedbackReferenced = "referenced" // cited by a saved lesson — strongest positive; recorded by the Phase 4 miner FeedbackDownvoted = "downvoted" // explicit `culi down` (weight 1.0) or abandoned pointer (0.1) )
Feedback kinds — event counters on card_stats. Weights are applied at multiplier time (utilityWeights), so counters stay interpretable as decayed event counts.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type BM25Hit ¶
SearchBM25 runs an FTS5 MATCH over the corpus and returns (rowid, rank) pairs, best first. matchExpr MUST be pre-sanitized by the caller (internal/retrieve owns the sanitizer) — raw prompt text here is a bug.
type Effectiveness ¶ added in v0.3.0
type Effectiveness struct {
Bucket string `json:"bucket"`
Positive float64 `json:"positive"` // weighted: 3·expanded + 5·referenced
Negative float64 `json:"negative"` // weighted: 5·downvoted
PullRate float64 `json:"pull_rate"` // (expanded+referenced) per observed injection
}
Effectiveness is one card's verdict with the rates behind it.
func ClassifyEffectiveness ¶ added in v0.3.0
func ClassifyEffectiveness(cs CardStats, usage WindowUsage) Effectiveness
ClassifyEffectiveness buckets one card from its decayed feedback counters and its retention-window usage.
Deliberate asymmetry: full-body pushes are unobservable (plan §9 — an injected body can help without ever being expanded), so "no signal" alone never marks a card noisy. Negative inference comes only from explicit downvotes and the abandoned-pointer penalty, both already in Downvoted; signal-free cards are at most "expensive" when their token cost is high.
type EmbedTarget ¶
type EmbedTarget struct {
Rowid int64
ContentHash string
Text string // title + summary — what gets embedded
}
EmbedTarget is a card needing (re-)embedding.
type FileState ¶
FileState is the (mtime, size) fingerprint the indexer compares before re-hashing a file.
type InjectionAgg ¶
type InjectionAgg struct {
Event string `json:"event"`
Granularity string `json:"granularity"`
Count int `json:"count"`
Tokens int `json:"tokens"`
}
InjectionAgg is one (event, granularity) bucket of the injection history. Only the retention window exists — PruneStale bounds it to ~7 days. JSON tags serve `culi stats --json` (snake_case like the rest of that schema).
type InjectionRecord ¶
InjectionRecord is one emitted card at one granularity.
type InjectionRow ¶
type InjectionRow struct {
SessionID string
TS string
Event string
CardID string
Granularity string
Tokens int
Cwd string
Harness string // source agent: claude | codex
}
InjectionRow is one logged card injection with its timestamp — the raw material the review console groups into the Activity "Injections" view. Read-only, off the hot path.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store wraps the SQLite handle. Safe for concurrent use (database/sql pools; WAL allows one writer + many readers).
func Open ¶
Open opens (creating if needed) the index at path. WAL + busy_timeout are set per-connection via the DSN so every pooled connection gets them.
func (*Store) AddFeedback ¶
AddFeedback folds decay-to-now into a card's counters and bumps one of them. kind must be a Feedback* constant; delta is the event count (0.1 for an abandoned pointer, 1 otherwise).
func (*Store) AllCardStats ¶
AllCardStats returns every card's counters decayed to now — the raw material for stats' top-pulled / noisy lists. Off the hot path.
func (*Store) AllCards ¶
func (s *Store) AllCards(ctx context.Context) ([]StoredCard, error)
AllCards returns every card (corpus is small; callers filter in Go).
func (*Store) AllCardsMeta ¶
func (s *Store) AllCardsMeta(ctx context.Context) ([]StoredCard, error)
AllCardsMeta returns every card WITHOUT bodies — the hot-path scan. Bodies are hydrated per-candidate via CardsByRowid only for the few cards the packer actually emits.
func (*Store) CardWindowUsage ¶ added in v0.3.0
CardWindowUsage aggregates the injection log per card over its retention window — the exposure and cost half of the effectiveness verdict. Off the hot path.
func (*Store) CardsByRowid ¶
CardsByRowid hydrates specific rows (e.g. FTS hits), preserving no order.
func (*Store) DailyTokens ¶
DailyTokens returns injected-token totals per calendar day (UTC), oldest first, for up to the last `days` days present in the retention window — the Overview hero sparkline. Empty when nothing has been injected yet.
func (*Store) DeleteCardByPath ¶
DeleteCardByPath removes a vanished file's card + FTS row.
func (*Store) Embeddings ¶
Embeddings returns rowid → vector for every card whose stored vector is FRESH: same model and same content hash as the current card row. Stale vectors are simply absent — the funnel degrades per-card, never errors.
func (*Store) FileStates ¶
FileStates returns path → state for every indexed card.
func (*Store) InjectedCardIDs ¶
InjectedCardIDs returns the set of card IDs with at least one row in the injection log's retention window — the "was this card ever shown" half of the staleness report.
func (*Store) InjectedLevels ¶
InjectedLevels returns card_id → highest granularity level already injected in this session.
func (*Store) InjectionAggs ¶
func (s *Store) InjectionAggs(ctx context.Context) ([]InjectionAgg, error)
InjectionAggs aggregates the injection log for `culi stats`.
func (*Store) InjectionsSince ¶ added in v0.3.0
InjectionsSince returns every retained injection at or after cutoff, oldest first. It is intentionally uncapped: analytics needs the complete retention window, while RecentInjections is the bounded feed used by the Activity UI. This query is only used by the local console and never runs on a hook path.
func (*Store) MissingEmbeddings ¶
MissingEmbeddings lists cards with no fresh vector for model.
func (*Store) PenalizeAbandonedPointers ¶
PenalizeAbandonedPointers applies the −0.5-equivalent nudge (0.1 downvote events × weight 5) to cards injected at hook granularity this session and never expanded. Only pointers earn negative inference: pushed bodies are unobservable (plan §9). Runs at session end, off the hot path.
func (*Store) PruneStale ¶
PruneStale deletes injection/session rows older than the retention window. Cheap, runs at SessionStart — keeps the disposable history bounded.
func (*Store) Rebuild ¶
Rebuild recreates only the file-backed card search index. Runtime-only injections, card stats, session state, and metadata are preserved.
func (*Store) RecentInjections ¶
RecentInjections returns injection-log rows newest first, capped at limit. The console groups them by session then by (ts, event) in Go; keeping the grouping out of SQL avoids a bespoke query the hot path never needs.
func (*Store) RecordInjections ¶
func (s *Store) RecordInjections(ctx context.Context, sessionID, event, promptHash, cwd string, h harness.Harness, recs []InjectionRecord) error
RecordInjections logs emitted cards for dedup + stats in one transaction. cwd is the working directory at injection time, stored for repo attribution in the review console ("" when unknown — e.g. a non-git prompt). h attributes the injection to its source agent for the console's harness filter.
func (*Store) ResetSession ¶
ResetSession clears dedup state for a session (SessionStart with source=compact: the context window was rebuilt, prior injections are gone).
func (*Store) SearchBM25 ¶
func (*Store) SessionCount ¶
SessionCount returns the number of distinct sessions in the injection log.
func (*Store) SessionTokens ¶
SessionTokens sums the tokens injected into one session — the statusline's "what did this session cost so far" number.
func (*Store) SwapLastPrompt ¶
SwapLastPrompt returns the previous prompt for the gate's novelty check ("" if none) and stores the new one, bounded to maxStoredPrompt bytes.
func (*Store) UpsertCard ¶
UpsertCard inserts or replaces a card and its FTS row atomically.
func (*Store) UpsertEmbedding ¶
func (s *Store) UpsertEmbedding(ctx context.Context, rowid int64, model string, vec []float32, contentHash string) error
UpsertEmbedding stores one card's vector, replacing any prior row. The content hash ties the vector to the card revision it was computed from.
func (*Store) UtilityMultipliers ¶
UtilityMultipliers returns card_id → ranking multiplier, decayed read-side (no writes on the hot path). Laplace-smoothed and clamped to [0.5, 1.5]: feedback tunes ordering but can never bury a card (plan §9). Cards without stats are simply absent — callers treat missing as 1.0.
type StoredCard ¶
StoredCard is a card row hydrated from the DB.
type WindowUsage ¶ added in v0.3.0
WindowUsage is one card's footprint in the injection-log retention window (~7 days, bounded by PruneStale): how often it was delivered and what it cost. This — not CardStats.Injected — is the ground truth for exposure: no code path bumps the injected counter (the hook path stays write-light), so classification must read the log.