mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 13:52:20 +00:00
docs(planning): record v1.4.20.1 hotfix wave
New `.planning/phase-v1421-hotfix-report.md` summarises the six fixes (Daily Briefing regenerate · Coach day-level snapshot · Coach prompt update · streaming-bubble dedup · drawer header · sources rail scope picker), the test-count delta (2026 → 2036 unit), the token-budget impact of the day-level snapshot (~190 → ~3000 tokens per full-scope turn), and a recommendation to ship as a v1.4.20.1 hotfix. STATE.md grows a Phase F section + status block tracking the six commits on develop. Includes prettier polish across the touched coach surfaces (sources-rail, drawer, message-thread, snapshot, chat route). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
08fd411ff1
commit
52dd9ff9c3
9 files changed
+253
-60
No files matched your search
@@ -143,12 +143,7 @@ async function handleChatRequest(request: NextRequest): Promise<Response> {
|
||||
{ status: 422 },
|
||||
);
|
||||
}
|
||||
const {
|
||||
conversationId,
|
||||
message,
|
||||
locale: bodyLocale,
|
||||
scope,
|
||||
} = parsed.data;
|
||||
const { conversationId, message, locale: bodyLocale, scope } = parsed.data;
|
||||
|
||||
await enforceBudget(userId);
|
||||
|
||||
|
||||
@@ -53,9 +53,8 @@ describe("<SourcesRail>", () => {
|
||||
|
||||
it("renders a checkbox per row + a window selector trigger", () => {
|
||||
const html = render(<SourcesRail />);
|
||||
const checkboxes = (
|
||||
html.match(/data-slot="coach-sources-checkbox"/g) ?? []
|
||||
).length;
|
||||
const checkboxes = (html.match(/data-slot="coach-sources-checkbox"/g) ?? [])
|
||||
.length;
|
||||
expect(checkboxes).toBe(5);
|
||||
expect(html).toContain('data-slot="coach-sources-window-trigger"');
|
||||
});
|
||||
|
||||
@@ -258,10 +258,7 @@ export function CoachDrawer({
|
||||
}
|
||||
sourcesRail={
|
||||
sourcesRail ?? (
|
||||
<SourcesRail
|
||||
scope={scope}
|
||||
onScopeChange={setScope}
|
||||
/>
|
||||
<SourcesRail scope={scope} onScopeChange={setScope} />
|
||||
)
|
||||
}
|
||||
thread={
|
||||
|
||||
@@ -67,9 +67,7 @@ export function MessageThread({
|
||||
messages.some((m) => m.id === streaming.messageId);
|
||||
const streamingActive =
|
||||
!streamingPersisted &&
|
||||
(!!streaming?.inProgress ||
|
||||
!!streaming?.content ||
|
||||
!!streaming?.errorCode);
|
||||
(!!streaming?.inProgress || !!streaming?.content || !!streaming?.errorCode);
|
||||
|
||||
// Track scroll position so we don't yank the viewport when the user
|
||||
// is browsing earlier turns.
|
||||
|
||||
@@ -82,7 +82,10 @@ export interface SourcesRailProps {
|
||||
* the controls still render but stay read-only — keeps the legend
|
||||
* surface stable for callers that don't yet wire scope through.
|
||||
*/
|
||||
onScopeChange?: (next: { sources: CoachScopeSource[]; window: CoachScopeWindow }) => void;
|
||||
onScopeChange?: (next: {
|
||||
sources: CoachScopeSource[];
|
||||
window: CoachScopeWindow;
|
||||
}) => void;
|
||||
}
|
||||
|
||||
interface SourceRow {
|
||||
|
||||
@@ -27,7 +27,11 @@ const featuresMock = extractFeatures as unknown as ReturnType<typeof vi.fn>;
|
||||
/**
|
||||
* Helper: produce a measurement row N days before "now" at 09:00 UTC.
|
||||
*/
|
||||
function daysAgo(n: number, value: number, type: string): {
|
||||
function daysAgo(
|
||||
n: number,
|
||||
value: number,
|
||||
type: string,
|
||||
): {
|
||||
type: string;
|
||||
value: number;
|
||||
measuredAt: Date;
|
||||
|
||||
@@ -53,15 +53,7 @@ const DEFAULT_SOURCES: ReadonlyArray<CoachScopeSource> = [
|
||||
"compliance",
|
||||
];
|
||||
|
||||
const WEEKDAY_KEYS = [
|
||||
"Sun",
|
||||
"Mon",
|
||||
"Tue",
|
||||
"Wed",
|
||||
"Thu",
|
||||
"Fri",
|
||||
"Sat",
|
||||
] as const;
|
||||
const WEEKDAY_KEYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] as const;
|
||||
|
||||
function windowToDays(window: CoachScopeWindow): number {
|
||||
switch (window) {
|
||||
@@ -163,7 +155,9 @@ function bucketWeekly(
|
||||
return Array.from(grouped.entries())
|
||||
.map(([weekISO, values]) => ({
|
||||
weekISO,
|
||||
mean: Math.round((values.reduce((s, v) => s + v, 0) / values.length) * 10) / 10,
|
||||
mean:
|
||||
Math.round((values.reduce((s, v) => s + v, 0) / values.length) * 10) /
|
||||
10,
|
||||
count: values.length,
|
||||
}))
|
||||
.sort((a, b) => a.weekISO.localeCompare(b.weekISO));
|
||||
@@ -177,8 +171,7 @@ function buildDailyValueRows(
|
||||
const grouped = dailyMeans(recent);
|
||||
return Array.from(grouped.entries())
|
||||
.map(([date, info]) => {
|
||||
const mean =
|
||||
info.values.reduce((s, v) => s + v, 0) / info.values.length;
|
||||
const mean = info.values.reduce((s, v) => s + v, 0) / info.values.length;
|
||||
return {
|
||||
date,
|
||||
weekday: utcWeekday(info.date),
|
||||
@@ -206,10 +199,8 @@ function buildDailyBpRows(
|
||||
for (const [day, info] of sysByDay) {
|
||||
const dia = diaByDay.get(day);
|
||||
if (!dia) continue;
|
||||
const sysMean =
|
||||
info.values.reduce((s, v) => s + v, 0) / info.values.length;
|
||||
const diaMean =
|
||||
dia.values.reduce((s, v) => s + v, 0) / dia.values.length;
|
||||
const sysMean = info.values.reduce((s, v) => s + v, 0) / info.values.length;
|
||||
const diaMean = dia.values.reduce((s, v) => s + v, 0) / dia.values.length;
|
||||
out.push({
|
||||
date: day,
|
||||
weekday: utcWeekday(info.date),
|
||||
@@ -310,10 +301,12 @@ export async function buildCoachSnapshot(
|
||||
: [];
|
||||
|
||||
const byType = (t: string) =>
|
||||
measurementRows.filter((r) => r.type === t).map((r) => ({
|
||||
measuredAt: r.measuredAt,
|
||||
value: r.value,
|
||||
}));
|
||||
measurementRows
|
||||
.filter((r) => r.type === t)
|
||||
.map((r) => ({
|
||||
measuredAt: r.measuredAt,
|
||||
value: r.value,
|
||||
}));
|
||||
|
||||
if (wantsBp && features.bloodPressure) {
|
||||
const sysRows = byType("BLOOD_PRESSURE_SYS");
|
||||
@@ -405,9 +398,7 @@ export async function buildCoachSnapshot(
|
||||
// Per-day adherence rate within the recent window. Older days
|
||||
// collapse into a single weekly bucket.
|
||||
const recent = intakeRows.filter((r) => r.scheduledFor >= recentCutoff);
|
||||
const olderRows = intakeRows.filter(
|
||||
(r) => r.scheduledFor < recentCutoff,
|
||||
);
|
||||
const olderRows = intakeRows.filter((r) => r.scheduledFor < recentCutoff);
|
||||
const recentByDay = new Map<
|
||||
string,
|
||||
{ date: Date; total: number; taken: number }
|
||||
|
||||
Reference in new issue
Block a user