//! Documented constants for the memory system
//!
//! This module contains all tunable parameters with justification for their values.
//! Centralizing constants prevents magic numbers and makes tuning easier.

// =============================================================================
// HEBBIAN LEARNING CONSTANTS
// Based on synaptic plasticity research: small incremental changes over time
// produce stable learning. Large changes cause instability.
// =============================================================================

/// Importance boost for helpful memories (+2.5%)
///
/// When a memory helps complete a task successfully (RetrievalOutcome::Helpful),
/// its importance is increased by this amount.
///
/// Justification:
/// - 2.5% is conservative to require many successful uses for significant impact
/// - Below biological synaptic strengthening (~3-7% per successful activation)
/// - 40 successful uses → importance increases from 0.5 to ~0.7 (compound effect)
/// - Prevents "rich get richer" effect where early memories dominate
///
/// Reference: Bi & Poo (1998) "Synaptic Modifications in Cultured Hippocampal Neurons"
pub const HEBBIAN_BOOST_HELPFUL: f32 = 0.025;

/// Importance decay for misleading memories (-10%)
///
/// When a memory misleads or causes errors (RetrievalOutcome::Misleading),
/// its importance is reduced by this multiplicative factor.
///
/// Justification:
/// - 10% is aggressive enough to quickly demote bad memories
/// - Asymmetric with boost (2:1 ratio) because false positives are more costly
///   than false negatives in retrieval systems
/// - After 7 consecutive misleading uses: 0.5 → 0.24 (memory becomes low-priority)
/// - Multiplicative decay ensures importance never goes negative
pub const HEBBIAN_DECAY_MISLEADING: f32 = 0.10;

/// Minimum importance floor after decay
///
/// Importance never drops below this value, allowing recovery if the memory
/// becomes useful again in a different context.
///
/// Justification:
/// - 5% floor prevents complete forgetting of potentially useful memories
/// - Matches "savings" effect in human memory (relearning is faster than learning)
/// - Allows for context-dependent recovery
pub const IMPORTANCE_FLOOR: f32 = 0.05;

// =============================================================================
// NEUROSCIENCE-INSPIRED DYNAMICS
// =============================================================================

/// Synaptic homeostasis scaling factor (Tononi & Cirelli 2003).
///
/// After each graph maintenance cycle, ALL edge strengths are multiplied by this
/// factor. Strong edges (>0.7) survive with minimal impact; weak edges (<0.2)
/// fall below prune threshold and get cleared. Edges with LtpStatus::Full are
/// protected (fully consolidated synapses resist homeostatic downscaling).
///
/// Justification:
/// - 0.995 = 0.5% reduction per cycle, conservative synaptic renormalization
/// - At 48 cycles/day: 0.995^48 ≈ 0.786 (21% daily reduction without reinforcement)
/// - Unreinforced L1 edge (0.4) reaches prune threshold (0.2) in ~3.5 days
/// - L3 edges at 0.7: 0.7 × 0.995^48 ≈ 0.55 (well above L3 prune threshold 0.3)
/// - Prevents runaway strengthening while giving edges time to prove themselves
///
/// Reference: Tononi & Cirelli (2003) "Sleep and synaptic homeostasis: a hypothesis"
pub const HOMEOSTASIS_SCALING_FACTOR: f32 = 0.995;

/// Emotional decay modulation factor (Amygdala-Hippocampal coupling).
///
/// High-arousal memories decay slower. The effective decay factor is:
///   effective_decay = base_decay × (1.0 - arousal × EMOTIONAL_DECAY_MODULATION)
///
/// At max arousal (1.0), decay is 30% slower. At zero arousal, baseline decay.
///
/// Justification:
/// - 0.3 is conservative — prevents immortal memories while preserving emotional salience
/// - Matches amygdala modulation of hippocampal consolidation strength
/// - Frustrating bugs (arousal=0.8) decay 24% slower than routine observations (arousal=0.2)
///
/// Reference: McGaugh (2004) "The amygdala modulates the consolidation of memories"
pub const EMOTIONAL_DECAY_MODULATION: f32 = 0.3;

/// Graph retrieval lateral inhibition strength (cortical winner-take-all dynamics).
///
/// After scoring candidates in graph retrieval, high-scoring memories suppress
/// semantically similar lower-ranked competitors by this factor of their score.
///
/// penalty = higher.score × GRAPH_LATERAL_INHIBITION_STRENGTH × cosine_similarity
///
/// Justification:
/// - 0.15 = mild suppression, sharpens recall without discarding valid alternatives
/// - Prevents retrieval of near-duplicate memories that dilute answer quality
/// - Total penalty capped at 50% of original score to prevent over-suppression
///
/// Note: Distinct from LATERAL_INHIBITION_STRENGTH (0.3) used in proactive_context.
/// Graph retrieval uses gentler inhibition because it feeds into the RRF fusion stage.
///
/// Reference: Rumelhart & Zipser (1985) "Feature discovery by competitive learning"
pub const GRAPH_LATERAL_INHIBITION_STRENGTH: f32 = 0.15;

/// Graph retrieval cosine similarity threshold for lateral inhibition.
///
/// Only memories with cosine similarity above this threshold to a higher-ranked
/// memory receive inhibitory suppression. Below threshold = independent memories.
///
/// Justification:
/// - 0.80 = very high similarity required before inhibition fires
/// - In MiniLM-L6 384-dim space, 0.80 cosine targets true near-duplicates/paraphrases
/// - 0.70 was too aggressive — caught related-but-distinct memories (e.g., two different
///   RocksDB issues would hit 0.72 cosine and suppress each other incorrectly)
/// - Conservative: only suppress when memories are genuinely redundant
pub const GRAPH_LATERAL_INHIBITION_THRESHOLD: f32 = 0.80;

/// Minimum prediction error multiplier (VTA/Dopamine system).
///
/// When feedback confirms expectations (high-score memory marked Helpful),
/// the learning signal is scaled down to this multiplier (0.5x).
///
/// Justification:
/// - Expected outcomes produce small prediction errors → modest learning
/// - Matches reward prediction error in dopaminergic systems (Schultz 1997)
/// - Prevents over-learning from confirmatory signals
///
/// Reference: Schultz et al. (1997) "A neural substrate of prediction and reward"
pub const PREDICTION_ERROR_MIN_MULTIPLIER: f32 = 0.5;

/// Maximum prediction error multiplier (VTA/Dopamine system).
///
/// When feedback surprises (high-score memory marked Misleading, or low-score
/// memory marked Helpful), the learning signal is scaled up to this multiplier (2.0x).
///
/// Justification:
/// - Surprising outcomes produce large prediction errors → accelerated learning
/// - 2.0x = double the normal learning rate for maximum surprise
/// - Enables rapid adaptation when retrieval confidence is miscalibrated
pub const PREDICTION_ERROR_MAX_MULTIPLIER: f32 = 2.0;

// =============================================================================
// MEMORY GRAPH EDGE CONSTANTS
// =============================================================================

/// Initial strength for new memory associations
///
/// When two memories are first co-activated, their edge starts at this strength.
///
/// Justification:
/// - 0.5 is neutral - not too strong, not too weak
/// - Allows for both strengthening and weakening based on usage patterns
/// - Matches the "initial synaptic strength" concept in neuroscience
pub const EDGE_INITIAL_STRENGTH: f32 = 0.5;

/// Minimum edge strength before pruning
///
/// Edges below this strength are removed during maintenance.
///
/// Justification:
/// - 5% threshold prevents memory graph from growing unboundedly
/// - Weak associations are unlikely to be useful for retrieval
/// - Matches IMPORTANCE_FLOOR for consistency
pub const EDGE_MIN_STRENGTH: f32 = 0.05;

/// Maximum number of edges per entity (insert-time degree cap)
///
/// When an entity exceeds this many edges after a new insertion,
/// the weakest edges (by effective_strength) are pruned to stay at cap.
///
/// Justification:
/// - Prevents O(n²) edge explosion: 100 entities × 100 = 10,000 edges max
/// - 500 edges per entity is generous for meaningful relationships
/// - Beyond 500, diminishing returns — weak edges add noise, not signal
/// - Matches neuroscience: synaptic pruning removes weak connections
///
/// Reference: Chechik et al. (1998) "Synaptic Pruning in Development"
pub const MAX_ENTITY_DEGREE: usize = 500;

/// Edge half-life base in hours (for time-based decay)
///
/// Associations decay exponentially with this half-life.
/// Stronger edges decay slower (adjusted by strength).
///
/// Justification:
/// - 24 hours base ensures daily cleanup of weak associations
/// - Strong edges (0.9 strength) decay much slower: ~96 hours effective half-life
/// - Matches circadian rhythms in memory consolidation
pub const EDGE_HALF_LIFE_HOURS: f64 = 24.0;

// =============================================================================
// GRAPH QUALITY: CONCEPT MERGING, SEMANTIC EDGES, DEGREE NORMALIZATION
// =============================================================================

/// Cosine similarity threshold for merging entity nodes as the same concept.
///
/// When string-based dedup (exact/case/stemmed) fails to find a match,
/// entities with name embeddings above this threshold are treated as synonyms
/// and merged into a single graph node.
///
/// Justification:
/// - 0.85 is conservative — avoids false merges ("Rust language" vs "Rust game")
/// - Catches abbreviations and synonyms ("authentication" ↔ "auth" ↔ "auth system")
/// - Based on typical cosine similarity distributions for MiniLM-L6-v2:
///   synonyms cluster 0.82–0.95, unrelated pairs fall below 0.60
///
/// Reference: Reimers & Gurevych (2019) "Sentence-BERT"
pub const ENTITY_CONCEPT_MERGE_THRESHOLD: f32 = 0.85;

/// Maximum entries in the in-memory entity embedding cache.
///
/// The cache is used for Tier 4 concept merging (synonym detection via cosine
/// similarity). When the cache exceeds this limit, the oldest entries (first
/// added, typically least recently mentioned) are evicted to bound memory.
///
/// At 384 dimensions × 4 bytes × 10,000 entries ≈ 15MB — acceptable for edge
/// deployment while covering the vast majority of active entity vocabularies.
pub const ENTITY_EMBEDDING_CACHE_MAX: usize = 10_000;

/// Floor multiplier for semantic edge weighting.
///
/// Initial edge weight = L1_INITIAL_WEIGHT × (floor + (1 − floor) × cosine_sim).
///
/// Justification:
/// - At floor=0.2: unrelated co-occurring entities get weight 0.06 (decays fast)
///   while semantically related pairs get up to 0.30 (full L1 weight)
/// - Prevents noisy edges from polluting the graph without blocking them entirely
/// - Floor > 0 ensures even unrelated pairs can strengthen through repeated
///   coactivation (Hebbian learning overrides initial weakness)
///
/// Reference: Lund & Burgess (1996) "Producing high-dimensional semantic spaces"
pub const EDGE_SEMANTIC_WEIGHT_FLOOR: f32 = 0.2;

/// Whether to apply degree normalization during spreading activation.
///
/// When true, outgoing activation per edge is divided by sqrt(1 + degree).
/// Effect: node with 1 edge → factor 0.71, 10 edges → 0.30,
/// 100 edges → 0.099, 500 edges → 0.045.
///
/// Justification:
/// - Without normalization, hub nodes (500+ edges) propagate identical per-edge
///   activation as leaf nodes (2 edges), drowning out precise signals
/// - sqrt(1+k) matches the fan effect in ACT-R spreading activation
/// - Can be disabled as an escape hatch without code changes
///
/// Reference: Anderson & Reder (1999) "The fan effect: New results and new theories"
pub const SPREADING_DEGREE_NORMALIZATION: bool = true;

// =============================================================================
// COMPRESSION THRESHOLDS
// Based on information theory and cognitive psychology research on memory decay
// =============================================================================

/// Importance threshold for LZ4 (lossless) compression
///
/// High-importance memories (above this threshold) use only lossless compression
/// to preserve all information.
///
/// Justification:
/// - 80% importance indicates frequently accessed or explicitly important memory
/// - Top 20% of memories by importance should be preserved fully
/// - Aligns with Pareto principle: 20% of memories likely serve 80% of queries
pub const COMPRESSION_IMPORTANCE_HIGH: f32 = 0.8;

/// Importance threshold for semantic (lossy) compression
///
/// Low-importance memories below this threshold can be semantically compressed.
///
/// Justification:
/// - 50% is the median - half of memories by importance
/// - Combined with age (>30 days), targets truly obsolete information
/// - Semantic compression preserves keywords and summary for future retrieval
pub const COMPRESSION_IMPORTANCE_LOW: f32 = 0.5;

/// Age threshold in days for aggressive compression
///
/// Memories older than this AND below importance threshold get semantic compression.
///
/// Justification:
/// - 30 days matches Ebbinghaus forgetting curve plateau
/// - After 30 days, episodic details naturally fade in human memory
/// - System mimics human memory consolidation (episodic → semantic)
pub const COMPRESSION_AGE_DAYS: i64 = 30;

/// Access count threshold to skip compression
///
/// Frequently accessed memories (above this count) stay uncompressed.
///
/// Justification:
/// - 10 accesses indicates ongoing utility
/// - Avoids compressing memories that are still being actively used
/// - Cost of decompression outweighs storage savings at this usage level
pub const COMPRESSION_ACCESS_THRESHOLD: u32 = 10;

// =============================================================================
// RESOURCE LIMITS
// Based on memory profiling of typical experiences with embeddings
// =============================================================================

/// Estimated bytes per memory entry
///
/// Used for resource limit calculations. Uses 2x safety margin.
///
/// Breakdown:
/// - Experience content: ~2-5KB (text, metadata)
/// - Embeddings (384 dims): 1.5KB (384 * 4 bytes)
/// - Memory struct overhead: ~500 bytes
/// - Serialization overhead: ~200 bytes
/// - Buffer for large experiences: ~4KB
///
/// Total realistic estimate: 8-10KB average
/// We use 20KB (2x safety margin) to prevent false positive resource limits
/// while still protecting against runaway memory usage.
///
/// Math: 500MB default limit / 20KB = ~25,000 memories per user
/// This is sufficient for most use cases while preventing OOM.
pub const ESTIMATED_BYTES_PER_MEMORY: usize = 20 * 1024;

/// Vector search candidate multiplier
///
/// When searching for N results, we retrieve N * this multiplier candidates
/// then filter down to N.
///
/// Justification:
/// - 2x accounts for ~50% filter rejection rate in typical queries
/// - Higher values waste compute; lower values may miss results
/// - Adaptive systems should tune this based on observed filter selectivity
pub const VECTOR_SEARCH_CANDIDATE_MULTIPLIER: usize = 2;

// =============================================================================
// SALIENCE SCORING WEIGHTS
// Based on cognitive psychology research on memory retrieval
// =============================================================================

/// Weight for recency in salience scoring
///
/// How much recent memories are boosted over older ones.
///
/// Justification:
/// - 1.0 baseline - recent memories have full weight
/// - Decays logarithmically with age
/// - Matches primacy/recency effects in human recall
pub const SALIENCE_RECENCY_WEIGHT: f32 = 1.0;

/// Time ranges for recency scoring (days)
///
/// - 0-7 days: Full relevance (1.0)
/// - 8-30 days: High relevance (0.7)
/// - 31-90 days: Medium relevance (0.4)
/// - 90+ days: Low relevance (0.1)
///
/// Justification:
/// - Matches Ebbinghaus forgetting curve decay rates
/// - Weekly work cycle for immediate relevance
/// - Monthly cycles for project/task context
/// - Quarterly for long-term reference
pub const RECENCY_FULL_DAYS: i64 = 7;
pub const RECENCY_HIGH_DAYS: i64 = 30;
pub const RECENCY_MEDIUM_DAYS: i64 = 90;
pub const RECENCY_HIGH_WEIGHT: f32 = 0.7;
pub const RECENCY_MEDIUM_WEIGHT: f32 = 0.4;
pub const RECENCY_LOW_WEIGHT: f32 = 0.1;

// =============================================================================
// HYBRID RETRIEVAL WEIGHTS
// =============================================================================

/// Weight for semantic similarity in hybrid retrieval
///
/// Justification:
/// - 0.5 (50%) gives semantic search primary role
/// - Semantic search handles "what" questions
pub const HYBRID_SEMANTIC_WEIGHT: f32 = 0.5;

/// Weight for graph-based activation in hybrid retrieval
///
/// Justification:
/// - 0.35 (35%) for associative context
/// - Handles "related to" and "when I was working on X" queries
pub const HYBRID_GRAPH_WEIGHT: f32 = 0.35;

/// Weight for linguistic overlap in hybrid retrieval
///
/// Justification:
/// - 0.15 (15%) for exact term matching
/// - Handles specific entity/keyword queries
pub const HYBRID_LINGUISTIC_WEIGHT: f32 = 0.15;

// =============================================================================
// DENSITY-DEPENDENT RETRIEVAL WEIGHTS (SHO-26)
// Based on GraphRAG Survey (arXiv 2408.08921) - hybrid KG-Vector improves 13.1%
// Biological model: Dense graphs = noisy (fresh L1 edges), Sparse = curated (pruned)
// Graph weight INVERSELY scales with density: sparse graphs get MORE trust
// =============================================================================

/// Minimum graph weight (used for DENSE graphs)
///
/// Justification:
/// - 0.1 (10%) when graph has > 2.0 edges per memory
/// - Dense graphs have many untested L1 edges (noisy)
/// - Semantic/BM25 dominates when graph is fresh/noisy
/// - Biological basis: new synapses need pruning before trust
pub const DENSITY_GRAPH_WEIGHT_MIN: f32 = 0.1;

/// Maximum graph weight (used for SPARSE graphs)
///
/// Justification:
/// - 0.5 (50%) when graph has < 0.5 edges per memory
/// - Sparse graphs have survived Hebbian pruning (curated)
/// - Graph traversal is high-signal when edges are mature
/// - Biological basis: long-term potentiation = trusted paths
///
/// Reference: GraphRAG Survey (arXiv 2408.08921)
pub const DENSITY_GRAPH_WEIGHT_MAX: f32 = 0.5;

/// Linguistic weight for density-dependent retrieval
///
/// Justification:
/// - 0.15 (15%) fixed - exact term matching always useful
/// - Semantic weight = 1.0 - graph_weight - linguistic_weight
pub const DENSITY_LINGUISTIC_WEIGHT: f32 = 0.15;

/// Density threshold for sparse graphs (high graph weight)
///
/// Below this edges-per-memory ratio, graph is considered sparse/mature.
/// Sparse graphs use DENSITY_GRAPH_WEIGHT_MAX (trust graph more).
/// Biological basis: pruned graphs have curated, high-value edges.
pub const DENSITY_THRESHOLD_MIN: f32 = 0.5;

/// Density threshold for dense graphs (low graph weight)
///
/// Above this edges-per-memory ratio, graph is considered dense/noisy.
/// Dense graphs use DENSITY_GRAPH_WEIGHT_MIN (trust vector/BM25 more).
/// Biological basis: fresh graphs have many untested L1 edges.
pub const DENSITY_THRESHOLD_MAX: f32 = 2.0;

// =============================================================================
// IMPORTANCE-WEIGHTED DECAY (SHO-26)
// Based on spreadr R package (Siew, 2019) and ACT-R cognitive architecture
// Important memories spread activation slower (preserve signal)
// Weak memories spread faster but decay quickly (exploratory)
// =============================================================================

/// Minimum decay rate for high-importance memories
///
/// Justification:
/// - 0.05 decay preserves ~95% activation per hop for important nodes
/// - High-importance memories (decisions, learnings) maintain strong signal
/// - Enables 6-hop traversal with meaningful activation at destination
/// - At hop 6: e^(-0.05*6) = 0.74 retention (vs 0.55 at 0.1)
///
/// Reference: spreadr R package (Siew, 2019), decay range 0.1-0.3
/// Tuning (2026-01): Lowered from 0.1 to 0.05 for deep traversal
pub const IMPORTANCE_DECAY_MIN: f32 = 0.05;

/// Maximum decay rate for low-importance memories
///
/// Justification:
/// - 0.15 decay for transient observations/context
/// - At hop 6: e^(-0.15*6) = 0.41 retention (enough signal)
/// - Preserves signal for 6-hop chains even on weak edges
/// - Still differentiates important vs transient memories
///
/// Tuning (2026-01): Lowered from 0.4 to 0.15 for deep traversal
pub const IMPORTANCE_DECAY_MAX: f32 = 0.15;

/// Type-based importance: Decision memories
///
/// Justification:
/// - 0.30 weight - highest importance
/// - Decisions represent explicit choices and preferences
/// - Critical for agent memory consistency
pub const IMPORTANCE_TYPE_DECISION: f32 = 0.30;

/// Type-based importance: Learning memories
pub const IMPORTANCE_TYPE_LEARNING: f32 = 0.25;

/// Type-based importance: Error memories
pub const IMPORTANCE_TYPE_ERROR: f32 = 0.25;

/// Type-based importance: Discovery/Pattern memories
pub const IMPORTANCE_TYPE_DISCOVERY: f32 = 0.20;

/// Type-based importance: Task memories
pub const IMPORTANCE_TYPE_TASK: f32 = 0.15;

/// Type-based importance: Context/Observation memories
pub const IMPORTANCE_TYPE_OBSERVATION: f32 = 0.10;

/// Entity presence boost for importance calculation
///
/// Justification:
/// - 0.04 per entity (max ~0.12 for 3 entities)
/// - Named entities indicate factual, retrievable content
pub const IMPORTANCE_ENTITY_BOOST: f32 = 0.04;

/// Max entities for importance boost calculation
pub const IMPORTANCE_ENTITY_MAX: usize = 3;

/// Graph connectivity boost for importance
///
/// Justification:
/// - 0.03 per connected memory (max ~0.15 for 5 connections)
/// - Well-connected memories are central to knowledge graph
pub const IMPORTANCE_CONNECTIVITY_BOOST: f32 = 0.03;

/// Max connections for importance boost calculation
pub const IMPORTANCE_CONNECTIVITY_MAX: usize = 5;

/// Recency boost for importance (within threshold)
///
/// Justification:
/// - 0.20 boost for memories within IMPORTANCE_RECENCY_DAYS
/// - Recent memories more likely to be contextually relevant
pub const IMPORTANCE_RECENCY_BOOST: f32 = 0.20;

/// Days threshold for recency importance boost
pub const IMPORTANCE_RECENCY_DAYS: f64 = 7.0;

// =============================================================================
// SEMANTIC CONSOLIDATION THRESHOLDS
// =============================================================================

/// Minimum supporting memories to extract a semantic fact
///
/// Justification:
/// - 2 minimum ensures pattern isn't a one-off
/// - Higher values (3-5) for more confidence but slower learning
pub const CONSOLIDATION_MIN_SUPPORT: usize = 2;

/// Minimum age in days before consolidation
///
/// Justification:
/// - 7 days allows patterns to emerge through repeated use
/// - Matches weekly work cycles
pub const CONSOLIDATION_MIN_AGE_DAYS: i64 = 7;

/// Jaccard similarity threshold for grouping consolidation patterns
///
/// Justification:
/// - Stemmed-token Jaccard collapses inflections ("deployed"/"deploying" → "deploy")
/// - 0.45 allows semantically similar but differently worded patterns to cluster
/// - Lower than FactStore dedup (0.7) because we want broader grouping at extraction
/// - Below 0.3 would create overly broad clusters mixing unrelated topics
pub const CONSOLIDATION_JACCARD_THRESHOLD: f32 = 0.45;

/// Maximum fact candidates extracted per memory during consolidation
///
/// Justification:
/// - Multi-extractor pipeline runs all extractors on every memory
/// - 5 is generous: procedure + definition + pattern + preference + salient
/// - Prevents one verbose memory from dominating the candidate pool
pub const CONSOLIDATION_MAX_CANDIDATES_PER_MEMORY: usize = 5;

/// Grace period before any fact decay begins (days)
///
/// Facts are immune to decay for this period after last reinforcement.
/// Facts already survived 7-day aging + 2+ support + clustering + 4-gate dedup —
/// they represent hard-won knowledge that deserves a long stability plateau.
///
/// Reference: Wixted (2004) — power-law forgetting with initial stability plateau
pub const FACT_DECAY_GRACE_DAYS: i64 = 90;

/// Base half-life for fact confidence decay (days)
///
/// After the grace period, confidence follows exponential decay:
///   confidence = original × 0.5^(elapsed / half_life)
///
/// Extended by FACT_DECAY_HALF_LIFE_PER_SUPPORT_DAYS for well-corroborated facts.
/// Total half-life = base + (support_count × per_support).
///
/// Reference: Wixted & Carpenter (2007) — spacing effect on long-term retention
pub const FACT_DECAY_HALF_LIFE_BASE_DAYS: f64 = 180.0;

/// Additional half-life per supporting memory (days)
///
/// Each independent source memory adds 30 days to the half-life.
/// Linear scaling (not log) because each corroborating source is genuine evidence.
///
/// Concrete behavior:
/// - support=2 (minimum): 240-day half-life
/// - support=5: 330-day half-life
/// - support=10: 480-day half-life (~16 months)
pub const FACT_DECAY_HALF_LIFE_PER_SUPPORT_DAYS: f64 = 30.0;

/// Cosine similarity threshold for hybrid fact deduplication
///
/// Justification:
/// - MiniLM-L6-v2 cosine > 0.80 indicates near-paraphrase for short factual statements
/// - Below 0.80 risks merging topically related but semantically distinct facts
/// - Combined with entity + polarity gates for high precision
/// Reference: Reimers & Gurevych 2019 (Sentence-BERT)
pub const FACT_DEDUP_COSINE_THRESHOLD: f32 = 0.80;

/// Jaccard sanity floor for hybrid fact deduplication
///
/// Justification:
/// - Even with high cosine, facts with zero lexical overlap are suspicious
/// - 0.30 ensures at least ~30% word overlap as a cross-check
/// - Prevents pure embedding hallucination from causing false merges
pub const FACT_DEDUP_JACCARD_FLOOR: f32 = 0.30;

/// Legacy Jaccard-only threshold (fallback when embedder is unavailable)
///
/// Justification:
/// - Original threshold for pure Jaccard dedup (preserved for graceful degradation)
/// - Used when embedder fails (circuit breaker open, model load failure)
/// - Also used per-candidate when an existing fact has no stored embedding
pub const FACT_DEDUP_JACCARD_FALLBACK: f32 = 0.70;

/// Negation markers for polarity detection in fact deduplication
///
/// Justification:
/// - "X uses JWT" vs "X does not use JWT" have high cosine but opposite meaning
/// - Scanning for negation markers catches the most common contradictions
/// - Double-negation handling: even count of markers = positive polarity
pub const FACT_NEGATION_MARKERS: &[&str] = &[
    "not",
    "n't",
    "never",
    "no",
    "none",
    "neither",
    "nor",
    "cannot",
    "can't",
    "won't",
    "don't",
    "doesn't",
    "didn't",
    "shouldn't",
    "wouldn't",
    "couldn't",
    "isn't",
    "aren't",
    "wasn't",
    "weren't",
    "hasn't",
    "haven't",
    "hadn't",
];

// =============================================================================
// DEFAULT CONFIGURATION VALUES
// =============================================================================

/// Total RocksDB block cache capacity shared across ALL DB instances (bytes).
///
/// A single LRU cache is shared by every per-user MemoryStorage, per-user
/// GraphMemory, and the shared global DB. Index blocks, filter blocks, data
/// blocks, and memtable charges all draw from this pool.
///
/// Justification:
/// - 256MB provides excellent hit rates for typical workloads (1-100 users)
/// - Prevents unbounded C++ heap growth from per-user isolated caches
/// - Follows industry standard: YugabyteDB, Apache Flink, Kafka Streams all
///   share a single block cache across RocksDB instances
///
/// Reference: https://github.com/facebook/rocksdb/wiki/Block-Cache
/// "Set the same Cache object on all the table_options for all the Column
/// Families of all DB's managed by the process."
pub const ROCKSDB_SHARED_CACHE_BYTES: usize = 256 * 1024 * 1024;

/// Per-DB write buffer size for MemoryStorage (bytes).
///
/// Reduced from 32MB to 8MB because total memtable memory is now bounded
/// by the shared cache. Smaller individual buffers means more users can
/// coexist before triggering flushes.
pub const ROCKSDB_MEMORY_WRITE_BUFFER_BYTES: usize = 8 * 1024 * 1024;

/// Per-DB write buffer size for GraphMemory (bytes).
///
/// Graph entries are small KV pairs (entities, edges), so 4MB is sufficient.
pub const ROCKSDB_GRAPH_WRITE_BUFFER_BYTES: usize = 4 * 1024 * 1024;

/// Default working memory capacity (entries)
pub const DEFAULT_WORKING_MEMORY_SIZE: usize = 100;

/// Default session memory size (MB)
pub const DEFAULT_SESSION_MEMORY_SIZE_MB: usize = 100;

/// Default max heap per user (MB)
///
/// Justification:
/// - 500MB is generous for edge devices with 4GB+ RAM
/// - Prevents single user from OOMing multi-tenant system
/// - Adjust down for Raspberry Pi (256MB) or up for servers (2GB)
pub const DEFAULT_MAX_HEAP_PER_USER_MB: usize = 500;

/// Default importance threshold for long-term storage
pub const DEFAULT_IMPORTANCE_THRESHOLD: f32 = 0.7;

// =============================================================================
// COWAN'S MODEL TIER PROMOTION CONSTANTS
// Based on Cowan (1988) "Evolving conceptions of memory storage"
// and memory consolidation research (Rasch & Born, 2013)
// Tier promotion is based on importance + time, not size
// =============================================================================

/// Minimum importance for Working → Session promotion
/// Memories must reach this threshold through Hebbian strengthening
/// or initial high-importance assignment before promotion
///
/// Justification:
/// - 0.35 allows moderately important memories to consolidate
/// - Combined with time threshold, prevents noise from entering session memory
/// - Matches ~65th percentile of memories by initial importance
pub const TIER_PROMOTION_WORKING_IMPORTANCE: f32 = 0.35;

/// Minimum age in seconds for Working → Session promotion
/// Based on early consolidation window in hippocampal memory formation
///
/// Justification:
/// - 30 minutes (1800s) matches synaptic consolidation window
/// - Allows for replay/rehearsal before promotion
/// - Short enough for practical use, long enough for consolidation
///
/// Reference: McGaugh (2000) "Memory - a century of consolidation"
pub const TIER_PROMOTION_WORKING_AGE_SECS: i64 = 1800; // 30 minutes

/// Minimum importance for Session → LongTerm promotion
/// Higher threshold ensures only well-consolidated memories persist
///
/// Justification:
/// - 0.5 requires either high initial importance or Hebbian strengthening
/// - Memories must prove their value through access patterns
/// - Matches median importance threshold for durable memories
pub const TIER_PROMOTION_SESSION_IMPORTANCE: f32 = 0.5;

/// Minimum age in seconds for Session → LongTerm promotion
/// Based on hippocampal-cortical memory transfer timeline
///
/// Justification:
/// - 24 hours (86400s) matches sleep-dependent consolidation cycle
/// - Allows for multiple replay cycles before permanent storage
/// - Hippocampal → cortical transfer primarily occurs during sleep
///
/// Reference: Rasch & Born (2013) "About Sleep's Role in Memory"
pub const TIER_PROMOTION_SESSION_AGE_SECS: i64 = 86400; // 24 hours

/// Potentiation boost applied during each maintenance cycle
/// Applied to ALL memories based on access count (Hebbian strengthening)
///
/// Justification:
/// - 0.5% per cycle is gradual (requires ~40 cycles for noticeable effect)
/// - Prevents runaway importance inflation
/// - Matches slow synaptic strengthening in biological systems
pub const POTENTIATION_MAINTENANCE_BOOST: f32 = 0.005; // 0.5% per cycle

/// Access count threshold for potentiation boost
/// Memories accessed more than this get a maintenance boost
///
/// Justification:
/// - 3 accesses indicates pattern of use, not single retrieval
/// - Prevents potentiation of noise/rarely-used memories
pub const POTENTIATION_ACCESS_THRESHOLD: u32 = 3;

// =============================================================================
// MEMORY-EDGE TIER COUPLING CONSTANTS
// =============================================================================
// These constants control the bidirectional coupling between memory tiers
// (Working→Session→LongTerm) and edge tiers (L1→L2→L3).
//
// Reference: Fusi et al. (2005) "Cascade models of synaptically stored memories"
// Principle: Multi-scale consolidation requires cross-system feedback signals.
// =============================================================================

/// When an edge promotes L1→L2, boost the source memory's importance by this amount.
/// Rationale: episodic consolidation signals genuine utility.
///
/// Justification:
/// - 0.015 is ~3x the per-cycle potentiation boost (0.005)
/// - L1→L2 promotion means the edge survived initial decay — meaningful signal
/// - Small enough to avoid runaway inflation from many edges promoting simultaneously
pub const EDGE_PROMOTION_MEMORY_BOOST_L2: f64 = 0.015;

/// When an edge promotes L2→L3, boost the source memory's importance by this amount.
/// Rationale: semantic permanence is a strong signal of value.
///
/// Justification:
/// - 0.03 is 2x the L1→L2 boost — semantic promotion is rarer and more significant
/// - L2→L3 means the edge survived sustained use — memory is genuinely useful
pub const EDGE_PROMOTION_MEMORY_BOOST_L3: f64 = 0.03;

/// When a memory loses all graph edges (orphaned), apply this compensatory boost.
/// Prevents immediate decay death; gives the memory one more cycle to prove value.
///
/// Justification:
/// - 0.05 raises a memory at floor (0.05) to 0.10 — buys ~1-2 more cycles
/// - Not large enough to promote a memory on its own
/// - Analogous to "synaptic tagging" — recently connected memories get brief protection
pub const ORPHAN_COMPENSATORY_BOOST: f64 = 0.05;

/// When a memory has strong graph connections (>= GRAPH_HEALTH_EDGE_SATURATION L2+ edges),
/// reduce the importance threshold for tier promotion by this fraction.
/// E.g., Session→LongTerm normally requires 0.50; with discount: 0.50 * (1 - 0.15) = 0.425
///
/// Justification:
/// - Well-connected memories have proven relational value even if importance is borderline
/// - 15% discount is conservative — won't promote clearly unimportant memories
pub const GRAPH_HEALTH_PROMOTION_DISCOUNT: f64 = 0.15;

/// When a memory has zero graph edges, increase the importance threshold for tier promotion.
/// Penalizes isolated memories that have entities but no surviving connections.
/// E.g., Session→LongTerm normally requires 0.50; with penalty: 0.50 * (1 + 0.10) = 0.55
///
/// Justification:
/// - Isolated memories lack relational context — they need higher intrinsic importance
/// - 10% penalty is mild — doesn't block promotion, just raises the bar
pub const GRAPH_HEALTH_NO_EDGES_PENALTY: f64 = 0.10;

/// Number of L2+ (Episodic or Semantic tier) edges for full promotion discount.
/// Below this count, the discount scales linearly.
///
/// Justification:
/// - 3 L2+ edges means the memory participates in multiple consolidated relationships
/// - Achievable but not trivial — requires sustained co-activation patterns
pub const GRAPH_HEALTH_EDGE_SATURATION: f64 = 3.0;

/// Default compression age (days)
pub const DEFAULT_COMPRESSION_AGE_DAYS: u32 = 7;

/// Default max results for queries
pub const DEFAULT_MAX_RESULTS: usize = 10;

// =============================================================================
// SPREADING ACTIVATION CONSTANTS
// Based on Anderson & Pirolli (1984) "Spread of Activation"
// =============================================================================

/// Activation decay rate for spreading activation
///
/// Formula: A(d) = A₀ × e^(-λd) where λ = SPREADING_DECAY_RATE
///
/// Justification:
/// - 0.5 provides moderate decay - activation halves every ~1.4 hops
/// - Lower values (0.3) spread further but may activate irrelevant nodes
/// - Higher values (0.7) focus on immediate neighbors only
///
/// Reference: Anderson & Pirolli (1984), ACT-R cognitive architecture
pub const SPREADING_DECAY_RATE: f32 = 0.5;

/// Maximum hops for spreading activation (upper bound)
///
/// Justification:
/// - 6 hops captures deep conceptual chains and distant associations
/// - With aggressive decay tuning, signal survives to hop 5-6
/// - Enables discovery of non-obvious relationships
/// - Adaptive algorithm may terminate earlier (see SPREADING_EARLY_TERMINATION_*)
///
/// Tuning (2026-01): Increased from 3 to 6 for deep traversal
pub const SPREADING_MAX_HOPS: usize = 6;

/// Minimum hops before early termination is allowed
///
/// Ensures at least some spreading even when initial activation is high.
/// Prevents returning only directly connected entities.
///
/// Tuning (2026-01): Increased from 1 to 3 to guarantee deep exploration
pub const SPREADING_MIN_HOPS: usize = 3;

/// Activation threshold for pruning weak activations (initial/strict)
///
/// Justification:
/// - 0.005 allows weak but meaningful signals through
/// - Enables 6-hop traversal even with moderate edge strengths
/// - Below this, activation is truly noise
///
/// Tuning (2026-01): Lowered from 0.01 to 0.005 for deep traversal
pub const SPREADING_ACTIVATION_THRESHOLD: f32 = 0.005;

/// Relaxed activation threshold when too few candidates found
///
/// If fewer than SPREADING_MIN_CANDIDATES are activated, the threshold
/// is lowered to this value to allow more exploration.
///
/// Tuning (2026-01): Lowered from 0.005 to 0.001 for deep traversal
pub const SPREADING_RELAXED_THRESHOLD: f32 = 0.001;

/// Minimum candidates before relaxing threshold
///
/// If fewer than this many entities are activated after a hop,
/// the activation threshold is relaxed to explore more.
pub const SPREADING_MIN_CANDIDATES: usize = 5;

/// Early termination threshold - ratio of new activations
///
/// If (new_activations / total_activations) < this ratio,
/// spreading has saturated and we terminate early.
/// Value of 0.05 = less than 5% new activations → terminate.
///
/// Tuning (2026-01): Lowered from 0.1 to 0.05 to resist early termination
pub const SPREADING_EARLY_TERMINATION_RATIO: f32 = 0.05;

/// Early termination threshold - minimum candidate count
///
/// If we have at least this many candidates after minimum hops,
/// we can terminate early (we have enough coverage).
///
/// Tuning (2026-01): Increased from 20 to 50 for richer exploration
pub const SPREADING_EARLY_TERMINATION_CANDIDATES: usize = 50;

/// Activation normalization factor per hop
///
/// Prevents unbounded activation growth by normalizing per hop.
/// After each hop, activations are scaled so max = 1.0 × this factor.
/// Value > 1.0 allows some accumulation while preventing explosion.
///
/// Tuning (2026-01): Increased from 1.5 to 2.0 for more signal preservation
pub const SPREADING_NORMALIZATION_FACTOR: f32 = 2.0;

/// Salience boost factor for initial entity activation (ACT-R inspired)
///
/// Formula: activation = IC_weight × (1 + SALIENCE_BOOST_FACTOR × normalized_salience)
///
/// Justification:
/// - ACT-R uses salience weights (wj) to reflect "attentional weighting"
/// - Normalized salience ensures entities compete for attention budget
/// - Value of 1.0 means high-salience entity (1.0) gets 2x boost vs zero-salience
/// - The fan effect naturally dilutes high-connectivity entities
///
/// Reference: Anderson (1983) "A spreading activation theory of memory"
/// Tuning (2026-01): Initial value 1.0 for full salience effect
pub const SALIENCE_BOOST_FACTOR: f32 = 1.0;

// =============================================================================
// ONTOLOGICAL RETRIEVAL CONSTANTS
// =============================================================================
// Emergent ontology: entity labels and relation types are already stored on every
// graph node/edge. These constants control how that dormant type information is
// activated during retrieval when query signals indicate type-constrained intent.
//
// Reference: Collins & Quillian (1969) "Retrieval time from semantic memory"

/// Minimum ontological intent confidence to activate type-aware retrieval.
/// Below this, retrieval proceeds unfiltered (backward compatible).
/// 0.3 = requires at least a question word OR a matching verb.
pub const ONTOLOGICAL_MIN_CONFIDENCE: f32 = 0.3;

/// Penalty multiplier for edges whose RelationType doesn't match the inferred intent.
/// 0.4 = wrong-type edges carry 40% of normal activation. Not zero — preserves
/// serendipitous discovery through unexpected paths.
/// Reference: Selective spreading (Anderson 1983) — attention gates activation paths.
pub const ONTOLOGICAL_RELATION_PENALTY: f32 = 0.4;

/// Penalty multiplier for target entities whose EntityLabel doesn't match expected types.
/// 0.5 = more generous than relation penalty because NER labels are noisier.
pub const ONTOLOGICAL_ENTITY_PENALTY: f32 = 0.5;

/// Graph density threshold above which ontological filtering is disabled.
/// Dense/young graphs have too many noisy L1 edges for type filtering to help.
/// Uses same scale as entities_average_density() (edges per entity).
pub const ONTOLOGICAL_DENSITY_THRESHOLD: f32 = 8.0;

/// Post-RRF boost per type-matching entity connected to a memory (Layer 4.9).
/// Additive on fused score. 0.08 per match, max 0.25.
pub const ONTOLOGICAL_RERANK_BOOST: f32 = 0.08;

/// Maximum ontological re-rank boost per memory.
pub const ONTOLOGICAL_RERANK_MAX: f32 = 0.25;

// =============================================================================
// RECIPROCAL RANK FUSION (RRF) CONSTANTS
// Based on Cormack, Clarke & Büttcher (2009) — "Reciprocal Rank Fusion
// outperforms Condorcet and individual Rank Learning Methods"
// Standard RRF uses K=60. Lower K gives more weight to top-ranked results.
// Two-stage fusion: inner pass (BM25+vector) and outer pass (graph+hybrid)
// use different K values reflecting different rank distribution properties.
// =============================================================================

/// RRF K for inner BM25+vector fusion in HybridSearchEngine
///
/// Used in `hybrid_search.rs::search_with_dynamic_weights()` to fuse BM25
/// keyword scores with vector similarity scores.
///
/// K=45 (vs standard K=60) slightly emphasizes top-ranked results from each
/// signal. Both BM25 and vector produce well-calibrated rank orderings,
/// so moderate top-weighting is appropriate.
pub const RRF_K_HYBRID_FUSION: f32 = 45.0;

/// RRF K for outer graph+hybrid fusion in Layer 4 of semantic_retrieve()
///
/// Used in `mod.rs::semantic_retrieve()` to fuse graph spreading activation
/// results with the already-fused hybrid (BM25+vector) results.
///
/// K=30 (more aggressive than inner K=45) because graph results are pre-sorted
/// by activation strength and the top graph results carry high signal — justified
/// by ACT-R's spreading activation model where top-activated items have
/// disproportionately stronger evidence (Anderson & Lebiere, 1998).
pub const RRF_K_GRAPH_FUSION: f32 = 30.0;

// =============================================================================
// RETRIEVAL PIPELINE BOOST CONSTANTS
// All boosts are multiplicative factors applied to the RRF-fused base score.
// Multiplicative (not additive) prevents boosts from dwarfing the semantic
// relevance signal computed by RRF fusion.
//
// Design: base_score × (1 + Σ boost_i) × feedback_multiplier
// Each boost_i is small enough that the base score remains the dominant signal.
// =============================================================================

/// Attribute query boost — multiplicative factor for attribute-matching memories
///
/// When a memory matches both entity AND attribute synonyms for an attribute
/// query (e.g., "What is Caroline's relationship status?"), multiply its score.
///
/// Justification:
/// - Attribute matches are strong relevance signals (user is asking about a
///   specific property), so we use a significant boost
/// - 2.5x multiplier (1.0 + 1.5) keeps the base semantic score dominant while
///   giving clear priority to attribute-matching memories
/// - Previously hardcoded as additive 0.5 (31x RRF scale — overwhelming)
///
/// Reference: Attribute-value pair retrieval in knowledge-grounded QA systems
pub const ATTRIBUTE_QUERY_BOOST: f32 = 1.5;

/// Temporal fact boost — multiplicative factor for temporal fact source memories
///
/// When a temporal query matches a fact's temporal references, boost the
/// source memory (e.g., "When did Melanie paint a sunrise?" boosts the memory
/// that recorded the painting event).
///
/// Justification:
/// - Temporal fact matches are high-confidence relevance signals
/// - 2.0x multiplier (1.0 + 1.0) is strong but doesn't override semantic ranking
/// - Previously hardcoded as additive 0.4 (25x RRF scale — overwhelming)
///
/// Reference: TEMPR temporal retrieval approach (multi-hop temporal reasoning)
pub const TEMPORAL_FACT_BOOST: f32 = 1.0;

/// Activation bonus scale — maximum contribution of graph spreading activation
///
/// ACT-R spreading activation provides a relevance signal from the knowledge
/// graph topology. This constant scales the activation value (0-1) into a
/// multiplicative boost on the base score.
///
/// Justification:
/// - Graph activation is a complementary signal, not a primary one
/// - 0.3 means a fully-activated memory gets 1.3x its base score
/// - Scaled further by graph_w (density weight), so effective range is 0.03-0.15
/// - Previously hardcoded as additive 0.2 * graph_w (up to 6x RRF scale)
///
/// Reference: Anderson & Lebiere (1998) ACT-R spreading activation theory
pub const ACTIVATION_BONUS_SCALE: f32 = 0.3;

/// Recency boost scale — maximum multiplicative boost for recent memories
///
/// Applied as: recency_boost = exp(-RECENCY_DECAY_RATE × hours) × scale
/// This modulates base score rather than adding to it.
///
/// Justification:
/// - 0.5 means a just-created memory gets up to 1.5x its base score
/// - Decays exponentially: 24h ≈ 1.37x, 72h ≈ 1.24x, 168h ≈ 1.09x
/// - Previously hardcoded as additive 0.1 (5x RRF scale — dominant signal)
///
/// Reference: Wixted (2004) exponential-power law forgetting curves
pub const RECENCY_BOOST_SCALE: f32 = 0.5;

/// Arousal boost scale — multiplicative weight for emotional arousal signal
///
/// High-arousal memories (errors, breakthroughs, critical decisions) should
/// be more easily retrieved. Arousal is in [0, 1].
///
/// Justification:
/// - 0.15 means max arousal gives 1.15x boost (modest but meaningful)
/// - Previously hardcoded as additive 0.05 (3x RRF scale)
///
/// Reference: LaBar & Cabeza (2006) emotional arousal enhances memory retrieval
pub const AROUSAL_BOOST_SCALE: f32 = 0.15;

/// Credibility boost scale — multiplicative weight for source credibility
///
/// Applied only when credibility > 0.5 (above-average sources).
/// Formula: (credibility - 0.5) × scale
///
/// Justification:
/// - 0.2 means credibility=1.0 gives (0.5 × 0.2) = 0.1 → 1.1x boost
/// - Modest: source credibility is a tiebreaker, not a ranking signal
/// - Previously hardcoded as additive (credibility - 0.5) * 0.1
pub const CREDIBILITY_BOOST_SCALE: f32 = 0.2;

/// Same-episode boost — additive score for memories sharing the current episode
///
/// When the query specifies an episode_id and a candidate belongs to the same
/// episode, this boost surfaces co-occurring memories from the same work session.
///
/// Justification:
/// - 0.3 is an additive constant in the contextual scoring layer (not the RRF
///   multiplicative pipeline), matching the scale of other additive adjustments
///   in `apply_context_scoring` (credibility +0.05, mood congruence +0.1)
/// - Episode membership is a strong relevance signal — co-occurring memories
///   share causal/temporal context that semantic similarity alone cannot capture
///
/// Reference: Tulving (1983) "Elements of Episodic Memory" — encoding specificity
pub const SAME_EPISODE_BOOST: f32 = 0.3;

/// Temporal match boost — maximum multiplicative boost for temporal date matching
///
/// Three tiers:
/// - Exact date match: TEMPORAL_MATCH_BOOST_EXACT
/// - Within 7 days: linearly scaled
/// - Within 30 days: smaller linearly scaled boost
///
/// Justification:
/// - Exact temporal match is a very strong relevance signal for temporal queries
/// - 1.5x for exact, decaying to 1.0x at 30 days
/// - Previously hardcoded as additive 0.25/0.15/0.05 (16x/10x/3x RRF scale)
///
/// Reference: TEMPR multi-hop temporal retrieval
pub const TEMPORAL_MATCH_BOOST_EXACT: f32 = 0.5;
pub const TEMPORAL_MATCH_BOOST_WEEK: f32 = 0.3;
pub const TEMPORAL_MATCH_BOOST_MONTH: f32 = 0.1;

/// Temporal pre-filter boost — multiplicative boost for memories within the query's date range
///
/// When a query has parsed temporal references (e.g., "yesterday", "in March 2026"),
/// we pre-fetch memories from that date range via SearchCriteria::ByDate and boost
/// them in Layer 4.45 of the fusion pipeline. This ensures date-relevant memories
/// rise above semantically similar but temporally wrong results.
///
/// 0.15 is conservative: a moderate nudge that won't override strong semantic matches
/// but gives temporal-range memories a meaningful advantage.
pub const TEMPORAL_PREFILTER_BOOST: f32 = 0.15;

/// Minimum confidence for temporal prefix injection into query embeddings
///
/// Only inject a temporal context prefix (e.g., "[March 2026]") into the query
/// embedding when parsed temporal refs have confidence >= this threshold.
/// Prevents noisy prefix injection from low-confidence date parses.
pub const TEMPORAL_PREFIX_MIN_CONFIDENCE: f32 = 0.8;

/// Prospective signal boost — per-match multiplicative factor for goal-relevant memories
///
/// Memories matching active goals/reminders get boosted to surface proactively.
///
/// Justification:
/// - 0.25 per match, max 0.75 total → up to 1.75x boost
/// - Previously hardcoded as additive 0.15 per match, max 0.5
pub const PROSPECTIVE_BOOST_PER_MATCH: f32 = 0.25;
pub const PROSPECTIVE_BOOST_MAX: f32 = 0.75;

/// Hebbian association weight — contribution of learned graph associations
///
/// Scales the Hebbian boost from strengthened graph edges into the base score.
///
/// Justification:
/// - 0.1 (10%) means graph associations are a modest supplement to RRF
/// - Previously hardcoded as 0.1 (additive, but magnitude was correct given
///   Hebbian scores are already in a small range)
pub const HEBBIAN_ASSOCIATION_WEIGHT: f32 = 0.1;

/// Importance scoring factor — how much importance modulates the retrieval score
///
/// Formula: importance_factor = SCORING_IMPORTANCE_FLOOR + importance × SCORING_IMPORTANCE_RANGE
/// Range [0.7, 1.0]: low-importance memories lose up to 30% of base score.
///
/// Reference: Importance as a modulator of encoding strength (Craik & Lockhart 1972)
pub const SCORING_IMPORTANCE_FLOOR: f32 = 0.7;
pub const SCORING_IMPORTANCE_RANGE: f32 = 0.3;

/// Feedback momentum range — symmetric ±15% multiplicative adjustment
///
/// Positive momentum (helpful) boosts up to 15%, negative (misleading) suppresses up to 15%.
///
/// Reference: Reinforcement learning in memory retrieval (Anderson & Bjork 1994)
pub const FEEDBACK_MOMENTUM_SCALE: f32 = 0.15;

/// Recency decay rate — exponential time constant for recency scoring
///
/// Formula: exp(-RECENCY_DECAY_RATE × hours_old)
/// λ = 0.01 means ~50% at 70 hours, ~25% at 140 hours
///
/// Reference: Wixted (2004) time-based forgetting curves
pub const RECENCY_DECAY_RATE: f32 = 0.01;

// =============================================================================
// EDGE-TIER TRUST WEIGHTS FOR SPREADING ACTIVATION
// Based on hippocampal-cortical consolidation: edges that survive decay are
// more reliable for graph traversal. Dense graphs (L1) are noisy for search,
// sparse consolidated graphs (L3) have high-signal paths.
//
// Key insight: Graph search optimality depends on edge tier:
// - L1 (Working): Dense, noisy - graph search doesn't discriminate well
// - L2 (Episodic): Moderate - balanced trust
// - L3 (Semantic): Sparse, proven - graph search follows meaningful paths
// - LTP: Gold standard - survived many activations
// =============================================================================

/// Trust weight for L1 (Working) tier edges in spreading activation
///
/// Justification:
/// - 0.20 (20%) - low trust because L1 is dense and noisy
/// - New edges haven't proven their value yet
/// - Graph search not optimal for dense regions
pub const EDGE_TIER_TRUST_L1: f32 = 0.20;

/// Trust weight for L2 (Episodic) tier edges in spreading activation
///
/// Justification:
/// - 0.50 (50%) - moderate trust, edges have survived initial decay
/// - In transition between working and semantic memory
/// - Some signal, some noise
pub const EDGE_TIER_TRUST_L2: f32 = 0.50;

/// Trust weight for L3 (Semantic) tier edges in spreading activation
///
/// Justification:
/// - 0.80 (80%) - high trust, edges consolidated to long-term storage
/// - Sparse graph = high-signal paths
/// - Graph search is optimal for these edges
pub const EDGE_TIER_TRUST_L3: f32 = 0.80;

/// Trust weight for LTP (potentiated) edges in spreading activation
///
/// Justification:
/// - 0.95 (95%) - highest trust, survived 10+ co-activations
/// - These are gold-standard learned associations
/// - Graph search strongly follows these paths
pub const EDGE_TIER_TRUST_LTP: f32 = 0.95;

// =============================================================================
// MEMORY-TIER GRAPH WEIGHT MULTIPLIERS (SHO-D2)
// Based on Cowan's model: memories in different tiers have different graph trust.
// Working memories are dense/noisy, LongTerm memories are sparse/proven.
// These multiply the base graph_weight in hybrid scoring.
// =============================================================================

/// Graph weight multiplier for Working tier memories
///
/// Justification:
/// - 0.3 (30% of base graph weight) - working memory is dense, noisy
/// - Vector search dominates for recent/working memories
/// - Graph associations haven't been tested yet
pub const MEMORY_TIER_GRAPH_MULT_WORKING: f32 = 0.3;

/// Graph weight multiplier for Session tier memories
///
/// Justification:
/// - 0.6 (60% of base graph weight) - session memory is transitional
/// - Some associations have proven useful within the session
/// - Balanced between vector and graph
pub const MEMORY_TIER_GRAPH_MULT_SESSION: f32 = 0.6;

/// Graph weight multiplier for LongTerm tier memories
///
/// Justification:
/// - 1.0 (100% of base graph weight) - long-term memories are proven
/// - Associations survived decay, graph search is optimal
/// - Full trust in graph-based retrieval
pub const MEMORY_TIER_GRAPH_MULT_LONGTERM: f32 = 1.0;

/// Graph weight multiplier for Archive tier memories
///
/// Justification:
/// - 1.2 (120% of base graph weight) - archived memories are gold
/// - Only accessed via strong associations
/// - Boost graph weight to surface archived knowledge
pub const MEMORY_TIER_GRAPH_MULT_ARCHIVE: f32 = 1.2;

// =============================================================================
// LONG-TERM POTENTIATION (LTP) CONSTANTS
// Based on synaptic plasticity and Hebbian learning theory
// =============================================================================

/// Learning rate for Hebbian edge strengthening
///
/// How much edge strength increases per co-activation.
///
/// Justification:
/// - 0.1 (10%) is moderate - requires ~10 co-activations to saturate
/// - Matches empirical synaptic LTP rates
///
/// Reference: Bi & Poo (1998), Hebbian learning
pub const LTP_LEARNING_RATE: f32 = 0.1;

/// Half-life in days for synapse decay without use
///
/// Justification:
/// - 14 days matches typical project/task cycles
/// - Unused associations fade but don't disappear immediately
/// - LTP edges decay 10x slower (see LTP_DECAY_FACTOR)
pub const LTP_DECAY_HALF_LIFE_DAYS: f64 = 14.0;

/// Activation count threshold for Long-Term Potentiation
///
/// After this many co-activations, the synapse becomes "potentiated"
/// and decays much slower.
///
/// Justification:
/// - 10 activations indicates consistent pattern, not coincidence
/// - Matches biological LTP threshold (~10-100 activations)
pub const LTP_THRESHOLD: u32 = 10;

/// Time-aware LTP: activation threshold for long-lived edges (SHO-D3)
///
/// Edges older than LTP_TIME_AWARE_DAYS can potentiate with fewer activations.
/// An edge that persists for 30+ days with 5 activations shows sustained value.
///
/// Justification:
/// - 5 activations over 30+ days = sustained, not coincidental
/// - Rewards edges that survive despite decay pressure
/// - Mimics biological late-phase LTP (protein synthesis dependent)
pub const LTP_TIME_AWARE_THRESHOLD: u32 = 5;

/// Time-aware LTP: minimum edge age in days (SHO-D3)
///
/// Edges must be at least this old to qualify for time-aware LTP.
///
/// Justification:
/// - 30 days = ~1 month of sustained relevance
/// - Matches memory consolidation timeline (hippocampal → cortical)
/// - Combined with 5 activations, prevents false positives
pub const LTP_TIME_AWARE_DAYS: i64 = 30;

/// Decay factor for potentiated synapses
///
/// Potentiated synapses decay at this fraction of the normal rate.
///
/// Justification:
/// - 0.1 means potentiated edges decay 10x slower
/// - Important associations persist longer
pub const LTP_DECAY_FACTOR: f32 = 0.1;

/// Minimum synapse strength floor
///
/// Synapses never decay below this value.
///
/// Justification:
/// - 0.01 (1%) allows recovery if pattern re-emerges
/// - Matches SPREADING_ACTIVATION_THRESHOLD
pub const LTP_MIN_STRENGTH: f32 = 0.01;

/// LTP prune floor — strip LTP protection when strength reaches this level
///
/// If a potentiated edge's strength drops to LTP_PRUNE_FLOOR, its LTP status
/// is downgraded to None and normal prune logic applies. This prevents
/// immortal zombie edges that retain LTP protection despite near-zero strength.
///
/// Justification:
/// - 0.05 is above LTP_MIN_STRENGTH (0.01) so the edge has meaningfully decayed
/// - 5x above the absolute floor gives enough margin for recovery attempts
/// - Without this, Weekly/Full edges persist forever even if never reactivated
pub const LTP_PRUNE_FLOOR: f32 = 0.05;

// =============================================================================
// MULTI-SCALE LTP CONSTANTS (PIPE-4)
// Based on multi-timescale memory consolidation research
// Different activation patterns indicate different types of learning:
// - Burst: High immediate interest (working memory consolidation)
// - Weekly: Routine/habit (procedural knowledge)
// - Monthly: Sustained low-frequency (semantic knowledge)
// =============================================================================

/// Burst LTP: minimum activations within 24 hours
///
/// High-frequency activation indicates immediate high interest.
///
/// Justification:
/// - 5 activations in 24h is significant engagement
/// - Matches early-phase LTP (E-LTP) in neuroscience
/// - Triggers temporary protection to allow pattern to prove sustained value
///
/// Reference: Frey & Morris (1997) "Synaptic tagging and LTP"
pub const LTP_BURST_THRESHOLD: u32 = 5;

/// Burst LTP: time window in hours
///
/// Activations must occur within this window for burst detection.
pub const LTP_BURST_WINDOW_HOURS: i64 = 24;

/// Burst LTP: decay protection factor
///
/// Burst-potentiated edges decay at this fraction of normal rate.
/// Less protection than full LTP - must prove sustained value.
///
/// Justification:
/// - 0.5 = 2x slower decay (temporary protection)
/// - Allows burst patterns time to consolidate or fade
pub const LTP_BURST_DECAY_FACTOR: f32 = 0.5;

/// Burst LTP: protection duration in hours
///
/// Burst potentiation expires after this duration without upgrade.
///
/// Justification:
/// - 48 hours gives pattern time to recur or consolidate
/// - Matches E-LTP duration in biological systems
pub const LTP_BURST_DURATION_HOURS: i64 = 48;

/// Weekly LTP: minimum activations per week
///
/// Regular weekly activation indicates habit/routine.
///
/// Justification:
/// - 3 activations per week = consistent but not excessive use
/// - Matches habit formation research (~3 repetitions to form habit)
pub const LTP_WEEKLY_THRESHOLD: u32 = 3;

/// Weekly LTP: minimum weeks of consistent activation
///
/// Pattern must persist for this many weeks to qualify.
///
/// Justification:
/// - 2 weeks ensures pattern is not a one-off busy period
/// - Matches habit consolidation timeline (14-21 days)
///
/// Reference: Lally et al. (2010) "How habits are formed"
pub const LTP_WEEKLY_MIN_WEEKS: u32 = 2;

/// Weekly LTP: decay protection factor
///
/// Weekly-potentiated edges decay at this fraction of normal rate.
/// More protection than burst, less than full LTP.
///
/// Justification:
/// - 0.3 = ~3x slower decay (moderate protection)
/// - Proven weekly value deserves more protection than burst
pub const LTP_WEEKLY_DECAY_FACTOR: f32 = 0.3;

/// Activation history capacity for L2 (Episodic) tier edges
///
/// L2 edges store this many recent activation timestamps.
///
/// Calibration:
/// - 30 timestamps = 1 per day × 30-day episodic lifecycle (L2_MAX_AGE_DAYS)
/// - Perfectly sized: no wasted capacity, full lifecycle coverage
/// - Sufficient for weekly pattern detection within the episodic window
/// - Memory: 30 × 8 bytes = 240 bytes per L2 edge
pub const ACTIVATION_HISTORY_L2_CAPACITY: usize = 30;

/// Activation history capacity for L3 (Semantic) tier edges
///
/// L3 edges store this many recent activation timestamps.
///
/// Calibration:
/// - 200 timestamps ≈ 15 months at 3×/week activation frequency
/// - L3 edges are near-permanent (L3_DECAY_PER_MONTH = 0.02), so deep history is warranted
/// - Sufficient for monthly and seasonal pattern detection on long-lived semantic edges
/// - Memory: 200 × 8 bytes = 1600 bytes per L3 edge
pub const ACTIVATION_HISTORY_L3_CAPACITY: usize = 200;

// =============================================================================
// UNIFIED LTP READINESS MODEL (PIPE-5)
// Based on neuroscience: multiple paths to LTP (E-LTP intensity, L-LTP repetition)
// Unified scoring allows either path to dominate while both contribute.
// Reference: Frey & Morris (1997) Synaptic tagging and capture
// =============================================================================

/// Weight for activation count in LTP readiness formula
///
/// count_score = activation_count / adjusted_threshold
/// Higher weight means repetition matters more.
///
/// Justification:
/// - 0.5 gives equal initial weight to count and strength
/// - Hebbian "fire together wire together" requires repeated co-activation
pub const LTP_READINESS_COUNT_WEIGHT: f32 = 0.5;

/// Weight for strength in LTP readiness formula
///
/// strength_score = strength / strength_floor
/// Higher weight means intensity/durability matters more.
///
/// Justification:
/// - 0.5 gives equal initial weight to count and strength
/// - Strength surviving decay proves consistent relevance
pub const LTP_READINESS_STRENGTH_WEIGHT: f32 = 0.5;

/// Weight for entity confidence bonus (tagging effect)
///
/// tag_bonus = entity_confidence * LTP_READINESS_TAG_WEIGHT
/// High-confidence entities provide synaptic tagging advantage.
///
/// Justification:
/// - 0.3 max bonus allows high-confidence edges to reach LTP ~30% faster
/// - Based on synaptic tagging: behaviorally relevant synapses consolidate faster
///
/// Reference: Moncada & Viola (2007) Behavioral tagging
pub const LTP_READINESS_TAG_WEIGHT: f32 = 0.3;

/// Minimum LTP threshold (for high-confidence edges)
///
/// Edges with high entity confidence need fewer activations.
///
/// Justification:
/// - 7 activations for confidence >= 0.8
/// - High-quality entity extraction indicates reliable semantic signal
pub const LTP_THRESHOLD_MIN: u32 = 7;

/// Maximum LTP threshold (for low-confidence edges)
///
/// Edges with low entity confidence need more activations.
///
/// Justification:
/// - 13 activations for confidence <= 0.3
/// - Weak entity extraction needs more behavioral evidence
pub const LTP_THRESHOLD_MAX: u32 = 13;

/// Strength floor for L2 edges to qualify for Full LTP
///
/// L2 edges need strength above this to reach Full LTP status.
///
/// Justification:
/// - 0.65 ensures edge has survived some decay pressure
/// - Lower than L3 because L2 is still proving itself
pub const LTP_STRENGTH_FLOOR_L2: f32 = 0.65;

/// Strength floor for L3 edges to qualify for Full LTP
///
/// L3 edges need strength above this to reach Full LTP status.
///
/// Justification:
/// - 0.80 matches the old auto-LTP threshold
/// - L3 edges must demonstrate high durability for permanent protection
pub const LTP_STRENGTH_FLOOR_L3: f32 = 0.80;

/// LTP readiness threshold for Full LTP status
///
/// When ltp_readiness() >= this value, edge gets Full LTP.
///
/// Justification:
/// - 1.0 requires either balanced contribution from both paths
///   or dominant contribution from one path + tag bonus
pub const LTP_READINESS_THRESHOLD: f32 = 1.0;

// =============================================================================
// BIDIRECTIONAL SPREADING ACTIVATION (PIPE-7)
// Based on ACT-R spreading activation and meet-in-middle search optimization
// Reference: Collins & Loftus (1975) "A spreading-activation theory of semantic processing"
// =============================================================================

/// Minimum focal entities to enable bidirectional spreading
///
/// With fewer entities, unidirectional spreading is sufficient.
/// Bidirectional only helps when we can split query entities into two groups.
///
/// Justification:
/// - 2 entities minimum allows forward/backward split
/// - Single entity queries use standard spreading (no intersection possible)
pub const BIDIRECTIONAL_MIN_ENTITIES: usize = 2;

/// Activation boost for intersection entities (found by both directions)
///
/// Entities activated from both forward and backward directions are
/// "bridge" concepts connecting the query entities - highly relevant.
///
/// Justification:
/// - 1.5x boost prioritizes intersection entities without overwhelming
/// - ACT-R: activation spreads from multiple sources, intersection = high relevance
/// - Higher values (2.0+) might over-emphasize bridges vs directly connected
pub const BIDIRECTIONAL_INTERSECTION_BOOST: f32 = 1.5;

/// Minimum activation from each direction to qualify as intersection
///
/// Both forward and backward activations must exceed this threshold
/// for an entity to receive the intersection boost.
///
/// Justification:
/// - Uses half of SPREADING_ACTIVATION_THRESHOLD (0.0025)
/// - Prevents noise from counting as intersection
/// - Entity must receive meaningful activation from both sides
pub const BIDIRECTIONAL_INTERSECTION_MIN: f32 = 0.0025;

// -----------------------------------------------------------------------------
// Density-Adaptive Hop Count
// Graph lifecycle: Dense (fresh) → Sparse (mature) as decay prunes weak edges
// Fresh system: many noisy L1 edges → fewer hops to avoid noise
// Mature system: curated L2/L3 edges → more hops to find valuable connections
// -----------------------------------------------------------------------------

/// Hops per direction for dense graphs (density > DENSE_THRESHOLD)
///
/// Dense graphs have many paths, risk of noise overwhelming signal.
/// Fewer hops prevents over-exploration of noisy connections.
///
/// Justification:
/// - Fresh systems have many L1 working memory edges (high noise)
/// - 2 hops sufficient to find direct and 1-hop-away connections
/// - Effective depth = 4 (2 forward + 2 backward)
pub const BIDIRECTIONAL_HOPS_DENSE: usize = 2;

/// Hops per direction for medium-density graphs
///
/// Balanced exploration for graphs in transition from dense to sparse.
///
/// Justification:
/// - 3 hops is the "Goldilocks" zone for most graphs
/// - Effective depth = 6 (matches SPREADING_MAX_HOPS)
pub const BIDIRECTIONAL_HOPS_MEDIUM: usize = 3;

/// Hops per direction for sparse graphs (density < SPARSE_THRESHOLD)
///
/// Sparse graphs have fewer but higher-quality edges (survived decay).
/// More hops needed to find connections through curated paths.
///
/// Justification:
/// - Mature systems have mostly L2/L3 edges (high signal)
/// - 4 hops allows finding connections through longer semantic chains
/// - Effective depth = 8 (deeper exploration is safe with quality edges)
pub const BIDIRECTIONAL_HOPS_SPARSE: usize = 4;

/// Density threshold below which graph is considered sparse
///
/// Sparse = fewer edges per memory, higher quality, needs more exploration.
///
/// Justification:
/// - 0.5 edges/memory means most memories have 0-1 graph connections
/// - Indicates mature, pruned graph where surviving edges are valuable
/// - Aligns with DENSITY_THRESHOLD_MIN used in density-weighted scoring
pub const BIDIRECTIONAL_DENSITY_SPARSE: f32 = 0.5;

/// Density threshold above which graph is considered dense
///
/// Dense = many edges per memory, potentially noisy, limit exploration.
///
/// Justification:
/// - 2.0 edges/memory means active graph with many connections
/// - Fresh systems or highly interconnected domains
/// - Aligns with DENSITY_THRESHOLD_MAX used in density-weighted scoring
pub const BIDIRECTIONAL_DENSITY_DENSE: f32 = 2.0;

// =============================================================================
// HYBRID DECAY MODEL CONSTANTS (SHO-103)
// Based on Wixted & Ebbesen (1991) - power-law forgetting matches human memory
// Hybrid model: exponential for consolidation, power-law for long-term retention
// =============================================================================

/// Crossover point in days from exponential to power-law decay
///
/// Below this threshold: exponential decay (fast consolidation)
/// Above this threshold: power-law decay (slow long-term forgetting)
///
/// Justification:
/// - 3 days matches memory consolidation window in neuroscience
/// - Hippocampal-cortical transfer takes ~72 hours
/// - Short-term plasticity is exponential, long-term follows power-law
///
/// Reference: Wixted (2004) "The psychology and neuroscience of forgetting"
pub const DECAY_CROSSOVER_DAYS: f64 = 3.0;

/// Power-law exponent (β) for long-term forgetting
///
/// Formula: A(t) = A_cross × (t / t_cross)^(-β)
///
/// Justification:
/// - β = 0.5 produces moderate long-term retention
/// - Lower β (0.3) = slower forgetting, heavier tail
/// - Higher β (0.7) = faster forgetting, lighter tail
/// - 0.5 matches empirical human forgetting curves
///
/// Reference: Wixted & Ebbesen (1991), Anderson & Schooler (1991)
pub const POWERLAW_BETA: f64 = 0.5;

/// Power-law exponent for potentiated/important memories
///
/// Potentiated synapses and high-importance memories use lower β
/// for even slower forgetting (heavier tail).
///
/// Justification:
/// - 0.3 exponent means 50% retention at ~11 days vs ~4 days for β=0.5
/// - Matches LTP protection ratio (10x slower decay)
pub const POWERLAW_BETA_POTENTIATED: f64 = 0.3;

/// Exponential decay rate (λ) for consolidation phase
///
/// Used during t < DECAY_CROSSOVER_DAYS.
/// λ = ln(2) / half_life, where half_life ≈ 1 day for consolidation.
///
/// Justification:
/// - Fast initial decay clears noise and weak associations
/// - Matches short-term synaptic depression rates
/// - After 3 days at this rate: ~12.5% retention → power-law takes over
pub const DECAY_LAMBDA_CONSOLIDATION: f64 = 0.693; // ln(2) / 1.0 day

// =============================================================================
// INFORMATION CONTENT (IC) WEIGHTS
// Based on linguistic analysis for query parsing
// Reference: Lioma & Ounis (2006) "Information Content Weighting"
// =============================================================================

/// Information content weight for nouns
///
/// Nouns are the most discriminative in queries.
///
/// Justification:
/// - 2.3 matches empirical IC measurements for English nouns
/// - Nouns carry the core semantic meaning ("Rust", "database", "memory")
pub const IC_NOUN: f32 = 2.3;

/// Information content weight for adjectives
///
/// Adjectives provide discriminative context.
///
/// Justification:
/// - 1.7 reflects moderate discriminative power
/// - Adjectives narrow down ("fast database" vs "reliable database")
pub const IC_ADJECTIVE: f32 = 1.7;

/// Information content weight for verbs
///
/// Verbs are less discriminative ("bus stops" in IR terminology).
///
/// Justification:
/// - 1.0 baseline weight
/// - Common verbs like "is", "has", "uses" add little discriminative value
pub const IC_VERB: f32 = 1.0;

// =============================================================================
// SERVER TIMEOUT CONSTANTS
// =============================================================================

/// Graceful shutdown timeout in seconds
///
/// Maximum time to wait for the full graceful shutdown sequence.
///
/// Justification:
/// - 120 seconds accommodates drain (5s) + flush (30s) + vector save (60s)
/// - Overall budget prevents indefinite hangs even if individual phases stall
pub const GRACEFUL_SHUTDOWN_TIMEOUT_SECS: u64 = 120;

/// Database flush timeout in seconds
///
/// Maximum time to wait for RocksDB flush on shutdown.
///
/// Justification:
/// - 30 seconds accommodates large write buffers and multiple per-user DBs
/// - Prevents data loss on clean shutdown
pub const DATABASE_FLUSH_TIMEOUT_SECS: u64 = 30;

/// Vector index save timeout in seconds
///
/// Maximum time to wait for HNSW index persistence.
///
/// Justification:
/// - 60 seconds handles large indices (100K+ vectors)
/// - Startup rebuild is fallback if save is interrupted
pub const VECTOR_INDEX_SAVE_TIMEOUT_SECS: u64 = 60;

// =============================================================================
// COMPRESSION SAFETY LIMITS
// =============================================================================

/// Maximum decompressed size in bytes (safety limit)
///
/// Prevents zip bomb attacks and memory exhaustion.
///
/// Justification:
/// - 10MB is generous for any single memory entry
/// - Larger content should be chunked at ingestion time
pub const MAX_DECOMPRESSED_SIZE: i32 = 10 * 1024 * 1024;

/// Maximum allowed compression ratio (decompressed / compressed)
///
/// Prevents zip bomb attacks where small payloads decompress to huge sizes.
/// Normal LZ4 compression ratios are typically 2:1 to 5:1 for text.
/// Ratios above 100:1 are suspicious and indicate potential attack.
///
/// Justification:
/// - LZ4 typical ratio for text: 2-5x
/// - LZ4 maximum theoretical ratio: ~255x (all zeros)
/// - 100:1 is generous for legitimate data while catching attacks
/// - Combined with MAX_DECOMPRESSED_SIZE for defense in depth
pub const MAX_COMPRESSION_RATIO: usize = 100;

// =============================================================================
// PREFETCH RECENCY BOOST CONSTANTS
// For anticipatory prefetch relevance scoring
// =============================================================================

/// Hours threshold for full recency boost in prefetch
///
/// Memories younger than this get the full recency boost.
///
/// Justification:
/// - 24 hours captures "today's context"
/// - Recent memories are most likely to be relevant
pub const PREFETCH_RECENCY_FULL_HOURS: i64 = 24;

/// Hours threshold for partial recency boost in prefetch
///
/// Memories between FULL and this threshold get partial boost.
///
/// Justification:
/// - 168 hours = 1 week
/// - Weekly work cycles are common patterns
pub const PREFETCH_RECENCY_PARTIAL_HOURS: i64 = 168;

/// Recency boost for very recent memories (< 24h)
pub const PREFETCH_RECENCY_FULL_BOOST: f32 = 0.1;

/// Recency boost for recent memories (24h - 1 week)
pub const PREFETCH_RECENCY_PARTIAL_BOOST: f32 = 0.05;

/// Temporal window (hours) for time-of-day pattern matching in prefetch
///
/// Justification:
/// - 2 hour window captures "similar time of day" patterns
/// - E.g., if it's 10am, consider memories from 8am-12pm
pub const PREFETCH_TEMPORAL_WINDOW_HOURS: i32 = 2;

// =============================================================================
// MEMORY REPLAY CONSTANTS (SHO-105)
// Based on sleep consolidation research: hippocampal replay during rest
// strengthens important memory traces through co-activation
// =============================================================================

/// Minimum importance score for a memory to be eligible for replay
///
/// Justification:
/// - 0.3 threshold selects top ~30% of memories by importance
/// - Low-importance memories don't benefit from replay (noise)
/// - Combined with recency, targets recent + important memories
///
/// Reference: Rasch & Born (2013) "About Sleep's Role in Memory"
pub const REPLAY_IMPORTANCE_THRESHOLD: f32 = 0.3;

/// Maximum age in days for memories eligible for replay
///
/// Justification:
/// - 14 days gives wider window for hourly consolidation cycles
/// - Older memories are already consolidated (in power-law phase)
/// - Focus replay resources on recent, unconsolidated memories
pub const REPLAY_MAX_AGE_DAYS: i64 = 14;

/// Minimum emotional arousal for priority replay
///
/// High-arousal memories (emotional events) get priority in replay queue.
///
/// Justification:
/// - 0.6 threshold selects emotionally significant events
/// - Matches amygdala modulation threshold for enhanced encoding
/// - Emotional memories benefit most from replay consolidation
///
/// Reference: LaBar & Cabeza (2006) "Cognitive neuroscience of emotional memory"
pub const REPLAY_AROUSAL_THRESHOLD: f32 = 0.6;

/// Strength boost per replay cycle for memory activation
///
/// Justification:
/// - 0.05 (5%) is small enough to require multiple replays for significant effect
/// - Matches biological synaptic strengthening rates
/// - 10 replay cycles → ~60% increase (compound effect)
pub const REPLAY_STRENGTH_BOOST: f32 = 0.05;

/// Strength boost for edges during replay co-activation
///
/// Justification:
/// - 0.08 (8%) strengthens associations during replay
/// - Slightly higher than memory boost (edges benefit more from co-activation)
/// - Simulates sleep spindle-mediated synaptic strengthening
pub const REPLAY_EDGE_BOOST: f32 = 0.08;

/// Maximum memories to replay per cycle
///
/// Justification:
/// - 50 memories per cycle limits computational cost
/// - Typical replay session processes 30-50 memories
/// - Higher values may cause maintenance cycle delays
pub const REPLAY_BATCH_SIZE: usize = 50;

/// Minimum connected memories required for replay network
///
/// Justification:
/// - 0 allows importance-only qualification (connections not required)
/// - Isolated memories can bootstrap graph edges through replay co-activation
/// - Prevents chicken-and-egg: memories need connections to replay, but replay creates connections
pub const REPLAY_MIN_CONNECTIONS: usize = 0;

// =============================================================================
// MEMORY INTERFERENCE CONSTANTS (SHO-106)
// Based on interference theory: similar memories compete and can disrupt each other
// =============================================================================

/// Similarity threshold for interference detection
///
/// When a new memory exceeds this similarity to an existing memory,
/// interference effects are triggered.
///
/// Justification:
/// - 0.85 cosine similarity indicates highly similar content
/// - Below this, memories are distinct enough to coexist
/// - Above this, memories compete for the same retrieval cues
///
/// Reference: Postman & Underwood (1973) "Critical issues in interference theory"
pub const INTERFERENCE_SIMILARITY_THRESHOLD: f32 = 0.85;

/// Similarity threshold for severe interference (conflicting memories)
///
/// Justification:
/// - 0.95 indicates nearly identical content
/// - At this level, one memory should dominate
/// - Retroactive interference is strongest
pub const INTERFERENCE_SEVERE_THRESHOLD: f32 = 0.95;

/// Strength reduction per interference event (retroactive)
///
/// When new memory interferes with old, old memory loses this much strength.
///
/// Justification:
/// - 0.1 (10%) is moderate - requires multiple interferences to significantly weaken
/// - Matches empirical retroactive interference rates
/// - Allows recovery if new memory is later found to be incorrect
pub const INTERFERENCE_RETROACTIVE_DECAY: f32 = 0.1;

/// Strength reduction for proactive interference
///
/// Strong old memories can suppress encoding of similar new memories.
///
/// Justification:
/// - 0.05 (5%) is weaker than retroactive (new info typically wins)
/// - Only applies when old memory is very strong (>0.8 importance)
/// - Prevents over-learning effects
pub const INTERFERENCE_PROACTIVE_DECAY: f32 = 0.05;

/// Importance threshold for proactive interference
///
/// Only memories above this importance can cause proactive interference.
///
/// Justification:
/// - 0.8 selects only very strong, well-established memories
/// - Weak memories shouldn't block new learning
/// - Matches schema-based learning effects
pub const INTERFERENCE_PROACTIVE_THRESHOLD: f32 = 0.8;

/// Competition factor during retrieval
///
/// How much similar memories suppress each other during retrieval.
///
/// Justification:
/// - 0.15 provides moderate competition
/// - Higher values cause winner-take-all behavior
/// - Lower values allow more co-retrieval
///
/// Reference: Anderson & Neely (1996) "Interference and inhibition in memory retrieval"
pub const INTERFERENCE_COMPETITION_FACTOR: f32 = 0.15;

/// Time window for interference sensitivity (hours)
///
/// New memories are most vulnerable to interference within this window.
///
/// Justification:
/// - 24 hours matches synaptic consolidation window
/// - After this, memories become more resistant to interference
/// - Combined with DECAY_CROSSOVER_DAYS for full model
pub const INTERFERENCE_VULNERABILITY_HOURS: i64 = 24;

/// Maximum interference events to track per memory
///
/// Justification:
/// - 10 events provides sufficient history
/// - Beyond this, early interference events are less relevant
/// - Limits memory overhead
pub const INTERFERENCE_MAX_TRACKED: usize = 10;

/// Competition close-competitor threshold — ratio above which suppression fires
///
/// When two memories compete, suppression is applied only if the loser's score
/// is within this ratio of the winner's score. Below this ratio, the memory
/// is clearly weaker and passes through without suppression.
///
/// Justification:
/// - 0.9 means only the top 10% competitive band triggers suppression
/// - Below 0.9, the score gap is large enough that interference is minimal
///
/// Reference: Anderson & Bjork (1994) — retrieval-induced forgetting operates
/// primarily on strong competitors, not weak ones
pub const COMPETITION_CLOSE_RATIO: f32 = 0.9;

/// Competition suppression multiplier — scales the suppression penalty
///
/// Formula: suppression = INTERFERENCE_COMPETITION_FACTOR × (1 - ratio) × COMPETITION_SUPPRESSION_SCALE
///
/// Justification:
/// - 10.0 maps the tiny ratio gap (0.01-0.10) to meaningful suppression
/// - With INTERFERENCE_COMPETITION_FACTOR=0.15: max suppression = 0.15 × 0.1 × 10 = 0.15
pub const COMPETITION_SUPPRESSION_SCALE: f32 = 10.0;

/// Minimum score for a suppressed memory to survive competition
///
/// If a memory's score falls below this after suppression, it is fully removed.
/// Above this, it survives with a reduced score.
pub const COMPETITION_SURVIVAL_FLOOR: f32 = 0.1;

/// Interference damage scaling for close survivors vs fully suppressed
///
/// Close survivors (score > COMPETITION_SURVIVAL_FLOOR) record mild interference
/// at this fraction of the full suppression amount. Fully suppressed memories
/// record the full amount.
pub const COMPETITION_SURVIVOR_DAMAGE_RATIO: f32 = 0.3;

/// Connectivity factor divisor for replay candidate prioritization
///
/// Higher connectivity = more important for consolidation.
/// Formula: 1.0 + (connections / divisor).min(max_boost)
pub const REPLAY_CONNECTIVITY_DIVISOR: f32 = 10.0;
pub const REPLAY_CONNECTIVITY_MAX_BOOST: f32 = 0.5;

/// Minimum activation floor after interference decay
pub const INTERFERENCE_ACTIVATION_FLOOR: f32 = 0.05;

// =============================================================================
// PATTERN-TRIGGERED REPLAY CONSTANTS (PIPE-2)
// Based on hippocampal sharp-wave ripple research (Rasch & Born 2013)
// Consolidation should be triggered by meaningful patterns, not fixed intervals
// =============================================================================

/// Entity overlap threshold for pattern detection
///
/// Memories must share entities above this threshold to form a co-occurrence pattern.
///
/// Justification:
/// - 0.4 ensures meaningful overlap (at least 40% shared entities)
/// - Lower values would trigger too many false patterns
/// - Based on entity salience research (Lioma & Ounis 2006)
pub const ENTITY_COOCCURRENCE_THRESHOLD: f32 = 0.4;

/// Minimum memories required for entity co-occurrence pattern
///
/// Justification:
/// - 3 memories ensures a real pattern, not coincidence
/// - Matches "wisdom of crowds" threshold in clustering literature
/// - Fewer than 3 would trigger replay too aggressively
pub const MIN_MEMORIES_PER_PATTERN: usize = 3;

/// Minimum memories required for semantic cluster
///
/// Justification:
/// - 3 memories ensures dense, meaningful cluster
/// - Matches minimum cluster size in DBSCAN-style algorithms
pub const MIN_CLUSTER_SIZE: usize = 3;

/// Semantic similarity threshold for cluster formation
///
/// Justification:
/// - 0.75 cosine similarity indicates strong semantic relationship
/// - Higher than typical retrieval threshold (0.6-0.7)
/// - Ensures clusters are truly semantically coherent
pub const SEMANTIC_CLUSTER_THRESHOLD: f32 = 0.75;

/// Temporal window for session-based clustering (seconds)
///
/// Memories created within this window are grouped into a session cluster.
///
/// Justification:
/// - 1800 seconds (30 minutes) matches typical work session patterns
/// - Based on "attention span" research in HCI literature
/// - Longer windows would merge unrelated sessions
pub const TEMPORAL_CLUSTER_WINDOW_SECS: i64 = 1800;

/// Minimum memories required for temporal cluster trigger
///
/// Justification:
/// - 2 memories is minimum for meaningful session
/// - Single-memory sessions don't benefit from replay
pub const MIN_MEMORIES_PER_SESSION: usize = 2;

/// High importance threshold for salience spike detection
///
/// Memories above this importance trigger immediate replay consideration.
///
/// Justification:
/// - 0.7 selects top ~30% by importance
/// - Matches "significant event" threshold in cognitive psychology
/// - Combined with arousal for emotional modulation
pub const HIGH_IMPORTANCE_THRESHOLD: f32 = 0.7;

/// High arousal threshold for salience spike detection
///
/// Memories above this arousal level trigger immediate replay consideration.
///
/// Justification:
/// - 0.7 arousal indicates emotionally significant content
/// - Matches amygdala activation threshold from neuroscience
/// - Error/surprise events typically exceed this
pub const HIGH_AROUSAL_THRESHOLD: f32 = 0.7;

/// Lower arousal threshold for anticipatory prefetch relevance boosting
///
/// Two-tier arousal gating: prefetch uses a lower bar than salience spike detection.
/// Prefetch asks "is this worth surfacing proactively?" while salience spike asks
/// "is this exceptional enough to trigger replay?"
///
/// Justification:
/// - 0.6 captures moderately arousing memories for prefetch (LaBar & Cabeza, 2006)
/// - Distinct from HIGH_AROUSAL_THRESHOLD (0.7) which gates replay/salience spikes
/// - Memories in [0.6, 0.7) get prefetch boost but don't trigger salience replay
pub const PREFETCH_AROUSAL_THRESHOLD: f32 = 0.6;

/// Surprise factor threshold for salience-triggered replay
///
/// Deviation from running importance average that triggers surprise detection.
///
/// Justification:
/// - 0.3 (30% deviation) is statistically significant
/// - Matches "novelty detection" thresholds in RL literature
/// - Lower values would trigger on normal variation
pub const SURPRISE_THRESHOLD: f32 = 0.3;

/// Minimum confidence for entity pattern triggers
///
/// Pattern confidence must exceed this for replay trigger.
///
/// Justification:
/// - 0.75 ensures high-quality patterns
/// - Combines recency and frequency factors
/// - Prevents triggering on stale or weak patterns
pub const ENTITY_PATTERN_CONFIDENCE: f32 = 0.75;

/// Behavioral pattern change window (hours)
///
/// Minimum time between behavioral change triggers.
///
/// Justification:
/// - 1 hour prevents trigger flooding on rapid changes
/// - Matches typical task-switching patterns
/// - Allows consolidation to complete before next trigger
pub const BEHAVIORAL_PATTERN_WINDOW_HOURS: i64 = 1;

// =============================================================================
// 3-TIER GRAPH DENSITY CONSTANTS (SHO-200)
// Based on neuroscience research on hippocampal-cortical memory consolidation
// =============================================================================
//
// Research basis:
// - Dentate Gyrus (L1): 2-4% active neurons (dense, fast encoding)
// - CA1/CA3 (L2): 0.5-2.5% active (moderate, pattern separation)
// - Neocortex (L3): <1% active (sparse, long-term storage)
//
// References:
// - Engram Memory Encoding (arXiv:2506.01659)
// - Population sparseness & Hebbian plasticity (bioRxiv:2025.06.16.659837)
// - Sparse coding of episodic memory (PNAS 2014)
// =============================================================================

// === L1: WORKING MEMORY (Hippocampus/Dentate Gyrus-like) ===
// Dense connections, fast encoding, aggressive pruning

/// Target edge density for L1 (working memory tier)
///
/// 5% of possible edges should be active at any time.
/// Dense enough for rich associations, sparse enough to avoid noise.
pub const L1_TARGET_DENSITY: f32 = 0.05;

/// Initial weight for new edges in L1
///
/// Edges start at 0.4, needing one co-activation to reach L2 promotion (0.5).
pub const L1_INITIAL_WEIGHT: f32 = 0.4;

/// Decay rate per hour for unused L1 edges
///
/// Calibrated so edges reach L1_PRUNE_THRESHOLD (0.1) at 48 hours:
/// 0.4 × e^(-0.029 × 48) ≈ 0.1
pub const L1_DECAY_PER_HOUR: f32 = 0.029;

/// Maximum age in hours before L1 edge must promote or die
///
/// 48-hour window gives edges time to accumulate co-activations before pruning.
pub const L1_MAX_AGE_HOURS: u32 = 48;

/// Minimum weight threshold for L1 edges
///
/// Edges below this are immediately pruned.
pub const L1_PRUNE_THRESHOLD: f32 = 0.1;

/// Minimum weight required to promote from L1 to L2
///
/// Only edges that reach this strength survive to episodic memory.
pub const L1_PROMOTION_THRESHOLD: f32 = 0.5;

// === L2: EPISODIC MEMORY (CA1/CA3-like) ===
// Moderate density, Hebbian learning determines survival

/// Target edge density for L2 (episodic memory tier)
///
/// 2.5% of possible edges - sparser than L1 but still associative.
pub const L2_TARGET_DENSITY: f32 = 0.025;

/// Initial weight for edges promoted to L2 from L1
///
/// Promoted edges start at 0.5 (they already proved value in L1).
pub const L2_PROMOTION_WEIGHT: f32 = 0.5;

/// Decay rate per day for unused L2 edges
///
/// Calibrated so edges reach L2_PRUNE_THRESHOLD (0.2) at 30 days:
/// 0.5 × e^(-0.031 × 30) ≈ 0.2
pub const L2_DECAY_PER_DAY: f32 = 0.031;

/// Maximum age in days before L2 edge must promote or die
///
/// 30-day window gives episodic associations a full month to consolidate to L3.
pub const L2_MAX_AGE_DAYS: u32 = 30;

/// Minimum weight threshold for L2 edges
///
/// Higher bar than L1 - weak edges don't survive episodic tier.
pub const L2_PRUNE_THRESHOLD: f32 = 0.2;

/// Minimum weight required to promote from L2 to L3
///
/// Only strongly reinforced edges become permanent semantic knowledge.
pub const L2_PROMOTION_THRESHOLD: f32 = 0.7;

// === L3: SEMANTIC MEMORY (Neocortex-like) ===
// Very sparse, near-permanent, abstract associations

/// Target edge density for L3 (semantic memory tier)
///
/// <1% of possible edges - only the most important survive.
pub const L3_TARGET_DENSITY: f32 = 0.008;

/// Initial weight for edges promoted to L3 from L2
///
/// Consolidated edges are strong (0.7) and resistant to decay.
pub const L3_PROMOTION_WEIGHT: f32 = 0.7;

/// Decay rate per month for unused L3 edges
///
/// Very slow decay: 2% per month. Near-permanent.
pub const L3_DECAY_PER_MONTH: f32 = 0.02;

/// Minimum weight threshold for L3 edges
///
/// Highest bar - only strongest associations remain.
pub const L3_PRUNE_THRESHOLD: f32 = 0.3;

// === HEBBIAN STRENGTHENING ===
// Co-activation and retrieval success boost edge weights

/// Weight boost per co-access event
///
/// When two memories are retrieved together, their edge strengthens by 15%.
pub const TIER_CO_ACCESS_BOOST: f32 = 0.15;

/// Weight boost when edge contributes to successful retrieval
///
/// If traversing an edge helped answer a query correctly, +25% strength.
pub const TIER_RETRIEVAL_SUCCESS_BOOST: f32 = 0.25;

/// Threshold for Long-Term Potentiation (LTP) status
///
/// Edges above 0.8 weight are considered "potentiated" and decay even slower.
pub const TIER_LTP_THRESHOLD: f32 = 0.8;

// === ADAPTIVE INVOLUNTARY MEMORY (Berntsen 2009) ===
// Constraints that keep proactive_context adaptive rather than pathological.
// proactive_context is an involuntary memory system — cue-driven surfacing
// without explicit search. Without biological constraints, it degrades into
// pathological intrusions (Ehlers & Clark 2000). These constants implement
// the adaptive mechanisms from Berntsen's involuntary autobiographical memory
// model: habituation, lateral inhibition, elaboration gating, and steep
// recency gradients.

/// Habituation decay factor for repeated surfacing without utility.
///
/// When a memory surfaces via proactive_context and receives no positive
/// feedback (the agent never references it), its proactive retrieval weight
/// decays logarithmically: penalty = factor * ln(1 + surfacings_without_utility).
/// This mirrors neural habituation — repeated stimulation without reinforcement
/// diminishes the response (Thompson & Spencer 1966).
///
/// 0.08 gives: 1 miss → -0.055, 3 misses → -0.111, 10 misses → -0.192
///
/// Reference: Thompson & Spencer (1966) "Habituation: A model phenomenon
/// for the study of neuronal substrates of behavior"
pub const HABITUATION_DECAY_FACTOR: f32 = 0.08;

/// Maximum habituation penalty — prevents permanent suppression.
///
/// Biological habituation is never permanent; dishabituation occurs when
/// context changes. The cap ensures memories can recover when context shifts.
pub const HABITUATION_MAX_PENALTY: f32 = 0.4;

/// Cosine similarity threshold for lateral inhibition between candidates.
///
/// When two surfaced memories are more similar than this threshold, the
/// weaker one is suppressed — modeling lateral inhibition in neural
/// pattern separation. Only the most distinctive match for each "region"
/// of memory space survives.
///
/// 0.75 is high enough that genuinely different memories about the same
/// topic survive, while near-duplicates and paraphrases suppress each other.
///
/// Reference: O'Reilly & McClelland (1994) "Hippocampal conjunctive encoding,
/// storage, and recall: avoiding a trade-off"
pub const LATERAL_INHIBITION_THRESHOLD: f32 = 0.75;

/// Strength of lateral inhibition suppression.
///
/// When two candidates exceed the similarity threshold, the weaker one's
/// score is reduced by: strength * similarity * (winner_score / loser_score).
/// Higher values create more aggressive winner-take-all dynamics.
pub const LATERAL_INHIBITION_STRENGTH: f32 = 0.3;

/// Recency decay rate for proactive (involuntary) retrieval.
///
/// Involuntary memories show a steeper recency gradient than voluntary recall
/// (Berntsen 2009, Ch. 6). proactive_context uses 0.03/hour vs the default
/// 0.01/hour in semantic_retrieve, giving:
///   - 50% boost remaining at ~23 hours (vs ~69 hours for voluntary)
///   - 10% remaining at ~77 hours (vs ~230 hours for voluntary)
///
/// This ensures proactive surfacing strongly favors recent context while
/// voluntary recall can still reach older memories when explicitly requested.
///
/// Reference: Berntsen (2009) "Involuntary Autobiographical Memories:
/// An Introduction to the Unbidden Past", Ch. 6 (recency gradient)
pub const PROACTIVE_RECENCY_DECAY_RATE: f32 = 0.03;

/// Minimum elaboration quality factor for proactive surfacing.
///
/// Prevents zero-quality memories from being completely suppressed.
/// Floor of 0.3 means even the shortest valid memories retain 30% of
/// their score, while rich elaborated memories get full weight.
pub const ELABORATION_QUALITY_MIN: f32 = 0.3;

/// Tag relevance boost for proactive_context scoring.
///
/// When a memory's structured tags (tool:*, file:*, error) match patterns
/// detected in the current context, the memory receives a multiplicative boost
/// of (1 + TAG_RELEVANCE_BOOST × min(matches, 3)). This connects hook-written
/// metadata to retrieval ranking without overriding semantic similarity.
///
/// Range: [0.0, 1.0]. At 0.05, maximum boost is +15% for 3 matching patterns.
pub const TAG_RELEVANCE_BOOST: f32 = 0.05;

// =============================================================================
// TEMPORAL CREDIT ASSIGNMENT CONSTANTS
// Multi-turn feedback attribution with exponential discounting.
//
// When memories are surfaced at turn T, they receive discounted credit from
// signals at turns T+1 through T+W. This models delayed utility: a memory
// surfaced early in a session may guide actions several turns later.
//
// Reference: Sutton & Barto (2018) "Reinforcement Learning", Ch. 7 (n-step TD)
// =============================================================================

/// Temporal discount factor (gamma) for multi-turn credit assignment.
///
/// credit(memory, turn) = signal(turn) * gamma^(turn - surfaced_turn)
///
/// At gamma = 0.7:
///   T+1: 0.70, T+2: 0.49, T+3: 0.34, T+4: 0.24, T+5: 0.17
///
/// After 5 turns, 83% of total credit has been assigned.
///
/// Reference: Sutton (1988) "Learning to Predict by the Methods of Temporal Differences"
pub const TEMPORAL_DISCOUNT_GAMMA: f32 = 0.70;

/// Maximum turns in the feedback window.
///
/// Memories older than this stop accumulating credit.
/// 5 turns covers ~90% of useful attribution (gamma^5 = 0.17).
/// Memory overhead: ~5 entries × ~20 memories × ~500 bytes ≈ 50KB/user.
pub const FEEDBACK_WINDOW_SIZE: usize = 5;

/// Session gap threshold in seconds.
///
/// If time between proactive_context calls exceeds this, the window is
/// flushed and a new session starts. 30 minutes matches standard web
/// analytics session definitions (Google Analytics).
pub const FEEDBACK_SESSION_GAP_SECS: i64 = 1800;

/// Minimum turns of sustained engagement to detect task completion.
///
/// When the user has >= this many turns on the same topic (cosine > 0.5)
/// followed by a topic change (cosine < 0.3), all window memories get
/// a session-level completion boost.
pub const SESSION_COMPLETION_MIN_TURNS: u32 = 3;

/// Session-level completion boost for all window memories.
///
/// Applied once per detected task completion. Conservative at 0.15 to
/// avoid overwhelming per-turn signals (range -1.0 to +1.0).
pub const SESSION_COMPLETION_BOOST: f32 = 0.15;

/// Session-level abandonment penalty for recent memories.
///
/// Applied to memories in the last 2 window entries when abandonment
/// is detected. Mild at -0.10 because abandonment is ambiguous (user
/// may have been interrupted, not dissatisfied).
pub const SESSION_ABANDONMENT_PENALTY: f32 = -0.10;

/// Re-engagement boost for topic return.
///
/// When a user returns to a topic after a gap, memories from the original
/// topic receive this boost — they were worth returning to. Stronger
/// than completion boost because re-engagement is a clearer utility signal.
pub const SESSION_REENGAGEMENT_BOOST: f32 = 0.20;

/// Minimum cumulative discounted attribution to trigger a momentum update.
///
/// Deferred credits below this are silently discarded to prevent noise
/// from micro-signals polluting the momentum EMA.
pub const TEMPORAL_CREDIT_MIN_THRESHOLD: f32 = 0.02;

// =============================================================================
// FORMAN-RICCI CURVATURE CONSTANTS
// Discrete Ricci curvature on the knowledge graph, computed during heavy
// maintenance cycles. Measures information flow structure: bridges vs. clusters.
//
// Reference: Leal, Restrepo, Stadler, Jost (2018) arXiv:1811.07825
//            "Forman-Ricci curvature for hypergraphs"
// Neuroscience: Farooq et al. (2019) Nature Communications — Ricci curvature
//               detects structural differences in brain networks invisible to
//               traditional graph metrics.
// =============================================================================

/// Selectivity threshold below which an entity is classified as a "stop word."
///
/// Entities with selectivity below this connect to everything uniformly
/// (like `impl`, `check`, `encode`) — their LTP protection is reduced,
/// allowing curvature-accelerated decay to clean up their noise edges.
///
/// Entities with selectivity above this have community structure (like
/// "Hebbian learning", "RocksDB") and retain full LTP protection.
///
/// Derived from live graph measurement: noise hubs have selectivity ~0.0-0.5,
/// concept entities have selectivity ~1.0-10.0+.
pub const SELECTIVITY_STOP_WORD_THRESHOLD: f32 = 0.5;

/// Half-saturation constant for selectivity-gated LTP.
///
/// Controls how sharply LTP protection transitions from full to zero
/// as selectivity decreases. The effective LTP factor is:
///   ltp_factor * (selectivity / (selectivity + SELECTIVITY_HALF_SAT))
///
/// At selectivity = SELECTIVITY_HALF_SAT: 50% of normal LTP protection.
/// At selectivity = 2 * SELECTIVITY_HALF_SAT: 67% protection.
/// At selectivity = 0: 0% protection (stop word, no LTP).
///
/// Set to match the stop word threshold for smooth transition.
pub const SELECTIVITY_HALF_SAT: f32 = 0.5;

/// Minimum number of edges required before curvature computation runs.
///
/// Below this threshold the graph is too sparse for curvature to be meaningful.
/// A single connected component needs at least ~10 edges for the degree
/// distribution to produce non-trivial curvature variation.
pub const CURVATURE_MIN_EDGES: usize = 10;

/// Scale factor for curvature → path_boost conversion in retrieval.
/// Positive curvature (community) increases boost, negative (bridge) decreases.
/// 0.05 gives ±0.5 range for typical curvature values of [-10, +10].
pub const CURVATURE_PATH_BOOST_SCALE: f32 = 0.05;

// =============================================================================
// CAUSAL LINEAGE CONSTANTS (SHO-118)
// Lineage inference detects causal relationships between memories using
// temporal proximity, entity overlap, and memory type patterns.
//
// Reference: Shanahan (2005) "Perception as Abduction: Turning Sensor Data
// into Meaningful Representation" — causal abduction from temporal sequences
// =============================================================================

/// Maximum temporal gap (days) between memories for causal inference.
///
/// Associative learning literature caps validated causal windows at 21 hours
/// (Greville & Buehner 2023, "Temporal predictability facilitates causal learning").
/// However, software development operates on longer ecological timescales — a bug
/// found on Monday causes a fix on Thursday, a design decision this week shapes
/// implementation next week. We use 14 days as an ecological compromise:
///
/// - Captures within-sprint causality (typical 2-week sprint)
/// - The temporal_factor formula applies linear decay (14-day gap → factor 0.0),
///   so distant connections self-attenuate without hard cutoff artifacts
/// - 7-day gap → factor 0.5, 3-day gap → factor 0.79 (natural recency bias)
///
/// Previous value: 7 days (too aggressive, missed cross-week links).
/// Previous value: 30 days (no literature support, produced noise edges).
///
/// Reference: Greville & Buehner (2023) — max validated causal delay: 21 hours
pub const LINEAGE_MAX_TEMPORAL_GAP_DAYS: i64 = 14;

/// Minimum semantic signal (Jaccard entity overlap or cosine embedding similarity)
/// for causal inference.
///
/// Memories must share at least 30% semantic overlap to infer causation.
/// This threshold serves as a pattern completion cue fraction: Marr (1971)
/// showed that 20-30% of the original cue pattern suffices for successful
/// recall in autoassociative networks. 0.3 sits at the upper bound,
/// trading recall for precision — appropriate for causal inference where
/// false positives (spurious causal links) are costlier than false negatives.
///
/// Also used as the gate for embedding-only inference (when NER produces
/// no entities), ensuring cosine similarity meets the same bar.
///
/// Reference: Marr (1971) "Simple memory: a theory for archicortex" — 0.2-0.3 cue fraction
pub const LINEAGE_MIN_ENTITY_OVERLAP: f32 = 0.3;

/// Maximum candidate memories to evaluate for lineage inference.
///
/// Caps the per-memory inference cost. 20 candidates × O(1) inference
/// ≈ negligible latency. Higher values catch more distant causal links
/// but increase background task duration.
pub const LINEAGE_MAX_CANDIDATES: usize = 20;

/// Lookback window (days) for finding candidate memories during inference.
///
/// Controls how far back the graph entity index is searched for
/// co-occurring memories. Set to half of LINEAGE_MAX_TEMPORAL_GAP_DAYS
/// because candidate discovery is capped at LINEAGE_MAX_CANDIDATES (20).
/// 7 days captures the most causally-dense period (within-week links)
/// while the recency fallback in remember.rs catches edge cases.
pub const LINEAGE_LOOKBACK_DAYS: i64 = 7;

/// Base confidence for Caused relation (Error → Task).
pub const LINEAGE_CONFIDENCE_CAUSED: f32 = 0.8;

/// Base confidence for ResolvedBy relation (Task → Learning).
pub const LINEAGE_CONFIDENCE_RESOLVED_BY: f32 = 0.85;

/// Base confidence for InformedBy relation (Learning/Discovery → Decision).
pub const LINEAGE_CONFIDENCE_INFORMED_BY: f32 = 0.7;

/// Base confidence for SupersededBy relation (Decision → Decision).
pub const LINEAGE_CONFIDENCE_SUPERSEDED_BY: f32 = 0.6;

/// Base confidence for TriggeredBy relation (Discovery/Learning → Task).
pub const LINEAGE_CONFIDENCE_TRIGGERED_BY: f32 = 0.75;

/// Base confidence for BranchedFrom relation (pivot detection).
pub const LINEAGE_CONFIDENCE_BRANCHED_FROM: f32 = 0.9;

/// Base confidence for RelatedTo relation (same-group fallback).
pub const LINEAGE_CONFIDENCE_RELATED_TO: f32 = 0.5;

/// Scale factor for propagating lineage confidence into graph edge weights.
///
/// When a causal lineage edge is inferred between two memories with confidence C,
/// the corresponding graph edges between their entities are strengthened by
/// C * LINEAGE_GRAPH_BOOST_SCALE. This bidirectionally couples the lineage system
/// (explicit causal chains) with the knowledge graph (spreading activation), so
/// causally-linked memories naturally co-activate during retrieval.
///
/// Conservative value: lineage inferences are probabilistic, so we attenuate the
/// boost to prevent false causal links from dominating the graph topology.
///
/// Reference: Anderson (1983) "The Architecture of Cognition" — spreading activation
/// strength should reflect the reliability of the association source.
pub const LINEAGE_GRAPH_BOOST_SCALE: f32 = 0.15;

/// Boost applied when a user explicitly confirms a lineage edge.
///
/// Confirmation is a strong signal — the user validated the causal relationship.
/// This uses a higher boost than automatic inference to reward human-in-the-loop
/// validation and make confirmed causal paths more prominent in retrieval.
pub const LINEAGE_CONFIRM_GRAPH_BOOST: f32 = 0.3;

/// Scale factor for lineage-aware retrieval score boosting.
///
/// When recalled memories have causal chain connections, the connected memories
/// receive a score boost of `edge.confidence * LINEAGE_RETRIEVAL_BOOST_SCALE`.
/// This implements spreading activation from causally-linked memories.
///
/// Anderson (1983) ACT-R theory predicts 5-15% facilitation from associative
/// priming. At 0.06 per edge with typical confidence 0.7, the per-edge boost
/// is ~4.2%, requiring 2-3 converging edges to reach meaningful facilitation.
/// This prevents single weak causal links from distorting retrieval while
/// rewarding memories with multiple independent causal paths.
///
/// Max boost per memory capped at LINEAGE_RETRIEVAL_MAX_BOOST.
///
/// Reference: Anderson (1983) "The Architecture of Cognition" — spreading activation
pub const LINEAGE_RETRIEVAL_BOOST_SCALE: f32 = 0.06;

/// Maximum total lineage boost per memory during retrieval.
///
/// Prevents a memory with many lineage edges from dominating results.
/// With BOOST_SCALE=0.06 and this cap=0.15, a memory needs ~3 high-confidence
/// edges to reach the cap. The 15% ceiling aligns with the upper bound of
/// Anderson (1983)'s observed priming facilitation range (5-15%).
///
/// Reference: Anderson (1983) "The Architecture of Cognition" — 5-15% priming
pub const LINEAGE_RETRIEVAL_MAX_BOOST: f32 = 0.15;

/// Minimum edge confidence for lineage retrieval boosting.
///
/// Only edges above this threshold affect retrieval scores. This filters out
/// low-confidence inferred edges that haven't been reinforced by feedback.
/// At 0.5, roughly half of freshly-inferred edges qualify (those with good
/// entity overlap and temporal proximity), while weakened edges (after
/// misleading feedback) are excluded.
pub const LINEAGE_RETRIEVAL_MIN_CONFIDENCE: f32 = 0.5;

/// Maximum number of causally-connected memories to inject into recall results.
///
/// When recalled memories have high-confidence causal edges to memories NOT
/// in the result set, those connected memories are fetched and appended.
/// This prevents causal chains from being invisible when the connected memory
/// didn't score high enough on semantic similarity alone.
///
/// Capped at 3 to avoid overwhelming results with tangential causal links.
/// Each injected memory gets a score derived from the connecting edge's
/// confidence and the source memory's score, so they sort naturally.
pub const LINEAGE_EXPANSION_MAX: usize = 3;

/// Minimum edge confidence for candidate expansion (higher bar than boost).
///
/// Candidate expansion injects new memories into results, which is a stronger
/// signal than re-ranking. Requires higher confidence (0.7) than boost (0.5)
/// to prevent noise injection. Only confirmed or strongly-inferred edges qualify.
pub const LINEAGE_EXPANSION_MIN_CONFIDENCE: f32 = 0.7;

// =============================================================================
// CONSTANTS USAGE DOCUMENTATION
// =============================================================================
//
// This section documents where each constant is used in the codebase.
// Updated: 2025-12-09
//
// ## Hebbian Learning Constants
// | Constant                  | File                      | Function/Context                    |
// |---------------------------|---------------------------|-------------------------------------|
// | HEBBIAN_BOOST_HELPFUL     | memory/types.rs           | Memory::boost_importance()          |
// | HEBBIAN_DECAY_MISLEADING  | memory/types.rs           | Memory::decay_importance()          |
// | IMPORTANCE_FLOOR          | memory/types.rs           | Memory::decay_importance() - floor  |
//
// ## Memory Graph Edge Constants
// | Constant                  | File                      | Function/Context                    |
// |---------------------------|---------------------------|-------------------------------------|
// | EDGE_INITIAL_STRENGTH     | memory/retrieval.rs       | EdgeWeight::default()               |
// | EDGE_MIN_STRENGTH         | memory/retrieval.rs       | EdgeWeight::decay(), find_assoc()   |
// | EDGE_HALF_LIFE_HOURS      | memory/retrieval.rs       | EdgeWeight::decay()                 |
//
// ## Compression Constants
// | Constant                      | File                  | Function/Context                    |
// |-------------------------------|---------------------- |-------------------------------------|
// | COMPRESSION_IMPORTANCE_HIGH   | memory/compression.rs | should_compress() - LZ4 threshold   |
// | COMPRESSION_IMPORTANCE_LOW    | memory/compression.rs | should_compress() - semantic thresh |
// | COMPRESSION_AGE_DAYS          | memory/compression.rs | should_compress() - age check       |
// | COMPRESSION_ACCESS_THRESHOLD  | memory/compression.rs | should_compress() - access count    |
// | MAX_DECOMPRESSED_SIZE         | memory/compression.rs | decompress() - safety limit         |
//
// ## Vector Search Constants
// | Constant                          | File                | Function/Context                  |
// |-----------------------------------|---------------------|-----------------------------------|
// | VECTOR_SEARCH_CANDIDATE_MULTIPLIER| memory/retrieval.rs | search_ids(), similarity_search() |
// |                                   | main.rs             | hybrid recall query building      |
// | ESTIMATED_BYTES_PER_MEMORY        | (unused)            | Resource estimation               |
//
// ## Salience/Recency Scoring Constants (Ebbinghaus Forgetting Curve)
// | Constant                  | File                | Function/Context                      |
// |---------------------------|---------------------|---------------------------------------|
// | SALIENCE_RECENCY_WEIGHT   | memory/types.rs     | Memory::salience_score()              |
// | RECENCY_FULL_DAYS         | memory/types.rs     | Memory::salience_score() - 7 day tier |
// | RECENCY_HIGH_DAYS         | memory/types.rs     | Memory::salience_score() - 30 day     |
// | RECENCY_MEDIUM_DAYS       | memory/types.rs     | Memory::salience_score() - 90 day     |
// | RECENCY_HIGH_WEIGHT       | memory/types.rs     | Memory::salience_score() - 0.7        |
// | RECENCY_MEDIUM_WEIGHT     | memory/types.rs     | Memory::salience_score() - 0.4        |
// | RECENCY_LOW_WEIGHT        | memory/types.rs     | Memory::salience_score() - 0.1        |
//
// ## Hybrid Retrieval Weights
// | Constant                  | File                      | Function/Context                  |
// |---------------------------|---------------------------|-----------------------------------|
// | HYBRID_SEMANTIC_WEIGHT    | memory/graph_retrieval.rs | spreading_activation_retrieve()   |
// | HYBRID_GRAPH_WEIGHT       | memory/graph_retrieval.rs | spreading_activation_retrieve()   |
// | HYBRID_LINGUISTIC_WEIGHT  | memory/graph_retrieval.rs | spreading_activation_retrieve()   |
//
// ## Semantic Consolidation Constants
// | Constant                      | File                  | Function/Context                  |
// |-------------------------------|---------------------- |-----------------------------------|
// | CONSOLIDATION_MIN_SUPPORT     | memory/compression.rs | consolidate_semantic_facts()      |
// | CONSOLIDATION_MIN_AGE_DAYS    | memory/compression.rs | consolidate_semantic_facts()      |
// | CONSOLIDATION_JACCARD_THRESHOLD | memory/compression.rs | group_candidates_by_similarity()  |
// | CONSOLIDATION_MAX_CANDIDATES_PER_MEMORY | memory/compression.rs | extract_fact_candidates() |
// | FACT_DECAY_GRACE_DAYS              | memory/mod.rs, compression.rs | fact decay grace period     |
// | FACT_DECAY_HALF_LIFE_BASE_DAYS     | memory/mod.rs, compression.rs | fact decay half-life base  |
// | FACT_DECAY_HALF_LIFE_PER_SUPPORT_DAYS | memory/mod.rs, compression.rs | fact decay per support  |
// | FACT_DEDUP_COSINE_THRESHOLD   | memory/facts.rs       | find_similar() hybrid dedup       |
// | FACT_DEDUP_JACCARD_FLOOR      | memory/facts.rs       | find_similar() hybrid dedup       |
// | FACT_DEDUP_JACCARD_FALLBACK   | memory/facts.rs       | find_similar() fallback mode      |
// | FACT_NEGATION_MARKERS         | memory/facts.rs       | detect_polarity()                 |
//
// ## Default Configuration Constants
// | Constant                      | File                | Function/Context                    |
// |-------------------------------|---------------------|-------------------------------------|
// | DEFAULT_WORKING_MEMORY_SIZE   | memory/types.rs     | MemoryConfig::default()             |
// | DEFAULT_SESSION_MEMORY_SIZE_MB| memory/types.rs     | MemoryConfig::default()             |
// | DEFAULT_MAX_HEAP_PER_USER_MB  | memory/types.rs     | MemoryConfig::default()             |
// | DEFAULT_IMPORTANCE_THRESHOLD  | memory/types.rs     | MemoryConfig::default()             |
// | DEFAULT_COMPRESSION_AGE_DAYS  | memory/types.rs     | MemoryConfig::default()             |
// | DEFAULT_MAX_RESULTS           | memory/types.rs     | Query::default()                    |
//
// ## Spreading Activation Constants (Anderson & Pirolli 1984)
// | Constant                      | File                      | Function/Context                |
// |-------------------------------|---------------------------|---------------------------------|
// | SPREADING_DECAY_RATE          | memory/graph_retrieval.rs | spreading_activation_retrieve() |
// | SPREADING_MAX_HOPS            | memory/graph_retrieval.rs | spreading_activation_retrieve() |
// | SPREADING_ACTIVATION_THRESHOLD| memory/graph_retrieval.rs | spreading_activation_retrieve() |
//
// ## Long-Term Potentiation (LTP) Constants
// | Constant                  | File              | Function/Context                      |
// |---------------------------|-------------------|---------------------------------------|
// | LTP_LEARNING_RATE         | graph_memory.rs   | Synapse::activate()                   |
// | LTP_DECAY_HALF_LIFE_DAYS  | graph_memory.rs   | Synapse::decay()                      |
// | LTP_THRESHOLD             | graph_memory.rs   | Synapse::is_potentiated()             |
// | LTP_DECAY_FACTOR          | graph_memory.rs   | Synapse::decay() - potentiated rate   |
// | LTP_MIN_STRENGTH          | graph_memory.rs   | Synapse::decay() - minimum floor      |
//
// ## Information Content (IC) Weights (Lioma & Ounis 2006)
// | Constant      | File                    | Function/Context                        |
// |---------------|-------------------------|-----------------------------------------|
// | IC_NOUN       | memory/query_parser.rs  | analyze_query() - noun IC weight        |
// | IC_ADJECTIVE  | memory/query_parser.rs  | analyze_query() - adjective IC weight   |
// | IC_VERB       | memory/query_parser.rs  | analyze_query() - verb IC weight        |
//
// ## Server Timeout Constants
// | Constant                      | File      | Function/Context                        |
// |-------------------------------|-----------|-----------------------------------------|
// | GRACEFUL_SHUTDOWN_TIMEOUT_SECS| main.rs   | graceful_shutdown() - request drain     |
// | DATABASE_FLUSH_TIMEOUT_SECS   | main.rs   | graceful_shutdown() - RocksDB flush     |
// | VECTOR_INDEX_SAVE_TIMEOUT_SECS| main.rs   | graceful_shutdown() - HNSW persist      |
//
// ## Prefetch Recency Constants
// | Constant                      | File                | Function/Context                    |
// |-------------------------------|---------------------|-------------------------------------|
// | PREFETCH_RECENCY_FULL_HOURS   | memory/retrieval.rs | AnticipatoryPrefetch::relevance()   |
// | PREFETCH_RECENCY_PARTIAL_HOURS| memory/retrieval.rs | AnticipatoryPrefetch::relevance()   |
// | PREFETCH_RECENCY_FULL_BOOST   | memory/retrieval.rs | AnticipatoryPrefetch::relevance()   |
// | PREFETCH_RECENCY_PARTIAL_BOOST| memory/retrieval.rs | AnticipatoryPrefetch::relevance()   |
//
// ## Ontological Retrieval Constants (Collins & Quillian 1969)
// | Constant                      | File                      | Function/Context                    |
// |-------------------------------|---------------------------|-------------------------------------|
// | ONTOLOGICAL_MIN_CONFIDENCE    | memory/graph_retrieval.rs | spreading_activation_retrieve()     |
// | ONTOLOGICAL_RELATION_PENALTY  | memory/graph_retrieval.rs | spread_single_direction()           |
// | ONTOLOGICAL_ENTITY_PENALTY    | memory/graph_retrieval.rs | spread_single_direction()           |
// | ONTOLOGICAL_DENSITY_THRESHOLD | memory/graph_retrieval.rs | spreading_activation_retrieve()     |
// | ONTOLOGICAL_DENSITY_THRESHOLD | memory/mod.rs             | semantic_retrieve() density gating  |
// | ONTOLOGICAL_MIN_CONFIDENCE    | memory/mod.rs             | semantic_retrieve() density gating  |
// | ONTOLOGICAL_RERANK_BOOST      | memory/mod.rs             | semantic_retrieve() Layer 4.9       |
// | ONTOLOGICAL_RERANK_MAX        | memory/mod.rs             | semantic_retrieve() Layer 4.9       |
//
// ## RRF Fusion Constants (Cormack et al. 2009, Anderson & Lebiere 1998)
// | Constant                      | File                      | Function/Context                    |
// |-------------------------------|---------------------------|-------------------------------------|
// | RRF_K_HYBRID_FUSION           | memory/hybrid_search.rs   | search_with_dynamic_weights()       |
// | RRF_K_GRAPH_FUSION            | memory/mod.rs             | semantic_retrieve() Layer 4         |
// | ATTRIBUTE_QUERY_BOOST         | memory/mod.rs             | semantic_retrieve() Layer 4.5       |
// | TEMPORAL_FACT_BOOST           | memory/mod.rs             | semantic_retrieve() Layer 4.55      |
// | ACTIVATION_BONUS_SCALE        | memory/mod.rs             | semantic_retrieve() Layer 4 graph   |
// | PROSPECTIVE_BOOST_PER_MATCH   | memory/mod.rs             | semantic_retrieve() Layer 4.7       |
// | PROSPECTIVE_BOOST_MAX         | memory/mod.rs             | semantic_retrieve() Layer 4.7       |
// | RECENCY_BOOST_SCALE           | memory/mod.rs             | semantic_retrieve() Layer 5         |
// | RECENCY_DECAY_RATE            | memory/mod.rs             | semantic_retrieve() Layer 5         |
// | AROUSAL_BOOST_SCALE           | memory/mod.rs             | semantic_retrieve() Layer 5         |
// | CREDIBILITY_BOOST_SCALE       | memory/mod.rs             | semantic_retrieve() Layer 5         |
// | TEMPORAL_MATCH_BOOST_EXACT    | memory/mod.rs             | semantic_retrieve() Layer 5         |
// | TEMPORAL_MATCH_BOOST_WEEK     | memory/mod.rs             | semantic_retrieve() Layer 5         |
// | TEMPORAL_MATCH_BOOST_MONTH    | memory/mod.rs             | semantic_retrieve() Layer 5         |
// | TEMPORAL_PREFILTER_BOOST      | memory/mod.rs             | semantic_retrieve() Layer 4.45      |
// | TEMPORAL_PREFIX_MIN_CONFIDENCE| memory/mod.rs             | semantic_retrieve() embedding       |
// | HEBBIAN_ASSOCIATION_WEIGHT    | memory/mod.rs             | semantic_retrieve() Layer 5         |
// | SCORING_IMPORTANCE_FLOOR      | memory/mod.rs             | semantic_retrieve() Layer 5         |
// | SCORING_IMPORTANCE_RANGE      | memory/mod.rs             | semantic_retrieve() Layer 5         |
// | FEEDBACK_MOMENTUM_SCALE       | memory/mod.rs             | semantic_retrieve() Layer 5         |
//
// ## Emotional Arousal Constants (LaBar & Cabeza 2006)
// | Constant                      | File                      | Function/Context                    |
// |-------------------------------|---------------------------|-------------------------------------|
// | HIGH_AROUSAL_THRESHOLD        | memory/pattern_detection.rs| check_salience_spike()             |
// | PREFETCH_AROUSAL_THRESHOLD    | memory/retrieval.rs       | PrefetchContext::relevance_score() |
//
// ## Adaptive Involuntary Memory Constants (Berntsen 2009)
// | Constant                      | File                      | Function/Context                    |
// |-------------------------------|---------------------------|-------------------------------------|
// | HABITUATION_DECAY_FACTOR      | handlers/recall.rs        | proactive_context() habituation     |
// | HABITUATION_MAX_PENALTY       | handlers/recall.rs        | proactive_context() habituation cap |
// | LATERAL_INHIBITION_THRESHOLD  | handlers/recall.rs        | proactive_context() pattern sep.    |
// | LATERAL_INHIBITION_STRENGTH   | handlers/recall.rs        | proactive_context() inhibition      |
// | PROACTIVE_RECENCY_DECAY_RATE  | handlers/recall.rs        | proactive_context() recency curve   |
// | ELABORATION_QUALITY_MIN       | handlers/recall.rs        | proactive_context() quality gate    |
// | TAG_RELEVANCE_BOOST           | handlers/recall.rs        | proactive_context() tag boost       |
//
// ## Temporal Credit Assignment Constants (Sutton & Barto 2018)
// | Constant                      | File                      | Function/Context                    |
// |-------------------------------|---------------------------|-------------------------------------|
// | TEMPORAL_DISCOUNT_GAMMA       | handlers/recall.rs        | proactive_context() multi-turn TD   |
// | FEEDBACK_WINDOW_SIZE          | memory/feedback.rs        | FeedbackWindow sliding window       |
// | FEEDBACK_SESSION_GAP_SECS     | memory/feedback.rs        | Session boundary detection          |
// | SESSION_COMPLETION_MIN_TURNS  | memory/feedback.rs        | Task completion detection            |
// | SESSION_COMPLETION_BOOST      | memory/feedback.rs        | Session-level positive signal        |
// | SESSION_ABANDONMENT_PENALTY   | memory/feedback.rs        | Session-level negative signal        |
// | SESSION_REENGAGEMENT_BOOST    | memory/feedback.rs        | Topic return detection               |
// | TEMPORAL_CREDIT_MIN_THRESHOLD | memory/feedback.rs        | Deferred credit noise filter         |
//
// ## Entity Salience Reward Loop Constants (Piece 3)
// | Constant                            | File                | Function/Context                     |
// |-------------------------------------|---------------------|--------------------------------------|
// | ENTITY_SALIENCE_HELPFUL_BOOST       | handlers/recall.rs  | reinforce_feedback() entity salience |
// | ENTITY_SALIENCE_MISLEADING_PENALTY  | handlers/recall.rs  | reinforce_feedback() entity salience |
// | ENTITY_SALIENCE_HABITUATION_PENALTY | handlers/recall.rs  | proactive_context() habituation      |
// | ENTITY_SALIENCE_FILTER_FLOOR        | handlers/state.rs   | process_experience entity filtering  |
// | ENTITY_SALIENCE_FILTER_MIN_MENTIONS | handlers/state.rs   | process_experience entity filtering  |
//
// =============================================================================

// =============================================================================
// ENTITY SALIENCE REWARD LOOP
// =============================================================================

/// Salience boost per helpful recall feedback (+3%).
/// Asymmetric with penalty: rewards accumulate slowly, punishments hit harder.
/// This prevents runaway positive feedback while allowing noise to decay fast.
pub const ENTITY_SALIENCE_HELPFUL_BOOST: f32 = 0.03;

/// Salience penalty per misleading recall feedback (-5%).
/// Asymmetric: misleading is penalized ~1.7× harder than helpful is rewarded.
/// Rationale: a single misleading recall is more damaging than a single helpful
/// recall is valuable — false positives erode trust faster than true positives build it.
pub const ENTITY_SALIENCE_MISLEADING_PENALTY: f32 = -0.05;

/// Salience penalty per habituation event (-1%).
/// Applied when a memory has been surfaced 3+ times without utility.
/// Tiny because habituation is noisy — the user may ignore a memory for reasons
/// unrelated to entity quality (context mismatch, timing, etc.).
pub const ENTITY_SALIENCE_HABITUATION_PENALTY: f32 = -0.01;

/// Minimum salience before an entity gets filtered from extraction (feedback-driven floor).
/// Entities driven below this by the reward loop are excluded from new memories.
/// The floor is 0.15, well below the default 0.5 — entities need sustained negative
/// feedback to reach this threshold.
pub const ENTITY_SALIENCE_FILTER_FLOOR: f32 = 0.15;

/// Minimum mention count before salience filtering applies.
/// Prevents filtering entities that haven't accumulated enough feedback signal.
/// An entity must have been seen 5+ times before its salience is trusted as a quality signal.
pub const ENTITY_SALIENCE_FILTER_MIN_MENTIONS: usize = 5;

/// Minimum surfacings without utility before habituation penalty kicks in.
/// Below this threshold, we assume the memory might still be useful in the right context.
pub const ENTITY_SALIENCE_HABITUATION_THRESHOLD: u32 = 3;
