Documentation
¶
Overview ¶
Package atom implements the MemoryAtom domain: atomic fact units extracted from decisions, knowledge items, and procedural memories. Atoms form a directed graph via memory_links, enabling graph traversal queries.
Index ¶
- Constants
- Variables
- func IsValidDigestStatus(status string) bool
- type AddAtomParams
- type AddLinkParams
- type Atom
- type Link
- type PromotionEligibility
- type Store
- func (s *Store) AddAtom(ctx context.Context, p AddAtomParams) (*Atom, error)
- func (s *Store) AddLink(ctx context.Context, p AddLinkParams) error
- func (s *Store) CountByDigestStatus(ctx context.Context, workspaceID *uuid.UUID, status string) (int64, error)
- func (s *Store) CountTotal(ctx context.Context, workspaceID *uuid.UUID) (int64, error)
- func (s *Store) ListByDigestStatus(ctx context.Context, workspaceID *uuid.UUID, status string, limit int) ([]Atom, error)
- func (s *Store) ListByParent(ctx context.Context, parentTable string, parentID uuid.UUID) ([]Atom, error)
- func (s *Store) PruneAtoms(ctx context.Context, cutoff time.Time) (int64, error)
- func (s *Store) Search(ctx context.Context, workspaceID *uuid.UUID, query string, limit int) ([]Atom, error)
- func (s *Store) SetDigestStatus(ctx context.Context, atomID uuid.UUID, status string, errMsg string) error
- func (s *Store) Traverse(ctx context.Context, startAtomID uuid.UUID, depth int) (*TraverseResult, error)
- type StoreIface
- type TraverseResult
Constants ¶
const ( // DigestStatusPending is the initial state written by AddAtom, before // the LLM atomizer has processed the atom. DigestStatusPending = "pending" // DigestStatusDone marks an atom whose digestion step (atomization or // consolidation) finished successfully. DigestStatusDone = "done" // DigestStatusFailed marks an atom whose digestion step errored; // error_msg carries the failure detail. DigestStatusFailed = "failed" // DigestStatusConsolidated marks an original atom that the daily // atom-consolidation cron (internal/scheduler/atom_consolidation.go) // merged into a condensed atom. DigestStatusConsolidated = "consolidated" // DigestStatusPromoted marks a consolidated atom that was promoted to a // pending knowledge proposal, either manually via the // promote_atom_to_knowledge MCP tool or automatically by the weekly // atom-bridge cron (internal/scheduler/atom_bridge.go). DigestStatusPromoted = "promoted" )
Digest status enum — the single source of truth for the legal values of memory_atoms.digest_status. Both backend stores (Postgres: store.go; SQLite: internal/storage/sqlite/atom.go) validate every SetDigestStatus call against this enum before issuing the UPDATE, so nothing — including a prompt-injected MCP tool call carrying an arbitrary string — can write an unrecognised status into the column (.claude/rules/backend-security-design.md §2: adversarial input handling).
Values mirror the migration COMMENT on the column (migrations/000062_memory_atoms_promoted_status.up.sql): 'pending | done | failed | consolidated | promoted'. Do not re-declare these literals elsewhere — reference these constants instead (G6 decision 8f25c58d).
const ( // PromotionMinContentRunes is the minimum atom content length, measured // in Unicode runes (not bytes, so CJK/emoji content is measured // correctly), required for promotion to a knowledge proposal. PromotionMinContentRunes = 80 // PromotionMinTags is the minimum tag count required for promotion. PromotionMinTags = 2 )
Promotion eligibility gate — shared by the manual promote_atom_to_knowledge MCP tool (internal/mcp/tools_atom.go) and the weekly atom-bridge cron job (internal/scheduler/atom_bridge.go) so the two promotion paths can never silently diverge on quality-gate criteria (G6 decision 8f25c58d).
Variables ¶
var ErrInvalidDigestStatus = errors.New("atom: invalid digest_status")
ErrInvalidDigestStatus is returned by SetDigestStatus when the caller supplies a status outside the five-value enum above.
var ErrNotFound = errors.New("atom: not found")
ErrNotFound is returned when a requested atom does not exist.
Functions ¶
func IsValidDigestStatus ¶
IsValidDigestStatus reports whether status is one of the five legal digest_status enum values. Both backend stores' SetDigestStatus call this before sending the UPDATE so an adversarial or buggy caller cannot write an arbitrary string into digest_status.
Types ¶
type AddAtomParams ¶
type AddAtomParams struct {
WorkspaceID *uuid.UUID
ParentTable string
ParentID uuid.UUID
Content string
Keywords []string
Tags []string
}
AddAtomParams holds parameters for creating a new atom.
type AddLinkParams ¶
type AddLinkParams struct {
FromAtomID uuid.UUID
ToAtomID uuid.UUID
LinkType string
Confidence float64
}
AddLinkParams holds parameters for creating a directed link between two atoms.
type Atom ¶
type Atom struct {
ID uuid.UUID `json:"id"`
WorkspaceID *uuid.UUID `json:"workspace_id,omitempty"`
ParentTable string `json:"parent_table"`
ParentID uuid.UUID `json:"parent_id"`
Content string `json:"content"`
Keywords []string `json:"keywords"`
Tags []string `json:"tags"`
CreatedAt time.Time `json:"created_at"`
DigestStatus *string `json:"digest_status,omitempty"`
}
Atom is the domain model for an atomic fact unit.
type Link ¶
type Link struct {
FromAtomID uuid.UUID `json:"from_atom_id"`
ToAtomID uuid.UUID `json:"to_atom_id"`
LinkType string `json:"link_type"` // same_entity|same_action|same_time|same_project
Confidence float64 `json:"confidence"`
CreatedAt time.Time `json:"created_at"`
}
Link is a directed edge between two atoms.
type PromotionEligibility ¶
PromotionEligibility reports the outcome of the promotion quality gate for a candidate atom's content and tags. Both sub-checks are exposed (rather than a single bool) so callers can surface a specific error message per failing condition, as promote_atom_to_knowledge does; callers that only need the combined verdict use Eligible().
func CheckPromotionEligibility ¶
func CheckPromotionEligibility(content string, tags []string) PromotionEligibility
CheckPromotionEligibility evaluates content and tags against the shared promotion quality gate: content length >= PromotionMinContentRunes runes AND len(tags) >= PromotionMinTags.
func (PromotionEligibility) Eligible ¶
func (e PromotionEligibility) Eligible() bool
Eligible reports whether both the content-length and tag-count gates passed.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is the Postgres-backed implementation of StoreIface.
func New ¶
New returns a Store backed by the given pool, scoped to the optional workspaceID. nil workspaceID = legacy unscoped mode.
func (*Store) AddLink ¶
func (s *Store) AddLink(ctx context.Context, p AddLinkParams) error
AddLink inserts a directed link. ON CONFLICT DO NOTHING so repeated calls are idempotent.
func (*Store) CountByDigestStatus ¶
func (s *Store) CountByDigestStatus(ctx context.Context, workspaceID *uuid.UUID, status string) (int64, error)
CountByDigestStatus counts atoms with the given digest_status, optionally scoped to a workspace.
func (*Store) CountTotal ¶
CountTotal returns the total number of atoms scoped to the given workspace.
func (*Store) ListByDigestStatus ¶
func (s *Store) ListByDigestStatus(ctx context.Context, workspaceID *uuid.UUID, status string, limit int) ([]Atom, error)
ListByDigestStatus returns up to limit atoms with the given digest_status, ordered by created_at ASC. Mirrors the CountByDigestStatus query shape.
func (*Store) ListByParent ¶
func (s *Store) ListByParent(ctx context.Context, parentTable string, parentID uuid.UUID) ([]Atom, error)
ListByParent returns all atoms for a given parent table + id.
func (*Store) PruneAtoms ¶
PruneAtoms hard-deletes memory_atoms rows older than cutoff. Called by the daily decay pruner to enforce the 90-day TTL. Link rows referencing pruned atoms are deleted first (no FK cascade per project red-line #9; referential integrity is enforced in code).
func (*Store) Search ¶
func (s *Store) Search(ctx context.Context, workspaceID *uuid.UUID, query string, limit int) ([]Atom, error)
Search returns atoms whose content, keywords, or tags match query via ILIKE. Scoped to workspaceID when non-nil.
func (*Store) SetDigestStatus ¶
func (s *Store) SetDigestStatus(ctx context.Context, atomID uuid.UUID, status string, errMsg string) error
SetDigestStatus updates the digest_status and optional error_msg for a single atom. status is validated against the five-value enum (digest_status.go) before the UPDATE is sent — an invalid value is rejected with ErrInvalidDigestStatus rather than silently written to the column (backend-security-design.md §2: adversarial input handling).
func (*Store) Traverse ¶
func (s *Store) Traverse(ctx context.Context, startAtomID uuid.UUID, depth int) (*TraverseResult, error)
Traverse performs iterative BFS from startAtomID up to min(depth, 5) hops. Returns all visited atoms and the links that were followed. Total atoms returned is capped at maxTraverseAtoms (50).
type StoreIface ¶
type StoreIface interface {
// AddAtom inserts a new atom and returns the persisted record.
AddAtom(ctx context.Context, p AddAtomParams) (*Atom, error)
// AddLink inserts a directed link between two atoms.
// If the (from_atom_id, to_atom_id, link_type) triple already exists the
// call is a no-op (ON CONFLICT DO NOTHING).
AddLink(ctx context.Context, p AddLinkParams) error
// ListByParent returns all atoms whose parent_table and parent_id match.
ListByParent(ctx context.Context, parentTable string, parentID uuid.UUID) ([]Atom, error)
// Traverse does BFS from startAtomID up to depth hops (capped at 5),
// returning all visited atoms and the links that connect them.
// Total atoms returned is capped at 50.
Traverse(ctx context.Context, startAtomID uuid.UUID, depth int) (*TraverseResult, error)
// Search returns atoms whose content, keywords, or tags contain query
// (ILIKE on Postgres, LIKE on SQLite). Scoped to workspaceID when non-nil.
Search(ctx context.Context, workspaceID *uuid.UUID, query string, limit int) ([]Atom, error)
// PruneAtoms hard-deletes memory_atoms rows older than cutoff.
// Called daily by the decay.Pruner to enforce the 90-day TTL.
PruneAtoms(ctx context.Context, cutoff time.Time) (int64, error)
// SetDigestStatus updates the digest_status and error_msg for the given atom.
// status must be one of the five DigestStatus* constants in
// digest_status.go (Pending, Done, Failed, Consolidated, Promoted); both
// backend implementations validate against IsValidDigestStatus before
// issuing the UPDATE and return ErrInvalidDigestStatus for anything else.
// errMsg is stored only when status=DigestStatusFailed; pass "" otherwise.
SetDigestStatus(ctx context.Context, atomID uuid.UUID, status string, errMsg string) error
// CountByDigestStatus returns the number of atoms with the given digest_status
// in the given workspace (nil = all workspaces).
CountByDigestStatus(ctx context.Context, workspaceID *uuid.UUID, status string) (int64, error)
// ListByDigestStatus returns up to limit atoms with the given digest_status,
// scoped to workspaceID (nil = all workspaces), ordered by created_at ASC.
// Used by the atom-bridge weekly scheduler job to collect consolidated atoms
// eligible for promotion to knowledge proposals.
ListByDigestStatus(ctx context.Context, workspaceID *uuid.UUID, status string, limit int) ([]Atom, error)
// CountTotal returns the total number of atoms scoped to the given workspace
// (nil = all workspaces). Used by the M9 consolidation cron to compare
// against the configured capacity threshold.
CountTotal(ctx context.Context, workspaceID *uuid.UUID) (int64, error)
}
StoreIface is the backend-agnostic contract for the MemoryAtom bounded context. Postgres-backed *Store and SQLite-backed *AtomStore both satisfy this interface.
type TraverseResult ¶
TraverseResult contains atoms and links visited during a BFS traversal.