Documentation
¶
Overview ¶
Package store implements the persistent memory engine for Engram.
It uses SQLite with FTS5 full-text search to store and retrieve observations from AI coding sessions. This is the core of Engram — everything else (HTTP server, MCP server, CLI, plugins) talks to this.
Index ¶
- Constants
- Variables
- func ClassifyTool(toolName string) string
- func ExtractLearnings(text string) []string
- func NormalizeProject(project string) (normalized string, warning string)
- func Now() string
- func SuggestTopicKey(typ, title, content string) string
- func ValidateObservationTitle(title string) error
- type AddObservationParams
- type AddPromptParams
- type Candidate
- type CandidateOptions
- type CloudSyncSummary
- type CloudUpgradeLegacyMutationFinding
- type CloudUpgradeLegacyMutationReport
- type CloudUpgradeRepairReport
- type CloudUpgradeSnapshot
- type CloudUpgradeState
- type Config
- type ContextOptions
- type DeferredRow
- type DeleteProjectResult
- type DiagnosticSessionEvidence
- type EnrolledProject
- type ExportData
- type ImportResult
- type InvalidSessionIdentityEvidence
- type JudgeBySemanticParams
- type JudgeRelationParams
- type ListDeferredOptions
- type ListRelationsOptions
- type MergeResult
- type MigrateResult
- type Observation
- type ObservationRelations
- type ObservationRequiredFieldsEvidence
- type ObservationSnippet
- type ObservationSourceTitleRepairAction
- type ObservationSourceTitleRepairReport
- type OrphanedObservationSessionEvidence
- type PassiveCaptureParams
- type PassiveCaptureResult
- type PendingSyncMutationProjectCount
- type ProjectNameCount
- type ProjectRescueBlocked
- type ProjectRescueParams
- type ProjectRescueResult
- type ProjectStats
- type Prompt
- type PruneResult
- type QuarantinedPulledSessionEvidence
- type Relation
- type RelationListItem
- type RelationStats
- type ReplayDeferredResult
- type SQLiteLockSnapshot
- type SaveRelationParams
- type ScanOptions
- type ScanResult
- type SearchOptions
- type SearchPreviewResult
- type SearchResult
- type SemanticRunner
- type SemanticVerdict
- type Session
- type SessionProjectReclassification
- type SessionProjectReclassificationCounts
- type SessionProjectReclassificationResult
- type SessionSummary
- type Stats
- type Store
- func (s *Store) AckSyncMutationSeqs(targetKey string, seqs []int64) error
- func (s *Store) AckSyncMutations(targetKey string, lastAckedSeq int64) error
- func (s *Store) AcquireSyncLease(targetKey, owner string, ttl time.Duration, now time.Time) (bool, error)
- func (s *Store) ActiveRuntimeSessions(project string, directories ...string) ([]string, error)
- func (s *Store) AddObservation(p AddObservationParams) (int64, error)
- func (s *Store) AddPrompt(p AddPromptParams) (int64, error)
- func (s *Store) AddPromptIfMissing(p AddPromptParams) (int64, bool, error)
- func (s *Store) AllObservations(project, scope string, limit int) ([]Observation, error)
- func (s *Store) AllSessions(project string, limit int) ([]SessionSummary, error)
- func (s *Store) ApplyPulledChunk(targetKey, chunkID string, mutations []SyncMutation) error
- func (s *Store) ApplyPulledMutation(targetKey string, mutation SyncMutation) error
- func (s *Store) ApplySessionProjectReclassification(actions []SessionProjectReclassification) (SessionProjectReclassificationResult, error)
- func (s *Store) BackupSQLite() (string, error)
- func (s *Store) CanRollbackCloudUpgrade(project string) (bool, error)
- func (s *Store) ClearCloudUpgradeState(project string) error
- func (s *Store) Close() error
- func (s *Store) CloudSyncSummary() (CloudSyncSummary, error)
- func (s *Store) ContentTruncation(content string) TruncationMetadata
- func (s *Store) CountDeferredAndDead() (deferred, dead int, err error)
- func (s *Store) CountDeferredAndDeadForScope(targetKey, project string) (deferred, dead int, err error)
- func (s *Store) CountObservationsForProject(name string) (int, error)
- func (s *Store) CountPendingNonEnrolledSyncMutations(targetKey string) ([]PendingSyncMutationProjectCount, error)
- func (s *Store) CountRelationSyncMutations() (int, error)
- func (s *Store) CountRelations(opts ListRelationsOptions) (int, error)
- func (s *Store) CreateSession(id, project, directory string) error
- func (s *Store) CreateSessionWithOwnershipMode(id, project, directory, mode string) error
- func (s *Store) DB() *sql.DB
- func (s *Store) DataDir() string
- func (s *Store) DeleteObservation(id int64, hardDelete bool) error
- func (s *Store) DeleteProject(project string, hardDelete bool) (*DeleteProjectResult, error)
- func (s *Store) DeletePrompt(id int64) error
- func (s *Store) DeleteSession(id string) error
- func (s *Store) DiagnoseCloudUpgradeLegacyMutations(project string) (CloudUpgradeLegacyMutationReport, error)
- func (s *Store) EndSession(id string, summary string) error
- func (s *Store) EnrollProject(project string) error
- func (s *Store) EnsureEnrolledProjectSyncMutations(ctx context.Context) error
- func (s *Store) EstimateSessionProjectReclassification(actions []SessionProjectReclassification) (SessionProjectReclassificationCounts, error)
- func (s *Store) Export() (*ExportData, error)
- func (s *Store) ExportProject(project string) (*ExportData, error)
- func (s *Store) ExportRelationMutations(project string) ([]SyncMutation, error)
- func (s *Store) FindCandidates(savedID int64, opts CandidateOptions) ([]Candidate, error)
- func (s *Store) FormatCompactionContext(sessionID string) (string, error)
- func (s *Store) FormatContext(project, scope string) (string, error)
- func (s *Store) FormatContextWithOptions(project, scope string, opts ContextOptions) (string, error)
- func (s *Store) GetCloudUpgradeState(project string) (*CloudUpgradeState, error)
- func (s *Store) GetDeferred(syncID string) (DeferredRow, error)
- func (s *Store) GetObservation(id int64) (*Observation, error)
- func (s *Store) GetObservationBySyncID(syncID string) (*Observation, error)
- func (s *Store) GetRelation(syncID string) (*Relation, error)
- func (s *Store) GetRelationByIntID(id int64) (*RelationListItem, error)
- func (s *Store) GetRelationStats(project string) (RelationStats, error)
- func (s *Store) GetRelationsForObservations(syncIDs []string) (map[string]ObservationRelations, error)
- func (s *Store) GetRelationsForObservationsContext(ctx context.Context, syncIDs []string) (map[string]ObservationRelations, error)
- func (s *Store) GetSession(id string) (*Session, error)
- func (s *Store) GetSyncState(targetKey string) (*SyncState, error)
- func (s *Store) GetSyncedChunks() (map[string]bool, error)
- func (s *Store) GetSyncedChunksForTarget(targetKey string) (map[string]bool, error)
- func (s *Store) HasPendingSyncMutationsForProject(project string) (bool, error)
- func (s *Store) HasProjectOwnedSessions() (bool, error)
- func (s *Store) HasProjectOwnedSessionsForProject(project string) (bool, error)
- func (s *Store) Import(data *ExportData) (*ImportResult, error)
- func (s *Store) IsProjectEnrolled(project string) (bool, error)
- func (s *Store) JudgeBySemantic(p JudgeBySemanticParams) (string, error)
- func (s *Store) JudgeRelation(p JudgeRelationParams) (*Relation, error)
- func (s *Store) ListDeferred(opts ListDeferredOptions) ([]DeferredRow, error)
- func (s *Store) ListDeferredProjectsForTarget(targetKey string) ([]string, error)
- func (s *Store) ListDiagnosticObservationRequiredFields(project string) ([]ObservationRequiredFieldsEvidence, error)
- func (s *Store) ListDiagnosticSessions(project string) ([]DiagnosticSessionEvidence, error)
- func (s *Store) ListEnrolledProjects() ([]EnrolledProject, error)
- func (s *Store) ListInvalidSessionIdentityEvidence(project string) ([]InvalidSessionIdentityEvidence, error)
- func (s *Store) ListObservationSyncPayloads() ([]any, error)
- func (s *Store) ListOrphanedObservationSessionEvidence(project string) ([]OrphanedObservationSessionEvidence, error)
- func (s *Store) ListPendingProjectMutations(project string) ([]SyncMutation, error)
- func (s *Store) ListPendingSyncMutations(targetKey string, limit int) ([]SyncMutation, error)
- func (s *Store) ListPendingSyncMutationsAfterSeq(targetKey string, afterSeq int64, limit int) ([]SyncMutation, error)
- func (s *Store) ListProjectNames() ([]string, error)
- func (s *Store) ListProjectsForCloudEnrollment() ([]string, error)
- func (s *Store) ListProjectsWithStats() ([]ProjectStats, error)
- func (s *Store) ListQuarantinedPulledSessionEvidence(project string) ([]QuarantinedPulledSessionEvidence, error)
- func (s *Store) ListRelations(opts ListRelationsOptions) ([]RelationListItem, error)
- func (s *Store) MarkReviewed(id int64) error
- func (s *Store) MarkReviewedForProject(id int64, project string) error
- func (s *Store) MarkSyncAuthRequired(targetKey, message string) error
- func (s *Store) MarkSyncBlocked(targetKey, reasonCode, message string) error
- func (s *Store) MarkSyncFailure(targetKey, message string, backoffUntil time.Time) error
- func (s *Store) MarkSyncFailureWithReason(targetKey, reasonCode, message string, backoffUntil time.Time) error
- func (s *Store) MarkSyncHealthy(targetKey string) error
- func (s *Store) MarkSyncPaused(targetKey, message string) error
- func (s *Store) MarkSyncPending(targetKey string) error
- func (s *Store) MaxObservationLength() int
- func (s *Store) MergeProjects(sources []string, canonical string) (*MergeResult, error)
- func (s *Store) MigrateProject(oldName, newName string) (*MigrateResult, error)
- func (s *Store) ObservationsNeedingReview(project string, limit int) ([]Observation, error)
- func (s *Store) PassiveCapture(p PassiveCaptureParams) (*PassiveCaptureResult, error)
- func (s *Store) PinObservation(id int64) error
- func (s *Store) PinnedObservations(project, scope string) ([]Observation, error)
- func (s *Store) ProjectExists(name string) (bool, error)
- func (s *Store) PruneProject(project string) (*PruneResult, error)
- func (s *Store) QuarantineIrreparableSyncMutations(targetKey, project string, apply bool) (SyncMutationQuarantineReport, error)
- func (s *Store) ReadSQLiteLockSnapshot(ctx context.Context) (SQLiteLockSnapshot, error)
- func (s *Store) RecentObservations(project, scope string, limit int) ([]Observation, error)
- func (s *Store) RecentPrompts(project string, limit int) ([]Prompt, error)
- func (s *Store) RecentSessions(project string, limit int) ([]SessionSummary, error)
- func (s *Store) RecordSyncedChunk(chunkID string) error
- func (s *Store) RecordSyncedChunkForTarget(targetKey, chunkID string) error
- func (s *Store) ReleaseSyncLease(targetKey, owner string) error
- func (s *Store) RemirrorProject(project string) error
- func (s *Store) RepairCloudUpgrade(project string, apply bool) (CloudUpgradeRepairReport, error)
- func (s *Store) RepairObservationMutationTitles(project string, apply bool) (SyncMutationTitleRepairReport, error)
- func (s *Store) RepairObservationSourceTitles(project string, apply bool) (ObservationSourceTitleRepairReport, error)
- func (s *Store) ReplayDeferred() (ReplayDeferredResult, error)
- func (s *Store) ReplayDeferredForScope(targetKey, project string) (result ReplayDeferredResult, err error)
- func (s *Store) RescueNullProjectOwnership(p ProjectRescueParams) (*ProjectRescueResult, error)
- func (s *Store) RollbackCloudUpgrade(project string) (CloudUpgradeState, error)
- func (s *Store) SaveCloudUpgradeState(state CloudUpgradeState) error
- func (s *Store) SaveRelation(p SaveRelationParams) (*Relation, error)
- func (s *Store) ScanAllProjects(opts ScanOptions) (ScanResult, error)
- func (s *Store) ScanProject(opts ScanOptions) (ScanResult, error)
- func (s *Store) Search(query string, opts SearchOptions) ([]SearchResult, error)
- func (s *Store) SearchContext(ctx context.Context, query string, opts SearchOptions) ([]SearchResult, error)
- func (s *Store) SearchPreviewsContext(ctx context.Context, query string, opts SearchOptions) ([]SearchPreviewResult, error)
- func (s *Store) SearchPrompts(query string, project string, limit int) ([]Prompt, error)
- func (s *Store) SessionObservations(sessionID string, limit int) ([]Observation, error)
- func (s *Store) SkipAckNonEnrolledMutations(targetKey string) (int64, error)
- func (s *Store) StartSession(id, project, directory string) error
- func (s *Store) Stats() (*Stats, error)
- func (s *Store) StatsProject(project string) (*Stats, error)
- func (s *Store) Timeline(observationID int64, before, after int) (*TimelineResult, error)
- func (s *Store) UnenrollProject(project string) error
- func (s *Store) UnpinObservation(id int64) error
- func (s *Store) UpdateObservation(id int64, p UpdateObservationParams) (*Observation, error)
- type SyncMutation
- type SyncMutationPayloadValidation
- type SyncMutationQuarantineAction
- type SyncMutationQuarantineReport
- type SyncMutationTitleRepairAction
- type SyncMutationTitleRepairReport
- type SyncState
- type TimelineEntry
- type TimelineResult
- type TruncationMetadata
- type UpdateObservationParams
Constants ¶
const ( RelationPending = "pending" RelationRelated = "related" RelationCompatible = "compatible" RelationScoped = "scoped" RelationConflictsWith = "conflicts_with" RelationSupersedes = "supersedes" RelationNotConflict = "not_conflict" )
Valid relation type values. Type compatibility is NOT enforced in Phase 1; the agent does that judgment.
const ( JudgmentStatusPending = "pending" JudgmentStatusJudged = "judged" JudgmentStatusOrphaned = "orphaned" JudgmentStatusIgnored = "ignored" )
Valid judgment_status values.
const ( SessionOwnershipProjectOwned = "project_owned" )
const ( ObservationStateActive = "active" ObservationStateNeedsReview = "needs_review" )
const ( DefaultSyncTargetKey = "cloud" LocalChunkTargetKey = "local" SyncLifecycleIdle = "idle" SyncLifecyclePending = "pending" SyncLifecycleRunning = "running" SyncLifecycleHealthy = "healthy" SyncLifecycleDegraded = "degraded" SyncEntitySession = "session" SyncEntityObservation = "observation" SyncEntityPrompt = "prompt" SyncEntityRelation = "relation" SyncOpUpsert = "upsert" SyncOpDelete = "delete" SyncSourceLocal = "local" SyncSourceRemote = "remote" SyncSessionIdentityInvalidReasonCode = "sync_session_identity_invalid" )
const ( SyncMutationDispositionPending = "pending" SyncMutationDispositionQuarantined = "quarantined" )
const ( UpgradeStagePlanned = "planned" UpgradeStageDoctorReady = "doctor_ready" UpgradeStageDoctorBlocked = "doctor_blocked" UpgradeStageRepairApplied = "repair_applied" UpgradeStageBootstrapEnrolled = "bootstrap_enrolled" UpgradeStageBootstrapPushed = "bootstrap_pushed" UpgradeStageBootstrapVerified = "bootstrap_verified" UpgradeStageRolledBack = "rolled_back" UpgradeRepairClassNone = "none" UpgradeRepairClassReady = "ready" UpgradeRepairClassRepairable = "repairable" UpgradeRepairClassBlocked = "blocked" UpgradeRepairClassPolicy = "policy" )
const ( UpgradeReasonRepairableLegacyMutationPayload = "upgrade_repairable_legacy_mutation_payload" UpgradeReasonBlockedLegacyMutationManual = "upgrade_blocked_legacy_mutation_manual" )
const ( // RescueBlockedOwnedByOtherProject means the row itself already belongs to a // project other than the target. RescueBlockedOwnedByOtherProject = "owned_by_other_project" // RescueBlockedSessionOwnedByOtherProject means the row is unowned but its // parent session belongs to another project, so moving it would split it. RescueBlockedSessionOwnedByOtherProject = "session_owned_by_other_project" // RescueBlockedDependentRecordOwnedByOtherProject means an unowned session // was left in place because it parents a record owned by another project. RescueBlockedDependentRecordOwnedByOtherProject = "dependent_record_owned_by_other_project" // RescueBlockedMissing means the requested row does not exist. RescueBlockedMissing = "missing" )
Reasons a record or session was left behind by a rescue.
const DefaultScanLimit = 100
DefaultScanLimit bounds each conflict scan page and its FTS candidate queries.
const RescueOwnershipCommand = "engram projects rescue-ownership"
RescueOwnershipCommand is the repair an operator can always run, whatever the server authorization configuration is: it reaches the local store directly and never goes through the HTTP endpoint. Every ownership error names it, so the failure carries its own remedy instead of pointing at a changelog.
Variables ¶
var ( ErrSessionNotFound = errors.New("session not found") ErrSessionIDRequired = errors.New("session id is required") ErrSessionAlreadyEnded = errors.New("session has already ended") ErrSessionHasObservations = errors.New("session still has observations") ErrSessionDeleteBlocked = errors.New("session deletion is blocked while cloud sync enrollment is active") ErrObservationNotFound = errors.New("observation not found") ErrPromptNotFound = errors.New("prompt not found") ErrProjectNotFound = errors.New("project not found") ErrProjectRequired = errors.New("project identity is required") ErrInvalidSessionOwnershipMode = errors.New("invalid session ownership mode") ErrSessionOwnershipMismatch = errors.New("session ownership does not match write project") ErrProjectRescueInvalidRequest = errors.New("project rescue request is invalid") // ErrProjectOwnershipAmbiguous is returned when an unowned session cannot // adopt a write's project because it already parents records owned by a // different one. Guessing there would split a record from its session. ErrProjectOwnershipAmbiguous = errors.New("session project ownership is ambiguous") ErrObservationProjectImmutable = errors.New("observation project cannot be reassigned") ErrObservationTitleRequired = errors.New("observation title is required") ErrObservationContentRequired = errors.New("observation content is required") ErrPromptContentRequired = errors.New("prompt content is required") )
Sentinel errors returned by Store operations so callers can use errors.Is.
var ( // ErrRelationFKMissing is returned by applyRelationUpsertTx when one or // both observations referenced by the relation payload do not exist locally // yet. The caller must write the mutation to sync_apply_deferred and ACK // the sequence so the cursor does not stall. ErrRelationFKMissing = errors.New("relation FK precondition not met: referenced observation missing") // ErrCrossProjectRelation is returned by JudgeRelation when the source and // target observations belong to different projects. The write is rejected // entirely; no memory_relations row is created and no sync mutation is // enqueued. ErrCrossProjectRelation = errors.New("relation rejected: source and target observations are in different projects") // ErrApplyDead is returned when a deferred relation payload cannot be // decoded or fails a hard validation. The row is written to // sync_apply_deferred with apply_status='dead' and is never retried // automatically; Phase 3 adds a republish CLI. ErrApplyDead = errors.New("relation apply permanently failed: payload invalid or undecodable") // ErrPulledSessionIdentityInvalid identifies an invalid identity after successful decoding and legacy fallback. ErrPulledSessionIdentityInvalid = errors.New("pulled session identity is invalid") )
Sentinel errors for relation sync apply path (Phase 2).
var ErrDatabaseGenerationChanged = errors.New("Engram database generation changed; restart Engram")
ErrDatabaseGenerationChanged means Engram's SQLite files were replaced while this process was running. Restart Engram before accessing the store again.
var ErrSemanticPromptBuilderRequired = errors.New("semantic scan requires a non-nil BuildPrompt function")
ErrSemanticPromptBuilderRequired is returned by ScanProject when ScanOptions.Semantic is true but ScanOptions.BuildPrompt is nil.
var ErrSemanticRunnerRequired = errors.New("semantic scan requires a non-nil Runner")
ErrSemanticRunnerRequired is returned by ScanProject when ScanOptions.Semantic is true but ScanOptions.Runner is nil.
Functions ¶
func ClassifyTool ¶
ClassifyTool returns the observation type for a given tool name.
func ExtractLearnings ¶
ExtractLearnings parses structured learning items from text. It looks for sections like "## Key Learnings:" or "## Aprendizajes Clave:" and extracts numbered (1. text) or bullet (- text) items. Returns learnings from the LAST matching section (most recent output).
func NormalizeProject ¶
NormalizeProject applies canonical project name normalization: lowercase + trim whitespace + collapse consecutive hyphens/underscores. Returns the normalized name and a warning message if the name was changed (empty string if no change was needed). Exported so MCP and CLI handlers can surface the warning to users.
func SuggestTopicKey ¶
SuggestTopicKey generates a stable topic key suggestion from type/title/content. It infers a topic family (e.g. architecture/*, bug/*) and then appends a normalized segment from title/content for stable cross-session keys.
func ValidateObservationTitle ¶
ValidateObservationTitle is the one definition of "an observation has a usable title", shared by every write path so the CLI, MCP and HTTP entry points can reject a titleless write before they create a session or open a transaction. It mirrors what ValidateSyncMutationPayload requires of an observation upsert: cloud sync rejects a payload whose title is empty, and because the mutation queue is an ordered log, one rejected row blocks every later mutation for the same project.
Types ¶
type AddObservationParams ¶
type AddObservationParams struct {
SessionID string `json:"session_id"`
Type string `json:"type"`
Title string `json:"title"`
Content string `json:"content"`
ToolName string `json:"tool_name,omitempty"`
Project string `json:"project,omitempty"`
Scope string `json:"scope,omitempty"`
TopicKey string `json:"topic_key,omitempty"`
}
type AddPromptParams ¶
type Candidate ¶
type Candidate struct {
// ID is the integer primary key of the candidate observation.
ID int64
// SyncID is the TEXT sync_id of the candidate observation.
SyncID string
// Title is the candidate's title.
Title string
// Type is the candidate's observation type.
Type string
// TopicKey is the candidate's topic_key (may be nil).
TopicKey *string
// Score is the FTS5 BM25 rank; lower values are better matches.
Score float64
// JudgmentID is the sync_id of the pending memory_relations row created
// for this (source, candidate) pair.
JudgmentID string
}
Candidate represents a potential conflict candidate surfaced by FindCandidates.
type CandidateOptions ¶
type CandidateOptions struct {
// Project filters candidates to the same project as the saved observation.
Project string
// Scope filters candidates to the same scope as the saved observation.
Scope string
// Type is reserved for Phase 2 type-compatibility filtering; NOT enforced Phase 1.
Type string
// Limit caps the number of candidates returned. Default 3 when nil or <=0.
Limit int
// BM25MaxRank is the largest acceptable raw FTS5 BM25 rank. Smaller ranks
// are better matches, so candidates whose rank is greater are excluded. nil
// uses 0.0, which retains ordinary negative FTS5 ranks.
BM25MaxRank *float64
// BM25Floor preserves the deprecated legacy minimum-rank behavior. Candidates
// below this value are excluded. It cannot be combined with BM25MaxRank.
BM25Floor *float64
// Query optionally overrides the saved observation title as the candidate
// query source. Empty uses the saved title.
Query string
// SkipInsert controls whether FindCandidates inserts pending relation rows.
// When true, candidates are returned but NO rows are written to memory_relations.
// Default false preserves the existing behavior (rows are inserted).
SkipInsert bool
}
CandidateOptions controls the FindCandidates query.
type CloudSyncSummary ¶
type CloudSyncSummary struct {
LastSuccessAt string
PendingMutations int64
LastError string
ReasonCode string
}
CloudSyncSummary is the aggregate local cloud state shown when no project is selected.
type CloudUpgradeLegacyMutationFinding ¶
type CloudUpgradeLegacyMutationFinding struct {
Seq int64 `json:"seq"`
Entity string `json:"entity"`
Op string `json:"op"`
ReasonCode string `json:"reason_code"`
Message string `json:"message"`
Repairable bool `json:"repairable"`
RepairHint string `json:"repair_hint,omitempty"`
EntityKey string `json:"entity_key,omitempty"`
TargetKey string `json:"target_key,omitempty"`
Project string `json:"project,omitempty"`
}
type CloudUpgradeLegacyMutationReport ¶
type CloudUpgradeLegacyMutationReport struct {
Project string `json:"project"`
RepairableCount int `json:"repairable_count"`
BlockedCount int `json:"blocked_count"`
Findings []CloudUpgradeLegacyMutationFinding `json:"findings,omitempty"`
}
type CloudUpgradeSnapshot ¶
type CloudUpgradeState ¶
type CloudUpgradeState struct {
Project string `json:"project"`
Stage string `json:"stage"`
RepairClass string `json:"repair_class"`
Snapshot CloudUpgradeSnapshot `json:"snapshot"`
LastErrorCode string `json:"last_error_code,omitempty"`
LastErrorMessage string `json:"last_error_message,omitempty"`
FindingsJSON string `json:"findings_json,omitempty"`
AppliedActions string `json:"applied_actions,omitempty"`
UpdatedAt string `json:"updated_at"`
}
type Config ¶
type Config struct {
DataDir string
MaxObservationLength int
MaxContextResults int
MaxSearchResults int
DedupeWindow time.Duration
}
func DefaultConfig ¶
func FallbackConfig ¶
FallbackConfig returns a Config with the given DataDir and default values. Use this when DefaultConfig fails and you have resolved the home directory through alternative means.
type ContextOptions ¶
type ContextOptions struct {
// MaxBytes caps the complete rendered context in bytes. Zero preserves the
// unbounded legacy output; a positive value returns at most that many bytes.
MaxBytes int
// Observations caps the "### Recent Observations" section (unpinned).
Observations int
// Prompts caps the "### Recent User Prompts" section.
Prompts int
// Sessions caps the "### Recent Sessions" section.
Sessions int
// Pinned caps the "### Pinned" section.
Pinned int
// Compact drops the inline content preview from observation-shaped
// bullets — both "### Pinned" and "### Recent Observations" render
// `- [type] **title**` instead of `- [type] **title**: <300 chars of
// body>`. Sessions and prompts bullets are unaffected.
Compact bool
}
ContextOptions tunes FormatContextWithOptions, capping how many rows each section of the "## Memory from Previous Sessions" block renders.
Every field follows the same convention:
- 0 uses that section's legacy default.
- >0 caps the section at that many rows.
- <0 omits the section entirely, including its "### ..." header.
The legacy defaults are Sessions 5, Prompts 10, Observations s.cfg.MaxContextResults, and Pinned unlimited (no SQL LIMIT) — exactly what FormatContext has always produced, so a zero-value ContextOptions{} reproduces FormatContext's output byte-for-byte. See issue #163 (bounded-size injection).
type DeferredRow ¶
type DeferredRow struct {
SyncID string `json:"sync_id"`
Entity string `json:"entity"`
TargetKey string `json:"target_key"`
Project string `json:"project"`
ScopeClass string `json:"scope_class"`
RemoteSeq int64 `json:"remote_seq,omitempty"`
EntityKey string `json:"entity_key,omitempty"`
Op string `json:"op,omitempty"`
ReasonCode string `json:"reason_code,omitempty"`
Payload map[string]any `json:"payload,omitempty"`
PayloadRaw string `json:"payload_raw"`
PayloadValid bool `json:"payload_valid"`
ApplyStatus string `json:"apply_status"`
RetryCount int `json:"retry_count"`
LastError *string `json:"last_error,omitempty"`
LastAttemptedAt *string `json:"last_attempted_at,omitempty"`
FirstSeenAt string `json:"first_seen_at"`
}
DeferredRow represents a row in sync_apply_deferred with the payload decoded.
type DeleteProjectResult ¶
type DeleteProjectResult struct {
Project string `json:"project"`
ObservationsDeleted int64 `json:"observations_deleted"`
PromptsDeleted int64 `json:"prompts_deleted"`
SessionsDeleted int64 `json:"sessions_deleted"`
HardDelete bool `json:"hard_delete"`
}
DeleteProjectResult summarises a cascade project deletion.
type DiagnosticSessionEvidence ¶
type DiagnosticSessionEvidence struct {
ID string `json:"id"`
Project string `json:"project"`
OwnershipMode string `json:"ownership_mode"`
Directory string `json:"directory"`
Name string `json:"name"`
}
DiagnosticSessionEvidence is the read-only session projection used by operational diagnostics. It intentionally avoids observation/prompt payloads.
type EnrolledProject ¶
type EnrolledProject struct {
Project string `json:"project"`
EnrolledAt string `json:"enrolled_at"`
}
EnrolledProject represents a project enrolled for cloud sync.
type ExportData ¶
type ExportData struct {
Version string `json:"version"`
ExportedAt string `json:"exported_at"`
Sessions []Session `json:"sessions"`
Observations []Observation `json:"observations"`
Prompts []Prompt `json:"prompts"`
}
ExportData is the full serializable dump of the engram database.
type ImportResult ¶
type InvalidSessionIdentityEvidence ¶
type InvalidSessionIdentityEvidence struct {
Project string `json:"project"`
SessionID string `json:"session_id"`
ObservationCount int64 `json:"observation_count"`
PromptCount int64 `json:"prompt_count"`
InvalidJournalCount int64 `json:"invalid_journal_count"`
}
InvalidSessionIdentityEvidence describes a corrupt source session and the dependent local data that cannot be repaired without a canonical ID.
type JudgeBySemanticParams ¶
type JudgeBySemanticParams struct {
// SourceID is the TEXT sync_id of the source observation (required).
SourceID string
// TargetID is the TEXT sync_id of the target observation (required).
TargetID string
// Relation is the verdict verb (required); must be in validRelationVerbs.
// not_conflict is persisted like every other valid semantic verdict.
Relation string
// Confidence is the LLM's self-reported confidence score [0.0, 1.0].
Confidence float64
// Reasoning is the LLM's short explanation.
Reasoning string
// Model is the LLM model identifier. Stored as marked_by_model.
Model string
}
JudgeBySemanticParams holds the inputs for JudgeBySemantic.
type JudgeRelationParams ¶
type JudgeRelationParams struct {
// JudgmentID is the sync_id of the relation row to update (required).
JudgmentID string
// Relation is the verdict verb (required); must be one of validRelationVerbs.
Relation string
// Reason is an optional free-text explanation.
Reason *string
// Evidence is optional free-form JSON or text evidence.
Evidence *string
// Confidence is optional 0..1 confidence score.
Confidence *float64
// MarkedByActor is the actor identifier (e.g. "agent:claude-sonnet-4-6" or "user").
MarkedByActor string
// MarkedByKind is the actor kind ("agent", "human", "system").
MarkedByKind string
// MarkedByModel is the model ID (may be empty for human actors).
MarkedByModel string
// SessionID is the session in which the judgment was made (optional).
SessionID string
}
JudgeRelationParams holds the inputs for JudgeRelation.
type ListDeferredOptions ¶
type ListDeferredOptions struct {
// Status filters by apply_status. Empty means no status filter.
Status string
// Limit caps the number of rows returned. 0 or negative means no limit.
Limit int
// Offset is the pagination offset.
Offset int
}
ListDeferredOptions controls ListDeferred queries.
type ListRelationsOptions ¶
type ListRelationsOptions struct {
// Project filters by the project of the source OR target observation (via JOIN).
// Empty means no project filter (return all).
Project string
// Status filters by judgment_status. Empty means no status filter.
Status string
// SinceTime filters to rows created_at >= SinceTime. Zero value means no filter.
SinceTime time.Time
// Limit caps the number of rows returned. 0 or negative means no limit.
Limit int
// Offset is the pagination offset.
Offset int
// ExcludeNotConflict omits persisted not_conflict verdicts from conflict-facing views.
ExcludeNotConflict bool
}
ListRelationsOptions controls ListRelations and CountRelations queries.
type MergeResult ¶
type MergeResult struct {
Canonical string `json:"canonical"`
SourcesMerged []string `json:"sources_merged"`
ObservationsUpdated int64 `json:"observations_updated"`
SessionsUpdated int64 `json:"sessions_updated"`
PromptsUpdated int64 `json:"prompts_updated"`
}
MergeResult summarizes the result of merging multiple project name variants into a single canonical project name.
type MigrateResult ¶
type Observation ¶
type Observation struct {
ID int64 `json:"id"`
SyncID string `json:"sync_id"`
SessionID string `json:"session_id"`
Type string `json:"type"`
Title string `json:"title"`
Content string `json:"content"`
ToolName *string `json:"tool_name,omitempty"`
Project *string `json:"project,omitempty"`
Scope string `json:"scope"`
TopicKey *string `json:"topic_key,omitempty"`
RevisionCount int `json:"revision_count"`
DuplicateCount int `json:"duplicate_count"`
LastSeenAt *string `json:"last_seen_at,omitempty"`
ReviewAfter *string `json:"review_after,omitempty"`
Pinned bool `json:"-"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
DeletedAt *string `json:"deleted_at,omitempty"`
}
func (Observation) State ¶
func (o Observation) State() string
State returns the virtual lifecycle state derived from review_after.
type ObservationRelations ¶
type ObservationRelations struct {
// AsSource holds relations where this observation is source_id.
AsSource []Relation
// AsTarget holds relations where this observation is target_id.
AsTarget []Relation
}
ObservationRelations groups relations for a single observation, split by role.
type ObservationRequiredFieldsEvidence ¶
type ObservationRequiredFieldsEvidence struct {
ID int64 `json:"id"`
SyncID string `json:"sync_id"`
Project string `json:"project"`
MissingFields []string `json:"missing_fields"`
}
ObservationRequiredFieldsEvidence identifies a corrupt source observation without exposing its content in diagnostic output.
type ObservationSnippet ¶
ObservationSnippet carries the fields needed by BuildPrompt to construct an LLM comparison prompt without importing internal/llm from this package.
type ObservationSourceTitleRepairAction ¶
type ObservationSourceTitleRepairAction struct {
ID int64 `json:"id"`
SyncID string `json:"sync_id"`
Project string `json:"project"`
Title string `json:"title"`
}
ObservationSourceTitleRepairAction records a title derived from a corrupt source observation's first non-empty line.
type ObservationSourceTitleRepairReport ¶
type ObservationSourceTitleRepairReport struct {
Project string `json:"project,omitempty"`
Applied bool `json:"applied"`
Actions []ObservationSourceTitleRepairAction `json:"actions"`
BackupPath string `json:"backup_path,omitempty"`
}
ObservationSourceTitleRepairReport is the local recovery result for source observation title repairs. A backup is created only when an apply changes rows.
type OrphanedObservationSessionEvidence ¶
type OrphanedObservationSessionEvidence struct {
Project string `json:"project"`
SessionID string `json:"session_id"`
ObservationCount int64 `json:"observation_count"`
}
OrphanedObservationSessionEvidence identifies observations whose stored session reference has no matching local session. It is grouped so diagnostics can report the affected reference without exposing observation payloads.
type PassiveCaptureParams ¶
type PassiveCaptureParams struct {
SessionID string `json:"session_id"`
Content string `json:"content"`
Project string `json:"project,omitempty"`
Source string `json:"source,omitempty"` // e.g. "subagent-stop", "session-end"
}
PassiveCaptureParams holds the input for passive memory capture.
type PassiveCaptureResult ¶
type PassiveCaptureResult struct {
Extracted int `json:"extracted"` // Total learnings found in text
Saved int `json:"saved"` // New observations created
Duplicates int `json:"duplicates"` // Skipped because already existed
}
PassiveCaptureResult holds the output of passive memory capture.
type ProjectNameCount ¶
ProjectNameCount holds a project name and how many observations it has.
type ProjectRescueBlocked ¶
type ProjectRescueBlocked struct {
// Kind is "session", "observation", or "prompt".
Kind string `json:"kind"`
// ID is the session id or the decimal record id.
ID string `json:"id"`
// Reason is one of the RescueBlocked* constants.
Reason string `json:"reason"`
// OwnedBy is the conflicting project, when one is known.
OwnedBy string `json:"owned_by,omitempty"`
}
ProjectRescueBlocked names one row the rescue deliberately did not move, and why. It is what lets an operator tell "everything moved" apart from "some things were left behind" without guessing from counters.
type ProjectRescueParams ¶
type ProjectRescueParams struct {
TargetProject string
ObservationIDs []int64
SessionIDs []string
PromptIDs []int64
}
ProjectRescueParams identifies historical rows whose missing project ownership or blank same-project ownership mode was explicitly confirmed by an operator.
type ProjectRescueResult ¶
type ProjectRescueResult struct {
RescuedObservations int64 `json:"rescued_observations"`
RescuedSessions int64 `json:"rescued_sessions"`
RescuedPrompts int64 `json:"rescued_prompts"`
ConflictingRecords int64 `json:"conflicting_records"`
SkippedRecords int64 `json:"skipped_records"`
Journaled bool `json:"journaled"`
// Complete is true only when every requested row now belongs to the target
// project and nothing was left behind.
Complete bool `json:"complete"`
// Blocked lists exactly what was left behind, and why.
Blocked []ProjectRescueBlocked `json:"blocked"`
}
ProjectRescueResult reports local ownership recovery. Journaled means a canonical pending local mutation exists after the call, whether newly inserted or already pending; it does not imply a cloud acknowledgement.
func (ProjectRescueResult) Rescued ¶
func (r ProjectRescueResult) Rescued() int64
type ProjectStats ¶
type ProjectStats struct {
Name string `json:"name"`
ObservationCount int `json:"observation_count"`
SessionCount int `json:"session_count"`
PromptCount int `json:"prompt_count"`
Directories []string `json:"directories"` // unique directories from sessions
}
ProjectStats holds aggregate statistics for a single project.
type PruneResult ¶
type PruneResult struct {
Project string `json:"project"`
SessionsDeleted int64 `json:"sessions_deleted"`
PromptsDeleted int64 `json:"prompts_deleted"`
}
PruneResult holds the outcome of pruning a single project.
type QuarantinedPulledSessionEvidence ¶
type QuarantinedPulledSessionEvidence struct {
SyncID string `json:"sync_id"`
TargetKey string `json:"target_key"`
Project string `json:"project"`
EntityKey string `json:"entity_key"`
Op string `json:"op"`
RemoteSeq int64 `json:"remote_seq"`
ReasonCode string `json:"reason_code"`
FirstSeenAt string `json:"first_seen_at"`
}
QuarantinedPulledSessionEvidence describes a pulled session mutation that was skipped because its identity is blank or inconsistent. The pull cursor advances past such a mutation instead of halting, so this row is the only record that remote data was dropped.
type Relation ¶
type Relation struct {
ID int64 `json:"id"`
SyncID string `json:"sync_id"`
SourceID string `json:"source_id"`
TargetID string `json:"target_id"`
Relation string `json:"relation"`
Reason *string `json:"reason,omitempty"`
Evidence *string `json:"evidence,omitempty"`
Confidence *float64 `json:"confidence,omitempty"`
JudgmentStatus string `json:"judgment_status"`
MarkedByActor *string `json:"marked_by_actor,omitempty"`
MarkedByKind *string `json:"marked_by_kind,omitempty"`
MarkedByModel *string `json:"marked_by_model,omitempty"`
SessionID *string `json:"session_id,omitempty"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
// Annotation fields — populated by GetRelationsForObservations via LEFT JOIN.
// Excluded from JSON output (used only for in-process annotation building).
// REQ-005, REQ-012 | Design §7, §8.
SourceIntID int64 `json:"-"` // integer primary key of source observation
SourceTitle string `json:"-"` // title of source observation; empty if missing/deleted
SourceMissing bool `json:"-"` // true if source is soft-deleted or not found
TargetIntID int64 `json:"-"` // integer primary key of target observation
TargetTitle string `json:"-"` // title of target observation; empty if missing/deleted
TargetMissing bool `json:"-"` // true if target is soft-deleted or not found
}
Relation represents a row in memory_relations.
type RelationListItem ¶
type RelationListItem struct {
ID int64 `json:"id"`
SyncID string `json:"sync_id"`
Relation string `json:"relation"`
JudgmentStatus string `json:"judgment_status"`
SourceID string `json:"source_id"`
SourceTitle string `json:"source_title"`
TargetID string `json:"target_id"`
TargetTitle string `json:"target_title"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
RelationListItem represents a single row in a ListRelations result, enriched with observation titles via JOIN (no full Relation struct).
type RelationStats ¶
type RelationStats struct {
Project string `json:"project"`
ByRelation map[string]int `json:"by_relation"`
ByJudgmentStatus map[string]int `json:"by_judgment_status"`
DeferredCount int `json:"deferred"`
DeadCount int `json:"dead"`
}
RelationStats holds aggregate counts of relations for a project.
type ReplayDeferredResult ¶
ReplayDeferredResult holds counts returned by ReplayDeferred.
type SQLiteLockSnapshot ¶
type SQLiteLockSnapshot struct {
JournalMode string `json:"journal_mode"`
BusyTimeoutMS int `json:"busy_timeout_ms"`
CheckpointBusy int `json:"checkpoint_busy"`
CheckpointLog int `json:"checkpoint_log"`
CheckpointedFrames int `json:"checkpointed_frames"`
}
SQLiteLockSnapshot captures conservative SQLite lock/contention indicators. wal_checkpoint(PASSIVE) is an observational probe for this diagnostic surface; callers must not interpret it as a repair action.
type SaveRelationParams ¶
type SaveRelationParams struct {
// SyncID is the unique identifier for this relation row (format: rel-<16hex>).
SyncID string
// SourceID is the TEXT sync_id of the source observation.
SourceID string
// TargetID is the TEXT sync_id of the target observation.
TargetID string
}
SaveRelationParams holds the inputs for SaveRelation.
type ScanOptions ¶
type ScanOptions struct {
// Project is required — scopes the observation walk.
Project string
// Since filters observations to created_at >= Since. Zero value means no filter.
Since time.Time
// Limit caps this scan page. Zero uses DefaultScanLimit.
Limit int
// Cursor resumes after this observation ID. Zero starts from the first row.
Cursor int64
// Apply controls whether new relation rows are inserted.
// When false (dry-run, default), candidates are reported but not written.
Apply bool
// MaxInsert caps the number of new relation rows inserted in a single Apply run.
// Default 100 when 0 or negative.
MaxInsert int
// Semantic controls whether the worker pool LLM-judge step runs.
// When false (default), ScanProject behaves exactly as Phase 3.
Semantic bool
// Concurrency is the worker pool size for semantic calls. Default 5 if 0.
Concurrency int
// TimeoutPerCall is the per-pair context timeout for runner.Compare.
// Default 60s if zero.
TimeoutPerCall time.Duration
// MaxSemantic caps the number of LLM calls in a single semantic scan. Default 100 if 0.
MaxSemantic int
// Runner is the SemanticRunner used for LLM comparison. Required when Semantic=true.
Runner SemanticRunner
// BuildPrompt constructs the LLM prompt for a given (a, b) pair.
// Required when Semantic=true.
BuildPrompt func(a, b ObservationSnippet) string
}
ScanOptions controls a ScanProject call.
type ScanResult ¶
type ScanResult struct {
Project string `json:"project"`
Inspected int `json:"inspected"`
RankedQueries int `json:"ranked_queries"`
CandidatesFound int `json:"candidates_found"`
NextCursor *int64 `json:"next_cursor,omitempty"`
AlreadyRelated int `json:"already_related"`
RelationsInserted int `json:"inserted"`
// Capped means work remains but this result has no continuation cursor. Re-run
// the same incoming observation cursor with a higher applicable cap.
Capped bool `json:"capped"`
DryRun bool `json:"dry_run"`
// Semantic counters — populated only when ScanOptions.Semantic is true.
// Zero-value is safe for existing JSON consumers.
SemanticJudged int `json:"semantic_judged"`
SemanticSkipped int `json:"semantic_skipped"`
SemanticErrors int `json:"semantic_errors"`
}
ScanResult holds the output of a ScanProject call.
type SearchOptions ¶
type SearchPreviewResult ¶
type SearchPreviewResult struct {
ID int64 `json:"id"`
SyncID string `json:"sync_id"`
Type string `json:"type"`
Title string `json:"title"`
Preview string `json:"preview"`
Truncated bool `json:"truncated"`
Project *string `json:"project,omitempty"`
Scope string `json:"scope"`
ReviewAfter *string `json:"review_after,omitempty"`
Pinned bool `json:"-"`
CreatedAt string `json:"created_at"`
Rank float64 `json:"rank"`
}
SearchPreviewResult is the bounded result shape used by preview-only callers. Content is deliberately excluded so those callers do not hydrate full bodies.
func (SearchPreviewResult) State ¶
func (r SearchPreviewResult) State() string
State returns the virtual lifecycle state derived from review_after.
type SearchResult ¶
type SearchResult struct {
Observation
Rank float64 `json:"rank"`
}
type SemanticRunner ¶
type SemanticRunner interface {
Compare(ctx context.Context, prompt string) (SemanticVerdict, error)
}
SemanticRunner is a duck-typed interface satisfied by *llm.ClaudeRunner and *llm.OpenCodeRunner without requiring this package to import internal/llm. Any value whose Compare method matches this signature satisfies the interface.
type SemanticVerdict ¶
type SemanticVerdict struct {
// Relation is one of: conflicts_with, supersedes, scoped, related, compatible, not_conflict.
Relation string
// Confidence is a 0.0–1.0 score from the runner.
Confidence float64
// Reasoning is a short human-readable explanation (≤200 chars).
Reasoning string
// Model is the model identifier reported by the CLI (e.g. "claude-haiku-4-5").
Model string
// DurationMS is wall-clock time for the CLI invocation in milliseconds.
DurationMS int64
}
SemanticVerdict is the result of a semantic comparison between two observations. It mirrors llm.Verdict but lives in this package to avoid a store→llm import cycle.
type SessionProjectReclassificationResult ¶
type SessionProjectReclassificationResult struct {
Counts SessionProjectReclassificationCounts
BackupPath string
}
type SessionSummary ¶
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
func (*Store) AckSyncMutationSeqs ¶
AckSyncMutationSeqs acknowledges specific mutation sequence numbers without requiring them to be contiguous.
func (*Store) AckSyncMutations ¶
func (*Store) AcquireSyncLease ¶
func (*Store) ActiveRuntimeSessions ¶
MostRecentActiveSession resolves the active (un-ended) session for a project from the persisted sessions table. It returns the session ID and ok=true when such a session exists, or ok=false when none does.
This is the cross-process resolution that fixes issue #386: the SessionStart hook registers a UUID session via the HTTP server (POST /sessions) in one process, while mem_save runs in the separate MCP (stdio) process. The two share only the SQLite store, so the active session must be read from disk — never from in-memory state.
Candidate rules:
- Scope to the (normalized) project.
- Scope to the current runtime directory.
- Require ended_at IS NULL — ended sessions are never returned.
- Require recent effective activity. ended_at IS NULL alone means "never closed", not "in use": a session whose process is long gone stays a candidate forever, and two such rows make resolution fail permanently for that project and directory (#1101). Effective activity is the last observation the session recorded, falling back to started_at when it recorded none. The sessions table carries no pid or heartbeat, so liveness is not observable; recency of recorded work is.
- Exclude the manual-save fallback sessions (id LIKE 'manual-save%'); those are created by the fallback path itself and must not be resolved as "the active session", which would make resolution circular.
ActiveRuntimeSessions returns active, non-manual sessions for a project and runtime directories. The directories narrow candidates; callers must not treat them as session identity.
func (*Store) AddObservation ¶
func (s *Store) AddObservation(p AddObservationParams) (int64, error)
func (*Store) AddPromptIfMissing ¶
func (s *Store) AddPromptIfMissing(p AddPromptParams) (int64, bool, error)
func (*Store) AllObservations ¶
func (s *Store) AllObservations(project, scope string, limit int) ([]Observation, error)
AllObservations returns recent observations ordered by most recent first (for TUI browsing).
func (*Store) AllSessions ¶
func (s *Store) AllSessions(project string, limit int) ([]SessionSummary, error)
AllSessions returns recent sessions ordered by most recent first (for TUI browsing). A database upgraded from the schema where sessions.project was nullable still carries rows that identify no project, so the column is read through ifnull(): an unscoped listing reads every session row and must not die on one of them.
func (*Store) ApplyPulledChunk ¶
func (s *Store) ApplyPulledChunk(targetKey, chunkID string, mutations []SyncMutation) error
ApplyPulledChunk atomically applies all mutations contained in a pulled chunk and records the chunk as synced in the same transaction. This guarantees retry safety: a failed chunk import leaves no partial semantic mutations.
It shares ApplyPulledMutation's skip-plus-evidence rule for invalid session identities: such a mutation is quarantined and the rest of the chunk still applies, so one historical blank identity cannot block the chunk forever. A payload that does not even decode stays fail-closed and rolls back the whole chunk, because an undecodable payload is a transport-level fault rather than known-corrupt historical data.
func (*Store) ApplyPulledMutation ¶
func (s *Store) ApplyPulledMutation(targetKey string, mutation SyncMutation) error
ApplyPulledMutation applies one remote mutation and advances the pull cursor.
Session-identity semantics are skip-plus-evidence, not fail-closed. A blank or inconsistent session identity in a pulled mutation is quarantined through deadLetterPulledSessionIdentityTx and the cursor still advances past it. Failing closed here would be a permanent retry loop: servers that predate the identity rule hold historical chunks with blank session IDs, and no local action can ever make such a mutation valid, so halting would pin the cursor forever and block every later mutation behind it. Quarantining keeps the dropped data visible — `engram doctor --check invalid_session_identity` reports it and `engram conflicts deferred` lists the raw row.
Every other apply failure keeps its existing fail-closed behavior.
func (*Store) ApplySessionProjectReclassification ¶
func (s *Store) ApplySessionProjectReclassification(actions []SessionProjectReclassification) (SessionProjectReclassificationResult, error)
func (*Store) BackupSQLite ¶
func (*Store) CanRollbackCloudUpgrade ¶
func (*Store) ClearCloudUpgradeState ¶
func (*Store) CloudSyncSummary ¶
func (s *Store) CloudSyncSummary() (CloudSyncSummary, error)
CloudSyncSummary returns status across project-scoped cloud targets only. It deliberately excludes the legacy global cloud target because explicit cloud sync records state under cloud:<project>.
func (*Store) ContentTruncation ¶
func (s *Store) ContentTruncation(content string) TruncationMetadata
ContentTruncation returns the byte-based truncation metadata used by storage writes.
func (*Store) CountDeferredAndDead ¶
CountDeferredAndDead returns global administrative totals, including legacy unscoped rows that normal scoped imports intentionally leave untouched.
func (*Store) CountDeferredAndDeadForScope ¶
func (s *Store) CountDeferredAndDeadForScope(targetKey, project string) (deferred, dead int, err error)
CountDeferredAndDeadForScope returns queue totals for an optional target and project. Scoped totals exclude legacy-unscoped rows; empty filters return the global administrative totals.
func (*Store) CountObservationsForProject ¶
CountObservationsForProject returns the number of non-deleted observations for the given project name. Used by handleSave for the similar-project warning instead of the heavier ListProjectsWithStats.
func (*Store) CountPendingNonEnrolledSyncMutations ¶
func (s *Store) CountPendingNonEnrolledSyncMutations(targetKey string) ([]PendingSyncMutationProjectCount, error)
func (*Store) CountRelationSyncMutations ¶
CountRelationSyncMutations returns the number of sync_mutations rows whose entity is NOT 'session', 'observation', or 'prompt'. Used by integration tests to verify the enrollment gate: an UNENROLLED project must never enqueue relation sync mutations (the enqueue in JudgeBySemantic/JudgeRelation is guarded by an enrollment check). The test that calls this uses an unenrolled store, so the count must remain zero.
Note: relation sync mutations ARE valid for enrolled projects (#313/#379/#383 enabled cloud relation sync; #496 extends it with backfill). This function is not a blanket "relations are local-only" check — it is an enrollment-gate regression guard scoped to the unenrolled test context that uses it.
func (*Store) CountRelations ¶
func (s *Store) CountRelations(opts ListRelationsOptions) (int, error)
CountRelations returns the total number of relation rows matching opts. Uses the same WHERE conditions as ListRelations.
func (*Store) CreateSession ¶
func (*Store) CreateSessionWithOwnershipMode ¶
CreateSessionWithOwnershipMode creates a session with an explicit persisted ownership policy. CreateSession remains the compatibility wrapper for shared runtime sessions.
func (*Store) DB ¶
DB returns the underlying *sql.DB. Intended for test helpers and integration tests that need to inject raw rows (e.g. legacy data with non-normalized project names) without going through the Store's public API.
func (*Store) DeleteObservation ¶
func (*Store) DeleteProject ¶
func (s *Store) DeleteProject(project string, hardDelete bool) (*DeleteProjectResult, error)
DeleteProject removes all data associated with a project in a single transaction.
When hardDelete is true: observation rows are permanently removed, prompts are hard-deleted, and sessions are hard-deleted. memory_relations that reference any removed observation are marked orphaned (audit history).
When hardDelete is false: observations are soft-deleted (deleted_at set), and prompts are hard-deleted. Sessions are NOT removed in this path because observations.session_id is a NOT NULL FK to sessions — removing sessions while soft-deleted observation rows still reference them would violate the FK constraint. The session rows remain and can be cleaned up with engram delete session <id> once the observations are purged.
Returns ErrProjectNotFound when no sessions or observations exist for the given project name.
func (*Store) DeletePrompt ¶
DeletePrompt hard-deletes a single prompt by ID and records a sync tombstone. It returns ErrPromptNotFound if no prompt with that ID exists.
func (*Store) DeleteSession ¶
DeleteSession hard-deletes a session and its prompts. It returns ErrSessionHasObservations if the session has any observations (including soft-deleted ones) to prevent orphaned rows. It returns ErrSessionNotFound if no session with that ID exists.
When the session belongs to an enrolled project, this operation also enqueues a session/delete mutation so cloud replicas can remove the session.
func (*Store) DiagnoseCloudUpgradeLegacyMutations ¶
func (s *Store) DiagnoseCloudUpgradeLegacyMutations(project string) (CloudUpgradeLegacyMutationReport, error)
func (*Store) EnrollProject ¶
EnrollProject registers a project for cloud sync. Idempotent — re-enrolling an already-enrolled project is a no-op.
func (*Store) EnsureEnrolledProjectSyncMutations ¶
EnsureEnrolledProjectSyncMutations repairs legacy enrolled-project journal entries before a sync operation reads them. A successful repair is memoized for this Store's lifetime; failures are returned to callers and retried by a later synchronization attempt.
func (*Store) EstimateSessionProjectReclassification ¶
func (s *Store) EstimateSessionProjectReclassification(actions []SessionProjectReclassification) (SessionProjectReclassificationCounts, error)
func (*Store) Export ¶
func (s *Store) Export() (*ExportData, error)
func (*Store) ExportProject ¶
func (s *Store) ExportProject(project string) (*ExportData, error)
ExportProject returns an export restricted to records relevant to a single normalized project. This avoids full-database exports when only one project needs to sync.
func (*Store) ExportRelationMutations ¶
func (s *Store) ExportRelationMutations(project string) ([]SyncMutation, error)
ExportRelationMutations returns relation upsert mutations for non-orphaned relation rows whose source and target observations are available locally.
func (*Store) FindCandidates ¶
func (s *Store) FindCandidates(savedID int64, opts CandidateOptions) ([]Candidate, error)
FindCandidates runs a post-transaction FTS5 candidate query for the given savedID and returns at most opts.Limit candidates above the BM25 floor.
For each candidate, a pending memory_relations row is inserted and the row's sync_id is exposed as Candidate.JudgmentID. Candidates with an existing judged relation in either direction are excluded.
Errors from this method are expected to be logged and swallowed by callers — detection failure must never fail the originating save.
func (*Store) FormatCompactionContext ¶
FormatCompactionContext returns runtime context that is strictly limited to one persisted session. The session's project is derived from the store and is never supplied by the caller.
func (*Store) FormatContext ¶
FormatContext is a thin wrapper around FormatContextWithOptions using a zero-value ContextOptions, preserving the pre-ContextOptions call signature so existing callers and tests keep working unchanged.
func (*Store) FormatContextWithOptions ¶
func (s *Store) FormatContextWithOptions(project, scope string, opts ContextOptions) (string, error)
FormatContextWithOptions renders the "## Memory from Previous Sessions" markdown block for the given project/scope, honoring the per-section caps and Compact rendering in opts. See ContextOptions for the cap convention.
func (*Store) GetCloudUpgradeState ¶
func (s *Store) GetCloudUpgradeState(project string) (*CloudUpgradeState, error)
func (*Store) GetDeferred ¶
func (s *Store) GetDeferred(syncID string) (DeferredRow, error)
GetDeferred returns a single row from sync_apply_deferred by sync_id. Returns an error wrapping "not found" when no row exists (matches FindCandidates style).
func (*Store) GetObservation ¶
func (s *Store) GetObservation(id int64) (*Observation, error)
func (*Store) GetObservationBySyncID ¶
func (s *Store) GetObservationBySyncID(syncID string) (*Observation, error)
func (*Store) GetRelation ¶
GetRelation retrieves a single relation row by its sync_id.
func (*Store) GetRelationByIntID ¶
func (s *Store) GetRelationByIntID(id int64) (*RelationListItem, error)
GetRelationByIntID retrieves a single relation enriched with source/target observation titles by its integer primary key. Returns a *RelationListItem (same shape as ListRelations rows) so HTTP handlers share one response type. Returns an error wrapping "not found" when the id does not exist.
func (*Store) GetRelationStats ¶
func (s *Store) GetRelationStats(project string) (RelationStats, error)
GetRelationStats returns aggregate counts for a project's relations plus the deferred and dead queue totals. Two queries are executed: one GROUP BY and one delegated to CountDeferredAndDead.
func (*Store) GetRelationsForObservations ¶
func (s *Store) GetRelationsForObservations(syncIDs []string) (map[string]ObservationRelations, error)
GetRelationsForObservations returns a map of observation sync_id → ObservationRelations for all observations in syncIDs. Relations with judgment_status='orphaned' are excluded.
A single SQL query with IN/OR and LEFT JOINs avoids N+1 queries. The returned Relation values are enriched with source/target integer IDs and titles via LEFT JOIN, used by the MCP annotation builder (REQ-005, REQ-012). Missing or soft-deleted observations set the corresponding *Missing flag to true.
func (*Store) GetRelationsForObservationsContext ¶
func (s *Store) GetRelationsForObservationsContext(ctx context.Context, syncIDs []string) (map[string]ObservationRelations, error)
GetRelationsForObservationsContext enriches observations with relations while honoring cancellation from the caller, including while materializing rows.
func (*Store) GetSyncedChunks ¶
GetSyncedChunks returns local-target chunk IDs for backwards compatibility.
func (*Store) GetSyncedChunksForTarget ¶
GetSyncedChunksForTarget returns chunk IDs tracked for a specific sync target.
func (*Store) HasPendingSyncMutationsForProject ¶
func (*Store) HasProjectOwnedSessions ¶
HasProjectOwnedSessions reports whether any session requires ownership-mode sync.
func (*Store) HasProjectOwnedSessionsForProject ¶
HasProjectOwnedSessionsForProject reports whether project requires ownership-mode sync.
func (*Store) Import ¶
func (s *Store) Import(data *ExportData) (*ImportResult, error)
func (*Store) IsProjectEnrolled ¶
IsProjectEnrolled returns true if the given project is enrolled for cloud sync.
func (*Store) JudgeBySemantic ¶
func (s *Store) JudgeBySemantic(p JudgeBySemanticParams) (string, error)
JudgeBySemantic persists a semantic verdict produced by an AgentRunner into the memory_relations table with system provenance (marked_by_kind="system", marked_by_actor="engram", marked_by_model=params.Model).
Idempotency: if a row already exists for (source_id, target_id) in either direction, the existing row is updated (UPSERT). The returned sync_id is always the canonical row's sync_id.
Returns ErrCrossProjectRelation when source and target belong to different projects. Returns a validation error when required fields are missing or Confidence is out of [0.0, 1.0].
func (*Store) JudgeRelation ¶
func (s *Store) JudgeRelation(p JudgeRelationParams) (*Relation, error)
JudgeRelation records a verdict on an existing pending relation row.
Re-judge policy: OVERWRITE the existing row (design decision). The updated row is returned on success.
Phase 2: wraps the UPDATE in a transaction to atomically enqueue a sync mutation when the source observation's project is enrolled for cloud sync. Returns ErrCrossProjectRelation if source and target belong to different projects.
Returns an error if the judgment_id is unknown or the relation verb is invalid.
func (*Store) ListDeferred ¶
func (s *Store) ListDeferred(opts ListDeferredOptions) ([]DeferredRow, error)
ListDeferred returns rows from sync_apply_deferred with optional status filter and pagination. The payload field is decoded to map[string]any; on malformed JSON, PayloadValid is false and PayloadRaw is preserved.
func (*Store) ListDeferredProjectsForTarget ¶
func (*Store) ListDiagnosticObservationRequiredFields ¶
func (s *Store) ListDiagnosticObservationRequiredFields(project string) ([]ObservationRequiredFieldsEvidence, error)
ListDiagnosticObservationRequiredFields reports active source observations whose cloud-required title, content, or type is NULL, empty, or whitespace. It is deliberately independent of sync_mutations so doctor can find source corruption even when no journal row remains.
func (*Store) ListDiagnosticSessions ¶
func (s *Store) ListDiagnosticSessions(project string) ([]DiagnosticSessionEvidence, error)
ListDiagnosticSessions returns session evidence scoped by project when provided. The query is read-only and ordered for deterministic diagnostics.
project is read through ifnull() for the same reason directory already is: a database upgraded from the schema where sessions.project was nullable still carries NULL ownership, and no migration rewrites the column. Scanning that raw would abort every diagnostic on exactly the databases doctor exists to report on. NULL and blank both mean "identifies no project" to every caller here, so collapsing them to the empty string loses nothing.
func (*Store) ListEnrolledProjects ¶
func (s *Store) ListEnrolledProjects() ([]EnrolledProject, error)
ListEnrolledProjects returns all projects currently enrolled for cloud sync, ordered alphabetically by project name.
func (*Store) ListInvalidSessionIdentityEvidence ¶
func (s *Store) ListInvalidSessionIdentityEvidence(project string) ([]InvalidSessionIdentityEvidence, error)
ListInvalidSessionIdentityEvidence reports blank source session IDs together with affected references and invalid session journal entries. It is read-only.
The source-row predicate uses the shared whitespace trim set rather than SQLite's bare trim(), so a legacy identity made of tabs, newlines or carriage returns cannot bypass the scan while still being rejected by the Go guards.
project is read through ifnull() because a corrupt session row on an upgraded database can also carry the legacy NULL ownership; reporting the corrupt identity must not depend on whether that row's project survived the upgrade.
func (*Store) ListObservationSyncPayloads ¶
ListObservationSyncPayloads returns the decoded payloads of all sync_mutations rows whose entity = 'observation'. Used by integration tests to assert that new observation columns (review_after, expires_at, embedding*) are NOT present in the sync wire format in Phase 1 (REQ-009).
func (*Store) ListOrphanedObservationSessionEvidence ¶
func (s *Store) ListOrphanedObservationSessionEvidence(project string) ([]OrphanedObservationSessionEvidence, error)
ListOrphanedObservationSessionEvidence reports grouped observation references whose parent sessions are absent. It includes soft-deleted observations because they remain local data that can block inspection or recovery.
func (*Store) ListPendingProjectMutations ¶
func (s *Store) ListPendingProjectMutations(project string) ([]SyncMutation, error)
ListPendingProjectMutations returns pending cloud mutations for one project, or all projects when project is empty, without enrollment filtering. Doctor needs to diagnose blocked metadata even when a project is not enrolled.
func (*Store) ListPendingSyncMutations ¶
func (s *Store) ListPendingSyncMutations(targetKey string, limit int) ([]SyncMutation, error)
func (*Store) ListPendingSyncMutationsAfterSeq ¶
func (*Store) ListProjectNames ¶
ListProjectNames returns all distinct project names from observations, ordered alphabetically. Used for fuzzy matching and consolidation.
func (*Store) ListProjectsForCloudEnrollment ¶
ListProjectsForCloudEnrollment returns every local identity that can be enrolled, normalized and ordered deterministically for the cloud TUI.
func (*Store) ListProjectsWithStats ¶
func (s *Store) ListProjectsWithStats() ([]ProjectStats, error)
ListProjectsWithStats returns all projects with aggregated counts. Ordered by observation count descending.
func (*Store) ListQuarantinedPulledSessionEvidence ¶
func (s *Store) ListQuarantinedPulledSessionEvidence(project string) ([]QuarantinedPulledSessionEvidence, error)
ListQuarantinedPulledSessionEvidence returns the pulled session mutations the apply path skipped because their identity is blank or inconsistent.
The pull deliberately does not fail closed on these mutations: halting would pin the cursor forever on a historical chunk written before the identity rule existed. Instead each one is quarantined here so doctor can report exactly what remote data was dropped. It is read-only.
func (*Store) ListRelations ¶
func (s *Store) ListRelations(opts ListRelationsOptions) ([]RelationListItem, error)
ListRelations returns a paginated list of relation rows filtered by the given options. Project filtering is done via LEFT JOIN to observations (no schema change). Uses idx_memrel_status_created for efficient status+date ordering.
func (*Store) MarkReviewed ¶
MarkReviewed resets an observation's review_after using its type's configured decay offset. Types without a decay offset return to a NULL review_after value. This lifecycle reset is intentionally local-only until the sync wire format includes review_after.
func (*Store) MarkReviewedForProject ¶
MarkReviewedForProject resets an observation's review lifecycle only when it remains owned by project at the mutation boundary.
func (*Store) MarkSyncAuthRequired ¶
func (*Store) MarkSyncBlocked ¶
func (*Store) MarkSyncFailure ¶
func (*Store) MarkSyncFailureWithReason ¶
func (s *Store) MarkSyncFailureWithReason(targetKey, reasonCode, message string, backoffUntil time.Time) error
MarkSyncFailureWithReason records a degraded failure while preserving its reason code.
func (*Store) MarkSyncHealthy ¶
func (*Store) MarkSyncPaused ¶
func (*Store) MarkSyncPending ¶
func (*Store) MaxObservationLength ¶
MaxObservationLength returns the configured maximum content length for observations.
func (*Store) MergeProjects ¶
func (s *Store) MergeProjects(sources []string, canonical string) (*MergeResult, error)
MergeProjects migrates all records from each source project name into the canonical name. Every source must normalize to the canonical name; sources that exactly equal the canonical name or have no records are skipped. All updates are performed inside a single transaction for atomicity.
func (*Store) MigrateProject ¶
func (s *Store) MigrateProject(oldName, newName string) (*MigrateResult, error)
func (*Store) ObservationsNeedingReview ¶
func (s *Store) ObservationsNeedingReview(project string, limit int) ([]Observation, error)
ObservationsNeedingReview returns non-deleted observations whose review_after has passed. An empty project searches all projects, matching existing browse/search conventions.
func (*Store) PassiveCapture ¶
func (s *Store) PassiveCapture(p PassiveCaptureParams) (*PassiveCaptureResult, error)
PassiveCapture extracts learnings from text and saves them as observations. It deduplicates against existing observations using content hash matching.
func (*Store) PinObservation ¶
func (*Store) PinnedObservations ¶
func (s *Store) PinnedObservations(project, scope string) ([]Observation, error)
PinnedObservations returns every pinned observation for project/scope, most-recent-first, with no row limit — pinning is an explicit, hand-bounded action, so returning all pinned rows has always been the legacy default. Callers that need a cap (e.g. FormatContextWithOptions via ContextOptions.Pinned) use the unexported pinnedObservationsLimit helper, which this delegates to with limit=0 ("no LIMIT clause", i.e. unbounded).
func (*Store) ProjectExists ¶
ProjectExists returns true if the named project has at least one record in any of observations, sessions, prompts, or enrollment tables. Uses a single UNION ALL LIMIT 1 query for efficiency (REQ-315). The sync_enrolled_projects branch ensures a project enrolled via EnrollProject() without any other data is still recognized (JC1).
func (*Store) PruneProject ¶
func (s *Store) PruneProject(project string) (*PruneResult, error)
PruneProject removes prompts and sessions without observations for a project that has zero active observations. Soft-deleted observations and their sessions are retained.
func (*Store) QuarantineIrreparableSyncMutations ¶
func (s *Store) QuarantineIrreparableSyncMutations(targetKey, project string, apply bool) (SyncMutationQuarantineReport, error)
QuarantineIrreparableSyncMutations explicitly disposes of pending mutations the existing legacy validator proves cannot be repaired from local state. It never acknowledges, rewrites, or deletes a mutation.
func (*Store) ReadSQLiteLockSnapshot ¶
func (s *Store) ReadSQLiteLockSnapshot(ctx context.Context) (SQLiteLockSnapshot, error)
ReadSQLiteLockSnapshot returns SQLite lock-related PRAGMA values without starting an application write transaction.
func (*Store) RecentObservations ¶
func (s *Store) RecentObservations(project, scope string, limit int) ([]Observation, error)
func (*Store) RecentPrompts ¶
func (*Store) RecentSessions ¶
func (s *Store) RecentSessions(project string, limit int) ([]SessionSummary, error)
A database upgraded from the schema where sessions.project was nullable still carries rows that identify no project, so the column is read through ifnull(): an unscoped listing reads every session row and must not die on one of them.
func (*Store) RecordSyncedChunk ¶
RecordSyncedChunk marks a local-target chunk as imported/exported.
func (*Store) RecordSyncedChunkForTarget ¶
RecordSyncedChunkForTarget marks a chunk as imported/exported for a target.
func (*Store) ReleaseSyncLease ¶
func (*Store) RemirrorProject ¶
RemirrorProject enqueues a fresh current-state replay for an enrolled project. It leaves historical delivery rows untouched so recovery remains auditable.
func (*Store) RepairCloudUpgrade ¶
func (s *Store) RepairCloudUpgrade(project string, apply bool) (CloudUpgradeRepairReport, error)
func (*Store) RepairObservationMutationTitles ¶
func (s *Store) RepairObservationMutationTitles(project string, apply bool) (SyncMutationTitleRepairReport, error)
RepairObservationMutationTitles restores the single title field that can be proved from a matching titleless local observation. It deliberately does not enqueue a replacement mutation: the existing journal row keeps its sequence and all delivery state while only its frozen payload is corrected.
func (*Store) RepairObservationSourceTitles ¶
func (s *Store) RepairObservationSourceTitles(project string, apply bool) (ObservationSourceTitleRepairReport, error)
RepairObservationSourceTitles restores only source titles that can be derived from the first non-empty line of their own content. It never invents content or type. When no pending local mutation exists and the repaired row is otherwise sync-valid, it emits one canonical upsert for the local repair.
func (*Store) ReplayDeferred ¶
func (s *Store) ReplayDeferred() (ReplayDeferredResult, error)
ReplayDeferred retries every deferred row, including legacy-unscoped rows. It is reserved for deliberate administrative operations such as conflict recovery.
func (*Store) ReplayDeferredForScope ¶
func (s *Store) ReplayDeferredForScope(targetKey, project string) (result ReplayDeferredResult, err error)
ReplayDeferredForScope retries deferred rows for one sync target and optional project. An empty project includes all safely target-scoped rows for that target; an empty target is reserved for ReplayDeferred's administrative global replay. Legacy-unscoped rows are excluded whenever a target or project scope is supplied.
It retries rows with apply_status='deferred' (up to 50 per call, ordered by first_seen_at). For each row:
- Calls applyPulledMutationTx inside a transaction.
- On success: the apply itself deletes the deferred row (applyRelationUpsertTx already includes DELETE FROM sync_apply_deferred on success path).
- On ErrRelationFKMissing: increments retry_count; if retry_count reaches 5, marks apply_status='dead'. Otherwise updates last_error + last_attempted_at.
- On ErrApplyDead or other decode errors: marks apply_status='dead'.
Dead rows are never retried. Idempotent: calling twice in one cycle does not double-retry because successful rows are deleted and failed rows update retry_count in place.
Returns counts (retried, succeeded, failed, dead) for caller logging.
func (*Store) RescueNullProjectOwnership ¶
func (s *Store) RescueNullProjectOwnership(p ProjectRescueParams) (*ProjectRescueResult, error)
RescueNullProjectOwnership assigns an explicit project only to selected legacy records with NULL ownership and enqueues their missing canonical mutations.
func (*Store) RollbackCloudUpgrade ¶
func (s *Store) RollbackCloudUpgrade(project string) (CloudUpgradeState, error)
func (*Store) SaveCloudUpgradeState ¶
func (s *Store) SaveCloudUpgradeState(state CloudUpgradeState) error
func (*Store) SaveRelation ¶
func (s *Store) SaveRelation(p SaveRelationParams) (*Relation, error)
SaveRelation inserts a new pending relation row. The SyncID field must be unique (enforced by the UNIQUE constraint on memory_relations.sync_id).
func (*Store) ScanAllProjects ¶
func (s *Store) ScanAllProjects(opts ScanOptions) (ScanResult, error)
func (*Store) ScanProject ¶
func (s *Store) ScanProject(opts ScanOptions) (ScanResult, error)
ScanProject walks one ID-ordered observation page in the given project (filtered by Since) and calls FindCandidates with SkipInsert=true for each row. If Apply is true and below MaxInsert cap, each new candidate pair is inserted as a pending relation (after a pre-check to skip already-related pairs).
Phase 4 extension: when ScanOptions.Semantic is true, after the FTS5 candidate collection a bounded worker pool calls Runner.Compare on each pair. Applied scans persist judged verdicts via JudgeBySemantic, while dry-runs report their semantic results without persistence. Semantic=false (zero value) preserves Phase 3 behaviour exactly.
Returns a ScanResult with counts, a continuation cursor when another page remains, and whether an insert or semantic cap was hit.
func (*Store) Search ¶
func (s *Store) Search(query string, opts SearchOptions) ([]SearchResult, error)
Search preserves the original non-context API for callers that do not need cancellation.
func (*Store) SearchContext ¶
func (s *Store) SearchContext(ctx context.Context, query string, opts SearchOptions) ([]SearchResult, error)
SearchContext searches observations while honoring cancellation from the caller, including while materializing rows.
func (*Store) SearchPreviewsContext ¶
func (s *Store) SearchPreviewsContext(ctx context.Context, query string, opts SearchOptions) ([]SearchPreviewResult, error)
SearchPreviewsContext searches using the same ranking and filters as SearchContext while selecting only a bounded Unicode-safe content preview.
func (*Store) SearchPrompts ¶
func (*Store) SessionObservations ¶
func (s *Store) SessionObservations(sessionID string, limit int) ([]Observation, error)
SessionObservations returns all observations for a specific session.
func (*Store) SkipAckNonEnrolledMutations ¶
SkipAckNonEnrolledMutations acks (marks as skipped) all pending mutations that belong to non-enrolled projects, preventing journal bloat. Empty-project mutations are never skipped — they always sync regardless of enrollment.
func (*Store) StartSession ¶
StartSession registers a new session or idempotently starts an active one. It refuses to reuse an ended session ID so MCP callers cannot silently strand later writes on a fallback session.
func (*Store) StatsProject ¶
StatsProject returns aggregate counts restricted to one project. Selection policy belongs to callers; this method only applies the supplied scope.
func (*Store) Timeline ¶
func (s *Store) Timeline(observationID int64, before, after int) (*TimelineResult, error)
func (*Store) UnenrollProject ¶
UnenrollProject removes a project from cloud sync enrollment. Idempotent — unenrolling a non-enrolled project is a no-op.
func (*Store) UnpinObservation ¶
func (*Store) UpdateObservation ¶
func (s *Store) UpdateObservation(id int64, p UpdateObservationParams) (*Observation, error)
type SyncMutation ¶
type SyncMutation struct {
Seq int64 `json:"seq"`
TargetKey string `json:"target_key"`
Entity string `json:"entity"`
EntityKey string `json:"entity_key"`
Op string `json:"op"`
Payload string `json:"payload"`
Source string `json:"source"`
Project string `json:"project"`
OccurredAt string `json:"occurred_at"`
AckedAt *string `json:"acked_at,omitempty"`
Disposition string `json:"disposition"`
DispositionReason string `json:"disposition_reason,omitempty"`
DispositionEvidence string `json:"disposition_evidence,omitempty"`
DispositionAt *string `json:"disposition_at,omitempty"`
}
type SyncMutationPayloadValidation ¶
type SyncMutationPayloadValidation struct {
Entity string `json:"entity"`
Op string `json:"op"`
EntityKey string `json:"entity_key,omitempty"`
MissingFields []string `json:"missing_fields,omitempty"`
ReasonCode string `json:"reason_code,omitempty"`
Message string `json:"message,omitempty"`
}
SyncMutationPayloadValidation describes deterministic required-field issues in a pending sync mutation payload.
func ValidateSyncMutationPayload ¶
func ValidateSyncMutationPayload(entity, op, payload, entityKey string) SyncMutationPayloadValidation
ValidateSyncMutationPayload performs pure required-field validation for sync payloads. It is intentionally conservative: malformed/empty/unsupported payloads are reported as manual blocks, while complete payloads return an empty validation.
type SyncMutationQuarantineAction ¶
type SyncMutationQuarantineAction struct {
Seq int64 `json:"seq"`
Project string `json:"project"`
Entity string `json:"entity"`
EntityKey string `json:"entity_key"`
Op string `json:"op"`
ReasonCode string `json:"reason_code"`
Message string `json:"message"`
Evidence string `json:"evidence"`
}
SyncMutationQuarantineAction records one deterministic local quarantine.
type SyncMutationQuarantineReport ¶
type SyncMutationQuarantineReport struct {
Project string `json:"project,omitempty"`
Applied bool `json:"applied"`
Actions []SyncMutationQuarantineAction `json:"actions"`
}
SyncMutationQuarantineReport is the explicit local recovery result.
type SyncMutationTitleRepairAction ¶
type SyncMutationTitleRepairAction struct {
Seq int64 `json:"seq"`
Project string `json:"project"`
Entity string `json:"entity"`
EntityKey string `json:"entity_key"`
Op string `json:"op"`
Title string `json:"title"`
}
SyncMutationTitleRepairAction records a title restored from its matching local observation without creating another sync mutation.
type SyncMutationTitleRepairReport ¶
type SyncMutationTitleRepairReport struct {
Project string `json:"project,omitempty"`
Applied bool `json:"applied"`
Actions []SyncMutationTitleRepairAction `json:"actions"`
}
SyncMutationTitleRepairReport is the local recovery result for title-only observation upserts. Repairs remain pending so the original sequence is delivered normally.
type SyncState ¶
type SyncState struct {
TargetKey string `json:"target_key"`
Lifecycle string `json:"lifecycle"`
LastEnqueuedSeq int64 `json:"last_enqueued_seq"`
LastAckedSeq int64 `json:"last_acked_seq"`
LastPulledSeq int64 `json:"last_pulled_seq"`
ConsecutiveFailures int `json:"consecutive_failures"`
BackoffUntil *string `json:"backoff_until,omitempty"`
LeaseOwner *string `json:"lease_owner,omitempty"`
LeaseUntil *string `json:"lease_until,omitempty"`
ReasonCode *string `json:"reason_code,omitempty"`
ReasonMessage *string `json:"reason_message,omitempty"`
LastError *string `json:"last_error,omitempty"`
LastSuccessAt *string `json:"last_success_at,omitempty"`
UpdatedAt string `json:"updated_at"`
}
type TimelineEntry ¶
type TimelineEntry struct {
ID int64 `json:"id"`
SessionID string `json:"session_id"`
Type string `json:"type"`
Title string `json:"title"`
Content string `json:"content"`
ToolName *string `json:"tool_name,omitempty"`
Project *string `json:"project,omitempty"`
Scope string `json:"scope"`
TopicKey *string `json:"topic_key,omitempty"`
RevisionCount int `json:"revision_count"`
DuplicateCount int `json:"duplicate_count"`
LastSeenAt *string `json:"last_seen_at,omitempty"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
DeletedAt *string `json:"deleted_at,omitempty"`
IsFocus bool `json:"is_focus"` // true for the anchor observation
}
type TimelineResult ¶
type TimelineResult struct {
Focus Observation `json:"focus"` // The anchor observation
Before []TimelineEntry `json:"before"` // Observations before the focus (chronological)
After []TimelineEntry `json:"after"` // Observations after the focus (chronological)
SessionInfo *Session `json:"session_info"` // Session that contains the focus observation
TotalInRange int `json:"total_in_range"`
}
type TruncationMetadata ¶
type TruncationMetadata struct {
OriginalBytes int `json:"original_bytes"`
LimitBytes int `json:"limit_bytes"`
Truncated bool `json:"truncated"`
}
TruncationMetadata describes storage content processing after private-tag redaction.