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 })
479}
480
481fn slot(seed: u64, index: usize, salt: u64, len: usize) -> usize {
482 debug_assert!(len > 0);
483 let mixed = splitmix64(seed ^ salt.wrapping_mul(0x9E37_79B9_7F4A_7C15) ^ index as u64);
484 (mixed as usize) % len
485}
486
487fn splitmix64(mut value: u64) -> u64 {
488 value = value.wrapping_add(0x9E37_79B9_7F4A_7C15);
489 value = (value ^ (value >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
490 value = (value ^ (value >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
491 value ^ (value >> 31)
492}
493
494fn corpus_hash(corpus: &[GoldenMemory]) -> Result<String> {
495 let bytes = serde_json::to_vec(corpus).context("serialize capacity corpus for hash")?;
496 let mut hasher = Sha256::new();
497 hasher.update(bytes);
498 Ok(format!("sha256:{:x}", hasher.finalize()))
499}
500
501impl Display for CapacityEvalReport {
502 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
503 writeln!(
504 f,
505 "remem eval-capacity - seed={} k={} scales={:?}",
506 self.seed, self.k, self.scale_factors
507 )?;
508 writeln!(f, "dataset: {}", self.dataset_path)?;
509 for scale in &self.scales {
510 writeln!(
511 f,
512 " {}x: corpus={} noise={} R@{}={:.3} nDCG@10={:.3} evidence@{}={:.3} p95={:.2}ms hash={}",
513 scale.scale,
514 scale.corpus_size,
515 scale.noise_count,
516 self.k,
517 scale.fused.recall_at_k,
518 scale.fused.ndcg_at_10,
519 self.k,
520 scale.fused.evidence_recall_at_k,
521 scale.fused.p95_latency_ms,
522 scale.corpus_hash
523 )?;
524 for (channel, metrics) in &scale.channels {
525 writeln!(
526 f,
527 " {channel}: R@{}={:.3} nDCG@10={:.3} evidence@{}={:.3} p95={:.2}ms",
528 self.k,
529 metrics.recall_at_k,
530 metrics.ndcg_at_10,
531 self.k,
532 metrics.evidence_recall_at_k,
533 metrics.p95_latency_ms
534 )?;
535 }
536 }
537 writeln!(
538 f,
539 "degradation at {}x: R@{} loss={:.3}, nDCG@10 loss={:.3}, evidence@{} loss={:.3}",
540 self.degradation.largest_scale,
541 self.k,
542 self.degradation.fused_recall_at_k_loss,
543 self.degradation.fused_ndcg_at_10_loss,
544 self.k,
545 self.degradation.fused_evidence_recall_at_k_loss
546 )
547 }
548}
549
550#[cfg(test)]
551mod tests {
552 use super::*;
553 use crate::eval::golden::{EvidenceRef, GoldenQuery};
554
555 #[test]
556 fn capacity_synthesis_is_deterministic_for_same_seed_and_scale() -> Result<()> {
557 let dataset = tiny_dataset();
558 let first = synthesize_capacity_dataset(&dataset, 7, 3)?;
559 let second = synthesize_capacity_dataset(&dataset, 7, 3)?;
560 let different_seed = synthesize_capacity_dataset(&dataset, 8, 3)?;
561
562 assert_eq!(first.corpus_hash, second.corpus_hash);
563 assert_eq!(first.noise_count, 4);
564 assert_eq!(first.dataset.corpus.len(), 6);
565 assert_eq!(first.dataset.queries[0].id, dataset.queries[0].id);
566 assert_ne!(first.corpus_hash, different_seed.corpus_hash);
567 Ok(())
568 }
569
570 #[test]
571 fn capacity_report_includes_scale_curve_and_degradation() -> Result<()> {
572 let report = run_capacity_eval_for_dataset(
573 CapacityEvalOptions {
574 dataset_path: "inline-test".to_string(),
575 seed: 9,
576 scales: vec![3, 1],
577 k: 5,
578 },
579 tiny_dataset(),
580 )?;
581
582 assert_eq!(report.scale_factors, vec![1, 3]);
583 assert_eq!(report.base_corpus_size, 2);
584 assert_eq!(report.scales.len(), 2);
585 assert_eq!(report.scales[0].scale, 1);
586 assert_eq!(report.scales[0].noise_count, 0);
587 assert_eq!(report.scales[1].scale, 3);
588 assert_eq!(report.scales[1].noise_count, 4);
589 assert_eq!(report.degradation.largest_scale, 3);
590 assert!(report.scales[0].channels.contains_key("fts"));
591 assert!(report.scales[0].channels.contains_key("vector"));
592 assert!(report.degradation.channels.contains_key("fts"));
593
594 let json = serde_json::to_value(&report)?;
595 assert_eq!(json["seed"], 9);
596 assert_eq!(json["scales"][1]["fused"]["scored_queries"], 2);
597 assert_eq!(json["scales"][1]["channels"]["fts"]["scored_queries"], 2);
598 assert!(json["degradation"]["channels"]["fts"]["recall_at_k_loss"].is_number());
599 assert!(json["omitted_followups"]
600 .as_array()
601 .expect("omitted followups should be an array")
602 .contains(&serde_json::json!("nightly_dashboard_ingestion")));
603 assert!(!json["omitted_followups"]
604 .as_array()
605 .expect("omitted followups should be an array")
606 .contains(&serde_json::json!("per_channel_attribution")));
607 Ok(())
608 }
609
610 #[test]
611 fn capacity_report_quality_is_stable_for_same_seed() -> Result<()> {
612 let dataset = tiny_dataset();
613 let first = run_capacity_eval_for_dataset(
614 CapacityEvalOptions {
615 dataset_path: "inline-test".to_string(),
616 seed: 11,
617 scales: vec![1, 2],
618 k: 5,
619 },
620 dataset.clone(),
621 )?;
622 let second = run_capacity_eval_for_dataset(
623 CapacityEvalOptions {
624 dataset_path: "inline-test".to_string(),
625 seed: 11,
626 scales: vec![1, 2],
627 k: 5,
628 },
629 dataset,
630 )?;
631
632 assert_eq!(quality_signature(&first), quality_signature(&second));
633 Ok(())
634 }
635
636 #[test]
637 fn capacity_scales_require_one_x_baseline() {
638 let err = normalize_scales(vec![2, 3])
639 .expect_err("missing 1x scale should fail")
640 .to_string();
641 assert!(err.contains("requires scale 1"));
642 }
643
644 fn quality_signature(
645 report: &CapacityEvalReport,
646 ) -> Vec<(
647 usize,
648 String,
649 usize,
650 f64,
651 f64,
652 f64,
653 Vec<(String, f64, f64, f64)>,
654 )> {
655 report
656 .scales
657 .iter()
658 .map(|scale| {
659 (
660 scale.scale,
661 scale.corpus_hash.clone(),
662 scale.fused.scored_queries,
663 scale.fused.recall_at_k,
664 scale.fused.ndcg_at_10,
665 scale.fused.evidence_recall_at_k,
666 scale
667 .channels
668 .iter()
669 .map(|(channel, metrics)| {
670 (
671 channel.clone(),
672 metrics.recall_at_k,
673 metrics.ndcg_at_10,
674 metrics.evidence_recall_at_k,
675 )
676 })
677 .collect(),
678 )
679 })
680 .collect()
681 }
682
683 fn tiny_dataset() -> GoldenDataset {
684 GoldenDataset {
685 version: Some("capacity-test".to_string()),
686 description: Some("capacity test fixture".to_string()),
687 corpus: vec![
688 GoldenMemory {
689 project: "synthetic/capacity".to_string(),
690 topic_key: Some("capacity-alpha-anchor".to_string()),
691 title: "Alpha routing anchor".to_string(),
692 content: "Alpha routing anchor lives in src/alpha.rs".to_string(),
693 memory_type: "decision".to_string(),
694 branch: Some("main".to_string()),
695 scope: "project".to_string(),
696 status: "active".to_string(),
697 files: Some("src/alpha.rs".to_string()),
698 created_at_epoch: Some(1_700_000_000),
699 access_count: Some(0),
700 last_accessed_epoch: None,
701 },
702 GoldenMemory {
703 project: "synthetic/capacity".to_string(),
704 topic_key: Some("capacity-beta-anchor".to_string()),
705 title: "Beta retry anchor".to_string(),
706 content: "Beta retry anchor belongs to src/beta.rs".to_string(),
707 memory_type: "bugfix".to_string(),
708 branch: Some("main".to_string()),
709 scope: "project".to_string(),
710 status: "active".to_string(),
711 files: Some("src/beta.rs".to_string()),
712 created_at_epoch: Some(1_700_000_010),
713 access_count: Some(0),
714 last_accessed_epoch: None,
715 },
716 ],
717 queries: vec![
718 GoldenQuery {
719 id: "alpha".to_string(),
720 query: "Alpha routing anchor".to_string(),
721 category: "retrieval".to_string(),
722 slice: Some("capacity_test".to_string()),
723 hop_path: None,
724 project: Some("synthetic/capacity".to_string()),
725 branch: Some("main".to_string()),
726 memory_type: None,
727 relevant_ids: vec![],
728 evidence_refs: vec![EvidenceRef {
729 topic_key: Some("capacity-alpha-anchor".to_string()),
730 ..EvidenceRef::default()
731 }],
732 expect_abstain: false,
733 false_premise: false,
734 notes: None,
735 },
736 GoldenQuery {
737 id: "beta".to_string(),
738 query: "Beta retry anchor".to_string(),
739 category: "retrieval".to_string(),
740 slice: Some("capacity_test".to_string()),
741 hop_path: None,
742 project: Some("synthetic/capacity".to_string()),
743 branch: Some("main".to_string()),
744 memory_type: None,
745 relevant_ids: vec![],
746 evidence_refs: vec![EvidenceRef {
747 topic_key: Some("capacity-beta-anchor".to_string()),
748 ..EvidenceRef::default()
749 }],
750 expect_abstain: false,
751 false_premise: false,
752 notes: None,
753 },
754 ],
755 }
756 }
757}