Skip to content

System Architecture

Spector Memory is organized around a biological metaphor where each Java package corresponds to a brain region or cognitive mechanism. This isn't just naming β€” the architecture genuinely mirrors how biological memory systems interact.


Extensibility

Component Extension point What you can customize
SpectorMemory Single entry point for all operations Configure tiers, capacities, embedding providers
TierStore interface Add new memory tiers Implement the interface + register in TierRouter β€” no other changes needed
AbstractTierStore Common tier lifecycle Extend for new off-heap tier stores with Arena/segment management
RecallListener Post-recall hooks Add async listeners for co-activation tracking, logging, metrics
CognitiveIngestionTarget / RecallPipeline Discrete processing steps Each step is independently testable and replaceable

Data Flow: Ingestion

The ingestion pipeline is split across two layers:

  • IngestionPipeline (in spector-ingestion) β€” handles step 1 (embed) and chunking for large documents
  • CognitiveIngestionTarget (in spector-memory) β€” handles steps 2–9 (synaptic encoding β†’ WAL)
sequenceDiagram
    participant App as Application
    participant SM as SpectorMemory
    participant CT as CognitiveIngestionTarget
    participant EP as EmbeddingProvider
    participant SD as SurpriseDetector
    participant FP as FlashbulbPolicy
    participant SQ as ScalarQuantizer
    participant TR as TierRouter
    participant MI as MemoryIndex
    participant WAL as MemoryWal
    participant HG as HebbianGraph
    participant TC as TemporalChain
    participant EG as EntityGraph

    App->>SM: remember(id, text, type, tags)
    SM->>CT: ingestCognitive(id, text, vector, type, tags, ...)

    Note over CT: Step 1: Embed (done by unified IngestionPipeline)
    Note over CT: or via CognitiveIngestionTarget.ingestCognitive()
    CT->>EP: embed(text)
    EP-->>CT: float[4096]

    Note over CT: Step 2: Encode tags
    CT->>CT: SynapticTagEncoder.encode(tags) β†’ 64-bit Bloom

    Note over CT: Step 3: Surprise detection
    CT->>SD: computeImportance(l2Norm)
    SD-->>CT: importance (0.0 – 1.0)

    Note over CT: Step 4: Flashbulb check
    CT->>FP: evaluate(zScore)
    FP-->>CT: flashbulb? β†’ pin + max importance

    Note over CT: Step 5: Quantize
    CT->>SQ: encode(float[]) β†’ byte[]

    Note over CT: Step 6: Build header
    CT->>CT: CognitiveHeader(timestamp, tags, importance, ...)

    Note over CT: Step 7: Route & write
    CT->>TR: write(type, header, quantized)
    TR-->>CT: byte offset

    Note over CT: Step 8: Index
    CT->>MI: register(id, location, text, source, tags)

    Note over CT: Step 9a: WAL
    CT->>WAL: appendRemember(id, quantized)

    Note over CT: Step 9b: Hebbian edge strengthening
    CT->>HG: strengthen(currentIdx, previousIdx, 1.0f)

    Note over CT: Step 9c: Temporal chain linking
    CT->>TC: link(currentIdx, lastIdx, sessionId)

    Note over CT: Step 9d: Entity extraction & graph population
    CT->>EG: addEntity() + linkToMemory() + addRelation()

    Note over CT: Step 10: Circadian check
    CT->>CT: triggerReflectIfDue()

Note

When ingestion comes through the unified IngestionPipeline (e.g., file ingestion), embedding (step 1) is handled by the pipeline itself. CognitiveIngestionTarget.ingest() receives a pre-embedded vector and executes steps 2–9. When called via SpectorMemory.remember(), CognitiveIngestionTarget.ingestCognitive() handles embedding internally.

Note

Steps 9b–9d are gracefully degrading: if any graph component is null (not configured) or throws, the step is skipped with a log.warn() and ingestion continues normally.


Data Flow: Recall

The recall pipeline executes parallel tier scans using Virtual Threads:

sequenceDiagram
    participant App as Application
    participant RP as RecallPipeline
    participant EP as EmbeddingProvider
    participant PS as ProspectiveScheduler
    participant CT as ConcurrentTasks
    participant CS as CognitiveScorer
    participant SS as SuppressionSet
    participant HP as HabituationPenalty
    participant HG as HebbianGraph
    participant TC as TemporalChain
    participant EG as EntityGraph

    App->>RP: recall("query", options)

    Note over RP: Step 1: Embed query
    RP->>EP: embed("query")
    EP-->>RP: float[4096]

    Note over RP: Step 2: Prospective reminders
    RP->>PS: collectDue()
    PS-->>RP: due reminders

    Note over RP: Step 3: Parallel tier scanning
    RP->>CT: forkJoinAll(scanTasks)

    par Working Memory
        CT->>CS: score(workingSegment, ...)
    and Episodic Partition 1
        CT->>CS: score(partition1, ...)
    and Episodic Partition 2
        CT->>CS: score(partition2, ...)
    and Semantic
        CT->>CS: score(semanticSlab, ...)
    and Procedural
        CT->>CS: score(proceduralSegment, ...)
    end

    CS-->>RP: List<ScoredRecord>

    Note over RP: Step 4: Filter suppressed
    RP->>SS: isSuppressed(id)?

    Note over RP: Step 5a: Habituation penalty
    RP->>HP: recordAndComputePenalty(id)

    Note over RP: Step 5b: STDP causal boost
    RP->>RP: CoActivationTracker.getPredictiveStrength()

    Note over RP: Step 5c: Hebbian spreading activation
    RP->>HG: activateNeighbors(seedIdx, depth=2)
    HG-->>RP: graph-activated memory indices

    Note over RP: Step 5d: Temporal chain extension
    RP->>TC: followForward/Backward(idx, maxHops=3)
    TC-->>RP: temporally-linked memory indices

    Note over RP: Step 5e: Entity graph traversal
    RP->>EG: extract query entities β†’ BFS 2-hop
    EG-->>RP: entity-linked memory indices

    Note over RP: Step 6: Merge, dedup, sort β†’ final top-K
    RP-->>App: List<CognitiveResult>

    Note over RP: Step 7: Async listeners (Virtual Thread)
    RP->>RP: notify(HebbianListener, LtpListener)

Package Dependency Graph

graph LR
    SM[SpectorMemory<br/>FaΓ§ade] --> CT[pipeline/<br/>CognitiveIngestionTarget]
    SM --> RP[pipeline/<br/>RecallPipeline]
    SM --> TR[cortex/<br/>TierRouter]
    SM --> MI[index/<br/>MemoryIndex]

    CT --> EP[embed-api/<br/>EmbeddingProvider]
    CT --> SQ[core/<br/>ScalarQuantizer]
    CT --> SD[dopamine/<br/>SurpriseDetector]
    CT --> TR
    CT --> MI
    CT --> WAL[sync/<br/>MemoryWal]
    CT --> HG[hebbian/<br/>HebbianGraph]
    CT --> TC[temporal/<br/>TemporalChain]
    CT --> EG[graph/<br/>EntityGraph]
    CT --> EX[graph/<br/>EntityExtractor]

    RP --> EP
    RP --> CS[synapse/<br/>CognitiveScorer]
    RP --> TR
    RP --> MI
    RP --> SS[inhibition/<br/>SuppressionSet]
    RP --> HP[habituation/<br/>HabituationPenalty]
    RP --> HG
    RP --> TC
    RP --> EG

    CS --> SF[core/<br/>SimilarityFunction]
    CS --> DS[synapse/<br/>DecayStrategy]

    TR --> WM[cortex/<br/>WorkingMemoryStore]
    TR --> EM[cortex/<br/>EpisodicMemoryStore]
    TR --> SE[cortex/<br/>SemanticMemoryStore]
    TR --> PR[cortex/<br/>ProceduralMemoryStore]

    RP -.->|async| HL[pipeline/<br/>HebbianListener]
    RP -.->|async| LL[pipeline/<br/>LtpListener]

    style SM fill:#4a90d9,color:white
    style CS fill:#e74c3c,color:white
    style TR fill:#2ecc71,color:white
    style HG fill:#e74c3c,color:white
    style EG fill:#9b59b6,color:white
    style TC fill:#f39c12,color:white

The 32-Byte Cognitive Record

Every memory is stored as a fixed-size binary record in off-heap memory:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   32-Byte Synaptic Header                 β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ timestamp  β”‚ synaptic β”‚ exactNormβ”‚ import β”‚ centroidId   β”‚
β”‚ 8 bytes    β”‚ tags     β”‚ 4 bytes  β”‚ ance   β”‚ 4 bytes      β”‚
β”‚ (offset 0) β”‚ 8 bytes  β”‚ (off 16) β”‚ 4 bytesβ”‚ (offset 24)  β”‚
β”‚            β”‚ (off 8)  β”‚          β”‚(off 20)β”‚              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”¬β”€β”€β”€β”€
β”‚                                           β”‚recallβ”‚valβ”‚flgβ”‚
β”‚              (continued)                  β”‚count β”‚encβ”‚s  β”‚
β”‚                                           β”‚2B    β”‚1B β”‚1B β”‚
β”‚                                           β”‚off 28β”‚o30β”‚o31β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”΄β”€β”€β”€β”€
β”‚              Quantized Vector (N bytes)                   β”‚
β”‚              INT8 values, 32-byte aligned                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Total record size = 32 (header) + N (quantized vector bytes), aligned to 32 bytes.

At 768 dimensions (INT8): 32 + 768 = 800 bytes/memory β€” 50,000 memories fit in 40 MB of off-heap RAM.


Next Steps