1.6 KiB
Adversarial Bug Report: add-outputs-retention
Probed _get_retention and _gc_outputs with non-contract inputs.
A1 — retention as float
_get_retention({"outputs": {"retention": 3.14}}) → int(3.14) = 3. Not garbage but truncating. Acceptable (float is a numeric type; int() rounds toward zero). Not a regression.
A2 — retention as bool
_get_retention({"outputs": {"retention": True}}) → int(True) = 1. A user who sets retention: true intending "unlimited" gets 1 (wrong — they wanted 0). But bool is technically a subclass of int in Python; int(True) = 1 is documented behavior. Acceptable edge case — the user would need to write JSON true, which json.loads reads as True. Not blocking; int(True) = 1 is a narrow retention but valid.
A3 — retention string "inf" falls back to 20
_get_retention({"outputs": {"retention": "inf"}}) → int("inf") raises ValueError → caught → 20 with WARNING. Correct per SPEC D-O4.
A4 — GC handles large gaps in tick indices
Files tick1-*.json and tick100-*.json with nothing in between: max_seen=100, retention=20, cutoff=100-20+1=81. Deletes tick1- but keeps tick100-. Correct — the gap is intentional (maybe intermittent ticks). Not a bug.
A5 — Non-tick files tick-nope.md preserved
Hyphen-no-number prefix tick-nope.md doesn't match ^tick(\d+)-. Preserved. Correct per D-O6.
A6 — Empty outputs dir
_gc_outputs on dir with 0 files or missing dir returns cleanly. No crash. Confirmed.
No BLOCKERS
All adversarial cases produce deterministic documented results. Proceed to doc_review.