Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(coach): replay the newest messages, not the oldest 40 - #69

Merged
saksham2001 merged 2 commits into
saksham2001:mainfrom
rgvxsthi:fix/coach-history-recency
Jul 12, 2026
Merged

saksham2001 merged 2 commits into
saksham2001:mainfrom
rgvxsthi:fix/coach-history-recency

Conversation

@rgvxsthi

@rgvxsthi rgvxsthi commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Bug

CoachViewModel.recentMessages builds the conversation history replayed to the model each turn. It fetched with:

sortBy: [SortDescriptor(\.createdAt, order: .forward)]   // OLDEST first
descriptor.fetchLimit = 40

fetchLimit is applied after the sort, so ascending + limit 40 returns the oldest 40 rows. .suffix(limit) then takes the last N of those oldest 40 → messages ~31–40.

Impact

  • Conversation ≤ 40 messages: the fetch returns the whole thread, so .suffix genuinely is the newest N. Works — which is why it isn't caught in normal testing.
  • Conversation > 40 messages: the fetch clamps to the oldest 40, so the replayed context is frozen at messages 31–40 forever — everything from message 41 on is never sent to the model. The coach silently "forgets" all recent turns, answers from stale mid-history, and re-sends the same block every turn (wasted tokens).

The chat continues the most recent thread by default (CoachView picks allMessages.last?.conversationId), so an engaged user's single conversation accumulates past 40 (~20 back-and-forth turns) and hits this. recentMessages is the only conversation history passed to the model (verified in CoachOrchestrator), so nothing else compensates.

Fix

Fetch the newest 40 (descending), restore chronological order, then .suffix(limit) as before:

sortBy: [SortDescriptor(\.createdAt, order: .reverse)]   // newest first
descriptor.fetchLimit = 40
let rows = Array(((try? context.fetch(descriptor)) ?? []).reversed())  // back to chronological

historyTurns is 4/10 so 40 remains an ample buffer after filtering the current turn + error bubbles. The ≤40-message case is unchanged. Latent bug (only bites long threads); builds clean.

recentMessages built the model's conversation history with an ASCENDING
(createdAt .forward) sort plus fetchLimit = 40, which returns the OLDEST 40
rows; .suffix(limit) then took messages ~31-40. For any conversation past 40
messages the replayed context froze at messages 31-40 and never included
anything newer — the coach silently 'forgot' all recent turns (and re-sent the
same stale block every turn). Conversations accumulate (the chat continues the
latest thread), so an engaged user crosses 40 messages and hits this.

Fetch the newest 40 (descending), reverse back to chronological, then suffix as
before. historyTurns is 4/10, so 40 stays an ample buffer. The <=40-message case
is unchanged. recentMessages is the only conversation history sent to the model.
@rgvxsthi
rgvxsthi requested a review from saksham2001 as a code owner July 9, 2026 11:35
@saksham2001
saksham2001 merged commit 8837f8a into saksham2001:main Jul 12, 2026
2 checks passed
@rgvxsthi
rgvxsthi deleted the fix/coach-history-recency branch July 12, 2026 09:15
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