weavatrix-memory 0.3.1

Event-sourced, bitemporal context compiler for coding agents
Documentation
use super::super::{
    ExtractedEntity, ExtractionInput, LinkCandidate, LinkDecision, LinkMethod,
    key::{normalized, slug, stable_hash},
};
use crate::{Confidence, EntityId, Result};

pub(super) fn decision(
    mention: &ExtractedEntity,
    entity_id: Option<EntityId>,
    score: Confidence,
    method: LinkMethod,
    candidates: Vec<LinkCandidate>,
) -> LinkDecision {
    LinkDecision {
        mention_id: mention.local_id.clone(),
        entity_id,
        score,
        method,
        candidates,
    }
}

pub(super) fn generated_id(mention: &ExtractedEntity, input: &ExtractionInput) -> Result<EntityId> {
    let label = normalized(&mention.label);
    let hash = stable_hash(&[
        input.repository.as_deref().unwrap_or(""),
        input.branch.as_deref().unwrap_or(""),
        &normalized(&mention.kind),
        &label,
    ]);
    EntityId::new(format!("auto:{}:{hash:016x}", slug(&mention.kind)))
}

pub(super) fn scope_score(
    base: u16,
    node: &super::CatalogEntity,
    input: &ExtractionInput,
) -> (u16, bool) {
    let mut score = i32::from(base);
    let mut scoped = false;
    if let Some(repository) = &input.repository {
        match node.repository.as_ref() {
            Some(candidate) if candidate == repository => {
                score += 400;
                scoped = true;
            }
            Some(_) => score -= 2_000,
            None => score -= 300,
        }
    }
    if let Some(branch) = &input.branch {
        match node.branch.as_ref() {
            Some(candidate) if candidate == branch => {
                score += 200;
                scoped = true;
            }
            Some(_) => score -= 700,
            None => score -= 100,
        }
    }
    (
        u16::try_from(score.clamp(0, 10_000)).expect("clamped score fits u16"),
        scoped,
    )
}

pub(super) fn exact_key(value: &str) -> String {
    value.trim().to_lowercase()
}