Skip to main content

StackProtocol — The Memory Container

A stack is a self-contained memory system. It stores, retrieves, searches, and maintains memories. It knows nothing about models or plugins. It can be attached to one core, many cores, or none.
The stack’s value is independence of lifecycle. It persists unchanged when the core is reconfigured, the model is swapped, or plugins come and go.

StackProtocol Interface

The default implementation is kernle.stack.sqlite_stack.SQLiteStack.

Properties

Write Operations

Low-level storage methods. The intended write path is through the core’s routed methods (core.episode(), core.belief(), etc.) which populate provenance before calling these. All save_* methods return the memory ID of the saved record.

Atomic Updates (v0.13.12)

These methods use version-based optimistic locking. They return True on success, False if the record disappeared between read and write. When expected_version is provided, they also raise VersionConflictError if the version has changed.

Suggestion Lifecycle (v0.13.01)

Methods for reviewing and resolving auto-generated memory suggestions. accept_suggestion creates the target memory type (episode, belief, note, goal, value, relationship, or drive) with full provenance (source_entity="kernle:suggestion-promotion"), marks the suggestion as promoted, and returns the new memory ID. Returns None if the suggestion is not found, not pending, or was lint-rejected.

Batch Write

Read Operations

Strength-Tier Filtering (v0.10.0)

Read operations filter memories by strength tier. New memories start at strength 1.0 and decay over time through maintenance or explicit weakening.
Semantic search across all memory types. Results pass through _dispatch_on_search() so components can re-rank or filter.

Working Memory

Assembles the most relevant memories within a token budget. Returns a structured dict of values, beliefs, goals, episodes, notes, relationships, summaries, and self-narratives.

Meta-Memory

Trust Layer

Features

Sync

Stats and Export

Composition Hooks

Component Registry

Stacks have their own sub-plugin system: components. Components extend what the stack can do without modifying the stack itself.

Default Configuration (v0.10.0)

Default Components

Component Hooks

Components hook into the stack lifecycle through four dispatch points. Errors in any component are isolated — a failing component does not crash the operation.

on_save

Called after every memory save with the memory type, ID, and the memory object. Components can return metadata to persist on the memory (e.g., emotional tags). The stack writes returned fields to the database. Return None to leave unchanged. (v0.10.0: return values are now persisted — previously they were ignored.) Called after search results are assembled. Components can re-rank, filter, or augment results. Each component receives the current results list and returns a (possibly modified) version. Components are called in registration order.

on_load

Called during working memory assembly. Components contribute to the context dict.

on_maintenance

Called during periodic maintenance. Each component does background work and returns stats.

Working Memory Assembly

The load() method assembles the most relevant memories within a token budget. Here is the flow:

Priority Scoring

Each memory type has a base priority. Records with stronger attributes get a bonus: Values get a bonus from their priority field. Beliefs get a bonus from confidence. Drives get a bonus from intensity. This ensures that high-confidence beliefs and high-priority values are loaded before routine episodes.

Token Budget

Items are truncated at word boundaries (500 chars max per item) and tokens estimated at ~4 chars/token with a 1.3x safety margin. The budget defaults to 8,000 tokens, capped at 50,000.

Search Pipeline

  1. Backend search: The storage backend runs vector similarity (if embeddings exist) or text matching
  2. Result conversion: Storage results are converted to SearchResult dataclasses with content extracted per memory type
  3. Confidence filtering: If min_confidence is specified, low-confidence records are excluded
  4. Component dispatch: _dispatch_on_search() passes results through each component, which can re-rank or filter
  5. Return: Final list of SearchResult objects

Provenance and Write Discipline

Memories should be written through an attached core. The core ensures full provenance: source attribution, timestamps, context tags, derived_from chains, and source_type tracking. A detached stack is a portable data artifact — it can be opened, queried, exported, and maintained. But direct writes produce memories with incomplete provenance. This is sometimes necessary for migration or repair, but it degrades the stack’s ability to reason about its own contents. The save_*() methods on the stack are low-level storage operations. The core’s routed methods (entity.episode(), entity.belief(), etc.) are the protocol-compliant entry points.