Start a 25-minute focus timer. Wait 5 minutes (20 remaining). Pause for 10 minutes. Resume. The timer shows 10 minutes remaining instead of 20. The pause time was being counted as elapsed.

The Bug

public function getCurrentElapsedSeconds(): int
{
    $elapsed = $this->elapsed_seconds ?? 0;
    if ($this->status === 'running' && $this->paused_at === null) {
        // BUG: calculates from original start, ignoring accumulated pause time
        $elapsed = abs(now()->diffInSeconds($this->started_at));
    }
    return max(0, (int) $elapsed);
}

After resume, paused_at is null (cleared on resume), so it calculates now - started_at -- the total wall-clock time since the original start, including all pause durations.

The Fix

Added a resumed_at timestamp. On resume, it resets the reference point:

public function start(): void
{
    if ($this->status === 'paused') {
        $this->paused_at = null;
        $this->resumed_at = now();  // New reference point
    } else {
        $this->started_at = now();
        $this->resumed_at = now();
        $this->elapsed_seconds = 0;
    }
    $this->status = 'running';
    $this->save();
}

public function getCurrentElapsedSeconds(): int
{
    $elapsed = $this->elapsed_seconds ?? 0;  // Accumulated from previous cycles
    if ($this->status === 'running' && $this->resumed_at) {
        $elapsed += (int) abs(now()->diffInSeconds($this->resumed_at));
    }
    return max(0, (int) $elapsed);
}

The formula is now: total elapsed = accumulated (saved on pause) + time since last resume. Each pause accumulates elapsed time; each resume resets the reference point. Multiple pause/resume cycles work correctly because the accumulator carries forward.

The bug only manifested in the pause/resume path -- normal usage without pausing calculated now - started_at correctly, which is why it went undetected.