diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index 632f8b07..a48cb6c9 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -4218,19 +4218,19 @@ ## BUG-283 | 今日星语每次进入首页都停在“没能生成” -- 状态:resolved(本地修复,待提交与发布) +- 状态:regressed - 首次发现:2026-08-18 -- 最近更新:2026-08-18 +- 最近更新:2026-08-19 - 影响面:`/` 首页“今日星语”卡片、`POST /api/daily-starlanguage`、进程内缓存与浏览器当日缓存 - 用户现象:资料完整的账号每次打开首页,卡片都显示“今天的星语没能生成,稍后再回来看看;这里不会用通用文案顶替。”,没有当日内容。 - 触发条件:出生时间可用于个人星盘的账号进入首页。冷路径或换实例后必现。 - 根因:BUG-265 把卡片改成同步等待 Agent,失败就不展示内容;BUG-269 修好了请求守卫,但首页仍把 30–45 秒的模型生成当作首屏依赖。进程内缓存不跨实例、不跨重启,JSON 解析或超时失败后客户端只剩失败文案。再进入等于再走一条冷路径。 - 修复:星盘、双轨 Dasha、分盘和过境算完后,立刻用这份证据拼一张个人卡片返回,不再等待模型。Agent 改为后台润色,成功才覆盖缓存。首页按账号+资料指纹把当天卡片写入 localStorage,刷新先显示已有内容;只有星盘算不出来才落到失败文案。日期改用出生时区日历日,不再用 UTC。 - 验证:`frontend/tests/daily-starlanguage.test.ts` 覆盖证据卡片因盘而异、未确认出生时间不用上升宫、请求路径不阻塞 Agent、失败不覆盖已缓存卡片。 -- 防复发:首页入口卡片不得把 LLM 生成当作首屏成功条件。禁止用固定四句文案池顶替;也禁止在生成失败时让整张卡空白。 -- 相关记录:BUG-265、BUG-269 +- 防复发:首页入口卡片不得把 LLM 生成当作首屏成功条件。禁止用固定四句文案池顶替;也禁止在生成失败时让整张卡空白。这条防复发没有挡住请求路径继续等待完整 `/api/chart` 加四层附加计算,复发见 BUG-299。 +- 相关记录:BUG-265、BUG-269、BUG-299 - 复发自:BUG-265(诚实降级正确,但把模型生成当成了首页可用性) -- 修复版本:待提交 +- 修复版本:`9d8b91ac`(staging,已被 BUG-299 覆盖) ## BUG-284 | 技能激活的 77% 是 1592 条无说明的文件名,方法论从未进入任何一次 web 回答(referenceReads 恒为 0) @@ -4474,3 +4474,19 @@ - 相关记录:BUG-011、BUG-287 - 复发自:BUG-011 - 修复版本:待提交 + +## BUG-299 | 今日星语首屏仍失败:证据卡还在等完整星盘 + +- 状态:resolved +- 首次发现:2026-08-19 +- 最近更新:2026-08-19 +- 影响面:`/` 首页“今日星语”卡片、`POST /api/daily-starlanguage`、`/api/daily_guidance` +- 用户现象:资料完整的账号一进首页就看到“今天的星语没能生成。点这里从今日问起;不会用通用文案顶替。”,没有当日内容。 +- 触发条件:出生时间可用于个人星盘的账号打开首页。冷路径必现。 +- 根因:BUG-283 不再等模型,但首屏仍串行调用完整 `/api/chart`(含 VedAstro 总览、Shadbala、瑜伽等)再并行等 Vimshottari、Narayana、D9/D10、过境,最坏约 40 秒,超过路由 `maxDuration = 30`。超时后客户端只剩失败文案。失败文案还写了“点这里”,而整张卡本来就是点击热区。仓库里已有轻量 `/api/daily_guidance`(本命盘 + 今日月亮过境宫 + 当前大运),首页没用。 +- 修复:首屏只调 `/api/daily_guidance`。卡片按今日月亮过境宫与当前大运拼句,不用四句通用文案池,也不用本命月亮宫冒充今日。Agent 润色仍在后台。失败态改成“今天的星语还没写出来。”,行动改为“从今日问起”。引擎超时 8 秒,路由上限 20 秒。 +- 验证:`frontend/tests/daily-starlanguage.test.ts` 锁定 `/api/daily_guidance`、禁止首屏 `/api/chart`、今日过境宫与本命月宫区分、失败文案不含“点这里”;`tests/test_daily_guidance_service.py` 锁定结构化宫位/大运且不同日期会变。 +- 防复发:首页日签不得把完整 `/api/chart` 或四层附加计算当作首屏成功条件。失败文案不得写“点这里”,也不得解释内部“不用通用文案”政策。 +- 相关记录:BUG-265、BUG-269、BUG-283 +- 复发自:BUG-283(不再等模型,但仍把完整排盘当首页可用性) +- 修复版本:待提交 diff --git a/frontend/src/app/api/daily-starlanguage/route.ts b/frontend/src/app/api/daily-starlanguage/route.ts index 3f28b370..7f3f6d29 100644 --- a/frontend/src/app/api/daily-starlanguage/route.ts +++ b/frontend/src/app/api/daily-starlanguage/route.ts @@ -4,8 +4,8 @@ import { composeDailyStarlanguageCard, dailyStarlanguageCacheKey, dailyStarlanguageCardSchema, - dailyStarlanguageEvidence, dailyStarlanguagePrompt, + evidenceFromDailyGuidance, parseDailyStarlanguageText, type DailyStarlanguageCard, type DailyStarlanguageEvidence, @@ -19,7 +19,7 @@ import { createServerSupabaseClient } from "@/lib/supabase/server"; import { getDailyStarlanguageAgent } from "@/mastra"; export const runtime = "nodejs"; -export const maxDuration = 30; +export const maxDuration = 20; type Profile = GlobalBirthProfile & { birthTimeStatus?: string; timezoneId?: string }; type BirthPayload = NonNullable>>; @@ -35,7 +35,7 @@ type Generated = | { readonly kind: "failed"; readonly reason: "chart_unavailable" }; const jyotishApiBase = process.env.JYOTISH_API_BASE ?? "http://127.0.0.1:5200"; -const engineTimeoutMs = 20_000; +const engineTimeoutMs = 8_000; const agentTimeoutMs = 45_000; const cacheLimit = 500; @@ -89,37 +89,17 @@ async function fetchEngine(path: string, body: Record) { return await response.json() as Record; } -function chartPoints(chart: Record) { - const modules = chart.modules && typeof chart.modules === "object" ? chart.modules as Record : {}; - const chartModule = modules.chart && typeof modules.chart === "object" ? modules.chart as Record : chart; - const planets = chartModule.planets && typeof chartModule.planets === "object" ? chartModule.planets : undefined; - const ascendant = chartModule.ascendant && typeof chartModule.ascendant === "object" ? chartModule.ascendant : undefined; - return planets && ascendant ? { planets, ascendant } : null; -} - -async function collectLayers(payload: BirthPayload, today: string) { - const chart = await fetchEngine("/api/chart", payload).catch((error: unknown) => { - console.warn("daily_starlanguage_chart_unavailable", error); +async function loadDailyGuidance(payload: BirthPayload, today: string) { + const packet = await fetchEngine("/api/daily_guidance", { + ...payload, + date: today, + reference_date: today, + }).catch((error: unknown) => { + console.warn("daily_starlanguage_guidance_unavailable", error); return null; }); - const points = chart === null ? null : chartPoints(chart); - if (chart === null || !points) return null; - const tomorrow = new Date(`${today}T00:00:00.000Z`); - tomorrow.setUTCDate(tomorrow.getUTCDate() + 1); - const withPoints = { ...payload, planets: points.planets, ascendant: points.ascendant }; - const [vimshottari, narayana, varga, transit] = await Promise.all([ - fetchEngine("/api/dasha", { ...withPoints, dasha: "vimshottari", today }).catch(() => null), - fetchEngine("/api/dasha", { ...withPoints, dasha: "narayana", today }).catch(() => null), - fetchEngine("/api/varga_full", { ...withPoints, divisions: ["D9", "D10"] }).catch(() => null), - fetchEngine("/api/transit", { - natal_planets: points.planets, - ascendant: points.ascendant, - start: today, - end: tomorrow.toISOString().slice(0, 10), - planets_to_check: ["Saturn", "Jupiter", "Rahu", "Ketu", "Mars", "Sun"], - }).catch(() => null), - ]); - return { chart, vimshottari, narayana, varga, transit }; + if (!packet || packet.success !== true) return null; + return packet; } async function polishWithAgent(evidence: DailyStarlanguageEvidence): Promise { @@ -158,9 +138,10 @@ function scheduleAgentPolish(key: string, today: string, evidence: DailyStarlang } async function generateCard(payload: BirthPayload, profile: Profile, today: string): Promise { - const layers = await collectLayers(payload, today); - if (!layers) return { kind: "failed", reason: "chart_unavailable" }; - const evidence = dailyStarlanguageEvidence(layers, today, profile.birthTimeStatus === "confirmed"); + const packet = await loadDailyGuidance(payload, today); + if (!packet) return { kind: "failed", reason: "chart_unavailable" }; + const evidence = evidenceFromDailyGuidance(packet, today, profile.birthTimeStatus === "confirmed"); + if (!evidence) return { kind: "failed", reason: "chart_unavailable" }; return { kind: "card", card: composeDailyStarlanguageCard(evidence), diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index afe88b96..86cc605a 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -1297,6 +1297,20 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class text-wrap: pretty; } +.product-entrypoint-copy p[aria-busy="true"] { + color: var(--color-ink-tertiary); +} + +@media (prefers-reduced-motion: no-preference) { + .product-entrypoint-copy p[aria-busy="true"] { + animation: daily-starlanguage-pending 1.6s ease-in-out infinite; + } +} + +@keyframes daily-starlanguage-pending { + 50% { opacity: .55; } +} + .product-entrypoint-footer { display: flex; align-items: flex-end; diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index e3adb6b4..3daa9506 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -1274,12 +1274,18 @@ export default function Home() { const dailyStarlanguageFingerprint = dailyStarlanguageProfileKey(profile); const starterThemes = personalChartAvailable ? themes : generalGuidedJyotishTopics; + const dailyStarlanguageQuestion = !personalChartAvailable + ? "请帮我看一下今天的运势,重点告诉我适合推进什么、需要注意什么。" + : dailyStarlanguage.kind === "unavailable" + ? "从今日问起" + : "深入看今日"; const dailyStarlanguageTrend = dailyStarlanguage.kind === "ready" ? dailyStarlanguage.card.trend : dailyStarlanguage.kind === "pending" - ? "正在结合你的星盘写今天的星语。" - : "今天的星语没能生成。点这里从今日问起;不会用通用文案顶替。"; + ? "正在写下今天的星语。" + : "今天的星语还没写出来。"; const dailyStarlanguageAction = dailyStarlanguage.kind === "ready" ? dailyStarlanguage.card.action : ""; + const dailyStarlanguageBusy = personalChartAvailable && dailyStarlanguage.kind === "pending"; const onboardingPending = profileComplete && !onboarding && !onboardingError; const currentOnboardingMessage = onboardingJustCompleted ? startGreeting || completedOnboardingMessage(profileDraft.name.trim()) @@ -2448,9 +2454,7 @@ export default function Home() { function draftDailyStarlanguageQuestion() { chooseSuggestedQuestion( - personalChartAvailable - ? "深入看今日" - : "请帮我看一下今天的运势,重点告诉我适合推进什么、需要注意什么。", + dailyStarlanguageQuestion, "timing", personalChartAvailable ? "daily_starlanguage" : null, ); @@ -3550,17 +3554,20 @@ export default function Home() {