Documentation
¶
Overview ¶
Favorite setters for the DB-owned entities. Memories and notes are not here: their favorite flag lives in file frontmatter (the source of truth) and reaches the index through the files layer's re-index, never a direct UPDATE. A star is metadata, not authorship, so none of these bump updated_at -- starring must not churn recency sorts or the briefing recency trim.
Free-text search over the structured entities -- tasks, sessions, projects, plans, trials. Memories and notes are not here: they are indexed in the fts table and searched through FTSSearch (fused with semantic hits by internal/retrieve). These five have no FTS mirror, so they match with LIKE over the one or two columns a human would search by (a task's title, a session's name, a project's slug/name). That is a deliberate floor, not a stopgap: they are short, low-cardinality labels where substring matching is what the observer expects, and mirroring them into fts would mean maintaining index rows for high-churn state the files layer does not own.
Every query takes the search text as a bound parameter escaped through escapeLikePrefix, so a literal % or _ matches itself rather than acting as a wildcard.
Package store owns the SQLite database: connection setup, schema migrations, FTS5, and embedding storage. It takes a database path (not the config) to stay a leaf dependency. Files remain the source of truth for durable knowledge; the *_index tables and the fts virtual table are rebuildable mirrors.
Task CRUD and the declarations the rest of the tasks_*.go files share. The dependency-aware queue splits across: tasks_claim.go (leases), tasks_deps.go (the edge DAG), tasks_query.go (the ready/blocked/list reads), tasks_plans.go (plan rollups) and tasks_scan.go (row scanning).
Lease-based task claiming: the compare-and-set claim and the four ways a claim ends (holder release, owner force-release, session teardown, lease expiry). Expiry is enforced lazily inside ClaimTask -- there is no background sweeper.
The task dependency DAG: edge insertion with dangling-reference and cycle rejection, plus the depends-on reads (single and batched) the queue builds on.
Plan rollups: a plan's status is derived from its step tasks, never stored.
The task read surface: the ready queue, the blocked list with each task's blockers, and the plain per-project/per-plan listings. "Ready" means open with no blocker still open or in_progress -- one rule, repeated as a NOT EXISTS in each query here and mirrored by ClaimTask's branch (a) and ActivePlans.
Row scanning for the tasks table. scanTasksWithDeps is the batched path every multi-row task query drains through, so listing N tasks costs 1 query for the rows plus a handful for their edges -- not N+1.
Index ¶
- Constants
- Variables
- func ActiveAmbientByCWD(ctx context.Context, db *sql.DB, cwd string) ([]core.Session, error)
- func ActiveAmbientProjects(ctx context.Context, db *sql.DB, within time.Duration) ([]string, error)
- func ActiveAmbientSessionsForProject(ctx context.Context, db *sql.DB, project string, within time.Duration) ([]core.Session, error)
- func ActiveMemories(ctx context.Context, db *sql.DB, project string) ([]core.Memory, error)
- func ActiveMemoriesForProjects(ctx context.Context, db *sql.DB, slugs []string) ([]core.Memory, error)
- func ActiveMemoriesForScope(ctx context.Context, db *sql.DB, project string, extra []string) ([]core.Memory, error)
- func ActiveSessionIDs(ctx context.Context, db *sql.DB, ids []string) (map[string]bool, error)
- func ActiveSessionsByExternalIdentity(ctx context.Context, db *sql.DB, externalClient, externalSessionID string) ([]core.Session, error)
- func AddFamilyMembers(ctx context.Context, db *sql.DB, family string, slugs []string) ([]string, error)
- func AddRepoMapping(ctx context.Context, db *sql.DB, repoPath, slug string) error
- func AllActiveMemories(ctx context.Context, db *sql.DB) ([]core.Memory, error)
- func AllMemoriesIncludingInvalid(ctx context.Context, db *sql.DB) ([]core.Memory, error)
- func AllProposalKeys(ctx context.Context, db *sql.DB) (map[string]struct{}, error)
- func AllReadyTasks(ctx context.Context, db *sql.DB) ([]core.Task, error)
- func AllRetrievalStats(ctx context.Context, db *sql.DB) (map[string]RetrievalStat, error)
- func AllTasksByStatus(ctx context.Context, db *sql.DB, status core.TaskStatus) ([]core.Task, error)
- func AmbientSessionByExternalIdentity(ctx context.Context, db *sql.DB, externalClient, externalSessionID string) (core.Session, bool, error)
- func BriefingConfig(ctx context.Context, db *sql.DB, base config.Briefing) (cfg config.Briefing, overridden bool, err error)
- func BriefingExposureSince(ctx context.Context, db *sql.DB, since time.Time) (map[string]int, error)
- func ClearBriefingConfig(ctx context.Context, db *sql.DB) error
- func CompletedSessionsSince(ctx context.Context, db *sql.DB, since time.Time) ([]core.Session, error)
- func Cosine(a, b []float32) float64
- func CreateProject(ctx context.Context, db *sql.DB, p core.Project) error
- func CreateSession(ctx context.Context, db *sql.DB, s core.Session) error
- func CreateTask(ctx context.Context, db *sql.DB, t core.Task) error
- func CreateTrial(ctx context.Context, db *sql.DB, tr core.Trial) error
- func DecodeVector(b []byte) []float32
- func DeleteSetting(ctx context.Context, db *sql.DB, key string) error
- func DemandItemIDsSince(ctx context.Context, db *sql.DB, since time.Time) (map[string]struct{}, error)
- func DistinctPlanSlugsForProject(ctx context.Context, db *sql.DB, project string) ([]string, error)
- func EmbedderMode(ctx context.Context, db *sql.DB) (string, error)
- func EncodeVector(vec []float32) []byte
- func EnsureProject(ctx context.Context, db *sql.DB, slug, name string) (core.Project, error)
- func ExpireStaleSessions(ctx context.Context, db *sql.DB, cutoff time.Time) ([]core.Session, error)
- func ForceReleaseTask(ctx context.Context, db *sql.DB, id string, now time.Time) (core.Task, error)
- func GetSetting(ctx context.Context, db *sql.DB, key string) (string, bool, error)
- func LatestActiveAmbientSessionForProject(ctx context.Context, db *sql.DB, project string, within time.Duration) (core.Session, bool, error)
- func ListNotes(ctx context.Context, db *sql.DB) ([]core.Note, error)
- func ListProjects(ctx context.Context, db *sql.DB) ([]core.Project, error)
- func ListSessions(ctx context.Context, db *sql.DB, status core.SessionStatus, since time.Time, ...) ([]core.Session, error)
- func ListSessionsForProject(ctx context.Context, db *sql.DB, project string, status core.SessionStatus, ...) ([]core.Session, error)
- func ListTasks(ctx context.Context, db *sql.DB, project string, status core.TaskStatus) ([]core.Task, error)
- func ListTasksForPlan(ctx context.Context, db *sql.DB, project string, status core.TaskStatus, ...) ([]core.Task, error)
- func LooksLikeSessionULID(s string) bool
- func MemoriesByIDs(ctx context.Context, db *sql.DB, ids []string) (map[string]core.Memory, error)
- func MemoriesForSession(ctx context.Context, db *sql.DB, sess core.Session) ([]core.Memory, error)
- func MemoriesSuperseding(ctx context.Context, db *sql.DB, id string) ([]core.Memory, error)
- func MemoryByID(ctx context.Context, db *sql.DB, id string) (core.Memory, bool, error)
- func MemoryByName(ctx context.Context, db *sql.DB, project, name string) (core.Memory, bool, error)
- func MemoryByNameIncludingInvalid(ctx context.Context, db *sql.DB, project, name string) (core.Memory, bool, error)
- func NoteByID(ctx context.Context, db *sql.DB, id string) (core.Note, bool, error)
- func NoteBySlug(ctx context.Context, db *sql.DB, project, slug string) (core.Note, bool, error)
- func NotesByIDs(ctx context.Context, db *sql.DB, ids []string) (map[string]core.Note, error)
- func NotesByTag(ctx context.Context, db *sql.DB, project, tag string) ([]core.Note, error)
- func NotesByTagPrefix(ctx context.Context, db *sql.DB, project, prefix string) ([]core.Note, error)
- func Open(dbPath string) (*sql.DB, error)
- func ProjectBySlug(ctx context.Context, db *sql.DB, slug string) (core.Project, bool, error)
- func ProjectFamilies(ctx context.Context, db *sql.DB) (map[string][]string, error)
- func ProjectMemoriesIncludingInvalid(ctx context.Context, db *sql.DB, project string) ([]core.Memory, error)
- func ProjectsByParent(ctx context.Context, db *sql.DB, parent string) ([]core.Project, error)
- func QueryTrials(ctx context.Context, db *sql.DB, f TrialFilter) ([]core.Trial, error)
- func ReactivateAmbientSession(ctx context.Context, db *sql.DB, ...) (core.Session, bool, error)
- func ReadyTasks(ctx context.Context, db *sql.DB, project string) ([]core.Task, error)
- func ReadyTasksForPlan(ctx context.Context, db *sql.DB, project, plan string) ([]core.Task, error)
- func RebuildRetrievalStats(ctx context.Context, db *sql.DB) error
- func RecentFindings(ctx context.Context, db *sql.DB, project string, limit int) ([]core.Session, error)
- func RecentMishapItemIDs(ctx context.Context, db *sql.DB, project string, window time.Duration) (map[string]time.Time, error)
- func RecordProposalResult(ctx context.Context, db *sql.DB, id string, result map[string]any) error
- func ReleaseClaimsForSession(ctx context.Context, db *sql.DB, sessionID string, now time.Time) (int, error)
- func ReleaseTask(ctx context.Context, db *sql.DB, id, sessionID string, now time.Time) (core.Task, error)
- func RemoveFamilyMembers(ctx context.Context, db *sql.DB, family string, slugs []string) ([]string, error)
- func ReopenProposal(ctx context.Context, db *sql.DB, id string) error
- func RepoProjectMap(ctx context.Context, db *sql.DB) (map[string]string, error)
- func ResolveProjectForCWD(ctx context.Context, db *sql.DB, cwd string) (string, error)
- func ResolveProposal(ctx context.Context, db *sql.DB, id, status string, at time.Time) error
- func RetireProject(ctx context.Context, db *sql.DB, slug string, at time.Time) error
- func SaveProjectFamily(ctx context.Context, db *sql.DB, previousName, name string, members []string) ([]string, error)
- func SchemaVersion(db *sql.DB) (int, error)
- func SearchProjects(ctx context.Context, db *sql.DB, q string, limit int) ([]core.Project, error)
- func SearchProjectsSince(ctx context.Context, db *sql.DB, q string, since time.Time, limit int) ([]core.Project, error)
- func SearchSessions(ctx context.Context, db *sql.DB, q string, limit int) ([]core.Session, error)
- func SearchSessionsSince(ctx context.Context, db *sql.DB, q string, since time.Time, limit int) ([]core.Session, error)
- func SearchTasks(ctx context.Context, db *sql.DB, q string, limit int) ([]core.Task, error)
- func SearchTasksSince(ctx context.Context, db *sql.DB, q string, since time.Time, limit int) ([]core.Task, error)
- func SearchTrials(ctx context.Context, db *sql.DB, q string, limit int) ([]core.Trial, error)
- func SearchTrialsSince(ctx context.Context, db *sql.DB, q string, since time.Time, limit int) ([]core.Trial, error)
- func SessionByID(ctx context.Context, db *sql.DB, id string) (core.Session, bool, error)
- func SessionByName(ctx context.Context, db *sql.DB, name string) (core.Session, bool, error)
- func SetAmbientSessionModel(ctx context.Context, db *sql.DB, ...) error
- func SetAmbientSessionTokens(ctx context.Context, db *sql.DB, externalClient, externalSessionID string, ...) (bool, error)
- func SetBriefingConfig(ctx context.Context, db *sql.DB, b config.Briefing) error
- func SetEmbedderMode(ctx context.Context, db *sql.DB, mode string) error
- func SetProjectFamilies(ctx context.Context, db *sql.DB, families map[string][]string) error
- func SetProjectFavorite(ctx context.Context, db *sql.DB, slug string, favorite bool) error
- func SetProjectParent(ctx context.Context, db *sql.DB, slug, parent string, now time.Time) error
- func SetSessionFavorite(ctx context.Context, db *sql.DB, id string, favorite bool) error
- func SetSessionModel(ctx context.Context, db *sql.DB, id, model string) error
- func SetSetting(ctx context.Context, db *sql.DB, key, value string) error
- func SetTaskFavorite(ctx context.Context, db *sql.DB, id string, favorite bool) error
- func SetTrialFavorite(ctx context.Context, db *sql.DB, id string, favorite bool) error
- func SetUtilityActivation(ctx context.Context, db *sql.DB, a UtilityActivation) error
- func SiblingFindings(ctx context.Context, db *sql.DB, slugs []string, limit int) ([]core.Session, error)
- func SiblingProjects(ctx context.Context, db *sql.DB, project string) ([]string, error)
- func StaleMemories(ctx context.Context, db *sql.DB, cutoff time.Time) ([]core.Memory, error)
- func TableCount(db *sql.DB) (int, error)
- func TaskByID(ctx context.Context, db *sql.DB, id string) (core.Task, error)
- func TasksBlockedBy(ctx context.Context, db *sql.DB, id string) ([]core.Task, error)
- func TasksClaimedBy(ctx context.Context, db *sql.DB, sessionID string) ([]core.Task, error)
- func TouchAmbientSession(ctx context.Context, db *sql.DB, externalClient, externalSessionID string, ...) error
- func TouchSession(ctx context.Context, db *sql.DB, id string, now time.Time) error
- func TrialByID(ctx context.Context, db *sql.DB, id string) (core.Trial, bool, error)
- func UpdateAmbientFindings(ctx context.Context, db *sql.DB, ...) (bool, error)
- func UpdateProposalPayload(ctx context.Context, db *sql.DB, id string, payload map[string]any) error
- func UpdateSession(ctx context.Context, db *sql.DB, s core.Session) error
- func UpdateTask(ctx context.Context, db *sql.DB, id string, patch TaskPatch, actor string, ...) (core.Task, error)
- func UpsertEmbedding(ctx context.Context, db *sql.DB, itemID, kind, model string, vec []float32) error
- func UtilityDemandByProject(ctx context.Context, db *sql.DB, now time.Time, window time.Duration) (map[string]UtilityProjectDemand, error)
- func UtilityScores(ctx context.Context, db *sql.DB) (map[string]float64, error)
- type AgentError
- type BlockedTask
- type ClaimResult
- type CoverageBucket
- type EmbeddingModelStat
- type EmbeddingStats
- type KindReach
- type LabSummary
- type MemoryStat
- type MemoryVector
- type Migration
- type NamedCount
- type NamedScore
- type NavCounts
- type PlanRollup
- type PlanSearchRow
- type ProjectBoardRow
- type ProjectCounts
- type ProjectReach
- type Proposal
- func CreateProposal(ctx context.Context, db *sql.DB, kind string, payload map[string]any) (Proposal, error)
- func PendingProposals(ctx context.Context, db *sql.DB, kind string) ([]Proposal, error)
- func ProposalByID(ctx context.Context, db *sql.DB, id string) (Proposal, bool, error)
- func RecentResolvedProposals(ctx context.Context, db *sql.DB, limit int) ([]Proposal, error)
- type RecallHitQuery
- type RecallMiss
- type RepoMapAdoption
- type RetrievalReport
- type RetrievalStat
- type RetrievalWindow
- type SearchHit
- func CosineSearch(ctx context.Context, db *sql.DB, query []float32, model string, ...) ([]SearchHit, error)
- func CosineSearchSince(ctx context.Context, db *sql.DB, query []float32, model string, ...) ([]SearchHit, error)
- func FTSSearch(ctx context.Context, db *sql.DB, query string, kinds, projects []string, ...) ([]SearchHit, error)
- func FTSSearchAllTerms(ctx context.Context, db *sql.DB, terms []string, kinds, projects []string, ...) ([]SearchHit, error)
- func FTSSearchSince(ctx context.Context, db *sql.DB, query string, kinds, projects []string, ...) ([]SearchHit, error)
- type SessionCoverage
- type SnippetHit
- type SurfaceFunnel
- type TaskPatch
- type TrendBucket
- type TrialFilter
- type UsageSummary
- type UtilityActivation
- type UtilityComponents
- type UtilityProjectDemand
- type UtilityProjectState
Constants ¶
const ( SnippetStartMark = "\x01" SnippetEndMark = "\x02" )
Snippet marks wrap the matched terms inside a SnippetHit.Snippet. They are control characters rather than "<mark>" because a snippet is raw item text: handing HTML to the caller would make every consumer responsible for telling our markup from the item's own. A control char cannot survive HTML escaping as markup, so a consumer escapes first and substitutes second (see the console's highlightSnippet), and a sentinel a writer embedded in their own body can only ever produce a stray inert <mark>, never an injection.
const ( ProposalPending = "pending" ProposalApplied = "applied" ProposalDismissed = "dismissed" )
Proposal statuses.
const ( ProposalMerge = "merge" ProposalArchive = "archive" ProposalDigest = "digest" ProposalConsolidate = "consolidate" ProposalReproject = "reproject" // move one memory to another project ProposalSplit = "split" // set up child/shared projects + family for a project split ProposalAbandonPlan = "abandon_plan" // retag a never-approved captured plan plan-status:abandoned ProposalMemoryWanted = "memory_wanted" // agents repeatedly searched for knowledge that does not exist ProposalToolError = "tool_error" // agents keep hitting the same tool-call or hook-stage error ProposalRekind = "rekind" // change one memory's kind classification in place ProposalShipPlan = "ship_plan" // retag an implemented-but-unapproved captured plan plan-status:shipped )
Proposal kinds (mirrors the gardener_proposals.kind CHECK constraint).
const ( EmbedderModeAuto = "auto" EmbedderModeOff = "off" )
Embedder override modes. Auto defers to the LLM config (embeddings run when a provider is configured); Off disables them regardless of config.
const ( UtilityReadyMinAgeDays = 14 // first demand event at least this old UtilityReadyMinEvents = 20 // session-deduped demand events in the window UtilityReadyMinMemories = 10 // distinct memories that demand touched UtilityReadyWindow = 30 * 24 * time.Hour // trailing window for the two counts above )
Utility-activation readiness thresholds. A project's briefing switches to utility-blended ordering (in "auto" mode) only once its demand history is deep enough to rank on: old enough that decay has meaning, busy enough that the scores are not one session's noise, and broad enough that ordering by them changes more than a couple of lines. All three must hold. Exported so the gardener (which latches) and the console (which shows progress toward them) agree by construction.
const DefaultFunnelFollow = 24 * time.Hour
DefaultFunnelFollow is how long after an injection a query-gated pull of the same item still counts as that injection's funnel conversion. A session-scale horizon: reads driven by an injection happen within the same working day, while a pull weeks later says nothing about the injection that preceded it.
const SettingBriefingConfig = "briefing_config"
SettingBriefingConfig is the settings key holding the console-saved briefing override: a JSON-encoded config.Briefing. When present it layers over the file/env briefing config (see BriefingConfig), so the owner can tune the SessionStart injection from the console without editing seamless.yaml or restarting the daemon.
const SettingEmbedderMode = "embedder_mode"
SettingEmbedderMode is the settings key holding the owner's embedder override. The only stored value is EmbedderModeOff; EmbedderModeAuto clears the row, so "auto" and "no override" are the same state. The daemon reads it once at serve start (main.go resolves the embedder before anything holds it), so a console change applies from the next restart.
const SettingProjectFamilies = "project_families"
SettingProjectFamilies is the settings key holding project families: a JSON object {family-name: [slug, ...]}. Sibling briefings surface recent findings from a project's family members.
const SettingRepoProjectMap = "repo_project_map"
SettingRepoProjectMap is the settings key holding the cwd->project-slug map: a JSON object {absolute-path: slug}. The hooks and session_start resolve an agent's working directory to a project slug through it.
const SettingUtilityActivation = "utility_activation"
SettingUtilityActivation is the settings key holding the per-project utility-ranking activation state: which projects' briefings order their memory index by the utility blend. The gardener latches projects in as their demand data matures (see gardener.evaluateUtilityActivation); the owner can force a project on or off from the console. The bounded recall/prompt boosts are always on and are not gated here -- this state guards only the consequential surface, briefing index re-ordering.
Variables ¶
var ErrFamilyExists = errors.New("store: project family already exists")
ErrFamilyExists is returned when a family create or rename would overwrite a different existing family.
var ErrFamilyNoMembers = errors.New("store: project family has no members")
ErrFamilyNoMembers is returned when a family replacement contains no usable project slugs. Empty families are not persisted.
var ErrFamilyNotFound = errors.New("store: project family not found")
ErrFamilyNotFound is returned by RemoveFamilyMembers when the named family does not exist.
var ErrSessionIdentityExists = errors.New("store: ambient session identity already exists")
ErrSessionIdentityExists is returned when an ambient row already owns the same full external session id and client discriminator. It is distinct from a display-name collision so callers can safely converge a concurrent create on the existing authoritative identity.
var ErrSessionNameExists = errors.New("store: session name already exists")
ErrSessionNameExists is returned by CreateSession when the name is already taken (sessions.name is UNIQUE). It mirrors ErrSlugExists so a caller racing to create a named session can tell "someone beat me to this name" (resume it) apart from a real database failure, instead of having to match on error text.
It reads any uniqueness failure on the insert as a name collision. The table's other unique constraint is the id primary key, and callers mint a fresh ULID per call, so a PK collision cannot happen without an id-generation bug -- the same assumption ErrSlugExists makes.
var ErrSlugExists = errors.New("store: project slug already exists")
ErrSlugExists is returned by CreateProject when the slug is already taken.
var ErrTaskBlocked = errors.New("task blocked")
ErrTaskBlocked is returned when a claim targets an open task with an unfinished dependency; the message names the blockers. No one holds the task -- it becomes claimable the moment its blockers close.
var ErrTaskClaimConflict = errors.New("task already claimed")
ErrTaskClaimConflict is returned when a claim loses the compare-and-set race to another live lease (or, from Release/Update paths, when the caller is not the holder). A refused claim whose cause is not a holder reports ErrTaskBlocked or ErrTaskClosed instead -- those need a different fix (finish the blocker; nothing, respectively), and reporting them as "already claimed" used to send agents chasing a holder that did not exist ("held by \"\"").
var ErrTaskClosed = errors.New("task closed")
ErrTaskClosed is returned when a claim targets a done/dropped task. No one holds it either; there is simply nothing left to claim.
var ErrTaskCycle = errors.New("dependency would create a cycle")
ErrTaskCycle is returned when adding a dependency edge would create a cycle.
var ErrTaskNotFound = errors.New("task not found")
ErrTaskNotFound is returned when a task id does not exist (e.g. a dangling depends_on reference).
var InjectionSurfaces = []string{"session-start", "subagent-start"}
InjectionSurfaces are the briefing injection surfaces the read-after-inject funnel segments by, in display order. Each value is the hook name the hooks layer stamps into a retrieval.injected payload: "session-start" is the main SessionStart briefing, "subagent-start" the constraints-only child briefing.
var ProposalKinds = []string{ ProposalMerge, ProposalArchive, ProposalDigest, ProposalConsolidate, ProposalReproject, ProposalSplit, ProposalAbandonPlan, ProposalMemoryWanted, ProposalToolError, ProposalRekind, ProposalShipPlan, }
ProposalKinds lists every valid proposal kind. This is the canonical set: derive the MCP schema's enum from it rather than transcribing, so a new kind cannot reach the store while staying invisible at the boundary.
var RetrievalWindowKeys = []string{"24h", "7d", "30d", "all"}
RetrievalWindowKeys are the selectable trailing windows for the retrieval-health views, in display order. "24h" is the default; "all" spans every recorded event.
Functions ¶
func ActiveAmbientByCWD ¶
ActiveAmbientByCWD returns the active ambient (cc/* or cx/*) sessions whose cwd matches, most recent first. session_start consults it to link a freshly created explicit session to the Claude session that owns the cwd (via its claude_session_id), so a graceful SessionEnd can close both at once instead of leaving the explicit one to the idle reaper. An empty cwd matches nothing (no basis to link).
func ActiveAmbientProjects ¶
ActiveAmbientProjects returns the distinct project slugs that have at least one active ambient (cc/* or cx/*) session updated within the window, ordered by each project's most recent activity. The MCP fallback consults it to tell a safe single-project inference (len 1) from the ambiguous concurrent-agent case (len > 1, agents in different repos) where guessing would bleed a write into the wrong project. A non-positive within disables the recency filter. The global scope is reported as the empty string, distinct from any named project.
func ActiveAmbientSessionsForProject ¶
func ActiveAmbientSessionsForProject(ctx context.Context, db *sql.DB, project string, within time.Duration) ([]core.Session, error)
ActiveAmbientSessionsForProject returns every active ambient (cc/* or cx/*) session in the given project updated within the window, most recent first. resolveSession uses it to refuse targeting a session by inference when more than one same- project ambient could be the one meant -- two agents in the same repo -- so a session_update/end without an explicit id can't complete a sibling's session. A non-positive within disables the recency filter.
func ActiveMemories ¶
ActiveMemories returns the active (not superseded/archived) memories visible to a project: its own plus all global memories (project == ""). Rows carry index metadata only (no body); newest-updated first. Passing project == "" returns only global memories.
func ActiveMemoriesForProjects ¶
func ActiveMemoriesForProjects(ctx context.Context, db *sql.DB, slugs []string) ([]core.Memory, error)
ActiveMemoriesForProjects returns the active memories belonging to exactly the given project slugs -- no own-project or global union, unlike ActiveMemoriesForScope -- newest-updated first. It backs the briefing's opt-in sibling-memories cross-over, where the caller already holds the own scope and wants only the family members' rows. Blank/duplicate slugs are ignored; no slugs yields nil.
func ActiveMemoriesForScope ¶
func ActiveMemoriesForScope(ctx context.Context, db *sql.DB, project string, extra []string) ([]core.Memory, error)
ActiveMemoriesForScope returns the active memories visible to a project widened by extra project slugs (e.g. a shared parent whose memories a split injects into each child). It is ActiveMemories plus the union of extra: rows where invalid_at IS NULL and project is the project, global (”), or any extra slug -- deduped, newest-updated first. Blank/duplicate extras are ignored; with no extras it is exactly ActiveMemories.
func ActiveSessionIDs ¶
ActiveSessionIDs reports which of the given session ids belong to a currently active session. It backs the MCP server's connection-binding sweep: a binding whose session is no longer active -- ended by the session_end tool, the SessionEnd hook, or the idle reaper -- is evicted. Ids absent from the result (unknown or non-active) are simply not set.
func ActiveSessionsByExternalIdentity ¶ added in v0.3.7
func ActiveSessionsByExternalIdentity( ctx context.Context, db *sql.DB, externalClient, externalSessionID string, ) ([]core.Session, error)
ActiveSessionsByExternalIdentity returns every active session -- the ambient cc/* or cx/* plus any explicit session_start that linked to it -- stamped with the same full external id and client discriminator, ambient first. A graceful SessionEnd uses it to close only the issuing client's session family.
func AddFamilyMembers ¶
func AddFamilyMembers(ctx context.Context, db *sql.DB, family string, slugs []string) ([]string, error)
AddFamilyMembers adds slugs to the named family, creating the family when it is new, and persists the result. Existing members keep their order and duplicates are ignored, so callers may re-add the same slugs idempotently. Returns the family's resulting members.
The read-decode-mutate-write runs inside one transaction (the AddRepoMapping recipe): the pool is capped at a single connection (see Open), so the whole mutation is serialized against concurrent mutators and two callers growing different families at once can no longer clobber each other's family. If the pool ever grows past one connection this needs BEGIN IMMEDIATE, since two deferred transactions could still interleave read-then-write.
func AddRepoMapping ¶
AddRepoMapping records repoPath -> slug in the repo_project_map and persists it, so the mapping survives restarts and is shared by every agent. It is a no-op when that exact entry already exists, so callers may invoke it on every resolve without churning the setting.
func AllActiveMemories ¶
AllActiveMemories returns every active memory across all projects (index rows, no body), newest-updated first. It backs the gardener's reference scan, which reads each file's body to find [[name]] links.
func AllMemoriesIncludingInvalid ¶
AllMemoriesIncludingInvalid returns every memory index row -- active and invalid (superseded or archived) -- newest-updated first. It backs the console Memories browser, which renders supersession chains and archived items.
func AllProposalKeys ¶
AllProposalKeys returns the set of payload "key" values across proposals of EVERY status. The gardener consults it before proposing, so a suggestion the owner already applied or dismissed is never raised again.
func AllReadyTasks ¶
AllReadyTasks returns the ready tasks across every project (see ReadyTasks for the readiness rule), oldest-created first. It backs the console Tasks page.
func AllRetrievalStats ¶
AllRetrievalStats loads the whole retrieval_stats table into a map keyed by item id, so a caller (the console) can annotate many memories with one query instead of N GetRetrievalStat calls.
func AllTasksByStatus ¶
AllTasksByStatus returns every task with the given status across all projects, newest-updated first.
func AmbientSessionByExternalIdentity ¶ added in v0.3.7
func AmbientSessionByExternalIdentity( ctx context.Context, db *sql.DB, externalClient, externalSessionID string, ) (core.Session, bool, error)
AmbientSessionByExternalIdentity resolves the authoritative ambient row for a client-issued session id. Display names are deliberately absent from the predicate so legacy and digest-suffixed names behave identically.
func BriefingConfig ¶
func BriefingConfig(ctx context.Context, db *sql.DB, base config.Briefing) (cfg config.Briefing, overridden bool, err error)
BriefingConfig returns the effective briefing config: base (the file/env values) with the console-saved override row, when present, decoded over it. overridden reports whether such a row exists. Absent fields in a stored override keep their base value, so a row written by an older console version stays forward-compatible.
func BriefingExposureSince ¶ added in v0.4.1
func BriefingExposureSince(ctx context.Context, db *sql.DB, since time.Time) (map[string]int, error)
BriefingExposureSince counts, per item id, session-start briefing injections since the cutoff -- the exposure denominator for dead-weight detection.
func ClearBriefingConfig ¶
ClearBriefingConfig removes the console briefing override, reverting the effective briefing config to the file/env base.
func CompletedSessionsSince ¶
func CompletedSessionsSince(ctx context.Context, db *sql.DB, since time.Time) ([]core.Session, error)
CompletedSessionsSince returns completed sessions with non-empty findings updated on or after since, across all projects, newest first. It feeds the gardener's monthly digest pass.
func Cosine ¶
Cosine returns the cosine similarity of two equal-length vectors, in [-1, 1]. Mismatched lengths or a zero-magnitude vector yield 0.
func CreateProject ¶
CreateProject inserts a project. It returns ErrSlugExists if the slug is taken.
func CreateSession ¶
CreateSession inserts a session. The caller mints the ULID id; a duplicate name returns ErrSessionNameExists, while a duplicate authoritative ambient identity returns ErrSessionIdentityExists.
func CreateTask ¶
CreateTask inserts a task and its dependency edges in one transaction. Every depends_on must reference an existing task (dangling references are rejected) and must not create a cycle. The caller mints the ULID id and timestamps.
func CreateTrial ¶
CreateTrial inserts a trial row. The caller mints the ULID id and timestamp.
func DecodeVector ¶
DecodeVector reverses EncodeVector. A byte slice whose length is not a multiple of four yields nil (corrupt row; skip it).
func DeleteSetting ¶
DeleteSetting removes a settings key. Deleting an absent key is a no-op.
func DemandItemIDsSince ¶ added in v0.4.1
func DemandItemIDsSince(ctx context.Context, db *sql.DB, since time.Time) (map[string]struct{}, error)
DemandItemIDsSince returns every item id that saw query-gated demand -- a recall hit, a prompt-recall match, or an explicit read -- since the cutoff. Passive briefing injections do not count, mirroring the utility score.
func DistinctPlanSlugsForProject ¶
DistinctPlanSlugsForProject returns the full set of plan slugs a project has ever had, including completed plans, sorted. Unlike ActivePlans (which drops a plan once every step is closed) this keeps completed plans, so it backs a history/lineage view. It rejects an empty project for the same reason as ListSessionsForProject.
Plan slugs are unique only PER PROJECT: two projects may each have a plan "refactor". A cross-project consumer must key by (project, slug), never slug alone.
func EmbedderMode ¶ added in v0.4.1
EmbedderMode returns the stored embedder override: EmbedderModeOff when the owner switched embeddings off, EmbedderModeAuto otherwise. An unset row or an unrecognized stored value both read as auto -- the override can only ever narrow behavior, never invent a new state.
func EncodeVector ¶
EncodeVector serializes a float32 vector to a little-endian byte slice, the on-disk form of the embeddings.vec BLOB column.
func EnsureProject ¶
EnsureProject returns the project registered under slug, creating a minimal row when none exists yet. It is the idempotent upsert used by the importer and by session resolution so that every project referenced by memories, notes, or sessions also has a first-class projects-table row -- the row project_list reads. A blank slug is the global scope: it is never registered and yields the zero Project with no error. When name is blank the slug is used as the name.
func ExpireStaleSessions ¶
ExpireStaleSessions reaps active sessions whose last activity predates cutoff, flipping each to SessionExpired. It leaves updated_at untouched so the row still records when the session was last alive (the console orders and dates by it), which also keeps the flip idempotent: a reaped session no longer matches the active filter. It returns only the sessions it actually flipped (id + project) so the caller can release their task claims and record telemetry -- a session that was heartbeated or gracefully completed between the candidate read and the flip is skipped, never falsely reaped. Passing updated_at unchanged means a later graceful session_end/resume can still upgrade the row (its guards key off completed/active, not expired).
func ForceReleaseTask ¶
ForceReleaseTask unconditionally releases a claimed (in_progress) task, reopening it (status back to open, claim and lease cleared) regardless of who holds it or whether the lease is still live. It backs the owner override (the console "release lock" button and `seam task release --force`); it is not reachable from the agent MCP surface. Unlike ReleaseTask it does not check the holder. Returns ErrTaskClaimConflict when the task is not in_progress (nothing to release) and ErrTaskNotFound when the id is unknown.
func GetSetting ¶
GetSetting returns the value for a settings key. found is false when unset.
func LatestActiveAmbientSessionForProject ¶
func LatestActiveAmbientSessionForProject(ctx context.Context, db *sql.DB, project string, within time.Duration) (core.Session, bool, error)
LatestActiveAmbientSessionForProject returns the most recently updated active ambient (cc/* or cx/*) session in the given project, updated within the window, or found=false when none. It backs the MCP write-scope fallback: an agent that writes without calling session_start inherits its project's ambient session's provenance. A non-positive within disables the recency filter. Scoping to a single project is what prevents cross-agent bleed -- see ActiveAmbientProjects.
func ListNotes ¶
ListNotes returns every note, newest-updated first. Rows carry index metadata only (no body, which lives in the file). It backs the console Notes browser.
func ListProjects ¶
ListProjects returns every project, ordered by slug.
func ListSessions ¶
func ListSessions(ctx context.Context, db *sql.DB, status core.SessionStatus, since time.Time, limit int) ([]core.Session, error)
ListSessions returns sessions newest-updated first, optionally filtered by status and to those updated since a cutoff (a zero `since` means all time), capped at limit (default 100). It backs the console Sessions list.
func ListSessionsForProject ¶
func ListSessionsForProject(ctx context.Context, db *sql.DB, project string, status core.SessionStatus, since time.Time, limit int) ([]core.Session, error)
ListSessionsForProject returns a project's sessions newest-updated first, optionally filtered by status and to those updated since a cutoff (a zero `since` means all time), capped at limit (default 100).
It is STRICT per-slug (project_slug = ?): unlike RecentFindings it does NOT union the global scope, so a project sees only its own sessions. It rejects an empty project: an empty project_slug marks real global sessions, so "" is ambiguous between "all sessions" and "global-only" -- use ListSessions for all sessions (there is no global-only variant; pass an explicit slug for one project).
func ListTasks ¶
func ListTasks(ctx context.Context, db *sql.DB, project string, status core.TaskStatus) ([]core.Task, error)
ListTasks returns a project's tasks, optionally filtered by status, newest first. An empty status returns every status.
func ListTasksForPlan ¶
func ListTasksForPlan(ctx context.Context, db *sql.DB, project string, status core.TaskStatus, plan string) ([]core.Task, error)
ListTasksForPlan returns a plan's step tasks in a project, optionally filtered by status, newest first. Unlike ListTasks it includes (only) plan-scoped tasks.
func LooksLikeSessionULID ¶ added in v0.3.7
LooksLikeSessionULID reports whether s has the shape of a bare session ULID: no '/' (session names always contain one), exactly 26 chars, and a valid ULID encoding. It disambiguates the session-name vs session-id relation guards, and lets provenance consumers pick the right lookup for a source_session stamp (name for ambient stamps, ULID for bound stamps) without a doomed first query.
func MemoriesByIDs ¶
MemoriesByIDs returns the memories for the given ids in no particular order. Missing ids are simply absent from the result; callers key by ID.
func MemoriesForSession ¶
MemoriesForSession returns the memories a session produced -- the index rows whose source_session matches the session -- newest-updated first. Memory.SourceSession holds the session NAME for ambient stamps (cc/ab12cd34) but the session ULID for bound stamps (see the source-session-stamps memory), so the query matches either spelling and callers pass the whole session.
func MemoriesSuperseding ¶
MemoriesSuperseding returns the memories that the given memory replaced: the index rows whose superseded_by points at id. It is the inverse of a memory's own SupersededBy edge (which the store does not otherwise materialize), newest-updated first, and empty when the memory superseded nothing. It backs the console memory peek's reverse "supersedes" section.
func MemoryByID ¶
MemoryByID returns the memory with the given id. found is false when absent.
func MemoryByName ¶
MemoryByName returns the active memory with an exact (project, name), most recently updated first. found is false when none matches. It does not fall back to the global scope; a caller that wants that resolves it explicitly.
func MemoryByNameIncludingInvalid ¶
func MemoryByNameIncludingInvalid(ctx context.Context, db *sql.DB, project, name string) (core.Memory, bool, error)
MemoryByNameIncludingInvalid returns the most recently updated memory with an exact (project, name), whether or not it is still valid. It backs memory_read's warning path: a superseded memory (which MemoryByName excludes) is still readable, prefixed with a warning pointing at its replacement.
func NoteBySlug ¶
NoteBySlug returns the note with an exact (project, slug). found is false when none matches.
func NotesByIDs ¶
NotesByIDs returns the notes for the given ids keyed by ID; missing ids are simply absent.
func NotesByTag ¶
NotesByTag returns the notes carrying an exact tag, newest-updated first, optionally scoped to a project ("" = every project). Tags are stored as a JSON array, so the match runs through json_each rather than LIKE.
func NotesByTagPrefix ¶
NotesByTagPrefix returns the notes carrying at least one tag that begins with prefix (e.g. "plan:" for every plan composition), newest-updated first, optionally scoped to a project ("" = every project). It backs surfaces that enumerate a tag family without knowing the concrete values up front. prefix is treated literally: its LIKE metacharacters are escaped.
func Open ¶
Open opens (creating if needed) the SQLite database at dbPath, applies PRAGMAs via the DSN so every pooled connection inherits them, runs migrations, and returns the handle. The caller is responsible for closing the *sql.DB.
func ProjectBySlug ¶
ProjectBySlug returns the project with the given slug. found is false when absent.
func ProjectFamilies ¶
ProjectFamilies decodes the project_families setting into a map. An unset or blank value yields an empty map (not an error).
func ProjectMemoriesIncludingInvalid ¶
func ProjectMemoriesIncludingInvalid(ctx context.Context, db *sql.DB, project string) ([]core.Memory, error)
ProjectMemoriesIncludingInvalid returns a project's memory index rows -- both active and invalid (superseded/archived) -- newest-updated first, for a lineage view that must render supersession chains.
It is STRICT per-slug (project = ?): it does NOT union the global (empty) scope. This is the deliberate opposite of ActiveMemories, which DOES union global rows into a project's view. Mixing the two conventions is exactly how a project count comes out larger than expected, so callers must pick the strict variant on purpose. It rejects an empty project for the same reason as ListSessionsForProject.
func ProjectsByParent ¶
ProjectsByParent returns the projects whose parent_slug equals parent, ordered by slug (using idx_projects_parent). It backs walking a project's children in the parent/child topology a split builds.
func QueryTrials ¶
QueryTrials returns trials matching the filter, newest first. Lab/outcome/ project are filtered in SQL; MetricsEquals is applied in Go (labs are small, and this matches the DB-first metrics design without a JSON1 dependency).
func ReactivateAmbientSession ¶ added in v0.3.7
func ReactivateAmbientSession( ctx context.Context, db *sql.DB, externalClient, externalSessionID, project string, now time.Time, ) (core.Session, bool, error)
ReactivateAmbientSession resumes an ambient session by its authoritative external identity. The targeted UPDATE flips status back to active, re-scopes only when project is non-empty, and bumps recency without touching findings or metadata. Returning the row gives callers its actual display name, including a legacy pre-digest name preserved by migration 010.
func ReadyTasks ¶
ReadyTasks returns the actionable tasks for a project: open tasks with no blocking dependency still open or in_progress (done/dropped deps do not block). in_progress tasks are excluded (already claimed) but still block their dependents. Ordered oldest-created first, ties broken by id (ULID-monotonic), so the queue is stable and agent-predictable.
func ReadyTasksForPlan ¶
ReadyTasksForPlan returns the ready (claimable) step tasks of one plan in a project: open, unclaimed, with no unfinished blocker. Same readiness rule as ReadyTasks, scoped to plan_slug instead of excluding plan tasks.
func RebuildRetrievalStats ¶
RebuildRetrievalStats recomputes the entire retrieval_stats table from the event log. It is the canonical maintenance path (the gardener calls it at the top of each pass, and it is safe to call after an import): retrieval.injected events contribute inject_count + last_injected_at (per item id, whether the id is in the item_id column or the payload's item_ids array), and memory.read events contribute read_count + last_read_at. Events are read oldest-first so the last-seen timestamp wins. The same walk accumulates the utility score: each query-gated signal adds its class weight decayed by the event's age.
func RecentFindings ¶
func RecentFindings(ctx context.Context, db *sql.DB, project string, limit int) ([]core.Session, error)
RecentFindings returns ended sessions with meaningful findings visible to a project (its own plus global sessions), most recent first. It backs the briefing's "recent findings" section, so it excludes both blank findings and the core.FindingNoSummary sentinel (a session that ended with nothing to harvest) -- a content-free line is not worth an agent's context.
"Ended" is completed OR reaper-expired-while-ambient. A Codex ambient session has no SessionEnd event (design D5): the Stop hook harvests findings onto the live row and the idle reaper flips it to 'expired', so requiring 'completed' would hide every Codex session's findings. Restricting the expired arm to ambient rows keeps this exact: a CC ambient session only ever gains findings at SessionEnd (which also sets 'completed'), so no CC row is active-with-findings to be reaped, and a crashed explicit session (ambient = 0) still does not surface -- the set added here is precisely Codex's reaper-ended sessions.
func RecentMishapItemIDs ¶ added in v0.4.4
func RecentMishapItemIDs(ctx context.Context, db *sql.DB, project string, window time.Duration) (map[string]time.Time, error)
RecentMishapItemIDs returns the ids of memories referenced by a project's agent.mishap events recorded within window of now, each mapped to the timestamp of its most recent referencing mishap. It feeds the briefing's mishap promotion -- an ordering signal like favorites -- and deliberately NOT the utility score: RebuildRetrievalStats keeps its query-gated classes (read/recall/prompt) per the closed-loop-utility-signal-contract. Mishaps whose payload names no memory contribute nothing; no references yields an empty map, not an error.
func RecordProposalResult ¶ added in v0.4.6
RecordProposalResult stores what applying a proposal produced. It is called after ResolveProposal, so it deliberately does not filter on status -- the proposal is already applied by then. A missing row is not an error: the result is a convenience for undo, never a correctness requirement of apply.
func ReleaseClaimsForSession ¶
func ReleaseClaimsForSession(ctx context.Context, db *sql.DB, sessionID string, now time.Time) (int, error)
ReleaseClaimsForSession reopens every in_progress task currently claimed by sessionID (called on session_end so a departing agent's work returns to the queue). It returns the number of tasks released.
func ReleaseTask ¶
func ReleaseTask(ctx context.Context, db *sql.DB, id, sessionID string, now time.Time) (core.Task, error)
ReleaseTask releases a task held by sessionID, reopening it (status back to open, claim and lease cleared) so another agent can pick it up. Only the current holder may release; otherwise it returns ErrTaskClaimConflict (or ErrTaskNotFound when the id is unknown).
func RemoveFamilyMembers ¶
func RemoveFamilyMembers(ctx context.Context, db *sql.DB, family string, slugs []string) ([]string, error)
RemoveFamilyMembers removes slugs from the named family and persists the result. Passing no slugs removes the whole family; a family left with no members after the removal is dropped as well. Returns the family's resulting members, empty when the family was removed. It errors with ErrFamilyNotFound when the named family does not exist.
Like AddFamilyMembers the read-decode-mutate-write runs inside one transaction, serialized by the single-connection pool (see Open), so a concurrent removal from another family survives instead of being clobbered by this write-back. If the pool ever grows past one connection this needs BEGIN IMMEDIATE.
func ReopenProposal ¶ added in v0.4.6
ReopenProposal returns a resolved proposal to the pending queue, clearing the resolution stamp and the apply result. It is the store half of undo: the caller has already inverted the effect (or is undoing a dismissal, which had none). It errors if the proposal is missing or still pending, so a double undo cannot resurrect an already-restored proposal.
The payload key is deliberately left in place: AllProposalKeys reads every status, so dedup behaves the same whether the proposal is pending, resolved, or reopened.
func RepoProjectMap ¶
RepoProjectMap decodes the repo_project_map setting into a map. An unset or blank value yields an empty map (not an error), so an unconfigured install simply resolves every cwd to the global scope.
func ResolveProjectForCWD ¶
ResolveProjectForCWD maps an absolute working directory to a project slug via the repo_project_map, using a longest-prefix match. It is read-only and failure-soft: an unresolvable cwd (or an unconfigured map) returns "" (the global scope), never an error from the matching itself. Use it on read paths (briefing, prompt recall); use RegisterProjectForCWD on session-start paths that should grow the map.
func ResolveProposal ¶
ResolveProposal marks a proposal applied or dismissed and stamps resolved_at. It errors if the proposal is missing or already resolved (not pending).
func RetireProject ¶
RetireProject stamps a project's retired_at (marking it emptied by a split) and bumps updated_at. Passing the zero time clears it (un-retire). It is idempotent and leaves the project's rows and files intact -- retirement is a flag, never a delete. An unknown slug affects no rows and returns nil.
func SaveProjectFamily ¶ added in v0.3.9
func SaveProjectFamily(ctx context.Context, db *sql.DB, previousName, name string, members []string) ([]string, error)
SaveProjectFamily creates, replaces, or renames one family atomically. previousName is empty for a create; otherwise that family must exist. A create or rename never merges into an existing family because silently combining their context boundaries would be surprising. The submitted member set replaces the old set in full and must contain at least one slug.
func SchemaVersion ¶
SchemaVersion returns the highest applied migration version.
func SearchProjects ¶
SearchProjects returns projects whose slug or display name contains q, alphabetically by slug (projects are few and stable, so a name order reads better than a recency one).
func SearchProjectsSince ¶ added in v0.3.9
func SearchProjectsSince(ctx context.Context, db *sql.DB, q string, since time.Time, limit int) ([]core.Project, error)
SearchProjectsSince is SearchProjects restricted to projects updated at or after since. A zero since keeps the all-time behavior.
func SearchSessions ¶
SearchSessions returns sessions whose name contains q, newest-updated first. An exact id also matches.
func SearchSessionsSince ¶ added in v0.3.9
func SearchSessionsSince(ctx context.Context, db *sql.DB, q string, since time.Time, limit int) ([]core.Session, error)
SearchSessionsSince is SearchSessions restricted to sessions updated at or after since. A zero since keeps the all-time behavior.
func SearchTasks ¶
SearchTasks returns tasks whose title contains q, newest-updated first. An exact id also matches, so pasting a task id from a log finds its task.
func SearchTasksSince ¶ added in v0.3.9
func SearchTasksSince(ctx context.Context, db *sql.DB, q string, since time.Time, limit int) ([]core.Task, error)
SearchTasksSince is SearchTasks restricted to tasks updated at or after since. A zero since keeps the all-time behavior.
func SearchTrials ¶ added in v0.3.9
SearchTrials returns trials whose title or lab contains q, newest first. An exact id also matches, so pasting a trial id from a log finds its trial.
func SearchTrialsSince ¶ added in v0.3.9
func SearchTrialsSince(ctx context.Context, db *sql.DB, q string, since time.Time, limit int) ([]core.Trial, error)
SearchTrialsSince is SearchTrials restricted to trials created at or after since. A zero since keeps the all-time behavior.
func SessionByID ¶
SessionByID returns the session with the given id. found is false when absent.
func SessionByName ¶
SessionByName returns the session with the given (unique) name. found is false when absent.
func SetAmbientSessionModel ¶ added in v0.3.7
func SetAmbientSessionModel(ctx context.Context, db *sql.DB, externalClient, externalSessionID, model string) error
SetAmbientSessionModel records the model by authoritative external identity.
func SetAmbientSessionTokens ¶ added in v0.3.9
func SetAmbientSessionTokens( ctx context.Context, db *sql.DB, externalClient, externalSessionID string, tokens core.TokenUsage, now time.Time, ) (bool, error)
SetAmbientSessionTokens overwrites the harvested model token totals on an active ambient session, keyed by authoritative external identity. Like the other targeted ambient writers it is a single-purpose write (only the five token columns + updated_at, never a read-modify-write of the whole row), so a concurrent findings/model update cannot clobber it and vice versa.
The totals are absolute cumulative values, so this OVERWRITES rather than accumulates: re-harvesting a resumed session's grown transcript (Claude Code SessionEnd) or re-reading the latest cumulative token_count every turn (Codex Stop) writes the same absolute total again -- idempotent, never double-counted. The active-only + ambient guard means it never revives a completed/expired row and never touches an explicit session. Bumping updated_at doubles as a heartbeat. No-op on an incomplete identity or an empty (all-zero) usage (a turn with no token record leaves any prior harvest intact). Reports whether a row was updated.
func SetBriefingConfig ¶
SetBriefingConfig persists b as the console briefing override. Callers validate first (config.Briefing.Validate); this only encodes and stores.
func SetEmbedderMode ¶ added in v0.4.1
SetEmbedderMode persists the embedder override. Auto deletes the row (no override); Off stores it; anything else is rejected.
func SetProjectFamilies ¶
SetProjectFamilies persists the full families map as the project_families setting. Empty families (no members) are dropped and members are trimmed and deduped so the stored value stays canonical; passing an empty map clears the setting to "{}". It is the single writer the family CLI and mutators funnel through, so the setting never accumulates blanks or duplicates.
It reads nothing, so unlike the read-modify-write mutators below it needs no transaction: last-write-wins is inherent to its "set the whole map" contract.
func SetProjectFavorite ¶ added in v0.4.0
SetProjectFavorite sets or clears a project's favorite flag. Idempotent; an unknown slug affects no rows and returns nil (matching RetireProject).
func SetProjectParent ¶
SetProjectParent sets (or clears, with parent == "") a project's parent slug and bumps updated_at. The parent's active memories are injected into the child's briefing (see retrieve.Briefing). It is idempotent -- re-setting the same parent is a harmless no-op write -- so a split apply is retry-safe. An unknown slug affects no rows and returns nil (the caller ensures the row first).
func SetSessionFavorite ¶ added in v0.4.0
SetSessionFavorite sets or clears a session's favorite flag. Idempotent; an unknown id affects no rows and returns nil.
func SetSessionModel ¶ added in v0.3.7
SetSessionModel records which LLM powers an active session's agent, keyed by session ULID. The value is stored verbatim as the provider names it (e.g. "claude-fable-5"). Targeted single-column write for the same reason as ReactivateAmbientSession: no read-modify-write of the whole row, so it cannot clobber a concurrent findings/metadata update. The active-only guard keeps a completed/expired session's attribution frozen at what it ended with, and the model <> ? guard makes repeated reports of the same value free. No-op on an empty id or model (an agent that never learns its model is not an error), so a matched-zero-rows outcome is legitimate and deliberately unchecked.
func SetSetting ¶
SetSetting upserts a settings key/value.
func SetTaskFavorite ¶ added in v0.4.0
SetTaskFavorite sets or clears a task's favorite flag. Idempotent; an unknown id affects no rows and returns nil.
func SetTrialFavorite ¶ added in v0.4.0
SetTrialFavorite sets or clears a trial's favorite flag. Idempotent; an unknown id affects no rows and returns nil.
func SetUtilityActivation ¶ added in v0.4.1
SetUtilityActivation persists the activation state.
func SiblingFindings ¶
func SiblingFindings(ctx context.Context, db *sql.DB, slugs []string, limit int) ([]core.Session, error)
SiblingFindings returns the most recent ended sessions with meaningful findings across the given sibling projects, newest first, capped at limit. It backs the briefing's "Sibling projects" section, so -- like RecentFindings -- it excludes blank findings and the core.FindingNoSummary sentinel, and treats a reaper-expired ambient session as ended so Codex's SessionEnd-less sessions surface too (see RecentFindings for why the expired arm is ambient-only). An empty slugs slice yields no results.
func SiblingProjects ¶
SiblingProjects returns the other project slugs sharing a family with project, deduped and excluding project itself. A project may appear in more than one family; all such siblings are unioned. Returns nil for the global scope or a project with no family.
func StaleMemories ¶
StaleMemories returns active memories that have seen no activity since cutoff: neither updated, injected, nor read on or after that instant. It LEFT JOINs retrieval_stats (a memory with no stats row counts as never injected/read, so only its updated_at matters), oldest-updated first. It backs the gardener's staleness pass.
func TableCount ¶
TableCount returns the number of user tables (excluding SQLite internals and FTS shadow tables). Useful for the doctor's DB check.
func TaskByID ¶
TaskByID returns a task with its dependency ids populated. found is false when absent.
func TasksBlockedBy ¶
TasksBlockedBy returns the tasks that depend on id -- the inverse of a task's DependsOn edges, i.e. the tasks this one blocks. Unlike openBlockers it applies no status filter (closed dependents are included too), oldest-created first. It backs the console task peek's reverse "blocks" section.
func TasksClaimedBy ¶
TasksClaimedBy returns the tasks a session holds or held -- the rows whose claimed_by matches the session ULID -- oldest-created first (ties by id, the ULID-monotonic order the ready queue uses). Task.ClaimedBy stores the session ULID, never its name.
GUARD (the inverse of MemoriesForSession): if the argument is not ULID-shaped -- it contains '/' or is not 26 chars or fails to parse as a ULID -- it is almost certainly a session name that would silently match nothing; the call is rejected with a message pointing at SessionByName to resolve the id.
func TouchAmbientSession ¶ added in v0.3.7
func TouchAmbientSession(ctx context.Context, db *sql.DB, externalClient, externalSessionID string, now time.Time) error
TouchAmbientSession is TouchSession keyed by the full external session id and client discriminator. The active-only guard prevents late hook traffic from reviving a completed or reaped row.
func TouchSession ¶
TouchSession heartbeats an active session by bumping its updated_at, marking it still-alive for the idle reaper. It only touches active sessions (the WHERE guard), so it never resurrects a completed or expired one, and is a no-op on an empty id. Callers fire it on activity (a bound MCP tool call), best-effort.
func UpdateAmbientFindings ¶ added in v0.3.7
func UpdateAmbientFindings( ctx context.Context, db *sql.DB, externalClient, externalSessionID, findings string, now time.Time, ) (bool, error)
UpdateAmbientFindings upserts provisional findings onto an active ambient session by full external identity. It backs the Codex Stop hook, which harvests the last agent message every turn: Codex has no SessionEnd event, so findings must be in place BEFORE the idle reaper expires the session.
The write is TARGETED -- only findings + updated_at, never a read-modify-write of the whole row -- so a Stop landing between a resume's read and write cannot be clobbered (mirroring the ensureAmbientSession contract), and repeated Stops simply converge findings on the latest turn's message. The active-only + ambient guard means it never resurrects a completed/expired session and never touches an explicit session (whose findings the agent owns via session_update). Bumping updated_at doubles as a heartbeat. No-op on an incomplete identity or empty findings (a turn with nothing to harvest leaves any prior harvest intact). Reports whether a row was updated.
func UpdateProposalPayload ¶
func UpdateProposalPayload(ctx context.Context, db *sql.DB, id string, payload map[string]any) error
UpdateProposalPayload replaces a pending proposal's payload (e.g. retargeting a reproject to a different project before applying). It errors if the proposal is missing or not pending, so a resolved proposal is never silently rewritten.
func UpdateSession ¶
UpdateSession updates the mutable fields of a session (status, findings, metadata, project scope, updated_at) by id.
func UpdateTask ¶
func UpdateTask(ctx context.Context, db *sql.DB, id string, patch TaskPatch, actor string, now time.Time) (core.Task, error)
UpdateTask applies patch to the task and returns the updated task (with deps). Moving to a terminal status stamps closed_at; reopening clears it. Added deps are validated for existence and cycles like CreateTask.
actor is the caller's session id (empty for callers with no session). A task with a live claim (see core.Task.ClaimLive) is locked to its holder: any mutation by a different actor is rejected with ErrTaskClaimConflict, so an agent's claimed task cannot be edited or closed out from under it. The holder itself, an expired lease, and the owner override (ForceReleaseTask) are the only ways past the lock.
func UpsertEmbedding ¶
func UpsertEmbedding(ctx context.Context, db *sql.DB, itemID, kind, model string, vec []float32) error
UpsertEmbedding stores (or replaces) the vector for an item. dims is recorded from the vector length so a later model change is detectable.
func UtilityDemandByProject ¶ added in v0.4.1
func UtilityDemandByProject(ctx context.Context, db *sql.DB, now time.Time, window time.Duration) (map[string]UtilityProjectDemand, error)
UtilityDemandByProject walks the query-gated demand events (recall hits, prompt-recall matches, explicit reads -- the same classes utility scores) grouped by the event's project slug. Passive briefing injections do not count, mirroring the score. The window bounds the Recent* counters; Earliest is all-time.
func UtilityScores ¶ added in v0.4.1
UtilityScores loads item id -> normalized utility for every item with nonzero demand. It is the single wiring point from the stats projection into the ranking paths (briefing, prompt-recall, recall), which read the stored score as-is -- rebuilt hourly by the gardener and on console loads -- and never replay the event log themselves.
Types ¶
type AgentError ¶ added in v0.4.2
type AgentError struct {
TS time.Time
SessionID string
Project string
Surface string // "tool" | "hook"
Key string // surface "tool": tool name; surface "hook": stage label
Error string // first line of the error, as the payload recorded it
Args map[string]any // surface "tool" only: the call's (truncated) arguments
}
AgentError is one agent-facing failure read back out of the event log: an MCP/CLI tool call that returned an error (surface "tool") or a hook-stage failure swallowed fail-open (surface "hook"). The gardener's tool-error pass clusters these into proposals.
func HookErrorsSince ¶ added in v0.4.2
HookErrorsSince returns the hook.error events at or after the cutoff, oldest first. Rows without a stage or error are skipped.
func ToolErrorsSince ¶ added in v0.4.2
ToolErrorsSince returns the failed tool.call events at or after the cutoff, oldest first. Rows without an error or tool name are unusable for clustering and are skipped.
type BlockedTask ¶
BlockedTask is an open task that is not ready, paired with the blockers still keeping it off the ready queue (open/in_progress dependencies).
func AllBlockedTasks ¶
AllBlockedTasks returns open-but-not-ready tasks across every project, each with its still-open blockers.
func BlockedTasks ¶
BlockedTasks returns a project's open-but-not-ready tasks, each with its still-open blockers, so a caller can render the dependency chain legibly.
func BlockedTasksForPlan ¶
func BlockedTasksForPlan(ctx context.Context, db *sql.DB, project, plan string) ([]BlockedTask, error)
BlockedTasksForPlan returns a plan's open-but-not-ready step tasks, each with its still-open blockers.
type ClaimResult ¶
ClaimResult reports the outcome of a successful ClaimTask. Reclaimed is true when a different session's lapsed lease was stolen; PriorHolder then names that session so the caller can record a reclaim event.
func ClaimTask ¶
func ClaimTask(ctx context.Context, db *sql.DB, id, sessionID string, lease time.Duration, now time.Time) (ClaimResult, error)
ClaimTask atomically claims a task for sessionID, moving it to in_progress and stamping a lease that expires at now+lease. It is a compare-and-set (mirrors ResolveProposal): the write lands only when the task is claimable --
(a) open and ready (no open/in_progress blocker), or
(b) in_progress and held by sessionID (a re-claim / heartbeat that refreshes
the lease), or
(c) in_progress but carrying no live claim -- nobody holds it, so it is up for
grabs (a steal from a dead holder, or a row that was never claimed).
Otherwise it returns an error naming the actual refusal: ErrTaskClaimConflict when another live lease holds the task, ErrTaskBlocked when the task is open with an unfinished dependency (naming the blockers), ErrTaskClosed when it is done/dropped. Lease expiry is enforced lazily here -- there is no background sweeper. sessionID must be non-empty.
core.Task.ClaimLive is the single authority on whether a task is held, and branches (b)+(c) are exactly its negation for an in_progress row: an empty claimed_by (no holder), a NULL lease_expires_at (no lease stamped), or a lapsed lease (lease_expires_at <= now -- ClaimLive uses After, so a lease expiring exactly at now is already dead). Branch (c) must tolerate a claimless in_progress row because UpdateTask can produce one: `seam task start <id>` and MCP tasks_update status=in_progress patch the status without stamping a claim, which is a legitimate owner action. Such a row is already freely editable by anyone (UpdateTask's holder-lock also defers to ClaimLive), so refusing to claim it would strand it as permanently unclaimable until someone reopened it.
Branch (a) deliberately ignores claimed_by: a live claim exists only on an in_progress task with an unexpired lease, and every path that writes status='open' clears the claim fields, so a claim value on an open row can only be stale residue (rows written before reopen released claims). Claiming such a row overwrites the residue, self-healing it.
type CoverageBucket ¶
type CoverageBucket struct {
Label string `json:"label"`
Total int `json:"total"` // sessions created in the bucket
Covered int `json:"covered"` // of those, how many retained knowledge
}
CoverageBucket is one time bucket of the session-coverage trend: a pre-formatted tick label, how many sessions started in it, and how many of those retained knowledge. Total == 0 means no sessions in the bucket, so its coverage is undefined (the trend renders it as a dip to the floor, not a ceiling).
func SessionCoverageBuckets ¶
func SessionCoverageBuckets(ctx context.Context, db *sql.DB, w RetrievalWindow, now time.Time) ([]CoverageBucket, error)
SessionCoverageBuckets computes the windowed session-coverage trend in the viewer's local time -- hourly for the 24h window, daily otherwise (for "all", daily from the earliest session). It applies the same covered-ness test as GetSessionCoverage over the same window, so the coverage hero and this trend describe the same set of sessions. Buckets are contiguous (a quiet stretch reads as a dip), and it shares localBucketAxis/bucketKey with the injection trend so the two charts' x-axes line up. Returns nil when the window holds no sessions.
type EmbeddingModelStat ¶ added in v0.4.1
type EmbeddingModelStat struct {
Model string `json:"model"`
Dims int `json:"dims"`
Count int `json:"count"`
Memories int `json:"memories"`
Notes int `json:"notes"`
Updated time.Time `json:"updated"`
}
EmbeddingModelStat is one (model, dims) group of stored vectors: how many items it covers, split by kind, and when a vector in it was last written.
type EmbeddingStats ¶ added in v0.4.1
type EmbeddingStats struct {
Total int `json:"total"`
Models []EmbeddingModelStat `json:"models"`
ActiveMemories int `json:"activeMemories"`
Notes int `json:"notes"`
Missing int `json:"missing"`
}
EmbeddingStats summarizes the vector index for the console's Settings page: what is stored (grouped by model), how big the embeddable corpus is, and how much of it has no vector at all. Missing counts active memories and notes without an embeddings row; invalidated memories keep their vectors but are not owed one, so they never count as missing.
func GetEmbeddingStats ¶ added in v0.4.1
GetEmbeddingStats reads the vector-index summary. It is a set of plain aggregate queries -- no BLOB is materialized.
type KindReach ¶
type KindReach struct {
Kind string `json:"kind"`
Injects int `json:"injects"`
Memories int `json:"memories"`
}
KindReach is one memory kind's share of a window's injection activity: total injections (volume) and the number of distinct active memories of that kind that were surfaced at least once.
type LabSummary ¶ added in v0.3.9
type LabSummary struct {
Lab string `json:"lab"`
Trials int `json:"trials"`
Pass int `json:"pass"`
Fail int `json:"fail"`
Partial int `json:"partial"`
Inconclusive int `json:"inconclusive"`
Other int `json:"other"`
Projects []string `json:"projects"` // distinct project slugs ("" = global), sorted
Sessions int `json:"sessions"` // distinct recording sessions
FirstAt time.Time `json:"firstAt"`
LastAt time.Time `json:"lastAt"`
}
LabSummary aggregates one lab's trials for the console: outcome tallies over the conventional values (anything else -- including an empty outcome -- lands in Other), the distinct projects and sessions its trials touched, and the first/last trial stamps. A lab exists only as the label its trials carry, so this is the lab's whole identity.
type MemoryStat ¶
type MemoryStat struct {
ID string `json:"id"`
Name string `json:"name"`
Kind string `json:"kind"`
Project string `json:"project"`
Injects int `json:"injects"`
Reads int `json:"reads"`
Sessions int `json:"sessions"` // distinct sessions reached (retrieval-reach view)
Updated time.Time `json:"updated"`
LastInjected *time.Time `json:"lastInjected,omitempty"`
}
MemoryStat is an active memory annotated with its retrieval counts, for the Retrieval page's top-injected and stale lists.
type MemoryVector ¶
type MemoryVector struct {
ID string
Project string
Name string
Description string
Kind string
UpdatedAt time.Time
Vec []float32
}
MemoryVector pairs an active memory's index metadata with its stored embedding vector, for the gardener's pairwise dedup scan.
func ActiveMemoryVectors ¶
ActiveMemoryVectors returns the vectors of all active memories embedded under model, oldest-created first (stable ordering for deterministic pairing). It joins the embeddings table, so a memory with no vector for that model is omitted (the gardener simply cannot dedup it semantically).
type NamedCount ¶
NamedCount pairs an item's name with a count, for "top N" lists.
type NamedScore ¶ added in v0.4.1
type NamedScore struct {
ID string `json:"id"`
Name string `json:"name"`
Score float64 `json:"score"`
}
NamedScore pairs an item's name with a float score, for "top N" lists.
type NavCounts ¶
type NavCounts struct {
}
NavCounts are the cheap roll-up counts the console shows in its sidebar. It is a handful of COUNT queries, safe to run on every page load.
type PlanRollup ¶
type PlanRollup struct {
Slug string `json:"slug"`
Total int `json:"total"`
Done int `json:"done"`
InFlight int `json:"inFlight"`
Claimable int `json:"claimable"`
LastActivity time.Time `json:"lastActivity"`
}
PlanRollup is the per-plan aggregate the briefing surfaces: Total step tasks, how many are Done (closed), InFlight (in_progress), and Claimable (ready). LastActivity is the newest updated_at across the plan's steps.
func ActivePlans ¶
ActivePlans returns a rollup for each not-yet-complete plan in a project (plans whose every step is closed are omitted, like a done stage). Claimable counts ready open steps (same readiness rule as ReadyTasksForPlan); the plan's status is derived, never stored. One grouped query covers every plan. Ordered most-recently-active first (newest step updated_at), ties broken by slug, so the briefing and the console lead with what just moved.
type PlanSearchRow ¶
type PlanSearchRow struct {
Slug string
Project string
Title string
// Favorite is true when any of the plan's tagged notes is favorited. The
// authoritative flag lives on the plan's primary note, but this query cannot
// cheaply identify the primary; the two only disagree after deliberate
// hand-editing of a secondary note's frontmatter.
Favorite bool
Updated time.Time
}
PlanSearchRow is one plan hit. A plan is a composition, not a table, so a row is identified by (Project, Slug); Title is the best label found -- a matching note's title, or the slug itself when only tasks carry it.
func SearchPlans ¶
SearchPlans returns plans whose slug or narrative-note title contains q, newest-updated first.
Plans are bi-sourced (a note tagged plan:<slug>, and tasks carrying plan_slug), and either source alone can carry a match: a plan whose steps exist but whose note does not, or vice versa. Both are queried and merged deduped by (project, slug), keeping the newer Updated and preferring a note's title over the slug fallback -- the same merge the Plans screen does, done here so a search hit cannot disagree with the page it links to.
func SearchPlansSince ¶ added in v0.3.9
func SearchPlansSince(ctx context.Context, db *sql.DB, q string, since time.Time, limit int) ([]PlanSearchRow, error)
SearchPlansSince is SearchPlans restricted to matching plan sources updated at or after since. Plans are merged before the bound is applied, so either a matching narrative or matching task can keep the composition in-window.
type ProjectBoardRow ¶
type ProjectBoardRow struct {
Project string `json:"project"` // slug; "" is the global scope
Memories int `json:"memories"` // active memories, project = slug (strict, matches GetProjectCounts.Memories)
Sessions int `json:"sessions"` // all sessions project_slug = slug (matches GetProjectCounts.Sessions)
LiveSessions int `json:"liveSessions"` // live sessions: active AND updated within core.SessionIdleTTL (matches the Sessions screen)
OpenTasks int `json:"openTasks"` // status IN ('open','in_progress') (matches GetProjectCounts.OpenTasks)
Blocked int `json:"blocked"` // open tasks with >=1 open/in_progress blocker (== len(BlockedTasks))
Notes int `json:"notes"` // notes project = slug (matches GetProjectCounts.Notes)
LastActive time.Time `json:"lastActive"` // MAX(sessions.updated_at) for the project; zero when none
TokensTotal int `json:"tokensTotal"` // SUM(sessions.total_tokens): real model token burn harvested from transcripts
Unregistered bool `json:"unregistered"` // slug seen in data tables but absent from the projects table
ReachRate int `json:"reachRate"` // Surfaced/Active rounded %, from BuildRetrievalReport; 0 when absent
Surfaced int `json:"surfaced"` // distinct active memories surfaced >=1x in the window
Active int `json:"active"` // active memories (reach denominator) per BuildRetrievalReport
}
ProjectBoardRow is one project's health roll-up for the project board: the same strict per-slug counts GetProjectCounts computes for a single project, batched across every project in one pass, plus liveness (live sessions), backpressure (blocked tasks), recency (last session activity), and retrieval reach joined from BuildRetrievalReport.
Counts are STRICT per-slug (project = slug / project_slug = slug), never a global union: the global scope ("") is its own row whose counts are the global-scope counts, never folded into each named project. A slug that appears in the data tables but has no projects-table row (files are the source of truth and can drift ahead of the registry) still gets a row, flagged Unregistered so the caller can surface the drift rather than hide it.
func ProjectsWithCounts ¶
func ProjectsWithCounts(ctx context.Context, db *sql.DB, window RetrievalWindow, now time.Time, idleTTL time.Duration) ([]ProjectBoardRow, error)
ProjectsWithCounts returns a ProjectBoardRow for every project -- every slug that appears in memories_index / sessions / tasks / notes_index unioned with the projects table -- with strict per-slug counts, liveness, blocked-task backpressure, and last-active recency computed in SQL, then retrieval reach (ReachRate/Surfaced/Active) joined by slug from BuildRetrievalReport over the given window. LiveSessions counts sessions that are active AND updated within idleTTL of now (<= 0 falls back to core.SessionIdleTTL), matching the Sessions screen's live bucket, so an active-but-idle session awaiting the reaper never inflates the board.
The counts mirror GetProjectCounts exactly (same strict per-slug predicates), so a single row equals the single-project peek. The global scope ("") is its own row (never folded into named projects); it is never flagged Unregistered because the global scope is legitimately never registered. Rows are ordered by slug (the global "" scope first).
type ProjectCounts ¶
type ProjectCounts struct {
Memories int `json:"memories"` // active memories in the project
Sessions int `json:"sessions"` // sessions scoped to the project
OpenTasks int `json:"openTasks"` // open or in_progress tasks
Notes int `json:"notes"` // notes in the project
}
ProjectCounts are the per-project totals the console project peek shows. The channels do not overlap in the way coverage does; each is a plain count.
func GetProjectCounts ¶
GetProjectCounts computes the per-project roll-up for one slug in a single round trip (scalar subqueries). It backs the console project peek.
type ProjectReach ¶
type ProjectReach struct {
Project string `json:"project"`
Surfaced int `json:"surfaced"` // distinct active memories of this project surfaced
Active int `json:"active"` // total active memories in this project (reach denominator)
ReachRate int `json:"reachRate"` // Surfaced / Active, rounded %
Injects int `json:"injects"` // injection volume attributable to this project's memories
Created int `json:"created"` // memories created within the window (incl. since-retired ones)
Retired int `json:"retired"` // memories retired (superseded/archived) within the window
}
ProjectReach is one project's reach within the window: how many of its active memories were surfaced at least once, out of its total active memories, plus the injection volume attributable to them. It lets the global reach number be read per knowledge base -- a project whose memories never surface shows 0% even while the global rate looks healthy. Project "" is the global scope.
type Proposal ¶
type Proposal struct {
ID string `json:"id"`
Kind string `json:"kind"`
Payload map[string]any `json:"payload"`
Status string `json:"status"`
CreatedAt time.Time `json:"createdAt"`
ResolvedAt *time.Time `json:"resolvedAt,omitempty"`
Result map[string]any `json:"result,omitempty"`
}
Proposal is one gardener suggestion awaiting owner review. Payload carries the kind-specific detail; every payload includes a stable "key" string the gardener uses to avoid re-proposing the same thing on a later pass. Result is what applying it produced (the note it wrote, the task it opened, the memory it moved) -- nil until applied, and the handle undo uses to find the artifact again.
func CreateProposal ¶
func CreateProposal(ctx context.Context, db *sql.DB, kind string, payload map[string]any) (Proposal, error)
CreateProposal inserts a pending gardener proposal and returns it. The caller supplies the kind and payload; the id and created_at are stamped here.
func PendingProposals ¶
PendingProposals returns pending proposals, newest first. kind filters by proposal kind when non-empty.
func ProposalByID ¶
ProposalByID returns one proposal. found is false when absent.
func RecentResolvedProposals ¶ added in v0.4.6
RecentResolvedProposals returns the most recently applied or dismissed proposals, newest resolution first, capped at limit. It backs the console's "Recently decided" section, where each row offers an undo.
type RecallHitQuery ¶ added in v0.4.1
RecallHitQuery is the (project, query) of one successful recall tool call.
func RecallHitQueriesSince ¶ added in v0.4.1
func RecallHitQueriesSince(ctx context.Context, db *sql.DB, since time.Time) ([]RecallHitQuery, error)
RecallHitQueriesSince returns the query text of every recall tool call that surfaced hits since the cutoff. The memory-wanted pass uses these to suppress miss groups whose query also succeeds sometimes -- an intermittent miss is a ranking problem, not a knowledge gap.
type RecallMiss ¶ added in v0.4.1
RecallMiss is one zero-hit recall tool call read back out of the event log: an agent deliberately searched and found nothing. The gardener's memory-wanted pass clusters these into proposals.
func RecallMissesSince ¶ added in v0.4.1
RecallMissesSince returns the recall.miss events at or after the cutoff, oldest first. Rows whose payload carries no query text are unusable for clustering and are skipped.
type RepoMapAdoption ¶ added in v0.4.6
type RepoMapAdoption struct {
Slug string // the adopted project slug
NewPath string // the repo root that now owns the slug
OldPaths []string // the dead map entries that owned it, now removed
}
RepoMapAdoption reports that RegisterProjectForCWD re-pointed an existing project at a repo's new location instead of minting a fresh -N slug: every map entry owning the derived slug named a path that no longer exists on disk, so the repo was moved, not duplicated. The remap already happened; callers only surface it (an event, a console notice).
func RegisterProjectForCWD ¶
func RegisterProjectForCWD(ctx context.Context, db *sql.DB, cwd string) (string, *RepoMapAdoption, error)
RegisterProjectForCWD resolves cwd to a project slug like ResolveProjectForCWD, but grows the map at runtime when cwd falls outside every mapped repo: it finds the enclosing git repository, derives a slug from that repo's directory name, records repoRoot -> slug in the repo_project_map, and ensures a projects-table row. This is how the repo->project map evolves as agents work in new repos -- no recompile, no manual map-repo. For an already-mapped cwd it still backfills the registry row, so project_list stays complete. A blank cwd, or a cwd outside any git repo, resolves to the global scope ("") and registers nothing.
A moved repo heals itself here: when the derived slug is owned only by map entries whose paths no longer exist, the repo was moved, and the existing project is adopted (the dead entries are replaced by the new root) instead of minting a -N slug that would silently split the project's history. The non-nil *RepoMapAdoption reports that remap; it is nil on every other path.
type RetrievalReport ¶
type RetrievalReport struct {
Window RetrievalWindow `json:"window"`
Injected int `json:"injected"` // item-level injections in the window (volume) == sum(Trend)
InjectedTokens int `json:"injectedTokens"` // estimated tokens of injected context in the window (reach's cost side)
MemoriesSurfaced int `json:"memoriesSurfaced"` // distinct active memories surfaced >=1x
ActiveMemories int `json:"activeMemories"` // total active memories (reach denominator)
ReachRate int `json:"reachRate"` // MemoriesSurfaced / ActiveMemories, rounded %
SessionsReached int `json:"sessionsReached"` // distinct sessions that received >=1 injection
CreatedInWindow int `json:"createdInWindow"` // memories created within the window (incl. since-retired ones)
RetiredInWindow int `json:"retiredInWindow"` // memories retired (superseded/archived) within the window
ByKind []KindReach `json:"byKind"`
ByProject []ProjectReach `json:"byProject"` // reach sliced per project (denominator = that project's active memories)
Top []MemoryStat `json:"top"` // most-injected active memories, with sessions reached
Trend []TrendBucket `json:"trend"`
Hourly bool `json:"hourly"` // trend granularity: hourly (24h window) vs daily
// Loop health: whether what briefings push is what agents actually pull.
// Demand = query-gated signals only (recall hits, prompt matches, explicit
// reads); passive briefing injections are exposure, not demand.
BriefingSurfaced int `json:"briefingSurfaced"` // distinct active memories briefings surfaced in the window
DemandedOfSurfaced int `json:"demandedOfSurfaced"` // of those, how many also saw demand in the window
DemandRate int `json:"demandRate"` // DemandedOfSurfaced / BriefingSurfaced, rounded %
PromptMatches int `json:"promptMatches"` // prompt-recall injections in the window
RecallMisses int `json:"recallMisses"` // hook.prompt (matched nothing) events in the window
MissRate int `json:"missRate"` // RecallMisses / (RecallMisses+PromptMatches), rounded %
ToolMisses int `json:"toolMisses"` // recall.miss (zero-hit recall calls) events in the window
DeadWeight []MemoryStat `json:"deadWeight"` // most-briefing-injected active memories with no demand in 30d
}
RetrievalReport is the retrieval-REACH rollup for a window, computed live from the injection event stream. It measures how far the knowledge base actually reaches agents -- how many distinct memories get surfaced, across how many sessions -- rather than a read-back rate. (Read-after-inject is not tracked here: agents almost never memory_read what a briefing already surfaced, so the only honest consumption signal for this system is reach, not re-reads.)
func BuildRetrievalReport ¶
func BuildRetrievalReport(ctx context.Context, db *sql.DB, w RetrievalWindow, topN int) (RetrievalReport, error)
BuildRetrievalReport computes the windowed retrieval-reach rollup in a single pass over the injection event stream: total injection volume, how many distinct active memories were surfaced (reach), across how many sessions, plus the per-kind and most-injected breakdowns and the injection trend. topN caps the most-injected list (<=0 => 12). Archived/unknown injected ids still count toward the volume total and the trend, but drop out of the by-kind and most-injected breakdowns (scoped to active memories).
type RetrievalStat ¶
type RetrievalStat struct {
ItemID string
InjectCount int
ReadCount int
LastInjectedAt *time.Time
LastReadAt *time.Time
Utility float64
Components UtilityComponents
}
RetrievalStat mirrors one retrieval_stats row: how often an item has been surfaced (injected) to an agent and read back, when last, and its time-decayed utility score. It is a materialized projection of the append-only event log, rebuilt by RebuildRetrievalStats -- the events table is the source of truth.
func GetRetrievalStat ¶
GetRetrievalStat returns the stats row for an item. found is false when the item has never been injected or read.
type RetrievalWindow ¶
type RetrievalWindow struct {
Key string `json:"key"`
Label string `json:"label"`
Since time.Time `json:"since"`
}
RetrievalWindow is a resolved trailing time window for the retrieval-health views: a stable key (URL + selector), a human label, and the inclusive lower bound. A zero Since means "all time" (unbounded).
func ResolveRetrievalWindow ¶
func ResolveRetrievalWindow(key string, now time.Time) RetrievalWindow
ResolveRetrievalWindow maps a selector key to a window anchored at now. Unknown or empty keys fall back to "24h", the default: an unscoped console page answers about today rather than about all history.
type SearchHit ¶
SearchHit is one result of a cosine similarity search.
func CosineSearch ¶
func CosineSearch(ctx context.Context, db *sql.DB, query []float32, model string, kinds, projects []string, limit int) ([]SearchHit, error)
CosineSearch brute-force-scans stored vectors for the given model and returns the top-limit most similar items, highest score first. An empty kinds filter searches all kinds. At the corpus scale this system targets (thousands of items) a full scan is milliseconds, which is why there is no vector index.
projects restricts hits to items whose project is in the list (recall passes the bound project plus "" for global); an empty filter searches all projects. Filtering inside the candidate query keeps the whole candidate depth in scope, so a corpus dominated by out-of-scope vectors cannot starve in-scope results out of the top-limit window. Embedding rows carry no project, so the scope is resolved by joining the index tables; an embedding orphaned from both indexes matches no scope.
Superseded and archived memories are excluded on the same grounds: a memory keeps its vector after it is invalidated, and a retired revision sits close to the replacement that superseded it, so filtering validity after the LIMIT let dead revisions eat the candidate depth. An embedding with no memories_index row (a note, or an orphan) has nothing to invalidate it and survives this predicate -- the scope filter is what drops orphans.
func CosineSearchSince ¶ added in v0.3.9
func CosineSearchSince(ctx context.Context, db *sql.DB, query []float32, model string, kinds, projects []string, memKind string, since time.Time, limit int) ([]SearchHit, error)
CosineSearchSince is CosineSearch restricted to knowledge updated at or after since. A zero since is unbounded. Filtering before the top-K scan keeps old neighbors from occupying the entire candidate window of a recent search. memKind, when non-empty, restricts hits to memories of that frontmatter kind (memories_index.kind); notes have no memories_index row, so a memKind filter excludes them regardless of the kinds (item-type) filter.
func FTSSearch ¶
func FTSSearch(ctx context.Context, db *sql.DB, query string, kinds, projects []string, limit int) ([]SearchHit, error)
FTSSearch runs a full-text query over the unified fts table and returns hits ordered best-first. Scores are the negated bm25 rank (higher = better) so they share the "bigger is better" convention with CosineSearch. An empty kinds filter searches all kinds. A query with no usable terms yields no hits (not an error), so recall degrades quietly on punctuation-only input.
projects restricts hits to items whose project is in the list (recall passes the bound project plus "" for global); an empty filter searches all projects. Filtering inside the candidate query keeps the whole candidate depth in scope, so a corpus dominated by out-of-scope matches cannot starve in-scope results out of the top-limit window.
Superseded and archived memories are excluded for the same reason: the fts table is self-contained and keeps a full row for a memory that is no longer valid, so validity is resolved by joining memories_index. Filtering it after the LIMIT (as callers used to) let retired revisions of a name -- which match their replacement's queries almost as well, by construction -- eat the candidate depth and starve the live memory that replaced them. An fts row with no index row (notes, or an orphan) has nothing to invalidate it and is kept.
func FTSSearchAllTerms ¶ added in v0.4.1
func FTSSearchAllTerms(ctx context.Context, db *sql.DB, terms []string, kinds, projects []string, limit int) ([]SearchHit, error)
FTSSearchAllTerms is FTSSearch with AND semantics: a hit must contain every term. The OR of ftsQuery maximizes recall for ranking pipelines, but a presence probe -- "does knowledge covering all of this exist?" -- needs precision instead: with OR, any item sharing one common word would count. The gardener's memory-wanted liveness guard is the canonical caller. Terms are sanitized exactly like ftsQuery's tokens (split, short tokens dropped, quoted); unusable input yields no hits, not an error.
func FTSSearchSince ¶ added in v0.3.9
func FTSSearchSince(ctx context.Context, db *sql.DB, query string, kinds, projects []string, since time.Time, limit int) ([]SearchHit, error)
FTSSearchSince is FTSSearch restricted to index rows updated at or after since. A zero since keeps the unbounded behavior. The predicate lives before LIMIT so old, highly-ranked rows cannot crowd in-window matches out of the candidate set.
type SessionCoverage ¶
type SessionCoverage struct {
Total int `json:"total"` // all sessions (the denominator)
Covered int `json:"covered"` // sessions with >=1 durable artifact
Findings int `json:"findings"` // sessions whose findings are non-empty
Memories int `json:"memories"` // sessions that wrote >=1 memory
Notes int `json:"notes"` // sessions that created >=1 note
Trials int `json:"trials"` // sessions that recorded >=1 trial
}
SessionCoverage measures how much Claude Code session knowledge Seamless is retaining: a session is "covered" when it left a durable artifact behind -- non-empty findings, or at least one written memory, note, or recorded trial. It is a rough proxy for "how much of what happened in a session did we keep". The per-channel counts (Findings/Memories/Notes/Trials) overlap -- a session can be covered several ways -- so only Total and Covered partition the set.
func GetSessionCoverage ¶
GetSessionCoverage computes the coverage roll-up in a single pass over the sessions created within the window (a zero `since` means all time), testing each against the event log for durable artifacts. It reads the event log directly rather than retrieval_stats, so it needs no rebuild.
func GetSessionCoverageForProject ¶
func GetSessionCoverageForProject(ctx context.Context, db *sql.DB, project string, since time.Time) (SessionCoverage, error)
GetSessionCoverageForProject computes the session-coverage roll-up (see GetSessionCoverage) restricted to one project's sessions (project_slug = ?).
It rejects an empty project: an empty project_slug marks real global sessions, so "" is ambiguous between "all sessions" and "global-only" -- the caller who wants the all-sessions number must use GetSessionCoverage instead.
type SnippetHit ¶
SnippetHit is an FTS hit carrying the matched text in context. Snippet is the raw item text with the matched terms wrapped in SnippetStartMark/SnippetEndMark.
func FTSSearchSnippets ¶
func FTSSearchSnippets(ctx context.Context, db *sql.DB, query string, kinds, projects []string, limit int) ([]SnippetHit, error)
FTSSearchSnippets is FTSSearch plus a generated snippet per hit: the same query, filters, and ordering, with the matched terms marked in context. The two share ftsSearch so the validity predicate, the scope filter, and the ordering cannot drift between the snippet and no-snippet paths -- callers rely on both returning identical hits for identical inputs.
func FTSSearchSnippetsSince ¶ added in v0.3.9
func FTSSearchSnippetsSince(ctx context.Context, db *sql.DB, query string, kinds, projects []string, memKind string, since time.Time, limit int) ([]SnippetHit, error)
FTSSearchSnippetsSince is the windowed form of FTSSearchSnippets. See FTSSearchSince for why the time predicate is part of the candidate query. memKind, when non-empty, restricts hits to memories of that frontmatter kind (memories_index.kind); notes have no memories_index row, so a memKind filter excludes them regardless of the kinds (item-type) filter.
type SurfaceFunnel ¶ added in v0.4.4
type SurfaceFunnel struct {
Surface string `json:"surface"`
Injections int `json:"injections"` // item-level injections via this surface in the window (volume)
Items int `json:"items"` // distinct items this surface injected in the window
ItemsRead int `json:"itemsRead"` // of Items, how many were pulled within the follow window of an injection
ReadRate int `json:"readRate"` // ItemsRead / Items, rounded %
}
SurfaceFunnel is the read-after-inject funnel for one injection surface: how much the surface pushed, and how much of it agents pulled back.
func ReadAfterInjectFunnel ¶ added in v0.4.4
func ReadAfterInjectFunnel(ctx context.Context, db *sql.DB, since time.Time, follow time.Duration) ([]SurfaceFunnel, error)
ReadAfterInjectFunnel segments the read-after-inject funnel by injection surface over the events at or after since (zero = all time). An item converts for a surface when a deliberate pull -- an explicit memory.read or a recall-tool hit -- lands after one of that surface's injections of the item and within follow of it (<=0 = DefaultFunnelFollow); a pull with no preceding in-window injection converts nothing. Prompt-recall matches do not count as conversions: they are themselves automated injections, and Claude Code children have no UserPromptSubmit hook, so counting them would skew the session-start vs subagent-start comparison this funnel exists for.
The aggregation is read-only over the existing event log: it records nothing, adds no utility inputs, and leaves RebuildRetrievalStats untouched (closed-loop-utility-signal-contract).
type TaskPatch ¶
type TaskPatch struct {
Status *core.TaskStatus
Title *string
Body *string
ProjectSlug *string
AddDependsOn []string
}
TaskPatch is the set of mutable fields UpdateTask may change; nil fields are left untouched. AddDependsOn edges are added (existing edges are kept). ProjectSlug reassigns the task to another project (used when a split moves a project's open work to a child); the holder-lock still applies.
type TrendBucket ¶
TrendBucket is one time bucket of the injection trend: a pre-formatted tick label and the item-level injection count that fell in it. The report's total Injected equals the sum of bucket counts, so the hero number and the chart always describe the same quantity over the same window.
func ProjectRetrievalTrend ¶ added in v0.3.6
func ProjectRetrievalTrend(ctx context.Context, db *sql.DB, w RetrievalWindow, project string) ([]TrendBucket, error)
ProjectRetrievalTrend builds the injection trend for a single project's active memories over the window: the same time-bucketed series as RetrievalReport.Trend, but counting only injections of memories whose project is `project`. Attribution matches RetrievalReport.ByProject (by the injected memory's own project, not the injecting session's), so a project's trend agrees with the reach numbers shown beside it. Injections of archived/unknown ids -- which have no active project -- are excluded (unlike the global trend, they cannot be attributed to a slice).
type TrialFilter ¶
type TrialFilter struct {
Lab string
Outcome string
Project string
SessionID string
MetricsEquals map[string]any
Limit int
}
TrialFilter parameterizes QueryTrials. Empty string fields are not filtered; MetricsEquals matches trials whose metrics contain each given key with an equal value (compared after JSON normalization). A non-positive Limit defaults to 20.
type UsageSummary ¶
type UsageSummary struct {
Memories struct {
Active int `json:"active"`
ByKind map[string]int `json:"byKind"`
} `json:"memories"`
Notes int `json:"notes"`
Sessions map[string]int `json:"sessions"` // status -> count
Tasks map[string]int `json:"tasks"` // status -> count
Retrieval struct {
Injections int `json:"injections"`
Reads int `json:"reads"`
TopInjected []NamedCount `json:"topInjected"`
TopUtility []NamedScore `json:"topUtility"` // highest decayed-demand scores
// FunnelBySurface is the all-time read-after-inject funnel segmented by
// injection surface (conversions within DefaultFunnelFollow).
FunnelBySurface []SurfaceFunnel `json:"funnelBySurface"`
} `json:"retrieval"`
GardenerPending map[string]int `json:"gardenerPending"` // kind -> count
EventsByKind map[string]int `json:"eventsByKind"`
}
UsageSummary is a point-in-time roll-up of activity across the store, backing the usage_summary MCP tool and (later) the console's overview. It is derived entirely from the DB-of-record tables and the event log.
func GetUsageSummary ¶
GetUsageSummary computes the usage roll-up. It reads current retrieval_stats; callers wanting fresh numbers should RebuildRetrievalStats first.
type UtilityActivation ¶ added in v0.4.1
type UtilityActivation struct {
Projects map[string]UtilityProjectState `json:"projects"`
}
UtilityActivation is the stored activation map, keyed by project slug (project "" -- the global scope -- is a valid key).
func GetUtilityActivation ¶ added in v0.4.1
GetUtilityActivation loads the activation state; an absent row is an empty (nothing active) state, not an error.
func (UtilityActivation) Active ¶ added in v0.4.1
func (a UtilityActivation) Active(project, mode string) bool
Active reports whether utility ranking applies to a project's briefing under the given mode ("auto", "on", or "off" -- config.Briefing.UtilityMode).
type UtilityComponents ¶ added in v0.4.1
type UtilityComponents struct {
Read float64 `json:"read,omitempty"`
Recall float64 `json:"recall,omitempty"`
Prompt float64 `json:"prompt,omitempty"`
}
UtilityComponents is the decayed raw demand per signal class, kept alongside the normalized score so the console can show WHY a memory ranks where it does.
func (UtilityComponents) IsZero ¶ added in v0.4.1
func (c UtilityComponents) IsZero() bool
IsZero reports no demand in any class (exported for display-layer callers).
type UtilityProjectDemand ¶ added in v0.4.1
type UtilityProjectDemand struct {
Earliest time.Time // first query-gated demand event ever (zero = none)
RecentEvents int // session-deduped demand events within the window
RecentMemories int // distinct memories those recent events surfaced
}
UtilityProjectDemand summarizes a project's query-gated demand history for the readiness decision: when demand first appeared, and how much of it -- in session-deduped events and distinct memories -- the trailing window holds.
type UtilityProjectState ¶ added in v0.4.1
type UtilityProjectState struct {
ReadyAt *time.Time `json:"ready_at,omitempty"`
Forced string `json:"forced,omitempty"`
}
UtilityProjectState is one project's activation record. ReadyAt is the latch: once the gardener sets it, the project stays active (no flapping). Forced is the owner override: "on" and "off" both win over the latch; "" defers to it.
Source Files
¶
- console.go
- embeddings.go
- favorites.go
- fts.go
- gardener.go
- inject_funnel.go
- memories.go
- migrate.go
- mishaps.go
- notes.go
- project_board.go
- projects.go
- relations.go
- retrieval_report.go
- retrieval_stats.go
- scan.go
- search.go
- sessions.go
- settings.go
- store.go
- tasks.go
- tasks_claim.go
- tasks_deps.go
- tasks_plans.go
- tasks_query.go
- tasks_scan.go
- trials.go
- usage.go
- utility_activation.go