Add a Monday-morning weekly recap briefing

No US session closes into Monday KST, so Mondays (default 07:00, cron
in the daemon with restart catch-up) now publish a weekly review built
entirely from stored data: the quotes table collapsed into week-over-
week changes and the week's daily briefing headlines as the narrative.
The recap is a BriefingResult kind="weekly" reusing MarketPayload, so
the store, verification pass, all delivery channels (Blogger title
"미국 주간 리뷰 <monday>") and the dashboard (주간 리뷰 badge) work
unchanged. Manual runs via `run-once --weekly [--date <monday>]`.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jsahn
2026-09-12 23:52:31 +09:00
co-authored by Claude Fable 5
parent 1e6272da74
commit f5fa7ec70f
22 changed files with 995 additions and 33 deletions
+9 -2
View File
@@ -166,7 +166,11 @@ function renderList(
button.setAttribute("aria-current", String(s.krx_date === selected));
button.append(el("span", "briefing-date", `${s.krx_date} (KRX)`));
const line = el("span", "briefing-line");
line.append(`세션 ${s.session_date}`, badgeFor(s));
if (s.kind === "weekly") {
line.append(el("span", "badge badge-week", "주간 리뷰"), `~${s.session_date}`, badgeFor(s));
} else {
line.append(`세션 ${s.session_date}`, badgeFor(s));
}
if (s.data_gaps.length > 0) line.append(el("span", "badge badge-warn", `결측 ${s.data_gaps.length}`));
button.append(line);
if (s.headline) button.append(el("span", "briefing-line", s.headline));
@@ -179,8 +183,11 @@ function renderList(
function metaChips(result: BriefingResult): HTMLElement[] {
const p = result.payload;
const close = new Date(p.us_close_time);
const weekly = result.kind === "weekly";
const chips: HTMLElement[] = [
el("span", undefined, `미국 세션 ${p.session_date} → KRX ${p.krx_date}`),
weekly
? el("span", "badge badge-week", "주간 리뷰")
: el("span", undefined, `미국 세션 ${p.session_date} → KRX ${p.krx_date}`),
el(
"span",
undefined,
+1
View File
@@ -67,6 +67,7 @@ async function listDates(): Promise<string[]> {
function summarize(krxDate: string, r: BriefingResult): BriefingSummary {
return {
krx_date: krxDate,
kind: r.kind ?? "briefing",
session_date: r.payload.session_date,
generated_at: r.payload.generated_at,
krx_open_today: r.payload.krx_open_today,
+3
View File
@@ -93,12 +93,15 @@ export interface BriefingResult {
error: string | null;
/** Absent/null: verification not run (disabled, degraded, verifier failed, or pre-feature archive). */
verification?: Verification | null;
/** "weekly": Monday recap (quotes carry week-over-week changes). Absent on older archives. */
kind?: "briefing" | "weekly";
}
// ---------------------------------------------------------------- API shapes
export interface BriefingSummary {
krx_date: string;
kind: "briefing" | "weekly";
session_date: string;
generated_at: string;
krx_open_today: boolean;
+1
View File
@@ -205,6 +205,7 @@ button[aria-pressed="true"] { background: var(--grid); }
.badge-ok { background: var(--badge-ok-bg); color: var(--badge-ok-ink); }
.badge-warn { background: var(--badge-warn-bg); color: var(--badge-warn-ink); }
.badge-error { background: var(--badge-err-bg); color: var(--badge-err-ink); }
.badge-week { border: 1px solid var(--series-1); color: var(--series-1); background: transparent; }
/* ------------------------------------------------------------------ summary card */
.summary-card {