Dev#46
Conversation
[+] Implemented code-rabbit's suggested patches.
[+] Implemented code-rabbit's suggested patches.
📝 WalkthroughWalkthroughAdds a shared AI text renderer and markdown parser, then threads them through chat, insights, report, and reflection surfaces. Also adds partial-response handling, mood-log-aware insight/report logic, shared report narrative parsing with retries, and a few small UI/documentation updates. ChangesAI Response Rendering and Text Integrity
Text Integrity and Partial AI Responses
AI Text Rendering in UI
Mood Check-in Integration into Insights
Report Narrative Parsing Refactor
Minor Cosmetic Fixes
Estimated code review effort: 5 (Critical) | ~120 minutes 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 14
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
components/auth/auth-screen.tsx (1)
610-618: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
leading-6/leading-5inconsistency between the two social buttons.The "Continue with Google" text now uses
leading-6(Line 618) while "Continue with Apple" (Line 637) still usesleading-5, so the two visually-paired buttons no longer have matching line heights.✏️ Proposed fix
- <Text className="text-[15px] font-semibold leading-5 text-zinc-950"> + <Text className="text-[15px] font-semibold leading-6 text-zinc-950"> Continue with AppleAs per coding guidelines, "Keep 'leading-6' in all components, and fix if you find any un-matching ones."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@components/auth/auth-screen.tsx` around lines 610 - 618, The social button labels in auth-screen.tsx are inconsistent because the Google and Apple variants use different text line heights. Update the text styling in the shared social button area (the Text elements used by the Google and Apple buttons) so both use leading-6, keeping the paired buttons visually aligned and consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@AGENTS.md`:
- Line 284: The instruction text in AGENTS.md should capitalize the proper noun
“Google” in the sentence about creating the icon; update that guidance so the
reference to the Google icon uses the correctly capitalized name, and keep the
rest of the instruction unchanged.
In `@components/ai-chat/ai-chat-screen.tsx`:
- Around line 758-763: The first assistant bubble is being mutated before it
reaches AIResponseRenderer, so the integrity-logged render source no longer
matches the stored/validated assistant message. Update the AI-chat message
rendering path in ai-chat-screen.tsx so AIResponseRenderer receives the raw
assistant text for its content prop, and keep any greeting/prefix text separate
from the renderer’s logged source. Use the existing AIResponseRenderer call site
and the first-reply formatting logic that prepends the display name to ensure
the displayed UI can still show the greeting without altering the
integrity-checked content.
- Around line 239-251: The chat scroll handler in handleChatScroll only clears
the jump button state when near the bottom, so it never shows the control during
a manual upward scroll. Update handleChatScroll in ai-chat-screen.tsx so it also
calls setShowJumpToLatest(true) whenever isNearBottom is false, while keeping
isNearBottomRef.current in sync and preserving the existing hide behavior when
the user returns near the bottom.
In `@components/ai/ai-response-renderer.tsx`:
- Around line 72-73: The selectable render path in ai-response-renderer.tsx is
injecting U+200B into the same Text content that users can copy, which corrupts
copied URLs, IDs, and other unbroken tokens. Update the rendering logic around
AiResponseRenderer so selectable output keeps the original raw string, and move
any zero-width-space wrapping into a separate visual-only layer or disable that
injection when selectable is true. Check the token-wrapping logic in the
affected branches around the long-text/URL handling sections and the related
Text rendering helper so the displayed wrapping does not alter the copy source.
In `@components/insights/insights-screen.tsx`:
- Around line 125-134: Mood-log hydration failures are being treated as
successful readiness in the insights screen. Update insights-screen.tsx to
include the mood log store’s hydrationError alongside useJournalHydrationStore’s
hydrationError before deciding localDataHasHydrated and showHydrationState, so
the screen surfaces the failure instead of rendering as ready. Use the existing
useMoodLogStore selector and the localDataHasHydrated readiness gate as the main
place to apply the fix, and make sure any matching fallback logic around the
related render path also checks this error state.
In `@components/insights/report/ReportNarrativeBlocks.tsx`:
- Around line 106-110: Route the remaining `emotionalFlow` and `nextFocus`
narrative fields through `AIResponseRenderer` instead of rendering them as plain
`Text`, matching the existing shared AI rendering path used elsewhere in
`ReportNarrativeBlocks`. Update the relevant render branches for these sections
so they inherit markdown handling and `render_source` integrity logging, using
the same renderer component and props pattern already introduced for the other
report narrative blocks.
In `@hooks/useAIInsightReport.ts`:
- Around line 55-57: The new hydration guard in useAIInsightReport is incomplete
because useMoodLogStore can report hasHydrated=true even when rehydration
failed, so refresh() and requestGeneration() may proceed with missing mood logs
and overwrite a valid cached report. Update the checks around the mood log store
in useAIInsightReport to also block on state.hydrationError, mirroring how
cacheHydrationError is handled, and apply the same safeguard anywhere mood logs
are assumed ready in the refresh/requestGeneration flow. Use the existing
symbols useMoodLogStore, hasHydrated, hydrationError, refresh, and
requestGeneration to keep the condition consistent across the affected branches.
- Around line 90-96: The stale-source fingerprint in getLocalSource and
isReportStale still ignores mood-log-only changes, so update the local/report
contract to include mood-log identity data or the same snapshot hash used for
report freshness. Fold mood log IDs/counts (not just latestUpdatedAt) into the
source fingerprint returned from useAIInsightReport, and make isReportStale
compare against that expanded signature so syncs that add or change mood logs
invalidate the report correctly.
In `@lib/text/parse-ai-markdown.ts`:
- Around line 71-80: The table parsing in parse-ai-markdown.ts is too permissive
because the loop after isTableStart() treats any line containing "|" as part of
the table, causing non-table text to be swallowed. Update the row-collection
logic in the table block handling to validate each candidate line against the
table’s expected shape from the header/separator rows in addition to checking
for "|", and stop extending the table when the column count or structure no
longer matches.
In `@store/useChatStore.ts`:
- Around line 55-60: Keep the assistant surface label consistent across the
integrity pipeline by changing the `addMessage` path in `useChatStore` so
assistant messages use the same `surface` value as `remoteJournalAssistant` and
`ai-response-renderer` (that is, `ai_chat_message`) instead of deriving it from
`message.role`. Update the `logAITextIntegrity` call in `addMessage` so stored,
validate, and render stages can be correlated for assistant replies.
In `@supabase/functions/journal-ai-chat/index.ts`:
- Around line 1537-1540: The provider-response log in journal-ai-chat is missing
requestId, which makes it harder to correlate truncation metadata with a
specific request. Update the console.info call in the provider_response_received
path to include requestId, and verify the identifier is in scope at this call
site so the log stays consistent with the rest of the flow.
In `@supabase/functions/reflect-on-entry/index.ts`:
- Around line 942-945: The reflection text normalization in
ensureReflectionTerminal/isIncompleteReflectionText is allowing incomplete
endings like a trailing comma to become finalized after punctuation is appended.
Update the flow around the sanitization path in reflect-on-entry so incomplete
text is rejected before terminal normalization, or make ensureReflectionTerminal
preserve/refuse dangling punctuation; use the isIncompleteReflectionText check
and the terminal handling in the same validation path to catch cases before a
trailing period is added.
- Around line 690-691: Bound the provider-generated reflection lists in the
reflection parsing path, since getTrimmedStringArray currently accepts every
item for emotions and themes. Update the parsing logic around
parsedValue.emotions and parsedValue.themes to cap the number of returned
entries to a small fixed limit while still trimming and preserving valid items,
so the reflection card payload can’t grow unbounded from verbose or malformed
provider output.
In `@tests/report-analytics.test.ts`:
- Around line 108-136: Add a same-day mood-log precedence test in
report-analytics.test.ts to cover duplicate logs on the same date resolved by
latest updated_at. Extend the existing buildReportAnalytics setup by adding two
mood logs for the same day with different updated_at values, then assert that
moodTimeline picks the most recently updated mood as dominant. Use the existing
buildReportAnalytics, createMoodLog, and moodTimeline assertions to keep the
case near the current mood override tests.
---
Outside diff comments:
In `@components/auth/auth-screen.tsx`:
- Around line 610-618: The social button labels in auth-screen.tsx are
inconsistent because the Google and Apple variants use different text line
heights. Update the text styling in the shared social button area (the Text
elements used by the Google and Apple buttons) so both use leading-6, keeping
the paired buttons visually aligned and consistent.
🪄 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: Repository UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 367d1c83-cd48-4eb8-bd49-452d16cb8338
📒 Files selected for processing (40)
AGENTS.mdapp/(onboarding)/onboarding-screen-2.tsxapp/insights/report/[periodType].tsxcomponents/ai-chat/ai-chat-screen.tsxcomponents/ai/ai-response-renderer.tsxcomponents/auth/auth-screen.tsxcomponents/insights/RecurringThemesCard.tsxcomponents/insights/insights-screen.tsxcomponents/insights/report/MoodJourneyChart.tsxcomponents/insights/report/RecurringThemesChart.tsxcomponents/insights/report/ReportNarrativeBlocks.tsxcomponents/insights/report/ReportScreenStates.tsxcomponents/journal-editor/entry-ai-reflection-card.tsxdocs/dynamic-ai-text-rendering-audit.mddocs/dynamic-ai-text-test-matrix.mdhooks/useAIInsightReport.tslib/ai/entryReflectionService.tslib/ai/get-ai-text-length.tslib/ai/log-ai-text-integrity.tslib/ai/remoteJournalAssistant.tslib/insights/aiInsightReportMapper.tslib/insights/aiInsightReportService.tslib/insights/getPreferredMoodForRange.tslib/text/add-safe-break-opportunities.tslib/text/parse-ai-markdown.tslib/validation/persistedDataValidators.tsstore/useAIInsightReportStore.tsstore/useChatStore.tsstore/useEntryReflectionStore.tssupabase/functions/_shared/buildReportAnalytics.tssupabase/functions/_shared/parseReportNarrative.tssupabase/functions/generate-insight-report/index.tssupabase/functions/journal-ai-chat/index.tssupabase/functions/reflect-on-entry/index.tstests/ai-text-rendering.test.tstests/fixtures/ai-responses.tstests/preferred-mood.test.tstests/report-analytics.test.tstests/report-narrative-parser.test.tstypes/chat.ts
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
components/ai-chat/ai-chat-screen.tsx (1)
506-518: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winKeep the jump button on the same keyboard offset as the composer.
footerKeyboardOffsetlifts the footer on Android, but this new container ignores it. When the keyboard is open, the button stays in the old slot and can end up visually detached from or underneath the moved composer.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@components/ai-chat/ai-chat-screen.tsx` around lines 506 - 518, The jump-to-latest container in ai-chat-screen.tsx is not using the same keyboard offset as the composer, so it can մն stay fixed while the footer moves. Update the conditional View that renders when showJumpToLatest is true to apply the same footerKeyboardOffset handling used by the composer/footer layout, so the Pressable stays aligned with the lifted composer when the keyboard is open.
♻️ Duplicate comments (1)
hooks/useAIInsightReport.ts (1)
539-545: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winFail closed when snapshot hashes are unavailable.
moodLogCount/moodLogIdsare computed but only affect staleness throughsnapshotHash. For legacy reports with nosourceSnapshotHash, or while local hashing is unavailable, mood-log-only changes with olderupdatedAtcan still fall through as fresh.Proposed fix
- if ( - report.sourceSnapshotHash.trim() && - localSource.snapshotHash && - report.sourceSnapshotHash !== localSource.snapshotHash - ) { - return true; - } + const reportSnapshotHash = report.sourceSnapshotHash.trim(); + + if (reportSnapshotHash) { + if (!localSource.snapshotHash) { + return true; + } + + if (reportSnapshotHash !== localSource.snapshotHash) { + return true; + } + } else if (localSource.moodLogCount > 0) { + return true; + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@hooks/useAIInsightReport.ts` around lines 539 - 545, The staleness check in useAIInsightReport should fail closed when snapshot hashes are missing, because the current compare only uses report.sourceSnapshotHash and localSource.snapshotHash. Update the freshness logic around the source snapshot comparison to treat legacy reports with no sourceSnapshotHash, or cases where local hashing is unavailable, as stale when moodLogCount or moodLogIds could have changed. Use the existing report, localSource, moodLogCount, and moodLogIds values in this guard so the report is not considered fresh unless hash-based validation is actually possible.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@AGENTS.md`:
- Line 284: Fix the pronoun mismatch in the SVG guidance by updating the
instruction in AGENTS.md to use a plural reference for missing SVGs; the
sentence currently says “we don't have it” after referring to multiple SVGs.
Change that wording to “we don't have them” so the guidance stays consistent and
clear.
---
Outside diff comments:
In `@components/ai-chat/ai-chat-screen.tsx`:
- Around line 506-518: The jump-to-latest container in ai-chat-screen.tsx is not
using the same keyboard offset as the composer, so it can մն stay fixed while
the footer moves. Update the conditional View that renders when showJumpToLatest
is true to apply the same footerKeyboardOffset handling used by the
composer/footer layout, so the Pressable stays aligned with the lifted composer
when the keyboard is open.
---
Duplicate comments:
In `@hooks/useAIInsightReport.ts`:
- Around line 539-545: The staleness check in useAIInsightReport should fail
closed when snapshot hashes are missing, because the current compare only uses
report.sourceSnapshotHash and localSource.snapshotHash. Update the freshness
logic around the source snapshot comparison to treat legacy reports with no
sourceSnapshotHash, or cases where local hashing is unavailable, as stale when
moodLogCount or moodLogIds could have changed. Use the existing report,
localSource, moodLogCount, and moodLogIds values in this guard so the report is
not considered fresh unless hash-based validation is actually possible.
🪄 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: Repository UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: c49eca05-5acf-4a5c-9a1f-c0ba808e6439
📒 Files selected for processing (13)
AGENTS.mdcomponents/ai-chat/ai-chat-screen.tsxcomponents/ai/ai-response-renderer.tsxcomponents/auth/auth-screen.tsxcomponents/insights/insights-screen.tsxcomponents/insights/report/ReportNarrativeBlocks.tsxhooks/useAIInsightReport.tslib/text/parse-ai-markdown.tsstore/useChatStore.tssupabase/functions/journal-ai-chat/index.tssupabase/functions/reflect-on-entry/index.tstests/ai-text-rendering.test.tstests/report-analytics.test.ts
Summary by CodeRabbit
New Features
Bug Fixes
Documentation