capabilities: read() filters but no longer persists the prune

Operator ruling: repair belongs on the write path, not the read path.
read() still drops unrecognised names from what it returns (with a
warn!) so a stale name is never honoured, but it no longer writes the
pruned form back — the file heals the next time set_caps()/write() run,
which prune anyway since they're fed a map read() already filtered.

Refs #4474
This commit is contained in:
atlas 2026-09-17 20:59:43 +02:00 committed by mara
commit 968db59208

View file

@ -65,9 +65,10 @@ fn prune_unknown(map: &mut BTreeMap<String, Vec<String>>) -> bool {
/// Read the per-agent capability map. Returns an empty map when the
/// file is absent or unparsable — callers treat a missing entry as
/// "no extra capabilities". Any entry that isn't a recognised
/// [`Capability`] is dropped (with a `warn!`) and the pruned form is
/// written back so the file heals rather than carrying the junk
/// forever.
/// [`Capability`] is dropped (with a `warn!`) from what's returned —
/// a stale or typo'd name is never honoured — but `read` itself never
/// writes; the on-disk file only gets repaired the next time something
/// calls `write`/`set_caps` anyway.
#[must_use]
pub fn read() -> BTreeMap<String, Vec<String>> {
let path = capabilities_path();
@ -75,11 +76,7 @@ pub fn read() -> BTreeMap<String, Vec<String>> {
return BTreeMap::new();
};
let mut map: BTreeMap<String, Vec<String>> = serde_json::from_str(&raw).unwrap_or_default();
if prune_unknown(&mut map) {
// Best-effort: on write failure the caller still gets the
// pruned in-memory map, and the next read retries the repair.
let _ = write(&map);
}
prune_unknown(&mut map);
map
}