feat: timer extensions + bonus time (#90)#308
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📝 WalkthroughWalkthroughAdds bonus-seconds support: session model split into planned + bonus, UI buttons to extend sessions (+1m/+5m), propagation through iOS and web UIs, API and DB persistence of Changes
Sequence Diagram(s)sequenceDiagram
participant Client(UI) as Client (Web/iOS)
participant API as Server API
participant DB as Database
rect rgba(200,200,255,0.5)
Client->>API: POST /api/sessions { duration: planned, bonusSeconds }
API->>DB: INSERT session (duration, bonus_seconds)
DB-->>API: inserted session id
API-->>Client: 201 Created (session)
end
rect rgba(200,255,200,0.5)
Client->>API: GET /api/sessions
API->>DB: SELECT sessions including bonus_seconds
DB-->>API: rows with bonus_seconds
API->>API: aggregate bonusSecondsTotal -> bonusMinutesTotal
API-->>Client: 200 OK { sessions, stats: { bonusMinutesTotal, ... } }
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 📋 Issue PlannerBuilt with CodeRabbit's Coding Plans for faster development and fewer bugs. View plan used: ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
|
@CodeAnt-AI review |
|
CodeAnt AI is running the review. |
|
@cursor review |
|
@graphite-app re-review |
Sequence DiagramThis diagram shows how in-session +1/+5 minute extensions are tracked as bonusSeconds, saved with a session, and then used to compute and display bonus minute totals and updated thoughts-per-minute stats. sequenceDiagram
participant User
participant Client
participant API
participant Database
User->>Client: Tap +1 or +5 minute during session
Client->>Client: Increase bonusSeconds and extend countdown timer
User->>Client: Finish or abandon session
Client->>API: POST session with planned duration and bonusSeconds
API->>Database: Store session including bonusSeconds
Client->>API: GET sessions and stats
API->>Database: Aggregate sessions and compute stats with duration plus bonusSeconds and bonusMinutesTotal
API-->>Client: Sessions and stats including bonusMinutesTotal
Client-->>User: Show history rows with bonus minutes and updated stats
Generated by CodeAnt AI |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 3 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit efdfa0f. Configure here.
|
CodeAnt AI finished running the review. |
|
CodeAnt AI is reviewing your PR. |
|
CodeAnt AI finished reviewing your PR. |
|
CodeAnt AI is running Incremental review |
|
CodeAnt AI Incremental review completed. |
Add bonus_seconds on sessions (incremental SQL), accept it on POST /api/sessions, and expose bonusMinutesTotal on GET stats. Web and iOS show +1/+5 min during an active sit; planned duration stays separate from extensions for streak logic. History/Progress shows per-session bonus and running bonus-minute total. Thoughts-per-minute averages use planned duration plus bonus time. Co-authored-by: Bretton Auerbach <auerbachb@users.noreply.github.com>
Sequence DiagramThis PR lets clients extend an active session timer with +1/+5 minute controls, persists the extra time as bonusSeconds, and surfaces it in completion copy and history stats (including bonusMinutesTotal and updated thoughts-per-minute). sequenceDiagram
participant User
participant Client
participant SessionsAPI
participant Database
User->>Client: Run standard session and optionally extend timer with +1/+5 min
Client->>Client: Track bonusSeconds on top of planned duration
Client->>SessionsAPI: POST /api/sessions with duration and bonusSeconds
SessionsAPI->>Database: Save session including bonusSeconds
Client->>SessionsAPI: GET /api/sessions
SessionsAPI->>Database: Load user sessions
SessionsAPI-->>Client: Sessions and stats with bonusMinutesTotal and updated averages
Client-->>User: Show planned, bonus, and total time in completion and history views
Generated by CodeAnt AI |
|
CodeAnt AI finished running the review. |
|
CodeAnt AI is running the review. |
Sequence DiagramThis PR adds in-session +1/+5 minute extensions that accumulate as bonus time, persists them on the sessions API, and surfaces total bonus minutes in history stats. sequenceDiagram
participant User
participant Client
participant API
participant Database
participant History
User->>Client: Start standard session
User->>Client: Tap +1 or +5 minute controls
Client->>Client: Increase bonusSeconds and extend timer duration
Client->>API: Create session with duration and bonusSeconds
API->>Database: Save session with clamped bonusSeconds
History->>API: Request sessions and stats
API-->>History: Sessions with bonusMinutesTotal and bonusSeconds per session
Generated by CodeAnt AI |
| setIsActive(false); | ||
| const actualTime = Math.round((Date.now() - wallStartRef.current) / 1000); | ||
| const endT = elapsedRef.current || todayDuration; | ||
| const endT = elapsedRef.current || totalSeconds; |
There was a problem hiding this comment.
Suggestion: Falling back to totalSeconds when elapsed is 0 inflates the denominator for early end/abandon-at-start flows, which can report an incorrect clear-mind percentage despite near-zero elapsed time. Use actual elapsed for manual termination paths and only use total duration for true natural completion. [logic error]
Severity Level: Major ⚠️
- ❌ Immediate end sessions saved as full-length 100% awareness.
- ⚠️ Skews aggregate averages in calculateSessionStats for some users.Steps of Reproduction ✅
1. In the web app, start a solo session so `SessionView` mounts with `isActive = true` and
`elapsedRef.current = 0`, and `BlockTimer` begins but has not yet invoked
`onElapsedChange` (src/components/SessionView.tsx:46–50, 331–339, 268–271).
2. Immediately, before any elapsed tick occurs (so `elapsedRef.current` remains 0), click
"end early & keep" or "abandon" in the secondary chrome (SessionView.tsx:600–49),
triggering `handleEndEarly` (lines 221–240) or `handleAbandon` (lines 242–261).
3. In these handlers, `const endT = elapsedRef.current || totalSeconds;`
(SessionView.tsx:227 and 248) resolves `endT` to `totalSeconds` because
`elapsedRef.current` is 0; `snapshotForComplete()` returns an empty log for a
never-distracted session (SessionView.tsx:145–158), and
`computeClearPercentFromLog(resolvedLog, endT)` interprets this as a full `totalSeconds`
of clear time, returning 100% awareness (src/lib/mindStateSession.ts:29–44).
4. The completion or abandon payload containing `clearPercent = 100`, `actualTime ≈ 0` is
then POSTed to `/api/sessions` via `handleSessionComplete` or `handleSessionAbandon`
(src/app/app/page.tsx:11–41, 5–33), so history rows and statistics derived from GET
`/api/sessions` treat this near-instant aborted sit as a full-length, 100%-clear session,
skewing per-session reporting and aggregate averages.Fix in Cursor | Fix in VSCode Claude
(Use Cmd/Ctrl + Click for best experience)
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** src/components/SessionView.tsx
**Line:** 227:227
**Comment:**
*Logic Error: Falling back to `totalSeconds` when elapsed is `0` inflates the denominator for early end/abandon-at-start flows, which can report an incorrect clear-mind percentage despite near-zero elapsed time. Use actual elapsed for manual termination paths and only use total duration for true natural completion.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix|
CodeAnt AI finished running the review. |
|
CodeAnt AI is running the review. |
Sequence DiagramThis PR lets users extend an active sit with +1/+5 minute controls, saves the extra time as bonusSeconds, and surfaces aggregate bonus minutes and adjusted thoughts-per-minute stats in history views. sequenceDiagram
participant User
participant Client
participant API
participant Database
User->>Client: Start standard session
Client->>Client: Run timer using planned duration
User->>Client: Tap +1 or +5 minute while timer is running
Client->>Client: Extend timer and accumulate bonusSeconds
Client->>API: Save session with duration and bonusSeconds
API->>Database: Persist session, clamping bonusSeconds
User->>Client: Open history and stats
Client->>API: Fetch sessions and stats
API->>Database: Load sessions and aggregate bonusSeconds into bonusMinutesTotal and thoughts per minute
Client-->>User: Show per session bonus minutes and bonus min total in history
Generated by CodeAnt AI |
| {entry.bonusSeconds > 0 && ( | ||
| <> | ||
| <span style={{ color: "var(--fg-4)" }}>·</span> | ||
| <span style={{ color: "var(--fg-2)" }}>+{Math.round(entry.bonusSeconds / 60)}m bonus</span> | ||
| </> | ||
| )} |
There was a problem hiding this comment.
Suggestion: The bonus display rounds seconds to whole minutes with Math.round, which can show +0m bonus for valid positive values (e.g. 1–29 seconds) and generally misreport non-minute bonuses accepted by the API. Render bonus using seconds when under a minute (or a non-rounded minute format) so every positive bonus is displayed accurately. [logic error]
Severity Level: Major ⚠️
- ⚠️ HistoryView misreports bonus for non-minute extensions.
- ⚠️ Users see '+0m bonus' for short valid bonuses.
- ⚠️ Aggregate stats inconsistent with per-session bonus display.Steps of Reproduction ✅
1. Create a session via the backend POST handler at `src/app/api/sessions/route.ts:34-74`
by calling `POST /api/sessions` with JSON including a positive `bonusSeconds` value that
is not a whole minute, e.g. `{ ..., duration: 300, bonusSeconds: 30, sessionType:
"standard", completed: true, clearPercent: 80, thoughtCount: 3, sessionDate: "2024-01-01"
}`.
2. In the POST handler, `bonusSecondsRaw` is read from the body and normalized at
`src/app/api/sessions/route.ts:45-49` using `Math.floor`, so `bonusSeconds` is stored in
the `sessions` table exactly as 30 seconds (or any other positive non-minute value)
without converting to full minutes.
3. Load the History page in the web app, which mounts `HistoryView` at
`src/components/HistoryView.tsx:50-81`; its `useEffect` issues `fetch("/api/sessions")`,
and the GET handler at `src/app/api/sessions/route.ts:8-27` returns `sessions:
userSessions` including the stored `bonusSeconds` value from step 2.
4. `HistoryView` maps these sessions into `HistoryEntry` objects at
`src/components/HistoryView.tsx:94-123` (assigning `bonusSeconds: s.bonusSeconds ?? 0`),
then renders each row at `src/components/HistoryView.tsx:351-367`, where the bonus label
uses `+{Math.round(entry.bonusSeconds / 60)}m bonus`; for `bonusSeconds = 30` this renders
`+1m bonus` (overstating the bonus), and for `bonusSeconds` between 1–29 it renders `+0m
bonus` even though `entry.bonusSeconds > 0`.Fix in Cursor | Fix in VSCode Claude
(Use Cmd/Ctrl + Click for best experience)
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** src/components/HistoryView.tsx
**Line:** 362:367
**Comment:**
*Logic Error: The bonus display rounds seconds to whole minutes with `Math.round`, which can show `+0m bonus` for valid positive values (e.g. 1–29 seconds) and generally misreport non-minute bonuses accepted by the API. Render bonus using seconds when under a minute (or a non-rounded minute format) so every positive bonus is displayed accurately.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix|
|
||
| const extendSessionSeconds = useCallback((extra: number) => { | ||
| if (sessionFinished || showPostDistractionCapture || !isActive) return; | ||
| setBonusSeconds(b => b + extra); |
There was a problem hiding this comment.
Suggestion: Bonus extension is unbounded on the client, so repeated clicks can grow totalSeconds arbitrarily, creating huge block arrays in the timer rendering path and potentially freezing the UI; it also diverges from the API's 86400-second clamp. Clamp bonus accumulation client-side to the same max as the backend. [performance]
Severity Level: Major ⚠️
- ⚠️ Large extensions create huge BlockTimer grids and DOM.
- ⚠️ Extended sessions can lag or freeze session view UI.
- ⚠️ Client can exceed server bonusSeconds clamp in requests.Steps of Reproduction ✅
1. Open the main app page and enter the session view: when `view === "session"`,
`SessionView` is rendered with `onComplete={handleSessionComplete}` and
`onAbandon={handleSessionAbandon}` (`src/app/app/page.tsx:557-23`). `SessionView` computes
`plannedSeconds = durationForSession(sessionType, currentDay)` and `totalSeconds =
plannedSeconds + bonusSeconds` (`src/components/SessionView.tsx:47-49`).
2. While the timer is running, repeatedly click the "+5 min" and/or "+1 min" buttons
rendered near the bottom of `SessionView` (`src/components/SessionView.tsx:133-178`).
These buttons call `extendSessionSeconds(60)` or `extendSessionSeconds(300)` respectively
via their `onClick` handlers.
3. `extendSessionSeconds` is defined at `263-266` as `const extendSessionSeconds =
useCallback((extra: number) => { if (sessionFinished || showPostDistractionCapture ||
!isActive) return; setBonusSeconds(b => b + extra); }, [sessionFinished,
showPostDistractionCapture, isActive]);`. Because there is no upper bound, every click
increases `bonusSeconds` and therefore `totalSeconds` without limit on the client.
4. `BlockTimer` receives the ever-growing `totalSeconds` via the `totalSeconds` prop
(`SessionView` render at `331-333`) and uses it to construct the block grid: it builds
`blocks: BlockDef[]` sized from `totalSeconds` (`src/components/BlockTimer.tsx:85-118`),
computing `minuteBlockCount` and `totalBlocks` proportional to `totalSeconds`. As
`bonusSeconds` grows large from repeated extensions, the number of blocks (and the amount
of DOM/React work) grows linearly with `totalSeconds`, which can lead to very large
BlockTimer arrays and sluggish or frozen UI for heavily extended sessions.
5. When the session completes or is ended, `SessionView` persists the same unbounded
`bonusSeconds` to the backend via `handleSessionComplete` / `handleSessionAbandon`: they
send `bonusSeconds: data.bonusSeconds` in the body of the `/api/sessions` POST
(`src/app/app/page.tsx:20-24` and `14-18` in the respective handlers, plus the type
definition in `src/lib/api.ts:19-31`). This allows the client to report `bonusSeconds`
values larger than the server's documented 86400-second clamp, creating client/server
mismatches in recorded session lengths and stats.Fix in Cursor | Fix in VSCode Claude
(Use Cmd/Ctrl + Click for best experience)
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** src/components/SessionView.tsx
**Line:** 265:265
**Comment:**
*Performance: Bonus extension is unbounded on the client, so repeated clicks can grow `totalSeconds` arbitrarily, creating huge block arrays in the timer rendering path and potentially freezing the UI; it also diverges from the API's 86400-second clamp. Clamp bonus accumulation client-side to the same max as the backend.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix|
CodeAnt AI finished running the review. |
|
CodeAnt AI is running the review. |
Sequence DiagramThis diagram shows how timer extensions are tracked as bonusSeconds, persisted with sessions, and then used to compute and display updated history stats including bonusMinutesTotal and thoughts per minute. It focuses on the main client-to-API interactions introduced in this PR. sequenceDiagram
participant User
participant ClientApp
participant SessionsAPI
participant Database
User->>ClientApp: Tap plus one or plus five during active session
ClientApp->>ClientApp: Add bonusSeconds and extend session timer
ClientApp->>SessionsAPI: POST session with duration and bonusSeconds
SessionsAPI->>Database: Save session with duration and bonus_seconds
ClientApp->>SessionsAPI: GET sessions and stats
SessionsAPI->>Database: Load user sessions with bonus_seconds
SessionsAPI-->>ClientApp: Sessions and stats using duration plus bonusSeconds and bonusMinutesTotal
ClientApp-->>User: Show completion and history with planned, bonus and total time
Generated by CodeAnt AI |
|
CodeAnt AI finished running the review. |
|
CodeAnt AI is running the review. |
Sequence DiagramThis PR lets users extend an active session timer with bonus minutes, persists the extra time as bonusSeconds, and surfaces both per-session bonus and aggregate bonusMinutesTotal in history and progress stats. sequenceDiagram
participant User
participant App
participant SessionsAPI
User->>App: Start meditation session
App->>App: Add +1 or +5 minutes while timer is running
App->>SessionsAPI: POST session with duration and bonusSeconds
SessionsAPI->>SessionsAPI: Store session with duration and bonusSeconds
SessionsAPI-->>App: Return saved session
App->>App: Show completion with planned, bonus, and total time
App->>SessionsAPI: GET sessions history
SessionsAPI->>SessionsAPI: Load sessions and compute stats with bonusMinutesTotal
SessionsAPI-->>App: Return sessions and stats including bonusMinutesTotal
App->>App: Render history and progress with bonus minutes
Generated by CodeAnt AI |
|
CodeAnt AI finished running the review. |
|
CodeAnt AI is running the review. |
Sequence DiagramThis diagram shows how timer extensions are captured as bonusSeconds when a session completes and how the backend includes this bonus time in session stats and history views. sequenceDiagram
participant User
participant Client
participant API
participant Database
participant History
User->>Client: Start meditation session
User->>Client: Tap +1 or +5 minute during active session
Client->>API: Complete session with duration and bonusSeconds
API->>Database: Store session including bonusSeconds
User->>Client: Open history and stats
Client->>API: Request sessions and stats
API->>Database: Load sessions and compute bonusMinutesTotal
API-->>Client: Sessions with bonusSeconds and stats with bonusMinutesTotal
Client-->>User: Show planned, bonus, total time and bonus min total
Generated by CodeAnt AI |
|
CodeAnt AI finished running the review. |

User description
Closes #90
Summary
SessionView, iOS control panel). Extensions lengthen the timer beyond the planned duration; block grid and completion sound follow the extended total.bonus_secondsonsessions: planned duration stays induration; extensions accumulate inbonus_seconds. POST/api/sessionsaccepts optionalbonusSeconds(clamped 0–86400). GET/api/sessionsstats includebonusMinutesTotal(rounded sum of bonus seconds on standard sessions).bonusSeconds > 0; aggregate grid adds bonus min total.completed+sessionType === "standard"only.(duration + bonusSeconds) / 60so rates reflect actual sit length including extensions.Babysitter follow-up (commit after initial review)
data-session-chrome="secondary"on the secondary chrome wrapper (Playwright locator).&¬||); only extend while the timer is running (web + iOS), matching “active sit”.CompletionScreenshows planned / bonus / total when bonus > 0;bonusSecondsthreaded viaCompletionData.CompletionViewsubtitle usesdurationas planned andduration + bonusSecondsas total (fixes Graphite/Bugbot).avgThoughtsPerMinutedenominator uses plannedduration+ bonus, notactualTime + bonus(avoids double-count)./api/sessionsclampsbonusSecondsto 86400 like production.DB rollout
Apply incremental migration:
drizzle/sessions_bonus_seconds_incremental.sql(ADD COLUMN bonus_seconds integer DEFAULT 0 NOT NULL). Safe additive column with default for existing rows.Notes
StatsDTOdecodes optionalbonusMinutesTotal; UI test stats synthesize it when offline.CodeAnt-AI Description
Add session extensions, track bonus time, and show it in completion and history views
What Changed
Impact
✅ Fewer sessions ending before planned extensions are used✅ Clearer session summaries with planned time, bonus time, and total time✅ Accurate history stats for sessions that include extensions🔄 Retrigger CodeAnt AI Review
Details
💡 Usage Guide
Checking Your Pull Request
Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.
Talking to CodeAnt AI
Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.
Example
Preserve Org Learnings with CodeAnt
You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:
This helps CodeAnt AI learn and adapt to your team's coding style and standards.
Example
Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
Check Your Repository Health
To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.