Highlights
🔢 Prune-counter integrity (stats fix + loop containment)
The guard recorded prune savings on projected numbers before the deferred write was confirmed — so a deferred, read-only, or looping cycle that never actually wrote still incremented the global prune/tokens counters every interval. This was the in-the-wild prune-counter spike (timed to the 1.8.18 guard rollout). Savings are now recorded exactly once per persisted prune, across both the terminate-first reload path and the overflow path; futile/looping cycles record nothing.
🛡️ Prune-safety defense-in-depth (PR #114, @ynaamane + C8)
Post-prune structural validation (validate_post_prune, checks C1–C8), floor preservation, and last-of-type metadata-singleton protection (incl. permission-mode, the original unresumable-session root cause):
- C1 baseline-relative parent resolution, C2 orphan-shell-aware root preservation, C4–C7 singleton protection.
- C8 (new): a surviving
tool_usewhose pairedtool_resultwas pruned is rejected (a danglingtool_useis structurally valid but unresumable).
🪟 Windows guard fixes (@GravyaDev)
- #112: auto-prune
os.replaceonto a host-held transcript now defers gracefully instead of crashing (WinError 5). - #113: pidfile publish uses
os.replaceso the guard starts cleanly over a stale.pid(WinError 183).
🧹 Review nits
- background-poll-collapse: per-message protection re-check (defense-in-depth).
- floor:
preserve_last_k_turnscounts real conversational turns, not tool-result envelopes.
Thanks to @ynaamane, @GravyaDev, and @stephenfeather. Full QA: multi-agent adversarial review + 1100+ tests green.