- Add FEDERATION_STORE_LOCK (tokio::sync::Mutex<()>) guarding every
load-mutate-save cycle in federation/storage.rs, closing the race where
the 90s auto-sync loop's stale pre-removal snapshot could silently
re-save a peer the operator just removed (no error logged anywhere).
- Split load_nodes/save_nodes/tombstone_did/untombstone_did into thin
locked outer wrappers + lock-free *_inner bodies so remove_node and
add_node can hold the guard across their whole tombstone+save critical
section without self-deadlocking (Mutex is not re-entrant).
- Route load_nodes, save_nodes, remove_node, add_node, tombstone_did,
untombstone_did, set_trust_level, and update_node_state through the
lock (set_trust_level pulled forward from Task 2's scope — required for
test_concurrent_writes_do_not_lose_updates, part of Task 1's own
required-green test suite, to pass; documented in SUMMARY).
- Convert save_nodes_inner to an atomic write: serialize to a sibling
nodes.json.tmp in the same directory, then tokio::fs::rename onto the
real path, so a crash mid-write never leaves a partially-written
nodes.json for a concurrent reader.
- Add 3 new regression tests, two of which are fail-first proven: heavy
tokio::spawn-based concurrency (not just tokio::join!, since
remove_node's extra tombstone I/O hop structurally biased a simple
2-task race toward the safe ordering) reliably reproduced both the lost
concurrent write and the removed-node-reappears bug pre-fix; both are
green post-fix along with the existing suite (13/13).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>