8db71aaf81
- README.md is now the product/repo front door (architecture, repo map, local dev, test tiers, delivery flow, doc map). Engine positioning, VedAstro/Codex setup and the oracle/benchmark command reference move verbatim to docs/engine/README.md, docs/engine/vedastro-gateway.md and docs/benchmark/README.md. Capability badges realigned with the registry (91/78/8/0); tests/test_readme_badges.py was red on staging. - AGENTS.md: Part A (environment truth, delivery, worktrees, record placement, bug workflow, growth freeze, frontend red lines, privacy, pre-work check, test tiers) and Part B (reading-rigor constraints). GitHub issue-tracker/triage boilerplate removed: GitHub is a read-only mirror. All strings locked by tests/ are preserved. - CLAUDE.md added: roles, three working modes, task-brief sections, acceptance criteria, session discipline; imports AGENTS.md. - 50 tracked TASK-*/PROGRESS-* files and 3 never-committed briefs move to docs/tasks/ with an index; REPO_LAYOUT.md merged into README. Docs-only change (no gated path touched). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P
125 lines
21 KiB
Markdown
125 lines
21 KiB
Markdown
# PROGRESS · Agent 聊天流式体验与双会话面统一(2026-09-01)
|
||
|
||
工作树:`.worktrees/streaming-ux-20260901`
|
||
分支:`codex/streaming-ux-20260901`
|
||
基线:任务 0–2 时为 `origin/staging` @ `fbb80fa3`;2026-09-02 远端前进到 `058e5db9`(`bf6989ec` 第三批拆页、`124d3990` 引擎卫生、`058e5db9` Home lint 修复)后本分支 **rebase 到其上**,任务 3 在该基线上做。
|
||
|
||
**BUG 编号顺延**:staging 的 lint 修复占用了 BUG-470,本分支四条整体后移(470→471、471→472、472→473、473→474),`docs/BUG_HISTORY.md`、本文件、四个测试文件里的注释与四个 commit message 一并改(用 `git filter-branch --tree-filter/--msg-filter` 在原提交上逐个改,再 rebase,只在 `BUG_HISTORY.md` 冲突一次)。任务书 `TASK-chat-streaming-ux-20260901.md` 未动。
|
||
|
||
未改 `.gitea/workflows/**`。未合并 staging,未碰 main。未手写 `useCallback` / `useMemo`(`rectification-agentic-chat.tsx` 既有的 `useCallback` 原样保留)。未动服务端事件语义;校正 `thinking.delta` 在 `stream-mapping.ts` 公开边界的丢弃原样保留。`page.tsx` 一行未动。
|
||
|
||
| 任务 | 状态 | commit | BUG |
|
||
| --- | --- | --- | --- |
|
||
| 0 · 流式按帧合并 + 匀速吐字 + Markdown 前缀记忆 | 完成 | `dd4e3b37` | BUG-473 |
|
||
| 1 · 结算不闪、时间线开合过渡、流式可折叠、queued 行 | 完成 | `69d19e0f` | BUG-474、BUG-475 |
|
||
| 2 · 校正接入同一套时间线、删三条死路径、移除 thinking-orbs | 完成 | `cc17d971` | BUG-476 |
|
||
| 3 · 校正复用 ChatComposer / 统一滚动跟随 / JumpToLatest / 900px | 完成(第三批合入后解除阻塞) | `b72c1efc` | BUG-477、BUG-478 |
|
||
| 4 · DESIGN.md | 与 0/1/2 对应的条目随各任务改;与 3 对应的四条随任务 3 同 commit | — | — |
|
||
|
||
**第二次 rebase 与编号再顺延(2026-09-02)**:远端又合入 `1a3e14e7`(问题题干显示/占用后解锁采用),占了 BUG-471/472。本分支再次用 `filter-branch` 在原提交上改号后 rebase 到 `1a3e14e7`:471→473、472→474、473→475、474→476、475→477、476→478,`BUG_HISTORY`、本文件、测试注释、commit message 一并改。冲突两处:`docs/BUG_HISTORY.md`(取 staging 全文 + 我方条目续在后面);`rectification-agentic-chat.tsx`(两边都保留——staging 的 `collectSpokenPromptId` 与题干 `<p>` 原样,staging 加在 `<Textarea>` 上的 `aria-describedby` 改为经 `ChatComposer` 新增的 `describedBy` prop 传入,composer 把它与字数计数 id 合并成一个 `aria-describedby`)。staging 新测试 `rectification-spoken-collect` 锁 `aria-describedby={collectSpokenPrompt`(内联 Textarea),按红线 3 改锁 `describedBy={collectSpokenPrompt …}` + composer 的合并表达式,注释原值。`character-remaining-contract` 的 `aria-describedby={showRemaining ? remainingId : undefined}` 锁随之改为合并表达式。
|
||
|
||
## 任务 0 · 流式渲染
|
||
|
||
改动:`src/lib/stream-frame-buffer.ts`(新)、`src/lib/chat-markdown-split.ts`(新)、`src/components/chat-message-content.tsx`、`src/components/chat-message-row.tsx`(`streaming` prop)、`src/hooks/use-consultation-run.ts`、`src/components/rectification-agentic-chat.tsx`。
|
||
|
||
- 两个会话面的事件回调都不再直接 `setState`:事件写入累加器,`requestAnimationFrame` 每帧最多一次 flush;`run.completed` / `run.failed` / abort / 读流结束时 `settle()` 同步冲干净;`finally` 里 `dispose()`;校正 `attempt.reset` 走 `reset()`。
|
||
- 释放节奏:`max(2, ceil(积压 ÷ 12))` 字/帧,积压按「新文本到达时的未释放量」记,所以 3,000 字突发恰好 12 帧追平(测试锁死),稳态约落后网络 12 帧(≈200ms);页面隐藏走 250ms `setTimeout` 并一次放完。
|
||
- 重活只做一次:`parseAgentReply` / `applyThinkingSectionProgress` 只在 flush 里对已释放文本跑。
|
||
- Markdown:流式时在最后一个空行切「稳定前缀 / 尾块」,不切进代码围栏、同一列表的项之间、表格行之间;前缀交给 `memo` 的 `StableMarkdownPrefix`,只有尾块每帧 parse;切分对象是 `splitSpokenAnswerAndTechniqueAudit(text).spoken`,技法审计表留在审计折叠里。结算后仍整篇一次 parse,DOM 结构与流式时一致(react-markdown 不加包裹元素)。
|
||
- 没有另写"薄 hook":`use-consultation-run` 按前两轮拆页公证是"内部 0 个 React hook",缓冲器按每次 send 创建/销毁的工厂用法更贴合,两处调用都是这个形态。
|
||
|
||
渲染次数探针(`tests/home-streaming-render-split.test.ts` 新增用例,假 rAF 驱动):200 个单字 token、每帧到 4 个、共 50 帧 → **`streamingRowRenders = 50`**(改前的架构每 token 一次 = 200)。`tests/stream-frame-buffer.test.ts` 同一场景 flush 50 次,且每次释放 ≥2 字、结算后与网络全文一致。
|
||
|
||
Performance 面板录制(3k 字回答无 >50ms 长任务)**未做**:无登录态、无 Chrome,见 `BLOCKED.md`。
|
||
|
||
## 任务 1 · 结算与时间线
|
||
|
||
改动:`src/components/chat-transcript.tsx`(重写)、`src/lib/chat-message-view.ts`(`latestAssistantView`)、`src/lib/home-streaming-render-probe.ts`(`latestEntryMounts`)、`src/components/consultation-run-timeline.tsx`(重写)、`src/components/chat-message-row.tsx`(GSAP 0.16)、`src/app/globals.css`、`scripts/home-streaming-render-benchmark.mts`(改用 `LatestAssistantEntry`)。
|
||
|
||
- `ChatTranscript` = `SettledMessageList excludeLatestAssistant` + 一个 `LatestAssistantEntry key={renderKey}`。同一个 `MessageEntry` 渲染历史行、流式行、刚结算行,动作条与追问只在 settled 出现。`renderKey` 在流式(`message-N`,N=历史条数)与结算(末条下标 N)下相同,纯函数 `latestAssistantView` 有测试锁。
|
||
- `.message` 的 CSS `message-enter` 关键帧删除,只剩 GSAP 一份,时长 0.18 → 0.16(DESIGN.md §6 = 160ms)。
|
||
- 时间线:去掉 `key={live…}`;外层 `<section>` + `button[aria-expanded][aria-controls]`;`open = userOpen ?? live`;折叠是 `grid-template-rows 0fr↔1fr` 180ms,折叠后内容 `inert`;summary 文案换行 120ms 淡入;live 行与 summary 文案共用既有 `agent-activity-status__text` shimmer;`rows` 为空且 live 时渲染 `QUEUED_TIMELINE_ROW`「正在处理…」。reduced-motion 下三处过渡为 0(全局 `*` 规则之外再显式写了一次)。
|
||
- `latestEntryMounts` 探针只能在浏览器里手动读(无 DOM 测试库,`renderToString` 不跑 effect),见 `BLOCKED.md`。
|
||
|
||
## 任务 2 · 双会话面统一
|
||
|
||
改动:`src/lib/rectification-timeline-adapter.ts`(新)、`src/components/rectification-agentic-chat.tsx`、`src/components/agent-activity-status.tsx`(重写为兜底)、`src/components/chat-message-row.tsx`、`src/app/globals.css`、`package.json` / `package-lock.json`(`npm uninstall thinking-orbs`);删除 `consultation-thinking-report.tsx`、`thinking-step-tree.tsx`、`completed-activity-receipt.tsx`。
|
||
|
||
- 适配:tool trace → `calculate` 行(started=live / completed=done,标签沿用 `RECTIFICATION_TOOL_*_LABELS`);`failedTool` 的行标签加「未完成」且不挂 chips;回执 `methods` 经 `PUBLIC_RECTIFICATION_METHOD_LABELS` 去重 ≤8 作为最后一条完成 calculate 行的 `sources`;没有 live 行且 `activity.label` 以「正在」开头时补一条 live 行(`answer-composition` → `write`,其余 → `calculate`);think 类 trace 项映射为 think 行(现网不会出现,服务端丢弃)。
|
||
- 校正结算后与普通会话一样收成「已完成 N 步」一行,方法 chips 在行内展开;第二个回执折叠块、消息上方红字失败行、重新生成时的假「正在组织回答…」全部删除;失败走既有 `error` 通知;重新生成显示 queued 行。
|
||
- `chat-message-row.tsx` 只剩 timeline 一条主路径 + `AgentActivityStatus` 兜底(无 trace 分支、无 orb);live 标记全部 `InlineSpinner`;`.conversation.is-rectification .agent-thinking-step/.agent-thinking-marker` 14px 覆写删除。
|
||
- 校正的「已用时 N 秒」计时(`ActivityElapsed`)随 trace 面板一起退出——时间线本来就没有这个元素,两面一致优先。
|
||
|
||
## 任务 3 · 校正复用 ChatComposer、统一滚动跟随与「跳到最新」
|
||
|
||
开工时 `git log origin/staging -- frontend/src/app/page.tsx` 最新为 `551d6317`,第三批未合入,先按红线 7 登记 `BLOCKED.md`;远端合入 `bf6989ec`/`058e5db9` 后 rebase,阻塞解除,`BLOCKED.md` 该条改为已完成。
|
||
|
||
改动:`src/hooks/use-conversation-scroll-anchor.ts`、`src/components/jump-to-latest-button.tsx`(新)、`src/components/chat-composer.tsx`、`src/app/page.tsx`、`src/components/rectification-agentic-chat.tsx`、`src/app/globals.css`;删除 `src/lib/rectification-sticky-scroll.ts`。
|
||
|
||
- **ChatComposer**:新增可选 `value`(受控)与 `remainingId`。`const storeDraft = useComposerDraft(); const draft = value ?? storeDraft;`——store 订阅仍在组件内,主会话行为不变;校正面传 `value={draft}`,`grep composer-draft` 在校正文件里为 0(契约锁死)。校正面 `maxLength=500`(`RECTIFICATION_COMPOSER_MAX_LENGTH`,与主对话同上限),footer 放 `CharacterRemaining`(id 用 `useId`);三段 placeholder、Enter/Shift+Enter/`isComposing`、「停止回答」文案与 `stopRun` 原样。
|
||
- **滚动跟随只留一份**:`useConversationScrollAnchor` 增加 follow:`ResizeObserver` 观察滚动容器的子元素(`MutationObserver childList` 跟踪子元素增删并重新观察),anchored 时下一帧 `scrollTop = scrollHeight`;`resetKey` 变化时先落底一次;`anchored` 经 `anchoredRef` 在 effect 里同步(不在 render 写 ref,过 `react-hooks` 规则)。`page.tsx` 那个按 `activeStreamingText` 触发、结算时再补 smooth 的 effect 删除;hook 调用签名 `(conversation, !rectificationSurfaceOpen && !starterHomeVisible, activeSessionId)` 不变,starter home 不滚动的性质由 `active=false` 承担。校正面 `useConversationScrollAnchor(conversation, true, caseId)`,删除 `followTailRef` / `scrollFrameRef` / `updateFollowState` / `scrollToLatest` / `followLatestContent` / `showJumpToLatest` / `choiceCardsOpen` 与两个 `useLayoutEffect`;`rectification-sticky-scroll.ts` 删除。选择卡 360px 带宽的特例随之退出:卡片本来就在 transcript 里,看着它的人离底 ≤96px,本就 anchored。
|
||
- **JumpToLatestButton**:`.jump-to-latest`(absolute、`bottom: 100%`、`pointer-events: none`)+ `.jump-to-latest__button`(44px、`--shadow-elevated`、focus ring、hover 暖卡片色),两面共用,文案统一「跳到最新」带 `ArrowDown`;`page.tsx` 内联 Tailwind 版(`shadow-md`)与 `.rectification-jump-latest` 三条规则删除。
|
||
- **宽度**:`.rectification-workspace__chat .message-list { max-width: 720px }` 删除,两面同 `.message-list` 的 900px;`--rectification-jump-clearance` 底部留白保留。
|
||
- 手工项(两面各做一次「流式中上滚→停跟随→出现按钮→点回底并恢复跟随」)**未做**,无登录态无 Chrome;行为由 `chat-notice-and-scroll-contract`、`rectification-agentic-entry`、`rectification-answer-choice` 的 hook/组件锁与 `nextAnchorState` 纯函数断言代替。
|
||
|
||
## 任务 4 · DESIGN.md
|
||
|
||
已改:§3 思考正文 13px 写成显式例外;§5 Message 正文 16px → 17px(≤767px 时 16px,与 `globals.css` 一致)、Motion 改为"只有 GSAP 一份 160ms、结算不重放"、新增「Streaming states」七态表(含 failed 不用消息内红字、pacing 公式、reduced-motion 行为、校正只差不显示思考正文);§6 表新增 Thinking collapse 180ms / Timeline label swap 120ms / Text reveal;§9 等待表把时间线 live 步写成两面共用,注明 live 标记只有 `InlineSpinner`、orb 已移除。
|
||
|
||
随任务 3 已改:§4 阅读宽度写明两面同 900px;§5 Input and composer 加「One composer」条;§5 新增「Jump to latest」条目;§7 写明 `--shadow-soft` 的实际归属(叙事卡、会话内 composer 字段、starter 主题卡 focus、auth 模式 tab)与「浮动控件一律 `--shadow-elevated`、不得用工具类阴影」。
|
||
|
||
## 被触碰的既有断言(红线 3,每处源码上方均有原值与错因注释)
|
||
|
||
| 文件 | 原断言 | 现在 | 理由 |
|
||
| --- | --- | --- | --- |
|
||
| `tests/home-streaming-render-split.test.ts` | `split.streamingRowRenders === tokens.length` | `≤ tokens.length && ≥ 1`,另加按帧驱动的新用例 | 原值是"每 token 一次提交"的现状快照(BUG-473) |
|
||
| `tests/chat-bundle-splitting-contract.test.ts` | `duration: 0\.18` | `0\.16` | DESIGN.md §6 160ms,0.18 是 GSAP 副本漂移(BUG-474) |
|
||
| `tests/session-conversation-layout.test.ts` | `duration:\s*0\.18` | `0\.16` | 同上 |
|
||
| `tests/class-name-definition-contract.test.ts` | `knownUnstyled` 含 `consultation-run-timeline__summary`、`consultation-step-tree` | 两条移除 | 前者现在有样式(按钮),后者类名已随组件删除;该测试本身要求列表无过期项 |
|
||
| `tests/chat-bundle-splitting-contract.test.ts` | "the thinking orb loads on demand…"(`OrbState` 导入、`dynamic(...)`、20px 占位、`prefetchOnIdle(importThinkingOrb)`) | 改为"live 标记是共享 InlineSpinner,无 orb chunk" | orb 是第二种 live 标记且被 14px 裁切(BUG-476) |
|
||
| `tests/chat-stream-layout.test.ts` | `reportSource`/`treeSource` 读取;`<ThinkingOrb … size={20}`;`ConsultationThinkingReport`、`hasTrace`、`activityTrace={message.activityTrace}`、`activityTrace ?? []`、`function TraceStep`、`.agent-thinking-step.is-think:has(...)`、report/tree 内容锁、`consultation-step-tree__*` CSS 锁、`.consultation-thinking-report .message-thinking` | 改为锁"只剩一条路径"(`doesNotMatch` 这些符号)+ `InlineSpinner size={12}` | 锁的正是被删除的第二/第三条思考渲染路径(BUG-476) |
|
||
| `tests/rectification-agentic-entry.test.ts` | 读取 `completed-activity-receipt.tsx` 并锁其 `<details>`/不 open/「本轮完成 · N 个步骤」;`activityStatus /activityTrace/` | 改为锁 `chat /rectificationTimelineRows/`、`chat /completedReceipt/`、`activityStatus` 不含 `activityTrace` | 锁的是校正专属第二折叠块与 trace 面板(BUG-476) |
|
||
| `tests/rectification-activity-receipt.test.ts` | `deltaBranch` 内 `state: raw.trim() ? "streaming" : "thinking"`、`text: raw,`;`message.state === "settled" && message.failed` | 状态派生改锁 frame flush 段(`state: text.trim() ? …`、`text,`、`frames.setAnswer(raw)`);失败行改锁 `&& !message.failed` 且无 `rectification-activity-failure` | 前者锁的是每 chunk 一次 setMessages(BUG-473),后者锁的是校正专属红字失败行(BUG-476)。**注**:这个文件在任务 0 的聚焦跑里漏跑了,任务 2 跑全量前才发现,改在 `cc17d971` |
|
||
|
||
**任务 3 触碰的断言**(同样每处有原值与错因注释):
|
||
|
||
| 文件 | 原断言 | 现在 | 理由 |
|
||
| --- | --- | --- | --- |
|
||
| `tests/character-remaining-contract.test.ts` | `aria-describedby={showRemaining ? composerRemainingId : undefined}` | `remainingId = composerRemainingId` 默认 + `aria-describedby={showRemaining ? remainingId : undefined}` | composer 现为两面共用,校正传自己的计数 id(BUG-477) |
|
||
| `tests/composer-isolation-contract.test.ts` | `const draft = useComposerDraft()` | `const storeDraft = useComposerDraft(); const draft = value ?? storeDraft;` + 校正传 `value={draft}` 且不 import `composer-draft` | 受控 `value` 是任务书要求,隔离性质加强而非减弱(BUG-477) |
|
||
| `tests/chat-navigation-a11y-contract.test.ts` | 内联按钮的 `min-h-11` / `focus-visible:ring-3` / `pointer-events-auto` / `<ArrowDown … className="size-4"` | 页面锁 `<JumpToLatestButton onClick={conversationAnchor.anchorToLatest} />`,语义改锁组件源码 + `.jump-to-latest__button` 的 44px / focus-visible / pointer-events | 锁的是内联 Tailwind 按钮本身(BUG-478) |
|
||
| `tests/chat-notice-and-scroll-contract.test.ts` | 三处:page effect 内 `anchored` 守卫先于 `container.scrollTo`;jump control 的 Tailwind 类;`absolute inset-x-0 bottom-full` | follow 在 hook 内、`anchoredRef` 守卫先于 `scrollTop` 赋值、`page.tsx` 无 `scrollTo`、`ResizeObserver`/`MutationObserver` 存在;按钮改锁组件与 CSS;定位改锁 `.jump-to-latest { position: absolute; bottom: 100% }` | 锁的是每 token 一次 `scrollTo` 的 effect 与内联按钮(BUG-478) |
|
||
| `tests/chat-stream-layout.test.ts` | composerWrap 含 `pointer-events-none absolute inset-x-0 bottom-full` | 含 `<JumpToLatestButton` + CSS 锁 | 同上 |
|
||
| `tests/starter-questions.test.ts` | page effect 以 `if (starterHomeVisible) return` 开头并调 `scrollTo` | hook 调用第二参含 `!starterHomeVisible`,follow effect `if (!active \|\| !element) return` 先于赋值,page 无 `scrollTo` | 性质(starter home 不自动滚)由 `active` 承担(BUG-478)。这条在任务 3 第一次全量跑才暴露,改在最后的 docs commit |
|
||
| `tests/rectification-agentic-entry.test.ts` | 整段校正专属跟随锁(`followTailRef`、`isNearBottom`、`shouldShowJumpToLatest`、`choiceCardsOpen`、`回到最新`、`.rectification-jump-latest`);`className="composer-stop"`、`aria-label="停止回答"`;`<form className="composer"` | 锁 `useConversationScrollAnchor(conversation, true, caseId)`、`<JumpToLatestButton …>`、这些符号不再出现、`.jump-to-latest` 居中;`stopLabel="停止回答"`、`onStop={stopRun}`、`composer-stop` 在 `chat-composer.tsx`;`<ChatComposer` + `RECTIFICATION_COMPOSER_MAX_LENGTH = 500` + `<CharacterRemaining` | 锁的是第二套滚动与第二个输入框(BUG-477/476) |
|
||
| `tests/rectification-answer-choice.test.ts` | import `rectification-sticky-scroll` 并断言 `isNearBottom`/`shouldFollowLatest`/`shouldShowJumpToLatest`(含 360px 选择卡带宽);`回到最新`、`followTailRef.current` | import hook 的 `conversationAnchorThreshold`/`nextAnchorState`,断言 96px 与同一组距离判定;锁 `<JumpToLatestButton …>` 与 `conversationAnchor.anchored` | 模块已删除(BUG-478) |
|
||
|
||
新增测试:`tests/stream-frame-buffer.test.ts`(7)、`tests/chat-markdown-split.test.ts`(4)、`tests/chat-stream-settle-contract.test.ts`(4)、`tests/rectification-timeline-adapter.test.ts`(6)、`home-streaming-render-split` +1。
|
||
|
||
## 命令输出
|
||
|
||
`./node_modules/.bin/tsc --noEmit` → **0**(任务 0/1/2 各一次;rebase 后一次;任务 3 后一次,均无诊断)。
|
||
|
||
`./node_modules/.bin/eslint` 触及文件 → **0 error**。warning 只落在 `page.tsx`(43 条,与 `origin/staging` 同文件同数)与 `use-consultation-run.ts`(9 条既有 `no-unused-vars`,本轮只加一行 import);本轮新建的六个文件 0 warning。
|
||
|
||
`./node_modules/.bin/tsx --test tests/*.test.ts`(基线在干净的 `origin/staging` @ `058e5db9` 独立 worktree 里 `npm ci` 后跑):
|
||
|
||
| | 旧基线 `fbb80fa3` | 基线 `058e5db9` | 任务 3 后(`058e5db9` 上) | 最终(rebase 到 `1a3e14e7` 后,HEAD 树) |
|
||
| --- | ---: | ---: | ---: | ---: |
|
||
| tests | 2460 | 2461 | 2482 | 2484 |
|
||
| pass | 2426 | 2427 | 2448 | 2450 |
|
||
| fail | 24 | 24 | 24 | 24 |
|
||
| skipped | 10 | 10 | 10 | 10 |
|
||
|
||
`1a3e14e7` 本身只改了校正类测试文件(+2 用例),没有另跑一份干净基线;最终列的 24 条失败清单与 `058e5db9` 基线逐条比对差集为空,仍是同一组环境缺口。
|
||
|
||
24 条失败与新基线逐条比对**完全同一集合**(`comm` 差集为空):全部是本机无 Docker / PostgreSQL / 部署夹具的数据库、Better Auth、staging workflow YAML、live sync 类(admin code functions、Owner recovery、local PostgreSQL、redeem security、personal report job/sections RLS、migration runner、v9 migration 等)。10 条 skipped 同基线。无产品逻辑新增失败。任务 3 第一次全量跑曾多出 1 条 `starter-questions` 的源码锁失败,按红线 3 改锁 hook 后(见上表)复跑归零。
|
||
|
||
rebase 后、任务 3 前的聚焦 14 套件 117/117;任务 3 后的聚焦 17 套件 177/177。
|
||
|
||
`./node_modules/.bin/next build` → **0**。`✓ Compiled successfully in 22.6s`、`Finished TypeScript in 28.4s`、`Generating static pages (62/62)`,`/` 仍是 `○ (Static)`。5 条既有 `skill-package-registry.ts` filesystem-tracing warning,未失败。
|
||
|
||
## 未做与原因
|
||
|
||
见 `BLOCKED.md` 顶部:任务 3 已解除并完成;仍未做的是浏览器级手工验收(Performance 录制、结算录屏、两面并排截图、流式期间折叠、两面滚动跟随实机)——无登录态无 Chrome;`latestEntryMounts` 探针无 DOM 测试库不可驱动。
|