Skip to content

Bash-task storage and log hygiene (several small items) #759

Open

Description

@null-axiom

AFT: 0.60.0 observed; code references at v0.61.0 (crates/aft). OpenCode 2, macOS. Rechecked on 0.61.0 (Oct 22): empty session dirs grew to 182 of 210; retention … removed=0 (info) and persisted_gc_delivered_terminal (WARN) still log on every sweep; aft-plugin.log is now 16 MB with no rotation. retired 0 stopped after updating, and no new "giving up deleting" lines since Oct 20. Rechecked on 0.62.0 (48 min of logs): empty session dirs 187 of 219 and still growing; retention … removed=0 90 lines and bash task row deleted WARN 169 lines; plugin log 17 MB, no rotation. No "giving up" lines to confirm or rule out item 3. File:line references are from v0.61.0.

  • Per-session bash-task directories are never removed. bash_background/registry.rs:7125-7245 (cleanup_finished_at_with) deletes a task's bundle but never its parent bash-tasks/<session-hash>/; quarantine GC (:11156-11188) also leaves session dirs. No non-test code removes a session directory. Here: 141 of 162 dirs under opencode/bash-tasks/ are empty. Fix: non-recursive rmdir after the last task is deleted, ignoring ENOTEMPTY, under the creators' lock.
  • Routine work logged at info/warn. db/compression_events.rs:1088 logs bash task retention: … removed=0 at info on every 60 s sweep (db/mod.rs:596-605). db/bash_tasks.rs:168-170 logs bash task row deleted … persisted_gc_delivered_terminal at WARN for every GC'd row (~8k in 5 days here, one per real deletion), which buries real warnings. ("retired 0 undelivered completions" is already gated in 0.61.0.) Fix: debug when nothing changed; info for routine deletions.
  • "giving up deleting … layout not found" leaves the directory. 78 such lines here. A real background task layout not found is caught by is_task_layout_not_found (registry.rs:7199), but an empty task directory makes task_layout_entry_exists (persistence.rs:1709-1726) return true and resolve_uninitialized_task_layout (:1070-1072) fail with a plain OS NotFound, which isn't recognised, so it retries 5 times (FINISHED_DELETE_MAX_ATTEMPTS) and gives up. Fix: map NotFound on control/ / io/ to task_layout_not_found(). (Hypothesis for our log lines; the code path is confirmed.)
  • One unreadable binding file skips its whole batch, every cycle. storage_retention.rs:5310-5331 (prune_bindings) uses ? inside the loop over 32 entries; one truncated or newer-schema retention/roots/*.json aborts the batch, the consumed entries are skipped for the cycle, and the bad file stays. Fix: log, quarantine and continue.
  • Plugin log has no rotation. aft-plugin.log reached 13 MB in 5.5 days, about half periodic no-op lines (retired 0 ×6,187, retention removed=0 ×6,175, perf tick ×2,907, bash_wait_detach detached=false ×3,118).

Activity

  1. null-axiom commented on Oct 22, 2026

    @null-axiom
    Author

    More on the log side, observed on 0.62.0:

    • The plugin log duplicates the engine log. bridge.ts forwards every engine stderr line through logVia, and the engine already writes the same lines to aft-<pid>.log. 54,591 of ~65,000 lines (15.8 of 17.3 MB) in aft-plugin.log are [aft-plugin] [aft] … copies.
    • Levels are flattened. Forwarded engine lines are logged at INFO, so an engine WARN appears as INFO … [aft] WARN …. The plugin log has 65,098 INFO, 107 DEBUG and 1 WARN, so filtering by level can't find real warnings.
    • WARN cannot establish filesystem boundary for checkpoint sweep …/opencode/checkpoints on every engine start: checkpoint.rs calls DeviceBoundary::for_root on a directory that doesn't exist. Treating NotFound as "nothing to sweep" would silence it.
    • Idle perf tick lines still print. The "active" gate in logging.rs counts drain_slices > 0 as activity; 3,635 of 3,864 ticks read watcher={0,0,0} tier2=[none] semantic={0…}.
    • bash_wait_detach: … detached=false logs at INFO on every user message (4,091 over 7 days); logging only detached=true would be enough.
    • Stale writer.lease warnings after a kill. removing filesystem lock … from dead or recycled PID (81 lines) after engines exit without a shutdown line — an expected state after SIGTERM/SIGKILL.
    • Plugin startup repeats per location, including / and the home directory that AFT refuses to index: 662 starts in 7 days, each logging "Config loaded" three times plus the hints and ONNX lines.
    • Unused binary copy. The resolver logs Resolved binary from versioned cache: ~/.cache/aft/bin/v0.62.0/aft, but the running engines come from the npm platform package; the 97 MB cached copy appears unused.
  2. aft-alfonso commented on Oct 23, 2026

    @aft-alfonso

    Thanks for the detailed report. All five points were real, and the fixes are now on main. They'll ship in the next release.

    1. Empty per-session bash-tasks/ folders. Folders left with no task records are now removed during cleanup, unless a task still points at them.
    2. Noisy no-op sweeps. A cleanup pass that finds nothing to do now logs at debug level instead of info or warn.
    3. Cleanup giving up on an empty folder. Cleanup now removes incomplete task folders instead of retrying them and giving up.
    4. One unreadable retention file stalling the batch. An unreadable file is now set aside on its own, and the rest of the batch is processed as usual.
    5. Plugin log size. The plugin log does rotate: at 32 MiB, keeping one backup, and logs from processes that are no longer running are removed after 24 hours. Your 17 MB log was still under the limit. Our documentation said 20 MB with five generations, which was wrong; it now matches the code.

    I'll close this issue once the release is published.

  3. null-axiom commented on Oct 23, 2026

    @null-axiom
    Author

    A few more small items seen on 0.62.0:

    • The generic output cap drops failure lines. A plain command (no specific compressor) printing 3,000 lines with FAILED: at line 1500 and ERROR: at 2200 shows the first 521 and last 21 lines; both failure lines are dropped. The truncation marker, line counts and exit code are honest, but the "raw failure lines preserved" rescue in compress/mod.rs only runs after a specific compressor matches. Applying it to the generic cap would keep errors visible.
    • The cat rewrite refuses the host's approved temp directory and logs WARN bash rewrite rule cat declined … predicate=path_is_safe each time (for $TMPDIR/opencode/…). Either accept that directory or log at debug.
    • A failed edit echoes the whole oldString back in the not-found error (edit_match.rs), so a large old string is repeated into the transcript.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions