Summary
hooks/rules/tentacle.py _read_edits() migrates a legacy flat-set tentacle-edits marker into a legacy bucket, stamping each entry with t = now() on every read. Because the deny path returns before _write_edits() persists the migration, the marker stays in legacy format and is re-stamped fresh on every read, so _prune_ttl() (24 h TTL) never drops the entries.
Impact (Facts)
- A poisoned marker accumulated 2032 stale build-artifact paths that permanently triggered
TENTACLE REQUIRED: 2032 files across 774 modules, blocking legitimate small (e.g. 3-file) edits.
- The docstring states legacy entries should "expire naturally after 24 h" — current behavior contradicts it.
Root cause
Using now() as the legacy timestamp on each read perpetually refreshes the TTL.
Fix
Stamp legacy-migrated entries with the marker file's mtime instead of now(), so they expire ~24 h after the marker was last written (matches the docstring intent).
Acceptance criteria
Summary
hooks/rules/tentacle.py_read_edits()migrates a legacy flat-settentacle-editsmarker into alegacybucket, stamping each entry witht = now()on every read. Because the deny path returns before_write_edits()persists the migration, the marker stays in legacy format and is re-stamped fresh on every read, so_prune_ttl()(24 h TTL) never drops the entries.Impact (Facts)
TENTACLE REQUIRED: 2032 files across 774 modules, blocking legitimate small (e.g. 3-file) edits.Root cause
Using
now()as the legacy timestamp on each read perpetually refreshes the TTL.Fix
Stamp legacy-migrated entries with the marker file's
mtimeinstead ofnow(), so they expire ~24 h after the marker was last written (matches the docstring intent).Acceptance criteria
_prune_ttl.tests/test_hooks.pySection 17 passes.