Offline Consolidation¶
The Two Mechanisms¶
1. Offline Consolidation — Episodic → Semantic Promotion¶
The consolidation daemon performs K-Means clustering on episodic memories to extract semantic knowledge:
sequenceDiagram
participant RD as Consolidation Daemon
participant EP as Episodic Store
participant SE as Semantic Store
participant HG as Hebbian Graph
participant EG as Entity Graph
Note over RD: Circadian trigger (configurable interval)
RD->>EP: Get sealed partitions
loop Each sealed partition
RD->>EP: Read all records
Note over RD: K-Means clustering on header features
RD->>RD: Cluster by (tag overlap, importance)
loop Each cluster (size ≥ threshold)
Note over RD: Compute centroid header
RD->>RD: Tags = AND across cluster (common themes)
RD->>RD: Importance = average, Valence = max
RD->>SE: Write consolidated semantic record
end
RD->>HG: Decay edges (0.9× factor)
RD->>EG: Decay relations + merge similar entities
RD->>EP: Mark partition as reflectable
end Key behaviors:
- Tag merging: Uses bitwise AND across the cluster — only common tags survive, representing the shared theme
- Importance averaging: The consolidated memory inherits the mean importance of its source episodes
- Minimum cluster size: Small clusters (noise) are not promoted — only patterns are
- Cross-layer promotion: Strong Hebbian edges are promoted to Entity Graph relations
- Entity maintenance: Similar entities are merged (Levenshtein distance), stale relations decay
Example: Consolidation in Action
An agent encounters 15 episodic memories tagged [database, connection, error] over a week. The consolidation daemon clusters them and promotes a single semantic memory: "Database connection issues are recurring — check connection pool sizing and timeout settings."
2. Tombstone Compaction — Synaptic Pruning¶
When records are deleted via forget() or purge(), tombstones (FLAG_TOMBSTONE) are marked in the record header, allowing scorer Phase 1 to skip them in ~1 cycle. However, tombstoned records continue to consume disk and slab capacity until compaction reclaims the physical space.
Spector performs physical compaction via VacuumCompactor, which can be triggered on demand (SpectorMemoryAdmin.vacuum(tier, force) / POST /api/v1/memory/vacuum) or during background maintenance when the tombstone ratio exceeds the configured threshold.
flowchart LR
A["Partition Slab<br/>1000 records<br/>400 tombstoned"] -->|"VacuumCompactor.compact()"| B["Compacted Slab<br/>600 dense records<br/>0 tombstones"]
A -->|"Physical Delta"| C["Dead Slots Zeroed<br/>Space Reclaimed"]
style A fill:#e74c3c,color:white
style B fill:#2ecc71,color:white
style C fill:#27ae60,color:white The Compaction Process:
-
Census & Threshold Evaluation: The store's tombstone ratio is evaluated against
spector.memory.vacuum.threshold(default0.20). If the ratio is below threshold andforce=false, a census report (CompactionResult.census) is returned without moving records. -
Dense In-Place Relocation: Live records are copied sequentially to lower offsets within each partition slab (
compactFixedfor fixed-stride semantic/procedural engrams,compactEpisodicfor variable-length append logs). Abandoned trailing slots are zeroed out in-place. -
Atomic Counter Updates: Header prologues (
visibleCountandusedBytes) are atomically updated and published to readers via Panama MemorySegment stores. -
Index Offset Remapping Under Lock: Under
PartitionManager.withRollLock(the same lock coordinating partition roll),IndexEntryMemoryupdates record locations to their new offsets, guaranteeing continuous ID-to-record resolution across concurrent reads. -
Graph Edge Reconciliation: Surviving records maintain stable
graphSlotmappings, keeping Hebbian associations, temporal chains, and hyperedges intact. Dead records have theirgraphSlots detached across all four cognitive graph planes (Hebbian CSR, temporal chain, entity directory, hypergraph). -
Derived Index Reconciliation: Derived indexes (HNSW, BM25, SPLADE) are re-synchronized via
IndexReconcileEngine.reconcile(). -
Measured Space Reclamation: Reclaimed space is measured directly from the physical difference (
beforeUsedBytes - afterUsedBytes) rather than computed via an unverified multiplication.
3. Deletion Semantics: forget vs purge¶
Spector provides two distinct deletion verbs with documented semantics:
| Property | forget (Logical Tombstone) | purge (Physical Destruction) |
|---|---|---|
| Mechanism | Sets FLAG_TOMBSTONE (bit 0 of flags byte) | Overwrites payload and content headers with zeros in-place; sets FLAG_PURGED (0x40) |
| Payload on Disk | Retained verbatim in partition mmap slab and snapshots | Destroyed (zeroed off-heap vector, norm, Bloom filter, centroid ID, turn body, unshared text) |
| Graph Edges | Filtered during traversal | Detached across all 4 planes: Hebbian CSR rebuild, temporal chain unlink, entity directory unlink, and hyperedges scan |
| WAL Event | RECORD_WRITE tombstone bit update | PURGE opcode recorded before zeroing, re-applied during recovery |
| Legal Hold | Permitted (payload survives for discovery) | Refused (NamespaceLegalHoldException / HTTP 409) |
| Export Behavior | Omitted unless --include-tombstones is passed | Omitted unconditionally |
| Audit Report | Status confirmation | Returns PurgeResult disclosing unreachable copies (DR exports, replica disks, cold tier) |
| Space Reclaim | Reclaimed only when partition compaction runs | Space retained in-place (reclaimed only upon compaction) |
| Reversibility | Reversible in principle | Irreversible |
When memories are forget()'d, they are tombstoned (bit 0 of flags byte set to 1). The scorer skips them in Phase 1 (~1 cycle). When records must be permanently destroyed for privacy or compliance (e.g. GDPR erasure), purge() must be used. In either case, vacuum() subsequently compacts the partition to physically reclaim disk space.
3.1 Unreachable Copies Disclosure (PurgeResult.UNREACHABLE_COPIES)¶
A purge() physically overwrites bytes in the active local memory store. However, an honest erasure report must acknowledge the operational boundaries of the engine. Spector's PurgeResult.unreachableCopies explicitly enumerates the six storage domains outside the scope of a local memory purge:
| Unreachable Copy Domain | Explanation & Operational Boundary |
|---|---|
dr_exports | Offline archive tarballs and disaster recovery bundles generated prior to the purge. Purging the active store cannot mutate external archive media. |
replica_disks | Passive or unlinked physical replica disks not participating in synchronous WAL replication during the purge window. |
cold_tier_objects | Read-only object-store blobs (e.g. S3 / GCS tiers) containing archived historical partitions. |
filesystem_snapshots_and_backups | Point-in-time filesystem or volume snapshots (e.g. ZFS, EBS, LVM) created before the purge executed. |
wal_segments_prior_to_the_last_checkpoint | Rotated or archived WAL log segments written prior to the latest checkpoint. Active replay is bounded, but raw byte remnants may persist on disk until WAL log reaping. |
os_page_cache_and_ssd_wear_levelling_remnants | Unflushed OS dirty pages or physical NAND flash blocks preserved by SSD wear-leveling algorithms prior to block trimming. |
Every purge audit report includes PurgeResult.DISCLOSURE_TEXT, ensuring downstream compliance tooling is explicitly notified that external archives and snapshots require separate lifecycle management.
3.2 Content-Addressable Dedup Text Retention (hasLocalRetention)¶
Spector employs content-addressable storage deduplication for record text bodies in text.dat. When multiple memories share identical text content, they reference the same physical byte range:
flowchart TD
M1["Memory 1 (live)<br/>textOffset: 0x1000"] --> TEXT["Shared text bytes<br/>'System initialized'"]
M2["Memory 2 (purged)<br/>textOffset: 0x1000"] -.-> TEXT
style TEXT fill:#e67e22,color:white - When
Memory 2is purged, its off-heap vector, norm, Bloom filter, centroid reference, and graph edges are zeroed/detached immediately. - If live co-tenants (such as
Memory 1) still reference the shared text, zeroingtext.datwould corrupt the surviving records. - Consequently, the shared text is preserved until the final co-tenant is purged.
- In this scenario,
PurgeResult.hasLocalRetention()evaluates totrue,textRetainedSharedreports the exact retained byte count,textSharedWithindicates the count of surviving co-tenants, andtextDestroyed()returnsfalse. - Once the remaining co-tenants are purged, the shared byte range is zeroed, and
hasLocalRetention()returnsfalse.
3.3 Retained Audit Header Fields (PurgeResult.RETAINED_HEADER_FIELDS)¶
To ensure that the act of erasure itself remains verifiable and auditable without preserving identifying information or reconstructed text, PurgeResult retains structural metadata headers:
header_version,flags(withFLAG_PURGED = 0x40set andFLAG_TOMBSTONEpreserved)timestamp_ms,importance,valence,arousalencoding_profile,encoding_alpha,encoding_beta,soul_version
No vector payload, embedding, or text body survives in the record slot after a successful purge.
Circadian Trigger¶
The consolidation daemon runs on a configurable schedule:
flowchart LR
INGEST["Memory ingested"] --> CHECK{"Time since last<br/>consolidation > interval?"}
CHECK -->|"No"| SKIP["Continue normally"]
CHECK -->|"Yes"| REFLECT["Trigger consolidation cycle<br/><i>default: every 24 hours</i>"]
style REFLECT fill:#9b59b6,color:white The default interval is 24 hours — matching the biological circadian cycle. For testing, it can be set to any duration.
Partition State Machine¶
stateDiagram-v2
[*] --> ACTIVE: New day → create partition
ACTIVE --> SEALED: Day rolls over
SEALED --> REFLECTABLE: Consolidation processes
REFLECTABLE --> TOMBSTONED: tombstoneRatio > threshold
TOMBSTONED --> COMPACTED: VacuumCompactor compacts (dense)
ACTIVE --> TOMBSTONED: High forget rate during active day
note right of ACTIVE: Accepting writes
note right of SEALED: Read-only, awaiting consolidation
note right of REFLECTABLE: Consolidation complete, eligible for pruning
note right of TOMBSTONED: Tombstone ratio exceeds threshold
note right of COMPACTED: Compacted into dense slab with space reclaimed ReflectPathway — 9-Relay Sleep Pipeline¶
Spector 1.3.0 consolidates all sleep reflection operations into a single composable ReflectPathway pipeline with 9 specialized relays:
graph LR
subgraph "NREM Slow-Wave Sleep (SWS)"
R1["1. SynapticPruningRelay<br/><i>Downscaling & compaction</i>"]
R2["2. EpisodicLogConsolidationRelay<br/><i>Systems replay & gist extraction</i>"]
end
subgraph "REM Dream Sleep"
R3["3. SoulDriftRefusionRelay<br/><i>#503 Affective restamping & soul re-fusion</i>"]
R4["4. ProactiveInterferenceRelay<br/><i>Near-duplicate decay</i>"]
end
subgraph "Synaptic Homeostasis & Maintenance"
R5["5. HebbianHomeostasisRelay<br/><i>Edge decay</i>"]
R6["6. TemporalPruningRelay<br/><i>Retention decay</i>"]
R7["7. CrossLayerPromotionRelay<br/><i>Hebbian → Entity promotion</i>"]
R8["8. EntityMaintenanceRelay<br/><i>Entity merge & graph decay</i>"]
end
subgraph "Durability"
R9["9. WalJournalRelay<br/><i>WAL REFLECT checkpoint</i>"]
end
R1 --> R2 --> R3 --> R4 --> R5 --> R6 --> R7 --> R8 --> R9 Systems Consolidation & Soul-Drift Re-Fusion (#503)¶
Following biological systems consolidation (Diekelmann & Born, 2010), declarative memory replay and gist abstraction occur predominantly during NREM Slow-Wave Sleep (SWS). Emotional restamping and persona realignment occur during REM Sleep.
When an agent's cognitive soul or personality configuration evolves, older memories retained with stale soul version stamps undergo re-fusion during REM reflection. The SoulDriftRefusionRelay identifies candidates with header.soulVersion() < currentSoulVersion, prioritizes candidates via a max-heap of encoding surprise z-scores, adapts the generative prior mean toward the autobiographical centroid, re-scores importance using current ICNU/salience parameters and header-derived hints, and stamps updated headers in-place.
Consolidation Report¶
Each consolidation cycle produces a structured ReflectReport summarizing the sleep cycle:
| Metric | Description |
|---|---|
| consolidatedCount | Number of episodic records / facts promoted to Semantic tier |
| tombstonedCount | Number of memories tombstoned during Deep Sleep pruning |
| compactedPartitions | Count of partitions compacted and space reclaimed during reflection |
| temporalPrunedCount | Stale temporal chain nodes pruned |
| soulDriftedCount | Count of memories detected with outdated soul version stamps |
| soulRefusedCount | Count of soul-drifted memories re-fused with updated importance |
| averageImportanceDelta | Average absolute importance delta after soul re-fusion |
| logTurnsConsolidated | Episodic log conversation turns distilled into semantic memories |
| duration | Total reflection cycle time |
| graphHealth | Graph health metrics snapshot |
This report is logged, monitored, and exposed via the introspection API and Micrometer metrics.
Next Steps¶
- Cortex — Tier Stores — the 4-tier architecture
- Synapse — Tags & Scoring — the 64-byte header
- Dopamine — Surprise Detection — auto-importance scoring