State File Structure
Vibe Check maintains a JSON state file at~/.claude/vibe-check-state.json with the following schema:
Required Keys
The state validation logic requires these keys to be present (scripts/shared.py:27-28):
Key Definitions
| Key | Type | Purpose |
|---|---|---|
session_start | float | Unix timestamp when session began |
last_micro | float | Last micro-break (eyes, wrists) |
last_full | float | Last full break (stand, stretch) |
last_hydration | float | Last hydration reminder |
reminder_count | int | Total reminders fired this session |
last_active | float | Last user or system activity |
last_response_end | float | When Claude finished last response |
pending_break | object/null | Break awaiting compliance check |
tip_index | object | Rotation indices for each tip category |
last_reminder_fired_at | float | Deduplication guard timestamp |
Fresh State
When creating a fresh state, all timers are set to the current time (scripts/shared.py:31-45):
Loading State
Theload_state() function handles missing or corrupt state files gracefully (scripts/shared.py:48-60):
Atomic Writes
State updates use atomic write-then-rename to prevent corruption (scripts/shared.py:63-69):
- Write to
.tmpfile - Atomically rename to final path
- Ensures no partial writes are visible
Locking Mechanism
Vibe Check usesfcntl file locking to coordinate access across concurrent Claude sessions (scripts/shared.py:72-101):
Blocking Mode
All hooks useblocking=True, waiting indefinitely for the lock. This ensures safe concurrent access:
Non-Blocking Mode
The non-blocking mode is available for future use cases that need timeout behavior.Joining Active Sessions
Thesession_start.py hook checks if an active session exists (scripts/session_start.py:24-30):
last_active is within the stale threshold (default 60 minutes), the session continues with existing timers.
Stale Session Detection
Whenlast_active exceeds the stale threshold, a fresh state is created (scripts/check_reminder.py:76-80):
Pending Break State
After firing a reminder, the system storespending_break to track compliance:
last_response_end. If the gap exceeds the break’s threshold, the timer is reset:
Spontaneous Break Detection
If the user steps away for 15+ minutes (default) without a pending reminder, all timers reset (scripts/check_reminder.py:107-110):
Deduplication Guard
To prevent duplicate reminders in rapid succession, a 30-second dedup window is enforced (scripts/check_reminder.py:112-117):