use std::collections::BTreeMap;
use std::path::Path;
use serde::Serialize;
use crate::Engine;
use crate::anchor::{AnchorGrain, AnchorProvenanceClass, AnchorState};
use crate::binding::{Binding, CoverageSemantics, MediumCapabilities, medium_capabilities};
use crate::chunking::estimate_tokens;
use super::advance::read_advance_store;
use super::cursor::{enumerate_facet_files, source_moved};
use super::findings::{FindingClass, FindingKey, read_findings_store};
use super::resolve::{ChangeStrategy, ResolvedIngest, ResolvedSource, resolve_change_strategy};
pub const DEFAULT_REPORT_BUDGET: usize = 8_000;
pub const ALLOWED_REPORT_INCLUDE_KEYS: &[&str] =
&["uncovered_artifacts", "tree_fanout", "superseded_findings"];
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(tag = "kind", rename_all = "kebab-case")]
pub enum DenominatorBasis {
Enumerated {
count: usize,
},
NonEnumerable {
reason: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct TreeFanout {
pub entity: String,
pub artifact: String,
pub fanout: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct GrainCoverage {
pub denominator: DenominatorBasis,
pub direct_covered: usize,
pub tree_only_covered: usize,
pub uncovered: Vec<String>,
pub tree_anchors: Vec<TreeFanout>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
pub struct AnchorComposition {
pub by_class: BTreeMap<String, usize>,
pub by_grain: BTreeMap<String, usize>,
pub authored: usize,
pub observed: usize,
pub resolves: usize,
pub drifted: usize,
pub recheck: usize,
pub orphaned: usize,
pub unobserved: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct FacetCapability {
pub facet: String,
pub medium_type: String,
pub enumerable: bool,
pub change_signal: bool,
pub base_version_retrievable: bool,
pub anchor_namespace: String,
pub signal: String,
}
impl FacetCapability {
fn from_caps(
facet: String,
medium_type: String,
caps: MediumCapabilities,
strategy: ChangeStrategy,
) -> Self {
FacetCapability {
facet,
medium_type,
enumerable: caps.enumerable,
change_signal: caps.change_signal,
base_version_retrievable: caps.base_version_retrievable
&& strategy_retrieves_base(strategy),
anchor_namespace: caps.anchor_namespace.to_string(),
signal: signal_wire(strategy).to_string(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct FacetFreshness {
pub facet: String,
pub signal: String,
pub synced: Option<String>,
pub verified: Option<String>,
pub change_detectable: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct FidelityReport {
pub binding: String,
pub destination_mem: String,
pub adopt: bool,
pub coverage_semantics: CoverageSemantics,
pub coverage_semantics_declared: bool,
pub capabilities: Vec<FacetCapability>,
pub freshness: Vec<FacetFreshness>,
pub source_moved_past_synced: Option<bool>,
pub coverage: GrainCoverage,
pub anchors: AnchorComposition,
pub findings_by_class: BTreeMap<String, usize>,
pub backlog: usize,
pub superseded: Vec<String>,
pub disposed_excluded: usize,
pub disposed_excluded_rationales: Vec<(String, String)>,
pub degradations: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RenderedFidelityReport {
pub markdown: String,
pub mode: String,
pub hints: Vec<(String, usize)>,
pub budget_used: usize,
}
fn ratio(num: usize, den: usize) -> String {
if den == 0 {
format!("{num}/{den} (n/a)")
} else {
let pct = (num as f64) * 100.0 / (den as f64);
format!("{num}/{den} ({pct:.1}%)")
}
}
fn render_hard_required(report: &FidelityReport) -> String {
let mut md = String::new();
md.push_str(&format!("# Fidelity report — `{}`\n\n", report.binding));
md.push_str(&format!(
"- **Destination mem:** `{}`\n- **Coverage semantics:** {}{}\n\n",
report.destination_mem,
match report.coverage_semantics {
CoverageSemantics::Exhaustive => "exhaustive",
CoverageSemantics::Curated => "curated",
},
if report.coverage_semantics_declared {
""
} else {
" (resolved from the sources' media — not declared)"
}
));
if report.adopt {
md.push_str("## Adopting — first verify\n\n");
md.push_str(
"This mem predates its binding: it carries no anchors and has no prior sync \
baseline, so **0% anchored is expected — this is onboarding, not a failure.** \
Do not read the coverage numbers below as drift or a red verdict; the uncovered \
artifacts are the backfill worklist, not defects.\n\n",
);
md.push_str(&format!(
"**Backfill path:** run `memstead projection sync {}` to work through the in-scope \
source artifacts that carry no entity yet, covering the clearly-new concepts among \
them through the normal mutation surface. Backfilling is incremental — a partial \
pass is fine, and the next sync continues where you left off.\n\n",
report.binding
));
}
md.push_str("## Denominator provenance\n\n");
match &report.coverage.denominator {
DenominatorBasis::Enumerated { count } => md.push_str(&format!(
"Coverage is reported relative to the per-medium enumeration `S(D)` = **{count}** \
source artifact(s) in scope (after `deny_paths`).\n\n"
)),
DenominatorBasis::NonEnumerable { reason } => md.push_str(&format!(
"No `S(D)` denominator: {reason}. Coverage is reported over anchors only; the \
per-medium enumeration is unavailable.\n\n"
)),
}
md.push_str("## Capability matrix\n\n");
if report.capabilities.is_empty() {
md.push_str("_(no primary sources resolved)_\n\n");
} else {
for c in &report.capabilities {
md.push_str(&format!("### `{}` ({})\n\n", c.facet, c.medium_type));
md.push_str(&format!(
"- enumerable: {} | change_signal: {} | base_version_retrievable: {}\n",
c.enumerable, c.change_signal, c.base_version_retrievable
));
md.push_str(&format!(
"- anchor_namespace: `{}` | resolved signal: `{}`\n\n",
c.anchor_namespace, c.signal
));
}
}
md.push_str("## Freshness\n\n");
if report.freshness.is_empty() {
md.push_str("_(no source facets)_\n\n");
} else {
for f in &report.freshness {
md.push_str(&format!("### `{}`\n\n", f.facet));
md.push_str(&format!("- signal: `{}`\n", f.signal));
if !f.change_detectable {
md.push_str(
"- **freshness unknowable** — this medium is not change-detectable \
(no change signal); `#synced` / `#verified` cannot be adjudicated as fresh\n",
);
} else {
match &f.synced {
Some(t) => md.push_str(&format!("- `#synced`: `{t}`\n")),
None => md.push_str("- `#synced`: never synced\n"),
}
match &f.verified {
Some(t) => md.push_str(&format!("- `#verified`: `{t}`\n")),
None => md.push_str("- `#verified`: never verified\n"),
}
}
md.push('\n');
}
match report.source_moved_past_synced {
Some(true) => md.push_str(
"**Source moved past its `#synced` baseline** — the graph is stale for the \
moved facet(s); a sync pass is due.\n\n",
),
Some(false) => {
md.push_str("Every change-detectable source is at its `#synced` baseline.\n\n")
}
None => {}
}
}
md.push_str("## Coverage (grain-classed)\n\n");
let den = match &report.coverage.denominator {
DenominatorBasis::Enumerated { count } => *count,
DenominatorBasis::NonEnumerable { .. } => 0,
};
md.push_str(&format!(
"- direct-covered (file / span anchors): {}\n",
ratio(report.coverage.direct_covered, den)
));
let tree_files: usize = report.coverage.tree_anchors.iter().map(|t| t.fanout).sum();
md.push_str(&format!(
"- tree-anchor fan-out (separate axis): {} tree anchor(s) fanning out over {} file(s); \
{} file(s) covered ONLY via a tree anchor\n",
report.coverage.tree_anchors.len(),
tree_files,
report.coverage.tree_only_covered
));
md.push_str(&format!(
"- uncovered (no anchor): {}\n\n",
report.coverage.uncovered.len()
));
match report.coverage_semantics {
CoverageSemantics::Exhaustive if report.adopt => {
let backlog = report
.coverage
.uncovered
.len()
.saturating_sub(report.disposed_excluded);
md.push_str(&format!(
"**Exhaustive coverage (onboarding):** {backlog} in-scope artifact(s) carry no \
entity yet ({} disposed excluded) — the expected first-sync backfill worklist \
for a mem that predates its binding, not defects.\n\n",
report.disposed_excluded
));
}
CoverageSemantics::Exhaustive => {
let findings = report
.coverage
.uncovered
.len()
.saturating_sub(report.disposed_excluded);
md.push_str(&format!(
"**Exhaustive coverage:** {findings} unaccounted artifact(s) — not anchored, not \
declared-excluded, no persisted disposition ({} disposed excluded) — are \
**findings**.\n\n",
report.disposed_excluded
));
}
CoverageSemantics::Curated => {
md.push_str(&format!(
"**Curated coverage:** {} unaccounted artifact(s) are **information**, not \
defects — a curated binding covers a deliberate slice.\n\n",
report.coverage.uncovered.len()
));
}
}
if !report.disposed_excluded_rationales.is_empty() {
md.push_str("**Excluded on purpose (persisted dispositions):**\n");
for (artifact, rationale) in &report.disposed_excluded_rationales {
if rationale.is_empty() {
md.push_str(&format!("- `{artifact}`\n"));
} else {
md.push_str(&format!("- `{artifact}` — {rationale}\n"));
}
}
md.push('\n');
}
md.push_str("## Anchors\n\n");
md.push_str(&format!(
"- by class: {}\n",
render_counts(&report.anchors.by_class)
));
md.push_str(&format!(
"- by grain: {}\n",
render_counts(&report.anchors.by_grain)
));
md.push_str(&format!(
"- `authored` bucket (excluded from coverage/accuracy denominators): {}\n",
report.anchors.authored
));
md.push_str(&format!(
"- resolution (non-`authored`, observed): resolves {}, drifted {}, recheck {}, orphaned {}\n",
report.anchors.resolves,
report.anchors.drifted,
report.anchors.recheck,
report.anchors.orphaned
));
md.push_str(&format!(
"- **anchor-resolution %:** {}\n",
ratio(report.anchors.resolves, report.anchors.observed)
));
md.push_str(&format!(
"- unobserved this pass (state unavailable, never scored as resolved): {}\n\n",
report.anchors.unobserved
));
md.push_str("## Findings\n\n");
md.push_str(&format!(
"- by class: {}\n",
render_counts(&report.findings_by_class)
));
md.push_str(&format!(
"- **tier-3 adjudication backlog:** {}\n",
report.backlog
));
md.push_str(&format!(
"- superseded (prior `(hash(D), source_head)` key, segregated): {}\n\n",
report.superseded.len()
));
md.push_str("## Degradations\n\n");
if report.degradations.is_empty() {
md.push_str("_(none)_\n\n");
} else {
for d in &report.degradations {
md.push_str(&format!("- {d}\n"));
}
md.push('\n');
}
md
}
fn render_counts(counts: &BTreeMap<String, usize>) -> String {
if counts.is_empty() {
return "(none)".to_string();
}
counts
.iter()
.map(|(k, v)| format!("{k}={v}"))
.collect::<Vec<_>>()
.join(", ")
}
fn heavy_sections(report: &FidelityReport) -> Vec<(&'static str, String)> {
let mut out: Vec<(&'static str, String)> = Vec::new();
let mut s = String::new();
if !report.coverage.uncovered.is_empty() {
s.push_str("## Uncovered artifacts\n\n");
for a in &report.coverage.uncovered {
s.push_str(&format!("- `{a}`\n"));
}
s.push('\n');
}
out.push(("uncovered_artifacts", s));
let mut s = String::new();
if !report.coverage.tree_anchors.is_empty() {
s.push_str("## Tree-anchor fan-out (detail)\n\n");
for t in &report.coverage.tree_anchors {
s.push_str(&format!(
"- `{}` → `{}` fans out over {} file(s)\n",
t.entity, t.artifact, t.fanout
));
}
s.push('\n');
}
out.push(("tree_fanout", s));
let mut s = String::new();
if !report.superseded.is_empty() {
s.push_str("## Superseded findings (detail)\n\n");
for f in &report.superseded {
s.push_str(&format!("- {f}\n"));
}
s.push('\n');
}
out.push(("superseded_findings", s));
out
}
pub fn render_fidelity_report(
report: &FidelityReport,
budget: usize,
include: &[String],
) -> RenderedFidelityReport {
let hard = render_hard_required(report);
let hard_cost = estimate_tokens(&hard);
let overbudget = hard_cost > budget;
let include_set: std::collections::BTreeSet<&str> = include
.iter()
.map(String::as_str)
.filter(|k| ALLOWED_REPORT_INCLUDE_KEYS.contains(k))
.collect();
let unknown_includes: Vec<&String> = include
.iter()
.filter(|k| !ALLOWED_REPORT_INCLUDE_KEYS.contains(&k.as_str()))
.collect();
let sections = heavy_sections(report);
let mut emitted: Vec<String> = Vec::new();
let mut hints: Vec<(String, usize)> = Vec::new();
let mut used = hard_cost;
let mut remaining = budget.saturating_sub(hard_cost);
for (key, section_md) in §ions {
if section_md.is_empty() {
continue; }
let cost = estimate_tokens(section_md);
let forced = include_set.contains(key);
if forced {
emitted.push(section_md.clone());
used += cost;
remaining = remaining.saturating_sub(cost);
} else if !overbudget && remaining >= cost {
emitted.push(section_md.clone());
used += cost;
remaining -= cost;
} else {
hints.push(((*key).to_string(), cost));
}
}
let mode = if overbudget {
"overbudget"
} else if hints.is_empty() {
"complete"
} else {
"reduced"
};
let mut md = String::new();
md.push_str("---\n");
md.push_str(&format!("_report_mode: {mode}\n"));
md.push_str(&format!("_budget_requested: {budget}\n"));
md.push_str(&format!("_budget_used: {used}\n"));
md.push_str("---\n\n");
md.push_str(&hard);
for section in &emitted {
md.push_str(section);
}
if !hints.is_empty() {
md.push_str("## Hints\n\n");
md.push_str(
"_(heavy sections omitted under the token budget — re-query with the key)_\n\n",
);
for (key, tokens) in &hints {
md.push_str(&format!("- `{key}` — estimated_tokens: {tokens}\n"));
}
md.push('\n');
}
if !unknown_includes.is_empty() {
md.push_str("## Warnings\n\n");
for k in &unknown_includes {
md.push_str(&format!(
"- unknown include key `{k}` — allowed: {}\n",
ALLOWED_REPORT_INCLUDE_KEYS.join(", ")
));
}
md.push('\n');
}
RenderedFidelityReport {
markdown: md,
mode: mode.to_string(),
hints,
budget_used: used,
}
}
pub fn compute_fidelity_report(
engine: &Engine,
workspace_root: &Path,
binding: &Binding,
resolved: &ResolvedIngest,
key: &FindingKey,
) -> FidelityReport {
let binding_id = resolved.name.clone();
let dest = resolved.destination_mem.clone();
let sync_state = engine
.mem_config_for(&dest)
.map(|c| c.sync_state.clone())
.unwrap_or_default();
let mut capabilities: Vec<FacetCapability> = Vec::new();
let mut freshness: Vec<FacetFreshness> = Vec::new();
let mut any_change_detectable = false;
for source in &resolved.sources {
let ResolvedSource::Primary(p) = source else {
continue;
};
let caps = medium_capabilities(p.medium_type);
let medium_type = serde_json::to_value(p.medium_type)
.ok()
.and_then(|v| v.as_str().map(str::to_string))
.unwrap_or_default();
let strategy = resolve_change_strategy(p, workspace_root);
let signal = signal_wire(strategy).to_string();
let change_detectable = caps.change_signal && strategy != ChangeStrategy::None;
any_change_detectable |= change_detectable;
capabilities.push(FacetCapability::from_caps(
p.name.clone(),
medium_type,
caps,
strategy,
));
let synced = sync_state
.get(&format!("{binding_id}/{}#synced", p.name))
.cloned();
let verified = sync_state
.get(&format!("{binding_id}/{}#verified", p.name))
.cloned();
freshness.push(FacetFreshness {
facet: p.name.clone(),
signal,
synced,
verified,
change_detectable,
});
}
let source_moved_past_synced = if any_change_detectable {
Some(source_moved(engine, resolved, workspace_root))
} else {
None
};
let mut s_d: Vec<String> = Vec::new();
let mut enumerable_facets = 0usize;
for source in &resolved.sources {
if let ResolvedSource::Primary(p) = source {
let caps = medium_capabilities(p.medium_type);
if caps.enumerable {
enumerable_facets += 1;
}
s_d.extend(enumerate_facet_files(
p,
&resolved.deny_paths,
workspace_root,
));
}
}
s_d.sort();
s_d.dedup();
let denominator = if !s_d.is_empty() {
DenominatorBasis::Enumerated { count: s_d.len() }
} else if enumerable_facets == 0 {
DenominatorBasis::NonEnumerable {
reason: "the medium type(s) are not enumerable this cycle".to_string(),
}
} else {
DenominatorBasis::NonEnumerable {
reason: "no source artifacts enumerated in scope".to_string(),
}
};
let mut direct_covered = 0usize;
let mut tree_only_covered = 0usize;
let mut uncovered: Vec<String> = Vec::new();
let mut tree_fanout: BTreeMap<(String, String), usize> = BTreeMap::new();
for file in &s_d {
let refs = engine.anchors_referencing_artifact(file);
let mine: Vec<&(crate::EntityId, crate::anchor::Anchor)> = refs
.iter()
.filter(|(eid, _)| eid.mem() == dest.as_str())
.collect();
if mine.is_empty() {
uncovered.push(file.clone());
continue;
}
let has_non_tree = mine.iter().any(|(_, a)| a.grain != AnchorGrain::Tree);
if has_non_tree {
direct_covered += 1;
} else {
tree_only_covered += 1;
}
for (eid, a) in &mine {
if a.grain == AnchorGrain::Tree {
*tree_fanout
.entry((eid.as_ref().to_string(), a.artifact.clone()))
.or_insert(0) += 1;
}
}
}
let tree_anchors: Vec<TreeFanout> = tree_fanout
.into_iter()
.map(|((entity, artifact), fanout)| TreeFanout {
entity,
artifact,
fanout,
})
.collect();
let coverage = GrainCoverage {
denominator,
direct_covered,
tree_only_covered,
uncovered: uncovered.clone(),
tree_anchors,
};
let mut anchors = AnchorComposition::default();
for (_eid, resolved_anchor) in engine.mem_anchors_resolved(&dest) {
let a = &resolved_anchor.anchor;
*anchors
.by_class
.entry(a.class.as_wire().to_string())
.or_insert(0) += 1;
*anchors
.by_grain
.entry(a.grain.as_wire().to_string())
.or_insert(0) += 1;
if a.class == AnchorProvenanceClass::Authored {
anchors.authored += 1;
continue; }
match resolved_anchor.state {
Some(AnchorState::Resolves) => {
anchors.resolves += 1;
anchors.observed += 1;
}
Some(AnchorState::Drifted) => {
anchors.drifted += 1;
anchors.observed += 1;
}
Some(AnchorState::Recheck) => {
anchors.recheck += 1;
anchors.observed += 1;
}
Some(AnchorState::Orphaned) => {
anchors.orphaned += 1;
anchors.observed += 1;
}
None => anchors.unobserved += 1,
}
}
let mut findings_by_class: BTreeMap<String, usize> = BTreeMap::new();
let mut backlog = 0usize;
let mut superseded: Vec<String> = Vec::new();
if let Some((mem, name)) = binding_id.split_once('/')
&& let Ok(Some(store)) = read_findings_store(workspace_root, mem, name)
{
for f in store.current(key) {
*findings_by_class
.entry(f.class.as_wire().to_string())
.or_insert(0) += 1;
if f.class == FindingClass::QueuedForAdjudication {
backlog += 1;
}
}
for f in store.superseded(key) {
superseded.push(format!(
"[{}] {} ({})",
f.class.as_wire(),
finding_target_label(&f.target),
f.facet
));
}
}
let mut disposed_excluded_rationales: Vec<(String, String)> = Vec::new();
if let Some((mem, name)) = binding_id.split_once('/')
&& let Ok(Some(state)) = read_advance_store(workspace_root, mem, name)
{
let uncovered_set: std::collections::BTreeSet<&str> =
uncovered.iter().map(String::as_str).collect();
for (artifact, rationale) in &state.exclusions {
if uncovered_set.contains(artifact.as_str()) {
disposed_excluded_rationales.push((artifact.clone(), rationale.clone()));
}
}
}
let disposed_excluded = disposed_excluded_rationales.len();
let mut degradations: Vec<String> = Vec::new();
for c in &capabilities {
if !c.change_signal || c.signal == "none" {
degradations.push(format!(
"change-signal-none:`{}` — freshness is unknowable for this facet",
c.facet
));
}
if !c.enumerable {
degradations.push(format!(
"enumeration-unavailable:`{}` — `S(D)` coverage denominator not computable",
c.facet
));
}
if !c.base_version_retrievable {
degradations.push(format!(
"base-version-unretrievable:`{}` — prune degrades to conflict-flagging",
c.facet
));
}
}
if anchors.recheck > 0 {
degradations.push(format!(
"hash-adjudication-deferred — {} anchor(s) recheck (unstable medium / hash \
unavailable), not asserted drift",
anchors.recheck
));
}
if anchors.unobserved > 0 {
degradations.push(format!(
"anchors-unobserved — {} anchor(s) could not be observed this pass",
anchors.unobserved
));
}
let adopt = super::render::mem_predates_binding(engine, resolved);
let effective_coverage = crate::binding::effective_coverage_semantics(binding);
FidelityReport {
binding: binding_id,
destination_mem: dest,
adopt,
coverage_semantics: effective_coverage.value,
coverage_semantics_declared: effective_coverage.declared,
capabilities,
freshness,
source_moved_past_synced,
coverage,
anchors,
findings_by_class,
backlog,
superseded,
disposed_excluded,
disposed_excluded_rationales,
degradations,
}
}
fn strategy_retrieves_base(strategy: ChangeStrategy) -> bool {
matches!(strategy, ChangeStrategy::Git | ChangeStrategy::Graph)
}
fn signal_wire(strategy: ChangeStrategy) -> &'static str {
match strategy {
ChangeStrategy::None => "none",
ChangeStrategy::Git => "git",
ChangeStrategy::Mtime => "mtime",
ChangeStrategy::Graph => "graph",
}
}
fn finding_target_label(target: &super::findings::FindingTarget) -> String {
match target {
super::findings::FindingTarget::Anchor { entity, artifact } => {
format!("{entity} → {artifact}")
}
super::findings::FindingTarget::Artifact { artifact } => artifact.clone(),
}
}
#[cfg(test)]
mod tests {
use super::*;
fn base_report() -> FidelityReport {
FidelityReport {
binding: "engine/graph".to_string(),
destination_mem: "engine".to_string(),
adopt: false,
coverage_semantics: CoverageSemantics::Exhaustive,
coverage_semantics_declared: true,
capabilities: vec![FacetCapability {
facet: "src".to_string(),
medium_type: "codebase".to_string(),
enumerable: true,
change_signal: true,
base_version_retrievable: true,
anchor_namespace: "path".to_string(),
signal: "git".to_string(),
}],
freshness: vec![FacetFreshness {
facet: "src".to_string(),
signal: "git".to_string(),
synced: Some("deadbeef".to_string()),
verified: None,
change_detectable: true,
}],
source_moved_past_synced: Some(false),
coverage: GrainCoverage {
denominator: DenominatorBasis::Enumerated { count: 10 },
direct_covered: 6,
tree_only_covered: 3,
uncovered: vec!["src/a.rs".to_string()],
tree_anchors: vec![TreeFanout {
entity: "engine--big".to_string(),
artifact: "src/".to_string(),
fanout: 3,
}],
},
anchors: AnchorComposition {
by_class: BTreeMap::from([
("anchored".to_string(), 5),
("authored".to_string(), 2),
]),
by_grain: BTreeMap::from([("file".to_string(), 4), ("tree".to_string(), 1)]),
authored: 2,
observed: 5,
resolves: 4,
drifted: 0,
recheck: 1,
orphaned: 0,
unobserved: 0,
},
findings_by_class: BTreeMap::from([
("uncovered".to_string(), 1),
("queued-for-adjudication".to_string(), 1),
]),
backlog: 1,
superseded: Vec::new(),
disposed_excluded: 0,
disposed_excluded_rationales: Vec::new(),
degradations: vec!["hash-adjudication-deferred — 1 anchor(s) recheck".to_string()],
}
}
#[test]
fn b1_renders_all_elements_deterministically() {
let r = base_report();
let a = render_fidelity_report(&r, 8_000, &[]);
let b = render_fidelity_report(&r, 8_000, &[]);
assert_eq!(a.markdown, b.markdown, "deterministic — identical bytes");
let md = &a.markdown;
assert!(md.contains("direct-covered (file / span anchors): 6/10"));
assert!(md.contains(
"tree-anchor fan-out (separate axis): 1 tree anchor(s) fanning out over 3 file(s)"
));
assert!(
!md.contains("9/10"),
"tree fan-out must not blend into direct coverage"
);
assert!(md.contains("anchor-resolution %:** 4/5"));
assert!(md.contains("`authored` bucket (excluded from coverage/accuracy denominators): 2"));
assert!(md.contains("tier-3 adjudication backlog:** 1"));
assert!(md.contains("## Capability matrix"));
assert!(md.contains("## Degradations"));
assert!(md.contains("hash-adjudication-deferred"));
assert!(md.contains("per-medium enumeration `S(D)` = **10**"));
}
#[test]
fn b2_detectionless_medium_freshness_unknowable_never_green() {
let mut r = base_report();
r.capabilities = vec![FacetCapability {
facet: "manual".to_string(),
medium_type: "web".to_string(),
enumerable: false,
change_signal: false,
base_version_retrievable: false,
anchor_namespace: "url".to_string(),
signal: "none".to_string(),
}];
r.freshness = vec![FacetFreshness {
facet: "manual".to_string(),
signal: "none".to_string(),
synced: Some("should-never-render-green".to_string()),
verified: Some("nor-this".to_string()),
change_detectable: false,
}];
r.source_moved_past_synced = None;
let out = render_fidelity_report(&r, 8_000, &[]);
let md = &out.markdown;
assert!(md.contains("signal: `none`"));
assert!(md.contains("freshness unknowable"));
assert!(!md.contains("should-never-render-green"));
assert!(
!md.contains("`#synced`: `"),
"no synced token rendered for a non-detectable medium"
);
assert!(
!md.contains("at its `#synced` baseline"),
"no green 'at baseline' verdict"
);
}
#[test]
fn b1_base_retrievability_follows_resolved_strategy_not_medium_ceiling() {
use crate::pipeline::MediumType;
assert!(medium_capabilities(MediumType::Filesystem).base_version_retrievable);
let fs_mtime = FacetCapability::from_caps(
"prose".to_string(),
"filesystem".to_string(),
medium_capabilities(MediumType::Filesystem),
ChangeStrategy::Mtime,
);
assert!(
!fs_mtime.base_version_retrievable,
"filesystem+mtime has no retrievable base leg — degrades to conflict-flag"
);
assert_eq!(fs_mtime.signal, "mtime");
let fs_git = FacetCapability::from_caps(
"prose".to_string(),
"filesystem".to_string(),
medium_capabilities(MediumType::Filesystem),
ChangeStrategy::Git,
);
assert!(
fs_git.base_version_retrievable,
"filesystem backed by git keeps the never-clobber base leg"
);
assert!(!strategy_retrieves_base(ChangeStrategy::None));
assert!(!strategy_retrieves_base(ChangeStrategy::Mtime));
assert!(strategy_retrieves_base(ChangeStrategy::Git));
assert!(strategy_retrieves_base(ChangeStrategy::Graph));
let mut r = base_report();
r.capabilities = vec![fs_mtime.clone()];
r.degradations = if !fs_mtime.base_version_retrievable {
vec![format!(
"base-version-unretrievable:`{}` — prune degrades to conflict-flagging",
fs_mtime.facet
)]
} else {
Vec::new()
};
let md = render_fidelity_report(&r, 8_000, &[]).markdown;
assert!(
md.contains("base-version-unretrievable:`prose` — prune degrades to conflict-flagging"),
"filesystem+mtime surfaces the conflict-flag degradation in the report"
);
}
#[test]
fn b3_aggregates_always_ship_at_zero_budget() {
let r = base_report();
let out = render_fidelity_report(&r, 0, &[]);
assert_eq!(out.mode, "overbudget");
let md = &out.markdown;
assert!(md.contains("direct-covered (file / span anchors): 6/10"));
assert!(md.contains("tier-3 adjudication backlog:** 1"));
assert!(md.contains("## Capability matrix"));
assert!(!md.contains("## Uncovered artifacts"));
assert!(md.contains("## Hints"));
assert!(out.hints.iter().any(|(k, _)| k == "uncovered_artifacts"));
}
#[test]
fn b3_large_facet_list_truncates_then_include_forces() {
let mut r = base_report();
r.coverage.uncovered = (0..500).map(|i| format!("src/file_{i}.rs")).collect();
let hard_cost = estimate_tokens(&render_hard_required(&r));
let out = render_fidelity_report(&r, hard_cost + 5, &[]);
assert_eq!(out.mode, "reduced");
assert!(
!out.markdown.contains("src/file_499.rs"),
"big list not rendered unbounded"
);
assert!(out.markdown.contains("## Hints"));
let (_, est) = out
.hints
.iter()
.find(|(k, _)| k == "uncovered_artifacts")
.expect("uncovered list hinted");
assert!(*est > 5, "the hint carries a real estimated_tokens figure");
let forced =
render_fidelity_report(&r, hard_cost + 5, &["uncovered_artifacts".to_string()]);
assert!(
forced.markdown.contains("src/file_499.rs"),
"include forces the full list"
);
}
#[test]
fn b4_curated_vs_exhaustive_framing() {
let mut exhaustive = base_report();
exhaustive.coverage_semantics = CoverageSemantics::Exhaustive;
let ex_md = render_fidelity_report(&exhaustive, 8_000, &[]).markdown;
assert!(ex_md.contains("Exhaustive coverage:"));
assert!(ex_md.contains("are **findings**"));
let mut curated = base_report();
curated.coverage_semantics = CoverageSemantics::Curated;
let cur_md = render_fidelity_report(&curated, 8_000, &[]).markdown;
assert!(cur_md.contains("Curated coverage:"));
assert!(cur_md.contains("**information**"));
assert!(
!cur_md.contains("are **findings**"),
"curated never frames unaccounted as findings"
);
}
#[test]
fn b4_disposition_excludes_from_exhaustive_findings() {
let mut r = base_report();
r.coverage_semantics = CoverageSemantics::Exhaustive;
r.coverage.uncovered = vec!["src/a.rs".to_string(), "src/b.rs".to_string()];
r.disposed_excluded = 1;
let md = render_fidelity_report(&r, 8_000, &[]).markdown;
assert!(md.contains("1 unaccounted artifact(s)"));
assert!(md.contains("(1 disposed excluded)"));
}
#[test]
fn b4_authored_exclusion_rationale_is_rendered() {
let mut r = base_report();
r.coverage_semantics = CoverageSemantics::Exhaustive;
r.coverage.uncovered = vec!["src/gen.rs".to_string()];
r.disposed_excluded = 1;
r.disposed_excluded_rationales =
vec![("src/gen.rs".to_string(), "generated; no entity".to_string())];
let md = render_fidelity_report(&r, 8_000, &[]).markdown;
assert!(md.contains("Excluded on purpose (persisted dispositions):"));
assert!(md.contains("`src/gen.rs` — generated; no entity"));
}
#[test]
fn b5_denominator_provenance_stated() {
let r = base_report();
let md = render_fidelity_report(&r, 8_000, &[]).markdown;
assert!(md.contains("## Denominator provenance"));
assert!(md.contains("per-medium enumeration `S(D)` = **10**"));
let mut non = base_report();
non.coverage.denominator = DenominatorBasis::NonEnumerable {
reason: "the medium type(s) are not enumerable this cycle".to_string(),
};
let md2 = render_fidelity_report(&non, 8_000, &[]).markdown;
assert!(md2.contains("No `S(D)` denominator"));
assert!(md2.contains("not enumerable this cycle"));
}
#[test]
fn e1_adopt_report_renders_onboarding_no_red_verdict() {
let mut r = base_report();
r.adopt = true;
r.coverage_semantics = CoverageSemantics::Exhaustive;
r.coverage.uncovered = (0..5).map(|i| format!("src/file_{i}.rs")).collect();
let md = render_fidelity_report(&r, 8_000, &[]).markdown;
assert!(md.contains("## Adopting — first verify"));
assert!(md.contains("0% anchored is expected — this is onboarding, not a failure."));
assert!(md.contains("**Backfill path:** run `memstead projection sync engine/graph`"));
assert!(
!md.contains("are **findings**"),
"pre-binding history must not produce a red findings verdict"
);
assert!(md.contains("Exhaustive coverage (onboarding):"));
assert!(md.contains("backfill worklist"));
r.adopt = false;
let md2 = render_fidelity_report(&r, 8_000, &[]).markdown;
assert!(!md2.contains("## Adopting — first verify"));
assert!(md2.contains("are **findings**"));
}
#[test]
fn unknown_include_key_warns() {
let r = base_report();
let out = render_fidelity_report(&r, 8_000, &["bogus".to_string()]);
assert!(out.markdown.contains("unknown include key `bogus`"));
}
use crate::anchor::{Anchor, AnchorHashStability, AnchorProvenanceClass, AnchorSidecar};
use crate::binding::{
BINDING_VERSION, Binding, BuildMode, BuildOperation, DEFAULT_ADJUDICATION_CAP,
DEFAULT_FULL_RESYNC_EVERY, Operations, VerifyOperation,
};
use crate::ingest::findings::verify_binding;
use crate::ingest::resolve::resolve_binding_run;
use crate::pipeline::{IngestTrigger, MediumType, PatternEntry, PatternMode};
use crate::pipeline_store::{load_pipeline_configs, write_binding};
use crate::workspace::{
Mount, MountCapability, MountLifecycle, MountStorage, Workspace, WorkspaceSettings,
};
use crate::workspace_store::WorkspaceStoreAdapter;
#[test]
fn compute_report_end_to_end() {
let tmp = tempfile::tempdir().unwrap();
let root = tmp.path();
let mem_dir = root.join("mem");
std::fs::create_dir_all(mem_dir.join(".memstead")).unwrap();
std::fs::write(
mem_dir.join(".memstead").join("config.json"),
r#"{"format":1,"schema":"default@1.0.0","version":"1.0.0"}"#,
)
.unwrap();
std::fs::create_dir_all(root.join(".memstead")).unwrap();
std::fs::write(
root.join(".memstead").join("workspace.toml"),
"format = \"memstead-git-branch-2\"\n\n[persistence_adapter]\nname = \"file-two-layer\"\n",
)
.unwrap();
let mount = Mount {
mem: "engine".to_string(),
schema: Some("default@1.0.0".parse().unwrap()),
storage: MountStorage::Folder {
path: mem_dir.clone(),
},
capability: MountCapability::Write,
lifecycle: MountLifecycle::Eager,
cross_linkable: false,
migration_target: None,
};
crate::FileWorkspaceStore::new()
.save_state(
root,
&Workspace {
mounts: vec![mount],
settings: WorkspaceSettings::default(),
},
)
.unwrap();
let out = std::process::Command::new("git")
.args(["init", "-q"])
.current_dir(root)
.output()
.unwrap();
assert!(out.status.success());
std::fs::create_dir_all(root.join("src").join("sub")).unwrap();
std::fs::write(root.join("src").join("present.rs"), "fn a() {}\n").unwrap();
std::fs::write(root.join("src").join("uncovered.rs"), "fn b() {}\n").unwrap();
std::fs::write(root.join("src").join("sub").join("deep.rs"), "fn c() {}\n").unwrap();
let mk = |artifact: &str, grain: AnchorGrain, class: AnchorProvenanceClass| Anchor {
artifact: artifact.to_string(),
grain,
class,
at_version: None,
hash: class.is_hash_bearing().then(|| "recorded".to_string()),
hash_stability: AnchorHashStability::Stable,
derived_from: Vec::new(),
binding: None,
source: None,
};
let mut sidecar = AnchorSidecar::default();
sidecar.set(
"engine--direct",
vec![mk(
"src/present.rs",
AnchorGrain::File,
AnchorProvenanceClass::Anchored,
)],
);
sidecar.set(
"engine--tree",
vec![mk(
"src/sub/",
AnchorGrain::Tree,
AnchorProvenanceClass::Anchored,
)],
);
sidecar.set(
"engine--auth",
vec![mk(
"src/present.rs",
AnchorGrain::File,
AnchorProvenanceClass::Authored,
)],
);
std::fs::write(
mem_dir.join(crate::anchor::ANCHOR_SIDECAR_PATH),
sidecar.to_bytes(),
)
.unwrap();
write_binding(
root,
"engine",
"graph",
&Binding {
version: BINDING_VERSION,
intent: None,
sources: vec![crate::pipeline::Source {
name: "graph".to_string(),
medium_type: MediumType::Codebase,
pointer: String::new(),
change_detection: Some("git".to_string()),
scope: vec![PatternEntry {
path: "src/**/*.rs".to_string(),
mode: PatternMode::Allow,
}],
engagement: None,
preparation: None,
}],
reference_mems: Vec::new(),
destination_mem: "engine".to_string(),
deny_paths: Vec::new(),
coverage_semantics: None,
rules: None,
prune: None,
operations: Operations {
build: Some(BuildOperation {
mode: BuildMode::Discovery,
trigger: IngestTrigger::Loop,
batch_size: 20,
post_actions: None,
}),
sync: None,
verify: Some(VerifyOperation {
trigger: IngestTrigger::Manual,
batch_size: 20,
adjudication_cap: DEFAULT_ADJUDICATION_CAP,
full_resync_every: DEFAULT_FULL_RESYNC_EVERY,
}),
},
},
)
.unwrap();
let engine = Engine::from_workspace_root(root).unwrap();
let configs = load_pipeline_configs(root).unwrap();
let binding = &configs.bindings[0].config;
let resolved = resolve_binding_run("engine/graph", binding).unwrap();
let outcome = verify_binding(&engine, root, binding, &resolved).unwrap();
let report = compute_fidelity_report(&engine, root, binding, &resolved, &outcome.key);
assert_eq!(
report.coverage.denominator,
DenominatorBasis::Enumerated { count: 3 }
);
assert_eq!(report.coverage.direct_covered, 1);
assert_eq!(report.coverage.tree_only_covered, 1);
assert_eq!(
report.coverage.uncovered,
vec!["src/uncovered.rs".to_string()]
);
assert_eq!(report.coverage.tree_anchors.len(), 1);
assert_eq!(report.coverage.tree_anchors[0].fanout, 1);
assert_eq!(report.coverage.tree_anchors[0].artifact, "src/sub/");
assert_eq!(report.anchors.authored, 1);
assert_eq!(report.anchors.by_class.get("authored"), Some(&1));
assert_eq!(report.anchors.observed, 2);
assert_eq!(report.anchors.recheck, 1);
assert_eq!(report.anchors.drifted, 1);
assert_eq!(report.backlog, outcome.backlog);
assert!(
report
.degradations
.iter()
.any(|d| d.contains("hash-adjudication-deferred"))
);
let md = render_fidelity_report(&report, 8_000, &[]).markdown;
assert!(md.contains("per-medium enumeration `S(D)` = **3**"));
assert!(!report.adopt);
assert!(!md.contains("## Adopting — first verify"));
}
#[test]
fn compute_report_adopt_when_mem_predates_binding() {
let tmp = tempfile::tempdir().unwrap();
let root = tmp.path();
let mem_dir = root.join("mem");
std::fs::create_dir_all(mem_dir.join(".memstead")).unwrap();
std::fs::write(
mem_dir.join(".memstead").join("config.json"),
r#"{"format":1,"schema":"default@1.0.0","version":"1.0.0"}"#,
)
.unwrap();
std::fs::create_dir_all(root.join(".memstead")).unwrap();
std::fs::write(
root.join(".memstead").join("workspace.toml"),
"format = \"memstead-git-branch-2\"\n\n[persistence_adapter]\nname = \"file-two-layer\"\n",
)
.unwrap();
let mount = Mount {
mem: "engine".to_string(),
schema: Some("default@1.0.0".parse().unwrap()),
storage: MountStorage::Folder {
path: mem_dir.clone(),
},
capability: MountCapability::Write,
lifecycle: MountLifecycle::Eager,
cross_linkable: false,
migration_target: None,
};
crate::FileWorkspaceStore::new()
.save_state(
root,
&Workspace {
mounts: vec![mount],
settings: WorkspaceSettings::default(),
},
)
.unwrap();
let out = std::process::Command::new("git")
.args(["init", "-q"])
.current_dir(root)
.output()
.unwrap();
assert!(out.status.success());
std::fs::create_dir_all(root.join("src")).unwrap();
std::fs::write(root.join("src").join("a.rs"), "fn a() {}\n").unwrap();
std::fs::write(root.join("src").join("b.rs"), "fn b() {}\n").unwrap();
write_binding(
root,
"engine",
"graph",
&Binding {
version: BINDING_VERSION,
intent: None,
sources: vec![crate::pipeline::Source {
name: "graph".to_string(),
medium_type: MediumType::Codebase,
pointer: String::new(),
change_detection: Some("git".to_string()),
scope: vec![PatternEntry {
path: "src/**/*.rs".to_string(),
mode: PatternMode::Allow,
}],
engagement: None,
preparation: None,
}],
reference_mems: Vec::new(),
destination_mem: "engine".to_string(),
deny_paths: Vec::new(),
coverage_semantics: None,
rules: None,
prune: None,
operations: Operations {
build: Some(BuildOperation {
mode: BuildMode::Discovery,
trigger: IngestTrigger::Loop,
batch_size: 20,
post_actions: None,
}),
sync: None,
verify: Some(VerifyOperation {
trigger: IngestTrigger::Manual,
batch_size: 20,
adjudication_cap: DEFAULT_ADJUDICATION_CAP,
full_resync_every: DEFAULT_FULL_RESYNC_EVERY,
}),
},
},
)
.unwrap();
let engine = Engine::from_workspace_root(root).unwrap();
let configs = load_pipeline_configs(root).unwrap();
let binding = &configs.bindings[0].config;
let resolved = resolve_binding_run("engine/graph", binding).unwrap();
let outcome = verify_binding(&engine, root, binding, &resolved).unwrap();
let report = compute_fidelity_report(&engine, root, binding, &resolved, &outcome.key);
assert!(
report.adopt,
"a no-anchor, never-synced mem predates its binding"
);
let md = render_fidelity_report(&report, 8_000, &[]).markdown;
assert!(md.contains("## Adopting — first verify"));
assert!(md.contains("0% anchored is expected"));
assert!(!md.contains("are **findings**"));
assert!(md.contains("Exhaustive coverage (onboarding):"));
}
#[test]
fn report_marks_resolved_coverage_semantics() {
let mut resolved = base_report();
resolved.coverage_semantics = CoverageSemantics::Curated;
resolved.coverage_semantics_declared = false;
let md = render_hard_required(&resolved);
assert!(
md.contains("curated (resolved from the sources' media — not declared)"),
"resolved value carries the marker: {md}"
);
let declared = base_report(); let md = render_hard_required(&declared);
assert!(
md.contains("**Coverage semantics:** exhaustive\n"),
"declared value renders bare: {md}"
);
assert!(
!md.contains("(resolved from the sources' media"),
"no resolution marker on a declared value: {md}"
);
}
}