use std::{
collections::{BTreeMap, BTreeSet},
fs::File,
io::{BufRead, BufReader, Read},
path::{Path, PathBuf},
time::Instant,
};
use crate::config::GRAPH_FEATURE_REVISION_DOMINANCE_ENABLED_KEY;
use crate::core::conformal::{
WhyConformalCandidate, WhyConformalConfidenceIntervals, why_conformal_confidence_intervals,
};
use crate::core::degraded_aggregation::{DegradationAggregationInput, aggregate_degraded_entries};
use crate::core::influence::{
WhyCounterfactualInfluence, WhyInfluenceCandidate, WhyInfluenceDirection,
why_counterfactual_influence,
};
use crate::core::memory::{
EvidenceFreshness, EvidenceFreshnessStatus, assess_memory_evidence_freshness, memory_validity,
resolve_memory_seal_lineage,
};
use crate::core::provenance_health::{
MemoryProvenanceHealth, ProvenancePointerStatus, assess_memory_provenance_health,
};
use crate::db::{
DatabaseConfig, DbConnection,
read_pool::{PoolConfig, registered_process_read_pool},
};
use crate::models::{
AGENT_CONTEXT_PROFILE_SCHEMA_V1, AGENT_PROFILE_BIAS_CAP, AGENT_PROFILE_COLD_START_OUTCOMES,
AgentContextProfileCounts, RationaleTrace, RationaleTraceVisibility,
VerificationEvidenceRecord,
};
use crate::pack::redact_pack_provenance_text;
use crate::runtime::determinism::{Deterministic, Seed};
use serde_json::Value as JsonValue;
use sqlmodel_core::{Row, Value};
#[derive(Clone, Debug, PartialEq)]
pub struct StorageExplanation {
pub origin: String,
pub trust_class: String,
pub trust_subclass: Option<String>,
pub provenance_uri: Option<String>,
pub workflow_id: Option<String>,
pub created_at: String,
pub valid_from: Option<String>,
pub valid_to: Option<String>,
pub validity_status: String,
pub validity_window_kind: String,
}
#[derive(Clone, Debug, PartialEq)]
pub struct RetrievalExplanation {
pub confidence: f32,
pub utility: f32,
pub importance: f32,
pub tags: Vec<String>,
pub level: String,
pub kind: String,
}
#[derive(Clone, Debug, PartialEq)]
pub struct GraphRetrievalExplanation {
pub status: String,
pub source: GraphRetrievalSourceExplanation,
pub centrality_score: f64,
pub authority_score: f64,
pub hub_score: f64,
pub hits: Option<GraphHitsExplanation>,
pub community_id: Option<String>,
pub distance_to_query_seed: Option<u32>,
pub same_cluster_as_top_result: Option<bool>,
pub evidence_support_count: u32,
pub contradiction_count: u32,
pub orphan_penalty: f64,
pub stale_bridge_penalty: f64,
pub pagerank: GraphMetricExplanation,
pub betweenness: GraphMetricExplanation,
pub labels: Vec<String>,
pub reasons: Vec<String>,
pub centrality_formula: String,
pub orphan_penalty_formula: String,
pub stale_bridge_penalty_formula: String,
pub degraded: Vec<WhyDegradation>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct GraphRetrievalSourceExplanation {
pub kind: String,
pub workspace_id: Option<String>,
pub graph_type: Option<String>,
pub snapshot: Option<GraphRetrievalSnapshotExplanation>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct GraphRetrievalSnapshotExplanation {
pub id: String,
pub schema_version: String,
pub snapshot_version: u32,
pub source_generation: u32,
pub status: String,
pub content_hash: String,
pub created_at: String,
}
#[derive(Clone, Debug, PartialEq)]
pub struct GraphMetricExplanation {
pub raw: f64,
pub normalized: f64,
pub rank: Option<usize>,
pub weight: f64,
pub contribution: f64,
pub formula: String,
}
#[derive(Clone, Debug, PartialEq)]
pub struct GraphHitsExplanation {
pub schema: &'static str,
pub authority: GraphHitsScoreExplanation,
pub hub: GraphHitsScoreExplanation,
pub role_label: &'static str,
pub role_rationale: String,
}
#[derive(Clone, Debug, PartialEq)]
pub struct GraphHitsScoreExplanation {
pub raw: f64,
pub normalized: f64,
pub rank: Option<usize>,
pub percentile: Option<f64>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct SelectionExplanation {
pub selection_score: f32,
pub above_confidence_threshold: bool,
pub is_active: bool,
pub score_breakdown: String,
pub latest_pack_selection: Option<PackSelectionExplanation>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct AgentProfileSelectionExplanation {
pub schema: &'static str,
pub agent_name: String,
pub agent_name_hash: String,
pub helpful_count: u32,
pub harmful_count: u32,
pub ignored_count: u32,
pub observed_outcomes: u32,
pub bias: f64,
pub max_bias_magnitude: f64,
pub cold_start: bool,
pub cold_start_threshold: u32,
pub last_seen_at: String,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LifecycleExplanation {
pub status: &'static str,
pub tombstoned_at: Option<String>,
pub tombstoned_reason: Option<String>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct PackSelectionExplanation {
pub pack_id: String,
pub query: String,
pub profile: String,
pub rank: u32,
pub section: String,
pub estimated_tokens: u32,
pub relevance: f32,
pub utility: f32,
pub attempt_family_multiplicity: Option<JsonValue>,
pub why: String,
pub pack_hash: String,
pub ledger_hash: Option<String>,
pub ledger_status: String,
pub ledger_storage: JsonValue,
pub selected_at: String,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ContradictionMetadata {
pub event_id: String,
pub weight: f32,
pub source_type: String,
pub reason: Option<String>,
pub created_at: String,
pub applied: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub struct MemoryLinkSummary {
pub link_id: String,
pub linked_memory_id: String,
pub relation: String,
pub direction: String,
pub confidence: f32,
pub weight: f32,
pub evidence_count: u32,
pub source: String,
pub created_at: String,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MemoryHistorySummaryEntry {
pub audit_id: String,
pub timestamp: String,
pub actor: Option<String>,
pub action: String,
pub details: Option<String>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MemoryHistorySummary {
pub entries: Vec<MemoryHistorySummaryEntry>,
pub total_count: u32,
pub truncated: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub struct RationaleTraceSummary {
pub schema: &'static str,
pub trace_id: String,
pub kind: String,
pub posture: String,
pub visibility: String,
pub author: String,
pub summary: String,
pub confidence_basis_points: u16,
pub evidence_uris: Vec<String>,
pub linked_memory_ids: Vec<String>,
pub linked_context_pack_ids: Vec<String>,
pub linked_recorder_run_ids: Vec<String>,
pub linked_recorder_event_ids: Vec<String>,
pub linked_causal_trace_ids: Vec<String>,
pub supersedes_trace_ids: Vec<String>,
pub contradicted_by_trace_ids: Vec<String>,
pub created_at: String,
}
impl RationaleTraceSummary {
#[must_use]
pub fn from_trace(trace: &RationaleTrace) -> Option<Self> {
if trace.schema != crate::models::RATIONALE_TRACE_SCHEMA_V1
|| !trace.visibility.is_storable()
{
return None;
}
Some(Self {
schema: crate::models::RATIONALE_TRACE_SCHEMA_V1,
trace_id: trace.trace_id.clone(),
kind: trace.kind.as_str().to_string(),
posture: trace.posture.as_str().to_string(),
visibility: trace.visibility.as_str().to_string(),
author: trace.author.clone(),
summary: trace.summary.clone(),
confidence_basis_points: trace.confidence_basis_points,
evidence_uris: trace.evidence_uris.clone(),
linked_memory_ids: trace.linked_memory_ids.clone(),
linked_context_pack_ids: trace.linked_context_pack_ids.clone(),
linked_recorder_run_ids: trace.linked_recorder_run_ids.clone(),
linked_recorder_event_ids: trace.linked_recorder_event_ids.clone(),
linked_causal_trace_ids: trace.linked_causal_trace_ids.clone(),
supersedes_trace_ids: trace.supersedes_trace_ids.clone(),
contradicted_by_trace_ids: trace.contradicted_by_trace_ids.clone(),
created_at: trace.created_at.clone(),
})
}
fn is_visible_for_report(&self) -> bool {
let Ok(visibility) = self.visibility.parse::<RationaleTraceVisibility>() else {
return false;
};
visibility.is_storable()
&& !self.trace_id.trim().is_empty()
&& !self.summary.trim().is_empty()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CoordinationFallbackEvidenceSummary {
pub source_schema: &'static str,
pub evidence_id: String,
pub status: String,
pub source_kind: String,
pub reason_code: String,
pub captured_at: String,
pub content_hash: String,
pub linked_bead_ids: Vec<String>,
pub linked_verification_ids: Vec<String>,
pub linked_support_bundle_ids: Vec<String>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct BayesPosteriorSummary {
pub alpha: f64,
pub beta: f64,
pub mean: f64,
pub effective_sample_size: f64,
pub credible_interval_90: Option<(f64, f64)>,
pub credible_interval_50: Option<(f64, f64)>,
}
impl BayesPosteriorSummary {
#[must_use]
pub fn from_posterior(posterior: &crate::core::bayes::BetaPosterior) -> Self {
Self {
alpha: posterior.alpha(),
beta: posterior.beta(),
mean: posterior.mean(),
effective_sample_size: posterior.effective_sample_size(),
credible_interval_90: posterior.credible_interval(0.90),
credible_interval_50: posterior.credible_interval(0.50),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct WhyDegradation {
pub code: &'static str,
pub severity: &'static str,
pub message: String,
pub repair: Option<String>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct LoadBearingWhyExplanation {
pub is_load_bearing: bool,
pub load_bearing_score: Option<f64>,
pub authority_rank: Option<usize>,
pub citing_rule_count: usize,
pub citing_rules: Vec<LoadBearingRuleReference>,
pub interpretation: &'static str,
pub evidence: LoadBearingEvidence,
pub rationale: String,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LoadBearingRuleReference {
pub rule_id: String,
pub relation: &'static str,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LoadBearingEvidence {
pub schema: &'static str,
pub algorithm: &'static str,
pub projection: &'static str,
pub snapshot_version: u64,
}
#[derive(Clone, Debug)]
pub struct WhyReport {
pub version: &'static str,
pub memory_id: String,
pub entity: Option<WhyEntityExplanation>,
pub found: bool,
pub content: Option<String>,
pub storage: Option<StorageExplanation>,
pub provenance_health: Option<MemoryProvenanceHealth>,
pub retrieval: Option<RetrievalExplanation>,
pub graph_retrieval: Option<GraphRetrievalExplanation>,
pub selection: Option<SelectionExplanation>,
pub agent_profile: Option<AgentProfileSelectionExplanation>,
pub lifecycle: Option<LifecycleExplanation>,
pub bayes_posterior: Option<BayesPosteriorSummary>,
pub confidence_intervals: Option<WhyConformalConfidenceIntervals>,
pub counterfactual_influence: Option<WhyCounterfactualInfluence>,
pub causal_explanation: Option<serde_json::Value>,
pub revision_lineage: Option<serde_json::Value>,
pub load_bearing: Option<LoadBearingWhyExplanation>,
pub contradictions: Vec<ContradictionMetadata>,
pub links: Vec<MemoryLinkSummary>,
pub history: Option<MemoryHistorySummary>,
pub rationale_traces: Vec<RationaleTraceSummary>,
pub verification_evidence: Vec<VerificationEvidenceRecord>,
pub coordination_fallback_evidence: Vec<CoordinationFallbackEvidenceSummary>,
pub attestation_manifest: Option<JsonValue>,
pub degraded: Vec<WhyDegradation>,
pub error: Option<String>,
pub dedup_link: Option<DedupLinkEvidence>,
pub seal: Option<JsonValue>,
pub team_provenance: Option<crate::core::memory_scope::TeamProvenance>,
pub elevation: Option<TeamElevationExplanation>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct WhyEntityExplanation {
pub kind: String,
pub id: String,
pub revision: Option<String>,
pub details: JsonValue,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TeamElevationExplanation {
pub schema: &'static str,
pub from_trust_class: &'static str,
pub to_trust_class: &'static str,
pub reason: String,
pub member_display_name: Option<String>,
pub origin_event_id: Option<String>,
pub produced_at: Option<String>,
pub origin_time_assurance: &'static str,
}
pub const TEAM_ELEVATION_SCHEMA_V1: &str = "ee.team.elevation.v1";
impl TeamElevationExplanation {
#[must_use]
pub fn to_json(&self) -> JsonValue {
serde_json::json!({
"schema": self.schema,
"fromTrustClass": self.from_trust_class,
"toTrustClass": self.to_trust_class,
"reason": self.reason,
"memberDisplayName": self.member_display_name,
"originEventId": self.origin_event_id,
"producedAt": self.produced_at,
"originTimeAssurance": self.origin_time_assurance,
})
}
}
pub const WHY_DEDUP_LINK_SCHEMA_REF: &str = "ee.embed_dedup.link.v1";
#[derive(Clone, Debug, PartialEq)]
pub struct DedupLinkEvidence {
pub schema: &'static str,
pub link_id: String,
pub target_memory_id: String,
pub decision: String,
pub reason: String,
pub relationship: String,
pub hamming_distance: Option<u32>,
pub cosine_similarity: Option<f32>,
pub cosine_floor: Option<f32>,
pub link_source: String,
pub link_weight: f32,
pub link_confidence: f32,
pub created_at: String,
}
impl WhyReport {
#[must_use]
pub fn found(
memory_id: String,
storage: StorageExplanation,
retrieval: RetrievalExplanation,
selection: SelectionExplanation,
) -> Self {
Self {
version: env!("CARGO_PKG_VERSION"),
memory_id,
entity: None,
found: true,
content: None,
storage: Some(storage),
provenance_health: None,
retrieval: Some(retrieval),
graph_retrieval: None,
selection: Some(selection),
agent_profile: None,
lifecycle: None,
bayes_posterior: None,
confidence_intervals: None,
counterfactual_influence: None,
causal_explanation: None,
revision_lineage: None,
load_bearing: None,
contradictions: Vec::new(),
links: Vec::new(),
history: None,
rationale_traces: Vec::new(),
verification_evidence: Vec::new(),
coordination_fallback_evidence: Vec::new(),
attestation_manifest: None,
degraded: Vec::new(),
error: None,
dedup_link: None,
seal: None,
team_provenance: None,
elevation: None,
}
}
#[must_use]
pub fn with_dedup_link(mut self, dedup_link: DedupLinkEvidence) -> Self {
self.dedup_link = Some(dedup_link);
self
}
#[must_use]
pub fn with_optional_dedup_link(mut self, dedup_link: Option<DedupLinkEvidence>) -> Self {
self.dedup_link = dedup_link;
self
}
#[must_use]
pub fn with_optional_seal(mut self, seal: Option<JsonValue>) -> Self {
self.seal = seal;
self
}
#[must_use]
pub fn with_optional_team_provenance(
mut self,
team_provenance: Option<crate::core::memory_scope::TeamProvenance>,
) -> Self {
self.team_provenance = team_provenance;
self
}
#[must_use]
pub fn with_optional_elevation(mut self, elevation: Option<TeamElevationExplanation>) -> Self {
self.elevation = elevation;
self
}
#[must_use]
pub fn with_content(mut self, content: String) -> Self {
self.content = Some(content);
self
}
#[must_use]
pub fn with_entity(mut self, entity: WhyEntityExplanation) -> Self {
self.entity = Some(entity);
self
}
#[must_use]
pub fn not_found(memory_id: String) -> Self {
Self {
version: env!("CARGO_PKG_VERSION"),
memory_id,
entity: None,
found: false,
content: None,
storage: None,
provenance_health: None,
retrieval: None,
graph_retrieval: None,
selection: None,
agent_profile: None,
lifecycle: None,
bayes_posterior: None,
confidence_intervals: None,
counterfactual_influence: None,
causal_explanation: None,
revision_lineage: None,
load_bearing: None,
contradictions: Vec::new(),
links: Vec::new(),
history: None,
rationale_traces: Vec::new(),
verification_evidence: Vec::new(),
coordination_fallback_evidence: Vec::new(),
attestation_manifest: None,
degraded: Vec::new(),
error: None,
dedup_link: None,
seal: None,
team_provenance: None,
elevation: None,
}
}
#[must_use]
pub fn error(memory_id: String, message: String) -> Self {
Self {
version: env!("CARGO_PKG_VERSION"),
memory_id,
entity: None,
found: false,
content: None,
storage: None,
provenance_health: None,
retrieval: None,
graph_retrieval: None,
selection: None,
agent_profile: None,
lifecycle: None,
bayes_posterior: None,
confidence_intervals: None,
counterfactual_influence: None,
causal_explanation: None,
revision_lineage: None,
load_bearing: None,
contradictions: Vec::new(),
links: Vec::new(),
history: None,
rationale_traces: Vec::new(),
verification_evidence: Vec::new(),
coordination_fallback_evidence: Vec::new(),
attestation_manifest: None,
degraded: Vec::new(),
error: Some(message),
dedup_link: None,
seal: None,
team_provenance: None,
elevation: None,
}
}
#[must_use]
fn unsupported_result_target(
document_id: String,
source: WhyResultDocumentSource,
conn: &DbConnection,
) -> Self {
let storage = unsupported_result_target_storage(&document_id, source, conn);
let retrieval = unsupported_result_target_retrieval(source);
let selection = SelectionExplanation {
selection_score: 0.0,
above_confidence_threshold: false,
is_active: false,
score_breakdown:
"non-memory search result targets are not eligible for memory pack selection"
.to_owned(),
latest_pack_selection: None,
};
Self::found(document_id.clone(), storage, retrieval, selection).with_degradation(
WhyDegradation {
code: "why_result_target_unsupported_source",
severity: "medium",
message: format!(
"`result:{document_id}` targets a {} document, not a memory. `ee why` currently explains memory result targets and returns this source-level explanation instead of a memory not_found error.",
source.human_label()
),
repair: Some(source.repair().to_owned()),
},
)
}
#[must_use]
pub fn with_degradation(mut self, degraded: WhyDegradation) -> Self {
self.degraded.push(degraded);
self
}
#[must_use]
pub fn with_degradations(mut self, degraded: Vec<WhyDegradation>) -> Self {
self.degraded.extend(degraded);
self
}
#[must_use]
pub fn with_lifecycle(mut self, lifecycle: LifecycleExplanation) -> Self {
self.lifecycle = Some(lifecycle);
self
}
#[must_use]
pub fn with_contradictions(mut self, contradictions: Vec<ContradictionMetadata>) -> Self {
self.contradictions = contradictions;
self
}
#[must_use]
pub fn with_links(mut self, links: Vec<MemoryLinkSummary>) -> Self {
self.links = links;
self
}
#[must_use]
pub fn with_history(mut self, history: MemoryHistorySummary) -> Self {
self.history = Some(history);
self
}
#[must_use]
pub fn with_optional_history(mut self, history: Option<MemoryHistorySummary>) -> Self {
self.history = history;
self
}
#[must_use]
pub fn with_bayes_posterior(mut self, summary: Option<BayesPosteriorSummary>) -> Self {
self.bayes_posterior = summary;
self
}
#[must_use]
pub fn with_confidence_intervals(
mut self,
confidence_intervals: WhyConformalConfidenceIntervals,
) -> Self {
self.confidence_intervals = Some(confidence_intervals);
self
}
#[must_use]
pub fn with_counterfactual_influence(
mut self,
counterfactual_influence: WhyCounterfactualInfluence,
) -> Self {
self.counterfactual_influence = Some(counterfactual_influence);
self
}
#[must_use]
pub fn with_causal_explanation(mut self, causal_explanation: serde_json::Value) -> Self {
self.causal_explanation = Some(causal_explanation);
self
}
#[must_use]
pub fn with_revision_lineage(mut self, revision_lineage: serde_json::Value) -> Self {
self.revision_lineage = Some(revision_lineage);
self
}
#[must_use]
pub fn with_optional_load_bearing(
mut self,
load_bearing: Option<LoadBearingWhyExplanation>,
) -> Self {
self.load_bearing = load_bearing;
self
}
#[must_use]
pub fn with_graph_retrieval(mut self, graph_retrieval: GraphRetrievalExplanation) -> Self {
self.graph_retrieval = Some(graph_retrieval);
self
}
#[must_use]
pub fn with_agent_profile(
mut self,
agent_profile: Option<AgentProfileSelectionExplanation>,
) -> Self {
self.agent_profile = agent_profile;
self
}
#[must_use]
pub fn with_rationale_traces(
mut self,
mut rationale_traces: Vec<RationaleTraceSummary>,
) -> Self {
rationale_traces.retain(RationaleTraceSummary::is_visible_for_report);
rationale_traces.sort_by(|left, right| left.trace_id.cmp(&right.trace_id));
rationale_traces.dedup_by(|left, right| left.trace_id == right.trace_id);
self.rationale_traces = rationale_traces;
self
}
#[must_use]
pub fn with_verification_evidence(
mut self,
mut verification_evidence: Vec<VerificationEvidenceRecord>,
) -> Self {
verification_evidence
.sort_by(|left, right| left.verification_id.cmp(&right.verification_id));
verification_evidence.dedup_by(|left, right| left.verification_id == right.verification_id);
self.verification_evidence = verification_evidence;
self
}
#[must_use]
pub fn with_coordination_fallback_evidence(
mut self,
mut evidence: Vec<CoordinationFallbackEvidenceSummary>,
) -> Self {
evidence.sort_by(|left, right| {
left.evidence_id
.cmp(&right.evidence_id)
.then_with(|| left.content_hash.cmp(&right.content_hash))
});
evidence.dedup_by(|left, right| {
left.evidence_id == right.evidence_id && left.content_hash == right.content_hash
});
self.coordination_fallback_evidence = evidence;
self
}
#[must_use]
pub fn with_attestation_manifest(mut self, attestation_manifest: Option<JsonValue>) -> Self {
self.attestation_manifest = attestation_manifest;
self
}
#[must_use]
pub fn with_provenance_health(mut self, provenance_health: MemoryProvenanceHealth) -> Self {
self.provenance_health = Some(provenance_health);
self
}
}
#[derive(Clone, Debug)]
pub struct WhyOptions<'a> {
pub database_path: &'a Path,
pub memory_id: &'a str,
pub confidence_threshold: f32,
}
impl<'a> WhyOptions<'a> {
pub const DEFAULT_CONFIDENCE_THRESHOLD: f32 = 0.5;
}
fn why_trace_elapsed_ms(started: Instant) -> u64 {
u64::try_from(started.elapsed().as_millis()).unwrap_or(u64::MAX)
}
fn trace_why_math_checkpoint(
workspace_id: &str,
memory_id: &str,
bead_id: &'static str,
surface: &'static str,
phase: &'static str,
started: Instant,
degraded_codes: &[&str],
) {
tracing::info!(
workspace_id = %workspace_id,
request_id = %memory_id,
bead_id,
surface,
phase,
elapsed_ms = why_trace_elapsed_ms(started),
degraded_codes = ?degraded_codes,
"why math surface checkpoint"
);
}
fn trace_why_math_surfaces(
workspace_id: &str,
memory_id: &str,
phase: &'static str,
started: Instant,
degraded_codes: &[&str],
) {
trace_why_math_checkpoint(
workspace_id,
memory_id,
"bd-3usjw.44",
"conformal_prediction_sets",
phase,
started,
degraded_codes,
);
trace_why_math_checkpoint(
workspace_id,
memory_id,
"bd-3usjw.48",
"influence_function_why",
phase,
started,
degraded_codes,
);
}
pub fn explain_memory(options: &WhyOptions<'_>) -> WhyReport {
let target = resolve_why_target(options.memory_id);
let memory_id = target.document_id;
if !options.database_path.exists() {
return WhyReport::error(
memory_id.to_string(),
format!("Database not found at {}", options.database_path.display()),
);
}
let migration_connection = match DbConnection::open_file(options.database_path) {
Ok(connection) => connection,
Err(error) => {
return WhyReport::error(
memory_id.to_string(),
format!("Failed to open database before why migration: {error}"),
);
}
};
if let Err(error) = migration_connection.migrate() {
return WhyReport::error(
memory_id.to_string(),
format!("Failed to migrate database before why query: {error}"),
);
}
if let Err(error) = migration_connection.close() {
return WhyReport::error(
memory_id.to_string(),
format!("Failed to close migration connection before why query: {error}"),
);
}
let read_pool = registered_process_read_pool(
DatabaseConfig::file(options.database_path.to_path_buf()),
PoolConfig::default_single(),
);
let read_snapshot = match read_pool.pin_snapshot() {
Ok(snapshot) => snapshot,
Err(error) => {
return WhyReport::error(
memory_id.to_string(),
format!("Failed to acquire why read snapshot: {error}"),
);
}
};
let report = {
let conn = match read_snapshot.checked_connection() {
Ok(connection) => connection,
Err(error) => {
return WhyReport::error(
memory_id.to_string(),
format!("Why read snapshot became unavailable: {error}"),
);
}
};
match conn.needs_migration() {
Ok(false) => explain_memory_with_connection(options, conn),
Ok(true) => WhyReport::error(
memory_id.to_string(),
"Database migration is required before read-only why; run `ee migrate run --workspace .`."
.to_owned(),
),
Err(error) => WhyReport::error(
memory_id.to_string(),
format!("Failed to inspect database migration state before why: {error}"),
),
}
};
if let Err(error) = read_snapshot.commit() {
return WhyReport::error(
memory_id.to_string(),
format!("Failed to release why read snapshot: {error}"),
);
}
report
}
pub fn explain_memory_seeded(
options: &WhyOptions<'_>,
_determinism: &mut Deterministic<Seed>,
) -> WhyReport {
explain_memory(options)
}
fn explain_evidence_with_connection(
options: &WhyOptions<'_>,
conn: &DbConnection,
evidence_id: &str,
) -> WhyReport {
let span = match conn.get_evidence_span(evidence_id) {
Ok(Some(span)) => span,
Ok(None) => return WhyReport::not_found(evidence_id.to_owned()),
Err(error) => {
return WhyReport::error(
evidence_id.to_owned(),
format!("Failed to query evidence span: {error}"),
);
}
};
let session = match conn.get_session(&span.session_id) {
Ok(Some(session)) => session,
Ok(None) => return WhyReport::not_found(evidence_id.to_owned()),
Err(error) => {
return WhyReport::error(
evidence_id.to_owned(),
format!("Failed to query evidence provenance: {error}"),
);
}
};
if !span.is_direct_pack_admitted_for_session(&span.workspace_id, &session) {
return WhyReport::not_found(evidence_id.to_owned());
}
let egress = crate::policy::redact_public_replay_text(&span.excerpt);
let redaction_classes =
serde_json::from_str::<Vec<String>>(&span.redaction_classes_json).unwrap_or_default();
let latest_pack_selection =
match latest_pack_selection(conn, &span.workspace_id, "evidence_span", evidence_id) {
Ok(selection) => selection,
Err(message) => {
return evidence_why_report(
options,
&span,
&session,
egress,
redaction_classes,
None,
)
.with_degradation(WhyDegradation {
code: "why_pack_selection_unavailable",
severity: "low",
message,
repair: Some("ee doctor --json".to_owned()),
});
}
};
evidence_why_report(
options,
&span,
&session,
egress,
redaction_classes,
latest_pack_selection,
)
}
fn evidence_why_report(
options: &WhyOptions<'_>,
span: &crate::db::StoredEvidenceSpan,
session: &crate::db::StoredSession,
egress: crate::policy::PublicReplayTextRedactionReport,
redaction_classes: Vec<String>,
latest_pack_selection: Option<PackSelectionExplanation>,
) -> WhyReport {
let search_admitted = span.is_search_admitted_for_session(&span.workspace_id, session);
let pack_admitted = span.is_direct_pack_admitted_for_session(&span.workspace_id, session);
let selection_score = latest_pack_selection
.as_ref()
.map_or(0.0, |selection| selection.relevance);
let egress_reasons = egress
.redacted_reasons
.iter()
.map(|reason| (*reason).to_owned())
.collect::<Vec<_>>();
let entity = WhyEntityExplanation {
kind: "evidence_span".to_owned(),
id: span.id.clone(),
revision: Some(span.pack_entity_revision()),
details: serde_json::json!({
"workspaceId": &span.workspace_id,
"session": {
"id": &session.id,
"startLine": span.start_line,
"endLine": span.end_line,
},
"producer": {
"kind": &span.producer_kind,
"spanKind": &span.span_kind,
"role": &span.role,
},
"screening": {
"version": span.screening_version,
"instructionRisk": &span.instruction_risk,
"securityPolicyEpoch": span.security_policy_epoch,
"canonicalProvenanceRevision": span.canonical_provenance_revision,
"excerptHashVerified": true,
},
"redaction": {
"status": &span.secret_redaction_status,
"classes": redaction_classes,
"egressRedacted": egress.redacted,
"egressReasons": egress_reasons,
},
"admission": {
"posture": if pack_admitted { "admitted" } else { "denied" },
"search": search_admitted,
"pack": pack_admitted,
},
}),
};
WhyReport::found(
span.id.clone(),
StorageExplanation {
origin: "Imported CASS evidence span".to_owned(),
trust_class: "cass_evidence".to_owned(),
trust_subclass: Some("imported_transcript_excerpt".to_owned()),
provenance_uri: Some(span.canonical_provenance_uri()),
workflow_id: None,
created_at: span.created_at.clone(),
valid_from: session.started_at.clone(),
valid_to: session.ended_at.clone(),
validity_status: "not_applicable".to_owned(),
validity_window_kind: "session_line_range".to_owned(),
},
RetrievalExplanation {
confidence: 0.0,
utility: 0.5,
importance: 0.0,
tags: vec![
"typed_entity:evidence_span".to_owned(),
format!("producer:{}", span.producer_kind),
"search_admitted".to_owned(),
"pack_admitted".to_owned(),
],
level: "evidence".to_owned(),
kind: "evidence_span".to_owned(),
},
SelectionExplanation {
selection_score,
above_confidence_threshold: latest_pack_selection
.as_ref()
.is_some_and(|selection| selection.relevance >= options.confidence_threshold),
is_active: pack_admitted,
score_breakdown: "typed evidence has no memory confidence posterior; selectionScore is the latest integrity-verified pack relevance when available"
.to_owned(),
latest_pack_selection,
},
)
.with_content(egress.content)
.with_entity(entity)
.with_lifecycle(LifecycleExplanation {
status: "admitted",
tombstoned_at: None,
tombstoned_reason: None,
})
}
pub fn explain_memory_with_connection(options: &WhyOptions<'_>, conn: &DbConnection) -> WhyReport {
let started = Instant::now();
let target = resolve_why_target(options.memory_id);
let memory_id = target.document_id;
if target.result_source == Some(WhyResultDocumentSource::Evidence) {
return explain_evidence_with_connection(options, conn, memory_id);
}
if let Some(source) = target.unsupported_result_source() {
return WhyReport::unsupported_result_target(memory_id.to_string(), source, conn);
}
let memory = match conn.get_memory(memory_id) {
Ok(Some(m)) => m,
Ok(None) => return WhyReport::not_found(memory_id.to_string()),
Err(e) => {
return WhyReport::error(
memory_id.to_string(),
format!("Failed to query memory: {e}"),
);
}
};
trace_why_math_surfaces(&memory.workspace_id, memory_id, "input", started, &[]);
let tags = match conn.get_memory_tags(memory_id) {
Ok(t) => t,
Err(e) => {
return WhyReport::error(memory_id.to_string(), format!("Failed to query tags: {e}"));
}
};
let contradiction_fetch = fetch_contradictions(&conn, memory_id);
let link_fetch = fetch_links(&conn, memory_id);
let history_fetch = fetch_history(&conn, memory_id);
let rationale_trace_fetch = fetch_rationale_traces(&conn, &memory.workspace_id, memory_id);
let verification_fetch = fetch_verification_evidence(&conn, "memory", memory_id);
let coordination_fallback_fetch = fetch_coordination_fallback_evidence(
options.database_path,
&memory,
&tags,
&verification_fetch.items,
);
let mut evidence_degradations = Vec::new();
if let Some(degradation) = contradiction_fetch.degradation {
evidence_degradations.push(degradation);
}
if let Some(degradation) = link_fetch.degradation {
evidence_degradations.push(degradation);
}
if let Some(degradation) = history_fetch.degradation {
evidence_degradations.push(degradation);
}
if let Some(degradation) = rationale_trace_fetch.degradation {
evidence_degradations.push(degradation);
}
if let Some(degradation) = verification_fetch.degradation {
evidence_degradations.push(degradation);
}
if let Some(degradation) = coordination_fallback_fetch.degradation {
evidence_degradations.push(degradation);
}
let attestation_manifest = match crate::core::attest::build_memory_attestation(&conn, memory_id)
{
Ok(Some(bundle)) => Some(crate::core::attest::attestation_surface_manifest(&bundle)),
Ok(None) => None,
Err(error) => {
evidence_degradations.push(WhyDegradation {
code: "why_attestation_unavailable",
severity: "low",
message: format!("Memory attestation bundle could not be built: {error}"),
repair: Some("ee attest memory <memory-id> --workspace . --json".to_owned()),
});
None
}
};
if verification_fetch.items.is_empty()
&& tags.iter().any(|tag| {
tag == "verification" || tag == "verification-required" || tag == "bead-closure"
})
{
evidence_degradations.push(WhyDegradation {
code: "verification_evidence_not_found",
severity: "low",
message: "No verification evidence ledger row is linked to this memory; verification-sensitive claims are reported as unverified rather than silently absent."
.to_owned(),
repair: Some("ee verification ingest --help".to_owned()),
});
}
let workspace_path = workspace_path_for_memory(&conn, &memory.workspace_id);
let freshness = assess_memory_evidence_freshness(&memory, workspace_path.as_deref());
if let Some(degradation) = why_evidence_freshness_degradation(memory_id, &freshness) {
evidence_degradations.push(degradation);
}
let provenance_health = assess_memory_provenance_health(&memory, workspace_path.as_deref());
if let Some(degradation) = why_provenance_health_degradation(&provenance_health) {
evidence_degradations.push(degradation);
}
let contradictions = contradiction_fetch.items;
let links = link_fetch.items;
let history = history_fetch.items.into_iter().next();
let lifecycle = lifecycle_for_memory(&memory, history.as_ref());
let rationale_traces = rationale_trace_fetch.items;
let verification_evidence = verification_fetch.items;
let coordination_fallback_evidence = coordination_fallback_fetch.items;
let validity = memory_validity(&memory.valid_from, &memory.valid_to);
let graph_retrieval = build_graph_retrieval_explanation(
&conn,
&memory.workspace_id,
memory_id,
&links,
&contradictions,
&validity.status,
);
let load_bearing = build_load_bearing_why_explanation(&conn, &memory.workspace_id, memory_id);
let storage = StorageExplanation {
origin: determine_origin(&memory.trust_class),
trust_class: memory.trust_class.clone(),
trust_subclass: memory.trust_subclass.clone(),
provenance_uri: memory
.provenance_uri
.clone()
.map(redact_why_search_result_provenance_uri),
workflow_id: memory.workflow_id.clone(),
created_at: memory.created_at.clone(),
valid_from: validity.valid_from,
valid_to: validity.valid_to,
validity_status: validity.status,
validity_window_kind: validity.window_kind,
};
let retrieval = RetrievalExplanation {
confidence: memory.confidence,
utility: memory.utility,
importance: memory.importance,
tags,
level: memory.level.clone(),
kind: memory.kind.clone(),
};
let is_active = memory.tombstoned_at.is_none();
let selection_score =
compute_selection_score(memory.confidence, memory.utility, memory.importance);
let above_threshold = memory.confidence >= options.confidence_threshold;
let agent_profile =
fetch_agent_profile_selection_explanation(&conn, &memory.workspace_id, memory_id);
let conformal_candidates = why_conformal_candidates(memory_id, selection_score, &links);
let counterfactual_influence =
why_counterfactual_influence(memory_id, selection_score, why_influence_candidates(&links));
let latest_pack_selection =
match latest_pack_selection(&conn, &memory.workspace_id, "memory", memory_id) {
Ok(selection) => selection,
Err(message) => {
let report = build_report(
memory_id,
storage,
retrieval,
ReportSelectionInputs {
is_active,
selection_score,
above_threshold,
latest_pack_selection: None,
lifecycle,
contradictions,
links,
history,
rationale_traces,
verification_evidence: Vec::new(),
coordination_fallback_evidence,
attestation_manifest,
graph_retrieval,
load_bearing,
degraded: evidence_degradations,
agent_profile,
dedup_link: find_embed_dedup_link(&conn, memory_id),
seal: fetch_seal(&conn, &memory),
},
)
.with_content(memory.content.clone())
.with_provenance_health(provenance_health.clone())
.with_optional_team_provenance(
crate::core::memory_scope::team_provenance_from_memory(&memory),
)
.with_optional_elevation(team_elevation_from_memory(&memory))
.with_counterfactual_influence(counterfactual_influence);
trace_why_math_surfaces(
&memory.workspace_id,
memory_id,
"response",
started,
&["why_pack_selection_unavailable"],
);
return report.with_degradation(WhyDegradation {
code: "why_pack_selection_unavailable",
severity: "low",
message,
repair: Some("ee doctor --json".to_string()),
});
}
};
let report = build_report(
memory_id,
storage,
retrieval,
ReportSelectionInputs {
is_active,
selection_score,
above_threshold,
latest_pack_selection,
lifecycle,
contradictions,
links,
history,
rationale_traces,
verification_evidence,
coordination_fallback_evidence,
attestation_manifest,
graph_retrieval,
load_bearing,
degraded: evidence_degradations,
agent_profile,
dedup_link: find_embed_dedup_link(&conn, memory_id),
seal: fetch_seal(&conn, &memory),
},
)
.with_content(memory.content.clone())
.with_provenance_health(provenance_health)
.with_optional_team_provenance(crate::core::memory_scope::team_provenance_from_memory(
&memory,
))
.with_optional_elevation(team_elevation_from_memory(&memory));
let report = report.with_confidence_intervals(why_conformal_confidence_intervals(
workspace_path.as_deref(),
memory_id,
selection_score,
conformal_candidates,
));
let report = report.with_counterfactual_influence(counterfactual_influence);
let report = match conn.get_memory_bayes_posterior(memory_id) {
Ok(Some((alpha, beta))) => {
let summary = crate::core::bayes::BetaPosterior::new(alpha, beta)
.map(|posterior| BayesPosteriorSummary::from_posterior(&posterior));
report.with_bayes_posterior(summary)
}
Ok(None) | Err(_) => report,
};
let report = if revision_dominance_feature_enabled(options.database_path) {
match revision_lineage_for_why(&conn, &memory.workspace_id, memory_id) {
Some(revision_lineage) => report.with_revision_lineage(revision_lineage),
None => report,
}
} else {
report.with_revision_lineage(revision_lineage_feature_disabled(memory_id))
};
trace_why_math_surfaces(&memory.workspace_id, memory_id, "response", started, &[]);
report
}
fn revision_dominance_feature_enabled(database_path: &Path) -> bool {
let Some(workspace_root) = workspace_root_from_database_path(database_path) else {
return false;
};
let options = crate::core::config_surface::ConfigSurfaceOptions {
workspace_root,
config_path: None,
};
crate::core::config_surface::get_config(&options, GRAPH_FEATURE_REVISION_DOMINANCE_ENABLED_KEY)
.map(|report| report.value == "true")
.unwrap_or(false)
}
fn workspace_root_from_database_path(database_path: &Path) -> Option<PathBuf> {
if database_path.file_name()?.to_str()? != "ee.db" {
return None;
}
let ee_dir = database_path.parent()?;
if ee_dir.file_name()?.to_str()? != ".ee" {
return None;
}
ee_dir.parent().map(Path::to_path_buf)
}
fn revision_lineage_feature_disabled(memory_id: &str) -> serde_json::Value {
let degraded = aggregate_why_revision_lineage_degraded([DegradationAggregationInput::new(
"why_revision_lineage",
"graph_feature_disabled",
"medium",
format!(
"Revision dominance is disabled by {GRAPH_FEATURE_REVISION_DOMINANCE_ENABLED_KEY}."
),
format!("ee config set {GRAPH_FEATURE_REVISION_DOMINANCE_ENABLED_KEY} true"),
)]);
serde_json::json!({
"sourceSchema": crate::graph::dominance::MEMORY_IMPACT_ANALYSIS_SCHEMA_V1,
"memoryId": memory_id,
"snapshotVersion": 0,
"rootMemoryId": serde_json::Value::Null,
"immediateDominator": serde_json::Value::Null,
"dominanceFrontier": [],
"ancestorsAtDepth": {},
"validationStatus": "disabled",
"degraded": degraded,
})
}
fn aggregate_why_revision_lineage_degraded<I>(entries: I) -> Vec<serde_json::Value>
where
I: IntoIterator<Item = DegradationAggregationInput>,
{
aggregate_degraded_entries(entries)
.into_iter()
.map(|entry| {
serde_json::json!({
"code": entry.code,
"severity": entry.severity,
"message": entry.message,
"repair": entry.repair,
"sources": entry.sources,
})
})
.collect()
}
fn aggregate_why_dominance_degraded(
degraded: &[crate::graph::dominance::DominanceDegradation],
) -> Vec<serde_json::Value> {
aggregate_why_revision_lineage_degraded(degraded.iter().map(|entry| {
DegradationAggregationInput::new(
"graph_dominance",
entry.code.clone(),
entry.severity.clone(),
entry.message.clone(),
entry
.repair
.clone()
.unwrap_or_else(|| "Refresh graph dominance diagnostics.".to_owned()),
)
}))
}
fn revision_lineage_for_why(
conn: &DbConnection,
workspace_id: &str,
memory_id: &str,
) -> Option<serde_json::Value> {
let graph = crate::graph::build_revision_dag_from_logical_ids(conn, workspace_id).ok()?;
let snapshot_version = conn
.get_latest_graph_snapshot(workspace_id, crate::db::GraphSnapshotType::RevisionDag)
.ok()
.flatten()
.map_or(0, |snapshot| u64::from(snapshot.snapshot_version));
let impact = crate::graph::dominance::compute_memory_impact_analysis(
&graph,
memory_id,
snapshot_version,
)
.ok()?;
let ancestors_at_depth = revision_ancestors_at_depth(&graph, memory_id);
let has_revision_context = ancestors_at_depth
.values()
.map(BTreeSet::len)
.sum::<usize>()
> 1
|| !graph.successors(memory_id).unwrap_or_default().is_empty();
let root_memory_id = ancestors_at_depth
.iter()
.next_back()
.and_then(|(_, ids)| ids.iter().next().cloned())
.unwrap_or_else(|| memory_id.to_owned());
let (immediate_dominator, dominance_frontier) = if has_revision_context {
let idoms = crate::graph::dominance::compute_immediate_dominators(&graph, &root_memory_id)
.ok()
.unwrap_or_default();
let frontiers =
crate::graph::dominance::compute_dominance_frontiers(&graph, &root_memory_id)
.ok()
.unwrap_or_default();
(
idoms
.get(memory_id)
.filter(|dominator| dominator.as_str() != memory_id)
.cloned(),
frontiers.get(memory_id).cloned().unwrap_or_default(),
)
} else {
(
impact.impact_analysis.immediate_dominator.clone(),
impact.impact_analysis.dominance_frontier.clone(),
)
};
let mut ancestors = serde_json::Map::new();
for (depth, ids) in ancestors_at_depth {
ancestors.insert(
depth.to_string(),
serde_json::Value::Array(ids.into_iter().map(serde_json::Value::String).collect()),
);
}
let degraded = aggregate_why_dominance_degraded(&impact.degraded);
Some(serde_json::json!({
"sourceSchema": impact.schema,
"memoryId": memory_id,
"snapshotVersion": snapshot_version,
"rootMemoryId": root_memory_id,
"immediateDominator": immediate_dominator,
"dominanceFrontier": dominance_frontier,
"ancestorsAtDepth": ancestors,
"validationStatus": impact.impact_analysis.validation_status,
"degraded": degraded,
}))
}
fn build_load_bearing_why_explanation(
conn: &DbConnection,
workspace_id: &str,
memory_id: &str,
) -> Option<LoadBearingWhyExplanation> {
let graph =
crate::graph::build_rule_provenance_bipartite_from_tables(conn, workspace_id).ok()?;
if graph.node_count() == 0 {
return None;
}
let hits = crate::graph::bipartite_provenance::compute_bipartite_hits(&graph).ok()?;
let snapshot_version = conn
.get_latest_graph_snapshot(workspace_id, crate::db::GraphSnapshotType::RuleProvenance)
.ok()
.flatten()
.map_or(0, |snapshot| u64::from(snapshot.snapshot_version));
let items = crate::graph::bipartite_provenance::load_bearing_memory_items(
&graph,
&hits,
snapshot_version,
);
let citing_rules = load_bearing_citing_rules(conn, workspace_id, memory_id).ok()?;
let ranked = items.iter().find(|item| item.memory_id == memory_id);
let evidence = LoadBearingEvidence {
schema: crate::graph::hits::HITS_REPORT_SCHEMA_V1,
algorithm: "bipartite_hits",
projection: "rule_provenance_bipartite",
snapshot_version,
};
Some(match ranked {
Some(item) => LoadBearingWhyExplanation {
is_load_bearing: true,
load_bearing_score: Some(item.load_bearing_score),
authority_rank: Some(item.rank),
citing_rule_count: item.citing_rule_count,
citing_rules,
interpretation: "load_bearing",
evidence,
rationale:
"This memory is cited by procedural rules in the rule-provenance bipartite projection."
.to_owned(),
},
None => {
let citing_rule_count = citing_rules.len();
LoadBearingWhyExplanation {
is_load_bearing: false,
load_bearing_score: None,
authority_rank: None,
citing_rule_count,
citing_rules,
interpretation: "not_load_bearing",
evidence,
rationale:
"This memory is not ranked as load-bearing in the current rule-provenance bipartite projection."
.to_owned(),
}
}
})
}
fn load_bearing_citing_rules(
conn: &DbConnection,
workspace_id: &str,
memory_id: &str,
) -> Result<Vec<LoadBearingRuleReference>, String> {
let rows = conn
.query(
"SELECT rsm.rule_id \
FROM rule_source_memories rsm \
JOIN procedural_rules rules ON rules.id = rsm.rule_id \
WHERE rsm.memory_id = ?1 \
AND rules.workspace_id = ?2 \
AND rules.tombstoned_at IS NULL \
ORDER BY rsm.rule_id ASC",
&[
Value::Text(memory_id.to_owned()),
Value::Text(workspace_id.to_owned()),
],
)
.map_err(|error| format!("Failed to query load-bearing citing rules: {error}"))?;
rows.iter()
.map(|row| {
Ok(LoadBearingRuleReference {
rule_id: required_text(row, 0, "rule_source_memories.rule_id")?,
relation: "cites",
})
})
.collect()
}
fn revision_ancestors_at_depth(
graph: &crate::graph::DiGraph,
memory_id: &str,
) -> BTreeMap<usize, BTreeSet<String>> {
let mut by_depth: BTreeMap<usize, BTreeSet<String>> = BTreeMap::new();
let mut seen = BTreeSet::new();
let mut frontier = BTreeSet::from([memory_id.to_owned()]);
let mut depth = 0usize;
while !frontier.is_empty() {
let mut next = BTreeSet::new();
for id in frontier {
if !seen.insert(id.clone()) {
continue;
}
by_depth.entry(depth).or_default().insert(id.clone());
for predecessor in graph.predecessors(&id).unwrap_or_default() {
if !seen.contains(predecessor) {
next.insert(predecessor.to_owned());
}
}
}
frontier = next;
depth = depth.saturating_add(1);
}
by_depth
}
fn workspace_path_for_memory(conn: &DbConnection, workspace_id: &str) -> Option<PathBuf> {
conn.get_workspace(workspace_id)
.ok()
.flatten()
.map(|workspace| PathBuf::from(workspace.path))
}
fn why_evidence_freshness_degradation(
memory_id: &str,
freshness: &EvidenceFreshness,
) -> Option<WhyDegradation> {
let code = match freshness.status {
EvidenceFreshnessStatus::MissingSource => "why_evidence_freshness_missing_source",
EvidenceFreshnessStatus::ChangedSource => "why_evidence_freshness_changed_source",
EvidenceFreshnessStatus::UnreachableSource => "why_evidence_freshness_unreachable_source",
EvidenceFreshnessStatus::UnsupportedSource => "why_evidence_freshness_unsupported_source",
EvidenceFreshnessStatus::Fresh | EvidenceFreshnessStatus::Unknown => return None,
};
let detail = redact_pack_provenance_text(&freshness.detail);
let repair = freshness.repair.as_deref().map(redact_pack_provenance_text);
Some(WhyDegradation {
code,
severity: "low",
message: format!(
"Memory {memory_id} evidence freshness is {}: {}",
freshness.status.as_str(),
detail
),
repair,
})
}
fn why_provenance_health_degradation(
provenance_health: &MemoryProvenanceHealth,
) -> Option<WhyDegradation> {
let code = match provenance_health.health {
ProvenancePointerStatus::Present => return None,
ProvenancePointerStatus::Moved => "why_provenance_freshness_moved",
ProvenancePointerStatus::Missing => "why_provenance_freshness_missing",
ProvenancePointerStatus::Unverifiable => "why_provenance_freshness_unverifiable",
};
let detail = provenance_health
.pointers
.iter()
.filter(|pointer| pointer.status.is_issue())
.map(|pointer| redact_pack_provenance_text(&pointer.detail))
.collect::<Vec<_>>()
.join("; ");
Some(WhyDegradation {
code,
severity: "low",
message: format!(
"Memory {} provenance health is {}: {}",
provenance_health.memory_id,
provenance_health.health.as_str(),
detail
),
repair: Some(
"Run `ee diag provenance --json` and revise or re-remember affected memories."
.to_string(),
),
})
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum WhyResultDocumentSource {
Memory,
Evidence,
Session,
Artifact,
CurationCandidate,
Unknown,
}
impl WhyResultDocumentSource {
fn from_document_id(document_id: &str) -> Self {
if document_id.starts_with("mem_") {
Self::Memory
} else if document_id.starts_with("ev_") {
Self::Evidence
} else if document_id.starts_with("sess_") {
Self::Session
} else if document_id.starts_with("art_") {
Self::Artifact
} else if document_id.starts_with("curate_") {
Self::CurationCandidate
} else {
Self::Unknown
}
}
const fn as_str(self) -> &'static str {
match self {
Self::Memory => "memory",
Self::Evidence => "evidence_span",
Self::Session => "session",
Self::Artifact => "artifact",
Self::CurationCandidate => "curation_candidate",
Self::Unknown => "unknown",
}
}
const fn human_label(self) -> &'static str {
match self {
Self::Memory => "memory",
Self::Evidence => "CASS evidence span",
Self::Session => "CASS session",
Self::Artifact => "artifact",
Self::CurationCandidate => "curation candidate",
Self::Unknown => "non-memory search",
}
}
const fn repair(self) -> &'static str {
match self {
Self::Memory => "ee why --help",
Self::Evidence => "ee why --help",
Self::Session => "ee import sessions --json",
Self::Artifact => "ee artifact show --help",
Self::CurationCandidate => "ee curate show --help",
Self::Unknown => "ee search --help",
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct WhyTarget<'a> {
document_id: &'a str,
result_source: Option<WhyResultDocumentSource>,
}
impl WhyTarget<'_> {
const fn unsupported_result_source(self) -> Option<WhyResultDocumentSource> {
match self.result_source {
Some(WhyResultDocumentSource::Memory | WhyResultDocumentSource::Evidence) | None => {
None
}
Some(source) => Some(source),
}
}
}
fn resolve_why_target(target_id: &str) -> WhyTarget<'_> {
let resolved = target_id
.strip_prefix("result:")
.filter(|doc_id| !doc_id.trim().is_empty())
.map_or(
WhyTarget {
document_id: target_id,
result_source: None,
},
|document_id| WhyTarget {
document_id,
result_source: Some(WhyResultDocumentSource::from_document_id(document_id)),
},
);
if resolved.result_source.is_none()
&& WhyResultDocumentSource::from_document_id(resolved.document_id)
== WhyResultDocumentSource::Evidence
{
WhyTarget {
document_id: resolved.document_id,
result_source: Some(WhyResultDocumentSource::Evidence),
}
} else {
resolved
}
}
#[cfg(test)]
fn resolve_why_memory_id(target_id: &str) -> &str {
resolve_why_target(target_id).document_id
}
fn unsupported_result_target_storage(
document_id: &str,
source: WhyResultDocumentSource,
conn: &DbConnection,
) -> StorageExplanation {
match source {
WhyResultDocumentSource::Session => {
conn.get_session(document_id).ok().flatten().map_or_else(
|| generic_unsupported_storage(document_id, source),
|session| StorageExplanation {
origin: "Imported CASS session search document".to_owned(),
trust_class: "cass_evidence".to_owned(),
trust_subclass: Some("search_result_session".to_owned()),
provenance_uri: Some(format!("cass-session://{}", session.id)),
workflow_id: None,
created_at: session.imported_at,
valid_from: session.started_at,
valid_to: session.ended_at,
validity_status: "not_applicable".to_owned(),
validity_window_kind: "search_document".to_owned(),
},
)
}
WhyResultDocumentSource::Artifact => {
conn.get_artifact(document_id).ok().flatten().map_or_else(
|| generic_unsupported_storage(document_id, source),
|artifact| StorageExplanation {
origin: "Registered artifact search document".to_owned(),
trust_class: "artifact_metadata".to_owned(),
trust_subclass: Some(artifact.artifact_type),
provenance_uri: artifact
.provenance_uri
.or(artifact.original_path)
.or(artifact.external_ref)
.map(redact_why_search_result_provenance_uri),
workflow_id: None,
created_at: artifact.created_at,
valid_from: None,
valid_to: None,
validity_status: "not_applicable".to_owned(),
validity_window_kind: "search_document".to_owned(),
},
)
}
WhyResultDocumentSource::Evidence => generic_unsupported_storage(document_id, source),
WhyResultDocumentSource::CurationCandidate | WhyResultDocumentSource::Unknown => {
generic_unsupported_storage(document_id, source)
}
WhyResultDocumentSource::Memory => generic_unsupported_storage(document_id, source),
}
}
fn redact_why_search_result_provenance_uri(value: String) -> String {
let path_redacted = redact_why_absolute_path_like_segments(&value);
let secret_redacted = crate::policy::redact_secret_like_content(&path_redacted).content;
redact_why_absolute_path_like_segments(&secret_redacted)
}
fn redact_why_absolute_path_like_segments(input: &str) -> String {
crate::search::redact_search_projection_absolute_path_like_segments(input)
}
fn generic_unsupported_storage(
document_id: &str,
source: WhyResultDocumentSource,
) -> StorageExplanation {
StorageExplanation {
origin: format!(
"{} search result target `{document_id}` is not stored as a memory",
source.human_label()
),
trust_class: "search_document".to_owned(),
trust_subclass: Some(format!("unsupported_{}", source.as_str())),
provenance_uri: Some(format!("ee://search-result/{document_id}")),
workflow_id: None,
created_at: "unknown".to_owned(),
valid_from: None,
valid_to: None,
validity_status: "not_applicable".to_owned(),
validity_window_kind: "search_document".to_owned(),
}
}
fn unsupported_result_target_retrieval(source: WhyResultDocumentSource) -> RetrievalExplanation {
RetrievalExplanation {
confidence: 0.0,
utility: 0.0,
importance: 0.0,
tags: vec![
"result_target".to_owned(),
format!("source:{}", source.as_str()),
"unsupported_for_why".to_owned(),
],
level: "search_document".to_owned(),
kind: source.as_str().to_owned(),
}
}
struct ReportSelectionInputs {
is_active: bool,
selection_score: f32,
above_threshold: bool,
latest_pack_selection: Option<PackSelectionExplanation>,
lifecycle: LifecycleExplanation,
contradictions: Vec<ContradictionMetadata>,
links: Vec<MemoryLinkSummary>,
history: Option<MemoryHistorySummary>,
rationale_traces: Vec<RationaleTraceSummary>,
verification_evidence: Vec<VerificationEvidenceRecord>,
coordination_fallback_evidence: Vec<CoordinationFallbackEvidenceSummary>,
attestation_manifest: Option<JsonValue>,
graph_retrieval: GraphRetrievalExplanation,
load_bearing: Option<LoadBearingWhyExplanation>,
degraded: Vec<WhyDegradation>,
agent_profile: Option<AgentProfileSelectionExplanation>,
dedup_link: Option<DedupLinkEvidence>,
seal: Option<JsonValue>,
}
fn why_conformal_candidates(
memory_id: &str,
selection_score: f32,
links: &[MemoryLinkSummary],
) -> Vec<WhyConformalCandidate> {
let mut candidates = Vec::with_capacity(links.len().saturating_add(1));
candidates.push(WhyConformalCandidate {
memory_id: memory_id.to_owned(),
score: selection_score,
source: "target".to_owned(),
});
candidates.extend(links.iter().map(|link| WhyConformalCandidate {
memory_id: link.linked_memory_id.clone(),
score: link.confidence,
source: format!("link:{}", link.relation),
}));
candidates
}
fn why_influence_candidates(links: &[MemoryLinkSummary]) -> Vec<WhyInfluenceCandidate> {
links
.iter()
.map(|link| WhyInfluenceCandidate {
memory_id: link.linked_memory_id.clone(),
relation: link.relation.clone(),
score: link_influence_score(link),
direction: relation_influence_direction(&link.relation),
})
.collect()
}
fn link_influence_score(link: &MemoryLinkSummary) -> f32 {
let confidence = finite_unit_score(link.confidence);
let weight = finite_unit_score(link.weight.abs());
confidence * weight
}
fn relation_influence_direction(relation: &str) -> WhyInfluenceDirection {
match relation.trim().to_ascii_lowercase().as_str() {
"contradicts" | "contradiction" | "contradicted_by" | "invalidates" | "refutes"
| "conflicts_with" | "supersedes" | "superseded_by" => WhyInfluenceDirection::Negative,
_ => WhyInfluenceDirection::Positive,
}
}
fn finite_unit_score(score: f32) -> f32 {
if score.is_finite() {
score.clamp(0.0, 1.0)
} else {
0.0
}
}
fn build_report(
memory_id: &str,
storage: StorageExplanation,
retrieval: RetrievalExplanation,
selection_inputs: ReportSelectionInputs,
) -> WhyReport {
let selection = SelectionExplanation {
selection_score: selection_inputs.selection_score,
above_confidence_threshold: selection_inputs.above_threshold,
is_active: selection_inputs.is_active,
score_breakdown: format!(
"selection_score = 0.5 * confidence({:.2}) + 0.3 * utility({:.2}) + 0.2 * importance({:.2}) = {:.2}",
retrieval.confidence,
retrieval.utility,
retrieval.importance,
selection_inputs.selection_score
),
latest_pack_selection: selection_inputs.latest_pack_selection,
};
WhyReport::found(memory_id.to_string(), storage, retrieval, selection)
.with_agent_profile(selection_inputs.agent_profile)
.with_lifecycle(selection_inputs.lifecycle)
.with_contradictions(selection_inputs.contradictions)
.with_links(selection_inputs.links)
.with_optional_history(selection_inputs.history)
.with_graph_retrieval(selection_inputs.graph_retrieval)
.with_optional_load_bearing(selection_inputs.load_bearing)
.with_rationale_traces(selection_inputs.rationale_traces)
.with_verification_evidence(selection_inputs.verification_evidence)
.with_coordination_fallback_evidence(selection_inputs.coordination_fallback_evidence)
.with_attestation_manifest(selection_inputs.attestation_manifest)
.with_degradations(selection_inputs.degraded)
.with_optional_dedup_link(selection_inputs.dedup_link)
.with_optional_seal(selection_inputs.seal)
}
fn fetch_seal(conn: &DbConnection, memory: &crate::db::StoredMemory) -> Option<JsonValue> {
let seal = resolve_memory_seal_lineage(conn, memory).ok().flatten()?;
Some(serde_json::json!({
"schema": crate::models::MEMORY_SEAL_SCHEMA_V1,
"contentCommitment": seal.content_commitment,
"sealedAt": seal.sealed_at,
"revealedAt": seal.revealed_at,
"revealVerified": seal.reveal_verified,
"sealed": seal.is_sealed(),
}))
}
fn lifecycle_for_memory(
memory: &crate::db::StoredMemory,
history: Option<&MemoryHistorySummary>,
) -> LifecycleExplanation {
let tombstoned_at = memory.tombstoned_at.clone();
let tombstoned_reason = tombstoned_at
.as_ref()
.and_then(|_| tombstone_reason_from_history(history));
LifecycleExplanation {
status: if tombstoned_at.is_some() {
"tombstoned"
} else {
"active"
},
tombstoned_at,
tombstoned_reason,
}
}
fn tombstone_reason_from_history(history: Option<&MemoryHistorySummary>) -> Option<String> {
history?
.entries
.iter()
.find(|entry| entry.action == crate::db::audit_actions::MEMORY_TOMBSTONE)
.and_then(|entry| entry.details.as_deref())
.and_then(|details| {
serde_json::from_str::<serde_json::Value>(details)
.ok()
.and_then(|value| {
value
.get("reason")
.and_then(serde_json::Value::as_str)
.map(str::trim)
.filter(|reason| !reason.is_empty())
.map(str::to_owned)
})
})
}
fn build_graph_retrieval_explanation(
conn: &DbConnection,
workspace_id: &str,
memory_id: &str,
links: &[MemoryLinkSummary],
contradictions: &[ContradictionMetadata],
validity_status: &str,
) -> GraphRetrievalExplanation {
let options = crate::graph::GraphFeatureEnrichmentOptions {
max_features: usize::MAX,
min_combined_score: 0.0,
..crate::graph::GraphFeatureEnrichmentOptions::default()
};
let snapshot = match conn
.get_latest_graph_snapshot(workspace_id, crate::db::GraphSnapshotType::MemoryLinks)
{
Ok(snapshot) => snapshot,
Err(error) => {
return graph_retrieval_unavailable(
workspace_id,
"graph_snapshot_query_failed",
"medium",
format!("Failed to query graph snapshot: {error}"),
"ee graph centrality-refresh",
links,
contradictions,
);
}
};
let report = crate::graph::enrich_graph_features_from_graph_snapshot(
snapshot.as_ref(),
workspace_id,
crate::db::GraphSnapshotType::MemoryLinks,
&options,
);
let feature = report
.features
.iter()
.find(|feature| feature.memory_id == memory_id);
let pagerank = feature.map_or_else(default_graph_metric, |feature| GraphMetricExplanation {
raw: round_graph_score(feature.pagerank),
normalized: round_graph_score(feature.pagerank_normalized),
rank: feature.pagerank_rank,
weight: 0.6,
contribution: round_graph_score(feature.pagerank_normalized * 0.6),
formula: "pagerank_contribution = pagerank.normalized * 0.6".to_owned(),
});
let betweenness = feature.map_or_else(default_graph_metric, |feature| GraphMetricExplanation {
raw: round_graph_score(feature.betweenness),
normalized: round_graph_score(feature.betweenness_normalized),
rank: feature.betweenness_rank,
weight: 0.4,
contribution: round_graph_score(feature.betweenness_normalized * 0.4),
formula: "betweenness_contribution = betweenness.normalized * 0.4".to_owned(),
});
let mut degraded = graph_degradations_from_report(&report);
let hits = match snapshot
.as_ref()
.map(|snapshot| graph_hits_explanation_from_snapshot(snapshot, memory_id))
{
Some(Ok(hits)) => hits,
Some(Err(degradation)) => {
degraded.push(degradation);
None
}
None => None,
};
let status = if feature.is_some() {
"available".to_owned()
} else if report.status == crate::graph::GraphFeatureEnrichmentStatus::Enriched {
degraded.push(WhyDegradation {
code: "graph_memory_not_in_snapshot",
severity: "low",
message: "Graph snapshot exists, but this memory has no graph score.".to_owned(),
repair: Some("ee graph centrality-refresh".to_owned()),
});
"memory_not_in_graph_snapshot".to_owned()
} else {
report.status.as_str().to_owned()
};
degraded.push(WhyDegradation {
code: "graph_query_relative_features_unavailable",
severity: "low",
message: "Community and query-seed graph features are present as stable fields but unavailable without community detection and query-seed expansion."
.to_owned(),
repair: Some("ee graph communities && ee search --explain".to_owned()),
});
let evidence_support_count = evidence_support_count(links);
let contradiction_count = contradiction_count(links, contradictions);
let orphan_penalty = orphan_penalty(links);
let stale_bridge_penalty = stale_bridge_penalty(validity_status, betweenness.normalized);
GraphRetrievalExplanation {
status,
source: graph_retrieval_source_from_report(&report),
centrality_score: round_graph_score(feature.map_or(0.0, |feature| feature.combined_score)),
authority_score: hits
.as_ref()
.map_or(pagerank.normalized, |hits| hits.authority.normalized),
hub_score: hits
.as_ref()
.map_or(pagerank.normalized, |hits| hits.hub.normalized),
hits,
community_id: None,
distance_to_query_seed: None,
same_cluster_as_top_result: None,
evidence_support_count,
contradiction_count,
orphan_penalty,
stale_bridge_penalty,
pagerank,
betweenness,
labels: feature.map_or_else(Vec::new, |feature| feature.labels.clone()),
reasons: feature.map_or_else(Vec::new, |feature| feature.reasons.clone()),
centrality_formula: "centrality_score = 0.6 * pagerank.normalized + 0.4 * betweenness.normalized"
.to_owned(),
orphan_penalty_formula: "orphan_penalty = 1.0 when no incident memory_links exist, else 0.0"
.to_owned(),
stale_bridge_penalty_formula:
"stale_bridge_penalty = betweenness.normalized when validity_status is expired, else 0.0"
.to_owned(),
degraded,
}
}
fn graph_retrieval_unavailable(
workspace_id: &str,
code: &'static str,
severity: &'static str,
message: String,
repair: &'static str,
links: &[MemoryLinkSummary],
contradictions: &[ContradictionMetadata],
) -> GraphRetrievalExplanation {
let pagerank = default_graph_metric();
let betweenness = default_graph_metric();
GraphRetrievalExplanation {
status: code.to_owned(),
source: GraphRetrievalSourceExplanation {
kind: "graph_snapshot".to_owned(),
workspace_id: Some(workspace_id.to_owned()),
graph_type: Some(crate::db::GraphSnapshotType::MemoryLinks.as_str().to_owned()),
snapshot: None,
},
centrality_score: 0.0,
authority_score: 0.0,
hub_score: 0.0,
hits: None,
community_id: None,
distance_to_query_seed: None,
same_cluster_as_top_result: None,
evidence_support_count: evidence_support_count(links),
contradiction_count: contradiction_count(links, contradictions),
orphan_penalty: orphan_penalty(links),
stale_bridge_penalty: 0.0,
pagerank,
betweenness,
labels: Vec::new(),
reasons: Vec::new(),
centrality_formula: "centrality_score = 0.6 * pagerank.normalized + 0.4 * betweenness.normalized"
.to_owned(),
orphan_penalty_formula: "orphan_penalty = 1.0 when no incident memory_links exist, else 0.0"
.to_owned(),
stale_bridge_penalty_formula:
"stale_bridge_penalty = betweenness.normalized when validity_status is expired, else 0.0"
.to_owned(),
degraded: vec![WhyDegradation {
code,
severity,
message,
repair: Some(repair.to_owned()),
}],
}
}
fn graph_hits_explanation_from_snapshot(
snapshot: &crate::db::StoredGraphSnapshot,
memory_id: &str,
) -> Result<Option<GraphHitsExplanation>, WhyDegradation> {
let centrality = crate::graph::graph_snapshot_centrality_report(snapshot).map_err(|error| {
WhyDegradation {
code: "graph_hits_scores_unavailable",
severity: "medium",
message: format!("Failed to read HITS scores from graph snapshot: {error}"),
repair: Some("ee graph centrality-refresh".to_owned()),
}
})?;
let Some(score) = centrality
.scores
.iter()
.find(|score| score.memory_id == memory_id)
else {
return Ok(None);
};
let hub_max = max_finite_graph_score(centrality.scores.iter().map(|score| score.hub));
let authority_max =
max_finite_graph_score(centrality.scores.iter().map(|score| score.authority));
let hub_ranks = graph_score_rank_map(¢rality.scores, |score| score.hub);
let authority_ranks = graph_score_rank_map(¢rality.scores, |score| score.authority);
let node_count = centrality.scores.len();
let authority = graph_hits_score(
score.authority,
authority_max,
authority_ranks.get(memory_id).copied(),
node_count,
);
let hub = graph_hits_score(
score.hub,
hub_max,
hub_ranks.get(memory_id).copied(),
node_count,
);
let role_label = graph_hits_role_label(authority.normalized, hub.normalized);
let role_rationale = format!(
"HITS role `{role_label}` from authority {:.4} (rank {}) and hub {:.4} (rank {}).",
authority.normalized,
authority
.rank
.map_or_else(|| "none".to_owned(), |rank| rank.to_string()),
hub.normalized,
hub.rank
.map_or_else(|| "none".to_owned(), |rank| rank.to_string())
);
Ok(Some(GraphHitsExplanation {
schema: crate::graph::hits::HITS_REPORT_SCHEMA_V1,
authority,
hub,
role_label,
role_rationale,
}))
}
fn graph_hits_score(
raw: f64,
max_score: f64,
rank: Option<usize>,
node_count: usize,
) -> GraphHitsScoreExplanation {
GraphHitsScoreExplanation {
raw: round_graph_score(raw),
normalized: normalize_why_graph_score(raw, max_score),
rank,
percentile: graph_rank_percentile(rank, node_count),
}
}
fn graph_hits_role_label(authority: f64, hub: f64) -> &'static str {
const STRONG_THRESHOLD: f64 = 0.5;
const BALANCE_EPSILON: f64 = 0.05;
if authority < STRONG_THRESHOLD && hub < STRONG_THRESHOLD {
"weak"
} else if (authority - hub).abs() <= BALANCE_EPSILON {
"balanced"
} else if authority > hub {
"authority"
} else {
"hub"
}
}
fn graph_rank_percentile(rank: Option<usize>, node_count: usize) -> Option<f64> {
rank.map(|rank| {
if node_count <= 1 {
1.0
} else {
let bounded_rank = rank.clamp(1, node_count);
1.0 - ((bounded_rank - 1) as f64 / (node_count - 1) as f64)
}
})
.map(round_graph_score)
}
fn graph_score_rank_map(
scores: &[crate::graph::MemoryCentralityScore],
score_fn: fn(&crate::graph::MemoryCentralityScore) -> f64,
) -> BTreeMap<String, usize> {
let mut ranked = scores
.iter()
.filter(|score| score_fn(score).is_finite() && score_fn(score) > 0.0)
.collect::<Vec<_>>();
ranked.sort_by(|left, right| {
score_fn(right)
.total_cmp(&score_fn(left))
.then_with(|| left.memory_id.cmp(&right.memory_id))
});
ranked
.into_iter()
.enumerate()
.map(|(index, score)| (score.memory_id.clone(), index + 1))
.collect()
}
fn max_finite_graph_score(values: impl Iterator<Item = f64>) -> f64 {
values
.filter(|value| value.is_finite())
.fold(0.0_f64, |max, value| max.max(value))
}
fn normalize_why_graph_score(value: f64, max_value: f64) -> f64 {
if value.is_finite() && max_value.is_finite() && max_value > 0.0 {
round_graph_score((value / max_value).clamp(0.0, 1.0))
} else {
0.0
}
}
fn graph_degradations_from_report(
report: &crate::graph::GraphFeatureEnrichmentReport,
) -> Vec<WhyDegradation> {
report
.degraded
.iter()
.map(|entry| WhyDegradation {
code: entry.code,
severity: entry.severity,
message: entry.message.clone(),
repair: Some(entry.repair.clone()),
})
.collect()
}
fn graph_retrieval_source_from_report(
report: &crate::graph::GraphFeatureEnrichmentReport,
) -> GraphRetrievalSourceExplanation {
GraphRetrievalSourceExplanation {
kind: report.source.kind.to_owned(),
workspace_id: report.source.workspace_id.clone(),
graph_type: report.source.graph_type.clone(),
snapshot: report.source.snapshot.as_ref().map(|snapshot| {
GraphRetrievalSnapshotExplanation {
id: snapshot.id.clone(),
schema_version: snapshot.schema_version.clone(),
snapshot_version: snapshot.snapshot_version,
source_generation: snapshot.source_generation,
status: snapshot.status.clone(),
content_hash: snapshot.content_hash.clone(),
created_at: snapshot.created_at.clone(),
}
}),
}
}
fn default_graph_metric() -> GraphMetricExplanation {
GraphMetricExplanation {
raw: 0.0,
normalized: 0.0,
rank: None,
weight: 0.0,
contribution: 0.0,
formula: "metric unavailable".to_owned(),
}
}
fn evidence_support_count(links: &[MemoryLinkSummary]) -> u32 {
links
.iter()
.filter(|link| link.relation == "supports")
.map(|link| link.evidence_count.max(1))
.fold(0_u32, u32::saturating_add)
}
fn contradiction_count(
links: &[MemoryLinkSummary],
contradictions: &[ContradictionMetadata],
) -> u32 {
let link_count = links
.iter()
.filter(|link| link.relation == "contradicts")
.map(|link| link.evidence_count.max(1))
.fold(0_u32, u32::saturating_add);
link_count.saturating_add(u32::try_from(contradictions.len()).unwrap_or(u32::MAX))
}
fn orphan_penalty(links: &[MemoryLinkSummary]) -> f64 {
if links.is_empty() { 1.0 } else { 0.0 }
}
fn stale_bridge_penalty(validity_status: &str, betweenness_normalized: f64) -> f64 {
if validity_status == "expired" {
round_graph_score(betweenness_normalized.clamp(0.0, 1.0))
} else {
0.0
}
}
fn round_graph_score(value: f64) -> f64 {
if value.is_finite() {
(value * 10_000.0).round() / 10_000.0
} else {
0.0
}
}
fn team_elevation_from_memory(
memory: &crate::db::StoredMemory,
) -> Option<TeamElevationExplanation> {
if memory.trust_class != "peer_human_attested" {
return None;
}
let provenance = crate::core::memory_scope::team_provenance_from_memory(memory);
Some(TeamElevationExplanation {
schema: TEAM_ELEVATION_SCHEMA_V1,
from_trust_class: provenance
.as_ref()
.map(|item| item.origin_trust_class)
.unwrap_or("human_explicit"),
to_trust_class: "peer_human_attested",
reason: "valid signed origin event from an active member; elevated to peer_human_attested because the receiver derived the producer from the verified node and authorization position. producedAt is member-attested provenance, not ranking or authorization authority.".to_owned(),
member_display_name: provenance
.as_ref()
.map(|value| value.member_display_name.clone()),
origin_event_id: memory.provenance_uri.clone().and_then(|uri| {
let trimmed = uri.trim();
(!trimmed.is_empty()).then(|| trimmed.to_owned())
}),
produced_at: provenance.as_ref().map(|value| value.produced_at.clone()),
origin_time_assurance: "member_attested",
})
}
fn determine_origin(trust_class: &str) -> String {
match trust_class {
"human_explicit" => "Explicitly remembered via `ee remember`".to_string(),
"peer_human_attested" => {
"A signed origin from an active member, elevated to peer_human_attested".to_string()
}
"agent_validated" => "Agent assertion with validated outcome evidence".to_string(),
"agent_assertion" => "Agent assertion awaiting validation".to_string(),
"cass_evidence" => "Imported from CASS session evidence".to_string(),
"legacy_import" => "Imported from a legacy Eidetic Engine store".to_string(),
_ => format!("Created with trust class: {trust_class}"),
}
}
fn compute_selection_score(confidence: f32, utility: f32, importance: f32) -> f32 {
0.5 * confidence + 0.3 * utility + 0.2 * importance
}
fn fetch_agent_profile_selection_explanation(
conn: &DbConnection,
workspace_id: &str,
memory_id: &str,
) -> Option<AgentProfileSelectionExplanation> {
let agent_name = crate::core::memory_scope::current_agent_name()?;
let profile = conn
.get_agent_context_profile(workspace_id, &agent_name, memory_id)
.ok()
.flatten()?;
Some(agent_profile_selection_explanation(
agent_name,
profile.counts,
profile.last_seen_at,
))
}
fn agent_profile_selection_explanation(
agent_name: String,
counts: AgentContextProfileCounts,
last_seen_at: String,
) -> AgentProfileSelectionExplanation {
let bias = counts.bias();
AgentProfileSelectionExplanation {
schema: AGENT_CONTEXT_PROFILE_SCHEMA_V1,
agent_name_hash: agent_context_profile_agent_hash(&agent_name),
agent_name,
helpful_count: counts.helpful_count,
harmful_count: counts.harmful_count,
ignored_count: counts.ignored_count,
observed_outcomes: counts.observed_outcomes(),
bias: bias.weight,
max_bias_magnitude: AGENT_PROFILE_BIAS_CAP,
cold_start: bias.cold_start,
cold_start_threshold: AGENT_PROFILE_COLD_START_OUTCOMES,
last_seen_at,
}
}
fn agent_context_profile_agent_hash(agent_name: &str) -> String {
let digest = blake3::hash(agent_name.as_bytes()).to_hex().to_string();
format!("blake3:{}", &digest[..12])
}
fn latest_pack_selection(
conn: &DbConnection,
workspace_id: &str,
entity_kind: &str,
entity_id: &str,
) -> Result<Option<PackSelectionExplanation>, String> {
const RECORD_SCAN_LIMIT: u32 = 128;
let pack_ids = conn
.list_recent_pack_record_ids_for_workspace(
workspace_id,
RECORD_SCAN_LIMIT.saturating_add(1),
)
.map_err(|error| format!("Failed to query pack selection: {error}"))?;
let scan_truncated = pack_ids.len() > RECORD_SCAN_LIMIT as usize;
for pack_id in pack_ids.into_iter().take(RECORD_SCAN_LIMIT as usize) {
let Some(record) = conn
.get_pack_record(&pack_id)
.map_err(|error| format!("Failed to load pack record: {error}"))?
else {
return Err("Pack history changed during selection inspection.".to_owned());
};
if record.workspace_id != workspace_id {
return Err("Pack history changed workspace during selection inspection.".to_owned());
}
let parsed_ledger = crate::db::parse_stored_pack_ledger(&record);
let Some(ledger) = parsed_ledger.available_ledger() else {
return Err(format!(
"Pack selection evidence is unavailable (status {}).",
parsed_ledger.status.as_str()
));
};
let Some(item) = crate::db::pack_ledger_core_array(ledger, "selectedItems")
.into_iter()
.flatten()
.find(|item| {
let typed_match = item.get("entityKind").and_then(JsonValue::as_str)
== Some(entity_kind)
&& item.get("entityId").and_then(JsonValue::as_str) == Some(entity_id);
let legacy_memory_match = entity_kind == "memory"
&& item.get("memoryId").and_then(JsonValue::as_str) == Some(entity_id);
typed_match || legacy_memory_match
})
else {
continue;
};
return pack_selection_from_ledger(record, ledger, item).map(Some);
}
if scan_truncated {
return Err("Pack selection history exceeded the bounded integrity scan.".to_owned());
}
Ok(None)
}
fn pack_selection_from_ledger(
record: crate::db::StoredPackRecord,
ledger: &JsonValue,
item: &JsonValue,
) -> Result<PackSelectionExplanation, String> {
let ledger_storage = crate::db::pack_ledger_storage_summary(record.ledger_json.as_deref());
let query = ledger
.pointer("/request/query")
.and_then(pack_ledger_safe_text)
.ok_or_else(|| "Integrity-verified pack query projection was unavailable.".to_owned())?;
let profile = ledger
.pointer("/request/profile")
.and_then(JsonValue::as_str)
.ok_or_else(|| "Integrity-verified pack profile was unavailable.".to_owned())?;
let why = item
.get("why")
.and_then(pack_ledger_safe_text)
.ok_or_else(|| "Integrity-verified selection explanation was unavailable.".to_owned())?;
Ok(PackSelectionExplanation {
pack_id: crate::models::public_pack_id(&record.id),
query,
profile: profile.to_owned(),
rank: pack_ledger_u32(item, "rank")?,
section: pack_ledger_text(item, "section")?.to_owned(),
estimated_tokens: pack_ledger_u32(item, "estimatedTokens")?,
relevance: pack_ledger_score(item, "/scores/relevance")?,
utility: pack_ledger_score(item, "/scores/utility")?,
attempt_family_multiplicity: item
.get("attemptFamilyMultiplicity")
.filter(|snapshot| !snapshot.is_null())
.cloned(),
why,
pack_hash: record.pack_hash,
ledger_hash: record.ledger_hash,
ledger_status: crate::db::PackLedgerStatus::Available.as_str().to_owned(),
ledger_storage,
selected_at: record.created_at,
})
}
fn pack_ledger_safe_text(value: &JsonValue) -> Option<String> {
let source = match value.get("redacted").and_then(JsonValue::as_bool) {
Some(true) => value.get("redactedText").and_then(JsonValue::as_str),
Some(false) => value.get("text").and_then(JsonValue::as_str),
None => return None,
}?;
Some(crate::policy::redact_public_replay_text(source).content)
}
fn pack_ledger_text<'a>(value: &'a JsonValue, field: &str) -> Result<&'a str, String> {
value
.get(field)
.and_then(JsonValue::as_str)
.ok_or_else(|| format!("Integrity-verified selection field {field} was unavailable."))
}
fn pack_ledger_u32(value: &JsonValue, field: &str) -> Result<u32, String> {
value
.get(field)
.and_then(JsonValue::as_u64)
.and_then(|value| u32::try_from(value).ok())
.ok_or_else(|| format!("Integrity-verified selection field {field} was unavailable."))
}
fn pack_ledger_score(value: &JsonValue, pointer: &str) -> Result<f32, String> {
value
.pointer(pointer)
.and_then(JsonValue::as_f64)
.map(|value| value as f32)
.filter(|value| value.is_finite())
.ok_or_else(|| "Integrity-verified selection score was unavailable.".to_owned())
}
fn required_text(row: &Row, index: usize, column: &str) -> Result<String, String> {
row.get(index)
.and_then(|value| value.as_str())
.map(str::to_string)
.ok_or_else(|| format!("Pack selection column {column} was missing or not text"))
}
struct WhyEvidenceFetch<T> {
items: Vec<T>,
degradation: Option<WhyDegradation>,
}
impl<T> WhyEvidenceFetch<T> {
fn available(items: Vec<T>) -> Self {
Self {
items,
degradation: None,
}
}
fn unavailable(code: &'static str, label: &str, error: impl std::fmt::Display) -> Self {
Self {
items: Vec::new(),
degradation: Some(WhyDegradation {
code,
severity: "medium",
message: format!(
"Could not read {label} for this memory; the evidence is omitted instead of treated as absent. Error: {error}"
),
repair: Some("ee migrate run --workspace . --json".to_owned()),
}),
}
}
}
fn fetch_contradictions(
conn: &DbConnection,
memory_id: &str,
) -> WhyEvidenceFetch<ContradictionMetadata> {
let events = match conn.list_feedback_events_for_target("memory", memory_id) {
Ok(e) => e,
Err(error) => {
return WhyEvidenceFetch::unavailable(
"why_contradictions_unavailable",
"contradiction feedback events",
error,
);
}
};
WhyEvidenceFetch::available(
events
.into_iter()
.filter(|e| e.signal == "contradiction")
.map(|e| ContradictionMetadata {
event_id: e.id,
weight: e.weight,
source_type: e.source_type,
reason: e.reason,
created_at: e.created_at,
applied: e.applied_at.is_some(),
})
.collect(),
)
}
pub fn find_embed_dedup_link(conn: &DbConnection, memory_id: &str) -> Option<DedupLinkEvidence> {
let links = conn.list_memory_links_for_memory(memory_id, None).ok()?;
for link in links {
let Some(raw_metadata) = link.metadata_json.as_deref() else {
continue;
};
let Ok(metadata) = serde_json::from_str::<serde_json::Value>(raw_metadata) else {
continue;
};
if metadata.get("schema").and_then(serde_json::Value::as_str)
!= Some(WHY_DEDUP_LINK_SCHEMA_REF)
{
continue;
}
let target_memory_id = if link.src_memory_id == memory_id {
link.dst_memory_id.clone()
} else {
link.src_memory_id.clone()
};
let decision = metadata
.get("decision")
.and_then(serde_json::Value::as_str)
.unwrap_or("")
.to_string();
let reason = metadata
.get("reason")
.and_then(serde_json::Value::as_str)
.unwrap_or("")
.to_string();
let relationship = metadata
.get("relationship")
.and_then(serde_json::Value::as_str)
.unwrap_or("embedding_reuse")
.to_string();
let hamming_distance = metadata
.get("hammingDistance")
.and_then(serde_json::Value::as_u64)
.and_then(|value| u32::try_from(value).ok());
let cosine_similarity = metadata
.get("cosineSimilarity")
.and_then(serde_json::Value::as_f64)
.map(|value| value as f32);
let cosine_floor = metadata
.get("cosineFloor")
.and_then(serde_json::Value::as_f64)
.map(|value| value as f32);
return Some(DedupLinkEvidence {
schema: WHY_DEDUP_LINK_SCHEMA_REF,
link_id: link.id,
target_memory_id,
decision,
reason,
relationship,
hamming_distance,
cosine_similarity,
cosine_floor,
link_source: link.source,
link_weight: link.weight,
link_confidence: link.confidence,
created_at: link.created_at,
});
}
None
}
fn fetch_links(conn: &DbConnection, memory_id: &str) -> WhyEvidenceFetch<MemoryLinkSummary> {
let stored_links = match conn.list_memory_links_for_memory(memory_id, None) {
Ok(links) => links,
Err(error) => {
return WhyEvidenceFetch::unavailable("why_links_unavailable", "memory links", error);
}
};
WhyEvidenceFetch::available(
stored_links
.into_iter()
.filter(|link| {
crate::graph::memory_link_mesh_metadata_visible(link.metadata_json.as_deref())
})
.map(|link| {
let direction = if !link.directed {
"undirected".to_string()
} else if link.src_memory_id == memory_id {
"outgoing".to_string()
} else {
"incoming".to_string()
};
let linked_memory_id = if link.src_memory_id == memory_id {
link.dst_memory_id.clone()
} else {
link.src_memory_id.clone()
};
MemoryLinkSummary {
link_id: link.id,
linked_memory_id,
relation: link.relation,
direction,
confidence: link.confidence,
weight: link.weight,
evidence_count: link.evidence_count,
source: link.source,
created_at: link.created_at,
}
})
.collect(),
)
}
const WHY_HISTORY_LIMIT: usize = 50;
const WHY_COORDINATION_FALLBACK_LIMIT: usize = 20;
const COORDINATION_FALLBACK_EVIDENCE_SCHEMA_V1: &str = "ee.coordination_fallback_evidence.v1";
const COORDINATION_FALLBACK_LEDGER_RECORD_SCHEMA_V1: &str =
"ee.coordination_fallback_ledger_record.v1";
const COORDINATION_FALLBACK_LEDGER_FILE: &str = "coordination-fallback-evidence.jsonl";
const COORDINATION_FALLBACK_LEDGER_MAX_BYTES: u64 = 16 * 1024 * 1024;
fn fetch_history(conn: &DbConnection, memory_id: &str) -> WhyEvidenceFetch<MemoryHistorySummary> {
let stored_entries = match conn.list_audit_by_target("memory", memory_id, None) {
Ok(entries) => entries,
Err(error) => {
return WhyEvidenceFetch::unavailable(
"why_history_unavailable",
"memory audit history",
error,
);
}
};
let visible_entries: Vec<_> = stored_entries
.into_iter()
.filter(|entry| entry.action != crate::db::audit_actions::WHY_INSPECTED)
.collect();
let total_count = visible_entries.len() as u32;
let truncated = visible_entries.len() > WHY_HISTORY_LIMIT;
let entries = visible_entries
.into_iter()
.take(WHY_HISTORY_LIMIT)
.map(|entry| MemoryHistorySummaryEntry {
audit_id: entry.id,
timestamp: entry.timestamp,
actor: entry.actor,
action: entry.action,
details: entry.details.map(redact_why_history_details),
})
.collect();
WhyEvidenceFetch::available(vec![MemoryHistorySummary {
entries,
total_count,
truncated,
}])
}
fn redact_why_history_details(details: String) -> String {
match serde_json::from_str::<serde_json::Value>(&details) {
Ok(mut value) => {
redact_why_history_json_value(&mut value);
serde_json::to_string(&value)
.unwrap_or_else(|_| redact_why_search_result_provenance_uri(details))
}
Err(_) => redact_why_search_result_provenance_uri(details),
}
}
fn redact_why_history_json_value(value: &mut serde_json::Value) {
match value {
serde_json::Value::String(text) => {
*text = redact_why_search_result_provenance_uri(std::mem::take(text));
}
serde_json::Value::Array(items) => {
for item in items {
redact_why_history_json_value(item);
}
}
serde_json::Value::Object(fields) => {
for item in fields.values_mut() {
redact_why_history_json_value(item);
}
}
serde_json::Value::Null | serde_json::Value::Bool(_) | serde_json::Value::Number(_) => {}
}
}
fn fetch_verification_evidence(
conn: &DbConnection,
target_type: &str,
target_id: &str,
) -> WhyEvidenceFetch<VerificationEvidenceRecord> {
match crate::core::verify::verification_records_for_target(conn, target_type, target_id) {
Ok(records) => WhyEvidenceFetch::available(records),
Err(error) => WhyEvidenceFetch::unavailable(
"why_verification_evidence_unavailable",
"verification evidence",
error,
),
}
}
fn fetch_coordination_fallback_evidence(
database_path: &Path,
memory: &crate::db::StoredMemory,
tags: &[String],
verification_evidence: &[VerificationEvidenceRecord],
) -> WhyEvidenceFetch<CoordinationFallbackEvidenceSummary> {
let Some(workspace_root) = workspace_root_from_database_path(database_path) else {
return WhyEvidenceFetch::available(Vec::new());
};
let ledger_path = workspace_root
.join(".ee")
.join(COORDINATION_FALLBACK_LEDGER_FILE);
let file = match File::open(&ledger_path) {
Ok(file) => file,
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
return WhyEvidenceFetch::available(Vec::new());
}
Err(error) => {
return WhyEvidenceFetch::unavailable(
"why_coordination_fallback_evidence_unavailable",
"coordination fallback evidence",
error,
);
}
};
let memory_link_ids = why_memory_coordination_link_ids(memory, tags);
let verification_ids = verification_evidence
.iter()
.map(|evidence| evidence.verification_id.clone())
.collect::<BTreeSet<_>>();
let mut malformed_count = 0_u32;
let mut items = Vec::new();
for line in BufReader::new(file.take(COORDINATION_FALLBACK_LEDGER_MAX_BYTES)).lines() {
let line = match line {
Ok(line) => line,
Err(error) => {
return WhyEvidenceFetch::unavailable(
"why_coordination_fallback_evidence_unavailable",
"coordination fallback evidence",
error,
);
}
};
if line.trim().is_empty() {
continue;
}
let Some(summary) = serde_json::from_str::<serde_json::Value>(&line)
.ok()
.and_then(|record| coordination_fallback_summary_from_record(&record))
else {
malformed_count = malformed_count.saturating_add(1);
continue;
};
if coordination_fallback_matches_memory(&summary, &memory_link_ids, &verification_ids)
&& items.len() < WHY_COORDINATION_FALLBACK_LIMIT
{
items.push(summary);
}
}
items.sort_by(|left, right| {
left.evidence_id
.cmp(&right.evidence_id)
.then_with(|| left.content_hash.cmp(&right.content_hash))
});
items.dedup_by(|left, right| {
left.evidence_id == right.evidence_id && left.content_hash == right.content_hash
});
let degradation = (malformed_count > 0).then(|| WhyDegradation {
code: "why_coordination_fallback_evidence_malformed",
severity: "low",
message: format!(
"Skipped {malformed_count} malformed coordination fallback ledger record(s) while explaining this memory."
),
repair: Some("Repair or regenerate .ee/coordination-fallback-evidence.jsonl with `ee coordination evidence ingest`.".to_owned()),
});
WhyEvidenceFetch { items, degradation }
}
fn why_memory_coordination_link_ids(
memory: &crate::db::StoredMemory,
tags: &[String],
) -> BTreeSet<String> {
let mut ids = BTreeSet::new();
if let Some(workflow_id) = memory.workflow_id.as_deref().map(str::trim)
&& !workflow_id.is_empty()
{
ids.insert(workflow_id.to_owned());
}
for tag in tags {
if let Some(bead_id) = coordination_bead_id_from_tag(tag) {
ids.insert(bead_id);
}
}
ids
}
fn coordination_bead_id_from_tag(tag: &str) -> Option<String> {
let trimmed = tag.trim();
let candidate = trimmed
.strip_prefix("bead:")
.or_else(|| trimmed.strip_prefix("bead="))
.or_else(|| trimmed.strip_prefix("issue:"))
.or_else(|| trimmed.strip_prefix("issue="))
.unwrap_or(trimmed)
.trim();
candidate.starts_with("bd-").then(|| candidate.to_owned())
}
fn coordination_fallback_matches_memory(
summary: &CoordinationFallbackEvidenceSummary,
memory_link_ids: &BTreeSet<String>,
verification_ids: &BTreeSet<String>,
) -> bool {
summary
.linked_bead_ids
.iter()
.any(|id| memory_link_ids.contains(id))
|| summary
.linked_verification_ids
.iter()
.any(|id| verification_ids.contains(id))
}
fn coordination_fallback_summary_from_record(
record: &serde_json::Value,
) -> Option<CoordinationFallbackEvidenceSummary> {
if record.get("schema").and_then(serde_json::Value::as_str)
!= Some(COORDINATION_FALLBACK_LEDGER_RECORD_SCHEMA_V1)
{
return None;
}
let content_hash = coordination_fallback_string(record, "/contentHash")?;
let evidence = record.get("evidence")?;
if evidence.get("schema").and_then(serde_json::Value::as_str)
!= Some(COORDINATION_FALLBACK_EVIDENCE_SCHEMA_V1)
{
return None;
}
if evidence.pointer("/summary/redacted") != Some(&serde_json::Value::Bool(true))
|| evidence.pointer("/redaction/rawInboxIncluded") != Some(&serde_json::Value::Bool(false))
|| evidence.pointer("/redaction/rawLogIncluded") != Some(&serde_json::Value::Bool(false))
|| evidence.pointer("/redaction/secretScanApplied") != Some(&serde_json::Value::Bool(true))
{
return None;
}
if !matches!(
evidence
.pointer("/redaction/pathPolicy")
.and_then(serde_json::Value::as_str),
Some("redact_home" | "hash_paths" | "labels_only")
) {
return None;
}
Some(CoordinationFallbackEvidenceSummary {
source_schema: COORDINATION_FALLBACK_EVIDENCE_SCHEMA_V1,
evidence_id: coordination_fallback_string(evidence, "/evidenceId")?,
status: coordination_fallback_string(evidence, "/status")?,
source_kind: coordination_fallback_string(evidence, "/source/kind")?,
reason_code: coordination_fallback_string(evidence, "/reasonCode")?,
captured_at: coordination_fallback_string(evidence, "/capturedAt")?,
content_hash,
linked_bead_ids: coordination_fallback_string_array(evidence.pointer("/links/beadIds")),
linked_verification_ids: coordination_fallback_string_array(
evidence.pointer("/links/verificationIds"),
),
linked_support_bundle_ids: coordination_fallback_string_array(
evidence.pointer("/links/supportBundleIds"),
),
})
}
fn coordination_fallback_string(value: &serde_json::Value, pointer: &str) -> Option<String> {
value
.pointer(pointer)
.and_then(serde_json::Value::as_str)
.map(str::trim)
.filter(|text| !text.is_empty())
.map(str::to_owned)
}
fn coordination_fallback_string_array(value: Option<&serde_json::Value>) -> Vec<String> {
let mut values = value
.and_then(serde_json::Value::as_array)
.into_iter()
.flatten()
.filter_map(serde_json::Value::as_str)
.map(str::trim)
.filter(|text| !text.is_empty())
.map(str::to_owned)
.collect::<Vec<_>>();
values.sort();
values.dedup();
values
}
fn fetch_rationale_traces(
conn: &DbConnection,
workspace_id: &str,
memory_id: &str,
) -> WhyEvidenceFetch<RationaleTraceSummary> {
let stored = match conn.list_rationale_traces_for_target(workspace_id, "memory", memory_id) {
Ok(traces) => traces,
Err(error) => {
return WhyEvidenceFetch::unavailable(
"why_rationale_traces_unavailable",
"rationale traces",
error,
);
}
};
WhyEvidenceFetch::available(
stored
.into_iter()
.filter_map(|s| RationaleTraceSummary::from_trace(&s.trace))
.collect(),
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::db::{
CreateArtifactInput, CreateGraphSnapshotInput, CreateMemoryInput,
CreateProceduralRuleInput, CreateSessionInput, CreateWorkspaceInput, GraphSnapshotType,
};
use crate::models::{
RationaleTraceKind, RationaleTracePosture, RationaleTraceVisibility, RedactionStatus,
};
type TestResult = Result<(), String>;
fn ensure<T: std::fmt::Debug + PartialEq>(actual: T, expected: T, ctx: &str) -> TestResult {
if actual == expected {
Ok(())
} else {
Err(format!("{ctx}: expected {expected:?}, got {actual:?}"))
}
}
#[test]
fn determine_origin_explains_peer_human_attestation_without_overclaiming() {
let origin = determine_origin("peer_human_attested");
assert!(origin.contains("signed origin"));
assert!(origin.contains("active member"));
assert!(!origin.contains("typed"));
}
#[test]
fn why_report_not_found_is_correct() -> TestResult {
let report = WhyReport::not_found("mem_test".to_string());
ensure(report.found, false, "found")?;
ensure(report.memory_id, "mem_test".to_string(), "memory_id")?;
ensure(report.storage.is_none(), true, "storage is none")?;
ensure(report.error.is_none(), true, "no error")
}
#[test]
fn why_report_error_captures_message() -> TestResult {
let report = WhyReport::error("mem_test".to_string(), "db error".to_string());
ensure(report.found, false, "found")?;
ensure(report.error, Some("db error".to_string()), "error message")
}
#[test]
fn why_report_version_matches_package() -> TestResult {
let report = WhyReport::not_found("mem_test".to_string());
ensure(report.version, env!("CARGO_PKG_VERSION"), "version")
}
#[test]
fn why_evidence_freshness_degradation_redacts_provenance_detail_and_repair() -> TestResult {
let secret = "AbCDefGhIjKlMnOpQrStUvWxYz0123456789abCDefGhIj";
let freshness = EvidenceFreshness {
status: EvidenceFreshnessStatus::MissingSource,
provenance_uri: Some(
"file:/Users/jemanuel/projects/eidetic_engine_cli/CLOSE_THE_GAP_PLAN.md#L1186"
.to_string(),
),
detail: format!(
"Referenced provenance file /Users/jemanuel/projects/eidetic_engine_cli/CLOSE_THE_GAP_PLAN.md is missing; token={secret}."
),
repair: Some(format!(
"Restore /Users/jemanuel/projects/eidetic_engine_cli/CLOSE_THE_GAP_PLAN.md with token={secret}."
)),
};
let degradation = why_evidence_freshness_degradation("mem_test", &freshness)
.ok_or("expected freshness degradation")?;
ensure(
degradation.message.contains("[REDACTED_PATH]"),
true,
"message path placeholder",
)?;
ensure(
degradation.message.contains("[REDACTED:token]"),
true,
"message token placeholder",
)?;
ensure(
degradation.message.contains("/Users/jemanuel"),
false,
"message raw path leak",
)?;
ensure(
degradation.message.contains(secret),
false,
"message raw token leak",
)?;
let repair = degradation
.repair
.as_deref()
.ok_or("expected redacted repair")?;
ensure(
repair.contains("[REDACTED_PATH]"),
true,
"repair path placeholder",
)?;
ensure(
repair.contains("[REDACTED:token]"),
true,
"repair token placeholder",
)?;
ensure(
repair.contains("/Users/jemanuel"),
false,
"repair raw path leak",
)
}
#[test]
fn selection_score_computation() -> TestResult {
let score = compute_selection_score(0.8, 0.6, 0.7);
let expected = 0.5 * 0.8 + 0.3 * 0.6 + 0.2 * 0.7;
ensure((score - expected).abs() < 0.001, true, "score computation")
}
#[test]
fn explain_memory_emits_team_provenance_for_peer_human_attested() -> TestResult {
let temp = tempfile::tempdir().map_err(|error| error.to_string())?;
let database_path = temp.path().join(".ee").join("ee.db");
std::fs::create_dir_all(
database_path
.parent()
.ok_or("database path should have parent")?,
)
.map_err(|error| error.to_string())?;
let workspace_id = "wsp_00000000000000000000001077";
let memory_id = "mem_00000000000000000000001077";
let connection =
crate::db::DbConnection::open_file(&database_path).map_err(|e| e.to_string())?;
connection.migrate().map_err(|e| e.to_string())?;
connection
.insert_workspace(
workspace_id,
&CreateWorkspaceInput {
path: temp.path().display().to_string(),
name: Some("why team provenance".to_owned()),
},
)
.map_err(|e| e.to_string())?;
connection
.insert_memory(
memory_id,
&CreateMemoryInput {
workspace_id: workspace_id.to_owned(),
level: "semantic".to_owned(),
kind: "fact".to_owned(),
content: "Teammate analysis of the checkout.".to_owned(),
workflow_id: None,
confidence: 0.8,
utility: 0.7,
importance: 0.6,
provenance_uri: Some("evt_team_origin_analysts_0001".to_owned()),
trust_class: "peer_human_attested".to_owned(),
trust_subclass: Some(
"agent:Analysts; produced_at=2026-08-16T00:00:00Z; project=acme-analysis; origin_trust=agent_assertion"
.to_owned(),
),
tags: Vec::new(),
valid_from: None,
valid_to: None,
},
)
.map_err(|e| e.to_string())?;
drop(connection);
let report = explain_memory(&WhyOptions {
database_path: &database_path,
memory_id,
confidence_threshold: 0.5,
});
let provenance = report
.team_provenance
.as_ref()
.ok_or("expected teamProvenance on teammate memory")?;
ensure(
provenance.member_display_name.as_str(),
"Analysts",
"member display name",
)?;
ensure(
provenance.origin_trust_class,
"agent_assertion",
"origin trust class",
)?;
ensure(
provenance.project_name.as_deref().unwrap_or(""),
"acme-analysis",
"project name",
)?;
ensure(
provenance.produced_at.as_str(),
"2026-08-16T00:00:00Z",
"produced at",
)?;
let elevation = report
.elevation
.as_ref()
.ok_or("expected elevation on teammate memory")?;
ensure(
elevation.to_trust_class,
"peer_human_attested",
"elevated trust class",
)?;
ensure(
elevation.from_trust_class,
"agent_assertion",
"origin declared trust class",
)?;
ensure(
elevation.reason.contains("signed origin event"),
true,
"elevation names the signed origin",
)?;
ensure(
elevation.origin_event_id.as_deref(),
Some("evt_team_origin_analysts_0001"),
"origin event id",
)?;
let json = crate::output::render_why_json(&report);
ensure(
json.contains("\"teamProvenance\""),
true,
"why JSON must carry teamProvenance",
)?;
ensure(json.contains("Analysts"), true, "why JSON member name")?;
ensure(
json.contains("\"elevation\""),
true,
"why JSON must carry elevation",
)?;
ensure(
json.contains("peer_human_attested"),
true,
"why JSON elevation trust class",
)
}
#[test]
fn explain_memory_attaches_hits_role_scores_from_graph_snapshot() -> TestResult {
let temp = tempfile::tempdir().map_err(|error| error.to_string())?;
let database_path = temp.path().join(".ee").join("ee.db");
std::fs::create_dir_all(
database_path
.parent()
.ok_or("database path should have parent")?,
)
.map_err(|error| error.to_string())?;
let workspace_id = "wsp_00000000000000000000001001";
let memory_id = "mem_00000000000000000000001001";
let connection =
crate::db::DbConnection::open_file(&database_path).map_err(|e| e.to_string())?;
connection.migrate().map_err(|e| e.to_string())?;
connection
.insert_workspace(
workspace_id,
&CreateWorkspaceInput {
path: temp.path().display().to_string(),
name: Some("why HITS".to_owned()),
},
)
.map_err(|e| e.to_string())?;
connection
.insert_memory(
memory_id,
&CreateMemoryInput {
workspace_id: workspace_id.to_owned(),
level: "semantic".to_owned(),
kind: "fact".to_owned(),
content: "Hub memory orients a dependency map.".to_owned(),
workflow_id: None,
confidence: 0.9,
utility: 0.8,
importance: 0.7,
provenance_uri: None,
trust_class: "human_explicit".to_owned(),
trust_subclass: None,
tags: Vec::new(),
valid_from: Some("2026-05-20T00:00:00Z".to_owned()),
valid_to: None,
},
)
.map_err(|e| e.to_string())?;
connection
.insert_graph_snapshot(
"gsnap_0000000000000000000001001",
&CreateGraphSnapshotInput {
workspace_id: workspace_id.to_owned(),
snapshot_version: 7,
schema_version: "ee.graph.snapshot.v1".to_owned(),
graph_type: GraphSnapshotType::MemoryLinks,
node_count: 3,
edge_count: 2,
metrics_json: serde_json::json!({
"nodes": [
{
"memoryId": memory_id,
"pagerank": 0.2,
"betweenness": 0.1,
"hub": 0.9,
"authority": 0.2
},
{
"memoryId": "mem_00000000000000000000001002",
"pagerank": 0.8,
"betweenness": 0.2,
"hub": 0.1,
"authority": 0.8
},
{
"memoryId": "mem_00000000000000000000001003",
"pagerank": 0.4,
"betweenness": 0.3,
"hub": 0.3,
"authority": 0.4
}
],
"edges": []
})
.to_string(),
content_hash: "blake3:why-hits".to_owned(),
source_generation: 11,
expires_at: None,
},
)
.map_err(|e| e.to_string())?;
drop(connection);
let report = explain_memory(&WhyOptions {
database_path: &database_path,
memory_id,
confidence_threshold: WhyOptions::DEFAULT_CONFIDENCE_THRESHOLD,
});
let graph = report
.graph_retrieval
.as_ref()
.ok_or_else(|| "why should include graph retrieval features".to_owned())?;
let hits = graph
.hits
.as_ref()
.ok_or_else(|| "why should include HITS scores".to_owned())?;
ensure(
hits.schema,
crate::graph::hits::HITS_REPORT_SCHEMA_V1,
"schema",
)?;
ensure(hits.role_label, "hub", "role label")?;
ensure(hits.hub.rank, Some(1_usize), "hub rank")?;
ensure(hits.hub.percentile, Some(1.0), "hub percentile")?;
ensure(hits.authority.rank, Some(3_usize), "authority rank")?;
ensure(hits.authority.percentile, Some(0.0), "authority percentile")?;
ensure(graph.hub_score, 1.0, "top-level hub score")?;
ensure(graph.authority_score, 0.25, "top-level authority score")
}
#[test]
fn agent_profile_selection_explanation_reports_counts_and_cap() -> TestResult {
let explanation = agent_profile_selection_explanation(
"FrostyMoose".to_string(),
AgentContextProfileCounts::new(20, 1, 3),
"2026-05-16T01:12:00Z".to_string(),
);
ensure(
explanation.schema,
AGENT_CONTEXT_PROFILE_SCHEMA_V1,
"schema",
)?;
ensure(explanation.helpful_count, 20, "helpful count")?;
ensure(explanation.harmful_count, 1, "harmful count")?;
ensure(explanation.ignored_count, 3, "ignored count")?;
ensure(explanation.observed_outcomes, 24, "observed outcomes")?;
ensure(explanation.cold_start, false, "cold start")?;
ensure(
explanation.bias.abs() <= AGENT_PROFILE_BIAS_CAP,
true,
"bias cap",
)?;
ensure(
explanation.cold_start_threshold,
AGENT_PROFILE_COLD_START_OUTCOMES,
"cold start threshold",
)
}
#[test]
fn result_target_resolves_to_search_doc_id() -> TestResult {
ensure(
resolve_why_memory_id("result:mem_00000000000000000000000001"),
"mem_00000000000000000000000001",
"result target",
)
}
#[test]
fn result_source_repair_strings_are_actionable_after_bd_11pjb() -> TestResult {
for source in [
WhyResultDocumentSource::Memory,
WhyResultDocumentSource::Session,
WhyResultDocumentSource::Artifact,
WhyResultDocumentSource::CurationCandidate,
WhyResultDocumentSource::Unknown,
] {
let repair = source.repair();
ensure(
crate::core::degraded_honesty::classify_repair_command(repair),
crate::core::degraded_honesty::RepairCommandKind::Actionable,
&format!("repair for {source:?} should be Actionable"),
)?;
}
Ok(())
}
#[test]
fn result_target_classifies_non_memory_sources() -> TestResult {
ensure(
resolve_why_target("result:sess_00000000000000000000000001")
.unsupported_result_source(),
Some(WhyResultDocumentSource::Session),
"session result target",
)?;
ensure(
resolve_why_target("result:art_00000000000000000000000001").unsupported_result_source(),
Some(WhyResultDocumentSource::Artifact),
"artifact result target",
)?;
ensure(
resolve_why_target("result:curate_0000000000000000000001").unsupported_result_source(),
Some(WhyResultDocumentSource::CurationCandidate),
"curation candidate result target",
)?;
ensure(
resolve_why_target("result:mem_00000000000000000000000001").unsupported_result_source(),
None,
"memory result target",
)
}
#[test]
fn empty_result_target_stays_queryable_for_not_found_errors() -> TestResult {
ensure(
resolve_why_memory_id("result:"),
"result:",
"empty result target",
)
}
#[test]
fn determine_origin_for_explicit_memory() -> TestResult {
let origin = determine_origin("human_explicit");
ensure(
origin.contains("ee remember"),
true,
"human_explicit origin mentions ee remember",
)
}
#[test]
fn determine_origin_for_cass_import() -> TestResult {
let origin = determine_origin("cass_evidence");
ensure(
origin.contains("CASS"),
true,
"cass_evidence origin mentions CASS",
)
}
#[test]
fn found_report_can_carry_pack_selection() -> TestResult {
let selection = PackSelectionExplanation {
pack_id: "pack_00000000000000000000000001".to_string(),
query: "prepare release".to_string(),
profile: "compact".to_string(),
rank: 1,
section: "procedural_rules".to_string(),
estimated_tokens: 8,
relevance: 0.91,
utility: 0.8,
attempt_family_multiplicity: None,
why: "selected because it matches the release task".to_string(),
pack_hash: "hash".to_string(),
ledger_hash: None,
ledger_status: "missing".to_string(),
ledger_storage: serde_json::json!({"mode": "missing"}),
selected_at: "2026-04-29T12:00:00Z".to_string(),
};
let report = build_report(
"mem_00000000000000000000000001",
StorageExplanation {
origin: "Explicitly remembered via `ee remember`".to_string(),
trust_class: "human_explicit".to_string(),
trust_subclass: None,
provenance_uri: None,
workflow_id: None,
created_at: "2026-04-29T12:00:00Z".to_string(),
valid_from: None,
valid_to: None,
validity_status: "unknown".to_string(),
validity_window_kind: "unbounded".to_string(),
},
RetrievalExplanation {
confidence: 0.9,
utility: 0.8,
importance: 0.7,
tags: Vec::new(),
level: "procedural".to_string(),
kind: "rule".to_string(),
},
ReportSelectionInputs {
is_active: true,
selection_score: 0.83,
above_threshold: true,
latest_pack_selection: Some(selection),
lifecycle: LifecycleExplanation {
status: "active",
tombstoned_at: None,
tombstoned_reason: None,
},
contradictions: Vec::new(),
links: Vec::new(),
history: None,
rationale_traces: Vec::new(),
verification_evidence: Vec::new(),
coordination_fallback_evidence: Vec::new(),
attestation_manifest: None,
graph_retrieval: graph_retrieval_unavailable(
"wsp_01234567890123456789012345",
"graph_snapshot_missing",
"medium",
"No persisted graph snapshot exists for feature enrichment.".to_string(),
"ee graph centrality-refresh",
&[],
&[],
),
load_bearing: None,
degraded: Vec::new(),
agent_profile: None,
dedup_link: None,
seal: None,
},
);
ensure(report.found, true, "found")?;
ensure(
report
.selection
.and_then(|selection| selection.latest_pack_selection)
.map(|selection| selection.rank),
Some(1),
"pack rank",
)
}
#[test]
fn explain_memory_includes_linked_verification_evidence() -> TestResult {
let temp = tempfile::tempdir().map_err(|error| error.to_string())?;
let database_path = temp.path().join(".ee").join("ee.db");
std::fs::create_dir_all(
database_path
.parent()
.ok_or("database path should have parent")?,
)
.map_err(|error| error.to_string())?;
let memory_id = "mem_00000000000000000000001005";
let evidence = crate::models::sample_verification_evidence_records()
.into_iter()
.next()
.ok_or("sample evidence exists")?;
let record_report = crate::core::verify::record_verification_evidence(
crate::core::verify::VerificationRecordOptions {
database_path: &database_path,
workspace_path: temp.path(),
target_type: "memory",
target_id: memory_id,
actor: Some("codex:test"),
evidence: evidence.clone(),
},
)
.map_err(|error| error.to_string())?;
let connection = crate::db::DbConnection::open_file(&database_path)
.map_err(|error| error.to_string())?;
connection
.insert_memory(
memory_id,
&crate::db::CreateMemoryInput {
workspace_id: record_report.workspace_id,
level: "procedural".to_owned(),
kind: "rule".to_owned(),
content: "Run verification gates before closing beads.".to_owned(),
workflow_id: None,
confidence: 0.8,
utility: 0.7,
importance: 0.6,
provenance_uri: None,
trust_class: "agent_assertion".to_owned(),
trust_subclass: None,
tags: vec!["verification".to_owned()],
valid_from: None,
valid_to: None,
},
)
.map_err(|error| error.to_string())?;
let report = explain_memory(&WhyOptions {
database_path: &database_path,
memory_id,
confidence_threshold: WhyOptions::DEFAULT_CONFIDENCE_THRESHOLD,
});
ensure(report.found, true, "why found memory")?;
ensure(
report.verification_evidence.len(),
1_usize,
"verification evidence count",
)?;
ensure(
report.verification_evidence[0].verification_id.as_str(),
evidence.verification_id.as_str(),
"verification id",
)
}
#[test]
fn explain_memory_includes_linked_coordination_fallback_evidence() -> TestResult {
let temp = tempfile::tempdir().map_err(|error| error.to_string())?;
let database_path = temp.path().join(".ee").join("ee.db");
let ledger_dir = database_path
.parent()
.ok_or("database path should have parent")?;
std::fs::create_dir_all(ledger_dir).map_err(|error| error.to_string())?;
let conn = DbConnection::open_file(&database_path).map_err(|error| error.to_string())?;
conn.migrate().map_err(|error| error.to_string())?;
let workspace_id = "wsp_00000000000000000000001004";
let memory_id = "mem_00000000000000000000001004";
conn.insert_workspace(
workspace_id,
&CreateWorkspaceInput {
path: temp.path().display().to_string(),
name: Some("coordination-fallback-why".to_string()),
},
)
.map_err(|error| error.to_string())?;
conn.insert_memory(
memory_id,
&crate::db::CreateMemoryInput {
workspace_id: workspace_id.to_string(),
level: "procedural".to_string(),
kind: "rule".to_string(),
content: "Preserve coordination fallback evidence for handoff.".to_string(),
workflow_id: Some("bd-1zb7k.13.2".to_string()),
confidence: 0.8,
utility: 0.7,
importance: 0.6,
provenance_uri: None,
trust_class: "agent_assertion".to_string(),
trust_subclass: None,
tags: Vec::new(),
valid_from: None,
valid_to: None,
},
)
.map_err(|error| error.to_string())?;
let matched = serde_json::json!({
"schema": COORDINATION_FALLBACK_LEDGER_RECORD_SCHEMA_V1,
"contentHash": "blake3:matched",
"evidence": {
"schema": COORDINATION_FALLBACK_EVIDENCE_SCHEMA_V1,
"evidenceId": "coord_fallback_why_01",
"capturedAt": "2026-05-16T21:06:00Z",
"status": "blocked",
"source": {
"kind": "agent_mail",
"sourceId": "file:///Users/alice/private/agent-mail.jsonl?api_key=redaction-fixture"
},
"reasonCode": "agent_mail_transport_unavailable",
"summary": {
"text": "raw summary text must not enter why",
"contentHash": "blake3:summary",
"redacted": true
},
"links": {
"beadIds": ["bd-1zb7k.13.2"],
"verificationIds": ["rch_cmd_test"],
"supportBundleIds": ["bundle_01"]
},
"fallbackAction": {
"kind": "record_only",
"summary": "Preserve redacted fallback evidence.",
"command": null,
"manualStep": null
},
"redaction": {
"rawInboxIncluded": false,
"rawLogIncluded": false,
"secretScanApplied": true,
"pathPolicy": "labels_only"
},
"producer": {
"schema": "ee.producer.metadata.v1",
"sourceSystem": "coordination_fallback",
"identity": {"status": "unknown", "agentName": null, "harness": null, "model": null},
"run": {"runId": "coord-fallback-test", "sessionId": null, "workspaceFingerprint": "repo:test"},
"observedAt": "2026-05-16T21:06:00Z"
}
}
});
let unmatched = serde_json::json!({
"schema": COORDINATION_FALLBACK_LEDGER_RECORD_SCHEMA_V1,
"contentHash": "blake3:unmatched",
"evidence": {
"schema": COORDINATION_FALLBACK_EVIDENCE_SCHEMA_V1,
"evidenceId": "coord_fallback_why_02",
"capturedAt": "2026-05-16T21:07:00Z",
"status": "unavailable",
"source": {"kind": "beads", "sourceId": "beads-local"},
"reasonCode": "beads_snapshot_stale",
"summary": {"text": "unmatched", "contentHash": "blake3:summary2", "redacted": true},
"links": {"beadIds": ["bd-unrelated"], "verificationIds": [], "supportBundleIds": []},
"fallbackAction": {"kind": "record_only", "summary": "Preserve.", "command": null, "manualStep": null},
"redaction": {
"rawInboxIncluded": false,
"rawLogIncluded": false,
"secretScanApplied": true,
"pathPolicy": "labels_only"
}
}
});
std::fs::write(
ledger_dir.join(COORDINATION_FALLBACK_LEDGER_FILE),
format!("{matched}\n{unmatched}\n"),
)
.map_err(|error| error.to_string())?;
let report = explain_memory(&WhyOptions {
database_path: &database_path,
memory_id,
confidence_threshold: WhyOptions::DEFAULT_CONFIDENCE_THRESHOLD,
});
ensure(report.found, true, "why found memory")?;
ensure(
report.coordination_fallback_evidence.len(),
1_usize,
"coordination fallback evidence count",
)?;
let fallback = &report.coordination_fallback_evidence[0];
ensure(
fallback.evidence_id.as_str(),
"coord_fallback_why_01",
"linked evidence id",
)?;
ensure(
fallback.linked_bead_ids.clone(),
vec!["bd-1zb7k.13.2".to_string()],
"linked bead ids",
)?;
ensure(
fallback.content_hash.as_str(),
"blake3:matched",
"content hash",
)
}
#[test]
fn explain_memory_redacts_stored_memory_provenance_uri() -> TestResult {
let temp = tempfile::tempdir().map_err(|error| error.to_string())?;
let database_path = temp.path().join(".ee").join("ee.db");
std::fs::create_dir_all(
database_path
.parent()
.ok_or("database path should have parent")?,
)
.map_err(|error| error.to_string())?;
let conn = DbConnection::open_file(&database_path).map_err(|error| error.to_string())?;
conn.migrate().map_err(|error| error.to_string())?;
conn.insert_workspace(
"wsp_00000000000000000000001005",
&CreateWorkspaceInput {
path: temp.path().display().to_string(),
name: Some("why-redaction".to_string()),
},
)
.map_err(|error| error.to_string())?;
conn.insert_memory(
"mem_00000000000000000000001005",
&crate::db::CreateMemoryInput {
workspace_id: "wsp_00000000000000000000001005".to_string(),
level: "procedural".to_string(),
kind: "rule".to_string(),
content: "Do not leak stored provenance in why output.".to_string(),
workflow_id: None,
confidence: 0.8,
utility: 0.7,
importance: 0.6,
provenance_uri: Some(
concat!(
"file:///Users/alice/private/repo/notes.md?",
"api",
"_key=redaction-fixture"
)
.to_string(),
),
trust_class: "human_explicit".to_string(),
trust_subclass: None,
tags: Vec::new(),
valid_from: None,
valid_to: None,
},
)
.map_err(|error| error.to_string())?;
let report = explain_memory(&WhyOptions {
database_path: &database_path,
memory_id: "mem_00000000000000000000001005",
confidence_threshold: WhyOptions::DEFAULT_CONFIDENCE_THRESHOLD,
});
let provenance = report
.storage
.as_ref()
.and_then(|storage| storage.provenance_uri.as_deref())
.ok_or_else(|| "stored memory provenance present".to_string())?;
ensure(
provenance.contains("/Users/alice"),
false,
"stored provenance path redacted",
)?;
ensure(
provenance.contains("[REDACTED_PATH]"),
true,
"stored provenance path placeholder",
)?;
ensure(
provenance.contains("redaction-fixture"),
false,
"stored provenance secret value redacted",
)?;
ensure(
provenance.contains("[REDACTED:"),
true,
"stored provenance secret placeholder",
)
}
#[test]
fn explain_memory_redacts_history_details_without_mutating_audit_row() -> TestResult {
let temp = tempfile::tempdir().map_err(|error| error.to_string())?;
let database_path = temp.path().join(".ee").join("ee.db");
std::fs::create_dir_all(
database_path
.parent()
.ok_or("database path should have parent")?,
)
.map_err(|error| error.to_string())?;
let conn = DbConnection::open_file(&database_path).map_err(|error| error.to_string())?;
conn.migrate().map_err(|error| error.to_string())?;
let workspace_id = "wsp_00000000000000000000001006";
let memory_id = "mem_00000000000000000000001006";
conn.insert_workspace(
workspace_id,
&CreateWorkspaceInput {
path: temp.path().display().to_string(),
name: Some("why-history-redaction".to_string()),
},
)
.map_err(|error| error.to_string())?;
conn.insert_memory(
memory_id,
&crate::db::CreateMemoryInput {
workspace_id: workspace_id.to_string(),
level: "procedural".to_string(),
kind: "rule".to_string(),
content: "History details are redacted at the why surface.".to_string(),
workflow_id: None,
confidence: 0.8,
utility: 0.7,
importance: 0.6,
provenance_uri: None,
trust_class: "human_explicit".to_string(),
trust_subclass: None,
tags: Vec::new(),
valid_from: None,
valid_to: None,
},
)
.map_err(|error| error.to_string())?;
let raw_details = serde_json::json!({
"schema": "ee.audit.memory_create.v1",
"provenanceUri": concat!(
"file:///Users/alice/private/repo/notes.md?",
"api",
"_key=redaction-fixture"
),
"nested": {
"reason": "reviewed /Volumes/USBNVME16TB/private/session.jsonl"
},
"safe": "cass://session/public#L1"
})
.to_string();
conn.insert_audit(
&crate::testing::audit("whyhist"),
&crate::db::CreateAuditInput {
workspace_id: Some(workspace_id.to_string()),
actor: Some("test-agent".to_string()),
action: crate::db::audit_actions::MEMORY_CREATE.to_string(),
target_type: Some("memory".to_string()),
target_id: Some(memory_id.to_string()),
details: Some(raw_details.clone()),
},
)
.map_err(|error| error.to_string())?;
let report = explain_memory(&WhyOptions {
database_path: &database_path,
memory_id,
confidence_threshold: WhyOptions::DEFAULT_CONFIDENCE_THRESHOLD,
});
let details = report
.history
.as_ref()
.and_then(|history| history.entries.first())
.and_then(|entry| entry.details.as_deref())
.ok_or_else(|| "why history details present".to_string())?;
ensure(
details.contains("/Users/alice") || details.contains("/Volumes/USBNVME16TB"),
false,
"why history details path redacted",
)?;
ensure(
details.contains("redaction-fixture"),
false,
"why history details secret value redacted",
)?;
ensure(
details.contains("[REDACTED_PATH]"),
true,
"why history details path placeholder",
)?;
ensure(
details.contains("[REDACTED:"),
true,
"why history details secret placeholder",
)?;
ensure(
details.contains("cass://session/public#L1"),
true,
"safe cass source remains visible",
)?;
let raw_audit_details = conn
.list_audit_by_target("memory", memory_id, None)
.map_err(|error| error.to_string())?
.into_iter()
.find(|entry| entry.action == crate::db::audit_actions::MEMORY_CREATE)
.and_then(|entry| entry.details)
.ok_or_else(|| "raw memory-create audit details present".to_string())?;
ensure(
raw_audit_details,
raw_details,
"raw audit details preserved",
)
}
#[test]
fn explain_memory_seeded_remains_read_only() -> TestResult {
fn run_seeded(seed: u64) -> Result<(), String> {
let temp = tempfile::tempdir().map_err(|error| error.to_string())?;
let database_path = temp.path().join(".ee").join("ee.db");
std::fs::create_dir_all(
database_path
.parent()
.ok_or("database path should have parent")?,
)
.map_err(|error| error.to_string())?;
let workspace_id = "wsp_00000000000000000000000001";
let memory_id = "mem_00000000000000000000000001";
let connection = crate::db::DbConnection::open_file(&database_path)
.map_err(|error| error.to_string())?;
connection.migrate().map_err(|error| error.to_string())?;
connection
.insert_workspace(
workspace_id,
&crate::db::CreateWorkspaceInput {
path: temp.path().display().to_string(),
name: Some("why-seeded".to_owned()),
},
)
.map_err(|error| error.to_string())?;
connection
.insert_memory(
memory_id,
&crate::db::CreateMemoryInput {
workspace_id: workspace_id.to_owned(),
level: "procedural".to_owned(),
kind: "rule".to_owned(),
content: "Use seeded why audit IDs in replay tests.".to_owned(),
workflow_id: None,
confidence: 0.8,
utility: 0.7,
importance: 0.6,
provenance_uri: None,
trust_class: "agent_assertion".to_owned(),
trust_subclass: None,
tags: Vec::new(),
valid_from: None,
valid_to: None,
},
)
.map_err(|error| error.to_string())?;
let mut determinism = Deterministic::from_seed(seed);
let report = explain_memory_seeded(
&WhyOptions {
database_path: &database_path,
memory_id,
confidence_threshold: WhyOptions::DEFAULT_CONFIDENCE_THRESHOLD,
},
&mut determinism,
);
ensure(report.found, true, "why found memory")?;
let audits = connection
.list_audit_by_target("memory", memory_id, None)
.map_err(|error| error.to_string())?;
let why_audit_count = audits
.iter()
.filter(|entry| entry.action == crate::db::audit_actions::WHY_INSPECTED)
.count();
ensure(why_audit_count, 0_usize, "why audit row count")?;
Ok(())
}
run_seeded(45_001)?;
run_seeded(45_001)?;
run_seeded(45_002)
}
#[test]
fn why_projection_stays_consistent_across_concurrent_writer_commit() -> TestResult {
let temp = tempfile::tempdir().map_err(|error| error.to_string())?;
let database_path = temp.path().join(".ee").join("ee.db");
std::fs::create_dir_all(
database_path
.parent()
.ok_or("database path should have parent")?,
)
.map_err(|error| error.to_string())?;
let workspace_id = "wsp_00000000000000000000000031";
let memory_id = "mem_00000000000000000000000031";
let writer =
crate::db::DbConnection::open_file(&database_path).map_err(|e| e.to_string())?;
writer.migrate().map_err(|e| e.to_string())?;
writer
.insert_workspace(
workspace_id,
&CreateWorkspaceInput {
path: temp.path().display().to_string(),
name: Some("why-snapshot".to_owned()),
},
)
.map_err(|error| error.to_string())?;
writer
.insert_memory(
memory_id,
&CreateMemoryInput {
workspace_id: workspace_id.to_owned(),
level: "procedural".to_owned(),
kind: "rule".to_owned(),
content: "snapshot content before writer commit".to_owned(),
workflow_id: None,
confidence: 0.8,
utility: 0.7,
importance: 0.6,
provenance_uri: None,
trust_class: "human_explicit".to_owned(),
trust_subclass: None,
tags: Vec::new(),
valid_from: None,
valid_to: None,
},
)
.map_err(|error| error.to_string())?;
let read_pool = registered_process_read_pool(
DatabaseConfig::file(database_path.clone()),
PoolConfig::default_single(),
);
let snapshot = read_pool
.pin_snapshot()
.map_err(|error| error.to_string())?;
let connection = snapshot
.checked_connection()
.map_err(|error| error.to_string())?;
let options = WhyOptions {
database_path: &database_path,
memory_id,
confidence_threshold: WhyOptions::DEFAULT_CONFIDENCE_THRESHOLD,
};
let before = explain_memory_with_connection(&options, connection);
ensure(
before.content,
Some("snapshot content before writer commit".to_owned()),
"why snapshot initial content",
)?;
writer
.execute_raw(
"UPDATE memories SET content = 'content committed by concurrent writer' \
WHERE id = 'mem_00000000000000000000000031'",
)
.map_err(|error| error.to_string())?;
let during = explain_memory_with_connection(&options, connection);
ensure(
during.content,
Some("snapshot content before writer commit".to_owned()),
"why snapshot excludes concurrent commit",
)?;
snapshot.commit().map_err(|error| error.to_string())?;
let after = explain_memory(&options);
ensure(
after.content,
Some("content committed by concurrent writer".to_owned()),
"new why snapshot observes concurrent commit",
)
}
#[test]
fn explain_memory_attaches_revision_lineage_for_revised_memory() -> TestResult {
let temp = tempfile::tempdir().map_err(|error| error.to_string())?;
let database_path = temp.path().join(".ee").join("ee.db");
std::fs::create_dir_all(
database_path
.parent()
.ok_or("database path should have parent")?,
)
.map_err(|error| error.to_string())?;
std::fs::write(
temp.path().join(".ee").join("config.toml"),
"[graph.feature.revision_dominance]\nenabled = true\n",
)
.map_err(|error| error.to_string())?;
let original_id = "mem_00000000000000000000001007";
let workspace_id = "wsp_00000000000000000000001007";
let connection =
crate::db::DbConnection::open_file(&database_path).map_err(|e| e.to_string())?;
connection.migrate().map_err(|e| e.to_string())?;
connection
.insert_workspace(
workspace_id,
&crate::db::CreateWorkspaceInput {
path: temp.path().display().to_string(),
name: Some("why-revision-lineage".to_owned()),
},
)
.map_err(|e| e.to_string())?;
connection
.insert_memory(
original_id,
&crate::db::CreateMemoryInput {
workspace_id: workspace_id.to_owned(),
level: "semantic".to_owned(),
kind: "fact".to_owned(),
content: "Revision lineage starts here.".to_owned(),
workflow_id: None,
confidence: 0.8,
utility: 0.7,
importance: 0.6,
provenance_uri: None,
trust_class: "agent_assertion".to_owned(),
trust_subclass: None,
tags: Vec::new(),
valid_from: Some("2026-05-15T00:00:00Z".to_owned()),
valid_to: None,
},
)
.map_err(|e| e.to_string())?;
drop(connection);
let revised =
crate::core::memory::revise_memory(&crate::core::memory::ReviseMemoryOptions {
database_path: &database_path,
original_memory_id: original_id,
content: Some("Revision lineage continues here."),
level: None,
kind: None,
confidence: None,
tags: None,
provenance_uri: None,
reason: crate::core::memory::ReviseReason::Update,
actor: Some("StormyCove"),
dry_run: false,
});
let revised_id = revised
.new_id
.clone()
.ok_or_else(|| "revision should create a new memory id".to_string())?;
let report = explain_memory(&WhyOptions {
database_path: &database_path,
memory_id: &revised_id,
confidence_threshold: WhyOptions::DEFAULT_CONFIDENCE_THRESHOLD,
});
ensure(report.found, true, "why found revised memory")?;
let lineage = report
.revision_lineage
.as_ref()
.ok_or_else(|| "why should attach revision lineage".to_string())?;
ensure(
lineage["memoryId"].as_str(),
Some(revised_id.as_str()),
"lineage memory id",
)?;
ensure(
lineage["immediateDominator"].as_str(),
Some(original_id),
"lineage immediate dominator",
)?;
ensure(
lineage["ancestorsAtDepth"]["0"][0].as_str(),
Some(revised_id.as_str()),
"lineage depth 0",
)?;
ensure(
lineage["ancestorsAtDepth"]["1"][0].as_str(),
Some(original_id),
"lineage depth 1",
)
}
#[test]
fn explain_memory_reports_disabled_revision_lineage_by_default() -> TestResult {
let temp = tempfile::tempdir().map_err(|error| error.to_string())?;
let database_path = temp.path().join(".ee").join("ee.db");
std::fs::create_dir_all(
database_path
.parent()
.ok_or("database path should have parent")?,
)
.map_err(|error| error.to_string())?;
let memory_id = "mem_00000000000000000000001008";
let workspace_id = "wsp_00000000000000000000001008";
let connection =
crate::db::DbConnection::open_file(&database_path).map_err(|e| e.to_string())?;
connection.migrate().map_err(|e| e.to_string())?;
connection
.insert_workspace(
workspace_id,
&crate::db::CreateWorkspaceInput {
path: temp.path().display().to_string(),
name: Some("why-disabled-revision-lineage".to_owned()),
},
)
.map_err(|e| e.to_string())?;
connection
.insert_memory(
memory_id,
&crate::db::CreateMemoryInput {
workspace_id: workspace_id.to_owned(),
level: "semantic".to_owned(),
kind: "fact".to_owned(),
content: "Revision dominance should be feature-gated.".to_owned(),
workflow_id: None,
confidence: 0.8,
utility: 0.7,
importance: 0.6,
provenance_uri: None,
trust_class: "agent_assertion".to_owned(),
trust_subclass: None,
tags: Vec::new(),
valid_from: Some("2026-05-15T00:00:00Z".to_owned()),
valid_to: None,
},
)
.map_err(|e| e.to_string())?;
drop(connection);
let report = explain_memory(&WhyOptions {
database_path: &database_path,
memory_id,
confidence_threshold: WhyOptions::DEFAULT_CONFIDENCE_THRESHOLD,
});
ensure(report.found, true, "why found memory")?;
let lineage = report
.revision_lineage
.as_ref()
.ok_or_else(|| "disabled gate should attach a lineage sentinel".to_string())?;
ensure(
lineage["validationStatus"].as_str(),
Some("disabled"),
"disabled validation status",
)?;
ensure(
lineage["degraded"][0]["code"].as_str(),
Some("graph_feature_disabled"),
"disabled degraded code",
)?;
ensure(
lineage["degraded"][0]["repair"].as_str(),
Some("ee config set graph.feature.revision_dominance.enabled true"),
"disabled repair",
)?;
ensure(
lineage["degraded"][0]["sources"][0].as_str(),
Some("why_revision_lineage"),
"disabled degraded source",
)
}
#[test]
fn explain_memory_attaches_load_bearing_rule_provenance() -> TestResult {
let temp = tempfile::tempdir().map_err(|error| error.to_string())?;
let database_path = temp.path().join(".ee").join("ee.db");
std::fs::create_dir_all(
database_path
.parent()
.ok_or("database path should have parent")?,
)
.map_err(|error| error.to_string())?;
let workspace_id = "wsp_00000000000000000000001009";
let memory_id = "mem_00000000000000000000001009";
let connection =
crate::db::DbConnection::open_file(&database_path).map_err(|e| e.to_string())?;
connection.migrate().map_err(|e| e.to_string())?;
connection
.insert_workspace(
workspace_id,
&CreateWorkspaceInput {
path: temp.path().display().to_string(),
name: Some("why load-bearing".to_owned()),
},
)
.map_err(|e| e.to_string())?;
connection
.insert_memory(
memory_id,
&CreateMemoryInput {
workspace_id: workspace_id.to_owned(),
level: "semantic".to_owned(),
kind: "fact".to_owned(),
content: "Load-bearing memory should be protected.".to_owned(),
workflow_id: None,
confidence: 0.9,
utility: 0.8,
importance: 0.7,
provenance_uri: None,
trust_class: "human_explicit".to_owned(),
trust_subclass: None,
tags: Vec::new(),
valid_from: Some("2026-05-20T00:00:00Z".to_owned()),
valid_to: None,
},
)
.map_err(|e| e.to_string())?;
for rule_id in [
crate::testing::rule("whyloadbearingalpha"),
crate::testing::rule("whyloadbearingbeta"),
] {
connection
.insert_procedural_rule(
&rule_id,
&CreateProceduralRuleInput {
workspace_id: workspace_id.to_owned(),
content: format!("{rule_id} cites load-bearing evidence."),
confidence: 0.9,
utility: 0.8,
importance: 0.7,
trust_class: "human_explicit".to_owned(),
scope: "workspace".to_owned(),
scope_pattern: None,
maturity: "validated".to_owned(),
protected: false,
source_memory_ids: vec![memory_id.to_owned()],
tags: Vec::new(),
},
)
.map_err(|e| e.to_string())?;
}
drop(connection);
let report = explain_memory(&WhyOptions {
database_path: &database_path,
memory_id,
confidence_threshold: WhyOptions::DEFAULT_CONFIDENCE_THRESHOLD,
});
let load_bearing = report
.load_bearing
.as_ref()
.ok_or_else(|| "why should attach load-bearing explanation".to_owned())?;
ensure(load_bearing.is_load_bearing, true, "load-bearing flag")?;
ensure(
load_bearing.authority_rank,
Some(1_usize),
"load-bearing rank",
)?;
ensure(
load_bearing.citing_rule_count,
2_usize,
"load-bearing citing rule count",
)?;
ensure(
load_bearing.interpretation,
"load_bearing",
"load-bearing interpretation",
)?;
ensure(
load_bearing.evidence.projection,
"rule_provenance_bipartite",
"load-bearing projection",
)
}
#[test]
fn why_revision_lineage_aggregates_dominance_degradations() -> TestResult {
let degraded = aggregate_why_dominance_degraded(&[
crate::graph::dominance::DominanceDegradation {
code: "graph_dominance_no_revision_chain".to_owned(),
severity: "info".to_owned(),
message: "low-detail graph dominance message".to_owned(),
repair: None,
},
crate::graph::dominance::DominanceDegradation {
code: "graph_dominance_no_revision_chain".to_owned(),
severity: "warning".to_owned(),
message: "higher-severity graph dominance message".to_owned(),
repair: Some("ee graph snapshot refresh --workspace .".to_owned()),
},
]);
ensure(degraded.len(), 1_usize, "aggregate duplicate code count")?;
ensure(
degraded[0]["code"].as_str(),
Some("graph_dominance_no_revision_chain"),
"aggregate code",
)?;
ensure(
degraded[0]["severity"].as_str(),
Some("warning"),
"aggregate severity",
)?;
ensure(
degraded[0]["message"].as_str(),
Some("higher-severity graph dominance message"),
"aggregate message",
)?;
ensure(
degraded[0]["repair"].as_str(),
Some("ee graph snapshot refresh --workspace ."),
"aggregate repair",
)?;
ensure(
degraded[0]["sources"][0].as_str(),
Some("graph_dominance"),
"aggregate source",
)
}
#[test]
fn memory_link_summary_direction_outgoing() -> TestResult {
let link = MemoryLinkSummary {
link_id: "link_test".to_string(),
linked_memory_id: "mem_other".to_string(),
relation: "supports".to_string(),
direction: "outgoing".to_string(),
confidence: 0.9,
weight: 1.0,
evidence_count: 3,
source: "agent".to_string(),
created_at: "2026-04-30T12:00:00Z".to_string(),
};
ensure(link.direction, "outgoing".to_string(), "direction")?;
ensure(link.relation, "supports".to_string(), "relation")
}
#[test]
fn memory_link_summary_direction_incoming() -> TestResult {
let link = MemoryLinkSummary {
link_id: "link_test".to_string(),
linked_memory_id: "mem_source".to_string(),
relation: "contradicts".to_string(),
direction: "incoming".to_string(),
confidence: 0.85,
weight: 0.8,
evidence_count: 1,
source: "human".to_string(),
created_at: "2026-04-30T12:00:00Z".to_string(),
};
ensure(link.direction, "incoming".to_string(), "direction")?;
ensure(link.relation, "contradicts".to_string(), "relation")
}
#[test]
fn memory_link_summary_undirected() -> TestResult {
let link = MemoryLinkSummary {
link_id: "link_test".to_string(),
linked_memory_id: "mem_related".to_string(),
relation: "related".to_string(),
direction: "undirected".to_string(),
confidence: 0.7,
weight: 0.5,
evidence_count: 2,
source: "auto".to_string(),
created_at: "2026-04-30T12:00:00Z".to_string(),
};
ensure(link.direction, "undirected".to_string(), "direction")?;
ensure(link.relation, "related".to_string(), "relation")
}
#[test]
fn why_report_with_links() -> TestResult {
let links = vec![
MemoryLinkSummary {
link_id: "link_01".to_string(),
linked_memory_id: "mem_support".to_string(),
relation: "supports".to_string(),
direction: "outgoing".to_string(),
confidence: 0.9,
weight: 1.0,
evidence_count: 2,
source: "agent".to_string(),
created_at: "2026-04-30T12:00:00Z".to_string(),
},
MemoryLinkSummary {
link_id: "link_02".to_string(),
linked_memory_id: "mem_contradict".to_string(),
relation: "contradicts".to_string(),
direction: "incoming".to_string(),
confidence: 0.8,
weight: 0.5,
evidence_count: 1,
source: "human".to_string(),
created_at: "2026-04-30T12:01:00Z".to_string(),
},
];
let report = WhyReport::not_found("mem_test".to_string()).with_links(links);
ensure(report.links.len(), 2, "link count")?;
ensure(
report.links[0].relation.clone(),
"supports".to_string(),
"first link relation",
)?;
ensure(
report.links[1].relation.clone(),
"contradicts".to_string(),
"second link relation",
)
}
#[test]
fn why_report_links_default_empty() -> TestResult {
let report = WhyReport::not_found("mem_test".to_string());
ensure(
report.links.is_empty(),
true,
"links should be empty by default",
)
}
fn insert_why_artifact(
conn: &DbConnection,
artifact_id: &str,
provenance_uri: Option<&str>,
original_path: Option<&str>,
external_ref: Option<&str>,
) -> TestResult {
let source_kind = if external_ref.is_some() {
"external"
} else {
"file"
};
conn.upsert_artifact(
artifact_id,
&CreateArtifactInput {
workspace_id: "wsp_00000000000000000000000001".to_owned(),
source_kind: source_kind.to_owned(),
artifact_type: "log".to_owned(),
original_path: original_path.map(ToOwned::to_owned),
canonical_path: original_path.map(ToOwned::to_owned),
external_ref: external_ref.map(ToOwned::to_owned),
content_hash:
"blake3:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
.to_owned(),
media_type: "text/plain".to_owned(),
size_bytes: 42,
redaction_status: "checked".to_owned(),
snippet: None,
snippet_hash: None,
provenance_uri: provenance_uri.map(ToOwned::to_owned),
metadata_json: Some(r#"{"title":"why artifact"}"#.to_owned()),
},
)
.map_err(|error| error.to_string())
}
fn insert_why_session(
conn: &DbConnection,
session_id: &str,
cass_session_id: &str,
source_path: Option<&str>,
) -> TestResult {
conn.insert_session(
session_id,
&CreateSessionInput {
workspace_id: "wsp_00000000000000000000000001".to_owned(),
cass_session_id: cass_session_id.to_owned(),
source_path: source_path.map(ToOwned::to_owned),
agent_name: Some("test-agent".to_owned()),
model: Some("test-model".to_owned()),
started_at: Some("2026-05-17T00:00:00Z".to_owned()),
ended_at: Some("2026-05-17T00:01:00Z".to_owned()),
message_count: 2,
token_count: Some(100),
content_hash:
"blake3:1111111111111111111111111111111111111111111111111111111111111111"
.to_owned(),
metadata_json: Some(r#"{"fixture":"why-session"}"#.to_owned()),
},
)
.map_err(|error| error.to_string())
}
#[test]
fn why_session_result_storage_uses_opaque_canonical_session_provenance() -> TestResult {
let conn = DbConnection::open_memory().map_err(|error| error.to_string())?;
conn.migrate().map_err(|error| error.to_string())?;
conn.insert_workspace(
"wsp_00000000000000000000000001",
&CreateWorkspaceInput {
path: "/tmp/why-session-redaction".to_owned(),
name: Some("why-session-redaction".to_owned()),
},
)
.map_err(|error| error.to_string())?;
insert_why_session(
&conn,
"sess_00000000000000000000000001",
"cass-session-a",
Some(concat!(
"file:///Volumes/USBNVME16TB/private/cass/session.jsonl?",
"api",
"_key=redaction-fixture"
)),
)?;
let report = WhyReport::unsupported_result_target(
"sess_00000000000000000000000001".to_owned(),
WhyResultDocumentSource::Session,
&conn,
);
let provenance = report
.storage
.as_ref()
.and_then(|storage| storage.provenance_uri.as_deref())
.ok_or_else(|| "session provenance present".to_owned())?;
ensure(
provenance.to_owned(),
"cass-session://sess_00000000000000000000000001".to_owned(),
"canonical opaque session provenance",
)?;
ensure(
provenance.contains("/Volumes/USBNVME16TB")
|| provenance.contains("cass-session-a")
|| provenance.contains("redaction-fixture")
|| provenance.contains("[REDACTED"),
false,
"canonical provenance omits source path, upstream id, secret, and placeholders",
)
}
#[test]
fn why_session_result_storage_does_not_expose_safe_upstream_cass_id() -> TestResult {
let conn = DbConnection::open_memory().map_err(|error| error.to_string())?;
conn.migrate().map_err(|error| error.to_string())?;
conn.insert_workspace(
"wsp_00000000000000000000000001",
&CreateWorkspaceInput {
path: "/tmp/why-session-fallback".to_owned(),
name: Some("why-session-fallback".to_owned()),
},
)
.map_err(|error| error.to_string())?;
insert_why_session(
&conn,
"sess_00000000000000000000000002",
"cass-session-b",
None,
)?;
let report = WhyReport::unsupported_result_target(
"sess_00000000000000000000000002".to_owned(),
WhyResultDocumentSource::Session,
&conn,
);
let provenance = report
.storage
.as_ref()
.and_then(|storage| storage.provenance_uri.as_deref())
.ok_or_else(|| "session fallback provenance present".to_owned())?;
ensure(
provenance.to_owned(),
"cass-session://sess_00000000000000000000000002".to_owned(),
"canonical session provenance uses only the stable internal id",
)
}
#[test]
fn why_artifact_result_storage_redacts_path_and_secret_provenance() -> TestResult {
let conn = DbConnection::open_memory().map_err(|error| error.to_string())?;
conn.migrate().map_err(|error| error.to_string())?;
conn.insert_workspace(
"wsp_00000000000000000000000001",
&CreateWorkspaceInput {
path: "/tmp/why-artifact-redaction".to_owned(),
name: Some("why-artifact-redaction".to_owned()),
},
)
.map_err(|error| error.to_string())?;
insert_why_artifact(
&conn,
"art_00000000000000000000000001",
None,
Some("/Users/alice/private/repo/build.log"),
None,
)?;
insert_why_artifact(
&conn,
"art_00000000000000000000000002",
None,
None,
Some("https://example.invalid/logs?api_key=redaction-fixture"),
)?;
let path_report = WhyReport::unsupported_result_target(
"art_00000000000000000000000001".to_owned(),
WhyResultDocumentSource::Artifact,
&conn,
);
let path_provenance = path_report
.storage
.as_ref()
.and_then(|storage| storage.provenance_uri.as_deref())
.ok_or_else(|| "path artifact provenance present".to_owned())?;
ensure(
path_provenance.contains("/Users/alice"),
false,
"raw path redacted",
)?;
ensure(
path_provenance.contains("[REDACTED_PATH]"),
true,
"path placeholder present",
)?;
let secret_report = WhyReport::unsupported_result_target(
"art_00000000000000000000000002".to_owned(),
WhyResultDocumentSource::Artifact,
&conn,
);
let secret_provenance = secret_report
.storage
.as_ref()
.and_then(|storage| storage.provenance_uri.as_deref())
.ok_or_else(|| "secret artifact provenance present".to_owned())?;
ensure(
secret_provenance.contains("api_key"),
true,
"non-secret key name remains as diagnostic context",
)?;
ensure(
secret_provenance.contains("redaction-fixture"),
false,
"secret value redacted",
)?;
ensure(
secret_provenance.contains("[REDACTED:"),
true,
"secret placeholder present",
)
}
fn insert_why_link_memory(
conn: &DbConnection,
workspace_id: &str,
memory_id: &str,
content: &str,
) -> TestResult {
conn.insert_memory(
memory_id,
&crate::db::CreateMemoryInput {
workspace_id: workspace_id.to_owned(),
level: "semantic".to_owned(),
kind: "fact".to_owned(),
content: content.to_owned(),
workflow_id: None,
confidence: 0.8,
utility: 0.7,
importance: 0.6,
provenance_uri: None,
trust_class: "agent_assertion".to_owned(),
trust_subclass: None,
tags: Vec::new(),
valid_from: None,
valid_to: None,
},
)
.map_err(|error| error.to_string())
}
fn denied_mesh_link_metadata() -> String {
serde_json::json!({
"mesh": {
"workspaceScopeDecision": "deny",
"materialLane": "graphSignal",
"cachedMaterialId": "mesh_why_denied",
"originWorkspaceId": "wsp_remote_private",
"originWorkspaceLabel": "/Users/alice/private/repo",
"producerPeerId": "peer_builder_one",
"producerPeerLabel": "/Users/alice/private/peer-agent",
"importDecisionId": "mesh_why_decision_denied",
"trustLane": "quarantined",
"redactionPosture": "metadata_only"
}
})
.to_string()
}
#[test]
fn why_fetch_links_ignores_denied_mesh_links() -> TestResult {
let conn = DbConnection::open_memory().map_err(|error| error.to_string())?;
conn.migrate().map_err(|error| error.to_string())?;
let workspace_id = "wsp_00000000000000000000000001";
let source_memory_id = "mem_00000000000000000000000001";
let allowed_memory_id = "mem_00000000000000000000000002";
let denied_memory_id = "mem_00000000000000000000000003";
conn.insert_workspace(
workspace_id,
&crate::db::CreateWorkspaceInput {
path: "/tmp/why-mesh-filter".to_owned(),
name: Some("why-mesh-filter".to_owned()),
},
)
.map_err(|error| error.to_string())?;
insert_why_link_memory(&conn, workspace_id, source_memory_id, "source why memory")?;
insert_why_link_memory(&conn, workspace_id, allowed_memory_id, "allowed neighbor")?;
insert_why_link_memory(
&conn,
workspace_id,
denied_memory_id,
"denied mesh neighbor",
)?;
conn.insert_memory_link(
"link_00000000000000000000000001",
&crate::db::CreateMemoryLinkInput {
src_memory_id: source_memory_id.to_owned(),
dst_memory_id: allowed_memory_id.to_owned(),
relation: crate::db::MemoryLinkRelation::Supports,
weight: 1.0,
confidence: 0.9,
directed: true,
evidence_count: 2,
last_reinforced_at: None,
source: crate::db::MemoryLinkSource::Agent,
created_by: Some("why-mesh-filter-test".to_owned()),
metadata_json: None,
},
)
.map_err(|error| error.to_string())?;
conn.insert_memory_link(
"link_00000000000000000000000002",
&crate::db::CreateMemoryLinkInput {
src_memory_id: source_memory_id.to_owned(),
dst_memory_id: denied_memory_id.to_owned(),
relation: crate::db::MemoryLinkRelation::Supports,
weight: 1.0,
confidence: 0.9,
directed: true,
evidence_count: 2,
last_reinforced_at: None,
source: crate::db::MemoryLinkSource::Import,
created_by: Some("why-mesh-filter-test".to_owned()),
metadata_json: Some(denied_mesh_link_metadata()),
},
)
.map_err(|error| error.to_string())?;
let links = fetch_links(&conn, source_memory_id);
ensure(
links.degradation.is_none(),
true,
"visible filtering is not a degradation",
)?;
ensure(links.items.len(), 1_usize, "visible link count")?;
ensure(
links.items[0].linked_memory_id.as_str(),
allowed_memory_id,
"allowed link remains",
)?;
ensure(
links
.items
.iter()
.any(|link| link.linked_memory_id == denied_memory_id),
false,
"denied mesh link is absent",
)
}
#[test]
fn why_evidence_fetchers_report_query_failures_as_degradations() -> TestResult {
let conn = DbConnection::open_memory().map_err(|error| error.to_string())?;
let contradictions = fetch_contradictions(&conn, "mem_missing_schema");
ensure(
contradictions.items.is_empty(),
true,
"failed contradiction query has no items",
)?;
let contradiction_degradation = contradictions
.degradation
.ok_or_else(|| "missing contradiction query degradation".to_string())?;
ensure(
contradiction_degradation.code,
"why_contradictions_unavailable",
"contradiction degradation code",
)?;
let links = fetch_links(&conn, "mem_missing_schema");
ensure(
links.items.is_empty(),
true,
"failed link query has no items",
)?;
let link_degradation = links
.degradation
.ok_or_else(|| "missing link query degradation".to_string())?;
ensure(
link_degradation.code,
"why_links_unavailable",
"link degradation code",
)?;
let rationale = fetch_rationale_traces(&conn, "wsp_missing_schema", "mem_missing_schema");
ensure(
rationale.items.is_empty(),
true,
"failed rationale trace query has no items",
)?;
let rationale_degradation = rationale
.degradation
.ok_or_else(|| "missing rationale trace query degradation".to_string())?;
ensure(
rationale_degradation.code,
"why_rationale_traces_unavailable",
"rationale trace degradation code",
)?;
ensure(
contradiction_degradation.severity,
"medium",
"contradiction severity",
)?;
ensure(link_degradation.severity, "medium", "link severity")?;
ensure(
rationale_degradation.severity,
"medium",
"rationale severity",
)
}
#[test]
fn why_evidence_fetchers_keep_true_empty_evidence_undegraded() -> TestResult {
let conn = DbConnection::open_memory().map_err(|error| error.to_string())?;
conn.migrate().map_err(|error| error.to_string())?;
let contradictions = fetch_contradictions(&conn, "mem_no_evidence");
ensure(
contradictions.items.is_empty(),
true,
"empty contradiction items",
)?;
ensure(
contradictions.degradation.is_none(),
true,
"empty contradiction evidence is not degraded",
)?;
let links = fetch_links(&conn, "mem_no_evidence");
ensure(links.items.is_empty(), true, "empty link items")?;
ensure(
links.degradation.is_none(),
true,
"empty links are not degraded",
)?;
let rationale = fetch_rationale_traces(&conn, "wsp_no_evidence", "mem_no_evidence");
ensure(rationale.items.is_empty(), true, "empty rationale items")?;
ensure(
rationale.degradation.is_none(),
true,
"empty rationale traces are not degraded",
)
}
#[test]
fn rationale_trace_summary_uses_only_storable_visible_trace() -> TestResult {
let trace = RationaleTrace::new(
"rat_supported_release",
RationaleTraceKind::Decision,
"agent:test",
"Release checklist evidence supports keeping formatting verification in the pack.",
"2026-05-03T18:50:00Z",
)
.map_err(|error| error.to_string())?
.with_posture(RationaleTracePosture::Supported)
.with_visibility(RationaleTraceVisibility::Redacted, RedactionStatus::Partial)
.with_evidence_uri("cass://session#L10-L14")
.with_memory_id("mem_release_rule")
.with_context_pack_id("pack_release")
.with_recorder_run_id("run_release")
.with_recorder_event_id("event_release")
.with_causal_trace_id("causal_release");
let summary = RationaleTraceSummary::from_trace(&trace)
.ok_or_else(|| "storable rationale trace was filtered".to_string())?;
ensure(
summary.trace_id,
"rat_supported_release".to_string(),
"trace id",
)?;
ensure(summary.kind, "decision".to_string(), "kind")?;
ensure(summary.posture, "supported".to_string(), "posture")?;
ensure(summary.visibility, "redacted".to_string(), "visibility")?;
ensure(
summary.linked_memory_ids,
vec!["mem_release_rule".to_string()],
"linked memory ids",
)?;
ensure(
summary.linked_causal_trace_ids,
vec!["causal_release".to_string()],
"linked causal trace ids",
)?;
let mut unknown = trace;
unknown.schema = "ee.rationale_trace.v999".to_owned();
ensure(
RationaleTraceSummary::from_trace(&unknown).is_none(),
true,
"unknown rationale schema is not relabeled as supported",
)
}
#[test]
fn rationale_trace_summary_rejects_private_visibility() -> TestResult {
let trace = RationaleTrace::new(
"rat_private_rejected",
RationaleTraceKind::Hypothesis,
"agent:test",
"Visible rejection marker for unexportable material.",
"2026-05-03T18:51:00Z",
)
.map_err(|error| error.to_string())?
.with_visibility(
RationaleTraceVisibility::PrivateRejected,
RedactionStatus::Full,
);
ensure(
RationaleTraceSummary::from_trace(&trace).is_none(),
true,
"private rejected rationale traces are not why evidence",
)
}
#[test]
fn why_report_rationale_traces_are_sorted_and_deduplicated() -> TestResult {
let report = WhyReport::not_found("mem_test".to_string()).with_rationale_traces(vec![
RationaleTraceSummary {
schema: crate::models::RATIONALE_TRACE_SCHEMA_V1,
trace_id: "rat_b".to_string(),
kind: "decision".to_string(),
posture: "supported".to_string(),
visibility: "public".to_string(),
author: "agent:test".to_string(),
summary: "Second trace.".to_string(),
confidence_basis_points: 7000,
evidence_uris: Vec::new(),
linked_memory_ids: Vec::new(),
linked_context_pack_ids: Vec::new(),
linked_recorder_run_ids: Vec::new(),
linked_recorder_event_ids: Vec::new(),
linked_causal_trace_ids: Vec::new(),
supersedes_trace_ids: Vec::new(),
contradicted_by_trace_ids: Vec::new(),
created_at: "2026-05-03T18:52:00Z".to_string(),
},
RationaleTraceSummary {
schema: crate::models::RATIONALE_TRACE_SCHEMA_V1,
trace_id: "rat_a".to_string(),
kind: "hypothesis".to_string(),
posture: "asserted".to_string(),
visibility: "public".to_string(),
author: "agent:test".to_string(),
summary: "First trace.".to_string(),
confidence_basis_points: 5000,
evidence_uris: Vec::new(),
linked_memory_ids: Vec::new(),
linked_context_pack_ids: Vec::new(),
linked_recorder_run_ids: Vec::new(),
linked_recorder_event_ids: Vec::new(),
linked_causal_trace_ids: Vec::new(),
supersedes_trace_ids: Vec::new(),
contradicted_by_trace_ids: Vec::new(),
created_at: "2026-05-03T18:51:00Z".to_string(),
},
RationaleTraceSummary {
schema: crate::models::RATIONALE_TRACE_SCHEMA_V1,
trace_id: "rat_a".to_string(),
kind: "hypothesis".to_string(),
posture: "asserted".to_string(),
visibility: "public".to_string(),
author: "agent:test".to_string(),
summary: "Duplicate trace.".to_string(),
confidence_basis_points: 5000,
evidence_uris: Vec::new(),
linked_memory_ids: Vec::new(),
linked_context_pack_ids: Vec::new(),
linked_recorder_run_ids: Vec::new(),
linked_recorder_event_ids: Vec::new(),
linked_causal_trace_ids: Vec::new(),
supersedes_trace_ids: Vec::new(),
contradicted_by_trace_ids: Vec::new(),
created_at: "2026-05-03T18:51:00Z".to_string(),
},
RationaleTraceSummary {
schema: crate::models::RATIONALE_TRACE_SCHEMA_V1,
trace_id: "rat_private".to_string(),
kind: "hypothesis".to_string(),
posture: "asserted".to_string(),
visibility: "private_rejected".to_string(),
author: "agent:test".to_string(),
summary: "Private rejected material should never render.".to_string(),
confidence_basis_points: 5000,
evidence_uris: Vec::new(),
linked_memory_ids: Vec::new(),
linked_context_pack_ids: Vec::new(),
linked_recorder_run_ids: Vec::new(),
linked_recorder_event_ids: Vec::new(),
linked_causal_trace_ids: Vec::new(),
supersedes_trace_ids: Vec::new(),
contradicted_by_trace_ids: Vec::new(),
created_at: "2026-05-03T18:53:00Z".to_string(),
},
]);
ensure(report.rationale_traces.len(), 2, "rationale trace count")?;
ensure(
report
.rationale_traces
.iter()
.any(|trace| trace.visibility.as_str().eq("private_rejected")),
false,
"private rejected summaries are filtered at report boundary",
)?;
ensure(
report.rationale_traces[0].trace_id.clone(),
"rat_a".to_string(),
"first trace id",
)?;
ensure(
report.rationale_traces[1].trace_id.clone(),
"rat_b".to_string(),
"second trace id",
)
}
#[test]
fn all_link_relation_types_supported() -> TestResult {
let relations = [
"supports",
"contradicts",
"derived_from",
"supersedes",
"related",
"co_tag",
"co_mention",
];
for relation in &relations {
let link = MemoryLinkSummary {
link_id: format!("link_{relation}"),
linked_memory_id: "mem_other".to_string(),
relation: relation.to_string(),
direction: "outgoing".to_string(),
confidence: 0.9,
weight: 1.0,
evidence_count: 1,
source: "agent".to_string(),
created_at: "2026-04-30T12:00:00Z".to_string(),
};
ensure(link.relation, relation.to_string(), "relation type")?;
}
Ok(())
}
#[test]
fn all_link_sources_supported() -> TestResult {
let sources = ["agent", "auto", "import", "maintenance", "human"];
for source in &sources {
let link = MemoryLinkSummary {
link_id: format!("link_{source}"),
linked_memory_id: "mem_other".to_string(),
relation: "supports".to_string(),
direction: "outgoing".to_string(),
confidence: 0.9,
weight: 1.0,
evidence_count: 1,
source: source.to_string(),
created_at: "2026-04-30T12:00:00Z".to_string(),
};
ensure(link.source, source.to_string(), "source type")?;
}
Ok(())
}
#[test]
fn find_embed_dedup_link_returns_none_when_no_dedup_link_persisted() -> TestResult {
let temp = tempfile::tempdir().map_err(|error| error.to_string())?;
let database_path = temp.path().join(".ee").join("ee.db");
std::fs::create_dir_all(
database_path
.parent()
.ok_or("database path should have parent")?,
)
.map_err(|error| error.to_string())?;
let conn = DbConnection::open_file(&database_path).map_err(|error| error.to_string())?;
conn.migrate().map_err(|error| error.to_string())?;
let workspace_id = "wsp_00000000000000000000001010";
let memory_id = "mem_00000000000000000000001010";
conn.insert_workspace(
workspace_id,
&CreateWorkspaceInput {
path: temp.path().display().to_string(),
name: Some("dedup-why-none".to_string()),
},
)
.map_err(|error| error.to_string())?;
conn.insert_memory(
memory_id,
&crate::db::CreateMemoryInput {
workspace_id: workspace_id.to_string(),
level: "procedural".to_string(),
kind: "rule".to_string(),
content: "memory without any dedup link".to_string(),
workflow_id: None,
confidence: 0.5,
utility: 0.5,
importance: 0.5,
provenance_uri: None,
trust_class: "agent_assertion".to_string(),
trust_subclass: None,
tags: Vec::new(),
valid_from: None,
valid_to: None,
},
)
.map_err(|error| error.to_string())?;
let result = super::find_embed_dedup_link(&conn, memory_id);
ensure(
result.is_none(),
true,
"find_embed_dedup_link must return None when no embed-dedup memory_link row exists for the memory",
)
}
#[test]
fn why_redacts_path_segments_with_spaces_without_tail_leakage() -> TestResult {
let redacted = super::redact_why_absolute_path_like_segments(
r#"source=file:///Users/alice/My Project/session.jsonl label=/workspace/Plain Path/log.txt note=done"#,
);
ensure(
redacted,
r#"source=file://[REDACTED_PATH] label=[REDACTED_PATH] note=done"#.to_owned(),
"why path redaction must consume spaced path tails",
)
}
#[test]
fn why_redacts_case_insensitive_macos_roots_and_env_paths() -> TestResult {
let redacted = super::redact_why_absolute_path_like_segments(
r#"users=/USERS/alice/private/session.jsonl volumes=/VOLUMES/USB/private/log.txt home=$HOME/.ssh/config ordinary=docs/USERS.md done=ok"#,
);
ensure(
redacted,
r#"users=[REDACTED_PATH] volumes=[REDACTED_PATH] home=[REDACTED_PATH] ordinary=docs/USERS.md done=ok"#.to_owned(),
"why redaction should mirror search projection path roots",
)
}
#[test]
fn why_redacts_windows_drive_prefix_casing_and_separator_variants() -> TestResult {
let redacted = super::redact_why_absolute_path_like_segments(
r#"upper=C:\Users\alice\secret lower=c:\Users\alice\secret slash=Z:/Users/alice/secret mixed=d:/data/private done=ok"#,
);
ensure(
redacted,
r#"upper=[REDACTED_PATH] lower=[REDACTED_PATH] slash=[REDACTED_PATH] mixed=[REDACTED_PATH] done=ok"#
.to_owned(),
"Windows drive variants must all be redacted in why provenance URIs",
)
}
}