1use kimetsu_core::memory::MemoryKind;
2use serde::{Deserialize, Serialize};
3
4use crate::context::{ContextBundle, ContextCapsule};
5
6pub const DEFAULT_BENCHMARK_DATASET: &str = "terminal-bench/terminal-bench-2";
7
8const TERMINAL_BENCH_SLUGS: &[&str] = &[
9 "make-mips-interpreter",
10 "circuit-fibsqrt",
11 "build-pov-ray",
12 "overfull-hbox",
13 "distribution-search",
14 "break-filter-js-from-html",
15 "video-processing",
16 "protein-assembly",
17 "path-tracing",
18 "compile-compcert",
19 "log-summary-date-ranges",
20 "openssl-selfsigned-cert",
21 "dna-assembly",
22 "caffe-cifar-10",
23 "install-windows-3-11",
24 "vulnerable-secret",
25];
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
28#[serde(rename_all = "snake_case")]
29pub enum BenchmarkWarmPolicy {
30 ColdBrain,
31 ReactiveWarm,
32 #[default]
33 FullWarm,
34}
35
36impl BenchmarkWarmPolicy {
37 pub fn parse(value: &str) -> Option<Self> {
38 match value.trim().to_ascii_lowercase().replace('-', "_").as_str() {
39 "" | "full" | "full_warm" | "warm" | "brain_on_warm" => Some(Self::FullWarm),
40 "reactive" | "reactive_warm" | "warm_reactive" | "optional_warm" => {
41 Some(Self::ReactiveWarm)
42 }
43 "cold" | "cold_brain" | "brain_on_cold" => Some(Self::ColdBrain),
44 _ => None,
45 }
46 }
47
48 pub const fn as_str(self) -> &'static str {
49 match self {
50 Self::ColdBrain => "cold_brain",
51 Self::ReactiveWarm => "reactive_warm",
52 Self::FullWarm => "full_warm",
53 }
54 }
55
56 pub const fn playbook_note(self) -> &'static str {
57 match self {
58 Self::ColdBrain => {
59 "Cold brain: memory capsules are intentionally excluded. This measures broker/repo/prior-run grounding without accepted memories."
60 }
61 Self::ReactiveWarm => {
62 "Reactive warm: Kimetsu memory is available when the harness or model asks for it, but task-specific benchmark memory is not required."
63 }
64 Self::FullWarm => {
65 "Full warm: the benchmark playbook is fetched before the task starts and may include task-specific benchmark memories."
66 }
67 }
68 }
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
72#[serde(rename_all = "snake_case")]
73pub enum BenchmarkMemoryRole {
74 #[default]
77 Episodic,
78 SemanticOperator,
80 AntiPattern,
82}
83
84impl BenchmarkMemoryRole {
85 pub fn parse(value: &str) -> Option<Self> {
86 match value.trim().to_ascii_lowercase().replace('-', "_").as_str() {
87 "" | "episodic" | "run" | "task_run" | "outcome" => Some(Self::Episodic),
88 "semantic" | "semantic_operator" | "operator" | "tactic" | "recipe" => {
89 Some(Self::SemanticOperator)
90 }
91 "anti" | "anti_pattern" | "antipattern" | "failure_pattern" | "warning" => {
92 Some(Self::AntiPattern)
93 }
94 _ => None,
95 }
96 }
97
98 pub const fn as_str(self) -> &'static str {
99 match self {
100 Self::Episodic => "episodic",
101 Self::SemanticOperator => "semantic_operator",
102 Self::AntiPattern => "anti_pattern",
103 }
104 }
105
106 pub const fn is_generalizable(self) -> bool {
107 matches!(self, Self::SemanticOperator | Self::AntiPattern)
108 }
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct BenchmarkBrainContext {
113 pub dataset: String,
114 pub task: String,
115 pub task_slug: Option<String>,
116 pub warm_policy: BenchmarkWarmPolicy,
117 pub query: String,
118 pub stage: String,
119 pub budget_tokens: u32,
120 pub used_tokens: u32,
121 pub capsule_count: usize,
122 pub memory_capsule_count: usize,
123 pub benchmark_memory_count: usize,
124 pub generalizable_memory_count: usize,
125 pub episodic_memory_count: usize,
126 pub required_ok: bool,
127 pub playbook_markdown: String,
128 pub capsules: Vec<ContextCapsule>,
129 pub excluded: Vec<ContextCapsule>,
130}
131
132#[derive(Debug, Clone)]
133pub struct BenchmarkMemoryProposal {
134 pub role: BenchmarkMemoryRole,
135 pub text: String,
136 pub task_family: Option<String>,
137 pub applies_to: Vec<String>,
138 pub does_not_apply_to: Vec<String>,
139 pub evidence_for: Vec<String>,
140 pub evidence_against: Vec<String>,
141 pub rationale: String,
142 pub confidence: f32,
143}
144
145#[derive(Debug, Clone, Default)]
146pub struct BenchmarkOutcome {
147 pub task: String,
148 pub dataset: String,
149 pub task_slug: Option<String>,
150 pub warm_policy: BenchmarkWarmPolicy,
151 pub mode: String,
152 pub passed: Option<bool>,
153 pub score: Option<f32>,
154 pub error: Option<String>,
155 pub summary: String,
156 pub commands: Vec<String>,
157 pub pitfalls: Vec<String>,
158 pub verify: Vec<String>,
159 pub cost_usd: Option<f32>,
160 pub duration_seconds: Option<f32>,
161 pub generalization: Option<BenchmarkMemoryProposal>,
162}
163
164pub fn normalize_task_slug(input: &str) -> Option<String> {
165 let lower = input.to_ascii_lowercase();
166 for slug in TERMINAL_BENCH_SLUGS {
167 if lower.contains(slug) {
168 return Some((*slug).to_string());
169 }
170 }
171
172 lower
173 .split(|ch: char| !(ch.is_ascii_alphanumeric() || ch == '-' || ch == '_'))
174 .filter_map(|token| {
175 let token = token.split("__").next().unwrap_or(token);
176 let token = token.trim_matches('-').replace('_', "-");
177 if looks_like_slug(&token) {
178 Some(token)
179 } else {
180 None
181 }
182 })
183 .next()
184}
185
186pub fn benchmark_query(
187 task: &str,
188 dataset: &str,
189 task_slug: Option<&str>,
190 warm_policy: BenchmarkWarmPolicy,
191) -> String {
192 let compact_task = compact_text(task, 1400);
193 match task_slug {
194 Some(slug) => format!(
195 "terminal-bench benchmark dataset:{dataset} warm-policy:{} terminal-bench:{slug} benchmark:{slug} task-slug:{slug} slug-words:{} task: {compact_task}",
196 warm_policy.as_str(),
197 slug.replace('-', " ")
198 ),
199 None => format!(
200 "terminal-bench benchmark dataset:{dataset} warm-policy:{} task: {compact_task}",
201 warm_policy.as_str()
202 ),
203 }
204}
205
206#[allow(clippy::too_many_arguments)]
208pub fn build_benchmark_context(
209 bundle: ContextBundle,
210 task: &str,
211 dataset: &str,
212 query: &str,
213 task_slug: Option<String>,
214 warm_policy: BenchmarkWarmPolicy,
215 require_benchmark_memory: bool,
216 max_capsules: usize,
217) -> BenchmarkBrainContext {
218 let max_capsules = max_capsules.clamp(1, 20);
219 let selected = prioritized_capsules(
220 &bundle.capsules,
221 task_slug.as_deref(),
222 warm_policy,
223 max_capsules,
224 );
225 let memory_capsule_count = selected
226 .iter()
227 .filter(|capsule| capsule.kind == "memory")
228 .count();
229 let benchmark_memory_count = selected
230 .iter()
231 .filter(|capsule| benchmark_memory_matches(capsule, task_slug.as_deref()))
232 .count();
233 let generalizable_memory_count = selected
234 .iter()
235 .filter(|capsule| {
236 benchmark_memory_role(capsule).is_some_and(BenchmarkMemoryRole::is_generalizable)
237 })
238 .count();
239 let episodic_memory_count = selected
240 .iter()
241 .filter(|capsule| benchmark_memory_role(capsule) == Some(BenchmarkMemoryRole::Episodic))
242 .count();
243 let required_ok =
244 !require_benchmark_memory || benchmark_memory_count > 0 || generalizable_memory_count > 0;
245 let playbook_markdown = format_playbook(
246 dataset,
247 task,
248 task_slug.as_deref(),
249 warm_policy,
250 query,
251 required_ok,
252 memory_capsule_count,
253 benchmark_memory_count,
254 generalizable_memory_count,
255 episodic_memory_count,
256 &selected,
257 );
258
259 BenchmarkBrainContext {
260 dataset: dataset.to_string(),
261 task: task.to_string(),
262 task_slug,
263 warm_policy,
264 query: query.to_string(),
265 stage: bundle.stage,
266 budget_tokens: bundle.budget_tokens,
267 used_tokens: bundle.used_tokens,
268 capsule_count: selected.len(),
269 memory_capsule_count,
270 benchmark_memory_count,
271 generalizable_memory_count,
272 episodic_memory_count,
273 required_ok,
274 playbook_markdown,
275 capsules: selected,
276 excluded: bundle.excluded,
277 }
278}
279
280pub fn benchmark_memory_matches(capsule: &ContextCapsule, task_slug: Option<&str>) -> bool {
281 if capsule.kind != "memory" {
282 return false;
283 }
284 let Some(slug) = task_slug else {
285 return false;
286 };
287 let slug = slug.to_ascii_lowercase();
288 let haystack = capsule_text(capsule);
289 haystack.contains(&format!("terminal-bench:{slug}"))
290 || haystack.contains(&format!("benchmark:{slug}"))
291 || haystack.contains(&format!("task-slug:{slug}"))
292 || haystack.contains(&slug)
293}
294
295pub fn benchmark_memory_role(capsule: &ContextCapsule) -> Option<BenchmarkMemoryRole> {
296 if capsule.kind != "memory" {
297 return None;
298 }
299 let haystack = capsule_text(capsule);
300 if has_role_marker(&haystack, BenchmarkMemoryRole::SemanticOperator) {
301 return Some(BenchmarkMemoryRole::SemanticOperator);
302 }
303 if has_role_marker(&haystack, BenchmarkMemoryRole::AntiPattern) {
304 return Some(BenchmarkMemoryRole::AntiPattern);
305 }
306 if has_role_marker(&haystack, BenchmarkMemoryRole::Episodic) {
307 return Some(BenchmarkMemoryRole::Episodic);
308 }
309 if haystack.contains("[terminal-bench:") && haystack.contains("status=") {
310 return Some(BenchmarkMemoryRole::Episodic);
311 }
312 None
313}
314
315fn has_role_marker(haystack: &str, role: BenchmarkMemoryRole) -> bool {
316 let role = role.as_str();
317 haystack.contains(&format!("memory_role={role}"))
318 || haystack.contains(&format!("memory-role={role}"))
319 || haystack.contains(&format!("role={role}"))
320}
321
322pub fn outcome_memory_kind(outcome: &BenchmarkOutcome) -> MemoryKind {
323 if outcome
324 .error
325 .as_ref()
326 .is_some_and(|value| !value.trim().is_empty())
327 {
328 return MemoryKind::FailurePattern;
329 }
330 match outcome.passed {
331 Some(false) => MemoryKind::FailurePattern,
332 Some(true) => MemoryKind::Command,
333 None => MemoryKind::Fact,
334 }
335}
336
337pub fn outcome_memory_text(outcome: &BenchmarkOutcome) -> String {
338 let task_slug = outcome
339 .task_slug
340 .clone()
341 .or_else(|| normalize_task_slug(&outcome.task))
342 .unwrap_or_else(|| "unknown".to_string());
343 let status = match (outcome.passed, outcome.error.as_deref()) {
344 (_, Some(error)) if !error.trim().is_empty() => "error",
345 (Some(true), _) => "pass",
346 (Some(false), _) => "fail",
347 (None, _) => "observed",
348 };
349
350 let mut parts = vec![format!(
351 "[terminal-bench:{task_slug}] dataset={} mode={} status={status}",
352 compact_text(&outcome.dataset, 120),
353 compact_text(&outcome.mode, 80),
354 )];
355 parts.push(format!(
356 "memory_role={}",
357 BenchmarkMemoryRole::Episodic.as_str()
358 ));
359 parts.push(format!("warm_policy={}", outcome.warm_policy.as_str()));
360 if let Some(score) = outcome.score {
361 parts.push(format!("score={score:.3}"));
362 }
363 if let Some(cost_usd) = outcome.cost_usd {
364 parts.push(format!("cost_usd={cost_usd:.4}"));
365 }
366 if let Some(duration) = outcome.duration_seconds {
367 parts.push(format!("duration_seconds={duration:.1}"));
368 }
369 if !outcome.summary.trim().is_empty() {
370 parts.push(format!("Summary: {}", compact_text(&outcome.summary, 500)));
371 }
372 if !outcome.commands.is_empty() {
373 parts.push(format!(
374 "Commands: {}",
375 compact_text(&outcome.commands.join("; "), 350)
376 ));
377 }
378 if !outcome.pitfalls.is_empty() {
379 parts.push(format!(
380 "Pitfalls: {}",
381 compact_text(&outcome.pitfalls.join("; "), 350)
382 ));
383 }
384 if !outcome.verify.is_empty() {
385 parts.push(format!(
386 "Verify: {}",
387 compact_text(&outcome.verify.join("; "), 250)
388 ));
389 }
390 if let Some(error) = outcome
391 .error
392 .as_deref()
393 .filter(|value| !value.trim().is_empty())
394 {
395 parts.push(format!("Error: {}", compact_text(error, 250)));
396 }
397 parts.join(". ")
398}
399
400pub fn proposal_memory_kind(proposal: &BenchmarkMemoryProposal) -> MemoryKind {
401 match proposal.role {
402 BenchmarkMemoryRole::AntiPattern => MemoryKind::FailurePattern,
403 BenchmarkMemoryRole::SemanticOperator | BenchmarkMemoryRole::Episodic => {
404 MemoryKind::Command
405 }
406 }
407}
408
409pub fn proposal_memory_text(
410 outcome: &BenchmarkOutcome,
411 proposal: &BenchmarkMemoryProposal,
412) -> String {
413 let task_slug = outcome
414 .task_slug
415 .clone()
416 .or_else(|| normalize_task_slug(&outcome.task))
417 .unwrap_or_else(|| "unknown".to_string());
418 let mut parts = vec![format!(
419 "[terminal-bench-memory] memory_role={} source_task_slug={} dataset={} mode={}",
420 proposal.role.as_str(),
421 task_slug,
422 compact_text(&outcome.dataset, 120),
423 compact_text(&outcome.mode, 80),
424 )];
425 if let Some(task_family) = proposal
426 .task_family
427 .as_deref()
428 .map(str::trim)
429 .filter(|value| !value.is_empty())
430 {
431 parts.push(format!("task_family={}", compact_text(task_family, 120)));
432 }
433 parts.push(format!("Rule: {}", compact_text(&proposal.text, 700)));
434 if !proposal.applies_to.is_empty() {
435 parts.push(format!(
436 "Applies_to: {}",
437 compact_text(&proposal.applies_to.join("; "), 350)
438 ));
439 }
440 if !proposal.does_not_apply_to.is_empty() {
441 parts.push(format!(
442 "Does_not_apply_to: {}",
443 compact_text(&proposal.does_not_apply_to.join("; "), 350)
444 ));
445 }
446 let evidence_for = if proposal.evidence_for.is_empty() {
447 vec![task_slug]
448 } else {
449 proposal.evidence_for.clone()
450 };
451 parts.push(format!(
452 "Evidence_for: {}",
453 compact_text(&evidence_for.join("; "), 250)
454 ));
455 if !proposal.evidence_against.is_empty() {
456 parts.push(format!(
457 "Evidence_against: {}",
458 compact_text(&proposal.evidence_against.join("; "), 250)
459 ));
460 }
461 if !proposal.rationale.trim().is_empty() {
462 parts.push(format!(
463 "Review_rationale: {}",
464 compact_text(&proposal.rationale, 250)
465 ));
466 }
467 parts.push(
468 "Human_review: pending; accept only if this transfers beyond the source task.".to_string(),
469 );
470 parts.join(". ")
471}
472
473fn prioritized_capsules(
474 capsules: &[ContextCapsule],
475 task_slug: Option<&str>,
476 warm_policy: BenchmarkWarmPolicy,
477 max_capsules: usize,
478) -> Vec<ContextCapsule> {
479 let mut ranked = capsules
480 .iter()
481 .enumerate()
482 .map(|(idx, capsule)| {
483 let role = benchmark_memory_role(capsule);
484 let exact_task = benchmark_memory_matches(capsule, task_slug);
485 let priority =
486 if warm_policy == BenchmarkWarmPolicy::ColdBrain && capsule.kind == "memory" {
487 9
488 } else if role.is_some_and(BenchmarkMemoryRole::is_generalizable) && exact_task {
489 0
490 } else if role.is_some_and(BenchmarkMemoryRole::is_generalizable) {
491 1
492 } else if exact_task {
493 2
494 } else if capsule.kind == "memory" && role == Some(BenchmarkMemoryRole::Episodic) {
495 6
496 } else if capsule.kind == "memory" {
497 3
498 } else {
499 4
500 };
501 (priority, idx, capsule)
502 })
503 .collect::<Vec<_>>();
504 ranked.sort_by(|left, right| {
505 left.0
506 .cmp(&right.0)
507 .then_with(|| left.1.cmp(&right.1))
508 .then_with(|| {
509 right
510 .2
511 .score
512 .partial_cmp(&left.2.score)
513 .unwrap_or(std::cmp::Ordering::Equal)
514 })
515 });
516 ranked
517 .into_iter()
518 .filter(|(_, _, capsule)| {
519 warm_policy != BenchmarkWarmPolicy::ColdBrain || capsule.kind != "memory"
520 })
521 .take(max_capsules)
522 .map(|(_, _, capsule)| capsule.clone())
523 .collect()
524}
525
526#[allow(clippy::too_many_arguments)]
528fn format_playbook(
529 dataset: &str,
530 task: &str,
531 task_slug: Option<&str>,
532 warm_policy: BenchmarkWarmPolicy,
533 query: &str,
534 required_ok: bool,
535 memory_capsule_count: usize,
536 benchmark_memory_count: usize,
537 generalizable_memory_count: usize,
538 episodic_memory_count: usize,
539 capsules: &[ContextCapsule],
540) -> String {
541 let mut out = String::new();
542 out.push_str("# Kimetsu Benchmark Playbook\n\n");
543 out.push_str(&format!("dataset: {dataset}\n"));
544 out.push_str(&format!(
545 "task_slug: {}\n",
546 task_slug.unwrap_or("<not-detected>")
547 ));
548 out.push_str(&format!("warm_policy: {}\n", warm_policy.as_str()));
549 out.push_str(&format!("required_ok: {required_ok}\n"));
550 out.push_str(&format!("memory_capsule_count: {memory_capsule_count}\n"));
551 out.push_str(&format!(
552 "benchmark_memory_count: {benchmark_memory_count}\n"
553 ));
554 out.push_str(&format!(
555 "generalizable_memory_count: {generalizable_memory_count}\n"
556 ));
557 out.push_str(&format!("episodic_memory_count: {episodic_memory_count}\n"));
558 out.push('\n');
559 out.push_str(warm_policy.playbook_note());
560 out.push_str("\nUse these capsules as execution constraints before broad exploration. Prefer accepted semantic_operator and anti_pattern memories first because they are intended to transfer across tasks. Use exact episodic run summaries as evidence, not as dominant instructions.\n\n");
561
562 if capsules.is_empty() {
563 out.push_str("No capsules were retrieved. If this is required mode, inspect Kimetsu brain status and seed or ingest memory before benchmarking.\n\n");
564 } else {
565 for (idx, capsule) in capsules.iter().enumerate() {
566 let role_text = benchmark_memory_role(capsule)
567 .map(|role| format!(" role={}", role.as_str()))
568 .unwrap_or_default();
569 out.push_str(&format!(
570 "{}. [{}{} score={:.3}] {}\n",
571 idx + 1,
572 capsule.kind,
573 role_text,
574 capsule.score,
575 compact_text(&capsule.summary, 500)
576 ));
577 if !capsule.expansion_handle.trim().is_empty() {
578 out.push_str(&format!(" source: {}\n", capsule.expansion_handle));
579 }
580 }
581 out.push('\n');
582 }
583
584 out.push_str("# Retrieval Query\n\n");
585 out.push_str(query);
586 out.push_str("\n\n# Original Task\n\n");
587 out.push_str(&compact_text(task, 1800));
588 out
589}
590
591fn capsule_text(capsule: &ContextCapsule) -> String {
592 let mut text = format!("{} {}", capsule.summary, capsule.expansion_handle);
593 for provenance in &capsule.provenance {
594 text.push(' ');
595 text.push_str(&provenance.source);
596 text.push(' ');
597 text.push_str(&provenance.id);
598 if let Some(excerpt) = &provenance.excerpt {
599 text.push(' ');
600 text.push_str(excerpt);
601 }
602 }
603 text.to_ascii_lowercase()
604}
605
606fn looks_like_slug(token: &str) -> bool {
607 token.len() >= 6
608 && token.contains('-')
609 && !is_generic_fallback_slug(token)
610 && token
611 .bytes()
612 .all(|byte| byte.is_ascii_alphanumeric() || byte == b'-')
613 && token.bytes().any(|byte| byte.is_ascii_alphabetic())
614}
615
616fn is_generic_fallback_slug(token: &str) -> bool {
617 matches!(
618 token,
619 "terminal-bench"
620 | "terminal-bench-2"
621 | "kimetsu-mcp"
622 | "kimetsu-brain"
623 | "codex-kimetsu"
624 | "full-warm"
625 | "reactive-warm"
626 | "cold-brain"
627 | "warm-policy"
628 | "task-slug"
629 | "brain-context"
630 | "benchmark-context"
631 )
632}
633
634fn compact_text(text: &str, max_chars: usize) -> String {
635 let compact = text.split_whitespace().collect::<Vec<_>>().join(" ");
636 if compact.len() <= max_chars {
637 return compact;
638 }
639 let mut truncated = compact.chars().take(max_chars).collect::<String>();
640 truncated.push_str("...");
641 truncated
642}
643
644#[cfg(test)]
645mod tests {
646 use super::*;
647 use crate::context::{ContextBundle, ContextCapsule, ProvenanceRef};
648
649 #[test]
650 fn detects_known_and_suffix_task_slugs() {
651 assert_eq!(
652 normalize_task_slug("compile-compcert__T6g5YAZ"),
653 Some("compile-compcert".to_string())
654 );
655 assert_eq!(
656 normalize_task_slug("Solve the build-pov-ray terminal task"),
657 Some("build-pov-ray".to_string())
658 );
659 }
660
661 #[test]
662 fn ignores_generic_terminal_bench_tokens() {
663 assert_eq!(
664 normalize_task_slug("terminal-bench task: solve the benchmark"),
665 None
666 );
667 assert_eq!(
668 normalize_task_slug("dataset terminal-bench/terminal-bench-2 warm-policy full-warm"),
669 None
670 );
671 }
672
673 #[test]
674 fn playbook_prioritizes_task_memory() {
675 let memory = capsule(
676 "memory",
677 "[terminal-bench:compile-compcert] memory_role=episodic Redirect make logs and patch config.",
678 "memory:1",
679 0.7,
680 );
681 let repo = capsule("repo_file", "src/main.rs", "file:src/main.rs", 0.99);
682 let bundle = ContextBundle {
683 stage: "localization".to_string(),
684 budget_tokens: 4000,
685 used_tokens: 20,
686 capsules: vec![repo, memory],
687 excluded: Vec::new(),
688 skipped: false,
689 top_score: 0.0,
690 top_abs_evidence: 0.0,
691 evidence_coverage: 1.0,
692 uncovered_terms: Vec::new(),
693 chronological: false,
694 known_fact_conflicts: vec![],
695 };
696
697 let context = build_benchmark_context(
698 bundle,
699 "compile-compcert",
700 DEFAULT_BENCHMARK_DATASET,
701 "terminal-bench:compile-compcert",
702 Some("compile-compcert".to_string()),
703 BenchmarkWarmPolicy::FullWarm,
704 true,
705 8,
706 );
707
708 assert!(context.required_ok);
709 assert_eq!(context.benchmark_memory_count, 1);
710 assert!(context.capsules[0].summary.contains("compile-compcert"));
711 assert!(
712 context
713 .playbook_markdown
714 .contains("Kimetsu Benchmark Playbook")
715 );
716 }
717
718 #[test]
719 fn outcome_memory_text_marks_episodic() {
720 let outcome = BenchmarkOutcome {
721 task: "compile-compcert".to_string(),
722 dataset: DEFAULT_BENCHMARK_DATASET.to_string(),
723 mode: "required-kimetsu".to_string(),
724 passed: Some(true),
725 summary: "Configured tools and verified the build.".to_string(),
726 ..BenchmarkOutcome::default()
727 };
728
729 let text = outcome_memory_text(&outcome);
730
731 assert!(text.contains("[terminal-bench:compile-compcert]"));
732 assert!(text.contains("memory_role=episodic"));
733 assert!(text.contains("status=pass"));
734 }
735
736 #[test]
737 fn proposal_memory_text_marks_generalizable_and_review_pending() {
738 let outcome = BenchmarkOutcome {
739 task: "compile-compcert".to_string(),
740 dataset: DEFAULT_BENCHMARK_DATASET.to_string(),
741 mode: "required-kimetsu".to_string(),
742 ..BenchmarkOutcome::default()
743 };
744 let proposal = BenchmarkMemoryProposal {
745 role: BenchmarkMemoryRole::SemanticOperator,
746 text: "For generated-artifact tasks with hidden verifiers, build a small checker and validate randomized cases before finalizing.".to_string(),
747 task_family: Some("generated-artifact-verification".to_string()),
748 applies_to: vec!["tasks with hidden validators".to_string()],
749 does_not_apply_to: vec!["pure installation tasks".to_string()],
750 evidence_for: vec!["compile-compcert".to_string()],
751 evidence_against: Vec::new(),
752 rationale: "The lesson transfers beyond the exact task slug.".to_string(),
753 confidence: 0.82,
754 };
755
756 let text = proposal_memory_text(&outcome, &proposal);
757
758 assert!(text.contains("[terminal-bench-memory]"));
759 assert!(text.contains("memory_role=semantic_operator"));
760 assert!(text.contains("Human_review: pending"));
761 assert!(text.contains("task_family=generated-artifact-verification"));
762 }
763
764 #[test]
765 fn playbook_prioritizes_generalizable_memory_over_exact_episodic() {
766 let repo = capsule("repo_file", "src/main.rs", "file:src/main.rs", 0.99);
767 let semantic = capsule(
768 "memory",
769 "[terminal-bench-memory] memory_role=semantic_operator task_family=generated-artifact Rule: Build a local checker before finalizing.",
770 "memory:semantic",
771 0.5,
772 );
773 let episodic = capsule(
774 "memory",
775 "[terminal-bench:compile-compcert] memory_role=episodic status=pass Commands: ./configure; make -j2.",
776 "memory:episodic",
777 0.9,
778 );
779 let bundle = ContextBundle {
780 stage: "localization".to_string(),
781 budget_tokens: 4000,
782 used_tokens: 20,
783 capsules: vec![repo, episodic, semantic],
784 excluded: Vec::new(),
785 skipped: false,
786 top_score: 0.0,
787 top_abs_evidence: 0.0,
788 evidence_coverage: 1.0,
789 uncovered_terms: Vec::new(),
790 chronological: false,
791 known_fact_conflicts: vec![],
792 };
793
794 let context = build_benchmark_context(
795 bundle,
796 "compile-compcert",
797 DEFAULT_BENCHMARK_DATASET,
798 "terminal-bench:compile-compcert",
799 Some("compile-compcert".to_string()),
800 BenchmarkWarmPolicy::FullWarm,
801 true,
802 8,
803 );
804
805 assert_eq!(context.generalizable_memory_count, 1);
806 assert_eq!(context.episodic_memory_count, 1);
807 assert_eq!(context.benchmark_memory_count, 1);
808 assert!(context.capsules[0].summary.contains("semantic_operator"));
809 assert!(context.playbook_markdown.contains("role=semantic_operator"));
810 assert!(context.playbook_markdown.contains("role=episodic"));
811 }
812
813 #[test]
814 fn required_mode_accepts_generalizable_memory_without_exact_slug() {
815 let semantic = capsule(
816 "memory",
817 "[terminal-bench-memory] memory_role=anti_pattern task_family=generated-artifact Rule: Do not treat compile success as proof; run a behavioral verifier.",
818 "memory:semantic",
819 0.8,
820 );
821 let bundle = ContextBundle {
822 stage: "localization".to_string(),
823 budget_tokens: 4000,
824 used_tokens: 20,
825 capsules: vec![semantic],
826 excluded: Vec::new(),
827 skipped: false,
828 top_score: 0.0,
829 top_abs_evidence: 0.0,
830 evidence_coverage: 1.0,
831 uncovered_terms: Vec::new(),
832 chronological: false,
833 known_fact_conflicts: vec![],
834 };
835
836 let context = build_benchmark_context(
837 bundle,
838 "compile-compcert",
839 DEFAULT_BENCHMARK_DATASET,
840 "terminal-bench:compile-compcert",
841 Some("compile-compcert".to_string()),
842 BenchmarkWarmPolicy::FullWarm,
843 true,
844 8,
845 );
846
847 assert!(context.required_ok);
848 assert_eq!(context.benchmark_memory_count, 0);
849 assert_eq!(context.generalizable_memory_count, 1);
850 }
851
852 #[test]
853 fn cold_brain_excludes_memory_capsules() {
854 let memory = capsule(
855 "memory",
856 "[terminal-bench:compile-compcert] Warm memory.",
857 "memory:1",
858 1.0,
859 );
860 let repo = capsule("repo_file", "src/main.rs", "file:src/main.rs", 0.5);
861 let bundle = ContextBundle {
862 stage: "localization".to_string(),
863 budget_tokens: 4000,
864 used_tokens: 20,
865 capsules: vec![memory, repo],
866 excluded: Vec::new(),
867 skipped: false,
868 top_score: 0.0,
869 top_abs_evidence: 0.0,
870 evidence_coverage: 1.0,
871 uncovered_terms: Vec::new(),
872 chronological: false,
873 known_fact_conflicts: vec![],
874 };
875
876 let context = build_benchmark_context(
877 bundle,
878 "compile-compcert",
879 DEFAULT_BENCHMARK_DATASET,
880 "terminal-bench:compile-compcert",
881 Some("compile-compcert".to_string()),
882 BenchmarkWarmPolicy::ColdBrain,
883 false,
884 8,
885 );
886
887 assert_eq!(context.memory_capsule_count, 0);
888 assert_eq!(context.benchmark_memory_count, 0);
889 assert!(
890 context
891 .capsules
892 .iter()
893 .all(|capsule| capsule.kind != "memory")
894 );
895 assert!(context.playbook_markdown.contains("cold_brain"));
896 }
897
898 fn capsule(kind: &str, summary: &str, handle: &str, score: f32) -> ContextCapsule {
899 ContextCapsule {
900 id: summary.to_string(),
901 kind: kind.to_string(),
902 summary: summary.to_string(),
903 token_estimate: 10,
904 expansion_handle: handle.to_string(),
905 provenance: vec![ProvenanceRef {
906 source: "test".to_string(),
907 id: handle.to_string(),
908 excerpt: Some(summary.to_string()),
909 }],
910 confidence: 1.0,
911 freshness: 1.0,
912 relevance: 1.0,
913 scope_weight: 1.0,
914 score,
915 superseded_hint: false,
916 rerank_policy_tier: 0,
917 claim_revision: None,
918 facts: vec![],
919 rerank_usefulness: None,
920 rerank_trust: None,
921 }
922 }
923}