What AutoDream Does Behind the Scenes When Claude Code Is Idle
The article analyzes AutoDream, Claude Code’s idle‑time background maintenance system that detects workspace entropy, quantifies it, and runs a four‑stage semantic cleanup pipeline using LLMs, with constraints on idle detection, token budget, and transparent git‑tracked logs.
01 | Problem Definition: Your Workspace Is Slowly Decaying
Long‑term use of Claude Code leads to growing memory files, duplicated tool definitions, and outdated project‑structure snapshots, which degrade response quality. This is described as workspace entropy, analogous to the second law of thermodynamics: a closed system inevitably becomes disordered.
Claude Code’s designers address this with a mechanism called AutoDream .
02 | Industry Survey: How Other Systems Perform Background Maintenance
Before designing AutoDream, the article reviews existing solutions:
Git GC: Reference Compression and Object Cleanup
Git’s git gc packs loose objects, removes unreachable objects, and compresses reflogs. It triggers when the number of loose objects exceeds a threshold (default 6700). This is a count‑based entropy detection but operates only on data structures, not semantics.
Database VACUUM: Space Reclamation and Statistics Update
PostgreSQL and SQLite VACUUM reclaim space from deleted rows and update optimizer statistics. Autovacuum runs automatically based on the proportion of dead tuples. Like Git GC, it is structural, not semantic.
macOS Spotlight: Incremental Indexing
Spotlight’s mds and mdworker listen for file‑system events (FSEvents) and update the inverted index in the background with low priority, only when files change. This event‑driven approach avoids unnecessary work and provides partial semantic awareness.
Human Sleep: Memory Consolidation
Neuroscience shows that during slow‑wave sleep the brain replays memories, abstracts them into general knowledge, and prunes irrelevant synaptic connections. This active, semantic reorganization inspires AutoDream’s design.
03 | Design Conclusion: Why the "Sleep Consolidation" Metaphor
The metaphor captures four constraints:
Constraint 1 – Work Only When Not Needed
AutoDream starts after an idle timeout (default five minutes), similar to how the brain consolidates only during sleep.
userLastInteraction → startTimer → if timeout (5 min) → AutoDream startsConstraint 2 – Low Priority Execution
Tasks run with a low‑priority queue (e.g., nice 19) and a token budget, ensuring they do not consume the LLM API quota needed for active work.
Constraint 3 – Semantic Understanding
AutoDream must merge memory entries that refer to the same concept and detect outdated comments, a capability only an LLM can provide.
Constraint 4 – Transparency and Audibility
Each run generates a dream log that records what was changed, deleted, or merged, allowing users to review or roll back changes via git.
04 | Entropy Quantification: Measuring Workspace Disorder
AutoDream defines an Entropy Score composed of weighted metrics:
Memory age score – older, rarely accessed entries contribute more entropy.
Duplication score – Jaccard similarity of text fragments.
Comment density score – excessive comment density (>40%) indicates high entropy.
Snapshot drift score – deviation between the stored project‑structure snapshot and the actual file system.
The TypeScript implementation provides functions calcMemoryAgeScore, calcDuplicationScore, and calcCommentDensityScore, which are combined with weights (0.35, 0.30, 0.20, 0.15) to produce totalEntropy. When totalEntropy exceeds 0.6, AutoDream proceeds with cleanup.
05 | Core Mechanism: AutoDream’s Sleep‑Consolidation Pipeline
The pipeline consists of four stages:
Evaluate current entropy.
Plan a prioritized task queue based on metric thresholds.
Execute tasks respecting the token budget and concurrency limit.
Generate a dream log and recompute entropy.
Key TypeScript snippets illustrate the implementation:
// Constants
const IDLE_THRESHOLD_MS = 5 * 60 * 1000; // 5 min idle
const ENTROPY_TRIGGER = 0.6;
const TOKEN_BUDGET_PER_RUN = 8000;
const MAX_CONCURRENT_TASKS = 2;
interface DreamTask {
type: 'memory-cleanup' | 'dedup-merge' | 'snapshot-rebuild' | 'startup-summary';
priority: number; // 1‑10, lower = higher priority
estimatedTokens: number;
}
class AutoDream {
private lastInteractionAt = new Date();
private idleTimer: NodeJS.Timeout | null = null;
private isRunning = false;
private limit = pLimit(MAX_CONCURRENT_TASKS);
onUserInteraction() {
this.lastInteractionAt = new Date();
if (this.idleTimer) clearTimeout(this.idleTimer);
this.idleTimer = setTimeout(() => this.triggerDream(), IDLE_THRESHOLD_MS);
}
private async triggerDream() {
if (this.isRunning) return;
console.log('[AutoDream] idle detected, evaluating entropy...');
this.isRunning = true;
try {
const metrics = await computeWorkspaceEntropy(process.cwd(), await this.loadMemoryEntries());
if (metrics.totalEntropy < ENTROPY_TRIGGER) {
console.log(`[AutoDream] entropy ${metrics.totalEntropy.toFixed(2)} < ${ENTROPY_TRIGGER}, no action`);
return;
}
console.log(`[AutoDream] entropy ${metrics.totalEntropy.toFixed(2)} → running consolidation`);
const tasks = this.planDreamTasks(metrics);
const results = await this.executeTasks(tasks);
const metricsAfter = await computeWorkspaceEntropy(process.cwd(), await this.loadMemoryEntries());
const log = this.buildDreamLog(metrics, metricsAfter, results);
await this.saveDreamLog(log);
console.log(`[AutoDream] completed. entropy: ${metrics.totalEntropy.toFixed(2)} → ${metricsAfter.totalEntropy.toFixed(2)}`);
} finally {
this.isRunning = false;
}
}
private planDreamTasks(metrics: EntropyMetrics): DreamTask[] {
const tasks: DreamTask[] = [];
if (metrics.memoryAgeScore > 0.5) tasks.push({type: 'memory-cleanup', priority: 1, estimatedTokens: 2000});
if (metrics.duplicationScore > 0.4) tasks.push({type: 'dedup-merge', priority: 2, estimatedTokens: 3000});
if (metrics.snapshotDriftScore > 0.3) tasks.push({type: 'snapshot-rebuild', priority: 3, estimatedTokens: 1500});
tasks.push({type: 'startup-summary', priority: 4, estimatedTokens: 1000});
return tasks.sort((a, b) => a.priority - b.priority);
}
private async executeTasks(tasks: DreamTask[]): Promise<DreamTaskResult[]> {
let remainingBudget = TOKEN_BUDGET_PER_RUN;
const results: DreamTaskResult[] = [];
for (const task of tasks) {
if (task.estimatedTokens > remainingBudget) {
console.log(`[AutoDream] token budget insufficient, skipping ${task.type}`);
continue;
}
const result = await this.limit(() => this.executeTask(task));
results.push(result);
remainingBudget -= result.tokensUsed;
}
return results;
}
private async executeTask(task: DreamTask): Promise<DreamTaskResult> {
switch (task.type) {
case 'memory-cleanup':
return this.runMemoryCleanup(task);
case 'dedup-merge':
return this.runDedupMerge(task);
case 'snapshot-rebuild':
return this.runSnapshotRebuild(task);
case 'startup-summary':
return this.runStartupSummary(task);
}
}
// Mock implementations illustrate the result shape
private async runMemoryCleanup(task: DreamTask): Promise<DreamTaskResult> {
return {task, success: true, changes: ['deleted 3 stale memory entries', 'merged 2 duplicate definitions'], tokensUsed: 1800};
}
private async runDedupMerge(task: DreamTask): Promise<DreamTaskResult> {
return {task, success: true, changes: ['merged 2 duplicate tool definitions'], tokensUsed: 2400};
}
private async runSnapshotRebuild(task: DreamTask): Promise<DreamTaskResult> {
return {task, success: true, changes: ['rebuilt project‑structure snapshot', 'updated 42 file‑path indexes'], tokensUsed: 1200};
}
private async runStartupSummary(task: DreamTask): Promise<DreamTaskResult> {
return {task, success: true, changes: ['generated startup brief: .claude/startup-brief.md'], tokensUsed: 900};
}
private buildDreamLog(before: EntropyMetrics, after: EntropyMetrics, results: DreamTaskResult[]): DreamLog {
const now = new Date().toISOString();
return {
startedAt: new Date(now),
completedAt: new Date(now),
entropyBefore: before.totalEntropy,
entropyAfter: after.totalEntropy,
tasksExecuted: results,
totalTokensUsed: results.reduce((s, r) => s + r.tokensUsed, 0),
};
}
private async saveDreamLog(log: DreamLog) {
const logPath = `.claude/dream-logs/${new Date().toISOString().slice(0,10)}.json`;
console.log(`[AutoDream] Dream log saved to ${logPath}`);
// Actual file write omitted for brevity
}
private async loadMemoryEntries(): Promise<MemoryEntry[]> {
// Load from .claude/memory.json – omitted for brevity
return [];
}
}
export const autoDream = new AutoDream();06 | Memory Cleanup: LLM as Semantic Filter
The most technically demanding part is cleaning the .claude/memory.md file. The TypeScript and Python prototypes batch‑send all entries to an LLM with a prompt that asks for JSON‑formatted suggestions: which entries to delete, which groups to merge, and the merged content. This reduces API calls from n to one, at the cost of a longer prompt.
Example TypeScript cleanup function (simplified):
async function cleanupMemoryWithLLM(entries: MemoryEntry[], tokenBudget: number): Promise<MemoryCleanupResult> {
const entriesText = entries.map((e,i) => `[${i}] ${e.content} (last accessed: ${e.lastAccessedAt.toLocaleDateString()})`).join('
');
const prompt = `You are a workspace memory manager. Below are the memory entries:
${entriesText}
Return JSON with "deleted_indices", "merge_groups", and "merge_contents" following the criteria...`;
const response = await callLLM(prompt, tokenBudget);
const suggestion = JSON.parse(response);
// Build result structures from suggestion
const deleted = suggestion.deleted_indices.map(i => entries[i].content);
const merged = suggestion.merge_groups.map((group: number[], idx: number) => ({
original: group.map(i => entries[i].content),
merged: suggestion.merge_contents[idx]
}));
const deletedSet = new Set(suggestion.deleted_indices);
const mergedSet = new Set(suggestion.merge_groups.flat());
const kept = entries.filter((_,i) => !deletedSet.has(i) && !mergedSet.has(i)).map(e => e.content);
return {deleted, merged, kept};
}
async function callLLM(prompt: string, tokenBudget: number): Promise<string> {
// Placeholder for actual Anthropic/OpenAI API call
return JSON.stringify({deleted_indices: [], merge_groups: [], merge_contents: []});
}`The Python version mirrors the same logic, using dataclasses for MemoryCleanupResult and MergeResult.
07 | Dream Log: Auditable Consolidation Record
Each AutoDream run writes a human‑readable log under .claude/. A typical log shows entropy reduction, token consumption, task outcomes, and a git command for rollback:
[AutoDream] 2024-03-15T03:42:18
──────────────────────────────────────
entropy: 0.72 → 0.41 (↓43%)
token consumption: 6,340 / 8,000 budget
Task records:
✅ memory-cleanup (1,820 tokens)
- deleted: "old fetch polyfill" (87 days)
- merged: "API timeout" + "retry strategy" → "API request config: timeout 10s, max retries 3, exponential backoff"
✅ dedup-merge (2,380 tokens)
- merged: tool:fetch_url + tool:http_get → tool:http_request
✅ snapshot-rebuild (1,240 tokens)
- rebuilt project‑structure snapshot, added 47 new paths, removed 12 stale indexes
✅ startup-summary (900 tokens)
- generated .claude/startup-brief.md with project status
Rollback with: git diff HEAD .claude/ (view changes)
──────────────────────────────────────The log guarantees three safety lines: non‑intrusive idle trigger, token‑budget enforcement, and full git‑tracked reversibility.
08 | Extended Thoughts: Possibilities Opened by AutoDream
AutoDream is presented as a minimal viable product for semantic background maintenance. Potential extensions include cross‑project knowledge migration, sharing dream logs across teams via the git repository, and user‑defined consolidation strategies such as auto‑generating issue drafts from TODO comments.
Conclusion
Workspace entropy is a reliability problem for AI‑assisted development tools that cannot be solved by user discipline alone; it requires systematic, semantic‑level maintenance. AutoDream demonstrates that an LLM can both generate the noise and clean it, using an idle‑timeout trigger, a token budget, and transparent git‑tracked logs to ensure non‑disruption, cost control, and auditability. The "sleep consolidation" metaphor accurately captures the design: when the system is not needed, it reorganizes fragmented memories into structured knowledge, establishing a new design paradigm where AI tools maintain themselves.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
James' Growth Diary
I am James, focusing on AI Agent learning and growth. I continuously update two series: “AI Agent Mastery Path,” which systematically outlines core theories and practices of agents, and “Claude Code Design Philosophy,” which deeply analyzes the design thinking behind top AI tools. Helping you build a solid foundation in the AI era.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
