feat(coach): default analysis window as user preference + per-conversation override

`coachPrefs.defaultWindow` joins the existing prefs blob (tone,
verbosity, excludeMetrics, showEvidenceByDefault). The picker lives in
the Coach settings sheet; the drawer header carries a small pill that
overrides the saved default for the current chat only. Closing the
drawer drops the override so the next session starts on the saved
preference. Backwards-compatible: legacy rows missing the key resolve
to "allTime", matching the v1.4.24 behaviour. The chat route folds the
saved window into snapshot scope when the client doesn't supply one.
This commit is contained in:
Marc-André Bombeck committed 2026-05-14 13:28:10 +02:00
1 parent 2bd29022b1
commit 330be965ac
10 files changed
+331 -8

No files matched your search

+11 -1
View File
@@ -206,12 +206,22 @@ async function handleChatRequest(request: NextRequest): Promise<Response> {
// the new user message. v1.4.23 H4 — fold per-user prefs into the
// system-prompt prefix; the snapshot builder reads the same prefs
// separately so excluded metrics never even leave the DB.
//
// v1.4.25 W5 — `coachPrefs.defaultWindow` is the user's saved
// analysis-window preference. Merge it into the snapshot scope when
// the client didn't supply a per-conversation override; the override
// (header pill / sources rail) always wins. Keep the merge cheap so
// we don't accidentally widen narrow per-call scopes.
const prefsRow = await prisma.user.findUnique({
where: { id: userId },
select: { coachPrefsJson: true },
});
const coachPrefs = parseCoachPrefs(prefsRow?.coachPrefsJson);
const snapshot = await buildCoachSnapshot(userId, scope);
const effectiveScope =
scope?.window === undefined && coachPrefs.defaultWindow
? { ...(scope ?? {}), window: coachPrefs.defaultWindow }
: scope;
const snapshot = await buildCoachSnapshot(userId, effectiveScope);
const systemPrompt = getCoachSystemPrompt(locale, coachPrefs);
const window = buildHistoryWindow([
...priorTurns,