mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
fix(coach): read free weekdays for a prompt only with the work section released
This commit is contained in:
1 parent
120b0966b1
commit
8ae60e2cb3
3 files changed
+67
-2
No files matched your search
@@ -15,7 +15,7 @@
|
||||
* values with weekday labels and aggregates the older window into
|
||||
* weekly buckets so the prompt budget stays tight.
|
||||
*/
|
||||
import { loadFreeWeekdaySpans } from "@/lib/profile/life-circumstances";
|
||||
import { loadFreeWeekdaySpansForPrompt } from "@/lib/profile/life-circumstances";
|
||||
import { prisma } from "@/lib/db";
|
||||
import { extractFeatures } from "@/lib/insights/features";
|
||||
import type { CoachDataCluster } from "@/lib/validations/coach-prefs";
|
||||
@@ -807,7 +807,7 @@ async function buildCoachSnapshotImpl(
|
||||
sourcePriorityJson: prefsRow?.sourcePriorityJson ?? null,
|
||||
userTz,
|
||||
ageYears: derivedProfile.ageYears,
|
||||
freeWeekdaySpans: await loadFreeWeekdaySpans(userId),
|
||||
freeWeekdaySpans: await loadFreeWeekdaySpansForPrompt(userId),
|
||||
snapshot,
|
||||
metrics,
|
||||
registerBlock,
|
||||
|
||||
@@ -47,6 +47,7 @@ import {
|
||||
computePackYears,
|
||||
DEFAULT_FREE_WEEKDAYS,
|
||||
isReviewDue,
|
||||
LIFE_KIND_AI_SECTION,
|
||||
screeningHints,
|
||||
type FreeWeekdaysSpan,
|
||||
type PackYears,
|
||||
@@ -214,6 +215,42 @@ export async function loadFreeWeekdaySpans(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The free-weekday answers for a prompt: only when the section they belong
|
||||
* to (`WORK`) is released to the AI. Otherwise the answers are never read or
|
||||
* decrypted for a prompt, and the night-by-night day type falls back to the
|
||||
* weekend default, exactly as for a record that never answered. The data
|
||||
* surfaces (the sleep page, the dashboard) read `loadFreeWeekdaySpans`
|
||||
* directly: they show the person their own answer, no model sees it.
|
||||
*/
|
||||
export async function loadFreeWeekdaySpansForPrompt(
|
||||
userId: string,
|
||||
): Promise<FreeWeekdaysSpan[]> {
|
||||
// Fail-soft and fail-closed, like the read below: a failed switch read
|
||||
// gives the weekend default and never the answers.
|
||||
try {
|
||||
const profile = await prisma.userHealthProfile.findUnique({
|
||||
where: { userId },
|
||||
select: { aiIncludedSections: true },
|
||||
});
|
||||
if (
|
||||
!storedSections(profile?.aiIncludedSections).has(
|
||||
LIFE_KIND_AI_SECTION.FREE_WEEKDAYS,
|
||||
)
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
} catch (error) {
|
||||
getEvent()?.addWarning(
|
||||
`free weekdays switch read failed: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
return loadFreeWeekdaySpans(userId);
|
||||
}
|
||||
|
||||
/** Assemble the whole read model for one record. */
|
||||
export async function buildLifeCircumstances(
|
||||
userId: string,
|
||||
|
||||
@@ -297,6 +297,34 @@ describe("life-circumstance facts", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("free weekdays in a prompt", () => {
|
||||
it("are read for a prompt only while the work section is released", async () => {
|
||||
const user = await makeUser();
|
||||
await signIn(user.id);
|
||||
await data(
|
||||
await postFact({ kind: "FREE_WEEKDAYS", value: VALUES.FREE_WEEKDAYS }),
|
||||
201,
|
||||
);
|
||||
const { loadFreeWeekdaySpans, loadFreeWeekdaySpansForPrompt } =
|
||||
await import("@/lib/profile/life-circumstances");
|
||||
|
||||
await getPrismaClient().userHealthProfile.upsert({
|
||||
where: { userId: user.id },
|
||||
create: { userId: user.id, aiIncludedSections: ["WORK"] },
|
||||
update: { aiIncludedSections: ["WORK"] },
|
||||
});
|
||||
expect(await loadFreeWeekdaySpansForPrompt(user.id)).toHaveLength(1);
|
||||
|
||||
await getPrismaClient().userHealthProfile.update({
|
||||
where: { userId: user.id },
|
||||
data: { aiIncludedSections: ["ABOUT_ME"] },
|
||||
});
|
||||
expect(await loadFreeWeekdaySpansForPrompt(user.id)).toEqual([]);
|
||||
// The data surfaces still show the person their own answer.
|
||||
expect(await loadFreeWeekdaySpans(user.id)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("AUDIT-C", () => {
|
||||
async function postAuditC(body: unknown): Promise<Response> {
|
||||
const { POST } = await import("@/app/api/anamnesis/audit-c/route");
|
||||
|
||||
Reference in new issue
Block a user