Skip to content

feat(bin): opt-in idle-worker pre-cache-expiry compaction (config/idle-compact) #3336

Description

@NewAiCoder

Motivation

On a subscription-billed Claude account the prompt cache has a one-hour TTL. A crewmate that has been idle for longer than that (parked at a gate, waiting on an owner decision, done and awaiting merge) reprocesses its entire context uncached on the next steer, which is real quota burn - and on a busy fleet those revivals add up to a large share of the daily budget.

Proposal: opt-in idle-worker pre-cache-expiry compaction

An opt-in housekeeping pass that compacts a genuinely idle Claude crewmate while its cache is still warm, so a later revival is cheap. Ships inert exactly like Relay: an absent, gitignored config/idle-compact means no behavior change anywhere. When present, its first non-empty line is the idle threshold in whole minutes (recommended 30); an empty-but-present file enables the documented default; a malformed value behaves like an absent file so a typo can never fail the watcher loop.

Shared owner bin/fm-idle-compact.sh, called from both bin/fm-watch.sh's main loop and bin/fm-supervise-daemon.sh's housekeeping tick on their existing cadence (one classifier, two callers - the same shape as fm-classify-lib.sh).

Eligibility is the strict intersection of:

  • kind != secondmate and harness is claude (the only harness with a verified /compact today);
  • bin/fm-crew-state.sh's reconciled state is parked, done, blocked, or paused - never working;
  • the task's last activity of any kind (meta, status log, turn-ended marker, pane-activity stamp) is at least the threshold old;
  • a live safety gate immediately before typing: fm_busy_classify reports an exact idle verdict and the composer reads affirmatively empty; a busy pane, an unproven verdict, or a non-empty composer defers to the next sweep.

A durable per-task marker (state/.idle-compact-<task>) drives a 4-phase state machine so one idle episode produces at most one compaction:

  1. eligible -> guarded message asking the crewmate to write its open decision keys, current gate/step, next actions, and key file paths to data/<id>/precompact-notes.md (survives teardown);
  2. notes present -> send /compact;
  3. after the compaction -> steer the crewmate to re-read its notes;
  4. the marker doubles as the activity stamp the next idle window is measured from, so a compacted worker is not re-compacted until it has been active again.

Everything is verified by the existing daemon test shard plus a new tests/fm-daemon.test.sh case; docs in docs/configuration.md ("Idle-worker pre-compaction"), docs/architecture.md, docs/scripts.md, and docs/verification/runtime-backends.md.

Relationship to #2341

#2341 standardizes durable pre-compaction task notes as part of the worker brief contract. This proposal is the trigger side: it produces exactly that note before compacting and re-reads it afterward, so the two compose - #2341's note format could become the file this pass asks for.

Reference implementation

A working implementation on top of main (single commit, ~860 lines incl. tests and docs) is at https://github.com/NewAiCoder/firstmate/tree/keep/idle-compact-eec826e - happy to rebase it onto current main and open a PR if the shape is welcome, or to adapt it to whatever contract you prefer.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions