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 iskernle.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.Search
_dispatch_on_search() so components can re-rank or filter.
Working Memory
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. ReturnNone to leave unchanged. (v0.10.0: return values are now persisted — previously they were ignored.)
on_search
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
Theload() 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
- Backend search: The storage backend runs vector similarity (if embeddings exist) or text matching
- Result conversion: Storage results are converted to
SearchResultdataclasses with content extracted per memory type - Confidence filtering: If
min_confidenceis specified, low-confidence records are excluded - Component dispatch:
_dispatch_on_search()passes results through each component, which can re-rank or filter - Return: Final list of
SearchResultobjects
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.