Port of the Zep/Graphiti memory model into .t27 with privacy designed in, replacing the empty stub specs/tri/agent/memory.t27.
Model (from reading graphiti_core): episodes (raw messages), entities (name, summary), facts (edges between entities) with four time fields (valid_at, invalid_at, created_at, expired_at); a contradicting fact expires the old one. Graphiti scopes only by group_id in a WHERE clause and has no encryption or access control.
Honest boundary: LLM extraction and embeddings need plaintext, and embeddings leak content. Encryption at rest stops dump/backup reads; keeping operators out needs client-side processing or a TEE with attestation.
Slices:
- types.t27 + temporal.t27: records, time fields, invalidation rules as pure functions with tests (this slice)
- scope.t27: group/user/thread scoping, no cross-group read by construction
- crypt.t27: per-user key, AEAD layout, HMAC tokens, forget = crypto-shred
- ingest.t27: extraction state machine, LLM calls as effect slots marked requires_plaintext + trust_domain
- dedup.t27, index.t27, search.t27: MinHash over keyed shingles, HMAC-BM25, BFS/RRF/MMR, context block
- attest.t27: optional TEE key-release policy
Related: gHashTag/999-multibots-telegraf#3876, #3877, #3878.
User Scenarios
- Given the empty stub
specs/tri/agent/memory.t27 exists, When the privacy-first graph memory specification is authored under specs/memory/graph/, Then the stub is replaced by a directory containing types.t27 and temporal.t27 that define episodes, entities, facts, and four time fields with invalidation rules as pure functions.
- Given a fact with
valid_at and invalid_at fields, When a new contradicting fact is inserted with overlapping validity, Then the old fact's invalid_at is set to the new fact's valid_at and the new fact becomes the sole valid edge between those entities.
- Given the temporal module exports pure functions for time-field validation, When the test suite runs, Then all invalidation rules pass without side effects.
Requirements
- FR-001: The specification
specs/memory/graph/types.t27 MUST define record types for Episode, Entity, and Fact with fields matching the Graphiti model (episodes as raw messages, entities with name and summary, facts as edges between entities).
- FR-002: The specification
specs/memory/graph/temporal.t27 MUST define four time fields on Fact: valid_at, invalid_at, created_at, expired_at with precise semantics.
- FR-003: The temporal module MUST provide pure functions for fact invalidation: inserting a contradicting fact MUST expire the old one by setting its
invalid_at to the new fact's valid_at.
- FR-004: The stub
specs/tri/agent/memory.t27 MUST be removed or replaced by a re-export pointing to the new graph memory specs.
- FR-005: All types and temporal functions MUST be compilable by
t27c and have accompanying property tests in the same .t27 files.
Success Criteria
t27c specs/memory/graph/types.t27 exits 0 and emits type definitions for Episode, Entity, Fact
t27c specs/memory/graph/temporal.t27 exits 0 and emits pure invalidation functions
t27c --test specs/memory/graph/types.t27 specs/memory/graph/temporal.t27 exits 0 with at least 10 tests passing
specs/tri/agent/memory.t27 no longer exists or re-exports specs/memory/graph
Boundary
- specs/memory/graph/types.t27
- specs/memory/graph/temporal.t27
- specs/tri/agent/memory.t27
Port of the Zep/Graphiti memory model into .t27 with privacy designed in, replacing the empty stub specs/tri/agent/memory.t27.
Model (from reading graphiti_core): episodes (raw messages), entities (name, summary), facts (edges between entities) with four time fields (valid_at, invalid_at, created_at, expired_at); a contradicting fact expires the old one. Graphiti scopes only by group_id in a WHERE clause and has no encryption or access control.
Honest boundary: LLM extraction and embeddings need plaintext, and embeddings leak content. Encryption at rest stops dump/backup reads; keeping operators out needs client-side processing or a TEE with attestation.
Slices:
Related: gHashTag/999-multibots-telegraf#3876, #3877, #3878.
User Scenarios
specs/tri/agent/memory.t27exists, When the privacy-first graph memory specification is authored underspecs/memory/graph/, Then the stub is replaced by a directory containingtypes.t27andtemporal.t27that define episodes, entities, facts, and four time fields with invalidation rules as pure functions.valid_atandinvalid_atfields, When a new contradicting fact is inserted with overlapping validity, Then the old fact'sinvalid_atis set to the new fact'svalid_atand the new fact becomes the sole valid edge between those entities.Requirements
specs/memory/graph/types.t27MUST define record types for Episode, Entity, and Fact with fields matching the Graphiti model (episodes as raw messages, entities with name and summary, facts as edges between entities).specs/memory/graph/temporal.t27MUST define four time fields on Fact:valid_at,invalid_at,created_at,expired_atwith precise semantics.invalid_atto the new fact'svalid_at.specs/tri/agent/memory.t27MUST be removed or replaced by a re-export pointing to the new graph memory specs.t27cand have accompanying property tests in the same.t27files.Success Criteria
t27c specs/memory/graph/types.t27exits 0 and emits type definitions for Episode, Entity, Factt27c specs/memory/graph/temporal.t27exits 0 and emits pure invalidation functionst27c --test specs/memory/graph/types.t27 specs/memory/graph/temporal.t27exits 0 with at least 10 tests passingspecs/tri/agent/memory.t27no longer exists or re-exportsspecs/memory/graphBoundary