Bundle Cleanup
This guide covers how to manage RunBundle artifacts to prevent disk space accumulation. You’ll learn about automatic cleanup policies, manual cleanup strategies, and how to protect important test runs.
The Problem
Section titled “The Problem”RunBundles accumulate over time:
.vibe-artifacts/├── test-abc123/ (2025-09-01, 45 MB)├── test-def456/ (2025-09-15, 120 MB)├── test-ghi789/ (2025-10-01, 89 MB)├── test-jkl012/ (2025-10-03, 67 MB)└── ... (100+ test runs, 10+ GB total)
Without cleanup:
- Disk space fills up - Especially in CI environments
- Git repositories bloat - If
.vibe-artifacts/
isn’t gitignored - Slower test startup - More artifacts to scan
Automatic Cleanup
Section titled “Automatic Cleanup”Vibe-check automatically deletes old bundles on test suite startup.
Default Policy
Section titled “Default Policy”Retention Period: 30 days
RunBundles older than 30 days are automatically deleted when tests start.
// Runs automatically before tests// Deletes bundles with mtime > 30 days ago
Configure Retention
Section titled “Configure Retention”Customize the retention period in vitest.config.ts
:
import { defineVibeConfig } from '@dao/vibe-check';
export default defineVibeConfig({ cleanup: { maxAgeDays: 7, // Delete bundles older than 7 days },});
Disable Automatic Cleanup
Section titled “Disable Automatic Cleanup”export default defineVibeConfig({ cleanup: { disabled: true // No automatic cleanup },});
Manual Cleanup
Section titled “Manual Cleanup”Use the cleanupBundles()
API for on-demand cleanup:
Basic Usage
Section titled “Basic Usage”import { cleanupBundles } from '@dao/vibe-check/artifacts';
// Delete bundles older than 7 daysconst result = await cleanupBundles({ maxAgeDays: 7});
console.log(`Deleted: ${result.deleted} bundles`);console.log(`Freed: ${result.freedMb.toFixed(2)} MB`);console.log(`Errors: ${result.errors.length}`);
Delete All Bundles
Section titled “Delete All Bundles”// ⚠️ Use with caution: deletes ALL bundlesconst result = await cleanupBundles({ maxAgeDays: 0});
CI/CD Integration
Section titled “CI/CD Integration”Add cleanup to your CI pipeline:
- name: Run tests run: bun test
- name: Clean up old bundles run: | bun run scripts/cleanup-bundles.ts
import { cleanupBundles } from '@dao/vibe-check/artifacts';
const result = await cleanupBundles({ maxAgeDays: 1 // Keep only today's runs in CI});
console.log(`Cleaned up ${result.deleted} bundles (${result.freedMb.toFixed(2)} MB freed)`);
if (result.errors.length > 0) { console.error('Cleanup errors:', result.errors);}
Disk Space Thresholds
Section titled “Disk Space Thresholds”Automatically clean up when disk space is low:
export default defineVibeConfig({ cleanup: { maxAgeDays: 30, minFreeDiskMb: 1000 // Clean up if <1GB free },});
How it works:
- Check free disk space before tests
- If
freeDiskMb < minFreeDiskMb
, trigger aggressive cleanup - Delete oldest bundles first until threshold is met
Protected Bundles
Section titled “Protected Bundles”Protect important test runs from automatic deletion:
Create Protection Marker
Section titled “Create Protection Marker”# Protect a specific test runtouch .vibe-artifacts/test-abc123/.vibe-keep
Bundles with .vibe-keep
are never automatically deleted.
When to Protect Bundles
Section titled “When to Protect Bundles”- Critical test runs - Production deployments, major releases
- Debugging - Runs being investigated
- Compliance - Audit trail requirements
Remove Protection
Section titled “Remove Protection”# Allow automatic deletion againrm .vibe-artifacts/test-abc123/.vibe-keep
List Protected Bundles
Section titled “List Protected Bundles”# Find all protected bundlesfind .vibe-artifacts -name '.vibe-keep'
Cleanup Strategies by Environment
Section titled “Cleanup Strategies by Environment”Local Development
Section titled “Local Development”Goal: Keep test history for debugging
export default defineVibeConfig({ cleanup: { maxAgeDays: 30, // 1 month retention minFreeDiskMb: 500 // Clean up if <500MB free },});
Why:
- Long retention for debugging old test runs
- Automatic cleanup only when disk fills
CI Environments
Section titled “CI Environments”Goal: Minimize disk usage
export default defineVibeConfig({ cleanup: { maxAgeDays: 1, // Keep only today's runs minFreeDiskMb: 1000 // Aggressive cleanup threshold },});
Why:
- CI runs don’t need history
- Prevent disk space issues in shared runners
Shared Development Servers
Section titled “Shared Development Servers”Goal: Balance history with disk space
export default defineVibeConfig({ cleanup: { maxAgeDays: 7, // 1 week retention minFreeDiskMb: 2000 // Clean up if <2GB free },});
Why:
- Short retention for team debugging
- Prevent one user from filling disk
Monitoring Disk Usage
Section titled “Monitoring Disk Usage”Check Bundle Size
Section titled “Check Bundle Size”import { readdir, stat } from 'node:fs/promises';import { join } from 'node:path';
async function getBundleStats() { const bundleDirs = await readdir('.vibe-artifacts'); let totalSize = 0; const bundles = [];
for (const dir of bundleDirs) { const bundlePath = join('.vibe-artifacts', dir); const stats = await stat(bundlePath); const size = await getFolderSize(bundlePath);
bundles.push({ name: dir, sizeMb: size / (1024 * 1024), age: Date.now() - stats.mtimeMs });
totalSize += size; }
return { totalMb: totalSize / (1024 * 1024), count: bundles.length, bundles: bundles.sort((a, b) => b.sizeMb - a.sizeMb) };}
async function getFolderSize(path: string): Promise<number> { const entries = await readdir(path, { withFileTypes: true }); let size = 0;
for (const entry of entries) { const fullPath = join(path, entry.name); if (entry.isDirectory()) { size += await getFolderSize(fullPath); } else { const stats = await stat(fullPath); size += stats.size; } }
return size;}
// Usageconst stats = await getBundleStats();console.log(`Total bundles: ${stats.count}`);console.log(`Total size: ${stats.totalMb.toFixed(2)} MB`);console.log('\nLargest bundles:');stats.bundles.slice(0, 5).forEach(b => { console.log(` ${b.name}: ${b.sizeMb.toFixed(2)} MB`);});
Add to Pre-Test Hook
Section titled “Add to Pre-Test Hook”import { beforeAll } from 'vitest';
beforeAll(async () => { const stats = await getBundleStats();
if (stats.totalMb > 1000) { // > 1GB console.warn(`⚠️ Bundle size: ${stats.totalMb.toFixed(2)} MB`); console.warn('Consider running cleanup: cleanupBundles({ maxAgeDays: 7 })'); }});
Best Practices
Section titled “Best Practices”1. Gitignore Artifacts
Section titled “1. Gitignore Artifacts”Always exclude .vibe-artifacts/
from version control:
.vibe-artifacts/
2. Set Environment-Specific Policies
Section titled “2. Set Environment-Specific Policies”const isCI = process.env.CI === 'true';
export default defineVibeConfig({ cleanup: { maxAgeDays: isCI ? 1 : 30, minFreeDiskMb: isCI ? 1000 : 500 },});
3. Protect Critical Runs
Section titled “3. Protect Critical Runs”# After important test runtouch .vibe-artifacts/$(ls -t .vibe-artifacts | head -1)/.vibe-keep
4. Monitor Disk Usage
Section titled “4. Monitor Disk Usage”Add a script to package.json:
{ "scripts": { "artifacts:size": "du -sh .vibe-artifacts", "artifacts:clean": "bun run scripts/cleanup-bundles.ts" }}
5. Schedule Cleanup in CI
Section titled “5. Schedule Cleanup in CI”name: Weekly Cleanupon: schedule: - cron: '0 0 * * 0' # Every Sunday
jobs: cleanup: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - run: bun install - run: bun run artifacts:clean
Troubleshooting
Section titled “Troubleshooting”Cleanup Not Running
Section titled “Cleanup Not Running”Symptom: Old bundles aren’t being deleted
Solutions:
-
Check config:
// Make sure cleanup is not disabledcleanup: { disabled: false } -
Check retention period:
// Bundles must be older than maxAgeDayscleanup: { maxAgeDays: 30 } -
Check for
.vibe-keep
markers:Terminal window find .vibe-artifacts -name '.vibe-keep'
Permission Errors
Section titled “Permission Errors”Symptom: EACCES
or EPERM
errors during cleanup
Solution: Ensure vibe-check has write permissions:
chmod -R u+w .vibe-artifacts
Disk Space Still Low
Section titled “Disk Space Still Low”Symptom: Cleanup runs but disk is still full
Solution: Lower maxAgeDays
or manually delete:
// More aggressive cleanupawait cleanupBundles({ maxAgeDays: 0 });
Manual Cleanup Strategies
Section titled “Manual Cleanup Strategies”Delete by Age
Section titled “Delete by Age”// Delete bundles older than 3 daysawait cleanupBundles({ maxAgeDays: 3 });
Delete by Size
Section titled “Delete by Size”// Keep only smallest 10 bundlesimport { readdir, stat, rm } from 'node:fs/promises';
const bundles = await getBundleStats();const toDelete = bundles.bundles .sort((a, b) => b.sizeMb - a.sizeMb) .slice(10); // Delete all but 10 smallest
for (const bundle of toDelete) { await rm(join('.vibe-artifacts', bundle.name), { recursive: true }); console.log(`Deleted: ${bundle.name} (${bundle.sizeMb.toFixed(2)} MB)`);}
Delete Failed Tests
Section titled “Delete Failed Tests”// Delete bundles from failed testsimport { readdir, rm } from 'node:fs/promises';import { join } from 'node:path';
const bundleDirs = await readdir('.vibe-artifacts');
for (const dir of bundleDirs) { const summaryPath = join('.vibe-artifacts', dir, 'summary.json'); const summary = JSON.parse(await readFile(summaryPath, 'utf-8'));
if (summary.failed) { await rm(join('.vibe-artifacts', dir), { recursive: true }); console.log(`Deleted failed test: ${dir}`); }}
Configuration Reference
Section titled “Configuration Reference”CleanupConfig
Section titled “CleanupConfig”interface CleanupConfig { /** Maximum age in days (default: 30) */ maxAgeDays?: number;
/** Minimum free disk space in MB (cleanup if below threshold) */ minFreeDiskMb?: number;
/** Disable automatic cleanup */ disabled?: boolean;}
cleanupBundles() Result
Section titled “cleanupBundles() Result”interface CleanupResult { /** Number of bundles deleted */ deleted: number;
/** Disk space freed in MB */ freedMb: number;
/** Errors encountered during cleanup */ errors: string[];}
What’s Next?
Section titled “What’s Next?”Now that you understand bundle cleanup, explore:
- Cost Optimization → - Reduce test costs
- Building Workflows → - Create workflows that generate bundles
- RunResult → - Understand what’s stored in bundles
Or learn more about vibe-check internals:
- Storage Architecture - How bundles are structured
- defineVibeConfig() → - Configuration API