Files
beads/cmd/bd/staleness.go
Steve Yegge b3e76ff1f6 feat: Add sandbox escape hatches for daemon lock issues (GH #353 Phase 1)
Implements three quick fixes for users stuck in sandboxed environments
(e.g., Codex) where daemon cannot be stopped:

1. **--force flag for bd import**
   - Forces metadata update even when DB is synced with JSONL
   - Fixes stuck state caused by stale daemon cache
   - Shows: "Metadata updated (database already in sync with JSONL)"

2. **--allow-stale global flag**
   - Emergency escape hatch to bypass staleness check
   - Shows warning: "⚠️  Staleness check skipped (--allow-stale)"
   - Allows operations on potentially stale data

3. **Improved error message**
   - Added sandbox-specific guidance to staleness error
   - Suggests --sandbox, --force, and --allow-stale flags
   - Provides clear fix steps for different scenarios

Also fixed:
- Removed unused import in cmd/bd/duplicates_test.go

Follow-up work filed:
- bd-u3t: Phase 2 - Sandbox auto-detection
- bd-e0o: Phase 3 - Daemon robustness enhancements
- bd-9nw: Documentation updates

Fixes #353 (Phase 1)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 18:53:34 -05:00

61 lines
2.2 KiB
Go

package main
import (
"context"
"fmt"
"os"
"github.com/steveyegge/beads/internal/autoimport"
)
// ensureDatabaseFresh checks if the database is in sync with JSONL before read operations.
// If JSONL is newer than database, refuses to operate with an error message.
// This prevents users from making decisions based on stale/incomplete data.
//
// NOTE: Callers must check if daemonClient != nil and skip calling this function
// when running in daemon mode (daemon auto-imports on staleness).
//
// Implements bd-2q6d: All read operations should validate database freshness.
// Implements bd-c4rq: Daemon check moved to call sites to avoid function call overhead.
func ensureDatabaseFresh(ctx context.Context) error {
if allowStale {
fmt.Fprintf(os.Stderr, "⚠️ Staleness check skipped (--allow-stale), data may be out of sync\n")
return nil
}
// Skip check if no storage available (shouldn't happen in practice)
if store == nil {
return nil
}
// Check if database is stale
isStale, err := autoimport.CheckStaleness(ctx, store, dbPath)
if err != nil {
// If we can't determine staleness, allow operation to proceed
// (better to show potentially stale data than block user)
fmt.Fprintf(os.Stderr, "Warning: could not check database staleness: %v\n", err)
return nil
}
if !isStale {
// Database is fresh, proceed
return nil
}
// Database is stale - refuse to operate
return fmt.Errorf(
"Database out of sync with JSONL. Run 'bd import' first.\n\n"+
"The JSONL file has been updated (e.g., after 'git pull') but the database\n"+
"hasn't been imported yet. This would cause you to see stale/incomplete data.\n\n"+
"To fix:\n"+
" bd import -i .beads/beads.jsonl # Import JSONL updates to database\n\n"+
"If in a sandboxed environment (e.g., Codex) where daemon can't be stopped:\n"+
" bd --sandbox ready # Use direct mode (no daemon)\n"+
" bd import --force # Force metadata update\n"+
" bd ready --allow-stale # Skip staleness check (use with caution)\n\n"+
"Or use daemon mode (auto-imports on every operation):\n"+
" bd daemon start\n"+
" bd <command> # Will auto-import before executing",
)
}