store

package
v2.0.0-rc.10 Latest Latest
Warning

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

Go to latest
Published: Sep 10, 2026 License: MIT Imports: 31 Imported by: 0

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

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

View Source
const (
	JudgmentStatusPending  = "pending"
	JudgmentStatusJudged   = "judged"
	JudgmentStatusOrphaned = "orphaned"
	JudgmentStatusIgnored  = "ignored"
)

Valid judgment_status values.

View Source
const (
	SessionOwnershipShared       = "shared"
	SessionOwnershipProjectOwned = "project_owned"
)
View Source
const (
	ObservationStateActive      = "active"
	ObservationStateNeedsReview = "needs_review"
)
View Source
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"
)
View Source
const (
	SyncMutationDispositionPending     = "pending"
	SyncMutationDispositionQuarantined = "quarantined"
)
View Source
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"
)
View Source
const (
	UpgradeReasonRepairableLegacyMutationPayload = "upgrade_repairable_legacy_mutation_payload"
	UpgradeReasonBlockedLegacyMutationManual     = "upgrade_blocked_legacy_mutation_manual"
)
View Source
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.

View Source
const DefaultScanLimit = 100

DefaultScanLimit bounds each conflict scan page and its FTS candidate queries.

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

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

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

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

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

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

func ClassifyTool(toolName string) string

ClassifyTool returns the observation type for a given tool name.

func ExtractLearnings

func ExtractLearnings(text string) []string

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

func NormalizeProject(project string) (normalized string, warning string)

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 Now

func Now() string

Now returns the current time formatted for SQLite.

func SuggestTopicKey

func SuggestTopicKey(typ, title, content string) string

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

func ValidateObservationTitle(title string) error

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 AddPromptParams struct {
	SessionID string `json:"session_id"`
	Content   string `json:"content"`
	Project   string `json:"project,omitempty"`
}

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 CloudUpgradeRepairReport

type CloudUpgradeRepairReport struct {
	Class         string `json:"class"`
	ReasonCode    string `json:"reason_code"`
	Message       string `json:"message"`
	PlannedAction string `json:"planned_action,omitempty"`
	Applied       bool   `json:"applied"`
}

type CloudUpgradeSnapshot

type CloudUpgradeSnapshot struct {
	Captured        bool `json:"captured"`
	ProjectEnrolled bool `json:"project_enrolled"`
}

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 DefaultConfig() (Config, error)

func FallbackConfig

func FallbackConfig(dataDir string) Config

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 ImportResult struct {
	SessionsImported         int `json:"sessions_imported"`
	ObservationsImported     int `json:"observations_imported"`
	ObservationsUpdated      int `json:"observations_updated"`
	ObservationsSkippedStale int `json:"observations_skipped_stale"`
	PromptsImported          int `json:"prompts_imported"`
}

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 MigrateResult struct {
	Migrated            bool  `json:"migrated"`
	ObservationsUpdated int64 `json:"observations_updated"`
	SessionsUpdated     int64 `json:"sessions_updated"`
	PromptsUpdated      int64 `json:"prompts_updated"`
}

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

type ObservationSnippet struct {
	ID      int64
	SyncID  string
	Title   string
	Type    string
	Content string
}

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 PendingSyncMutationProjectCount

type PendingSyncMutationProjectCount struct {
	Project string `json:"project"`
	Count   int64  `json:"count"`
}

type ProjectNameCount

type ProjectNameCount struct {
	Name  string `json:"name"`
	Count int    `json:"count"`
}

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 Prompt

type Prompt struct {
	ID        int64  `json:"id"`
	SyncID    string `json:"sync_id"`
	SessionID string `json:"session_id"`
	Content   string `json:"content"`
	Project   string `json:"project,omitempty"`
	CreatedAt string `json:"created_at"`
}

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

type ReplayDeferredResult struct {
	Retried   int
	Succeeded int
	Failed    int
	Dead      int
}

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 SearchOptions struct {
	Type      string `json:"type,omitempty"`
	Project   string `json:"project,omitempty"`
	Scope     string `json:"scope,omitempty"`
	Limit     int    `json:"limit,omitempty"`
	MatchMode string `json:"match_mode,omitempty"` // "all" (default) | "any"
}

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 Session

type Session struct {
	ID            string  `json:"id"`
	Project       string  `json:"project"`
	OwnershipMode string  `json:"ownership_mode,omitempty"`
	Directory     string  `json:"directory"`
	StartedAt     string  `json:"started_at"`
	EndedAt       *string `json:"ended_at,omitempty"`
	Summary       *string `json:"summary,omitempty"`
}

type SessionProjectReclassification

type SessionProjectReclassification struct {
	SessionID   string
	FromProject string
	ToProject   string
}

type SessionProjectReclassificationCounts

type SessionProjectReclassificationCounts struct {
	Sessions     int64
	Observations int64
	Prompts      int64
}

type SessionProjectReclassificationResult

type SessionProjectReclassificationResult struct {
	Counts     SessionProjectReclassificationCounts
	BackupPath string
}

type SessionSummary

type SessionSummary struct {
	ID               string  `json:"id"`
	Project          string  `json:"project"`
	StartedAt        string  `json:"started_at"`
	EndedAt          *string `json:"ended_at,omitempty"`
	Summary          *string `json:"summary,omitempty"`
	ObservationCount int     `json:"observation_count"`
}

type Stats

type Stats struct {
	TotalSessions     int      `json:"total_sessions"`
	TotalObservations int      `json:"total_observations"`
	TotalPrompts      int      `json:"total_prompts"`
	Projects          []string `json:"projects"`
}

type Store

type Store struct {
	// contains filtered or unexported fields
}

func New

func New(cfg Config) (*Store, error)

func (*Store) AckSyncMutationSeqs

func (s *Store) AckSyncMutationSeqs(targetKey string, seqs []int64) error

AckSyncMutationSeqs acknowledges specific mutation sequence numbers without requiring them to be contiguous.

func (*Store) AckSyncMutations

func (s *Store) AckSyncMutations(targetKey string, lastAckedSeq int64) error

func (*Store) AcquireSyncLease

func (s *Store) AcquireSyncLease(targetKey, owner string, ttl time.Duration, now time.Time) (bool, error)

func (*Store) ActiveRuntimeSessions

func (s *Store) ActiveRuntimeSessions(project string, directories ...string) ([]string, error)

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) AddPrompt

func (s *Store) AddPrompt(p AddPromptParams) (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 (s *Store) BackupSQLite() (string, error)

func (*Store) CanRollbackCloudUpgrade

func (s *Store) CanRollbackCloudUpgrade(project string) (bool, error)

func (*Store) ClearCloudUpgradeState

func (s *Store) ClearCloudUpgradeState(project string) error

func (*Store) Close

func (s *Store) Close() error

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

func (s *Store) CountDeferredAndDead() (deferred, dead int, err error)

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

func (s *Store) CountObservationsForProject(name string) (int, error)

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

func (s *Store) CountRelationSyncMutations() (int, error)

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 (s *Store) CreateSession(id, project, directory string) error

func (*Store) CreateSessionWithOwnershipMode

func (s *Store) CreateSessionWithOwnershipMode(id, project, directory, mode string) error

CreateSessionWithOwnershipMode creates a session with an explicit persisted ownership policy. CreateSession remains the compatibility wrapper for shared runtime sessions.

func (*Store) DB

func (s *Store) DB() *sql.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) DataDir

func (s *Store) DataDir() string

DataDir returns the directory containing the store database and cloud.json.

func (*Store) DeleteObservation

func (s *Store) DeleteObservation(id int64, hardDelete bool) error

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

func (s *Store) DeletePrompt(id int64) error

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

func (s *Store) DeleteSession(id string) error

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) EndSession

func (s *Store) EndSession(id string, summary string) error

func (*Store) EnrollProject

func (s *Store) EnrollProject(project string) error

EnrollProject registers a project for cloud sync. Idempotent — re-enrolling an already-enrolled project is a no-op.

func (*Store) EnsureEnrolledProjectSyncMutations

func (s *Store) EnsureEnrolledProjectSyncMutations(ctx context.Context) error

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

func (s *Store) FormatCompactionContext(sessionID string) (string, error)

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

func (s *Store) FormatContext(project, scope string) (string, error)

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

func (s *Store) GetRelation(syncID string) (*Relation, error)

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) GetSession

func (s *Store) GetSession(id string) (*Session, error)

func (*Store) GetSyncState

func (s *Store) GetSyncState(targetKey string) (*SyncState, error)

func (*Store) GetSyncedChunks

func (s *Store) GetSyncedChunks() (map[string]bool, error)

GetSyncedChunks returns local-target chunk IDs for backwards compatibility.

func (*Store) GetSyncedChunksForTarget

func (s *Store) GetSyncedChunksForTarget(targetKey string) (map[string]bool, error)

GetSyncedChunksForTarget returns chunk IDs tracked for a specific sync target.

func (*Store) HasPendingSyncMutationsForProject

func (s *Store) HasPendingSyncMutationsForProject(project string) (bool, error)

func (*Store) HasProjectOwnedSessions

func (s *Store) HasProjectOwnedSessions() (bool, error)

HasProjectOwnedSessions reports whether any session requires ownership-mode sync.

func (*Store) HasProjectOwnedSessionsForProject

func (s *Store) HasProjectOwnedSessionsForProject(project string) (bool, error)

HasProjectOwnedSessionsForProject reports whether project requires ownership-mode sync.

func (*Store) Import

func (s *Store) Import(data *ExportData) (*ImportResult, error)

func (*Store) IsProjectEnrolled

func (s *Store) IsProjectEnrolled(project string) (bool, error)

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 (s *Store) ListDeferredProjectsForTarget(targetKey string) ([]string, error)

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

func (s *Store) ListObservationSyncPayloads() ([]any, error)

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 (s *Store) ListPendingSyncMutationsAfterSeq(targetKey string, afterSeq int64, limit int) ([]SyncMutation, error)

func (*Store) ListProjectNames

func (s *Store) ListProjectNames() ([]string, error)

ListProjectNames returns all distinct project names from observations, ordered alphabetically. Used for fuzzy matching and consolidation.

func (*Store) ListProjectsForCloudEnrollment

func (s *Store) ListProjectsForCloudEnrollment() ([]string, error)

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

func (s *Store) MarkReviewed(id int64) error

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

func (s *Store) MarkReviewedForProject(id int64, project string) error

MarkReviewedForProject resets an observation's review lifecycle only when it remains owned by project at the mutation boundary.

func (*Store) MarkSyncAuthRequired

func (s *Store) MarkSyncAuthRequired(targetKey, message string) error

func (*Store) MarkSyncBlocked

func (s *Store) MarkSyncBlocked(targetKey, reasonCode, message string) error

func (*Store) MarkSyncFailure

func (s *Store) MarkSyncFailure(targetKey, message string, backoffUntil time.Time) error

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 (s *Store) MarkSyncHealthy(targetKey string) error

func (*Store) MarkSyncPaused

func (s *Store) MarkSyncPaused(targetKey, message string) error

func (*Store) MarkSyncPending

func (s *Store) MarkSyncPending(targetKey string) error

func (*Store) MaxObservationLength

func (s *Store) MaxObservationLength() int

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 (s *Store) PinObservation(id int64) error

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

func (s *Store) ProjectExists(name string) (bool, error)

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 (s *Store) RecentPrompts(project string, limit int) ([]Prompt, error)

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

func (s *Store) RecordSyncedChunk(chunkID string) error

RecordSyncedChunk marks a local-target chunk as imported/exported.

func (*Store) RecordSyncedChunkForTarget

func (s *Store) RecordSyncedChunkForTarget(targetKey, chunkID string) error

RecordSyncedChunkForTarget marks a chunk as imported/exported for a target.

func (*Store) ReleaseSyncLease

func (s *Store) ReleaseSyncLease(targetKey, owner string) error

func (*Store) RemirrorProject

func (s *Store) RemirrorProject(project string) error

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 (s *Store) SearchPrompts(query string, project string, limit int) ([]Prompt, error)

func (*Store) SessionObservations

func (s *Store) SessionObservations(sessionID string, limit int) ([]Observation, error)

SessionObservations returns all observations for a specific session.

func (*Store) SkipAckNonEnrolledMutations

func (s *Store) SkipAckNonEnrolledMutations(targetKey string) (int64, error)

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

func (s *Store) StartSession(id, project, directory string) error

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) Stats

func (s *Store) Stats() (*Stats, error)

func (*Store) StatsProject

func (s *Store) StatsProject(project string) (*Stats, error)

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

func (s *Store) UnenrollProject(project string) error

UnenrollProject removes a project from cloud sync enrollment. Idempotent — unenrolling a non-enrolled project is a no-op.

func (*Store) UnpinObservation

func (s *Store) UnpinObservation(id int64) error

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.

type UpdateObservationParams

type UpdateObservationParams struct {
	Type     *string `json:"type,omitempty"`
	Title    *string `json:"title,omitempty"`
	Content  *string `json:"content,omitempty"`
	Project  *string `json:"project,omitempty"`
	Scope    *string `json:"scope,omitempty"`
	TopicKey *string `json:"topic_key,omitempty"`
}

Jump to

Keyboard shortcuts

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