Files
Jyotisha/docs/tasks/PROGRESS-cend-rectification-20260916.md
T
Jesse_ChenandClaude Opus 5 ca0204984b
Independent Staging Quality Gate / validate (push) Canceled after 3m14s
Independent Staging Quality Gate / publish (push) Canceled after 0s
feat(rectification): 区间常驻条补代表分钟与已答题数,盘面五段折叠收成两段
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
2026-09-16 06:44:49 +00:00

17 KiB
Raw Blame History

PROGRESS · R7 生时校正(2026-09-16

任务书:docs/tasks/TASK-cend-surfaces-claude-alignment-20260916.mdR7T7.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. 开工基线数字

50ce02c8npm 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 Braw 251,634 B

2. 逐条验收对照

T7.1 可信区间提成常驻条

先说一个与任务书事故实证不一致的发现,它改变了本轮的做法。

任务书 E9 写「区间只以 .rectification-board__clock 的形式出现在右侧盘面板的标题行」。实际读代码:rectification-agentic-chat.tsx:1645 起,对话区顶部已经有一条常驻条 RectificationTimelineBUG-602/603 那一轮做的),它是 .rectification-workspace__chat 的第一个 grid 行,已经在显示当前区间与宽度view.rangeLabel / view.widthLabel)。窄屏 .is-compact.rectification-workspace__chatgrid-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.tsxworkingTime: 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 behaviouris-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 31diff 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.tsprogressClause(),由 answer-choice.ts:1885/2060agent-run.ts:1189openingRangeFromDossier(dossier) 调用——但它只作为旁白正文落库,没有结构化出口。客户端要拿到只能解析消息文本。
  2. 前端自己减会算错,不只是违规。 开场窗口在客户端确实有(case.candidate_rangesearchWindow),但它是当前搜索窗口,会放宽(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 返回 -1slice(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-timelinedisplay: 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/healthdeployment.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 分。同一时刻 R5cend-chart-eph)和 R6cend-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% 内