chore(release): v1.28.55 — the Today view comes to life

Bundles three daily-loop slices onto the Today view:
- Coach check-in: a due plan review surfaces as a calm Today card with
  keep/adjust/let-go, wired to the existing plan lifecycle; silence never
  changes a plan.
- Morning freshness: the sleep-dependent parts of the digest and score refresh
  the moment last night's sleep arrives (a debounced, timezone-correct per-user
  job across the Withings, WHOOP and Apple transports), with an honest
  provisional state until then. Adds migration 0244 (a per-user refresh marker).
- Daily push: an opt-in, once-a-day morning notification carrying the day's
  read, off by default, riding the existing dispatch cascade — a calm note,
  never an alert.

All read the overnight digest; nothing generates on open.
This commit is contained in:
Marc-André Bombeck committed 2026-07-16 23:33:33 +02:00
1 parent 04cb80da6a
commit e272453327
5 files changed
+48 -35

No files matched your search

@@ -6,7 +6,10 @@ import type { GenerateOutcome } from "@/lib/insights/comprehensive-generate";
const LOCAL_DATE = "2026-07-16";
function makePrisma(
user: { locale: string | null; morningDigestRefreshedOn: string | null } | null,
user: {
locale: string | null;
morningDigestRefreshedOn: string | null;
} | null,
) {
const updateMock = vi.fn(async () => ({}));
const prisma = {
@@ -30,12 +33,10 @@ describe("runMorningDigestRefresh", () => {
locale: "en",
morningDigestRefreshedOn: null,
});
const generate = vi.fn(
async (): Promise<GenerateOutcome> => ({
status: "generated",
providerType: "mock",
}),
);
const generate = vi.fn(async (): Promise<GenerateOutcome> => ({
status: "generated",
providerType: "mock",
}));
const result = await runMorningDigestRefresh(
prisma as never,
@@ -57,9 +58,9 @@ describe("runMorningDigestRefresh", () => {
locale: "de",
morningDigestRefreshedOn: null,
});
const generate = vi.fn(
async (): Promise<GenerateOutcome> => ({ status: "unchanged" }),
);
const generate = vi.fn(async (): Promise<GenerateOutcome> => ({
status: "unchanged",
}));
const result = await runMorningDigestRefresh(
prisma as never,
@@ -77,12 +78,10 @@ describe("runMorningDigestRefresh", () => {
locale: "en",
morningDigestRefreshedOn: null,
});
const generate = vi.fn(
async (): Promise<GenerateOutcome> => ({
status: "skipped",
reason: "no-provider",
}),
);
const generate = vi.fn(async (): Promise<GenerateOutcome> => ({
status: "skipped",
reason: "no-provider",
}));
const result = await runMorningDigestRefresh(
prisma as never,
@@ -99,12 +98,10 @@ describe("runMorningDigestRefresh", () => {
locale: "en",
morningDigestRefreshedOn: LOCAL_DATE,
});
const generate = vi.fn(
async (): Promise<GenerateOutcome> => ({
status: "generated",
providerType: "mock",
}),
);
const generate = vi.fn(async (): Promise<GenerateOutcome> => ({
status: "generated",
providerType: "mock",
}));
const result = await runMorningDigestRefresh(
prisma as never,
@@ -122,12 +119,10 @@ describe("runMorningDigestRefresh", () => {
locale: "en",
morningDigestRefreshedOn: null,
});
const generate = vi.fn(
async (): Promise<GenerateOutcome> => ({
status: "failed",
reason: "provider-timeout",
}),
);
const generate = vi.fn(async (): Promise<GenerateOutcome> => ({
status: "failed",
reason: "provider-timeout",
}));
const result = await runMorningDigestRefresh(
prisma as never,
@@ -142,9 +137,9 @@ describe("runMorningDigestRefresh", () => {
it("reports missing-user when the row is gone", async () => {
const { prisma, updateMock } = makePrisma(null);
const generate = vi.fn(
async (): Promise<GenerateOutcome> => ({ status: "unchanged" }),
);
const generate = vi.fn(async (): Promise<GenerateOutcome> => ({
status: "unchanged",
}));
const result = await runMorningDigestRefresh(
prisma as never,