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