# PROGRESS · 校正会话已结算行记忆化(2026-09-15)
工作树:`.worktrees/rectification-settled-render-split-20260915`
分支:`codex/rectification-settled-render-split-20260915`
开工基线:`origin/staging` @ `a8d29d1b`(任务书写的 `6b3248bf` 已被后续合入;以当前 origin/staging 为准)
开工后远端又快进 1 个纯文档提交 `11893c7f`(普通聊天审计三单),本分支 rebase 到其上。
**BUG 编号:** 本树最大号仍是 **BUG-720**。721–724 / 726 未进本树,**BUG-725** 无冲突,未顺延。
未改 `docs/tasks/README.md` 状态列。未 `git push origin HEAD:staging`。未碰 `page.tsx`、`route.ts`、Python 引擎、React Compiler、`React.lazy`。未改 DESIGN.md(无可见样式变化)。未新写第二套输入框 / 滚动跟随 / 加载动画。
## 改前红 / 改后绿
### 5.4 结算态 Markdown 解析次数
同一段文本连续 `renderToString` 两次,可计数桩 renderer:
```
# 改前(StableMarkdownPrefix 已 export,尚无内容缓存)
not ok 5 - settled markdown parses the same text only once across consecutive renders
Expected values to be strictly equal:
2 !== 1
```
```
# 改后(rememberedProse + 结算口语/报告走 StableMarkdownPrefix)
ok 5 - settled markdown parses the same text only once across consecutive renders
# tests 5
# pass 5
# fail 0
```
### 5.5 按帧渲染计数
```
# 改前(行组件文件尚不存在)
Error [ERR_MODULE_NOT_FOUND]: Cannot find module
'.../frontend/src/components/rectification-message-entry.tsx'
not ok 1 - tests\\rectification-settled-render-split.test.ts
```
```
# 改后
ok 1 - the chat container no longer derives per-row timeline, varga, or choice cards
ok 2 - the extracted row is memoised and takes no function props except actionsRef
ok 3 - the split architecture renders settled rectification rows once while streaming tokens
# tests 3
# pass 3
# fail 0
```
手驱动 4 条已结算 + 5 个流式帧:拆分路径 `settledRowRenders = 4`、`streamingRowRenders ≤ 5`;未拆分对照 `unsplitListRenders = 5`、`settledRowRenders = 25`。拆分计数严格小于对照。
## 做了什么
| 任务 | 状态 | 落点 |
| --- | --- | --- |
| 5.4 结算态 Markdown 记忆化 | 完成 | `chat-message-content.tsx`:`StableMarkdownPrefix` 加按文本解析缓存;结算口语层与报告层都走它 |
| 5.1 抽出 `RectificationMessageEntry` + `memo` | 完成 | 新文件 `rectification-message-entry.tsx`;时间轴 / varga / choiceCard / afterAnswer 全部在组件内 |
| 5.2 `actionsRef` | 完成 | 容器 `useEffect` 每帧写 `.current`(与咨询面 `page.tsx` 相同,避免渲染期写 ref 撞 `react-hooks/refs`) |
| 5.3 `ChatMessageRow` `memo` | 完成,未砍 | 咨询面同样受益 |
| 5.5 渲染计数断言 | 完成,未砍 | `tests/rectification-settled-render-split.test.ts` |
| 5.6 BUG-725 | 完成,未砍 | 关联 BUG-473、BUG-249 |
交付卡 / 只读范围仍在容器 `messages.map` 里(标量 host 判定 + 既有 `candidateResult` state),不把每帧新建的 JSX 当行 props。
## 验收命令
| 命令 | 结果 |
| --- | --- |
| `./node_modules/.bin/tsc --noEmit` | 退出 0,无输出 |
| `npm run lint` | **0 error**,119 warning(既有,本单未新增、未顺手修) |
| `npx tsx --test tests/rectification-*.test.ts tests/home-streaming-render-split.test.ts tests/chat-markdown-split.test.ts tests/stream-frame-buffer.test.ts` 及本单改过的合同文件 | 定向套件 fail=0 |
| `npm test` | **3108** 条(含本单新增 4 条:结算 Markdown 1 + 行拆分 3)。pass 3021 / fail 73 / skipped 14。失败清单见下,均为环境缺口,不是本单回归 |
| `npm run build` | 默认 Turbopack 因 worktree `node_modules` junction 指向主仓而 panic(`points out of the filesystem root`) |
| `next build --webpack` | Compile + TypeScript 通过(3.7 min + 46 s)。Collecting page data 在 `/api/birth-time-guide` 因 Windows `EPERM` 无法为 `SKILL.md` 建 runtime symlink 而失败。本机看不到 `○ /` 路由表。`page.tsx` / `layout.tsx` / `globals.css` 未改,首屏源码差为 0 |
## 全量失败清单(环境缺口,与本单无关)
Windows、无 Docker、无 Git Bash、skill runtime 不能建 symlink。典型几类:
- `database-*.test.ts` / 迁移 / RLS / Better Auth:无 Docker
- `skill-binding` / `rectification-v9-agent` / `rectification-v9-stream` / `rectification-exhaustion-exit` 等整文件:`SkillPackageRegistryError` `EPERM: symlink`
- `*.sh` / staging sync / compose env / is-docs-only-range:Windows 无 POSIX shell
- `every color token used as a Tailwind utility is exposed through @theme`:既有 warning 级合同,本单未改 CSS
本单引起、后来已修的合同红(改 token 路径,三栏见各测试注释):`chat-answer-detail`、`chat-bundle-splitting-contract`、`rectification-agentic-entry`、`rectification-spoken-collect`、`rectification-surface-contract`、`chat-composer-queue`、`rectification-adopt-flow`、`rectification-delivery-ui-simplify`、`rectification-targeted-card-live`、`rectification-timeline-adapter`、`chat-stream-layout`、`agent-voice-copy-contract`、`rectification-question-in-message`、`rectification-varga-style-copy`、`rectification-walkthrough-polish`。修完后这些在定向重跑里全绿。
## 既有断言三栏(摘要)
| 文件 | 原值 | 新值 | 原因 |
| --- | --- | --- | --- |
| `chat-markdown-split.test.ts` | `: renderProse(spoken, renderMarkdown)` | `: ` | BUG-725 结算口语层记忆化 |
| `chat-bundle-splitting-contract.test.ts` | `renderProse(spoken/report)` 计数 2 | `StableMarkdownPrefix` 结算两层计数仍为 2;尾块仍 `renderProse(split.tail)` | 同上,计数不放宽 |
| `chat-answer-detail.test.ts` | `{renderProse(report, renderMarkdown)}` | `` | 报告层同样记忆化 |
| 若干校正源码合同 | token 在 `rectification-agentic-chat.tsx` 的 `messages.map` | 同一 token 在 `rectification-message-entry.tsx` | 5.1 抽出行组件 |
## 真人验收
`docs/testing/rectification-settled-render-split-20260915.md`。本环境无登录态、无 Chrome,长会话是否还卡**未测**,不要写成通过。
## 明确未做
- 把流式文本从 `messages` 搬进独立 state(任务书 §3.4 否决)
- React Compiler
- 首屏包体积(584 KB gzip 是全站壳,另议;本单未改首屏源)
- 校正面任何交互缺陷