diff --git a/CHANGELOG.md b/CHANGELOG.md index 98c688da..dd0588e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ - 历史校正会话的名字改成显示算出来的出生时间或当前范围,不再在标题里重复日期。 - 今日节奏仍带日期。手改过的名字不动。Skill 版本不 bump。 +## 2026-09-25 — 控制台少两类红字 + +- 控制台不再出现星盘 SVG 属性警告,也没有后台任务时不再出现 404。 +- Skill 版本不 bump。 + ## 2026-09-24 — 报告操作收敛与桌面导出居中(本地未提交,依赖验收 blocked) - 报告列表已完成行只保留「查看报告」;阅读页仅保留返回和导出图标,不再自动消费旧 `#export` 链接。 diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index c5ff3e94..b5821104 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -13604,6 +13604,38 @@ - 复发自:BUG-999同输出边界的未覆盖换行路径,继承剥离器缺陷,非本轮新增回归。 - 修复版本:本地待提交。 +## BUG-906 | 星盘 SVG 把 CSS 的 height:auto 写成了属性 + +- 状态:investigating(分支已修,定向回归见进度记录;未部署,不标 resolved) +- 首次发现:2026-09-16 +- 最近更新:2026-09-25 +- 影响面:星盘页西洋盘 `WesternWheelSvg`、报告与星盘页共用的 `VedicChartSvg`。 +- 用户现象:控制台反复出现 `Error: attribute height: Expected length, "auto".`。盘仍能画出,但每张盘一条;报告页多张盘就多条。 +- 触发条件:打开星盘页西洋盘,或打开含北印度盘的报告页。 +- 根因:SVG 表现属性 `height` 只接受长度或百分比。`auto` 只是 CSS 值。星盘页与报告盘面把样式表里的 `height: auto` 写进了属性,浏览器丢弃属性并报警告。`.personal-report-chart-svg` 与 `.chart-page-western-svg` 的样式表规则已经包含 `height: auto`,属性是多余的。 +- 修复:删掉这两个组件上的 `height="auto"`。`viewBox` 与 `width="100%"` 不动,不新增 CSS。离线 Markdown 导出没有样式表,改为在序列化后的 ``,没有 `height="auto"`。`tsc --noEmit` 0 错。尚无 staging 控制台截图。数字见 `docs/tasks/PROGRESS-console-noise-20260916.md`。 +- 防复发:星盘 SVG 合同禁止再写 `height="auto"`。高度自适应只放在已有的 CSS 规则里;离线导出要长度时写在导出序列化,不写回组件属性。 +- 相关记录:无。引入提交为星盘页 `830799fa` 与报告盘面 `2fdcb14f`,不是已编号缺陷的复发。 +- 复发自:无 +- 修复版本:本分支 `codex/console-noise-20260916`,待提交与部署核对。 + +## BUG-907 | 没有后台咨询任务时,状态查询仍返回 404 + +- 状态:investigating(分支已修,定向回归见进度记录;未部署,不标 resolved) +- 首次发现:2026-09-16 +- 最近更新:2026-09-25 +- 影响面:`GET /api/consult/status` 无参查询,以及首页启动、页面重新可见、`online` 时的 `fetchActiveConsultationStatus`。 +- 用户现象:没有后台任务时,控制台出现 `GET /api/consult/status 404 (Not Found)`。切回标签页会再出现一次。业务上没有失败。 +- 触发条件:已登录且当前没有 `reserved` 咨询行,客户端不带 `sessionId` 与 `requestId` 查询活动任务。 +- 根因:无活动任务和「指定的 requestId 不存在」共用了同一个空结果 404。前者是常态空结果,浏览器仍把非 2xx 打成红字。带 `sessionId+requestId` 的 404 仍是 BUG-276 刷新恢复的计数依据,不能改。 +- 修复:`activeLookup` 查空返回 `204` 且无 body;带参查空仍返回 404「咨询请求不存在」,两支不再共用一个 `if (!data)`。`fetchActiveConsultationStatus` 在 `response.json()` 之前同时把 204 和 404 当成没有活动任务。带参的 `fetchConsultationStatus` 不改。 +- 验证:`consultation-stream-recovery.test.ts` 原结构断言保留,并新增 204 / 404 分叉与无参客户端兼容断言。`consultation-recovery.test.ts` 仍要求带参 404 计入缺失次数。`tsc --noEmit` 0 错。尚无 staging Network 证据。数字见 `docs/tasks/PROGRESS-console-noise-20260916.md`。 +- 防复发:无参空结果不得再返回 404。带参「咨询请求不存在」必须保持 404。客户端读无参状态必须先看 204/404,不得先解析没有 body 的 204。 +- 相关记录:BUG-276(带参 404 与刷新重放,本条不改那条语义)。控制台第三条 `reportAllChanges` 读取 `startTime` 的报错是浏览器扩展注入脚本,非本仓代码,不加 web-vitals、不加 try/catch;验证方法是无痕窗口。 +- 复发自:无 +- 修复版本:本分支 `codex/console-noise-20260916`,待提交与部署核对。 + ## BUG-1001 | 校正会话标题仍写日期,同一天多条无法区分 - 状态:resolved diff --git a/docs/tasks/PROGRESS-console-noise-20260916.md b/docs/tasks/PROGRESS-console-noise-20260916.md new file mode 100644 index 00000000..2a71e470 --- /dev/null +++ b/docs/tasks/PROGRESS-console-noise-20260916.md @@ -0,0 +1,40 @@ +# PROGRESS · 控制台噪音清理(2026-09-16 任务书,2026-09-25 执行) + +## 当前结论 + +T1–T3 已在本分支做完,未 push。BUG-906 / BUG-907 保持 investigating:定向回归已过,没有 staging 控制台或 Network 证据,不标 resolved。第三条 `reportAllChanges … startTime` 未处理。 + +- 实施基线:`514951d7`(`origin/staging`,2026-09-25 已 fetch)。任务书成文基线 `05cf20fb` 只作事故实证。 +- 分支 / 工作树:`codex/console-noise-20260916` / `G:\Ferti\Jyotisha\.worktrees\console-noise-20260916`。未 stash,未切分支,未 npm install,未动 `node_modules` junction。 + +## 做了什么 + +| 项 | 实现 | +| --- | --- | +| T1 SVG 属性 | 删掉 `western-wheel-svg.tsx` 与 `vedic-chart-svg.tsx` 上的 `height="auto"`。`viewBox` 与 `width="100%"` 未动。没有新增 CSS。 | +| T1 离线导出 | `report-chart-export.tsx` 在 `` 拍平成一条「最近」、星盘的两个入口(侧栏分组 + 账户菜单)收敛到一处、删掉逐条助手头像。**决策记录 D3 推翻 DESIGN.md「报告强调色与应用同源」一句**(报告刻意保留深棕)。原型图 https://claude.ai/code/artifact/da275da6-2954-4f50-99aa-32bb8694d38b(三套画面 + 明暗,页面标题就是建议字体栈的实际渲染)。环境缺口:无登录态无 Chrome,四项真机观感留 `docs/testing/`。BUG 段 737–738 | 待领取 | — | | `TASK-cend-surfaces-claude-alignment-20260916.md` | `PROGRESS-cend-shell-20260916.md`、`PROGRESS-cend-report-20260916.md`、`PROGRESS-cend-rectification-20260916.md`、`PROGRESS-cend-chart-eph-20260916.md` | **次级页面对齐(上一单的续篇,R4→R5/R6,R7、R8 可并行)**:星盘 `/chart`、星历 `/ephemeris`、报告 `/reports` **各是脱离 app 外壳的独立全屏页**,顶部只有一个「返回对话」链接、侧栏整个消失,且三家各写了一套一模一样的 `*-shell`/`*-topbar`/`*-hero` 骨架——与上一单 E5 同根因(营销站 band 结构被套到产品界面)。**R4** 抽只读导航外壳 `AppNavRail`(只用现成的 `GET /api/sessions` + `GET /api/account`,会话行走 `sessionHref` 跳 `/?c=`;**刻意不带**重命名/删除/收藏/归档——那套连着 `Home()` 的乐观更新与回滚,搬过来会撞 useState 增长门禁)。**R5** 星盘五 tab 下划线化 + 参数合表 + 行星表横向滚动;星历日期导航改 `‹ 日期 ›`。**R6** 报告中心卡片网格改行式列表;阅读页加常驻目录。**R7** 生时校正把可信区间从盘面板标题行提成常驻条(窄屏 `.is-compact` 下盘面板是 overlay,现在默认看不到区间),五个 `technique-audit` 折叠块收成两段。**R8** 设置内容区收窄(880px 弹窗里表单铺了 690px)、套餐卡三修饰符收敛成两态。**已解锁**:原挡路的设置单已于 `111b4a84`(BUG-698)合入。**两条不得回退**:BUG-698 的 `@supports (height: 1dvh)` 写法(重复声明回退会被 Lightning CSS 折叠)、BUG-616/617 的报告盘面 grid 实现。默认不占 BUG 号 | **R4–R8 全部已实现并验收合入** | R4:抽出 `AppNavRail`(只读,两个 GET,零写操作)+ `SecondaryShell`,三个次级页并入 app 外壳并删掉各自的 shell/topbar/hero;四个路由渲染标记**完全不变**(`/` `/chart` `/ephemeris` 仍 Static);CSS gzip −0.25%。`/reports/[reportId]` 留给 R6 与目录一起做。两处自身健壮性问题被测试抓到:`usePathname()` 可为 null、`fetch` 可能不存在。差点弄丢 BUG-717 的 eyebrow 文案(已放回)。R8:表单分区收窄到 440px(列表分区不变)、套餐卡三修饰符收敛成互斥的 `is-current` / `is-recommended`,`--highlighted` 删除改为滚动定位;手机端 `order:-1` 改挂 `[data-plan-alias]`(版位不是状态)。测试 3350→3354(净增 4),失败清单与基线逐条一致;`/` 仍 Static;我的干净构建实测 CSS gzip −3 字节。**遗留待产品拍板**:`?plan=` 深链现在完全没有视觉指向,只有滚动位置。R6:报告中心卡片网格改行式列表、阅读页并入外壳并把目录挪到右侧常驻。**任务书 E10 过期**——目录在 `cfcd369d` 就已存在,本轮是挪位置定稿而非从零加。挂外壳带出一个真实打印风险已处理:`.chat-app`/`.chat-panel` 是 `height:100%;overflow:hidden`,裸 `window.print()` 会把九节报告裁成一页,阅读页因此多挂一条只在挂载期生效的 print 样式解锁外壳。「生成中的分节进度」做不了——`REPORT_LIST_COLUMNS` 不返回节数,按 VOICE.md 不许前端编。R7:区间常驻条与盘面折叠收敛。**任务书 E9 也不准确**——对话区顶部早有常驻条 `RectificationTimeline` 且窄屏可见,真正只在盘面标题行的是**代表分钟**;因此没另造第二条,在既有条上补齐代表分钟与已答题数(与盘面同一次 `workingRectificationTime()` 调用)。折叠块实际是 **8 个**不是 5 个。**触发让步顺序第 5 条**:收窄进度未做——服务端无该字段,且 `candidate_range` 会放宽(BUG-572),前端相减会把一次放宽报成收窄,已写进 `BLOCKED.md`。顺带修掉一个**静默失效的旧断言**(`slice(indexOf(A), indexOf(B))` 在 B 改名后变成几乎整份文件,四条 `doesNotMatch` 假通过)| | — (产品口头拍板,无任务书) | `PROGRESS-chart-glyphs-20260918.md` | **星盘改用行星符号,度数进行星表(非 Bug,无 BUG 号)**:北印星盘(星盘页 / 校正右栏 / 我的报告三处共用 `VedicChartSvg`)宫内不再画「水 19°」「罗逆 29°」这类文本——一宫三颗星就叠三行、字号压到 8。改成九个彩色符号(☉☽☿♀♂♃♄☊☋),每行最多 3 个、逆行改符号下同色横线、第 1 宫加 `As`、盘下两行图例。颜色只认星不表吉凶,罗计共用中性灰,`--color-planet-*` 九个 token 在 `:root` 与两个深色块各一份;颜色挂 `is-*` 修饰类而非行内 `style`(报告标记合同禁 `style=`)。符号字体是 vendored 的 9 字形 Noto Sans Symbols 子集(OFL,1.5 KB),`--font-planet-glyphs`,♀♂ 带 U+FE0E 防 iOS 画成表情。星盘页行星表四列扩到八列(行星 / 黄道经度 / 速度 / 宫位 / 星宿 / 宿主 / pada / 状态),度数精确到分,首行是上升(`vedic.ascendant` 新字段)。数据来源:`toNorthIndianChart()` 加结构化 `occupantGlyphs`;校正与报告两条路线的模型只有引擎/golden 显示文本,按首字解析并有测试锁住。**星宿未译中文**——仓库无对照表,印度 27 宿与中国二十八宿不同源,不自造 | 已实现,待验收 | `codex/chart-glyphs-20260918`:tsc 0 / lint 0 error(118 warning 不变)/ npm test 3471→3484 条、36 红与基线 `84b293fb` 逐条相同、0 新红 / `○ /` Static / 首屏 JS gzip 1,450,774→1,455,017(+0.29%),CSS gzip +2.23%(构成见 PROGRESS §2)。真机走查欠(iOS 符号呈现、深浅两主题配色、375px 横滚、打印) | -| `TASK-console-noise-20260916.md` | `PROGRESS-console-noise-20260916.md` | 控制台噪音清理:两个星盘 SVG 写了非法的 `height="auto"` 属性,每张盘报一次(BUG-906);`/api/consult/status` 无参查询在没有后台任务时返回 404,浏览器每次切回标签页都打红字,改 204、带参路径仍 404(BUG-907)。第三条 `reportAllChanges … startTime` 是浏览器扩展注入脚本,非本仓代码,不处理 | 待领取 | — | +| `TASK-console-noise-20260916.md` | `PROGRESS-console-noise-20260916.md` | 控制台噪音清理:两个星盘 SVG 写了非法的 `height="auto"` 属性,每张盘报一次(BUG-906);`/api/consult/status` 无参查询在没有后台任务时返回 404,浏览器每次切回标签页都打红字,改 204、带参路径仍 404(BUG-907)。第三条 `reportAllChanges … startTime` 是浏览器扩展注入脚本,非本仓代码,不处理 | 待验收 | `codex/console-noise-20260916` | ### 待开的任务书(2026-09-03 审查决定) diff --git a/frontend/src/app/api/consult/status/route.ts b/frontend/src/app/api/consult/status/route.ts index d270b58c..22adecb9 100644 --- a/frontend/src/app/api/consult/status/route.ts +++ b/frontend/src/app/api/consult/status/route.ts @@ -67,7 +67,11 @@ export async function GET(request: Request) { if (error) { return NextResponse.json({ error: "暂时无法读取咨询状态" }, { status: 503 }); } - if (!data || typeof data.session_id !== "string") { + if (activeLookup) { + if (!data || typeof data.session_id !== "string") { + return new NextResponse(null, { status: 204 }); + } + } else if (!data || typeof data.session_id !== "string") { return NextResponse.json({ error: "咨询请求不存在" }, { status: 404 }); } diff --git a/frontend/src/components/chart-page/western-wheel-svg.tsx b/frontend/src/components/chart-page/western-wheel-svg.tsx index 51ba30c2..b22ab824 100644 --- a/frontend/src/components/chart-page/western-wheel-svg.tsx +++ b/frontend/src/components/chart-page/western-wheel-svg.tsx @@ -32,7 +32,6 @@ export function WesternWheelSvg({ western: suppliedWestern, skeleton = false }: { const response = await fetch("/api/consult/status", { signal, cache: "no-store" }); + if (response.status === 204 || response.status === 404) return null; const payload: unknown = await response.json().catch(() => null); - if (response.status === 404) return null; if (!response.ok) throw new Error(payloadMessage(payload, "暂时无法恢复后台回答")); const status = parseConsultationStatus(payload); if (status.status !== "reserved") throw new Error("后台回答状态无效"); diff --git a/frontend/src/lib/report-chart-export.tsx b/frontend/src/lib/report-chart-export.tsx index a3895950..3fb71c60 100644 --- a/frontend/src/lib/report-chart-export.tsx +++ b/frontend/src/lib/report-chart-export.tsx @@ -34,8 +34,7 @@ export function reportChartExportSvg(chart: NorthIndianChartModel, ariaLabel: st const rendered = serializeSvgNode(VedicChartSvgDrawing({ chart, ariaLabel, chartId: "export-chart" })) .replace(/[\s\S]*?<\/defs>/g, "") .replace(/ clip-path="[^"]*"/g, "") - .replace(/ { if (classes.includes("personal-report-chart-cell")) return 'fill="none" stroke="currentColor" stroke-width="1.25"'; if (classes.includes("personal-report-chart-retrograde")) return 'stroke="currentColor" stroke-width="1.9"'; diff --git a/frontend/tests/chart-page-view.test.tsx b/frontend/tests/chart-page-view.test.tsx index 57f2cfd3..d98e8b29 100644 --- a/frontend/tests/chart-page-view.test.tsx +++ b/frontend/tests/chart-page-view.test.tsx @@ -330,6 +330,15 @@ test("chart skeleton has one scoped breath rule and reduced motion is static", ( assert.match(globalStyles, /@media \(prefers-reduced-motion: reduce\)\s*\{\s*\.chart-page-skeleton \{ animation: none; opacity: 0\.5; \}/); }); +test("chart svgs omit the invalid height attribute and western height stays in css", () => { + const westernSvg = readFileSync(new URL("../src/components/chart-page/western-wheel-svg.tsx", import.meta.url), "utf8"); + assert.doesNotMatch(vedicSvg, /height="auto"/); + assert.doesNotMatch(westernSvg, /height="auto"/); + assert.match(westernSvg, /width="100%"/); + assert.match(vedicSvg, /width="100%"/); + assert.match(globalStyles, /\.chart-page-western-svg \{[^}]*height: auto/); +}); + test("the (app) layout mounts one sidebar for home and the four secondary routes", () => { // 原值:`(secondary)/layout.tsx` 挂只读侧栏,`useSidebarData` 另拉一份列表 // 新值:`(app)/layout.tsx` 一份 SessionListProvider + SidebarProvider,首页也在里面 diff --git a/frontend/tests/consultation-stream-recovery.test.ts b/frontend/tests/consultation-stream-recovery.test.ts index 83920117..b5ce10c6 100644 --- a/frontend/tests/consultation-stream-recovery.test.ts +++ b/frontend/tests/consultation-stream-recovery.test.ts @@ -152,3 +152,26 @@ test("status expires stale reservations after fifteen minutes but leaves fresh r assert.match(leaseBlock, /\.eq\("session_id", statusData\.session_id\)[\s\S]*\.eq\("request_id", statusData\.request_id\)/); assert.doesNotMatch(statusRoute.slice(0, statusRoute.indexOf('if (statusData.status === "reserved"')), /cancel_consultation_credit/); }); + +test("an empty active consultation lookup is 204 and a missing bound request stays 404", () => { + const activeEmpty = statusRoute.slice( + statusRoute.lastIndexOf("if (activeLookup)"), + statusRoute.indexOf("咨询请求不存在"), + ); + assert.match(activeEmpty, /!data \|\| typeof data\.session_id !== "string"/); + assert.match(activeEmpty, /new NextResponse\(null, \{ status: 204 \}\)/); + assert.doesNotMatch(activeEmpty, /咨询请求不存在|status: 404/); + assert.match(statusRoute, /return NextResponse\.json\(\{ error: "咨询请求不存在" \}, \{ status: 404 \}\)/); + const cloud = read("src/lib/home-cloud-sync.ts"); + const activeFn = cloud.slice( + cloud.indexOf("export async function fetchActiveConsultationStatus"), + cloud.indexOf("export async function patchSessionModel"), + ); + const boundFn = cloud.slice( + cloud.indexOf("export async function fetchConsultationStatus"), + cloud.indexOf("export async function fetchActiveConsultationStatus"), + ); + const statusGuard = activeFn.indexOf("if (response.status === 204 || response.status === 404) return null;"); + assert.ok(statusGuard >= 0 && statusGuard < activeFn.indexOf("response.json()")); + assert.doesNotMatch(boundFn, /status === 204/); +});