Skip to content

cmmd: growth control + daily-tick + fix-count budgets#1

Open
NagyVikt wants to merge 1 commit into
mainfrom
cmmd-hardening-part-1
Open

cmmd: growth control + daily-tick + fix-count budgets#1
NagyVikt wants to merge 1 commit into
mainfrom
cmmd-hardening-part-1

Conversation

@NagyVikt
Copy link
Copy Markdown
Contributor

Summary

Three classes of unbounded resource use the daemon had no guard against, plus two budget caps that lived only in agent prose:

  • history.jsonl grew forever (one row per tick per memory root).
  • /tmp/cmmd-tick-<id>.log agent transcripts accumulated indefinitely.
  • The memory dir's .git/objects grew with every pre-tick snapshot.
  • "≤3 fixes per tick" was advisory; the agent could quietly apply more.
  • No daily ceiling on agent spawns; a stuck-finding-issues loop could burn tokens forever.

What's in the diff

  • History rotation + per-tick-log TTL sweeper + per-MEMORY_ROOT git gc run once per main-loop iteration. New env knobs: HISTORY_MAX_LINES=10000, TICK_LOG_TTL_DAYS=7, GIT_GC_INTERVAL_DAYS=7.
  • Daily-tick counter is UTC-day rolled and persisted in state.json so the ceiling survives restarts. MAX_TICKS_PER_DAY=100 default. When hit, the iteration force-skips and records reason_skipped=MAX_TICKS_PER_DAY=N reached for <date>.
  • Fix-count cap enforced post-spawn by counting git status --porcelain entries vs pre_tick_sha; if exceeded, history::restore(pre_sha) rolls the whole tick back. MAX_FIXES_PER_TICK=3 default.
  • Six new Prometheus counters expose all of the above so a runaway loop is visible before it eats the disk.
  • Zero-dep UTC-date helper (Hinnant's civil-from-days) added to state.rs.

Test plan

  • cargo test --lib — 44 tests pass, 8 new (rotation, sweeper, daily-counter rollover, date anchors)
  • cargo clippy --lib --bins --no-deps — clean
  • cargo build --release — succeeds
  • Daemon restarted on the new binary; first tick ran cleanly; six new counters visible at /metrics
  • Watch cmmd_ticks_blocked_daily_cap_total over a week; verify counter increments when the daily cap is hit
  • Watch cmmd_history_rotations_total over a week; verify history.jsonl.1 exists after first rotation
  • Verify git gc marker file .git/cmmd-last-gc is created in each MEMORY_ROOT after first run

🤖 Generated with Claude Code

Three classes of unbounded resource use the daemon had no guard against:

- `history.jsonl` grew forever (one row per tick per memory root).
- `/tmp/cmmd-tick-<id>.log` agent transcripts accumulated indefinitely.
- The memory dir's `.git/objects` grew with every pre-tick snapshot.

Plus two budget caps that lived only in agent prose, not in code:

- "≤3 fixes per tick" was advisory — agent could quietly apply more.
- No daily ceiling on agent spawns — a stuck-finding-issues loop could
  burn tokens forever.

What this commit adds:

History rotation, per-tick-log TTL sweeper, and per-MEMORY_ROOT `git gc`
all run once per main-loop iteration (history.rs). New env knobs:
HISTORY_MAX_LINES=10000, TICK_LOG_TTL_DAYS=7, GIT_GC_INTERVAL_DAYS=7.

Daily-tick counter is UTC-day rolled and persisted in state.json so the
ceiling survives restarts; MAX_TICKS_PER_DAY=100 default. The agent is
not spawned for the rest of the day once the cap is hit, and the tick
is recorded as `reason_skipped=MAX_TICKS_PER_DAY reached`.

Fix-count cap is enforced post-spawn by counting `git status --porcelain`
entries vs the pre-tick SHA; if exceeded, `history::restore(pre_sha)`
rolls the whole tick back. MAX_FIXES_PER_TICK=3 default.

Six new Prometheus counters expose all of the above
(cmmd_history_rotations_total, cmmd_tick_logs_swept_total,
cmmd_git_gc_runs_total, cmmd_ticks_blocked_daily_cap_total,
cmmd_ticks_reverted_fix_cap_total, cmmd_day_ticks_ran).

Zero-dep UTC-date helper added to state.rs (Hinnant's civil-from-days)
to avoid a chrono dependency for one date string.

44 tests pass (8 new), clippy clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant