Files
automaton/tasks/complete/add-outputs-retention/IMPLEMENTATION.md
T

2.6 KiB

Implementation: add-outputs-retention

SCOPE

Add loop.json outputs.retention field (default 20) to bound growth of the outputs/ directory. GC runs after step 10 inside _loop_lock, deleting tick groups older than the retention window. Source: add-loop-runner/BUG_REPORT.md O5.

FILES TOUCHED

  • scripts/loop-runner.py
    • Added _get_retention(cfg) -> int: reads cfg.get("outputs", {}).get("retention", 20). Non-int types fall back to 20 with WARNING. Negative values are coerced to 0 (unlimited) with WARNING.
    • Added _gc_outputs(loop_path, retention): lists outputs/, finds max tick index from filenames matching ^tick(\d+)-, computes cutoff = max_seen - retention + 1, deletes files with tick index < cutoff. Non-tick files (README.txt, etc.) are preserved. Errors logged as WARNING via _append_tick_log and swallowed.
    • Modified cmd_tick: calls _get_retention(cfg) + _gc_outputs(loop_path, retention) after step 10 (_write_state_loop) and before step 11 (tick log), inside the _loop_lock block.
    • Updated docstring step list: added 10.5. GC outputs/....
  • templates/loops/self-improvement/loop.json
    • Added "outputs": {"retention": 20} block.

BUG FOUND AND FIXED INLINE

Off-by-one in GC formula: the initial implementation used cutoff = max_seen - retention, which kept retention + 1 tick groups (21 instead of 20 for retention=20). Fixed to cutoff = max_seen - retention + 1. Test test_gc_keeps_recent_deletes_old caught this (expected 20 kept, got 21 remaining → obvious failure when the remaining-count length check triggered).

DECISIONS LOCKED

  • D-O1: retention counts tick GROUPS (all tick{N}-* files), not individual files.
  • D-O2: GC runs INSIDE _loop_lock critical section (after state write, before tick log).
  • D-O3: Default 20.
  • D-O4: 0 = unlimited (no GC). Negative coerces to 0 with WARNING.
  • D-O5: GC based on outputs/ filenames (max_seen), NOT state.iteration_count.
  • D-O6: Regex ^tick(\d+)-. Non-matching files preserved.
  • D-O7: GC failure → WARNING log + swallow.

TESTS

New file tests/test_outputs_retention.py — 13 tests across 2 classes:

  • TestGetRetention (5): default main, explicit value, negative→0, non-int→20, None cfg→20.
  • TestGcOutputs (8): deletes old keeps recent, retention=0 skip, retention>count, missing dir, non-tick files preserved, unrelated tick-foo prefix preserved, single tick, error path.

TEST COUNT

  • Baseline: 469 passed (post-harden-parse-verdict).
  • New: +13 in tests/test_outputs_retention.py.
  • Final: 482 passed, 0 regressions.