fix(opencode): clean up tool output by file mtime, not wrapped id timestamps

Merged
PI
2mo ago
+10-8

Issue for this PR

Issues are disabled on this fork; defect documented below.

Type of change

  • Bug fix

What does this PR do?

Truncate.cleanup decided file age by decoding the timestamp back out of the tool-output filename id. The id encoder packs timestamp_ms × 4096 into 6 bytes, but current epoch-ms × 4096 needs ~53 bits, so ids silently wrap modulo 2^36 ms ≈ 795 days and Identifier.timestamp() returns epoch-mod-795-days. A wrap boundary fell ~7.7 days ago, landing between the CI test's 10-day-old file and the 7-day cutoff: the old file decodes as ~795 days in the future, cleanup keeps it, and the Truncate > cleanup test fails deterministically on dev (it would self-heal in ~3 days and recur every ~2.2 years; production cleanup mis-keeps old files across every wrap window).

Fix: age files by fs.stat mtime against Date.now() − RETENTION — no id decoding at all. Id encoding/ordering is untouched (ordering only ever compares the same truncated form, which is why nothing else breaks at the wrap).

How did you verify your code works?

  • The previously-failing test now sets the old file's mtime via fs.utimes and passes 19/19 twice, on a date where the id-decode approach deterministically fails.
  • bun run typecheck clean; bun run lint 0 errors.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

🤖 Generated with Claude Code


Summary by cubic

Clean up tool outputs by file mtime instead of decoding timestamps from tool_* ids. The old cleanup used id-decoded timestamps that wrap every ~795 days and mis-kept old files; the new cleanup compares fs.stat mtime to the retention cutoff.

  • Replace id timestamp checks with mtime: compute cutoff = Date.now() - RETENTION, stat each file, and delete when mtime < cutoff. If stat or mtime is unavailable, keep the file; ignore deletion errors.
  • Keep id encoding and ordering unchanged; only cleanup behavior changes.
  • Update tests to set the old file’s mtime via fs.utimes and remove reliance on timestamped ids.