Skip to main content

mecha_core/
harness.rs

1//! Harness rumination: the candidate store behind `mecha harness`, and the
2//! override layer an accepted change rides in.
3//!
4//! This is the persistence half of the self-improvement loop. `diagnose.rs`
5//! proposes, `replay_run.rs` measures, `candidate.rs` judges — and until this
6//! module existed, the judgement evaporated when the process exited, which is
7//! why the loop had never run unattended: a nightly that proposes changes
8//! nobody reads is worse than no nightly. Here a proposal becomes a record,
9//! the record carries the measurement it was decided from, and an accepted
10//! change becomes one entry in an overrides file that any run can read and
11//! one command can revert.
12//!
13//! ## The override layer, and why the user always wins
14//!
15//! `overrides.toml` is applied to a [`Config`] **after defaults and before
16//! any file layer** ([`apply_accepted_overrides`], called from
17//! `Config::load` / `load_global`). Layering is assignment, so a key the
18//! user names in `config.toml` overwrites the accepted value — an override
19//! only ever fills space the user left empty. That is §13.3's "reversible"
20//! made structural: reverting is deleting a line, and nothing the loop does
21//! can pin a value against the user's own file.
22//!
23//! ## The closed set
24//!
25//! [`OverrideKey`] is the same closed set `mecha eval --ab-config` accepts,
26//! defined once here so the measurement arm and the acceptance arm cannot
27//! drift apart — a candidate measured under one applier and applied under
28//! another would be accepted on evidence about a different change. An open
29//! set would let a proposer reach settings whose effect replay cannot
30//! measure, or worse, security settings, which are never a measurement's to
31//! decide. An unknown key in the overrides file is **skipped loudly and
32//! never applied** — the file is machine-written, but a boundary that trusts
33//! its writer is not one.
34
35use crate::candidate::{ChangeClass, Judgement, Metric};
36use crate::config::Config;
37use crate::message::Effort;
38use anyhow::{Context, Result};
39use serde::{Deserialize, Serialize};
40use std::os::fd::AsRawFd;
41use std::path::{Path, PathBuf};
42
43// ─── The closed set ─────────────────────────────────────────────────────────
44
45/// The configuration knobs an automated proposer may move. Closed on
46/// purpose; see the module docs.
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum OverrideKey {
49    CompactAtTokens,
50    MaxTurns,
51    MaxOutputTokens,
52    Effort,
53}
54
55impl OverrideKey {
56    pub const ALL: [OverrideKey; 4] = [
57        OverrideKey::CompactAtTokens,
58        OverrideKey::MaxTurns,
59        OverrideKey::MaxOutputTokens,
60        OverrideKey::Effort,
61    ];
62
63    pub fn parse(key: &str) -> Option<OverrideKey> {
64        match key {
65            "compact_at_tokens" => Some(OverrideKey::CompactAtTokens),
66            "max_turns" => Some(OverrideKey::MaxTurns),
67            "max_output_tokens" => Some(OverrideKey::MaxOutputTokens),
68            "effort" => Some(OverrideKey::Effort),
69            _ => None,
70        }
71    }
72
73    pub fn as_str(self) -> &'static str {
74        match self {
75            OverrideKey::CompactAtTokens => "compact_at_tokens",
76            OverrideKey::MaxTurns => "max_turns",
77            OverrideKey::MaxOutputTokens => "max_output_tokens",
78            OverrideKey::Effort => "effort",
79        }
80    }
81
82    /// The keys, for an error message that names the whole set.
83    pub fn names() -> String {
84        Self::ALL
85            .iter()
86            .map(|k| k.as_str())
87            .collect::<Vec<_>>()
88            .join(", ")
89    }
90}
91
92/// A `KEY=VALUE` change, parsed and value-validated. The value is kept as the
93/// canonical string it parsed from, so one shape serialises to the overrides
94/// file and re-parses on load through the same validation.
95#[derive(Debug, Clone, PartialEq)]
96pub struct ConfigChange {
97    pub key: OverrideKey,
98    pub value: String,
99}
100
101/// Parse a proposal's `KEY=VALUE` into the closed set, validating the value.
102///
103/// Refusal is the common case and the safe one: a proposal whose change does
104/// not parse here is not a config change this loop can measure or apply, so
105/// it goes to a human instead.
106pub fn parse_change(spec: &str) -> Result<ConfigChange> {
107    let (key, value) = spec
108        .split_once('=')
109        .with_context(|| format!("expected KEY=VALUE, got `{spec}`"))?;
110    let key = key.trim();
111    let value = value.trim();
112    let key = OverrideKey::parse(key).with_context(|| {
113        format!(
114            "`{key}` is not in the closed override set ({})",
115            OverrideKey::names()
116        )
117    })?;
118    let canonical = match key {
119        OverrideKey::CompactAtTokens => {
120            let n: u64 = value
121                .parse()
122                .with_context(|| format!("compact_at_tokens takes a number, got `{value}`"))?;
123            anyhow::ensure!(
124                n >= 1000,
125                "compact_at_tokens below 1000 would compact on nearly every turn"
126            );
127            n.to_string()
128        }
129        OverrideKey::MaxTurns => {
130            let n: u32 = value
131                .parse()
132                .with_context(|| format!("max_turns takes a number, got `{value}`"))?;
133            anyhow::ensure!(n >= 1, "max_turns must be at least 1");
134            n.to_string()
135        }
136        OverrideKey::MaxOutputTokens => {
137            let n: u64 = value
138                .parse()
139                .with_context(|| format!("max_output_tokens takes a number, got `{value}`"))?;
140            anyhow::ensure!(n >= 1, "max_output_tokens must be at least 1");
141            n.to_string()
142        }
143        OverrideKey::Effort => value
144            .parse::<Effort>()
145            .map_err(|e| anyhow::anyhow!("{e}"))?
146            .as_str()
147            .to_string(),
148    };
149    Ok(ConfigChange {
150        key,
151        value: canonical,
152    })
153}
154
155impl ConfigChange {
156    /// Apply onto an [`crate::config::AgentConfig`]. The value was validated
157    /// at parse time; a value that no longer parses (a hand-edited file) is
158    /// an error the caller reports, never a silent skip-and-apply-half.
159    pub fn apply_to_agent(&self, agent: &mut crate::config::AgentConfig) -> Result<()> {
160        match self.key {
161            OverrideKey::CompactAtTokens => agent.compact_at_tokens = Some(self.value.parse()?),
162            OverrideKey::MaxTurns => agent.max_turns = self.value.parse()?,
163            OverrideKey::MaxOutputTokens => agent.max_output_tokens = Some(self.value.parse()?),
164            OverrideKey::Effort => {
165                agent.effort = Some(self.value.parse().map_err(|e| anyhow::anyhow!("{e}"))?)
166            }
167        }
168        Ok(())
169    }
170
171    pub fn spec(&self) -> String {
172        format!("{}={}", self.key.as_str(), self.value)
173    }
174}
175
176// ─── Candidates ─────────────────────────────────────────────────────────────
177
178/// Waiting on a human — unmeasurable by replay, or measured without a verdict
179/// strong enough to act alone.
180pub const STATUS_STAGED: &str = "staged";
181/// Cleared the full gate; the override is live.
182pub const STATUS_ACCEPTED: &str = "accepted";
183/// Measured worse, or a guardrail moved, or a human said no.
184pub const STATUS_REJECTED: &str = "rejected";
185/// Was accepted, then a human took the override back out.
186pub const STATUS_REVERTED: &str = "reverted";
187
188/// One proposed harness change, from diagnosis through disposal.
189///
190/// The record keeps everything the decision was made from — the evidence
191/// brief, the prediction, the paired tallies — because "is this loop actually
192/// helping" has to be answerable from the store rather than from impression.
193/// Statuses are strings, not an enum, on the wire-format rule: a status this
194/// version does not know must not make the record unreadable to `list`.
195#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct HarnessCandidate {
197    pub id: String,
198    pub created_at: String,
199    pub class: ChangeClass,
200    /// The change as the diagnostician wrote it — `KEY=VALUE` for config.
201    pub change: String,
202    /// The metric it predicted this would reduce.
203    pub metric: Metric,
204    pub rationale: String,
205    /// The counters brief the diagnostician reasoned from. Machine-authored.
206    pub evidence: String,
207    /// Model whose corpus was diagnosed, and whose sessions were replayed.
208    #[serde(default)]
209    pub model: Option<String>,
210    /// `staged` | `accepted` | `rejected` | `reverted`.
211    pub status: String,
212    #[serde(default)]
213    pub measurement: Option<Measurement>,
214    #[serde(default)]
215    pub resolved_at: Option<String>,
216    /// Why it sits where it does, human-readable.
217    #[serde(default)]
218    pub reason: Option<String>,
219}
220
221impl HarnessCandidate {
222    /// Whether this candidate still needs a person to look at it.
223    pub fn pending(&self) -> bool {
224        self.status == STATUS_STAGED
225    }
226}
227
228#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
229pub struct TallyRecord {
230    pub wins: usize,
231    pub losses: usize,
232    pub ties: usize,
233}
234
235impl std::fmt::Display for TallyRecord {
236    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
237        write!(f, "{}+ {}- {}=", self.wins, self.losses, self.ties)
238    }
239}
240
241/// What the counterfactual replay measured, kept whole on the candidate.
242#[derive(Debug, Clone, Serialize, Deserialize)]
243pub struct Measurement {
244    pub measured_at: String,
245    pub model: String,
246    /// `accept` | `propose` | `reject` — the gate's own verdict, which is not
247    /// the candidate's status: a `propose` verdict stages for a human.
248    pub disposition: String,
249    /// The gate's reason, empty for accept.
250    pub reason: String,
251    pub selection: TallyRecord,
252    pub holdout: TallyRecord,
253    pub work_baseline: u64,
254    pub work_candidate: u64,
255    /// Session ids paired and judged.
256    pub episodes: Vec<String>,
257    /// Sessions dropped because an arm left the recording — a divergent
258    /// replay's stats describe a truncated run, and scoring one would let a
259    /// behaviour-visible change be graded on the fraction it tracked.
260    pub diverged: Vec<String>,
261    /// Sessions that could not be replayed at all (unreadable, no recorded
262    /// calls, tool surface moved). Never evidence for either arm.
263    pub skipped: usize,
264}
265
266impl Measurement {
267    pub fn record(
268        judgement: &Judgement,
269        model: &str,
270        measured_at: String,
271        episodes: Vec<String>,
272        diverged: Vec<String>,
273        skipped: usize,
274    ) -> Measurement {
275        use crate::candidate::Disposition;
276        let (disposition, reason) = match &judgement.disposition {
277            Disposition::Accept => ("accept", String::new()),
278            Disposition::Propose(r) => ("propose", r.clone()),
279            Disposition::Reject(r) => ("reject", r.clone()),
280        };
281        let tally = |t: &crate::candidate::Tally| TallyRecord {
282            wins: t.wins,
283            losses: t.losses,
284            ties: t.ties,
285        };
286        Measurement {
287            measured_at,
288            model: model.to_string(),
289            disposition: disposition.to_string(),
290            reason,
291            selection: tally(&judgement.selection),
292            holdout: tally(&judgement.holdout),
293            work_baseline: judgement.work_baseline,
294            work_candidate: judgement.work_candidate,
295            episodes,
296            diverged,
297            skipped,
298        }
299    }
300}
301
302// ─── The store ──────────────────────────────────────────────────────────────
303
304/// One accepted override: the line in `overrides.toml`, with its provenance.
305#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
306pub struct AcceptedOverride {
307    /// Canonical [`OverrideKey`] string. Kept as a string on the wire-format
308    /// rule; [`OverrideKey::parse`] is re-applied on every load.
309    pub key: String,
310    pub value: String,
311    /// The candidate this acceptance came from.
312    pub candidate: String,
313    pub accepted_at: String,
314}
315
316#[derive(Debug, Default, Serialize, Deserialize)]
317struct OverridesFile {
318    #[serde(default, rename = "override")]
319    overrides: Vec<AcceptedOverride>,
320}
321
322/// `~/.mecha/learning/harness/` — candidates and the overrides file.
323pub struct HarnessStore {
324    root: PathBuf,
325}
326
327impl HarnessStore {
328    /// Beside the learning store's other artifacts, so `MECHA_LEARNING_DIR`
329    /// relocates the whole learning surface together.
330    pub fn default_root() -> Result<PathBuf> {
331        Ok(crate::learning::LearningStore::default_root()?.join("harness"))
332    }
333
334    pub fn open(root: impl Into<PathBuf>) -> Result<HarnessStore> {
335        let root = root.into();
336        crate::create_private_dir(&root.join("candidates"))
337            .with_context(|| format!("creating {}", root.display()))?;
338        Ok(HarnessStore { root })
339    }
340
341    pub fn open_default() -> Result<HarnessStore> {
342        Self::open(Self::default_root()?)
343    }
344
345    pub fn root(&self) -> &Path {
346        &self.root
347    }
348
349    /// Write (or rewrite) one candidate, atomically — a nightly pass and a
350    /// `list` in a terminal must never meet over a half-written file.
351    pub fn write(&self, c: &HarnessCandidate) -> Result<()> {
352        let path = self.root.join("candidates").join(format!("{}.json", c.id));
353        let tmp = path.with_extension("json.tmp");
354        std::fs::write(&tmp, serde_json::to_string_pretty(c)?)?;
355        std::fs::rename(&tmp, &path)?;
356        Ok(())
357    }
358
359    /// Every candidate, oldest first. Unreadable files are skipped with a
360    /// warning — one bad record must not hide the store.
361    pub fn all(&self) -> Result<Vec<HarnessCandidate>> {
362        let dir = self.root.join("candidates");
363        if !dir.is_dir() {
364            return Ok(Vec::new());
365        }
366        let mut out = Vec::new();
367        for entry in std::fs::read_dir(&dir)? {
368            let path = entry?.path();
369            if path.extension().and_then(|e| e.to_str()) != Some("json") {
370                continue;
371            }
372            match serde_json::from_str(&std::fs::read_to_string(&path)?) {
373                Ok(c) => out.push(c),
374                Err(e) => tracing::warn!("skipping unreadable candidate {}: {e}", path.display()),
375            }
376        }
377        out.sort_by(|a: &HarnessCandidate, b: &HarnessCandidate| a.id.cmp(&b.id));
378        Ok(out)
379    }
380
381    /// Find one candidate by id or unique prefix. Ambiguity is an error
382    /// rather than a guess, same as session lookup.
383    pub fn find(&self, id: &str) -> Result<HarnessCandidate> {
384        let all = self.all()?;
385        let matches: Vec<&HarnessCandidate> = all.iter().filter(|c| c.id.starts_with(id)).collect();
386        match matches.len() {
387            0 => anyhow::bail!("no candidate matching `{id}`"),
388            1 => Ok(matches[0].clone()),
389            n => anyhow::bail!(
390                "`{id}` matches {n} candidates: {}",
391                matches
392                    .iter()
393                    .map(|c| c.id.as_str())
394                    .collect::<Vec<_>>()
395                    .join(", ")
396            ),
397        }
398    }
399
400    pub fn overrides_path(&self) -> PathBuf {
401        self.root.join("overrides.toml")
402    }
403
404    /// The currently accepted overrides, in file order.
405    pub fn overrides(&self) -> Result<Vec<AcceptedOverride>> {
406        let path = self.overrides_path();
407        if !path.exists() {
408            return Ok(Vec::new());
409        }
410        let text = std::fs::read_to_string(&path)
411            .with_context(|| format!("reading {}", path.display()))?;
412        let file: OverridesFile =
413            toml::from_str(&text).with_context(|| format!("parsing {}", path.display()))?;
414        Ok(file.overrides)
415    }
416
417    /// Install an override, replacing any earlier one on the same key.
418    /// Returns what it replaced, so the acceptance can record the reversal.
419    pub fn set_override(&self, ov: AcceptedOverride) -> Result<Option<AcceptedOverride>> {
420        let _lock = self.lock()?;
421        let mut all = self.overrides()?;
422        let replaced = all
423            .iter()
424            .position(|o| o.key == ov.key)
425            .map(|i| all.remove(i));
426        all.push(ov);
427        self.write_overrides(&all)?;
428        Ok(replaced)
429    }
430
431    /// Remove the override on `key`, returning it if there was one. Removal
432    /// returns the key to whatever the user's own config layers say — the
433    /// candidate files keep the history.
434    pub fn remove_override(&self, key: &str) -> Result<Option<AcceptedOverride>> {
435        let _lock = self.lock()?;
436        let mut all = self.overrides()?;
437        let removed = all.iter().position(|o| o.key == key).map(|i| all.remove(i));
438        if removed.is_some() {
439            self.write_overrides(&all)?;
440        }
441        Ok(removed)
442    }
443
444    fn write_overrides(&self, all: &[AcceptedOverride]) -> Result<()> {
445        let path = self.overrides_path();
446        let file = OverridesFile {
447            overrides: all.to_vec(),
448        };
449        let tmp = path.with_extension("toml.tmp");
450        std::fs::write(&tmp, toml::to_string_pretty(&file)?)?;
451        std::fs::rename(&tmp, &path)?;
452        Ok(())
453    }
454
455    /// Advisory flock over override mutations — a nightly acceptance and a
456    /// human `revert` are both read-modify-write on one file.
457    fn lock(&self) -> Result<std::fs::File> {
458        let file = std::fs::OpenOptions::new()
459            .create(true)
460            .truncate(false)
461            .write(true)
462            .open(self.root.join(".lock"))?;
463        // SAFETY: flock on an fd we own, held open by the returned guard.
464        if unsafe { libc::flock(file.as_raw_fd(), libc::LOCK_EX) } != 0 {
465            return Err(std::io::Error::last_os_error()).context("locking the harness store");
466        }
467        Ok(file)
468    }
469
470    /// Mint a candidate id: sortable timestamp plus a sub-second tail, so
471    /// two candidates minted close together cannot collide.
472    pub fn mint_id() -> String {
473        let now = chrono::Utc::now();
474        format!(
475            "hc-{}-{:04x}",
476            now.format("%Y%m%dT%H%M%S"),
477            (now.timestamp_subsec_nanos() ^ std::process::id()) & 0xffff
478        )
479    }
480}
481
482// ─── The config layer ───────────────────────────────────────────────────────
483
484/// Apply the accepted overrides at the default location onto a
485/// just-defaulted [`Config`]. Called from `Config::load` / `load_global`
486/// before any file layer merges, so the user's own config always wins.
487///
488/// Best-effort by design: an unreadable or malformed overrides file warns
489/// and applies nothing, because a performance knob must never be the reason
490/// every run fails to start. Contrast the sandbox, where silent degradation
491/// removes a boundary — an override that fails to apply leaves the config
492/// exactly as the user wrote it.
493pub fn apply_accepted_overrides(cfg: &mut Config) {
494    let Ok(root) = HarnessStore::default_root() else {
495        return;
496    };
497    apply_overrides_file(cfg, &root.join("overrides.toml"));
498}
499
500/// The testable half: apply one overrides file onto a config.
501pub fn apply_overrides_file(cfg: &mut Config, path: &Path) {
502    if !path.exists() {
503        return;
504    }
505    let text = match std::fs::read_to_string(path) {
506        Ok(t) => t,
507        Err(e) => {
508            tracing::warn!("harness overrides unreadable ({}): {e}", path.display());
509            return;
510        }
511    };
512    let file: OverridesFile = match toml::from_str(&text) {
513        Ok(f) => f,
514        Err(e) => {
515            tracing::warn!(
516                "harness overrides malformed ({}): {e} — applying none",
517                path.display()
518            );
519            return;
520        }
521    };
522    for ov in file.overrides {
523        // Re-validated on every load: the closed set is enforced where the
524        // value is *used*, not only where it was written.
525        let change = match parse_change(&format!("{}={}", ov.key, ov.value)) {
526            Ok(c) => c,
527            Err(e) => {
528                tracing::warn!(
529                    "harness override `{}={}` skipped: {e:#} (from candidate {})",
530                    ov.key,
531                    ov.value,
532                    ov.candidate
533                );
534                continue;
535            }
536        };
537        if let Err(e) = change.apply_to_agent(&mut cfg.agent) {
538            tracing::warn!(
539                "harness override `{}` failed to apply: {e:#}",
540                change.spec()
541            );
542        }
543    }
544}
545
546#[cfg(test)]
547mod tests {
548    use super::*;
549
550    fn temp_dir() -> std::path::PathBuf {
551        let dir = std::env::temp_dir()
552            .join("mecha-harness-test")
553            .join(uuid::Uuid::new_v4().to_string());
554        std::fs::create_dir_all(&dir).unwrap();
555        dir
556    }
557
558    #[test]
559    fn the_closed_set_refuses_everything_outside_it() {
560        // The keys eval's --ab-config accepts, and nothing else.
561        assert!(parse_change("compact_at_tokens=24000").is_ok());
562        assert!(parse_change("max_turns=30").is_ok());
563        assert!(parse_change("max_output_tokens=8000").is_ok());
564        assert!(parse_change("effort=low").is_ok());
565
566        // The settings a proposer must never reach.
567        for hostile in [
568            "sandbox=none",
569            "trifecta=allow",
570            "outbox.tools=",
571            "context_window=999999",
572            "temperature=2.0",
573        ] {
574            assert!(parse_change(hostile).is_err(), "{hostile} must be refused");
575        }
576        // And a shape that is not KEY=VALUE at all.
577        assert!(parse_change("just prose").is_err());
578    }
579
580    #[test]
581    fn values_are_validated_not_just_typed() {
582        assert!(parse_change("compact_at_tokens=1").is_err());
583        assert!(parse_change("max_turns=0").is_err());
584        assert!(parse_change("max_turns=notanumber").is_err());
585        assert!(parse_change("effort=extreme").is_err());
586        // Whitespace tolerated, value canonicalised.
587        let c = parse_change(" effort = LOW ").unwrap();
588        assert_eq!(c.value, "low");
589    }
590
591    #[test]
592    fn overrides_apply_beneath_the_user_and_unknown_keys_are_skipped() {
593        let dir = temp_dir();
594        let path = dir.join("overrides.toml");
595        std::fs::write(
596            &path,
597            r#"
598[[override]]
599key = "compact_at_tokens"
600value = "24000"
601candidate = "hc-test"
602accepted_at = "2026-08-22T00:00:00Z"
603
604[[override]]
605key = "sandbox"
606value = "none"
607candidate = "hc-evil"
608accepted_at = "2026-08-22T00:00:00Z"
609
610[[override]]
611key = "max_turns"
612value = "notanumber"
613candidate = "hc-corrupt"
614accepted_at = "2026-08-22T00:00:00Z"
615"#,
616        )
617        .unwrap();
618
619        let mut cfg = Config::default();
620        apply_overrides_file(&mut cfg, &path);
621        // The known, valid key applied.
622        assert_eq!(cfg.agent.compact_at_tokens, Some(24000));
623        // The unknown key was skipped, not smuggled anywhere.
624        // The corrupt value was skipped, and the default survived.
625        assert_eq!(cfg.agent.max_turns, 40);
626    }
627
628    #[test]
629    fn a_malformed_overrides_file_applies_nothing() {
630        let dir = temp_dir();
631        let path = dir.join("overrides.toml");
632        std::fs::write(&path, "this is not toml [[[").unwrap();
633        let mut cfg = Config::default();
634        let before = cfg.agent.max_turns;
635        apply_overrides_file(&mut cfg, &path);
636        assert_eq!(cfg.agent.max_turns, before);
637    }
638
639    #[test]
640    fn set_and_remove_override_round_trip_and_replacement_is_returned() {
641        let dir = temp_dir();
642        let store = HarnessStore::open(&dir).unwrap();
643
644        let first = AcceptedOverride {
645            key: "compact_at_tokens".into(),
646            value: "24000".into(),
647            candidate: "hc-a".into(),
648            accepted_at: "2026-08-22T00:00:00Z".into(),
649        };
650        assert!(store.set_override(first.clone()).unwrap().is_none());
651
652        // Same key again: replaced, and the replacement is reported so the
653        // acceptance can record it.
654        let second = AcceptedOverride {
655            key: "compact_at_tokens".into(),
656            value: "20000".into(),
657            candidate: "hc-b".into(),
658            ..first.clone()
659        };
660        let replaced = store.set_override(second).unwrap();
661        assert_eq!(replaced.unwrap().candidate, "hc-a");
662
663        let all = store.overrides().unwrap();
664        assert_eq!(all.len(), 1);
665        assert_eq!(all[0].value, "20000");
666
667        // And the file it wrote applies.
668        let mut cfg = Config::default();
669        apply_overrides_file(&mut cfg, &store.overrides_path());
670        assert_eq!(cfg.agent.compact_at_tokens, Some(20000));
671
672        // Revert: removed, returned, and the file no longer applies it.
673        let removed = store.remove_override("compact_at_tokens").unwrap();
674        assert_eq!(removed.unwrap().value, "20000");
675        let mut cfg = Config::default();
676        apply_overrides_file(&mut cfg, &store.overrides_path());
677        assert_eq!(cfg.agent.compact_at_tokens, None);
678        // Removing what is not there is a no-op, not an error.
679        assert!(store
680            .remove_override("compact_at_tokens")
681            .unwrap()
682            .is_none());
683    }
684
685    #[test]
686    fn candidates_round_trip_and_an_unknown_status_still_loads() {
687        let dir = temp_dir();
688        let store = HarnessStore::open(&dir).unwrap();
689        let c = HarnessCandidate {
690            id: "hc-20260822T000000-0001".into(),
691            created_at: "2026-08-22T00:00:00Z".into(),
692            class: ChangeClass::Config,
693            change: "compact_at_tokens=24000".into(),
694            metric: Metric::CutShort,
695            rationale: "runs are dying at the ceiling".into(),
696            evidence: "runs: 64".into(),
697            model: Some("qwen3.6-35b-a3b".into()),
698            status: STATUS_STAGED.into(),
699            measurement: None,
700            resolved_at: None,
701            reason: None,
702        };
703        store.write(&c).unwrap();
704        let read = store.find("hc-2026").unwrap();
705        assert_eq!(read.change, c.change);
706        assert!(read.pending());
707
708        // A status minted by a future version must not unread the record.
709        let mut future = c.clone();
710        future.id = "hc-20260823T000000-0002".into();
711        future.status = "escalated".into();
712        store.write(&future).unwrap();
713        assert_eq!(store.all().unwrap().len(), 2);
714    }
715}