feat(rectification): 区间常驻条补代表分钟与已答题数,盘面五段折叠收成两段
Independent Staging Quality Gate / validate (push) Canceled after 3m14s
Independent Staging Quality Gate / publish (push) Canceled after 0s

R7 / 决策 D10。任务书 docs/tasks/TASK-cend-surfaces-claude-alignment-20260916.md。

T7.1 常驻条。任务书 E9 说区间只在盘面标题行,实际读代码:对话区顶部
已有常驻条 RectificationTimeline 且已显示区间与宽度,窄屏 .is-compact 下
也可见。真正只存在于 .rectification-board__clock 的是代表分钟——窄屏盘面
是默认关闭的 overlay,代表分钟就此看不见。所以没有另造第二条(会撞固定条
高这条正确性约束),而是在既有条上补齐缺的两项:

  05:07–05:09 · 3 分钟 · 代表分钟 05:08 · 已答 6 题

代表分钟与盘面标题行是同一次 workingRectificationTime(result) 调用,一个
值两处用,不是两条推导路径。已答题数逐字读服务端的
inference_state.answered_probe_count,投影没给就整项不显示,不补「已答 0 题」。
条上不写确认,边界句仍只在交付/采用旁白里出现一次。读数只增宽不增高:条高
在滚动容器外,变高会让贴底读者的内容无声滑出视野。

收窄进度未做,触发让步顺序第 5 条。没有这个字段(服务端只把这句话拼进旁白
正文),且前端自己减会算错——candidate_range 是当前窗口且会放宽(BUG-572),
拿它当「最初」相减会把一次放宽报成一次收窄。缺口与解除条件写进 BLOCKED.md。

T7.2 盘面折叠。实际有 8 个 details(任务书记的五处漏了交界节奏、错标了两块),
按内容归成「候选分钟」(换升时刻 + 次级候选)与「参数与口径」(宫位表、确认门、
本轮技法、两段本命上升、经历与大运对照含双轨一致性、交界节奏)。每块保留原有
渲染条件,engineMeaningToDisplayCopy() 仍是 5 处,逐分钟行的 is-changed /
当前时间 一字未改。共享组件 technique-audit-disclosure.tsx 未动。

顺带修掉一个静默失效的断言:rectification-agentic-entry 用
board.slice(indexOf(A), indexOf(B)),B 改名后 indexOf 返回 -1,slice(i, -1)
会悄悄退化成几乎整份文件,底下四条 doesNotMatch 照样绿。已换成带越界断言的
sliceBetween()。两处改动的断言都在测试里写了原值/新值/原因。

tsc 0 错;lint 0 error / 118 warning(同基线);npm test 3350 → 3360,失败
仍是 31 条无 Docker 用例且与基线逐条一致;next build exit 0,/ 仍 ○ Static;
产物 CSS gzip 39,017 → 38,973 B(−0.11%)。

窄屏 .is-compact 下常驻条的实际可见性无 Chrome 无法验证,已按任务书写成环境
缺口,未标通过;CSS 上已确认无任何规则在该状态下隐藏该条,并写成测试断言。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P
This commit is contained in:
Jesse_Chen
2026-09-16 06:44:49 +00:00
co-authored by Claude Opus 5
parent a1a78c7ebc
commit ca0204984b
13 changed files with 751 additions and 72 deletions
@@ -0,0 +1,187 @@
# PROGRESS · R7 生时校正(2026-09-16
任务书:`docs/tasks/TASK-cend-surfaces-claude-alignment-20260916.md`**R7**T7.1 + T7.2,决策 D10)。
- 基线:`origin/staging` = `50ce02c8`
- 分支:`codex/cend-rectification-20260916`
- worktree`/workspace/Jyotisha/.claude/worktrees/agent-a8ca21a2f5ced3002`(本会话被隔离在这个 agent worktree,不是任务书里的 `.worktrees/cend-rectification-20260916`
- BUG 编号:**未占用**。本轮是产品改造,未发现需要建档的产品缺陷,`docs/BUG_HISTORY.md` 当前最大号仍是 **BUG-738**(已核对)。
---
## 1. 开工基线数字
`50ce02c8``npm ci` 后实测:
| 项 | 基线 |
| --- | --- |
| `tsc --noEmit` | 0 错 |
| `npm run lint` | **0 error / 118 warning** |
| `npm test` | `# tests 3350 / # pass 3304 / # fail 31` |
| 失败清单 | 31 条,全部是无 Docker 的数据库 / 部署 / 迁移类;存档在 `base.txt` |
| `next build` | exit 0`/` = `○ Static` |
| 产物 CSS gzip | `.next/static/chunks/*.css` 合计 **39,017 B**raw 251,634 B |
---
## 2. 逐条验收对照
### T7.1 可信区间提成常驻条
**先说一个与任务书事故实证不一致的发现,它改变了本轮的做法。**
任务书 E9 写「区间只以 `.rectification-board__clock` 的形式出现在右侧盘面板的标题行」。实际读代码:`rectification-agentic-chat.tsx:1645` 起,对话区顶部**已经有一条常驻条** `RectificationTimeline`BUG-602/603 那一轮做的),它是 `.rectification-workspace__chat` 的第一个 grid 行,已经在显示**当前区间与宽度**(`view.rangeLabel` / `view.widthLabel`)。窄屏 `.is-compact``.rectification-workspace__chat``grid-area: 1 / 1` 且三行俱在,所以这条**本来就可见**。
真正只存在于 `.rectification-board__clock` 的是**代表分钟**——窄屏下盘面变成默认关闭的 overlay,代表分钟就此看不见。这才是 E9 描述的那个死角。
因此本轮没有另造第二条常驻条(那会撞上固定条高这条正确性约束,也等于手写第二套),而是**在既有常驻条上补齐缺的两项**。
| 验收标准 | 结论 | 证据 |
| --- | --- | --- |
| 窄屏 `.is-compact` 下常驻条仍然可见 | **CSS 上确认,真机未验(环境缺口)** | 见 §5 |
| 边界语义不弱化 | 通过 | 条上只写「代表分钟 HH:MM」/「已采用 HH:MM」,不写确认;`REPRESENTATIVE_MINUTE_DISCLAIMER` 与交付/采用旁白一行未改。测试 `an adopted minute is labelled adopted...` 断言组件与 scale 模块都不出现「已确认 / 确认的唯一」 |
| 区间数字与 `.rectification-board__clock` 同源 | 通过 | 同一次调用:盘面 `rectification-board.tsx:59` `workingRectificationTime(result)`;条由 `rectification-agentic-chat.tsx``workingTime: workingRectificationTime(candidateResult)`。不是两条推导路径,是一个值两处用。测试第 1 条锁死这三行源码 |
条上最终四项,一行排完:
```
05:0705:09 · 3 分钟 · 代表分钟 05:08 · 已答 6 题
```
每项的服务端来源:
| 读数 | 来源 | 是否前端口算 |
| --- | --- | --- |
| 当前区间 | `credible_range`(服务端投影) | 否 |
| 宽度 | 该区间自身的闭区间宽度,`timelineDurationLabel()`,既有函数未改 | 否(任务书让步顺序第 5 条明确允许「只显示当前区间与宽度」) |
| 代表分钟 | `workingRectificationTime(result)` = `selectedTime ?? representativeTime ?? houseTable.time`,与盘面同源 | 否 |
| 已答题数 | `decisionReceipt.inference_state.answered_probe_count`,**服务端自己的计数**,逐字读出 | 否 |
| 收窄进度 | — | **未做,触发让步顺序第 5 条,见 §3** |
`answered_probe_count` 此前已经在线上(`compactInferenceProjection()` 写进 `inference_state`,随 receipt 下发),只是客户端没解析。本轮加 `parseAnsweredProbeCount()` 读出来,投影没给就整项不显示,不补 `已答 0 题`
### T7.2 盘面板折叠块收敛
任务书说五块(`:117/:132/:160/:182/:198`)。实际数下来盘面里有 **8 个** `<details className="technique-audit">`:本命宫位表、唯一分钟确认门、本轮技法、换升时刻(内嵌显示层)、两段本命上升、经历与大运对照、交界节奏。任务书把 `:117` 记成「盘面技法审计」、`:182` 记成「口径披露」,并漏了 `:212` 的交界节奏。按实际内容归的类:
| 新段 | 装进去的原块 |
| --- | --- |
| **候选分钟**`aria-label="候选分钟"`) | 换升时刻(改为段内组标题 + 原列表)、次级候选(原「显示层」子折叠) |
| **参数与口径**`aria-label="参数与口径"`) | 本命宫位表、唯一分钟确认门、本轮技法、两段本命上升、经历与大运对照(含双轨一致性那句)、交界节奏 |
| 验收标准 | 结论 | 证据 |
| --- | --- | --- |
| 五块内容一条不丢,只换归属 | 通过 | 测试 `the board folds to two sections and loses none of the five blocks` 在两段的源码切片里逐块断言 |
| `engineMeaningToDisplayCopy()` 调用点全部保留 | 通过 | 改前 5 处,改后 5 处,测试里 `assert.equal(..., 5)` 锁死 |
| 候选分钟列表 `is-changed` / 「当前时间」行为不变 | 通过 | 逐分钟行的 JSX 原样搬运未改;测试 `regrouping changed no minute-row behaviour``is-changed` / `is-working` / 「当前时间」/「已更新」四处 |
| 键盘可达性不弱于改前 | 通过(静态) | 每块仍是真 `<summary>`,无 `role="button"`、无 `onClick` 开合;测试断言 `<summary>` 数 ≥ 6 且无 `open`。嵌套多了一层 → 到最深一块要多按一次 Enter,这是收敛的固有代价,不是可达性缺失 |
**渲染条件逐块保持原样。** 确认门与本轮技法原本写在 `{table && ...}` 里面,现在仍跟着 `table` 走——没有星盘时它们本来就不出现,改后也一样。不是「一条不丢」的反面,是不多出一条。
### 硬约束核对
| # | 约束 | 结论 |
| --- | --- | --- |
| 1 | 数字只来自服务端投影 | 通过,四项来源见上表;收窄进度按让步顺序不做 |
| 2 | 边界语义不弱化、候选不得写成 confirmed | 通过 |
| 3 | 常驻条区间与盘面 clock 同源 | 通过 |
| 4 | 五块一条不丢、`engineMeaningToDisplayCopy()` 全留、`is-changed`/「当前时间」不变 | 通过 |
| 5 | 窄屏 `.is-compact` 下常驻条可见 | CSS 确认,真机是缺口(§5) |
| 6 | 只动 `rectification-*``globals.css``.rectification-*` / `.technique-audit` 区段 | 通过,见 §6 |
| 7 | 复用 `ChatComposer` / `useConversationScrollAnchor` | 通过,两者一行未改;未新增输入框与滚动跟随 |
| 8 | `tsc` 0 错、`lint` 0 error、warning ≤ 118 | 通过:0 错 / **0 error / 118 warning**(与基线同数) |
| 9 | `npm test` 失败清单与基线逐条一致 | 通过:`# tests 3360 / # pass 3314 / # fail 31``diff base.txt after.txt` **无差异** |
| 10 | 改断言写「原值/新值/原因」 | 通过,见 §4 |
| 11 | `next build``/``○ Static` | 通过,exit 0`┌ ○ /` |
| 12 | 揭幕后无 spinner / 骨架 /「正在加载」 | 通过,未改 `data-state="pending"` 等高空骨架;新增元素都是静态 `<span>` |
| 13 | 同提交更新 `DESIGN.md`、对照 `VOICE.md` | 通过,§10 与 §12 重写;文案对照见下 |
**首屏体积(红线:`/` gzip ±2%)。** Next 16.3.1 的构建输出没有 Size / First Load 两列,沿用 BUG-601 那轮的口径直接量产物:`.next/static/chunks/*.css` 合计 gzip **39,017 → 38,973 B(−44 B,−0.11%**,在 ±2% 内。raw 从 251,634 涨到 252,540 B+906 B),gzip 反而略降属正常。新增 JS(`rectification-timeline-scale.ts` 的两个 label 函数、`parseAnsweredProbeCount`)全部落在校正面的 `dynamic()` 分包里,不进 `/` 首屏。
**VOICE.md 对照。** 「代表分钟 05:08」「已采用 05:08」「已答 6 题」三句都是短事实,无公文腔,不含被禁的「概率 / 置信度 / 确定 / 相对支持度」,也不碰 DESIGN.md 里已明令砍掉的「已对照 N 件经历」——那是经历吻合数,归交付卡;条上的「已答 N 题」是问答题数,两个量不同,已在 DESIGN.md 表里写明区别。
---
## 3. 让步顺序:触发了第 5 条
**让步顺序第 5 条「T7.1 的收窄进度:若服务端投影里没有现成的『已从 N 分钟收到 M 分钟』字段 —— 只显示当前区间与宽度,不要前端口算进度」——已触发。**
依据,两条,第二条比第一条更要紧:
1. **没有这个字段。** 服务端确实能拼出这句话——`user-copy.ts``progressClause()`,由 `answer-choice.ts:1885/2060``agent-run.ts:1189``openingRangeFromDossier(dossier)` 调用——但它只作为**旁白正文**落库,没有结构化出口。客户端要拿到只能解析消息文本。
2. **前端自己减会算错,不只是违规。** 开场窗口在客户端确实有(`case.candidate_range``searchWindow`),但它是**当前**搜索窗口,会放宽(BUG-572:±15 → ±30 → ±60 → ±120)。拿它当「最初」去减,一次放宽会被报成一次收窄。这条写进了 `rectification-timeline-scale.ts` 的模块注释,免得下一个人「顺手补上」。
缺口已写进 `BLOCKED.md`(顶部新条目),含解除条件:投影里加一个记**开场**窗口、放宽时不跟着变的宽度/进度字段。`rectification-timeline-scale.ts` 已留好接入位置。**收窄进度这句话服务端照旧写进旁白,用户仍看得到,只是不在常驻条上——不是回归。**
让步顺序第 6 条(T7.2 退到三段)**未触发**,收敛成两段。
---
## 4. 被改断言:原值 / 新值 / 原因
两处,都在测试文件里就地写了三行注释。
| 文件 | 原值 | 新值 | 原因 |
| --- | --- | --- | --- |
| `frontend/tests/rectification-board-model.test.ts`"board shows a north-indian chart and folds 换升时刻 by default" | `assert.match(html, /换升时刻 · 2 个/)` | `assert.match(html, /候选分钟/)` + `/2 个时刻/` + `/换升时刻/`,另加 `/参数与口径/` | D10 五段收敛成两段,「换升时刻」从 summary 降为段内组标题,计数移到「候选分钟」的 summary。计数仍是 `grouped.length`,仍默认折叠。**没有弱化**:原来断言 1 个字符串,现在断言 4 个 |
| `frontend/tests/rectification-agentic-entry.test.ts`"the natal house table is a live board beside the chat, not a message" | `snapshot` 切到 `"{scoringMinutes.length > 0 \|\| displayMinutes.length > 0"`,并断言 `<RectificationHouseTableView` 在 snapshot 里 | `snapshot` 切到 `"{hasMinuteDetail && ("`;宫位表改在 `parameterSection` 切片里断言;新增 `assert.match(snapshot, /<VedicChartSvg/)``assert.doesNotMatch(snapshot, /<RectificationHouseTableView/)` | 本命宫位表随 D10 从 `.rectification-snapshot` 移进「参数与口径」,旧终点标记已不存在。测试原意(宫位表在盘面、不在消息流)原样保留 |
**第二条附带修掉一个静默失效。** 旧写法是 `board.slice(indexOf(A), indexOf(B))`:标记 B 改名后 `indexOf` 返回 `-1``slice(i, -1)` 会**静默**退化成「从 A 到文件倒数第二个字符」,底下四条 `doesNotMatch` 就变成在整份文件上断言,测试照样绿。实测确认过:只改组件不改测试时,这条测试**假通过**。已改成带 `assert.ok(start >= 0)` / `assert.ok(end > start)``sliceBetween()` 辅助函数,标记改名会直接报错并打印找不到的那个标记。
新增测试文件 `frontend/tests/rectification-range-bar-20260916.test.ts`,10 项:同源锁、采用标签与边界、无值不猜、计数来自投影、**无收窄进度口算**、一行四读数与固定条高、窄屏不隐藏、两段收敛不丢块、分钟行行为不变、折叠默认收起且可达。测试总数 3350 → 3360,未降。
---
## 5. 环境缺口(不得写成通过)
1. **「窄屏 `.is-compact` 下常驻条的实际可见性」——无 Chrome、无登录态,真机未验。** 这是本轮的核心收益,按任务书要求写成缺口。
**CSS 上确认到的事实(不等于真机通过):** `.rectification-workspace.is-compact``.rectification-workspace__chat``.rectification-board-overlay` 都放进 `grid-area: 1 / 1` 叠起来;`__chat` 在窄屏保留三行栅格,第一行就是常驻条;overlay 只在 `boardOpen` 时渲染,默认关闭。全表扫过一遍,**没有任何规则**在 `.is-compact` 下对 `.rectification-timeline``display: none` / `visibility: hidden` / `height: 0`;这条写成了测试断言(遍历所有 `.is-compact ... .rectification-timeline` 规则块)。
**真机仍需确认的:** 四项读数在 375px 实机字体下是否真的一行放得下(算下来约 269px,容器约 343px,留有余量,但中文字宽随字体变);被裁时是不是先裁「已答 N 题」;以及贴底跟随在条变宽后仍然正常(条高未变,理论上不受影响)。
2. **盘面两段折叠的键盘走查未做。** 静态上每块仍是 `<summary>`、默认收起、无假按钮;「Tab / Enter 依次展开两段并进到最深一块」需要真人按一遍。
3. **无 Docker。** `npm run test:db` 未跑。本轮未动表、未动迁移。
4. **本轮未推送**(按任务要求)。staging 部署与 `/api/health``deployment.gitCommit` 核对不在本轮。
前两条建议并入 `docs/testing/` 的校正走查清单;本轮未新建清单文件,避免与 R4–R8 其它轮次的清单重复。
---
## 6. 改动范围
只碰了这些,没有越界到 R5/R6/R8 的地盘(`.chart-page-*` / `.ephemeris-*` / `.report-center-*` / `.personal-report-*` / `.settings-*` / `.membership-*` 一个字符未动):
| 文件 | 改了什么 |
| --- | --- |
| `frontend/src/components/rectification-timeline.tsx` | 读数行加代表分钟与已答题数两个 `<span>`;轴的 `aria-label` 跟着补齐 |
| `frontend/src/lib/rectification-timeline-scale.ts` | 视图加 `workingLabel` / `answeredLabel`;新增 `workingMinuteLabel()` / `answeredProbeLabel()`;模块注释写明为何没有收窄进度 |
| `frontend/src/lib/rectification-candidate-result.ts` | 加 `answeredProbeCount` 字段与 `parseAnsweredProbeCount()` |
| `frontend/src/components/rectification-agentic-chat.tsx` | 给 `buildRectificationTimeline()` 多传三个参数;import `workingRectificationTime` |
| `frontend/src/components/rectification-board.tsx` | 五段折叠收敛成两段;加 `hasMinuteDetail` / `hasParameterDetail` |
| `frontend/src/app/globals.css` | `.rectification-timeline__readout` 改 nowrap + 新增 `__working` / `__answered``.rectification-board__group`;盘面嵌套 `.technique-audit` 的间距;`.is-compact` 规则加注释 |
| `frontend/DESIGN.md` | §10 重写(四项读数、只增宽不增高、代表分钟上条的理由与同源要求、收窄进度为何不在条上、砍掉清单加两行);§12 重写(两段折叠表) |
| `BLOCKED.md` | 顶部新增收窄进度字段缺口 |
| 三个测试文件 | 见 §4 |
`technique-audit-disclosure.tsx` **未改**——它是普通聊天也在用的共享组件,两个 `<details>` 原样嵌进「参数与口径」里。
---
## 7. 一件必须交代的事故:共享 stash 栈
量首屏 gzip 需要基线构建,本会话用了 `git stash push -u` → 构建基线 → `git stash pop`。**这是错的**:git 的 stash 栈是整个仓库共用的,不按 worktree 分。同一时刻 R5`cend-chart-eph`)和 R6`cend-report`)两个会话也在 stash。结果是我的 `pop` 弹出的不是我自己的 stash,而是 R5 的 chart-page / ephemeris 改动(9 个文件),落进了我的 worktree。
处理:先把 R5 的改动原样备份成 patch,再 `git stash push -u -m "RECOVERED cend-chart-eph work..."` 整份放回栈上,一个字符没丢。查 `git log -g refs/stash` 时发现 R6 会话也对我的 stash 做了同样的事(`RECOVERED cend-r7-wip ...`),互相救了回来。我自己的改动最后用 `git diff <stash-sha>` 生成 patch、`patch -p1` 打回,未跟踪的新测试文件用 `git show <untracked-parent>:<path>` 取回,11 个文件与 stash 的 diffstat 逐行一致,随后立刻提交,不再留在栈上。
**给后面轮次的教训:多 worktree 并行时不要用 `git stash`。** 要基线构建就另开一个 worktree 或临时 commit,别碰共享栈。
---
## 8. 最终数字
| 项 | 基线 | 本轮 | 结论 |
| --- | --- | --- | --- |
| `tsc --noEmit` | 0 错 | 0 错 | 通过 |
| `npm run lint` | 0 error / 118 warning | 0 error / 118 warning | 通过 |
| `npm test` 总数 | 3350 | 3360+10 | 未降 |
| `npm test` 失败 | 31 | 31 | 清单逐条一致,`diff` 无差异 |
| `next build` | exit 0`/` `○ Static` | exit 0`/` `○ Static` | 通过 |
| 产物 CSS gzip | 39,017 B | 38,973 B | 0.11%,在 ±2% 内 |
+1 -1
View File
@@ -157,7 +157,7 @@
| `TASK-engine-runtime-hygiene-20260901.md` | `PROGRESS-engine-runtime-hygiene-20260901.md` | Python 引擎运行时治理 | 已验收 | `124d3990`(staging 实机三条待部署窗口) |
| `TASK-billing-pricing-20260830.md` | `PROGRESS-billing-pricing-20260830.md` | 计费闭环与功能级定价 | 已合入 | `aaaa5fb7``a499c344` |
| `TASK-cend-ui-claude-alignment-20260916.md` | `PROGRESS-cend-ui-r1/r2/r3-20260916.md` | **C 端界面向 claude.ai 产品界面对齐(三轮串行 R1→R2→R3,都动 `globals.css`,不得并行)**:根因是 `frontend/CLAUDE_DESIGN.md` 扒的是 **claude.com 营销官网**,它自己在 Known Gaps 里写明 claude.ai 产品界面不在范围内,而 `DESIGN.md:3` 把它当成了产品界面的实现契约。**R1**:`--font-display` 里 Tiempos Headline / StyreneB **从未加载**(无 `@font-face`、`public/` 无字体、`layout.tsx` 只 vendor 了 Inter),中文标题全站落到 **宋体 / SimSun**,波及 20 处含助手回答的 h2/h3(BUG-737);亮色强调色 `#85432f` 与暗色 `#d78064` 不同源,产品拍板亮色换 **Claude coral `#cc785c`****易漏点**是 `globals.css:16` 的 `--color-ring` 硬编码在 `@theme inline` 里不跟随 `:root`,另有第四个 `:root` 亮色块(`:4358`)必须同步(BUG-738);`.composer-footer` 常驻 44px + 顶栏 68px + `--composer-reserve` 148px,每屏固定吃掉 216px,模型选择器移进输入框内部、删掉底栏、顶栏收到 46px 并删「分析对象」副标题。**R2**:空状态是营销落地页(hero 卡 + 两张 132px 入口大卡 + 3 列 156px 主题卡),输入框被压在 **800px 以上**内容之下,重排成「问候 + 居中输入框 + 两枚入口 pill + 一排 chip」。**R3**:侧栏两个 `<details>` 拍平成一条「最近」、星盘的两个入口(侧栏分组 + 账户菜单)收敛到一处、删掉逐条助手头像。**决策记录 D3 推翻 DESIGN.md「报告强调色与应用同源」一句**(报告刻意保留深棕)。原型图 https://claude.ai/code/artifact/da275da6-2954-4f50-99aa-32bb8694d38b(三套画面 + 明暗,页面标题就是建议字体栈的实际渲染)。环境缺口:无登录态无 Chrome,四项真机观感留 `docs/testing/`。BUG 段 737738 | 待领取 | — |
| `TASK-cend-surfaces-claude-alignment-20260916.md` | `PROGRESS-cend-shell-20260916.md`、`PROGRESS-cend-report-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=<uuid>`;**刻意不带**重命名/删除/收藏/归档——那套连着 `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、R6、R7、R8 已实现,R5 进行中** | 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 不许前端编 |
| `TASK-cend-surfaces-claude-alignment-20260916.md` | `PROGRESS-cend-shell-20260916.md`、`PROGRESS-cend-report-20260916.md`、`PROGRESS-cend-rectification-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=<uuid>`;**刻意不带**重命名/删除/收藏/归档——那套连着 `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、R6、R7、R8 已实现,R5 进行中** | 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` 假通过)|
### 待开的任务书(2026-09-03 审查决定)