Skip to main content

remem/eval/
capacity.rs

1use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
2use std::fmt::{Display, Formatter, Result as FmtResult};
3use std::time::Instant;
4
5use anyhow::{bail, ensure, Context, Result};
6use rusqlite::Connection;
7use serde::Serialize;
8use sha2::{Digest, Sha256};
9
10use crate::eval::golden::{self, GoldenDataset, GoldenMemory, QueryEvaluation, QueryMetrics};
11use crate::memory::Memory;
12use crate::retrieval::search::SearchExplain;
13
14mod noise;
15
16use noise::{COMMANDS, CRATE_NAMES, ERROR_SIGNATURES, FILE_PATHS, MEMORY_TYPES, OWNERS};
17
18pub const DEFAULT_DATASET_PATH: &str = "eval/golden.json";
19const REPORT_VERSION: &str = "2026-07-03";
20const RANK_K: usize = 10;
21const TRACKED_CHANNELS: [&str; 6] = [
22    "fts",
23    "entity",
24    "fact",
25    "temporal",
26    "vector",
27    "like_fallback",
28];
29
30#[derive(Debug, Clone)]
31pub struct CapacityEvalOptions {
32    pub dataset_path: String,
33    pub seed: u64,
34    pub scales: Vec<usize>,
35    pub k: usize,
36}
37
38impl Default for CapacityEvalOptions {
39    fn default() -> Self {
40        Self {
41            dataset_path: DEFAULT_DATASET_PATH.to_string(),
42            seed: 42,
43            scales: vec![1, 10],
44            k: 5,
45        }
46    }
47}
48
49#[derive(Debug, Clone, Serialize)]
50pub struct CapacityEvalReport {
51    pub version: &'static str,
52    pub dataset_path: String,
53    pub seed: u64,
54    pub k: usize,
55    pub scale_factors: Vec<usize>,
56    pub base_corpus_size: usize,
57    pub scales: Vec<CapacityScaleReport>,
58    pub degradation: CapacityDegradation,
59    pub omitted_followups: Vec<&'static str>,
60}
61
62#[derive(Debug, Clone, Serialize)]
63pub struct CapacityScaleReport {
64    pub scale: usize,
65    pub corpus_size: usize,
66    pub noise_count: usize,
67    pub corpus_hash: String,
68    pub fused: CapacityFusedMetrics,
69    pub channels: BTreeMap<String, CapacityFusedMetrics>,
70}
71
72#[derive(Debug, Clone, Serialize)]
73pub struct CapacityFusedMetrics {
74    pub scored_queries: usize,
75    pub hit_at_k: f64,
76    pub recall_at_k: f64,
77    pub ndcg_at_10: f64,
78    pub evidence_recall_at_k: f64,
79    pub p95_latency_ms: f64,
80}
81
82#[derive(Debug, Clone, Serialize)]
83pub struct CapacityDegradation {
84    pub largest_scale: usize,
85    pub fused_recall_at_k_loss: f64,
86    pub fused_ndcg_at_10_loss: f64,
87    pub fused_evidence_recall_at_k_loss: f64,
88    pub channels: BTreeMap<String, CapacityChannelDegradation>,
89}
90
91#[derive(Debug, Clone, Serialize)]
92pub struct CapacityChannelDegradation {
93    pub recall_at_k_loss: f64,
94    pub ndcg_at_10_loss: f64,
95    pub evidence_recall_at_k_loss: f64,
96}
97
98#[derive(Debug, Clone)]
99pub(in crate::eval) struct ScaledDataset {
100    pub dataset: GoldenDataset,
101    pub noise_count: usize,
102    pub corpus_hash: String,
103}
104
105pub fn run_capacity_eval(options: CapacityEvalOptions) -> Result<CapacityEvalReport> {
106    let dataset = golden::load_dataset(&options.dataset_path)?;
107    run_capacity_eval_for_dataset(options, dataset)
108}
109
110pub(in crate::eval) fn run_capacity_eval_for_dataset(
111    options: CapacityEvalOptions,
112    dataset: GoldenDataset,
113) -> Result<CapacityEvalReport> {
114    validate_capacity_dataset(&dataset)?;
115    let scales = normalize_scales(options.scales)?;
116    let k = options.k.max(1);
117    let mut scale_reports = Vec::with_capacity(scales.len());
118
119    for scale in &scales {
120        let scaled = synthesize_capacity_dataset(&dataset, options.seed, *scale)?;
121        scale_reports.push(
122            evaluate_capacity_scale(*scale, scaled, k)
123                .with_context(|| format!("run capacity eval scale {scale}"))?,
124        );
125    }
126
127    let baseline = scale_reports
128        .iter()
129        .find(|report| report.scale == 1)
130        .context("capacity eval requires a 1x baseline scale")?;
131    let largest = scale_reports
132        .iter()
133        .max_by_key(|report| report.scale)
134        .context("capacity eval requires at least one scale")?;
135    let degradation = CapacityDegradation {
136        largest_scale: largest.scale,
137        fused_recall_at_k_loss: positive_loss(
138            baseline.fused.recall_at_k,
139            largest.fused.recall_at_k,
140        ),
141        fused_ndcg_at_10_loss: positive_loss(baseline.fused.ndcg_at_10, largest.fused.ndcg_at_10),
142        fused_evidence_recall_at_k_loss: positive_loss(
143            baseline.fused.evidence_recall_at_k,
144            largest.fused.evidence_recall_at_k,
145        ),
146        channels: channel_degradation(&baseline.channels, &largest.channels),
147    };
148
149    Ok(CapacityEvalReport {
150        version: REPORT_VERSION,
151        dataset_path: options.dataset_path,
152        seed: options.seed,
153        k,
154        scale_factors: scales,
155        base_corpus_size: dataset.corpus.len(),
156        scales: scale_reports,
157        degradation,
158        omitted_followups: vec!["nightly_dashboard_ingestion", "50x_nightly_scale"],
159    })
160}
161
162fn evaluate_capacity_scale(
163    scale: usize,
164    scaled: ScaledDataset,
165    k: usize,
166) -> Result<CapacityScaleReport> {
167    let conn = Connection::open_in_memory().context("open in-memory capacity eval DB")?;
168    crate::migrate::run_migrations(&conn).context("migrate in-memory capacity eval DB")?;
169    golden::run::seed_fixture_corpus(&conn, &scaled.dataset.corpus)?;
170
171    let mut fused = MetricAccumulator::default();
172    let mut channels = TRACKED_CHANNELS
173        .iter()
174        .map(|channel| ((*channel).to_string(), MetricAccumulator::default()))
175        .collect::<BTreeMap<_, _>>();
176    let fetch_limit = k.max(RANK_K) as i64;
177
178    for query in &scaled.dataset.queries {
179        let query_tokens = golden::run::estimate_query_tokens(&query.query);
180        let started = Instant::now();
181        let (results, explain) = crate::retrieval::search::search_with_branch_explain(
182            &conn,
183            Some(&query.query),
184            query.project.as_deref(),
185            query.memory_type.as_deref(),
186            fetch_limit,
187            0,
188            false,
189            query.branch.as_deref(),
190        )?;
191        let retrieval_latency_ms = started.elapsed().as_secs_f64() * 1000.0;
192        let fused_evaluation =
193            golden::run::evaluate_query(query, &results, k, query_tokens, retrieval_latency_ms);
194        fused.add(&fused_evaluation);
195
196        let channel_hit_ids = channel_hit_ids(explain.as_ref());
197        for channel in TRACKED_CHANNELS {
198            let ids = channel_hit_ids.get(channel).cloned().unwrap_or_default();
199            let memories = load_ordered_memories(&conn, &ids)?;
200            let latency_ms = channel_latency_ms(explain.as_ref(), channel);
201            let channel_evaluation =
202                golden::run::evaluate_query(query, &memories, k, query_tokens, latency_ms);
203            channels
204                .entry(channel.to_string())
205                .or_default()
206                .add(&channel_evaluation);
207        }
208    }
209
210    Ok(CapacityScaleReport {
211        scale,
212        corpus_size: scaled.dataset.corpus.len(),
213        noise_count: scaled.noise_count,
214        corpus_hash: scaled.corpus_hash,
215        fused: fused.finish(),
216        channels: channels
217            .into_iter()
218            .map(|(channel, accumulator)| (channel, accumulator.finish()))
219            .collect(),
220    })
221}
222
223pub(in crate::eval) fn synthesize_capacity_dataset(
224    base: &GoldenDataset,
225    seed: u64,
226    scale: usize,
227) -> Result<ScaledDataset> {
228    validate_capacity_dataset(base)?;
229    ensure!(scale >= 1, "capacity scale must be >= 1");
230    let base_count = base.corpus.len();
231    let target_count = base_count
232        .checked_mul(scale)
233        .context("capacity scale overflows corpus size")?;
234    let noise_count = target_count.saturating_sub(base_count);
235    let mut dataset = base.clone();
236    let mut existing_topic_keys = topic_keys(&dataset.corpus);
237    let projects = corpus_projects(&dataset.corpus);
238
239    for noise_index in 0..noise_count {
240        let memory = noise_memory(seed, scale, noise_index, &projects, &existing_topic_keys)?;
241        if let Some(topic_key) = memory.topic_key.as_ref() {
242            existing_topic_keys.insert(topic_key.clone());
243        }
244        dataset.corpus.push(memory);
245    }
246
247    let corpus_hash = corpus_hash(&dataset.corpus)?;
248    Ok(ScaledDataset {
249        dataset,
250        noise_count,
251        corpus_hash,
252    })
253}
254
255pub(crate) fn normalize_scales(scales: Vec<usize>) -> Result<Vec<usize>> {
256    ensure!(
257        !scales.is_empty(),
258        "capacity eval requires at least one scale"
259    );
260    let mut normalized = BTreeSet::new();
261    for scale in scales {
262        ensure!(scale >= 1, "capacity scale must be >= 1");
263        normalized.insert(scale);
264    }
265    ensure!(
266        normalized.contains(&1),
267        "capacity eval requires scale 1 as the baseline"
268    );
269    Ok(normalized.into_iter().collect())
270}
271
272fn validate_capacity_dataset(dataset: &GoldenDataset) -> Result<()> {
273    ensure!(
274        dataset.has_fixture_corpus(),
275        "capacity eval requires a golden dataset with a fixture corpus"
276    );
277    ensure!(
278        !dataset.queries.is_empty(),
279        "capacity eval requires at least one query"
280    );
281    Ok(())
282}
283
284fn positive_loss(baseline: f64, current: f64) -> f64 {
285    (baseline - current).max(0.0)
286}
287
288#[derive(Default)]
289struct MetricAccumulator {
290    scored_queries: usize,
291    hit_at_k: f64,
292    recall_at_k: f64,
293    ndcg_at_10: f64,
294    evidence_recall_at_k: f64,
295    latencies_ms: Vec<f64>,
296}
297
298impl MetricAccumulator {
299    fn add(&mut self, evaluation: &QueryEvaluation) {
300        self.latencies_ms.push(evaluation.retrieval_latency_ms);
301        if let Some(metrics) = evaluation.metrics.as_ref() {
302            self.add_metrics(metrics);
303        }
304    }
305
306    fn add_metrics(&mut self, metrics: &QueryMetrics) {
307        self.scored_queries += 1;
308        self.hit_at_k += metrics.hit_at_k;
309        self.recall_at_k += metrics.recall_at_k;
310        self.ndcg_at_10 += metrics.ndcg_at_10;
311        self.evidence_recall_at_k += metrics.evidence_recall_at_k;
312    }
313
314    fn finish(self) -> CapacityFusedMetrics {
315        let denominator = self.scored_queries as f64;
316        CapacityFusedMetrics {
317            scored_queries: self.scored_queries,
318            hit_at_k: mean(self.hit_at_k, denominator),
319            recall_at_k: mean(self.recall_at_k, denominator),
320            ndcg_at_10: mean(self.ndcg_at_10, denominator),
321            evidence_recall_at_k: mean(self.evidence_recall_at_k, denominator),
322            p95_latency_ms: positive_zero(golden::run::percentile(self.latencies_ms, 95.0)),
323        }
324    }
325}
326
327fn mean(sum: f64, denominator: f64) -> f64 {
328    if denominator == 0.0 {
329        0.0
330    } else {
331        sum / denominator
332    }
333}
334
335fn positive_zero(value: f64) -> f64 {
336    if value == 0.0 {
337        0.0
338    } else {
339        value
340    }
341}
342
343fn channel_hit_ids(explain: Option<&SearchExplain>) -> BTreeMap<String, Vec<i64>> {
344    explain
345        .map(|explain| {
346            explain
347                .channels
348                .iter()
349                .filter(|channel| channel.enabled)
350                .map(|channel| {
351                    (
352                        channel.name.clone(),
353                        channel
354                            .hits
355                            .iter()
356                            .map(|hit| hit.memory_id)
357                            .collect::<Vec<_>>(),
358                    )
359                })
360                .collect()
361        })
362        .unwrap_or_default()
363}
364
365fn channel_latency_ms(explain: Option<&SearchExplain>, channel: &str) -> f64 {
366    positive_zero(
367        explain
368            .map(|explain| {
369                explain
370                    .timings
371                    .iter()
372                    .filter(|timing| timing.phase == channel)
373                    .map(|timing| timing.elapsed_ms as f64)
374                    .sum()
375            })
376            .unwrap_or_default(),
377    )
378}
379
380fn load_ordered_memories(conn: &Connection, ids: &[i64]) -> Result<Vec<Memory>> {
381    let loaded = crate::memory::get_memories_by_ids_with_suppressed_policy(conn, ids, None, false)?;
382    let id_to_memory = loaded
383        .into_iter()
384        .map(|memory| (memory.id, memory))
385        .collect::<HashMap<_, _>>();
386    Ok(ids
387        .iter()
388        .filter_map(|id| id_to_memory.get(id).cloned())
389        .collect())
390}
391
392fn channel_degradation(
393    baseline: &BTreeMap<String, CapacityFusedMetrics>,
394    largest: &BTreeMap<String, CapacityFusedMetrics>,
395) -> BTreeMap<String, CapacityChannelDegradation> {
396    baseline
397        .iter()
398        .filter_map(|(channel, baseline_metrics)| {
399            let largest_metrics = largest.get(channel)?;
400            Some((
401                channel.clone(),
402                CapacityChannelDegradation {
403                    recall_at_k_loss: positive_loss(
404                        baseline_metrics.recall_at_k,
405                        largest_metrics.recall_at_k,
406                    ),
407                    ndcg_at_10_loss: positive_loss(
408                        baseline_metrics.ndcg_at_10,
409                        largest_metrics.ndcg_at_10,
410                    ),
411                    evidence_recall_at_k_loss: positive_loss(
412                        baseline_metrics.evidence_recall_at_k,
413                        largest_metrics.evidence_recall_at_k,
414                    ),
415                },
416            ))
417        })
418        .collect()
419}
420
421fn topic_keys(corpus: &[GoldenMemory]) -> HashSet<String> {
422    corpus
423        .iter()
424        .filter_map(|memory| memory.topic_key.clone())
425        .collect()
426}
427
428fn corpus_projects(corpus: &[GoldenMemory]) -> Vec<String> {
429    let mut projects = Vec::new();
430    let mut seen = HashSet::new();
431    for memory in corpus {
432        if seen.insert(memory.project.as_str()) {
433            projects.push(memory.project.clone());
434        }
435    }
436    projects
437}
438
439fn noise_memory(
440    seed: u64,
441    scale: usize,
442    noise_index: usize,
443    projects: &[String],
444    existing_topic_keys: &HashSet<String>,
445) -> Result<GoldenMemory> {
446    let project = projects
447        .get(slot(seed, noise_index, 0, projects.len()))
448        .context("capacity noise requires at least one project")?
449        .clone();
450    let memory_type = MEMORY_TYPES[noise_index % MEMORY_TYPES.len()].to_string();
451    let file_path = FILE_PATHS[slot(seed, noise_index, 1, FILE_PATHS.len())];
452    let crate_name = CRATE_NAMES[slot(seed, noise_index, 2, CRATE_NAMES.len())];
453    let error = ERROR_SIGNATURES[slot(seed, noise_index, 3, ERROR_SIGNATURES.len())];
454    let command = COMMANDS[slot(seed, noise_index, 4, COMMANDS.len())];
455    let owner = OWNERS[slot(seed, noise_index, 5, OWNERS.len())];
456    let suffix = splitmix64(seed ^ ((scale as u64) << 32) ^ noise_index as u64);
457    let topic_key = format!("capacity-noise-{seed:x}-{scale}-{noise_index}-{suffix:x}");
458    if existing_topic_keys.contains(&topic_key) {
459        bail!("capacity noise topic key collision: {topic_key}");
460    }
461
462    Ok(GoldenMemory {
463        project,
464        topic_key: Some(topic_key),
465        title: format!("Capacity noise {crate_name} {error}"),
466        content: format!(
467            "Capacity distractor {noise_index}: {owner} investigated {file_path} in crate {crate_name}. \
468             Command `{command}` returned {error}. The follow-up marker is synthetic-noise-{seed:x}-{suffix:x}."
469        ),
470        memory_type,
471        branch: Some("main".to_string()),
472        scope: "project".to_string(),
473        status: "active".to_string(),
474        files: Some(file_path.to_string()),
475        created_at_epoch: Some(1_800_000_000 + noise_index as i64),
476        access_count: Some(0),
477        last_accessed_epoch: None,
478        search_context: None,
479    })
480}
481
482fn slot(seed: u64, index: usize, salt: u64, len: usize) -> usize {
483    debug_assert!(len > 0);
484    let mixed = splitmix64(seed ^ salt.wrapping_mul(0x9E37_79B9_7F4A_7C15) ^ index as u64);
485    (mixed as usize) % len
486}
487
488fn splitmix64(mut value: u64) -> u64 {
489    value = value.wrapping_add(0x9E37_79B9_7F4A_7C15);
490    value = (value ^ (value >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
491    value = (value ^ (value >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
492    value ^ (value >> 31)
493}
494
495fn corpus_hash(corpus: &[GoldenMemory]) -> Result<String> {
496    let bytes = serde_json::to_vec(corpus).context("serialize capacity corpus for hash")?;
497    let mut hasher = Sha256::new();
498    hasher.update(bytes);
499    Ok(format!("sha256:{:x}", hasher.finalize()))
500}
501
502impl Display for CapacityEvalReport {
503    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
504        writeln!(
505            f,
506            "remem eval-capacity - seed={} k={} scales={:?}",
507            self.seed, self.k, self.scale_factors
508        )?;
509        writeln!(f, "dataset: {}", self.dataset_path)?;
510        for scale in &self.scales {
511            writeln!(
512                f,
513                "  {}x: corpus={} noise={} R@{}={:.3} nDCG@10={:.3} evidence@{}={:.3} p95={:.2}ms hash={}",
514                scale.scale,
515                scale.corpus_size,
516                scale.noise_count,
517                self.k,
518                scale.fused.recall_at_k,
519                scale.fused.ndcg_at_10,
520                self.k,
521                scale.fused.evidence_recall_at_k,
522                scale.fused.p95_latency_ms,
523                scale.corpus_hash
524            )?;
525            for (channel, metrics) in &scale.channels {
526                writeln!(
527                    f,
528                    "    {channel}: R@{}={:.3} nDCG@10={:.3} evidence@{}={:.3} p95={:.2}ms",
529                    self.k,
530                    metrics.recall_at_k,
531                    metrics.ndcg_at_10,
532                    self.k,
533                    metrics.evidence_recall_at_k,
534                    metrics.p95_latency_ms
535                )?;
536            }
537        }
538        writeln!(
539            f,
540            "degradation at {}x: R@{} loss={:.3}, nDCG@10 loss={:.3}, evidence@{} loss={:.3}",
541            self.degradation.largest_scale,
542            self.k,
543            self.degradation.fused_recall_at_k_loss,
544            self.degradation.fused_ndcg_at_10_loss,
545            self.k,
546            self.degradation.fused_evidence_recall_at_k_loss
547        )
548    }
549}
550
551#[cfg(test)]
552mod tests {
553    use super::*;
554    use crate::eval::golden::{EvidenceRef, GoldenQuery};
555
556    #[test]
557    fn capacity_synthesis_is_deterministic_for_same_seed_and_scale() -> Result<()> {
558        let dataset = tiny_dataset();
559        let first = synthesize_capacity_dataset(&dataset, 7, 3)?;
560        let second = synthesize_capacity_dataset(&dataset, 7, 3)?;
561        let different_seed = synthesize_capacity_dataset(&dataset, 8, 3)?;
562
563        assert_eq!(first.corpus_hash, second.corpus_hash);
564        assert_eq!(first.noise_count, 4);
565        assert_eq!(first.dataset.corpus.len(), 6);
566        assert_eq!(first.dataset.queries[0].id, dataset.queries[0].id);
567        assert_ne!(first.corpus_hash, different_seed.corpus_hash);
568        Ok(())
569    }
570
571    #[test]
572    fn capacity_report_includes_scale_curve_and_degradation() -> Result<()> {
573        let report = run_capacity_eval_for_dataset(
574            CapacityEvalOptions {
575                dataset_path: "inline-test".to_string(),
576                seed: 9,
577                scales: vec![3, 1],
578                k: 5,
579            },
580            tiny_dataset(),
581        )?;
582
583        assert_eq!(report.scale_factors, vec![1, 3]);
584        assert_eq!(report.base_corpus_size, 2);
585        assert_eq!(report.scales.len(), 2);
586        assert_eq!(report.scales[0].scale, 1);
587        assert_eq!(report.scales[0].noise_count, 0);
588        assert_eq!(report.scales[1].scale, 3);
589        assert_eq!(report.scales[1].noise_count, 4);
590        assert_eq!(report.degradation.largest_scale, 3);
591        assert!(report.scales[0].channels.contains_key("fts"));
592        assert!(report.scales[0].channels.contains_key("vector"));
593        assert!(report.degradation.channels.contains_key("fts"));
594
595        let json = serde_json::to_value(&report)?;
596        assert_eq!(json["seed"], 9);
597        assert_eq!(json["scales"][1]["fused"]["scored_queries"], 2);
598        assert_eq!(json["scales"][1]["channels"]["fts"]["scored_queries"], 2);
599        assert!(json["degradation"]["channels"]["fts"]["recall_at_k_loss"].is_number());
600        assert!(json["omitted_followups"]
601            .as_array()
602            .expect("omitted followups should be an array")
603            .contains(&serde_json::json!("nightly_dashboard_ingestion")));
604        assert!(!json["omitted_followups"]
605            .as_array()
606            .expect("omitted followups should be an array")
607            .contains(&serde_json::json!("per_channel_attribution")));
608        Ok(())
609    }
610
611    #[test]
612    fn capacity_report_quality_is_stable_for_same_seed() -> Result<()> {
613        let dataset = tiny_dataset();
614        let first = run_capacity_eval_for_dataset(
615            CapacityEvalOptions {
616                dataset_path: "inline-test".to_string(),
617                seed: 11,
618                scales: vec![1, 2],
619                k: 5,
620            },
621            dataset.clone(),
622        )?;
623        let second = run_capacity_eval_for_dataset(
624            CapacityEvalOptions {
625                dataset_path: "inline-test".to_string(),
626                seed: 11,
627                scales: vec![1, 2],
628                k: 5,
629            },
630            dataset,
631        )?;
632
633        assert_eq!(quality_signature(&first), quality_signature(&second));
634        Ok(())
635    }
636
637    #[test]
638    fn capacity_scales_require_one_x_baseline() {
639        let err = normalize_scales(vec![2, 3])
640            .expect_err("missing 1x scale should fail")
641            .to_string();
642        assert!(err.contains("requires scale 1"));
643    }
644
645    fn quality_signature(
646        report: &CapacityEvalReport,
647    ) -> Vec<(
648        usize,
649        String,
650        usize,
651        f64,
652        f64,
653        f64,
654        Vec<(String, f64, f64, f64)>,
655    )> {
656        report
657            .scales
658            .iter()
659            .map(|scale| {
660                (
661                    scale.scale,
662                    scale.corpus_hash.clone(),
663                    scale.fused.scored_queries,
664                    scale.fused.recall_at_k,
665                    scale.fused.ndcg_at_10,
666                    scale.fused.evidence_recall_at_k,
667                    scale
668                        .channels
669                        .iter()
670                        .map(|(channel, metrics)| {
671                            (
672                                channel.clone(),
673                                metrics.recall_at_k,
674                                metrics.ndcg_at_10,
675                                metrics.evidence_recall_at_k,
676                            )
677                        })
678                        .collect(),
679                )
680            })
681            .collect()
682    }
683
684    fn tiny_dataset() -> GoldenDataset {
685        GoldenDataset {
686            version: Some("capacity-test".to_string()),
687            description: Some("capacity test fixture".to_string()),
688            corpus: vec![
689                GoldenMemory {
690                    project: "synthetic/capacity".to_string(),
691                    topic_key: Some("capacity-alpha-anchor".to_string()),
692                    title: "Alpha routing anchor".to_string(),
693                    content: "Alpha routing anchor lives in src/alpha.rs".to_string(),
694                    memory_type: "decision".to_string(),
695                    branch: Some("main".to_string()),
696                    scope: "project".to_string(),
697                    status: "active".to_string(),
698                    files: Some("src/alpha.rs".to_string()),
699                    created_at_epoch: Some(1_700_000_000),
700                    access_count: Some(0),
701                    last_accessed_epoch: None,
702                    search_context: None,
703                },
704                GoldenMemory {
705                    project: "synthetic/capacity".to_string(),
706                    topic_key: Some("capacity-beta-anchor".to_string()),
707                    title: "Beta retry anchor".to_string(),
708                    content: "Beta retry anchor belongs to src/beta.rs".to_string(),
709                    memory_type: "bugfix".to_string(),
710                    branch: Some("main".to_string()),
711                    scope: "project".to_string(),
712                    status: "active".to_string(),
713                    files: Some("src/beta.rs".to_string()),
714                    created_at_epoch: Some(1_700_000_010),
715                    access_count: Some(0),
716                    last_accessed_epoch: None,
717                    search_context: None,
718                },
719            ],
720            queries: vec![
721                GoldenQuery {
722                    id: "alpha".to_string(),
723                    query: "Alpha routing anchor".to_string(),
724                    category: "retrieval".to_string(),
725                    slice: Some("capacity_test".to_string()),
726                    hop_path: None,
727                    project: Some("synthetic/capacity".to_string()),
728                    branch: Some("main".to_string()),
729                    memory_type: None,
730                    relevant_ids: vec![],
731                    evidence_refs: vec![EvidenceRef {
732                        topic_key: Some("capacity-alpha-anchor".to_string()),
733                        ..EvidenceRef::default()
734                    }],
735                    expect_abstain: false,
736                    false_premise: false,
737                    notes: None,
738                },
739                GoldenQuery {
740                    id: "beta".to_string(),
741                    query: "Beta retry anchor".to_string(),
742                    category: "retrieval".to_string(),
743                    slice: Some("capacity_test".to_string()),
744                    hop_path: None,
745                    project: Some("synthetic/capacity".to_string()),
746                    branch: Some("main".to_string()),
747                    memory_type: None,
748                    relevant_ids: vec![],
749                    evidence_refs: vec![EvidenceRef {
750                        topic_key: Some("capacity-beta-anchor".to_string()),
751                        ..EvidenceRef::default()
752                    }],
753                    expect_abstain: false,
754                    false_premise: false,
755                    notes: None,
756                },
757            ],
758        }
759    }
760}