feat(memory): Store port with nop and fake adapters

This commit is contained in:
2026-06-05 19:33:46 -04:00
parent 582af0d68b
commit 7d4f9ca4a8
2 changed files with 116 additions and 0 deletions
+70
View File
@@ -0,0 +1,70 @@
// Package memory is Keel's durable, cross-run memory port. Modes Record derived
// events and read them back with Recent; the AW-backed adapter persists them to
// an ActivityWatch bucket. Event-kind names are owned by the modes that write
// them — this package stays a generic event store.
package memory
import (
"context"
"sync"
"time"
)
// Event is one derived event Keel chooses to remember. Kind is the event type
// (e.g. "proposal_made"); Data carries pointers and small values, not copies of
// other tools' source of truth.
type Event struct {
Kind string
At time.Time
Data map[string]any
}
// Store records derived events and reads recent ones back by kind.
type Store interface {
Record(ctx context.Context, e Event) error
Recent(ctx context.Context, kind string, n int) ([]Event, error)
}
// nopStore is the no-memory fallback used when AW is unreachable or disabled.
type nopStore struct{}
// NewNop returns a Store that drops writes and returns no history.
func NewNop() Store { return nopStore{} }
func (nopStore) Record(context.Context, Event) error { return nil }
func (nopStore) Recent(context.Context, string, int) ([]Event, error) { return nil, nil }
// Fake is an in-memory Store for tests.
type Fake struct {
mu sync.Mutex
events []Event
}
// NewFake returns an empty in-memory Store.
func NewFake() *Fake { return &Fake{} }
func (f *Fake) Record(_ context.Context, e Event) error {
f.mu.Lock()
f.events = append(f.events, e)
f.mu.Unlock()
return nil
}
func (f *Fake) Recent(_ context.Context, kind string, n int) ([]Event, error) {
f.mu.Lock()
defer f.mu.Unlock()
var out []Event
for i := len(f.events) - 1; i >= 0 && len(out) < n; i-- {
if f.events[i].Kind == kind {
out = append(out, f.events[i])
}
}
return out, nil
}
// Events returns a snapshot of everything recorded, for test assertions.
func (f *Fake) Events() []Event {
f.mu.Lock()
defer f.mu.Unlock()
return append([]Event(nil), f.events...)
}