Skip to content

Ensure note save payloads fail visibly#228

Merged
auerbachb merged 3 commits into
mainfrom
cursor/ios-note-saving-ff92
Apr 25, 2026
Merged

Ensure note save payloads fail visibly#228
auerbachb merged 3 commits into
mainfrom
cursor/ios-note-saving-ff92

Conversation

@auerbachb
Copy link
Copy Markdown
Owner

@auerbachb auerbachb commented Apr 25, 2026

Summary

  • Shared note/thought payload normalization between web batch saves and buddy personal-session saves.
  • Return 400 Invalid thoughts payload when submitted note payloads are malformed, mixed valid/invalid, non-array in buddy saves, or duplicate completion-note submissions.
  • Verify thought insert counts and add regression coverage for web completion notes, iOS buddy embedded-note DTO encoding, and note-normalization edge cases.

Testing

  • node --import tsx --test src/lib/thoughtSaving.test.ts
  • npm run build
  • npx tsc --noEmit
  • ✅ GitHub checks before latest test-only push: Web Build, E2E Web policy/smoke/critical, E2E Mobile Web chromium/webkit lanes, E2E iOS smoke/critical, Vercel, CodeRabbit
  • ⚠️ swift test (Swift toolchain is not installed in this Linux image)
  • ⚠️ coderabbit review --prompt-only (CodeRabbit CLI is not installed in this image)
  • ⚠️ npx coderabbit review --prompt-only (npm could not resolve a CodeRabbit executable)
  • ⚠️ @cursor review comment attempt failed via GitHub API with Resource not accessible by integration; Cursor Bugbot nevertheless started after the PR was marked ready
Open in Web Open in Cursor 

Summary by CodeRabbit

  • Bug Fixes

    • Stronger validation for submitted thoughts: non-array payloads, invalid entries, and duplicate completion notes are now rejected with clear 400 errors instead of being silently ignored.
    • Persistence now verifies inserted thought counts and surface an error if saved rows don’t match expected.
  • Tests

    • New tests for thought normalization and personal session request encoding to ensure correct acceptance, trimming, counts, and encoding.

Co-authored-by: Bretton Auerbach <auerbachb@users.noreply.github.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 25, 2026

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

Project Deployment Actions Updated (UTC)
still-point Ready Ready Preview, Comment Apr 25, 2026 2:06am

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 25, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1f658f3a-1eaa-48d0-a624-c2fcac298b8f

📥 Commits

Reviewing files that changed from the base of the PR and between 80751e7 and 60ad69f.

📒 Files selected for processing (1)
  • src/lib/thoughtSaving.test.ts
✅ Files skipped from review due to trivial changes (1)
  • src/lib/thoughtSaving.test.ts

📝 Walkthrough

Walkthrough

Adds thought-normalization utilities and tests; updates two API endpoints to validate/reject invalid thought submissions (including duplicate completion notes), verifies DB insert counts via returning(), and adds an iOS encoding unit test for batch thoughts.

Changes

Cohort / File(s) Summary
Thought Validation Utilities
src/lib/thoughtSaving.ts, src/lib/thoughtSaving.test.ts
Adds exported types (ThoughtInput, ThoughtNormalizationResult) and functions normalizeThoughtInputs and hasRejectedSubmittedThoughts. Normalization validates/trims/truncates text, counts submitted vs invalid items; tests cover web/iOS payload shapes, trimming, numeric time handling, and rejection cases.
API Endpoint Validation & Persistence
src/app/api/buddy/sessions/[id]/record-personal-session/route.ts, src/app/api/thoughts/batch/route.ts
Endpoints now use normalizeThoughtInputs, reject when body.thoughts is not an array or when hasRejectedSubmittedThoughts is true (HTTP 400 + logs). Batch route explicitly rejects duplicate completion notes (timeInSession === -1). DB inserts use returning() and assert returned row count matches expected, throwing THOUGHT_INSERT_MISMATCH on mismatch. Local inline parseThoughts removed.
iOS Encoding Test
ios/StillPointShared/Tests/StillPointSharedTests/RecordBuddyPersonalSessionRequestEncodingTests.swift
Adds XCTest that JSON-encodes a RecordBuddyPersonalSessionRequest with embedded batch thoughts and asserts server-facing field names and values (clearPercent, thoughtCount, actualTime, sessionDate, timeInSession, text).

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API as API Endpoint
    participant Norm as Thought Validation
    participant DB as Database

    Client->>API: POST /batch or /record-personal-session (body.thoughts)
    API->>Norm: normalizeThoughtInputs(body.thoughts)
    Norm-->>API: ThoughtNormalizationResult (submittedCount, invalidCount, thoughts)
    API->>API: hasRejectedSubmittedThoughts(result)?
    alt Rejected or invalid shape
        API-->>Client: 400 Invalid thoughts payload
    else Valid
        API->>API: check duplicate completion notes?
        alt Duplicate completion notes found
            API-->>Client: 400 Duplicate completion note
        else
            API->>DB: INSERT thoughts ... RETURNING(id)
            DB-->>API: inserted rows
            API->>API: verify inserted count == thoughts.length
            alt Mismatch
                API-->>API: throw THOUGHT_INSERT_MISMATCH
            else
                API-->>Client: 200 OK with inserted thoughts
            end
        end
    end
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Poem

🐰 I hop through inputs, trim each little thought,
Counting the valid, and leaving the naught,
I warn if duplicates crowd the end,
I hand the carrots to DB, then send,
Nose twitch of joy — tidy data, neatly caught.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Ensure note save payloads fail visibly' accurately captures the main objective of the pull request: making note/thought payload validation failures visible with explicit 400 errors instead of silent failures or ambiguous responses.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ 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/ios-note-saving-ff92

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

@cursor cursor Bot marked this pull request as ready for review April 25, 2026 00:30
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/lib/thoughtSaving.test.ts (1)

8-41: Add a mixed valid/invalid payload regression.

The suite only covers all-valid and all-invalid arrays. The PR’s main behavior change is that even one bad note in an otherwise valid submission must fail visibly, so please lock that case in too.

Suggested test
 describe("normalizeThoughtInputs", () => {
+  it("rejects mixed valid and invalid submitted payloads", () => {
+    const result = normalizeThoughtInputs([
+      { timeInSession: 12, text: "kept if normalized alone" },
+      { timeInSession: "13", text: "invalid" },
+    ]);
+
+    assert.equal(result.submittedCount, 2);
+    assert.equal(result.invalidCount, 1);
+    assert.deepEqual(result.thoughts, [{ timeInSession: 12, text: "kept if normalized alone" }]);
+    assert.equal(hasRejectedSubmittedThoughts(result), true);
+  });
+
   it("accepts web completion note payloads", () => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/thoughtSaving.test.ts` around lines 8 - 41, Add a new test case to
cover a mixed valid/invalid payload for normalizeThoughtInputs: call
normalizeThoughtInputs with an array containing at least one valid thought
(proper timeInSession number and non-empty text) and one invalid thought (e.g.,
timeInSession as string or empty text), then assert submittedCount equals total
inputs, invalidCount equals number of invalid items, thoughts contains only the
valid normalized entries, and hasRejectedSubmittedThoughts(result) is true;
reference normalizeThoughtInputs and hasRejectedSubmittedThoughts to locate
where to add this test in the existing describe block.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/app/api/buddy/sessions/`[id]/record-personal-session/route.ts:
- Around line 126-136: The code currently calls
normalizeThoughtInputs(body.thoughts) which masks non-array inputs and lets
malformed payloads silently pass; instead, validate that body.thoughts is an
array (and not null/undefined) before calling normalizeThoughtInputs and return
a 400 error if it’s not an array or is otherwise malformed; update the handler
in route.ts to check Array.isArray(body.thoughts) (and optionally verify each
item is an object/has required fields) and respond with NextResponse.json({
error: "Invalid thoughts payload" }, { status: 400 }) prior to invoking
normalizeThoughtInputs so hasRejectedSubmittedThoughts and thoughtItems only
operate on proper arrays.

In `@src/app/api/thoughts/batch/route.ts`:
- Around line 46-56: The route is silently dropping duplicate completion notes
(timeInSession === -1) because normalizeThoughtInputs collapses them before the
new count check; update the validation to detect multiple completion notes
before collapsing: in route.ts, after computing normalizedResult (from
normalizeThoughtInputs) or by inspecting the original thoughtItems, count items
with timeInSession === -1 and if count > 1 return a 400 error (similar to the
existing invalid payload response). Reference normalizeThoughtInputs,
hasRejectedSubmittedThoughts and the variable normalizedResult/normalized to
locate where to add this pre-collapse duplicate-completion validation.

---

Nitpick comments:
In `@src/lib/thoughtSaving.test.ts`:
- Around line 8-41: Add a new test case to cover a mixed valid/invalid payload
for normalizeThoughtInputs: call normalizeThoughtInputs with an array containing
at least one valid thought (proper timeInSession number and non-empty text) and
one invalid thought (e.g., timeInSession as string or empty text), then assert
submittedCount equals total inputs, invalidCount equals number of invalid items,
thoughts contains only the valid normalized entries, and
hasRejectedSubmittedThoughts(result) is true; reference normalizeThoughtInputs
and hasRejectedSubmittedThoughts to locate where to add this test in the
existing describe block.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5db5ba37-5737-421e-a49f-971e934c335c

📥 Commits

Reviewing files that changed from the base of the PR and between 9a42f11 and 2f6606c.

📒 Files selected for processing (5)
  • ios/StillPointShared/Tests/StillPointSharedTests/RecordBuddyPersonalSessionRequestEncodingTests.swift
  • src/app/api/buddy/sessions/[id]/record-personal-session/route.ts
  • src/app/api/thoughts/batch/route.ts
  • src/lib/thoughtSaving.test.ts
  • src/lib/thoughtSaving.ts

Comment thread src/app/api/buddy/sessions/[id]/record-personal-session/route.ts
Comment thread src/app/api/thoughts/batch/route.ts
Co-authored-by: Bretton Auerbach <auerbachb@users.noreply.github.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/lib/thoughtSaving.test.ts (1)

8-55: Tests cover core normalization scenarios well.

The test cases effectively validate the main behaviors: filtering invalid entries, whitespace trimming, handling multiple valid notes, and rejection semantics.

Consider adding edge case coverage for:

  • Empty array input (normalizeThoughtInputs([]))
  • Text that becomes empty after trimming ({ timeInSession: 1, text: " " })
  • Boundary timeInSession values (0, negative values other than -1)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/thoughtSaving.test.ts` around lines 8 - 55, Add unit tests to cover
edge cases for normalizeThoughtInputs: add a test for an empty array input
(normalizeThoughtInputs([])) asserting submittedCount 0, invalidCount 0,
thoughts [], and hasRejectedSubmittedThoughts(...) false; add a test where text
trims to empty (e.g., { timeInSession: 1, text: "   " }) asserting it is treated
as invalid and results in invalidCount increment and no saved thoughts; and add
tests for boundary timeInSession values (0 and negative values other than -1) to
assert they are either accepted or rejected per current intended behavior,
referencing normalizeThoughtInputs and hasRejectedSubmittedThoughts to locate
where to add these assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/lib/thoughtSaving.test.ts`:
- Around line 8-55: Add unit tests to cover edge cases for
normalizeThoughtInputs: add a test for an empty array input
(normalizeThoughtInputs([])) asserting submittedCount 0, invalidCount 0,
thoughts [], and hasRejectedSubmittedThoughts(...) false; add a test where text
trims to empty (e.g., { timeInSession: 1, text: "   " }) asserting it is treated
as invalid and results in invalidCount increment and no saved thoughts; and add
tests for boundary timeInSession values (0 and negative values other than -1) to
assert they are either accepted or rejected per current intended behavior,
referencing normalizeThoughtInputs and hasRejectedSubmittedThoughts to locate
where to add these assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 12292e9d-b115-41db-857c-d740dc269b39

📥 Commits

Reviewing files that changed from the base of the PR and between 2f6606c and 80751e7.

📒 Files selected for processing (3)
  • src/app/api/buddy/sessions/[id]/record-personal-session/route.ts
  • src/app/api/thoughts/batch/route.ts
  • src/lib/thoughtSaving.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/app/api/buddy/sessions/[id]/record-personal-session/route.ts

Co-authored-by: Bretton Auerbach <auerbachb@users.noreply.github.com>
@auerbachb auerbachb merged commit b4fe66d into main Apr 25, 2026
13 checks passed
@auerbachb auerbachb deleted the cursor/ios-note-saving-ff92 branch April 25, 2026 15:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants