Documentation
¶
Overview ¶
Package memory defines the core domain types for Thoughtline.
A Memory is a single observation persisted by the user (via an AI assistant calling tl_save). Each Memory carries:
- Type — one of the values in the memory taxonomy (see docs/design/memory-domain.md). Examples: game-design-decision, scene-pattern, asset-reference, perf-gotcha, pipeline-step, bugfix, convention.
- Scope — project (default; tied to a project id) or personal (cross-project, per-developer ergonomics).
- TopicKey — optional stable key for evolving topics. When set, tl_save upserts: same project + same topic_key replaces the prior row, preserving created_at and bumping revision_count.
- Project — string identifier of the active project (typically the working directory's basename, but explicitly configurable).
- Title — short, searchable headline. Imperative form preferred ("Fix N+1 in InventoryList").
- Content — the body. Markdown. No length cap is enforced at the domain layer; storage layer applies a soft limit and returns a clear error rather than truncating silently.
- CreatedAt — first time this memory (or its topic) appeared.
- UpdatedAt — last time this memory (or its topic) was upserted.
- Revision — number of times the topic has been re-saved (0 for first save, incremented on every upsert).
This package contains pure data types and validation. No I/O, no persistence concerns — those belong in internal/storage. The split keeps the domain reusable across storage backends.
Status: skeleton. Concrete types arrive in milestone M1.
Index ¶
Constants ¶
const ( MaxAgentLabelChars = 64 MaxSessionSummaryBytes = MaxContentBytes )
Limits exported so callers can show meaningful validation messages.
const ( MaxTitleChars = 200 MaxContentBytes = 64 * 1024 MaxTopicKeyLen = 128 MaxTagLen = 40 )
Limits — exported so callers can show meaningful validation messages.
Variables ¶
var ( ErrInvalidSessionID = errors.New("session: invalid id (must be a UUIDv7)") ErrEmptySessionProject = errors.New("session: project must not be empty") ErrAgentLabelTooLong = errors.New("session: agent_label exceeds maximum characters") ErrEmptySessionSummary = errors.New("session: summary must not be empty when ending a session") ErrSessionSummaryTooLong = errors.New("session: summary exceeds maximum bytes") ErrSessionEndedBeforeStart = errors.New("session: ended_at must be at or after started_at") )
Errors returned by ValidateSession. Tests assert against these directly.
var ( ErrInvalidType = errors.New("memory: invalid type") ErrInvalidScope = errors.New("memory: invalid scope") ErrPreferenceMustBePersonal = errors.New("memory: preference type requires personal scope") ErrNonPreferenceMustBeProject = errors.New("memory: non-preference types require project scope") ErrEmptyProject = errors.New("memory: project must not be empty") ErrEmptyTitle = errors.New("memory: title must not be empty") ErrTitleTooLong = errors.New("memory: title exceeds 200 characters") ErrEmptyContent = errors.New("memory: content must not be empty") ErrContentTooLong = errors.New("memory: content exceeds maximum bytes") ErrInvalidTopicKey = errors.New("memory: invalid topic_key format") ErrInvalidTag = errors.New("memory: invalid tag format") )
Errors returned by Validate. Tests assert against these directly.
Functions ¶
func Validate ¶
Validate enforces every domain rule documented in docs/design/memory-domain.md. It mutates nothing — callers can apply trimming themselves before saving.
Errors returned are sentinel values from this package (e.g. ErrEmptyTitle). Use errors.Is to match.
func ValidateSession ¶
ValidateSession enforces the Session invariants. Used by storage at StartSession / EndSession boundaries — the server layer never persists a Session that has not passed this check.
Types ¶
type Memory ¶
type Memory struct {
// ID is the local autoincrement primary key. Zero for unsaved memories.
ID int64
// SyncID is a UUIDv7 stable across upserts. Set by storage on first save.
SyncID string
// Project is the project identifier the memory belongs to. Required.
Project string
// Scope governs cross-project visibility.
Scope Scope
// Type categorises the memory.
Type Type
// TopicKey, when non-empty, makes (Project, TopicKey) the upsert key.
TopicKey string
// Title is a short, searchable headline.
Title string
// Content is the markdown body.
Content string
// Tags are free-form labels (engine name, platform, etc.). Optional.
Tags []string
// NormalizedHash is a content fingerprint used for noop detection. Set by
// storage on save; callers should leave this zero.
NormalizedHash string
// RevisionCount is the number of upserts on this topic. 0 for first save.
RevisionCount int
// CreatedAt is the first time this memory (or its topic) was saved.
CreatedAt time.Time
// UpdatedAt is the last time this memory (or its topic) was saved.
UpdatedAt time.Time
// DeletedAt is non-nil for soft-deleted memories.
DeletedAt *time.Time
// SessionID optionally associates this memory with a Session (UUIDv7).
// Empty when the memory was saved outside any session. Set via tl_save's
// optional session_id argument.
SessionID string
}
Memory is the canonical in-memory representation of a stored observation. Storage maps this to/from the SQL row; the server (MCP) maps this to/from the JSON request payload.
type Scope ¶
type Scope string
Scope governs whether a memory belongs to a specific project or travels with the developer across projects.
type Session ¶
type Session struct {
// ID is a UUIDv7 string assigned by storage at start time.
ID string
// Project the session belongs to. Required.
Project string
// AgentLabel is an optional client-provided tag like "claude-code",
// "cursor", "zed". Useful for cross-session forensics.
AgentLabel string
// StartedAt is when tl_session_start was called.
StartedAt time.Time
// EndedAt is non-nil once tl_session_summary has closed the session.
EndedAt *time.Time
// Summary is the structured end-of-session digest. Empty until ended.
Summary string
}
Session bookends a coding interaction so the AI has a stable narrative across context compactions. Sessions are append-once: started by tl_session_start, closed exactly once by tl_session_summary. A Memory may optionally reference its parent session via Memory.SessionID.
IDs are UUIDv7 strings — sortable by time, generated client-side.
type Type ¶
type Type string
Type is the kind of memory being stored. The set is closed by design — see docs/design/memory-domain.md for the rationale and per-type usage.
const ( TypeGameDesignDecision Type = "game-design-decision" TypeScenePattern Type = "scene-pattern" TypeAssetReference Type = "asset-reference" TypePerfGotcha Type = "perf-gotcha" TypePipelineStep Type = "pipeline-step" TypeScriptPattern Type = "script-pattern" TypeBugfix Type = "bugfix" TypeConvention Type = "convention" TypePreference Type = "preference" TypeDecision Type = "decision" // migrated from Engram; project-scoped TypeArchitecture Type = "architecture" // migrated from Engram; project-scoped )