Skip to content

feat: timer extensions + bonus time (#90)#308

Merged
auerbachb merged 6 commits into
mainfrom
cursor/issue-90-bonus-time-9e60
May 1, 2026
Merged

feat: timer extensions + bonus time (#90)#308
auerbachb merged 6 commits into
mainfrom
cursor/issue-90-bonus-time-9e60

Conversation

@auerbachb
Copy link
Copy Markdown
Owner

@auerbachb auerbachb commented Apr 30, 2026

User description

Closes #90

Summary

  • +1 min / +5 min during an active solo sit (web SessionView, iOS control panel). Extensions lengthen the timer beyond the planned duration; block grid and completion sound follow the extended total.
  • bonus_seconds on sessions: planned duration stays in duration; extensions accumulate in bonus_seconds. POST /api/sessions accepts optional bonusSeconds (clamped 0–86400). GET /api/sessions stats include bonusMinutesTotal (rounded sum of bonus seconds on standard sessions).
  • History / Progress: per-session row shows bonus when bonusSeconds > 0; aggregate grid adds bonus min total.
  • Streak / day advance: unchanged — still driven by completed + sessionType === "standard" only.
  • Thoughts/min averages: denominator uses (duration + bonusSeconds) / 60 so rates reflect actual sit length including extensions.

Babysitter follow-up (commit after initial review)

  • Mobile E2E: restored data-session-chrome="secondary" on the secondary chrome wrapper (Playwright locator).
  • Extension UX: hide +1/+5 row during thought capture (&& not ||); only extend while the timer is running (web + iOS), matching “active sit”.
  • Completion copy: web CompletionScreen shows planned / bonus / total when bonus > 0; bonusSeconds threaded via CompletionData.
  • iOS: CompletionView subtitle uses duration as planned and duration + bonusSeconds as total (fixes Graphite/Bugbot).
  • UITest stats: avgThoughtsPerMinute denominator uses planned duration + bonus, not actualTime + bonus (avoids double-count).
  • E2E mock: POST /api/sessions clamps bonusSeconds to 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

  • Buddy personal-session inserts omit bonus (default 0).
  • iOS StatsDTO decodes optional bonusMinutesTotal; UI test stats synthesize it when offline.
Open in Web Open in Cursor 

CodeAnt-AI Description

Add session extensions, track bonus time, and show it in completion and history views

What Changed

  • During an active session, users can add +1 min or +5 min, and the timer continues to the new total instead of ending at the original target.
  • Saved sessions now keep planned time separate from bonus time, and the app sends that extra time through the web and iOS completion flows.
  • History and stats now include bonus time totals, and sessions with bonus time show it in the progress list and completion screen.
  • Thought-rate averages now use the full session length, and bonus time does not change streak tracking.
  • Bonus time is accepted from the session API, stored in the database, and limited to a maximum of 24 hours.

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:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

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:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

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.

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 30, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
still-point Ignored Ignored Preview May 1, 2026 6:14pm

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 30, 2026

📝 Walkthrough

Walkthrough

Adds 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 bonus_seconds, and stats aggregation/display updates.

Changes

Cohort / File(s) Summary
Database Schema & Migration
drizzle/sessions_bonus_seconds_incremental.sql, src/db/schema.ts, scripts/seed.ts
Adds non-null sessions.bonus_seconds integer column with default 0; migration is idempotent; seeds updated to include bonusSeconds: 0.
iOS ViewModels & DTOs
ios/StillPointApp/ViewModels/.../AppViewModel.swift, ios/StillPointApp/ViewModels/.../SessionViewModel.swift, ios/StillPointShared/.../DTOs/DTOs.swift
AppView completion enum extended with bonusSeconds; SessionViewModel splits plannedSeconds + bonusSeconds, adds extendBonus(seconds:), adjusts resume/save to report planned vs bonus; shared DTOs add bonusSeconds/bonusMinutesTotal and CreateSessionRequest includes bonusSeconds.
iOS Views
ios/StillPointApp/Views/.../SessionView.swift, ios/StillPointApp/Views/.../CompletionView.swift, ios/StillPointApp/Views/.../RootView.swift, ios/StillPointApp/Views/.../HistoryView.swift, ios/StillPointApp/Views/BuddySession/BuddySessionContainerView.swift
Session UI adds "+1 min"/"+5 min" buttons (disabled appropriately); completion UI now receives bonusSeconds and conditionally displays planned/bonus/total; history shows "BONUS MIN TOTAL" and per-session bonus suffix; buddy container forwards bonusSeconds.
Web Pages & Components
src/app/app/page.tsx, src/components/.../SessionView.tsx, src/components/.../CompletionScreen.tsx, src/components/.../HistoryView.tsx, src/components/BlockTimer.tsx
Page handlers thread bonusSeconds through completion flows; SessionView component supports plannedSeconds + bonusSeconds, extend buttons, updated callbacks; CompletionScreen accepts optional bonusSeconds and adjusts labels; BlockTimer resume seeding logic relaxed.
Web API & Server Logic
src/app/api/sessions/route.ts, src/app/api/board/route.ts
POST: parses/clamps bonusSeconds (finite number → int, 0–86400, default 0) and persists it; GET: includes bonusMinutesTotal derived from bonusSecondsTotal; board route selects bonusSeconds.
Client API & Stats
src/lib/api.ts, src/lib/constants.ts, src/lib/sessionProgression.test.ts, e2e/fixtures/auth.fixture.ts
Session type adds optional bonusSeconds; create/get APIs accept/return bonus fields; calculateSessionStats includes bonusSeconds in effective durations and returns bonusSecondsTotal; tests and fixtures updated to expect/derive bonus totals.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

size:XL

Suggested reviewers

  • codeant-ai

Poem

🐰 I nudged the timer, gave time a lift,
+60, +300 — a small temporal gift.
Planned seconds steady, bonuses in store,
Stats now count minutes we added more.
Hop, tap, extend — the session roars for more!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 24.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: timer extensions + bonus time (#90)' accurately and concisely captures the main feature additions: UI controls for time extensions and bonus time tracking.
Linked Issues check ✅ Passed The PR fully implements the five objectives from issue #90: add +1/+5 min buttons, persist bonus_seconds separately, display bonus in history/progress, show planned/bonus/total in completion screens, and include bonus in metrics.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing bonus time functionality outlined in issue #90; no unrelated or out-of-scope modifications detected.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

📋 Issue Planner

Built with CodeRabbit's Coding Plans for faster development and fewer bugs.

View plan used: #90

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/issue-90-bonus-time-9e60

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread ios/StillPointApp/Views/CompletionView.swift
@auerbachb auerbachb marked this pull request as ready for review April 30, 2026 23:18
@auerbachb
Copy link
Copy Markdown
Owner Author

@CodeAnt-AI review

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Apr 30, 2026

CodeAnt AI is running the review.

@auerbachb
Copy link
Copy Markdown
Owner Author

@cursor review

@auerbachb
Copy link
Copy Markdown
Owner Author

@graphite-app re-review

@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Apr 30, 2026
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Apr 30, 2026

Sequence Diagram

This 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
Loading

Generated by CodeAnt AI

Comment thread e2e/fixtures/auth.fixture.ts Outdated
Comment thread src/components/SessionView.tsx
Comment thread ios/StillPointApp/Views/CompletionView.swift
Comment thread ios/StillPointApp/ViewModels/SessionViewModel.swift Outdated
Comment thread ios/StillPointApp/Views/SessionView.swift
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

❌ 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.

Comment thread ios/StillPointApp/Views/CompletionView.swift
Comment thread ios/StillPointShared/Sources/StillPointShared/APIClient.swift Outdated
Comment thread src/components/SessionView.tsx Outdated
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Apr 30, 2026

CodeAnt AI finished running the review.

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Apr 30, 2026

CodeAnt AI is reviewing your PR.

@codeant-ai codeant-ai Bot added size:L This PR changes 100-499 lines, ignoring generated files and removed size:L This PR changes 100-499 lines, ignoring generated files labels Apr 30, 2026
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Apr 30, 2026

CodeAnt AI finished reviewing your PR.

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented May 1, 2026

CodeAnt AI is running Incremental review

@codeant-ai codeant-ai Bot added size:L This PR changes 100-499 lines, ignoring generated files and removed size:L This PR changes 100-499 lines, ignoring generated files labels May 1, 2026
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented May 1, 2026

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>
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented May 7, 2026

Sequence Diagram

This 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
Loading

Generated by CodeAnt AI

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented May 7, 2026

CodeAnt AI finished running the review.

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented May 7, 2026

CodeAnt AI is running the review.

@codeant-ai codeant-ai Bot added size:L This PR changes 100-499 lines, ignoring generated files and removed size:L This PR changes 100-499 lines, ignoring generated files labels May 7, 2026
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented May 7, 2026

Sequence Diagram

This 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
Loading

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;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Copy Markdown

codeant-ai Bot commented May 7, 2026

CodeAnt AI finished running the review.

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented May 8, 2026

CodeAnt AI is running the review.

@codeant-ai codeant-ai Bot added size:L This PR changes 100-499 lines, ignoring generated files and removed size:L This PR changes 100-499 lines, ignoring generated files labels May 8, 2026
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented May 8, 2026

Sequence Diagram

This 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
Loading

Generated by CodeAnt AI

Comment on lines +362 to +367
{entry.bonusSeconds > 0 && (
<>
<span style={{ color: "var(--fg-4)" }}>&middot;</span>
<span style={{ color: "var(--fg-2)" }}>+{Math.round(entry.bonusSeconds / 60)}m bonus</span>
</>
)}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Copy Markdown

codeant-ai Bot commented May 8, 2026

CodeAnt AI finished running the review.

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented May 8, 2026

CodeAnt AI is running the review.

@codeant-ai codeant-ai Bot added size:L This PR changes 100-499 lines, ignoring generated files and removed size:L This PR changes 100-499 lines, ignoring generated files labels May 8, 2026
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented May 8, 2026

Sequence Diagram

This 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
Loading

Generated by CodeAnt AI

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented May 8, 2026

CodeAnt AI finished running the review.

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented May 8, 2026

CodeAnt AI is running the review.

@codeant-ai codeant-ai Bot added size:L This PR changes 100-499 lines, ignoring generated files and removed size:L This PR changes 100-499 lines, ignoring generated files labels May 8, 2026
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented May 8, 2026

Sequence Diagram

This 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
Loading

Generated by CodeAnt AI

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented May 8, 2026

CodeAnt AI finished running the review.

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented May 10, 2026

CodeAnt AI is running the review.

@codeant-ai codeant-ai Bot added size:L This PR changes 100-499 lines, ignoring generated files and removed size:L This PR changes 100-499 lines, ignoring generated files labels May 10, 2026
@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented May 10, 2026

Sequence Diagram

This 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
Loading

Generated by CodeAnt AI

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented May 10, 2026

CodeAnt AI finished running the review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

add buttons to add 1 & 5 minutes

2 participants