Skip to content

Latest commit

 

History

History
1103 lines (914 loc) · 225 KB

File metadata and controls

1103 lines (914 loc) · 225 KB

2026-09-11 · 前端重设计:组件体系 / 布局 / 动效(L0–L6 全层设计)

状态:设计已批准(2026-09-11 用户逐项裁决)· 待实施 定位:一次覆盖全站的前端重设计。回答用户的三个要求 —— ① 重新设计组件类型(是否有更好的组件负责已有功能)② 重新布局设计 ③ 为各组件添加动效 —— 并补齐用户未提到的基座、治理、未接线落地三类任务。 方法:三层只读侦察(未接线命令 / 组件与布局清单 / 动效与规格审计,证据均带 文件:行)+ 14 屏用户逐项裁决。 关联:长期优化清单 · UX 市场惯例审计 · 需求池 · ADR-010 · v0.20 原则:本地优先不破(数据不出本机、云端能力默认关闭且有本地降级)· 不引入路由库 · 不砍任何已接线功能 · 每批可编译可测试可回滚。


0. 一句话

把「一份内容只有一种样子」的产品,改造成「一份内容有多种样子 + 会呼吸、会生长、会显影的纸」—— 在此之前先补上它从未有过的地基:token 层、原语层、视图层。


1. 决策记录(用户逐项裁决,L0–L5)

L0 · 视觉基座

# 分叉 裁决
1 基座路线 C 全部从代码:回写 ui-ux-system.md / theme.md 为目标态,壳层与视觉自由重设计
2 视觉方向 C 活页(纸色 + 中文衬线正文 + 印章状态 + 到期刻度)
3 材质语言 D 显影(四档墨度 = 确定度;未确认档 3.22:1 + 三条升档规则)
4 来源标记 ① 剪报底纹,默认开启;②边注线不做;③套准角标不做
5 图标语言 A 自绘线性图标集(约 44 个 / 1.75px / 小圆角 / 零依赖)

L1 · 原语层

# 分叉 裁决
6 收敛范围 10 类原语(Modal / z-index 标尺 / EmptyState / Loading / StatusLine / Toast / ConfirmDialog / Button / Surface / Text)
7 删除哲学 甲 两档分级:低危可逆 → 撤销 toast 10s;高危不可逆 → 确认框并列明级联;级联不给撤销
8 软删/回收站 不做(需 schema 迁移),登记为独立技术债
9 拆件顺序 NotesPage → NoteListView → SessionDetailPanel → ClassroomPage(行为等价)
10 ADR ADR-032 / 033 / 034 / 035 四条 + 修订 ADR-010
11 原语消息通道 保留「探针」与「骨架屏微光」两条旧规格(分工:骨架=已知结构,探针=时长未知)

L2 · 容器与壳

# 分叉 裁决
12 导航形态 A′ 顶部 Tab(8 项:7 域 + AI 对话;设置下沉右上齿轮)+ ⌘K 命令入口
13 域数纪律 新增能力进域内页签,不再升格为域(v0.20.5 已写下但未执行到底的原则)
14 顶栏溢出 两级:≥1180 图标+文字;1024–1180 仅图标+悬浮名;<1024 由最小窗兜底
15 相变壳层 两态:采集态(LIVE 仪表 + 域导航隐藏)· 复习态(零 chrome)。不做四态
16 列契约 三列上限 + 单一注册表 ColumnSpec + 断点 1024/1100/1180 + --nav-h 变量
17 窗口 默认 1280×800,最小 1024×640;补 html,body,#root reset;滚动条 6px
18 AI 对话 Tab 保守保留为第 8 项 —— dock 是否承载 ChatPage 全部能力尚未核实,先核对再删

L3 · 视图层

# 分叉 裁决
19 视图层四部件 ViewSpec · viewRegistry · ViewSwitcher 原语 · 依赖方向 领域→视图→容器→原语
20 本批视图 会话 4(三轨 / 印样 / 原文 / 卡片流)· 笔记 3(原文 / 卡片流 / 带证据三轨)=新增 5 个组件
21 范围纪律 其余域的视图登记不排期
22 三条硬约束 原文永远保留 · 非默认视图惰性挂载 · 编辑态切视图先 flushSave(失败则阻断)
23 视图记忆 按对象类型记(view:default:{objectType}),不按单个对象

L4 · 动效

# 分叉 裁决
24 引擎 GSAP(core + Flip + ScrollTo + CustomEase)+ @gsap/react/useGSAP
25 纲领 「活的纸」四层:响应 / 环境 / 编排 / 生长。统一语法而非限制数量
26 基调 丙 双基调:凡有「读数」的界面用精密仪器,凡有「文字」的界面用纸墨
27 强度档 三档(节能 / 标准 / 丰富),系统 prefers-reduced-motion 优先于档位
28 「不做」清单 从 7 条降至 3 条(3D 翻转/粒子 · 夸张响应 · 页面转场滑动),且降级为材质判断而非设计原则
29 用户纲领 用户明确要求:「充满动效、充满创新设计、充满生命力,而不是呆板死板」 —— 本节为准绳

L5 · 未接线落地

# 分叉 裁决
30 A 桶 能接入 UI 的尽量接入(15 条 → 本批 12 条 + 登记不排期)
31 B 桶 4 条接入 UI;3 条从 IPC 撤下但保留内部函数
32 C 桶 除视频档案四连外删去
33 视频档案四连 删 3(video_profile_by_kind / video_profile_spec_by_kind / remember_video_profile)· 留 video_profile_memory(诊断)· 留 video_profile_for_spec(档位读端)
34 档位通道 A 做完整:start_live_session 加 tier + 记忆加 tier 字段 + 新 remember_video_profile_tier 命令
35 标签线 A 补完:标签可写 + 标签色成对 + tag_colors 补种子/迁移
36 AI 补缝三连 C 全删 + 连带模块 + 修订 ADR-010 为退役

2. 现状基线(三层侦察实测,实施前须复核)

维度 实测
壳层 无路由;useState<Page> + 9 个顶部 Tab;9 页全部常驻挂载(display:none);13 个窗口级状态,其中 9 个是手写「带参路由」focus* 字段
组件 131 个非测试组件,0 死组件;25 个 >300 行;4 个 >600 行硬限违规(ClassroomPage 724 / SessionDetailPanel 656 / NoteListView 609 / NotesPage 602)
样式 2,256 处内联 style / 仅 3 处 className / 0 个 CSS 变量 / 88 个不同 hex / 2,533 处 hex 字面量;App.css 是未 import 的死文件且非合法 UTF-8
弹层 20 个手写全屏弹层 · 17 个各不相同 z-index · createPortal 0 · role="dialog" 0 · 焦点陷阱 0 · 无退出动画机制
重复实现 空态 44 · 加载态 85 · 错误行 196 处 / 76 文件 · 弱化文本 251 · 卡片边框 180 · toast 4 套 · 确认框 2 套(21 处) · markdown 渲染器 3 套
布局 useColumnLayout 只被 4/9 页采用;ChatPage 硬编码 240、GoalsPage 硬编码 380;calc(100vh - 56px) 复制进 7 个文件
动效 0 个 @keyframes;5 条存活 transition;1 条坏动画(ChatMessageList.tsx:147 引用不存在的 chatBlink);prefers-reduced-motion 0 处;无动效库
包体 JS 2,093.85 kB / gzip 651.25 kB(单个 chunk,零代码分割);performance.md 预算为 <200KB gzip → 超标 3.26 倍
未接线 334 个 Tauri 命令中 47 个前端从未调用;其中 30 个连 Rust 内部也无调用方

2026-09-12 批 2 回写(原文一律保留):上表「包体」行的 651.25 kB 是批 2 开工前的过期快照 —— dev@f12aba6d 实测 2,106.46 kB 原始 / gzip 654.72 kB(+12.61 原始 / +3.47 gzip),故「超标 3.26 倍」应作 3.27×。 批 2 已收口(2026-09-12,e46e0e82..9921767c):首屏 JS gzip 654.72 → 92.79 kB(−85.83%)⇒ ✅ 达标(预算来源 docs/standards/performance.md:28 的 < 200 kB gzip,守卫 scripts/check-bundle-budget.mjs exit 0,余量 107.21 kB)。 诚实代价(不许省略):全部 JS gzip +12.91 kB(+1.97%)· >500 kB 警告仍在(vendor-editor 208.59 kB,懒 chunk)· dist 总量只降 4,415 B(−0.14%),59 个 KaTeX 字体 1,072,948 B 一字节未动 ⇒ 磁盘与安装包不因此变小。 包体瓶颈清单、逐条复现命令与 follow-ups 见 批 2 实施计划 §收口回写(批 3+ 计划的前置输入就是该清单)。

2026-09-12 批 3 回写(原文一律保留,只加注)—— 本表 4 处已漂移(D1–D7 中的 D1/D2/D7 + D3/D6 见 §6.2) D1 · 「壳层」行逐字写「9 个顶部 Tab」——不完整:批 3 开工时顶栏的真实形态是 9 个 Tab(含「⚙ 设置」)+ 对话面板钮 + 采集徽标 + AI toast(App.tsx:286-380,四项同在一行)。批 3 后:8 个域 Tab + ⌘K + 采集徽标 + 齿轮(设置下沉为齿轮),AI toast 移出导航行(进 fixed 覆盖层)⇒ 本行是开工前快照,今日形态以 §6.1 与其回写注为准。 D2 · 「布局」行逐字写「useColumnLayout 只被 4/9 页采用」——页数一致、处数不同:实测 4 页 / 7 处(NotesPage 一页 3 处)。批 3 后:9/9 页的目的地都在 shell/columnRegistry.ts 里,执行仍由该 hook(7 处旧调用点 + 4 处新接入)。 D7 · 「壳层」行逐字写「9 个是手写『带参路由』focus* 字段」——实测 10 个(多 focusChatId)。批 3 一个字段未删(只做入口收敛,状态机归批 5)。 D4 · 审计侧(不在本表):审计 J1-8 写「56px 魔数硬编码三处」,实测 10 处 / 9 文件(App.tsx height + AiConversationDock top + 8 个页面的 calc(100vh - 56px),其中 ReviewPage 占 2 处)——批 3 T2/T3 已把这 10 处全部改为消费 --ed-nav-h。审计文档本身未改(归批 8 的文档 pass)。 D5 · 审计侧(不在本表):审计落点 NotesPage:395 / App:225 因批 0-C2 拆件已过期(行号不再指向原处)——只登记,同归批 8 的文档 pass。

⚠️ [DEAD] 标记有污染:原侦察的「有无内部调用方」判定被测试文件引用污染(例:set_tag_color 实际零调用,因被自己单测引用而被误判为「有内部调用方」)。实施删命令时必须逐条重新确认一次调用方。


3. 红线与经批准的例外

红线(不得触碰)

  1. 本地优先:数据不出本机;云端能力默认关闭、须用户授权,且必须有本地降级路径。
  2. 不引入路由库:延续「页面保活挂载」模式;导航形态可改,技术底座不换。
  3. 不砍任何已接线功能:所有视图切换都是「+1 种形式」,不是「换掉原来的」。
  4. 每步可验证:每批结束 npx tsc --noEmit 0 错 · npx vitest run 全绿 · cargo test 全绿。
  5. 行数红线:新原语必须是独立小文件;不往 4 个超限文件里加代码,先拆再加。
  6. 后端数据模型零改动 —— 除下列两处经批准的例外。

经批准的两处例外(各自需登记原因 / 影响面 / 回滚方式)

# 例外 原因 影响面 回滚
E1 MemoryEntry 增加 tier 字段 档案记忆库根本没有 tier 轴,导致「画面档位选完不生效、也不跨会话记住」 video_profile_memory.json 序列化结构 + lookup/apply 路径 旧 JSON 无该字段时按 None 读取(向后兼容读,无需迁移)
E2 tag_colors 补种子/迁移写入 表建了但永远没有数据,导致前端色板第三档恒不生效 一条迁移 + 标签色写入路径 迁移可空跑;标签色本身是可逆 UI 状态

🔻 批 6 收口就地加注 · 红线 6 的第三处例外 E3 / E4 / E5(R0.1 第 1 条①② · R5.4 / R5.5 / R5.5-b;2026-09-13,上面原文一字未改): 上面 :127 的「除下列两处经批准的例外」与 :129 的「经批准的两处例外(各自需登记原因 / 影响面 / 回滚方式)」按本加注读作三处;上表两行原文保留,新增三行逐列如下(依据 = 用户 2026-09-13 裁决「批 6 范围 = B · 连承载面一起建」⇒ 三条硬后果第 1 条)。三条共同约束(逐条适用):只读 / 不新增表 / 不改既有字段与既有 SQL 文本的语义 / 零迁移(flashcards 建表后零 ALTER TABLE;CARD_COLUMNS 不动);都不触碰 AGENTS.md §10 的「SQLite schema / 迁移」面。

# 例外 原因 影响面 回滚
E3 Flashcard += 只读字段 intervalDays(camelCase;Rust 4 文件 / 5 处 + 前端可选字段) 真实间隔前端今天拿不到,但后端已经算出来了(scheduler.rs:69 的 interval_days: f32 与 :99 的 max(1.0)),而 review_card 的返回只给 state_json / due_at、写库只有 UPDATE flashcards SET state_json = ?1, due_at = ?2 ⇒ DB 无 interval 列;而 stateJson 的契约是「后端调度契约,前端透传不解析」⇒ 前端不得用 dueAt 差分或字符数自造「真实间隔」 Flashcard 是 6 条命令的共享返回类型 ⇒ 字段会同时出现在 list_due_cards / review_card / quiz_group_cards / create_model_card / list_group_cards / model_card_from_note 的返回里(本批只消费前两处,其余四处如实登记为「顺带变化」)。前端 app/src/types/notes.ts 加可选 intervalDays?: number(零夹具改动)。🔴 两个精度域必须在类型注释里显式声明:review_card 路径 = 精确值(当场覆写 —— ⚠️ 陷阱:该函数用 ..card 结构体更新语法,不覆写会漏出旧值);行派生路径 = 整天粒度(row_to_card 由 dueAt − stateJson.lastReviewMs 反推 max(round(…),1)) 删字段 + 还原 5 处(types_note.rs · db_flashcards.rs×2 · commands_flashcards.rs · types_contract_tests.rs 的追加行)+ 删前端可选字段。零迁移、零 SQL 变更 ⇒ 不影响任何已落库数据
E4 新增只读 IPC session_audio_path(命令注册 312 → 313) 音频真的落盘且已接线,但前端拿不到路径:audio_store.rs 写 {data_dir}/session-audio/{session_id}.wav(16 kHz 单声道 PCM16,44 字节 RIFF 头,~115 MB/小时);播放三件套已现成(tauri.conf.json:24 的 CSP 已含 media-src … asset:、:25-28 的 assetProtocol.enable=true + scope 覆盖 app_data_dir)⇒ 🔴 本批零 tauri.conf.json 改动(AGENTS.md §10 该审查面不触发) 本批唯一的新增 IPC:入参 session_id: i64;出参 Option<SessionAudioRef>(字段只许 path / aligned / durationMs —— aligned 自证量必须到 UI,Option<String> 装不下);计数 312 → 313。🔴 安全三条:① 必须校验入参 ② 路径必须限定在应用数据目录内(不许把任意路径透传给 asset protocol)③ 不许前端 fetch() 整取音频 ⇒ 只许 <audio src>。⚠️ 适用范围硬边界:只有实时采集会话有音频;导入会话恒 Ok(None) 删命令 + 删注册行 ⇒ 计数回 312;前端删调用点即回到「只读时间轨、无音频播放」。零依赖变更、零 tauri.conf.json 改动
E5 WAV 轴 ≡ 会话轴:写块路径按 chunk.timestamp_ms 的空档等长补静音 + 失效安全 + aligned 自证量 今天 segments[].start_ms / 1000 不能当 audio.currentTime:start_ms 是会话纪元墙钟 − total_paused_ms,而 WAV 轴 = 已写样本数 ÷ 16000、纯追加、无任何静音填充。⚠️ 暂停本身不产生偏移(端点 Stop ↔ 时间戳冻结严格抵消);间隙来自静默窗根本不产包 ⇒ 偏差 D2 最危险且无上界(大量静默的课程录音可达分钟级)。「只暴露 first_sample_ms + samples_written」的最小方案只修 D1、会交付一个「看起来对、实际会漂」的播放头 live_session*.rs(AGENTS.md §10 名单内 ⇒ 该任务必须显式执行 §10 审查记录)· audio_store.rs(write_chunk 加 timestamp_ms 入参 + 对齐簿记 + finalize 写 sidecar)· live_session_loop.rs 一个调用点 · 新纯函数 audio_align.rs + 测试 · audio_store_tests.rs 的既有调用点改签名(必要连带,只改调用形态、不改任何期望值)· ADR-013 就地加注。失效安全语义:时间戳缺失 / 非单调 / 超大空档(> MAX_GAP_MS = 10 分钟)⇒ 退回纯追加并置 aligned = false(绝不静默分歧),不丢样本、不阻断会话主链路。🔴 历史录音必须如实区分:本修复之前的 WAV 不保证对齐 ⇒ UI 对无基准的录音不得假装精确;禁止用恒为 true 的字段充当该标记 还原 1 个签名 + 1 个调用点 + 删 2 个新文件 + 停写 sidecar(补静音只影响新录音,既有 WAV 一字不动);aligned 随命令一同删除。零新增依赖、零迁移、零新数据面

① 🔴 cargo test --test app_lib_tests 本批真跑(R0.1 第 1 条③,不得把「未跑」写成「已跑」):running 2335 tests → test result: ok. 2329 passed; 0 failed; 6 ignored,exit 0(读数出处:task-34-report.md §3)。② 例外 E4 的落地实测:注册表 313 / 313 / 0(定义 313 / 注册 313 / 重复 0)。③ 逐条细则与「①域(精确值)不落库」的完整口径见 ADR-035 的「后端契约例外」节(本批定稿)。


4. L0 · 视觉基座

4.1 色阶(亮为主档,暗为「夜读」第二档)

变量 亮(主) 暗(夜读) 用途 / 约束
--bg-sunken #F1EEE7 #100F0E 输入槽 / 骨架 / 内嵌
--bg-canvas #FBFAF8 #141312 窗口底(纸)
--bg-surface #FFFFFF #1C1A18 卡片 / 列 / 阅读面
--bg-raised #FFFFFF + --shadow-1 #24211E 弹层 / 菜单 / 浮窗
--border #EAE7E0 #2E2A26 横格 / 分隔
--border-strong #C9C4B8 #423C36 输入框 / 刻度底 / 引线
--ink-4 未确认 #909088 #6E6A62 3.08 纸 / 3.22 面(暗 3.45 / 3.22)—— 过渡态,见 4.3
--ink-3 已重打分 #6E6E68 #9A958B 4.92 / 5.13(暗 6.23 / 5.82)
--ink-2 已确认 #3A3A36 #D6D1C8 10.95 / 11.42(暗 12.21 / 11.41)—— 正文基准
--ink-1 已改写 #1A1A1A #F5F1E8 16.68 / 17.40(暗 16.46 / 15.39);唯一使用字重 +1 档的档位
--mark-clip #F4F1E9 #221F1B 剪报底纹(默认开)
--stamp #B3271E #E0604F 唯一非中性色,只用于状态戳
--ok 掌握 #2F7A4F #4FAE74 掌握 / 已毕业
--due 到期 #9F5E10 #E0A44B 4.93 / 5.14(暗 8.47 / 7.92)· 剪报底 4.56(第二次修正,见下)· 到期刻度 / 低置信点线 / 记忆语义文字
--link #1F5FBF #6E9BE8 链接 / 时间码 / 引用
--overlay #1A1A1A(配 --ed-overlay-alpha: 0.34) 同左 弹层遮罩基色 —— 两档同值:遮罩是叠加在内容之上的中性压暗,不随主题翻转

变量前缀:上表为可读性省略了前缀,实际变量名一律带 --ed-(如 --ed-bg-canvas、--ed-ink-2)。前缀沿用 theme.md 既有意图,并避免与第三方 CSS(katex / @xyflow/react)的变量碰撞。

两组 token 的交付形态(防止批 4 迁移时重新硬编码): ① z-index 六档标尺以 TS 模块交付(app/src/ui/zIndex.ts,见 §5.1 与批 0-A 计划 Task 4),不作为 CSS 变量 —— 组件以 zIndex("modal") 消费它;写成 CSS 变量反而会重新打开「谁都能随手写个数字」的口子。§4.2 表中列 z-index 只为集中陈述档位值,不代表它是 CSS 变量。 ② --ed-shadow-1 / --ed-shadow-2 已定值(2026-09-11,用户裁决「纸感双层暖墨」;批 0-D 落地):

  • 亮档低层 --ed-shadow-1(菜单 / 浮层 / 小卡)= 0 1px 2px rgba(28,25,23,.06), 0 4px 12px rgba(28,25,23,.08)
  • 亮档高层 --ed-shadow-2(Modal / 浮窗)= 0 2px 4px rgba(28,25,23,.06), 0 12px 32px rgba(28,25,23,.14)
  • 暗档不用投影,改白色反相描边 0 0 0 1px rgba(255,255,255,.06)(沿用 docs/product/ui-ux-system.md:204 的既有做法)
  • 定值依据:全仓 boxShadow 实测 38 文件各写一份、18 个互不相同的值且天然聚成两档(低 0 4px 12px .12×6;高 0 8px 24px .14×5 / 0 10px 40px .15×4 / 0 12px 40px .2×3)—— 用户选的是纸感方向而非照抄这两簇。批 4 迁移时不得临时硬编码阴影。
  • 命名关系:ui-ux-system.md:186 原有的 --ed-shadow-card(亮/暗各一)被 --ed-shadow-1 取代,批 0-D 在该文档注明并给出新值(该文档属 AGENTS.md §10,文档与代码同提交)。

对比度纪律:亮档强调色不可照搬暗档 —— 同一 #17C3B2 在暗底 8.6:1、在白底仅 2.2:1(实测 2.21)。故两档各给一值。

表中数字均为实测(WCAG 2.1 公式,「纸」= --bg-canvas、「面」= --bg-surface)。两张底不同:正文实际坐在面上,故正文类断言以面为基准。上一版规格写的是估算值(11.6 / 5.0 / 3.1 / 11.5),与实测有 4 处出入,已按实测修正 —— 这也是为什么 token 层必须配对比度测试(见批 0-A 计划 Task 2/3)。

--due 亮档从 #B26A12 改为 #A05F10(第一次修正):原值对纸底仅 4.06:1、对面 4.23:1,低于正文 4.5:1 线,而它要承载「记忆语义」文字(如「3 天后」)。新值 4.86 / 5.07 达标。

--due 亮档第二次对比度修正(2026-09-11,批 0-D Task 1 落地):#A05F10 → #9F5E10。触发底是剪报底纹(--ed-mark-clip #F4F1E9)这第三种底:#A05F10 在其上仅 4.4950:1,低于正文线。求解规则 = 保持色相、RGB 三通道按同一系数 f 缩放(hex(round(ch × f))),取满足「① 纸 #FBFAF8 ≥4.5 且 ② 面 #FFFFFF ≥4.5 且 ③ 剪报底 #F4F1E9 ≥4.55」的最小加深量 ⇒ f = 0.994(余量 0.05 是硬要求:#A05F10 正是「卡在 4.4950」的反例,hex 量化与将来底色微调都会吃掉没有余量的值)。实测:剪报底 4.5571 / 纸 4.9309 / 面 5.1436。暗档 #E0A44B 不动(剪报底 7.4863 本就达标)。旧值 #A05F10 在剪报底上 <4.5 的反例守门落在 app/src/ui/contrast.test.ts。

4.2 其余 token

组 值
正文字体 "Source Han Serif SC", "Songti SC", SimSun, serif · 15.5px / 行高 1.9(= 29.5px) · 只随包 400 / 600 两个字重(子集 3–4MB)
界面字体 "Inter", "Segoe UI Variable", "Microsoft YaHei UI", system-ui, sans-serif(标题/按钮/标签用黑体;system-ui 为最终回落槽)
等宽字体 "JetBrains Mono", Consolas, ui-monospace, monospace + tabular-nums(时间码 / 元数据;monospace 为最终回落槽)
字阶 25/34·600 · 17/24·600 · 15.5/29.5·400 · 13/20·400 · 12/18·500 · 11.5/16·500 mono(下界 12px 约束正文与界面文字;11.5/16 mono 是唯一被点名的例外 —— 等宽用于时间码与元数据,11.5px 等宽的可读性高于同尺寸黑体。10px / 11px 一律消灭)
间距 4 · 8 · 12 · 16 · 24 · 32 · 48
圆角 3 印章 · 5 控件与卡 · 8 面板 · 10 浮层
图标 24 网格 · 描边 1.75 · 尺寸 16/20/24 · currentColor · 小圆角
z-index t1=10 吸顶 · t2=100 常驻面板/dock · t3=200 锚定弹层/菜单 · t4=300 Modal+遮罩 · t5=400 Modal 内嵌 · t6=500 Toast

字重与随包字体的关系:上表「正文字体」行的「只随包 400 / 600 两个字重」仅约束衬线正文字体(思源宋体子集);500 两档(12/18、11.5/16)属界面字体体系,由系统字体栈提供 —— Inter 与 Segoe UI Variable 有 500;微软雅黑只有 400/700,故在无 500 的系统上回落为 400,这是可接受的降级(小字标签用 400 亦成立),不因此增包第三个字重。

字阶的 CSS 交付形态(批 0-D Task 1 落地):字阶除人读串 SCALE_TOKENS.typeScale 外,另以 6 档 × 3 属性 = 18 个 CSS 变量交付 —— --ed-type-<n>-size / --ed-type-<n>-line / --ed-type-<n>-weight(n = 生成器 TYPE_SCALE 下标 + 1,自 1 起)。第 3 档的无单位行高 1.9 统一为 29.5px(15.5 × 1.9 = 29.45,按可用精度取一位小数 0.5px;差 0.05px 不影响任何排版判定,且比 29px 更接近真实行高)—— 无单位与 px 两种写法并存会让 CSS 无法统一消费。人读串由同一份结构化真源派生(TYPE_SCALE.map(...)),不允许各写一份。

🔻 批 4 收口就地加注(2026-09-12,§4.2 上表「圆角」行原文逐字保留**:| 圆角 | 3 印章 · 5 控件与卡 · 8 面板 · 10 浮层 |)—— 批 4 新增第 5 档 pill:上表是四档、今日实盘是五档:

  • 实测处数 = 3(域 = app/src/** 的 .ts/.tsx 减 *.test.ts(x) 减 ui/primitives/**,先剥注释;切片内处数同为 3):components/AiConversationDock.tsx:52 · components/RefineWorkbench.tsx:405 · :410。
  • 判定依据:控制方 B17 第 2 条逐字「切片内实测处数 ≥3 ⇒ 回来给原语加一档 radius="pill"」⇒ 3 ≥ 3 ⇒ 加档;落 T17-A 的 2360c3d9(feat(ui): add pill radius tier to surface,5 个文件 = Surface.{tsx,css} + Surface.test.tsx + style-contract.test.ts + style-seams.test.ts)。
  • 代价(必须一并读):① 本表没有 pill 档,token 真源 SCALE_TOKENS.radiusScale 与 app/src/ui/tokens.drift.test.ts:52 的 toEqual([3,5,8,10]) 今日仍是 4 档 ⇒ 原语走 var(--ed-radius-pill, 999px) 兜底(同 Text.css 的数值兜底范式);补 token 档要动四处(app/scripts/gen-tokens.mjs · ui/tokens.gen.ts · ui/tokens.css · ui/tokens.drift.test.ts:52)—— 归批 5/6;② 出处更正:本行是「四档圆角」的唯一原文出处(docs/product/ui-ux-system.md 无「四档圆角」字样,其圆角表述是 2026-08-24 旧目标态、§十一 自陈「待批 8 统一回写」⇒ 批 4 不动它);③ T17-B 没有落点用到 pill(3 处药丸落点按 B20 第 3 条属控件/徽标形态 ⇒ 不迁,登记在案)。
  • ⚠️ 一并登记的守卫性质:ui/primitives/style-seams.test.ts 的 SURFACE_CLASSES 与 toHaveLength(13) 实测是自计数式守卫(变异 MS1b:拿掉 pill 且同步把 13 改回 12 ⇒ 绿);完整性方向的牙在 style-contract.test.ts 的 Record<SurfaceRadius, string>(缺键 = TS2739)。是否升级为真判据 ⇒ follow-up(批 4 计划 §收口回写 §七)。

4.3 D 显影的四档墨度 —— 可及性裁决(必须写进规范)

四档墨度在浏览态下(「面对」= --bg-surface,正文实际所在的底):已确认 11.42:1 · 已重打分 5.13:1 · 未确认 3.22:1(低于正文 4.5:1 线)。规则:

  1. 未确认档是过渡态,不得承载唯一关键信息;
  2. 任何交互(悬停/聚焦/选中)立即升到正文墨度;
  3. 进入「审校模式」时全部升到 ≥4.5:1 —— 即「你真正开始读它的时候,它一定够黑」。

**🔻 批 4 收口就地加注(2026-09-12,上面三条原文一字未改)—— 条件 ③ 在仓内无实现,这一事实直接决定了批 4 的 Text 取值:

  • 实测:审校模式 / proofread 这一状态在 app/src/** 里没有任何实现(无该模式的开关、无「全体升到 ≥4.5:1」的墨度切换逻辑)⇒ 条件 ③ 今日不可满足(不是「已满足但没测」,是没有这个模式)。
  • 后果(T16-B 的保守取值,可逆):Text 迁移的墨度判定按判据原文「三条任一不满足 ⇒ 取 ink-3」执行 ⇒ ink-4 全程 0 使用(tone 一律 ink-3 = 5.13:1),仓内 ink-4 的生产调用点 = 0。
  • 去向:批 5/6 补「审校模式」实现之后,ink-4 的 3.22:1 例外才可能被合法使用(这是本行 ③ 的前置条件)。出处:task-16b-report.md §2 · 控制方 2026-09-12 追认。
  • 不受影响的两条:① 未确认档仍是过渡态、不承载唯一关键信息;② 交互立即升到正文墨度 —— 都在批 4 的 Text 档位与 interactive 语义内有落点。

🔻 批 6 收口就地加注 · 条件 ③ 的去向 + 环境层第 ③ 件的落点归属(R6.2 · R11.3 · R12.4 · R62⑤;2026-09-13,上面原文与批 4 加注一字未改):

  • 🔴 ink-4 的盘点终态 = 保持 0 生产调用点:上面批 4 加注的结论在批 6 未被推翻(本批零动作、未承接「审校模式」)⇒ 条件 ③ 的去向 = 批 7/8(ADR-035 的「登记」节逐字同名一条)。本批不为此新增任何例外。
  • 🔴 口径更正(R11.3 的落点归属,勿混用):环境层第 ③ 件「未确认段落墨度极缓慢起伏(幅度 2%)」的落点不是 ink-4,而是 lowConfidenceClass() + 类名 ed-text--low-confidence(落点 app/src/components/structuredBlocks.ts,消费者见下)—— ink-4 与它没有实现关系。⚠️ 兑现度如实登记:该件的生产调用点终态 = 1 处(components/session-detail/SessionRawView.tsx:137),而 R12.4 要求 ≥2 ⇒ 差 1(T25 / T27 均未补第 2 处;views/session/SessionTriTrackView.tsx 只剩 5 行余量)⇒ 本批只登记、不补(这是 R12.4 设计来抓的东西,不许用 T13 的 CSS seam 充数)。
  • 环境层四件的逐件兑现度(R11.3 逐字「不得把『落 seam 但 0 调用点』算作已交付」):① 探针摆动 = Loading.css 的 .ed-probe(既有,批 6 只对齐名单)· ② 采集脉冲 = seam 就绪、承载面未落(LiveBar 用波形条阵列,未做脉冲节拍)· ③ 未确认段落墨度起伏 = 1 处调用点(要求 ≥2,差 1) · ④ 到期刻度微光 = 落在 DueScale 的静态形态上(未做循环微光)。🔴 另:「丰富」档只兑现了频率提高,幅度上限未动(真正依据 = 本规格 §8.6.1 第 1 条逐字「环境层幅度上限不动」,不是「CSS 无原语」——逐档 keyframes 是可行的)。

🔻 批 7 收口就地加注 · 「审校模式」的规格定义**(C9.18;2026-09-13,上面原文与批 4 / 批 6 加注一字未改)—— 🔴 本节只定义,不实现:**

  • 🔴 状态逐字:未实现。 本节补的是条件 ③ 的前置定义(「进入审校模式」到底指什么);批 7 不写一行实现代码。⇒ 登记为批 8 输入(§11-7 与批 8 的输入清单里按名登记)。
  • ① 入口(哪个面、怎么进):阅读面(notes 对象)的全局开关,载体 = App 壳上的一个模式位(<html> 上的 data-proofread="on")+ 一个可发现的入口按钮(⌘/Ctrl+Shift+R,与既有 ⌘K / Esc 同族的键盘纪律)。不进视图注册表(它不是第四种「视图」,而是所有视图共同的面)⇒ 与 §7.2 的视图计数无关。
  • ② 作用域:当前阅读面的全部文本节点(含 NotesReadingColumn 的每一种视图、卡片流行、印样正文;不含编辑器(RichEditorView)与弹层外的外壳 —— 编辑态已有自己的墨度语义)。作用域随焦点走而不是随路由走:切到别的对象 ⇒ 模式保持(它是「我此刻在审读」的用户意图),但只影响当前挂载的阅读面。
  • ③ 退出:三条等价出口(都必须能出):再按同一个快捷键 · 点同一个入口按钮 · Esc(同 Modal 的退出纪律);退出后墨度立即回到未进入前的档位(不得留任何中间态)。
  • ④ data-* 形态(照 data-motion / data-tone 的范式):模式位写在 <html> 根元素上、不是类名(与 §8.3 的「载体逐字 = data-* 属性,禁止用类名区分」同一条纪律)—— · 属性名 data-proofread,取值 "on" | "off"(缺省 = off,且不落属性——与 data-motion 的缺省形态一致); · 墨度侧不新增 token:审校模式是档位覆盖(条件 ③ 逐字「全部升到 ≥4.5:1」),不是第五档墨度。
  • 🔻 批 8 T11 就地加注 · 第 ④ 条的属性名消歧(2026-09-13;上面第 ④ 条的原文一字未改):第 ④ 条原文的 data-proofread 与既有的「LLM 文本校对」同名不同物 —— 后者已占用 proofread 一族(src-tauri/src/commands_proofread.rs · components/ProofreadPanel.tsx · proofread_estimate / proofread_run;T11 实测,域 = git ls-files 1,628 文件,时点 4041eab5:子串 proofread = 250 处 / 38 文件)⇒ 🔴 本模式的属性名取 data-proofread-mode(控制方 §2 B2 逐字),不得用 data-proofread。 ⚠️ 第 ④ 条原文出现 data-proofread 的三处**(本注插入前实测 = 本文件 :235 / :239 / :241,域 = 规格全文)一律按本注改读为 data-proofread-mode;取值口径一个字不改(两态 "on" | "off",缺省 = off,且不落属性)。 ✅ 已落地的载体(批 8 T9):app/src/shell/proofreadMode.ts 的 PROOFREAD_MODE_ATTR = "data-proofread-mode";CSS 侧消费形态 = html[data-proofread-mode="on"](app/src/ui/proofread.css,由 main.tsx 在 token CSS 之后 import)。T11 实测:data-proofread-mode 形态全仓 = 46 处 / 6 文件(域 = git ls-files,时点 4041eab5)⇒ 名字已统一,没有任何生产代码再写 data-proofread。
  • ⑤ 实现层(一次覆盖,不改调用点):纯 CSS 一层完成 —— html[data-proofread="on"] 下把 --ed-ink-4 的值指向 --ed-ink-3(ui/tokens.css 的变量覆盖;.ed-text--ink-4 的规则一个字不用改)⇒ 「全部升到 ≥4.5:1」由变量重绑一次兑现,调用点零改动、零逐处判断。
  • ⑥ 今日实测(本节的两条读数):tone="ink-4" 的生产调用点 = 0 处 / 0 文件(正控 tone="ink-3" = 201 处 / 94 文件 ⇒ 非「假 0」;负控 zzz_no_such_symbol_zzz = 0);ink-4 的任意形态(含类型联合 / token 定义 / 对比度判据 / 注释)在生产侧 = 5 处 / 4 文件,全部是「ink-4 是过渡态、不得承载唯一关键信息」这类禁止性表述或定义,没有一处是把 ink-4 用在元素上。仪器:.superpowers/sdd/2026-09-12-frontend-redesign-batch7-unwired/tmp/t20/probe-spec-readings.mjs(只读;口径 = app/src/** 的 .ts/.tsx,PROD 排除 *.test.*)。
  • 🔻 补时点(2026-09-13 · T21 补轮;上面那条读数的数不动,只补时点与漂移声明):上面写的正控 tone="ink-3" = 201 处 / 94 文件 是某个时点的读数 —— 🔴 它在 fb73e313 那棵树上逐字可复现(侦察单元已证),此后 b72d4709(T20 的六条展示面)与 T23 又加了文件 ⇒ 数继续变。🔴 T21 于 53bd0759 用自己的仪器复测(入库域 = git ls-files;口径 = app/src/** 的 .ts/.tsx 内子串 tone="ink-3" 的出现次数):PROD(排除 *.test.*)= 214 处 / 99 文件 · TEST = 2 处 / 1 文件 · 全树 app/src = 216 处 / 100 文件。
  • 🔴 纪律逐字:该数随时点变化,引用必须带时点(并带仪器与域)。⚠️ 不得把 216/100 当成「真值」—— 它同样会漂;也不得把 201/94 当错值 —— 它是时点值。(本轮另见批 7 的「贴边件读数必须带时点」与「数字带时点」两条纪律,同源。)
  • 🔴 不得合并结案:条件 ③ 是环境层第 ③ 件之外的另一件事 —— 不能用「做了 lowConfidenceClass(未确认段落墨度起伏)」宣称本条已兑现(原文 §4.3 批 6 加注已逐字记载:ink-4 与它没有实现关系)。批 7 对 lowConfidenceClass 的加宽(生产调用点 1 → 2)不改变本条状态:仍是未实现。
  • 🔻 批 8 T3 就地加注 · 第 ⑤ 条的覆盖落点更正(2026-09-13;上面第 ⑤ 条的原文一字未改):第 ⑤ 条逐字点名的落点 ui/tokens.css 是生成物 ⇒ 不得在它里面写这一层覆盖。🔴 T3 实测:app/src/ui/tokens.css:2 逐字「由 scripts/gen-tokens.mjs 生成,请勿手改。」· :3 逐字「手改会被 src/ui/tokens.drift.test.ts 判失败。」;守卫本体 = app/src/ui/tokens.drift.test.ts:25-28(it("tokens.css 与生成器输出一致(手改即失败)") 断 expect(normalizeEol(onDisk)).toBe(normalizeEol(renderAll().css)))⇒ 手改必红。
  • ⇒ 🔴 批 8 的实际落点 = 新建 app/src/ui/proofread.css(控制方 §2 B1 逐字):在根元素模式位下把 --ed-ink-4 的值重绑到 --ed-ink-3,同时不动 app/scripts/gen-tokens.mjs 与 app/src/ui/primitives/Text.css。⚠️ 第 ⑤ 条原文的「ui/tokens.css 的变量覆盖」一句按本注改读为「专用 CSS 文件(app/src/ui/proofread.css)的变量覆盖」;本注只改落点、不改机制 —— 第 ⑤ 条的机制仍是「纯 CSS 一层、调用点零改动、零逐处判断」。

4.4 剪报底纹的防御性规则

底纹依赖「这段文字来自哪一路」。没有来源标记 → 按「无标记」渲染,绝不猜、绝不默认成某一通道;用户手写/改写的段落永远不标底纹。否则会出现「用户手写的句子被标成来自屏幕 OCR」这种事实性错误 —— 而这条底纹的全部价值就在于它可信。

前景裁决(2026-09-11 控制方,批 0-D Task 1 落地):剪报底纹只改背景,文字仍用所在层级的 --ed-ink-*,不新增前景 token。但 §4.3 的四档阈值是按「阅读面」定的 —— 换到剪报底就是换了一个底,故「底 × 墨」必须逐对实测(下列比值实测于 WCAG 2.1 公式,亮档底 --ed-mark-clip #F4F1E9、暗档底 #221F1B):

墨 亮档 / 剪报底 暗档 / 剪报底 判定
ink-1 15.4197 14.5564 ✅ ≥4.5
ink-2 10.1204 10.7931 ✅ ≥4.5
ink-3 4.5454 5.5052 ✅ ≥4.5(亮档余量仅 0.045)
ink-4 2.8489 3.0480 ❌ 亮档连 3:1 过渡态例外都不满足
stamp 5.7679 4.6533 ✅ ≥4.5
ok 4.6310 5.9688 ✅ ≥4.5
due 4.5571(第二次修正后) 7.4863 ✅ ≥4.5(修正前 #A05F10 仅 4.4950)
link 5.3968 5.8732 ✅ ≥4.5

规则(写死):剪报底纹上只用 ink-3 及更深(--ed-ink-3 / --ed-ink-2 / --ed-ink-1);--ed-ink-4 禁止用于剪报底纹 —— 它的 3:1 例外只在阅读面上成立(面对 3.22:1),剪报底上亮档实测 2.8489、暗档 3.0480。反例守门断言落在 app/src/ui/contrast.test.ts(「ink-4 亮档不得用于剪报底纹」toBeLessThan(3)),同一文档规则同步写进 docs/product/ui-ux-system.md §九。


5. L1 · 原语层

落地状态(批 0-D,2026-09-11):9 类原语已落地(第 10 类 z-index 标尺在批 0-A 已交付,本批只消费)。 目录 app/src/ui/primitives/(42 个文件,逐个 ≤300 行,最大 297),唯一导出面 index.ts;契约见 ADR-033。本批不迁移任何调用点 —— §5.1 的现状病灶要到批 4 才收敛, 界面外观零变化是设计意图(§10)。

原语 实际文件(实测行数 · countLines() 口径)
Text Text.tsx 89 · Text.css 46 · Text.test.tsx 130
Surface Surface.tsx 97 · Surface.css 73 · Surface.test.tsx 168
Button Button.tsx 132 · Button.css 92 · Button.test.tsx 286
Modal Modal.tsx 188 · Modal.css 81 · Modal.test.tsx 296 · Modal.exit.test.tsx 91
ConfirmDialog ConfirmDialog.tsx 192 · ConfirmDialog.css 58 · ConfirmDialog.test.tsx 297
Toast Toast.tsx 210 · Toast.css 65 · Toast.test.tsx 249 · Toast.interrupt.test.tsx 167 · Toast.style.test.ts 132
EmptyState EmptyState.tsx 127 · EmptyState.css 81 · EmptyState.test.tsx 276
Loading / Skeleton / Probe Loading.tsx 105 · Loading.css 79 · Loading.test.tsx 238
StatusLine StatusLine.tsx 94 · StatusLine.css 42 · StatusLine.test.tsx 267
共享内核 usePresence.ts 200 · usePresence.test.tsx 282 · usePresence.node.test.ts 71 · useFocusTrap.ts 122 · useFocusTrap.test.tsx 236 · ime.ts 18 · ime.test.ts 32
接缝 / 导出面 / 守卫 motion.css 74 · index.ts 45 · style-seams.test.ts 271 · style-contract.test.ts 214 · motion-coverage.test.ts 147

行数口径(唯一有效):countLines(),即 [System.IO.File]::ReadAllLines(path, UTF8).Count —— 含空行的全部行数(禁用 Get-Content / Measure-Object -Line / 数 0x0A 字节)。 权威来源:逐文件数值以机器生成的 行数豁免登记 为准(node scripts/line-limits.mjs --write 生成、--full 校验,不要手改其数字);本表只是人读摘要。 注:本表所列原语全部 ≤300 行,在登记表内属人工追加的记录块(--write 只重算 >300 行的登记区)⇒ 两份不一致时按上句口径在 HEAD 重新实测,再同步两份。

🔻 批 6 收口就地加注 · 这张 42 文件表的三副本同步**(R33.3 / R63.2 · T16 的二次刷新;2026-09-13,上表与上面 :260-262 的原文一字未改): 上面 countLines() 表与 ADR-033 §1 的表、行数豁免登记 的人工记录块是同一张 42 文件表的三份副本**;:211 的刷新规则要求三副本同步,而三份在批 6 收口时均已漂移 ⇒ 本节按 HEAD 提交树实测(countLines() 口径;探针 .superpowers/sdd/2026-09-12-frontend-redesign-batch6-motion/tmp/t35/primitives-42.txt,gitignored)逐值重列,三份副本一律按本加注读:

  • Text 119 / 66 / 171 · Surface 98 / 85 / 175 · Button 132 / 100 / 286 · Modal 260 / 81 / 296 + Modal.exit.test.tsx 91 · ConfirmDialog 202 / 58 / 297
  • Toast 257 / 76 / 249 + Toast.interrupt.test.tsx 167 + Toast.style.test.ts 134 · EmptyState 170 / 103 / 276 · Loading 105 / 79 / 238 · StatusLine 94 / 42 / 267
  • 共享内核 usePresence.ts / usePresence.test.tsx / usePresence.node.test.ts / useFocusTrap.ts / useFocusTrap.test.tsx / ime.ts / ime.test.ts = 200 / 282 / 71 / 122 / 236 / 18 / 32
  • 接缝 / 导出面 / 守卫:motion.css 335 · index.ts 48 · style-seams.test.ts 299 · style-contract.test.ts 296 · motion-coverage.test.ts 186
  • 与上表逐项漂移:Text.tsx 89→119 · Text.css 46→66 · Text.test.tsx 130→171 · Surface.tsx 97→98 · Surface.css 73→85 · Surface.test.tsx 168→175 · Button.css 92→100 · Modal.tsx 188→260 · ConfirmDialog.tsx 192→202 · Toast.tsx 210→257 · Toast.css 65→76 · Toast.style.test.ts 132→134 · EmptyState.tsx 127→170 · EmptyState.css 81→103 · motion.css 74→335 · index.ts 45→48 · style-seams.test.ts 271→299 · style-contract.test.ts 214→296 · motion-coverage.test.ts 147→186(其余 23 值与上表同)。
  • 42 个文件全部 ≤300 —— 唯一例外 = motion.css 335(.css 不在行数门禁的 SOURCE_EXT 视野内;源文件最大 = 299)。批 6 给它追加了 6 次(T5 / T6 / T12 / T13 / T19 / T29),已越过 R11.8 的 ≤300 自律线 ⇒ 逐条登记在 v0.22.md 批 6 节的「诚实代价」。
  • 🔴 口径缺口(登记,R33.3 第 2 条)::258 的机器锚点 <!-- line-count-src: … -->(本节与另两份各有一条)全仓 0 个读者 —— 规格 :835 / 豁免表 :211 所称「门禁按 files= 重测」今日无实现 ⇒ 三副本同步只能靠人工。锚点保留待将来实现(它刻意不写数值,故它自身不会腐化)。

5.1 收敛账本

原语 吸收 现状病灶
Modal 20 文件 role="dialog" 0 · createPortal 0 · 焦点陷阱 0 · 无退出机制
z-index 标尺 17 → 6 档 32 文件 45 处硬编码,值域 10…1150 两个不相交段
EmptyState 44 处 / 33 文件 5 套空态并存,首启路径无主行动按钮
Loading / Skeleton / 探针 85 处 / 30 文件 全站 0 骨架屏,长任务只有一行灰字
StatusLine 196 处 / 76 文件 三种红并存;错误常在列表最底部(视觉盲区)
Toast 4 套 → 1 现状嵌在导航行里,徽标一出现即被裁切
ConfirmDialog 21 处 → 1 window.confirm 在 WebView2 下可能静默返回 false
Button 107 处 / 63 文件 每面板重声明一份 btnStyle 常量
Surface 180 处 / 91 文件 1px solid #e5e7eb,radius 6/8/10/12 混用
Text 251 处 / 98 文件 字号与两个灰手写组合,对比度逐处失控

🔻 批 4 收口就地加注 · 五行各给「批 4 实测口径」(2026-09-12,上表原文一字未改)。口径:命令 + 读数 + 出处(HEAD = 199da54b);「计划数 → 实测数」一律并列,不许只换数字。

原语(上表行) 计划口径 批 4 实测口径(含机器判据)
Modal 20 文件 20/20 全部只经原语 —— dialogMigration.{a1,a2,b,e}.test.ts 逐文件断言「barrel 含 Modal ∧ 无深导入 ∧ 无自建遮罩 ∧ 无 keydown ∧ 无裸数字 z-index」;role="dialog" 的源码命中只允许出现在 ui/primitives/Modal.tsx(恰 1)与已登记例外(shell/CommandPalette.tsx 用 Modal)。构成 = A14 + B4 + E2 = 20(清单钉死在 dialogMigration.e.test.ts 的 DIALOG_20);另 14 个跨行命中不迁(11 锚定菜单 B1 + 3 覆盖层 B2,见 §11-2)。
z-index 标尺 17 → 6 档 裸值 58 处 / 43 文件 / 17 值 → 3 处 / 3 文件 / 3 值(10 / 999 / 1000,全部是 B2 例外且 ZINDEX_ACCOUNTS 逐条带 >20 字理由);冻结名单 58 → 3(ui/zIndex.guard.test.ts,7 用例,既有 4 条断言一字未改);六档逐字 = raised 10 · panel 100 · popover 200 · modal 300 · modalNested 400 · toast 500(ui/zIndex.ts 的 Z_TIER)。⚠️ 有意值收敛(可见观感变化,durable 登记):50/51/60/999/1000/1100/1150 → modal(300) · 30–61 → popover(200) · 900 → panel(100) —— 出处 task-4-report.md:208-212。
EmptyState 44 处 / 33 文件 B11 切片 = 28 文件(判据 ① 本批已触碰 ∪ ② pages/** ∪ ③ 有同名测试)⇒ 切片内非例外命中 = 0(emptyStateRatchet.test.ts ②);例外 8 条(逐条锚点 + 非空理由 + 尚未走原语);余量 5 文件冻结(只许减,转「冻结值恰等于实测值」);全仓 ≤44 棘轮 + 新增文件不得命中词表。切片外余量:见批 4 计划 §收口回写 §七(去向批 5/7)。
Loading / Skeleton / 探针 85 处 / 30 文件 棘轮域口径 = 可见加载文案 18 行 / 18 文件(裁:本仓「85 处」的读法不可复现 —— 计划写 19/19 是 includes("/ui/primitives/") 在 Windows 反斜杠下失效所致,见批 4 陷阱 #26);切片 14 文件 / 余量 4 文件;T14 迁移后收口到 8(FROZEN_LOADING_TEXT_TOTAL = 8 == RESIDUAL 8:4 余量 + 3 button-busy + 1 例外);MIGRATED_FILES 10 个文件 0 命中且必须含 <Loading/<Skeleton(⑤);冻结表 18 键被「迁移面(0) ∪ 残留面(1)」逐格钉死。
StatusLine 196 处 / 76 文件 迁移面 49 文件(MIGRATED 长度硬断言;原 55 减 6 个 B1/B2 回退文件 —— 那 6 个属 NON_MIGRATED_14,守卫禁止其 import 原语层,见 B21);三红字面量冻结 114 处 / 67 文件(剥注释口径;未剥注释是 116,其中 2 处来自注释:ClassroomBanners.tsx:13、utils/refineDiff.ts:13);逐文件下界 MIN_CALLS(8 个 >1 的键 + 默认 1)。
Toast 4 套 → 1 4 套 → 3 套收敛 + 1 套机器可判例外:① App.tsx AI toast(3500ms · belowNav)② SessionsPage(改 useTransientToast(3000),14 个调用点 diff 0 行)③ useTransientToast(状态机/API 保留,仅换渲染;档位由 popover(200) 归 toast(500))④ 面板级内联 banner 不迁(带 4 条理由 + 「仍无 fixed/role」+ 变异即红)。判据 toastMigration.test.tsx(15 例)+ 原语层 Toast.placement.test.tsx(6 例)。
ConfirmDialog 21 处 → 1 域内 confirm( 命中 24 处 = 8 处 window.confirm(全部迁移,落 6 个文件) + 16 处非 window.confirm(同名本地 helper 等,未迁、登记)⇒ 上表「21 处」与计划 Task 11 的「16 处」两个数都不成立(见「计划错处累计 ⑭」)。判据 confirmMigration.test.tsx(10 例,按 DOM 顺序逐段恰等,含总长守卫)。tier 走 B6 特殊条款(主判据 ≥3 不成立,实测消费者 0)⇒ 见 ADR-033 后果④ 的更正加注。
Button 107 处 / 63 文件 口径变化(三个实测口径并列):510 处 / 121 文件(原生 <button>)· 80 行 / 55 文件(const *Btn* 常量族)· 103 行 / 37 文件(style={xxxBtn} 消费者)。上表的 107/63 不可考(btnStyle 标识符全仓 0 命中)⇒ 批 4 走 B4:*Btn* 常量族 + 新代码 + 原生按钮棘轮。终态:原生 <button> 493 → 394 处 / 121 → 114 文件(Δ −99,逐文件 Δ 之和 = −99 对拍成立)· const *Btn* 79 → 56 行 / 55 → 44 文件 · FROZEN_NATIVE_BUTTON_TOTAL 510 → 394(且在 buttonMigration.test.ts ④ 补了 FROZEN == sum(entries) 与 ≤ PRE_T12 双判据)。余量 ≈407 处(原生按钮口径)→ 登记批 5/7。
Surface 180 处 / 91 文件 三族实测(域 = app/src/** 减测试减 ui/primitives/**,先剥注释):① 边框 1px solid #e5e7eb(含复合)240 处 / 111 文件 → 226 处 / 108 文件;② 越界圆角 6|12|14|999|2 270 → 261 处 / 112 → 109 文件;③ boxShadow: 24 处(值 11 种 → 6 种;20 处换 token,4 处有理由不改);④ FROZEN_SURFACE_TAG_TOTAL 0 → 14。「180 处卡片边框」高估了迁移面:今日 240 处按属性名分桶 = 整圈 border 172(真面) · 单向 57(分隔线,bordered 只出整圈 ⇒ 表达不了) · 三元条件边框色 11(interactive 给不了任意状态色);172 再分 = 控件 96 · NM14 14 · 锚定菜单 4 · 交互 4 · 条件底色 4 · 透明容器 28 · 有底色容器 22 ⇒ 够格 21、实迁 14。6→8 计划 254 处 ⇒ 实改 9 处(6 处随 <Surface radius="panel"> + 3 处就地改)—— 分区与去向逐条见批 4 计划 §收口回写 §八。
Text 251 处 / 98 文件 弱化灰 #9ca3af 249 = 迁移 186 + 例外 63(44 文件 / 7 类:interactive* 35 · ternary-no-equivalent 10 · nontext 6 · b1-non-migrated 9 · tag 1 · colorMap 2 · C 类 2),逐条理由进 textBaseline.RESIDUAL(未登记 0 · 僵尸豁免 0);字号越界 558 处 / 120 文件只冻结、一处未动(映射规则已写在 tmp/t16a/slice.md,批 5/6 执行);tone 一律 ink-3(5.13:1),ink-4 0 使用(§4.3 条件③无实现)。⚠️ 口径教训:T16-A 的行级归属把「同行 background: + color: 三元」记成 background ⇒ 计划的「24 处非弱化文本」作废,属性级重扫实为 6 处(color 241 · background 3 · border 2 · 常量/映射 3 = 249)。

本表的读法:上表的「现状病灶」是批 0-D 时点的侦察数(含 180 / 251 / 107 这类计划期估算);终态一律以本加注的实测口径为准,且任何一格都不得被读成「全量已清零」 —— 五类里只有切片内清零,余量各带去向(B11 的中间态,见 §11-3 加注)。

🔻 批 4 收口后更正(2026-09-12 · 收口评审 I-6 + 13 条 Minor + 收口修复单元 ed07d491 / 531eb93f 的 3 处数字变化;上表两行与本节其余加注的原文一字未改,此处只追加):

  • Button 行(上表第 8 行)的「终态 493 → 394 处 / 121 → 114 文件」是 f6dd012f 时点的读数,不是 HEAD(收口评审 M-11)。收口修复单元 531eb93f 把 nativeButtonBaseline 收紧到实测 ⇒ 终态 = 394 → 393 处 / 121 → 115 文件(FROZEN_NATIVE_BUTTON_TOTAL = 393;文件数 +1 来自 T13-b 拆件把 components/chat/ChatLaunchMenu.tsx 计入,SPLIT_MOVES 已登记守恒、守卫未失效)。独立复算(node .superpowers/sdd/2026-09-12-frontend-redesign-batch4-primitives/tmp/docfix/measure.mjs <tree>/app/src;域 = app/src/** 减 *.test.ts(x) 减 ui/primitives/** 减 ui/icons/**,剥注释后整段 /<button[\s>]/g):f6dd012f 导出树 394 处 / 114 文件 · HEAD 393 处 / 115 文件。
  • Text 行的弱化灰例外在收口后由 63 变 64:FROZEN_MUTED_GRAY_TOTAL = 63 是旧剥注释仪器的读数;ed07d491 修掉 sliceScan.stripComments 把 JSX 闭合标签 </x> 的 / 当正则起点的假阴后,受害行 components/NoteRowContextMenu.tsx:175 由「被抹掉」变「被数到」⇒ FROZEN_MUTED_GRAY_TOTAL = 64、逐文件格 components/NoteRowContextMenu.tsx 1 → 2(条目数仍 44、文件数仍 44)。物理恒等式 = 250 = 迁移 186 + 例外 64;249 = 186 + 63 是旧仪器口径下的恒等式。
    • 独立复算(同一探针,HEAD 工作树;三个剥注释实现并列):OLD(091d1c3d 的 sliceScan)63 行 / 44 文件 · NEW(HEAD 的 sliceScan)64 行 / 44 文件 · NAIVE(朴素 /* */ + //,不做正则启发式)64 行 / 44 文件;三口径唯一差异文件 = components/NoteRowContextMenu.tsx(OLD 1 / NEW 2 / NAIVE 2)。
  • 上表 Text 行的「7 类」逐类分解两种口径都不对(收口评审 I-6):原文写 interactive* 35 · ternary-no-equivalent 10 · nontext 6 · b1-non-migrated 9 · tag 1 · colorMap 2 · C 类 2 —— 自列之和 = 65 ≠ 63,且 colorMap 与「C 类」是同一类的重复计数(65 − 63 = 2 正来自这里)。真源 = textBaseline.RESIDUAL(44 条)+ FROZEN_MUTED_GRAY_BY_FILE(44 键)联立;我自测(node …/tmp/docfix/d2-kind.mjs):条目数 interactive 14 · interactive-no-equivalent 8 · ternary-no-equivalent 9 · b1-non-migrated 7 · nontext 4 · tag 1 · colorMap 1(Σ44)· 处数 interactive 19 · interactive-no-equivalent 11 · ternary-no-equivalent 11 · b1-non-migrated 14 · nontext 7 · tag 1 · colorMap 1(Σ64)。合并口径:interactive* = 22 条目 / 30 处(不是 35)。
  • 计划「296 是手抄噪声」这个机理不成立(收口评审 M-6):296 是朴素剥注释口径的真值,295 才是旧 sliceScan 的真值 —— 差额恰为 components/NoteRowContextMenu.tsx:175 那一处。独立复算(42e88740 导出树):OLD 295 行 / 105 文件 · NEW 296 行 / 105 文件 · NAIVE 296 行 / 105 文件。⇒ 那是口径差,不是手抄错;解释读数差异必须点明是哪两种口径(台账 §四 #32 的追加一例已入册)。

🔻 批 5 收口就地加注 · 错误行「位置」的处置(A6 = C11 = D11;2026-09-12,上面全部加注与本表原文一字未改)—— 批 5 只预留槽位,不重排任何既有 StatusLine 调用点;位置与像素判定转批 8****:

  • 槽位形态(两侧逐字一致):class="ed-view-error-slot" data-view-error-slot="",各恰 1 个,位于切换器之后、内容为空 —— 会话侧 app/src/components/session-detail/SessionViewHost.tsx:121(剥注释后 1 处;裸子串 2 处,第 32 行那处是头注释里的引文 ⇒ 裸/声明级两个读数并列)· 笔记侧 app/src/components/notes/NotesReadingColumn.tsx:268(自闭合空元素);切换器行号 103/108(会话)与 252/258(笔记)⇒ 槽位在切换器之后 ✅(顺序判据)。
  • 机器判据:app/src/views/architecture.guard.test.ts:250-258(A5③:剥注释 + 顺序 + 「恰 1 处」+ 类名钩子,2 passed)。反向对照(有牙):T15-M-A5(删掉会话侧槽位)⇒ failed 1 / passed 16,红 = A5③;T15-M-A5c(槽位整体上移到切换器之前,数量与属性逐字不变)⇒ failed 1 / passed 16,红 = A5③(顺序那一半);反向对照(必绿):T15-R-A5b(槽位加合法属性 role/aria-live,位置与数量不变)⇒ 0 failed / 17 passed ✅。
  • 既有调用点一处未动:本批没有把任何页面级 StatusLine 从列表底部搬到顶部(那会把页面级 status 拆成 5+ 个 state,状态面扩张且与三条硬约束无关)⇒ 「底部 vs 顶部」的可达性差异在 jsdom 里判不了(需真实视口 + 用户行为),实现与像素判定归批 8 的布局/像素 pass。
  • 出处:批 5 计划 §收口回写 §四(C11 行)· tmp/t18/measurements.md §2.3。

5.2 Modal 契约

  • 尺寸三档:S 380(确认类)· M 520(表单类)· L 720(向导/工作台);遮罩统一 rgba(26,26,26,.34),圆角 10。 落地(批 0-D):三档 = .ed-modal--s|m|l(ModalSize),层级两档 tier: "modal" | "modalNested"(300 / 400, 值一律从 zIndex() 标尺取),默认 m / modal。
  • 四个必补能力:① role="dialog" + aria-modal + aria-labelledby;② 焦点陷阱 + 打开聚焦首元素 + 关闭归还焦点;③ ESC / 点遮罩关闭(脏表单时先内联确认);④ 进出场接口(L4 动效的前置)。 落地(批 0-D):①②③④ 全部已实现(三条全仓 0 命中的契约 createPortal / role="dialog" / aria-modal 从 0 变成 1; 进出场 200/160,出场比进场快)。未做:body 滚动锁(现状 20 个弹层也没有,批 4 观察项)。
  • Enter 提交必须带 IME 组合守卫 —— 现状全库仅 1/9 处有,中文输入法回车会直接触发危险动作。 落地(批 0-D):判定出口 isImeComposing()(ime.ts)只建不接 —— 本批无表单;批 4/5 在每个「Enter 提交」处消费它。
  • ★ 两条批 0-D 实测新增的契约(不是设计推导,是踩出来的;批 4 必须遵守):
    1. Modal 是 Portal / 焦点陷阱 / ESC 栈的唯一持有者,消费者不得自建第二套。ConfirmDialog 已按此改写 (无 createPortal / 无 zIndex / 无文档级键盘监听 —— 有源码与 CSS 扫描断言钉住),28 个手写弹层迁移后同样不得 保留自己的遮罩与 window ESC 监听。ESC 的「最内层」判定必须用 React 树深度:实测朴素入栈序(effect 自底向上) 与 DOM 序(portal 插入序反转)都会把外层当成栈顶。
    2. 退场相位必须禁指针事件:[data-phase="exit"] ⇒ pointer-events: none。open=false 后弹层仍挂载 160ms (Modal.css),那段时间面板还在屏上 —— 点击是即时的而退场是异步的,删除 / 级联删除的误触面就在这里。 已落盘(T7 83710e27,遮罩与面板各一条;ConfirmDialog 侧另有两条文件内缓解,T8 ef240816)。 附带纪律:JS 侧兜底窗口与 CSS 侧过渡时长必须同值(Modal.tsx / Toast.tsx 的 EXIT_MS ↔ var(--ed-dur-overlay-out|toast-out, …) ↔ motion.css 定值,三方对拍守卫在 style-contract.test.ts)。

5.3 删除语义(两档分级)

档 动作 通道
低危可逆(单条笔记 / 单图 / 单引用) 立即生效 撤销 toast 10s —— 用保留的原行载荷重新插入,不改 schema
高危不可逆(组删除 / 体系级联 / 采集弃置 / 毕业结算) 确认框 ConfirmDialog,框内必须列明级联影响与保留项(例:「将删除 1 个组 · 3 条排序记录 · 笔记 7 篇保留」)
级联 — 一律不给撤销(只给确认),保持撤销栈边界清晰

5.4 拆件(行为等价,测试全绿,本批不加功能)

序 文件 行数 拆成
① pages/NotesPage.tsx 602 NotesToolbar · useNotesKeyboard · useNotesBatchActions
② components/NoteListView.tsx 609 NoteListToolbar · NoteListTree · useNoteListSelection · NoteListContextMenuHost
③ components/SessionDetailPanel.tsx 656 SessionDetailHeader(粘性头)· SessionTranscriptPane · SessionScreenCards · SessionSegmentsTimeline · useSessionDetailData
④ pages/ClassroomPage.tsx 724 ClassroomSourceColumn · ClassroomCapturePanel · ClassroomInputPanels · ClassroomBanners · useClassroomShortcuts

豁免表同步纠偏:~11 个 >300 行未登记(RefineWorkbench 471 · GoalDetail 371 · SessionsPage 352 · EnrichPanel 324 等)· 8 个登记值与实测不符 · 1 个可移除(AiConversationDock)· 统一「前端测试文件是否登记」的口径。


6. L2 · 壳层与列契约

6.1 壳层

  • 顶栏 8 项(📡 课堂 · 🗂 会话 · 📝 笔记 · ✅ 行动 · 🔄 复习 · 🧠 体系 · 🎯 目标 · 💬 AI 对话)+ 右上 ⌘K + 采集状态 + ⚙ 齿轮。
  • 溢出两级:≥1180 图标+文字(约 1070px,余量 ~200px);1024–1180 仅图标 + 悬浮名(约 720px)。
  • ⌘K 命令入口 用来替代现在手写的 9 个 focus* 参数跳转,数据源接 kb_search。

2026-09-12 批 3 回写(原文一律保留,只加注) ① 「约 1070px / 约 720px」两个估数的前提与实测(W20): · ≥1180 档:实测改前 natural = 1281.8 px(默认窗 1280 上溢出 16.8 px,A7 实测),比「约 1070px」高 ≈212 px。 逐元素拆解(T7 探针 parts):brand 135.53 + tabs 716.83 + 右侧簇 389.44(Ctrl+K 97.86 + 对话面板 110 + 徽标 73.58 + 设置 84 + gaps 24)+ padding 32 + gaps 8。 ⇒ 该估数只有在「右侧簇不带文字」的前提下成立 —— A7 裁决 R2 把右侧簇(⌘K / 对话面板 / 齿轮)在 <1400px 收成仅图标 + title、把 Tab 的 padding/gap 收紧后,实测 1027.94 px(含徽标;预测 1073.94,实现后更小),与「约 1070px」同一量级。 ⇒ R2 是把规格原本的设想补回来,不是偏离规格;1281.8 才是偏离。 · 1024 档:「约 720px」准确 —— 实测改前 717.11 px(Δ2.89);R2 后 691.11 px(含徽标)。 · 最坏徽标变体(「⏸ 暂停挂起(重连中)」= 145.58 px):1180 档 1099.94(余量 +65.06)· 1280 档余量 +165.06 ⇒ 三档 × toast 三态 × 徽标两变体 48 行全 OK(改前 21 行溢出)。 ② 「图标+文字」的适用对象(W18 / A2):本行「≥1180 图标+文字」指的是8 个域 Tab;右侧簇(⌘K / 对话面板 / 齿轮)在 max-width:1399px 内只有图标 + title —— 这是规格未写明的形态,由 A7 裁决 R2 落地,两级阈值分别住在 shell/breakpoints.ts 的 navFull = 1180 与 navActionsFull = 1400(单一真源,CSS 里写 1179px / 1399px 由守卫断言钉住)。 另:本行括号里的 emoji 是排版示意,不是规范态 —— 规范态是 §1 决策 5 的「自绘线性图标集」(A2 裁决:emoji 出局;它同时是测量毒物,见批 3 陷阱 #16)。 ③ 「9 个 focus* 参数跳转」实测是 10 个(D7):多出 focusChatId。批 3 的 ⌘K 覆盖了其中 2 类有载荷入口(检索命中 → 笔记高亮;新建体系)+ 页面命令覆盖全部 9 页目的地,10 个字段一个未删(收敛状态机归批 5);逐条映射表见批 3 计划 §收口回写。 ④ 采集状态的口径(A3):本行「+ 采集状态」= 顶栏右簇的采集徽标;AI toast 不在本行 —— 它已按 A3 移入 MainShell 最外层的 fixed 覆盖层(top: calc(var(--ed-nav-h) + 8px)),因为它是 1024 档溢出的唯一主因(单项 373.75 px = 视口 36.5%)。

6.2 列契约

ColumnSpec = { key, default, min, max, autoFoldBelow, pinnable },由单一注册表汇总,页面不再自建 hook。

页 列 默认/最小/最大 autoFoldBelow 本次改动
📡 课堂 源列 320 / 240 / 420 1100 阈值 860→1100;采集态整列隐藏
📡 课堂 右面板 flex — 统一 wrapper,消灭 640/全宽两档跳动
🗂 会话 列表列 320 / 240 / 420 1100 详情头改粘性
📝 笔记 组列 240 / 180 / 320 1024 并入注册表
📝 笔记 列表列 320 / 240 / 420 1024 工具栏三层合并为单行
📝 笔记 大纲列 180 / 140 / 260 1280 接线拖拽+记忆或删钩子(现为「假可调」)
✅ 行动 单列 页签内分区 — 不变
🔄 复习 单列 — — 复习态零 chrome
💬 AI 对话 侧栏 260 / 200 / 320 1100 接入列基础设施(现硬编码 240)
🧠 体系 体系列 260 / 200 / 360 1100 阈值 860→1100;折叠态持久化
🧠 体系 详情列 320 / 260 / 420 不折叠 34px 窄条收进统一列头
🎯 目标 左列 320 / 240 / 420 1100 接入列基础设施;默认 380 → 320
⚙ 设置 单列 居中 860 — 现 720 左对齐 → 改居中

阈值口径:按「折叠后正文是否仍 ≥520px」反推 —— 三列页 1024 · 两列页 1100 · 大纲列 1280。

记忆规则:记忆列宽与手动折叠态;不记忆自动折叠态、顶栏档位、相变态。

2026-09-12 批 3 回写(原文一律保留,只加注) ① ColumnSpec 实际是 7 个字段(W21):上面那句列了 6 个 —— { key, default, min, max, autoFoldBelow, pinnable };实现多一个 page: PageKey(app/src/shell/columnRegistry.ts)。加它的判据收益有两条:① columnsOf(page) 能按页取列(顶栏/页面接线不必另立清单)② 守卫能判「列不许挂在已删页上」(page ∈ 导航注册表键集)。⇒ 这是实现的加法,不是规格的反悔;本行原文保留,字段清单以这 7 个为准。 ② pinnable 是计划取的默认、无消费方:规格给了字段但未给逐行值 ⇒ 批 3 全行写 false,今天没有任何消费方(登记给批 6,与列折叠动效一起定)。 🔻 批 6 收口就地加注 · pinnable 的批 3 实际取值(R6.6;2026-09-13,上面原文一字未改):上面「批 3 全行写 false」与代码不符 ⇒ 代码是权威:app/src/shell/columnRegistry.ts 的 notes-outline 那一行逐字含 pinnable: true,且该文件的口径注释自称「除笔记大纲列…为 true」⇒ 批 3 的实际取值 = 除 notes-outline 外全 false(13 行逐行值:12 行 false / 1 行 true)。本规格两处(本②与 §10「批 3 的收口」行)都写「批 3 全行 false」⇒ 两处原文均按本加注读。消费方仍为 0(全 app/src 实测 15 处 pinnable 命中全在同一注册表文件内:1 处注释 + 1 处接口字段 + 13 行取值,无任何读取点)⇒ 本批(列折叠 Flip)只登记消费方缺口,不接线。 ③ 大纲列「接线拖拽+记忆或删钩子」的裁决(W22 / D6):走接线(不删钩子)。计划 Task 8 Step 4 逐字写「onToggleOutline 改为 outlineCol.expand()」——字面照做会把 ✕「收起大纲」也变成 expand(),凭空制造一个新缺陷;实现取 folded ? expand() : setManualFolded(true)(窄条 → 展开、✕ → 手动折叠),两个控件各自语义正确。旧实现的死局(J1-3:窄窗自动折叠下 folded ≡ true,点窄条永不展开)由此解开。 ④ 笔记列表列的阈值(D3):本表写 autoFoldBelow = 1024,而今日实测是 700(且三值并存:860×4 / 700×1 / 1100×1)——批 3 已按 §1 决策 16 与「折叠后正文仍 ≥520px」的口径改判为 BREAKPOINTS.threeCol = 1024,三值并存消灭;本行原文保留。 ⑤ 本表 13 行的落地状态:13 行全部进 shell/columnRegistry.ts(单一注册表);页面不再自建宽/夹取/阈值,但执行器仍是 useColumnLayout(A5 裁决:注册表持规格、hook 执行)。7 处旧调用点 + 4 处未接入点(ChatSidebar / GoalsPage / SettingsPage / ClassroomRightPane)全部改为从注册表取规格。 ⑥ 「本次改动」列里未做的两条:🗂 会话 · 详情头改粘性 与 📝 笔记 · 工具栏三层合并为单行 不属批 3(壳层与列契约),未做 —— 登记去向见批 3 计划 §收口回写「未做」表。 ⑦ 34px 窄条(🧠 体系 · 详情列)未吸收:KnowledgeDetailPanel 的折叠窄条 width: 34 仍在。理由:ColumnSpec 里没有「折叠窄条宽」这个概念,取 .min(=260) 会把 34 px 变成 260 px(可见回归);只加一个 collapsedWidth 字段则是扩写规格引用的契约形状去凑数(A1 明禁)⇒ 登记给批 4(与「统一列头 / ColumnBar 收敛」一起做才是真吸收)。

🔻 批 5 收口就地加注 · 本表「本次改动」列最后两条的处置(A5 = C13 / ADR-034 S-4;2026-09-12,上表与上面七条加注的原文一字未改):

  • 🗂 会话 · 详情头改粘性 → ✅ 批 5 已做:app/src/components/session-detail/SessionDetailHeader.tsx = 176 行(预算 ≤180),落地形态 = position: sticky + top: var(--ed-space-4)(= 4 px)+ zIndex("raised")(= 10,走 ui/zIndex.ts 标尺,不写裸值);补了 1 条行为级判据(该文件此前无同名测试):SessionDetailHeader.test.tsx 9 passed(改前 5 passed / 4 failed ⇒ 改后 9/9,删改的是它自己新增判据的中间态,既有断言零改动)。
  • ⚠️ 真实粘性不可验(诚实登记,不得读成「已验证」):jsdom 无布局引擎 ⇒ 只能给静态结构级判据(position: sticky ∧ top 来自 token)与祖先链核验;真实滚动下的粘性行为未验证,归属批 8 的真机/像素 pass(C13 逐字要求把这一点写进「未验证」)。
  • 📝 笔记 · 工具栏三层合并为单行 → 批 5 未做,转批 8(C13 逐字)。理由(C13):要动 NotesPage.tsx(拆件后 288/300)+ NoteListView.tsx(241)+ app/src/components/NoteListToolbar.tsx(95,事实更正:真身不是 NotesListToolbar.tsx)三个无测试面文件,「投入产出比最差」⇒ 与批 8 的布局/像素 pass 同批。本行原文保留,去向以本加注为准。
  • ADR-034 侧的同一条处置见该 ADR §登记 下 :115 的加注⑦ 与批 5 加注(本收口已就地补记「T18 的处置=登记为批 5 follow-up」的最终结果)。

🔻 批 6 收口就地加注 · pinnable 的消费方终态 + 本表与 §6.3 的内部矛盾(R5.7 · R40.1 · R55/R58 的路径 B 裁决;2026-09-13,上面原文与七条批 3 加注、批 5 加注一字未改):

  • pinnable 的消费方终态 = 仍为 0 处(本批未接线):全 app/src 实测命中仍全在同一注册表文件内(接口字段 + 13 行取值 + 口径注释),无任何读取点。批 6 的列折叠 Flip 走的是授权路径 B(data-flip-id 跨元素匹配、不动 DOM 结构),不消费 pinnable ⇒ 上面②「消费方仍为 0(只登记消费方缺口,不接线)」在批 6 依然成立,逐字登记为未接线。
  • 🔴 规格内部矛盾(R40.1,两处原文均保留、就地加注消歧):本表 📡 课堂 · 源列 行写「采集态整列隐藏」,而 §6.3 的相变态表写「源列保留(采集配置面)」—— 两句互斥。本规格不在此裁决可见性(模块与批 6 都未裁决):批 6 交付的是相位通道 + 顶栏相位 CSS + chrome 两态,列可见性未实现(app/src 内「采集态整列隐藏」0 处实现;shell/columnRegistry.ts 的注释已就地写「两个口径都不由这里兑现」)⇒ 两个口径按本加注并存,实现面以 §6.3 的「源列保留」为既成事实(采集配置面今天不隐藏),去向 = 批 7/8 或用户裁决。
  • 出处:task-34-report.md §10(生产调用点读数)· 批 6 rulings.md §四十(R40.1)· §五十五/§五十八(T33 的路径 B)。

6.3 相变两态

态 顶栏 域导航 列结构 顺带修掉的审计项
采集态 58px LIVE 仪表(波形 + 计时 + 暂停/标记/停止) 隐藏 源列保留(采集配置面) G3 · G7
复习态 零 chrome 隐藏 单列,卡片居中 640 B5
常态 A′ 顶栏 显示 按 6.2 契约 —

🔻 批 6 收口就地加注 · 上表的落地读数(R4.3–R4.5 · R42⑥ · R49.1④;2026-09-13,上表原文一字未改):

  • 通道:<html data-shell-phase>,取值逐字 "idle" | "capture" | "review";单一写入方终态成立 —— 全仓唯一 useShellPhase(...) 调用 = app/src/shell/phaseSource.ts:84(shellPhase.guard.test.ts 的名册钉住),载体 = App.tsx:394 的 <PhaseChrome phase={phase}>(1 处)。⚠️ readShellPhase 的 TS 读取仍 0 生产调用点(消费方是 CSS 的 3 条选择器)。
  • 采集态 58px LIVE 仪表 + 波形:--ed-nav-h-live: 58px 落 token 真源(T17,gen-tokens.mjs → tokens.css);链路闭合 1 / 1 / 1 —— <PhaseChrome>(App.tsx:394)→ shell/PhaseChrome.tsx:102 <LiveBar …> → shell/LiveBar.tsx:135 <Waveform …>(LiveBar 自身 0 处直挂,由 LiveBarHost 之内挂载;T18 结束时的「0 调用点」已由 T19 闭合)。
  • 复习态零 chrome 的实现路径 = 相位 CSS(motion.css 的相位块 + TopBar.css 的 html[data-shell-phase=…] 两态),不是列隐藏;相位块的块序是硬事实:相位块 → 档位块 → reduced-motion 块(实测行号 191 < 210 < 260)。
  • 🔴 navHeight.consumption.test.ts 零改动仍绿(T17 的自证:新增 --ed-nav-h-live 未破既有消费判据)。两态像素面(采集真的 58px / 复习真的零 chrome)jsdom 不可判、本批未测 ⇒ 只登记(task-34-report.md §12)。
  • 🔴 本表第 1 行「源列保留」与 §6.2 的「采集态整列隐藏」互斥 —— 消歧见 §6.2 的批 6 加注(本批未实现列隐藏,两个口径按加注并存)。

7. L3 · 视图层

7.1 四部件与依赖方向

部件 形态 解决的问题
ViewSpec { key, label, icon, appliesTo, Component, lazy } 把「这个视图能看什么对象」变成类型约束
viewRegistry 按对象类型查可用视图的单一注册表 现状展示形式硬编码在页面里,故一对象只能一种样子
ViewSwitcher 一个原语(分段控件),放正文列头部 体系域自建了 tabbar,别的域没有
依赖方向 领域 → 视图 → 容器 → 原语(禁止反向) 视图不得直接调 Tauri 命令,数据由容器注入

🔻 批 5 收口就地加注 · ViewSpec 的字段形态(C1;2026-09-12,上表原文一字未改)—— Component 静态字段与 lazy 布尔已被 load 取代,批 5 已按此实施:

  • 落地形态 = { key, label, icon, appliesTo, load? },其中 load?: () => Promise<{ default: ComponentType<P> }>;宿主用 React.lazy + Suspense(复用既有 ShellFallback)。⇒ 上表不是偏离:C1 的定性是「把 lazy 布尔升级为可执行加载器」—— 规格自己给了 lazy 字段,说明设计意图本就包含「非默认视图不静态加载」;而把 load 做成静态 Component(方案 a)会结构性把 5 个视图 + markdown 栈拉进首屏(shell/** 与 primitives barrel 都在首屏静态可达集内)⇒ 直接超 200 kB 预算。规格原文保留,字段清单以本加注为准。
  • 默认视图无 load(同步渲染 + 常驻):实测 session[0].hasLoad = false · note[0].hasLoad = false,非默认 5 个 spec 全部 hasLoad = true;views/registry.ts 顶部 4 条 import 逐字全是 import type ⇒ 0 个视图组件静态 import,且每个 load 体内恰 1 个 import((5/5)。
  • 落点:注册表 = app/src/views/registry.ts(109 行,预算 ≤120);ViewSwitcher 落在原语层 app/src/ui/primitives/(见 ADR-033 §1 的批 5 加注)。
  • 读数出处:批 5 计划 §收口回写 §一 · .superpowers/sdd/2026-09-12-frontend-redesign-batch5-view-layer/tmp/t18/measurements.md §1.1/§1.2(该目录 gitignored,不入库)。

7.2 对象 × 视图矩阵

对象 现有唯一形式 本批 登记不排期
🗂 会话 列表行 → 详情文档 三轨对齐 · 印样 · 原文 · 卡片流 —
📝 笔记 markdown 阅读 / 编辑 原文 · 卡片流 · 带证据三轨 大纲(回归评估)
🧠 体系 树/画布/图谱/概念/模型(已 5 种) 作为模板迁进视图层统一管理 —
🔄 复习 卡片 + 四档评分 — 印样式回顾 · 时间轴复习
✅ 行动 裁决队列列表 — 时间轴(按计划日)· 卡片流
🎯 目标 详情页 — 矩阵视图(目标 × 相位)

↳ 批 5 收口就地加注(C2 的条件项 + C17①;2026-09-12,上表原文一字未改)—— 「📝 笔记」行第三列的「带证据三轨」本批未交付;笔记侧交付 = 2 视图(原文 · 卡片流):

  • 依据 = C2 的条件判据(「先做只读数据可行性探针;存在 ⇒ 3 视图;不存在 ⇒ 2 视图 + 就地加注 §7.2 与 §11-6 + 登记批 7;不许做空壳视图」)+ T1 的只读探针(task-1-report.md · tmp/t1/c2-evidence-probe.md)+ T13 的独立复现(task-13-report.md §②):不存在逐段级「笔记 ↔ 证据」关联的读取路径 ⇒ NoteEvidenceTrackView 不做、不建空壳(实测全仓 NoteEvidenceTrack* = 0 命中)。本批笔记视图键终态 = ["raw","cardflow"](app/src/views/registry.ts:108)。
  • 三条独立证据(T13 复现读数;命令与原始输出见 task-13-report.md §②): ① app/src-tauri/src/**/*.rs 全树 571 文件,精确串 note_evidence|evidence_segment|note_segment = 0 命中(双侧自证:note_id → 400 · segment_id → 68 · zzz_no_such_symbol_zzz → 0 ⇒ 真 0 非假 0); ② 数据模型侧:db_migrations.rs 共 26 张实体表 + 1 张 FTS 虚表(kb_fts,:305;合计 27 处建表)与 26 条 ALTER TABLE … ADD COLUMN(24 个唯一列名)里无任何「笔记 × 证据」连接表或字段;notes 对会话只有整篇级 session_id(:408); ③ IPC 侧:唯一段级引用 artifact_blocks.refs_json.segment_id(artifact.rs:64)的维度是 session 而非 note,且 0 个 #[tauri::command] 返回 SessionArtifact/ArtifactBlock/BlockRefs(两种独立手法各 0;全仓 #[tauri::command] = 321 个)⇒ 前端读不到;前端 284 个 invoke 名里「同时含 note 与 segment|evidence|artifact|block」= 0。
  • 准确边界(本批接受 T1 的判定,并已在仓内逐条取证):笔记 markdown 的段首 in-band 锚点 [⏱ MM:SS]([[ts:ms]])(发射 concat.rs:122-127 · 契约 anchor_strip.rs:3-4 · 金测试 note_filter_golden_tests.rs:117-124 · 前端渲染器 utils/html.ts:32)不构成逐段级读取路径,三条理由各自独立成立: ① 是文本不是关联(它是 notes.content 这段 TEXT 里的字符——无外键、无约束、无索引,用户编辑即可删改); ② 是 ms 不是 id(证据本体是 session_segments.id / session_ocr_blocks.id;而 T13 的 E2 逐字要求 data-evidence-id 与关联 id 逐字一致,ms 在类型上无法满足); ③ 不保证存在(只在转写/结构/章节草稿的段首发射,且受 PurifyConfig.anchor_timestamps 开关控制——purify_config.rs:66,96 默认 true、金测试 :125-127 逐字钉住「关闭后无锚点」;BodySource::OcrDirect(图文会话)走 note_filter_ocr::rebuild_ocr_markdown(note_filter_render.rs:85,全仓 format_timestamp 调用点里 note_filter_ocr.rs = 0 处);BodySource::Web 是 #[allow(dead_code)] 预留变体、正文直取 web_session_pages.markdown(note_body_source.rs:24-27);手动笔记更不会有;且精修输入会主动剥离段落锚点(anchor_strip.rs:8-16,消费点 ai_refine_task.rs:155 · commands_ai_refine/workbench.rs:81 · commands_ai_refine/session.rs:57),只回挂章节锚点(ai_refine_protocol.rs:46))。 ⇒ 改判为「交付 3 视图」属改规格(E2 降级为 ms 最近邻 · E1/E3 的「派生段落数」改为「带锚点段落数」 · 并须新开「无锚点段落」的显式标记路径),不在执行单元权限内。
  • 去向 = 批 7(必须先写规格),逐条登记见 task-13-report.md §④(含 E2 降级口径、E1/E3 双分母、OcrDirect/Web/手动笔记/anchor_timestamps=false 四类无锚点 fixture、段落无稳定 id 的根因、以及若要用 artifact_blocks 必须新开 command ⇒ 触及规格 §3 红线 6「后端数据模型零改动」需另裁)。
  • R8 对拍(防「条件项被当成已交付」):本批不得出现「笔记 3 视图已交付」的表述;终态 = 2 视图,且 §11-6 的验收不因此变红(2 ≥ 2,见 §11-6 的批 5 加注)。

🔻 批 5 收口就地加注 · 会话侧矩阵的终态读数(A2;2026-09-12,上表原文一字未改):

  • 会话 = 5 个视图:FROZEN_VIEW_KEYS.session = ["raw","tritrack","proof","cardflow","preview"](app/src/views/registry.ts)—— 与上表「三轨对齐 · 印样 · 原文 · 卡片流」逐项对上,多出的 preview 是既有的笔记预览视图,按 §7.3② 一并进注册表并转为惰性(它原先住在页 chunk 里)。笔记 = 2 个视图:FROZEN_VIEW_KEYS.note = ["raw","cardflow"](第三列的条件项见上一段加注)。
  • 两个终态清单分别由守卫对拍:FROZEN_VIEW_KEYS 与 keysFor() 双向对拍(app/src/views/registry.test.ts 12 用例)· registry[0].key === "raw" 且默认视图无 load(app/src/views/architecture.guard.test.ts 17 用例 + architecture.slots.test.ts 2 用例,共 19 passed)。
  • 上表第 4 行「依赖方向」在本批的机器判据:views/** 8 个生产文件对 @tauri-apps 的裸子串 0 / 声明级 0(阳性对照 components/session-detail/SessionScreenCards.tsx 裸 1 / 声明级 1 ⇒ 仪器有牙);views/** 首屏静态可达 = 0(TS-API 口径 66 文件,工具口径 91 = 源 77 + CSS 14,双口径并列)。
  • 出处:批 5 计划 §收口回写 §一 · .superpowers/sdd/2026-09-12-frontend-redesign-batch5-view-layer/tmp/t18/measurements.md §1.2/§2.2(该目录 gitignored,不入库)。
  • 印样数据契约(proof 视图;规格未给 ⇒ 批 5 T8 定,登记在此;收口评审 Minor-4 落位):props = Pick<SessionViewSlot,"detail"|"imageUrl">(type-only、其余 8 槽一个不接 ⇒ 结构上不可能取数/交互;也不许自己 import convertFileSrc);DOM 锚点 = data-testid="session-proof-view"(根,空态分支也在)· 每张 data-proof-sheet={first_seen_ms} · figure[data-proof-figure](图注位恒在,有图/无图两条分支都渲染)· div[data-proof-body](正文区);不复用 SessionScreenCard(两形态只共用数据语义,代码真源是那张卡)。七条语义(F1–F7):F1 重叠谓词 = 半开区间 seg.start_ms < last && seg.end_ms > first(零宽屏 first === last 归一为点 [first, first+1);首尾相接不算重叠;与 useSessionDetailData 的「屏 → OCR 块」同源)· F2 跨屏段在两张印张上都出现(重叠归属而非切分归属;与任何屏都不重叠的段不出现在本视图 ——「原文不丢」由默认原文视图承载,见 §7.3①)· F3 段序 = start_ms 升序、并列时 id 升序(先复制再排序,不原地改 props 数组)· F4 <img> 仅在 image_ref !== null 且 imageUrl(ref) !== null 时渲染(且 imageUrl 只在 image_ref !== null 时被调用 —— 计划漏了「ref 非空但解析不出」这一支,否则渲染 src={null} = 碎图)· F5 两槽位锚点(台账口径的「两槽位锚点」= 上面的 figure[data-proof-figure] / div[data-proof-body];计划只锚了根与印张 ⇒ 没有这两个边界,P2「图注位保留」/P3「正文按区间归属」只能退化成「整篇 textContent 包含」的弱判据)· F6 序号 = screen_id ?? index + 1(与容器 SessionScreenCards 逐字同源)· F7 detail.screens.length === 0 ⇒ 走 EmptyState(testId="session-proof-empty",标题「本会话无画面要点」,说明指向原文视图;根 data-testid 仍在)。出处:task-8-report.md §2.1–§2.3(逐条理由 + props 表)· progress.md §二十(契约原文)—— 两者都在 .superpowers/**(gitignored、不入库)⇒ 本行即该契约在仓库内的持久记录。判据侧:views/session/SessionProofView.test.tsx 5 用例(P1–P5 各带变异体 M1–M8)+ 只读判据 P4 与 nativeButton.ratchet ①② 双红;SessionProofView.tsx 168 行。

7.3 三条硬约束

  1. 原文视图永远保留 —— 未标注内容(核心处理 γ 只为「值得长期调用」的类型建机制)、长文阅读、自由书写都需要文档形态。
  2. 惰性挂载 —— 默认视图常驻(保活滚动与编辑态);非默认视图惰性挂载 + 卸载,重挂载恢复 scrollTop。否则「9 页常驻 × 每对象 4 视图」会成为内存炸弹。
  3. 编辑态切视图先 flushSave —— RichEditorView 卸载会丢编辑器状态;落库失败则阻止切换并提示,绝不静默丢弃。

🔻 批 5 收口就地加注 · 三条硬约束的机器判据落点与终态读数(A3;2026-09-12,上面三条原文一字未改)。口径:每条判据给「文件 / 用例数 / exit」+ 反向对照的变异体出处(带任务号);三条合计 9 条判据全绿(出处:批 5 计划 §收口回写 §一 · tmp/t18/measurements.md §1)。

  • ① 原文视图永远保留 —— 判据四条:结构 1 registry.ts 的 SESSION_VIEWS[0].key === "raw" ∧ NOTE_VIEWS[0].key === "raw"(自写结构探针,exit 0)· 结构 2(G2) 默认视图不带 load 且非默认 5 个全带(app/src/views/registry.test.ts 12 用例,exit 0)· 行为 H1 会话「切到每一个非默认视图后原文节点仍在 DOM ∧ 挂载计数不减」(SessionViewHost.test.tsx 11/11 passed)· 行为 F5 笔记「切到 cardflow 后正文仍在 DOM,且原文子树节点同一性不变」(NotesReadingColumn.views.test.tsx 11/11 passed)。反向对照(有牙):T10-M1({resident} → {isDefault ? resident : null})⇒ 6 failed / 9 passed,红含 H1;T14-M5(默认视图改回互斥条件渲染 = 旧 SessionDetailPanel:200-206 形态)⇒ 1 failed / 10 passed,唯一红 = F5。
  • ② 非默认视图模块级惰性 —— 判据五条:A1(图级) app/src/views/** 首屏静态可达 = 0(工具口径与 TS-API 口径双列)· A2 生产运行时导入者恰 2(pages/NotesPage.tsx / pages/SessionsPage.tsx),views/** → registry 的生产侧 7 条边全是 import type(运行时边 0)· G7 每个 load 体内恰 1 个 import((5/5)且注册表顶部0 个视图组件静态 import(4 条 import 逐字全是 import type)· 行为 H2 切到 ⇒ 挂载 / 切走 ⇒ 不在 DOM(11/11)· 行为 H3 Suspense + ShellFallback 在位(11/11)· 行为 F6 笔记侧惰性 + 卸载 + 记忆恢复(11/11)。反向对照(有牙):T10-M2c(惰性视图粘滞常驻 ∧ 去掉默认视图门控)⇒ failed 1 / passed 14,仅 H2 红;T10-M3(删 <Suspense> 包裹)⇒ failed 5 / passed 10,红含 H3;T14-M6(非默认视图用 display:none 代替卸载)⇒ failed 1 / passed 10,唯一红 = F6。产物级:真实构建后懒 chunk 22 → 31,逐视图字面量恰命中 1 个懒 chunk(逐 chunk 具名台账见 §收口回写 §五/§八)。
  • ③ 编辑态切视图先 flushSave、失败则阻断 —— 判据五条:F1 reject ⇒ 值未变 + 目标未挂载 + role="alert" + 记忆未写 · F2 resolve ⇒ 切换发生 + 目标挂载 + 无错误行 · F3 非编辑态直通(editorRef.current === null)· F4 阻断全过程 0 个 toast 节点(并证明选择器有牙)· F9 既有断言原样绿(语义只在「切视图」这一条路径被修正)—— 四条 it 全在 app/src/components/notes/NotesReadingColumn.views.test.tsx(11/11 passed)。反向对照(有牙):T14-M1(把 catch 分支改成「保存失败也继续切换」= 本条逐字禁止形态)⇒ 1 failed / 10 passed,唯一红 = F1。显式登记未做:RichEditorView 内的 Ctrl+E / 完成按钮路径仍不阻断(今天就是 fire-and-forget,不是本批引入的回归)⇒ 批 8;flushSave 的返回类型未升级(NoteEditHandle)⇒ 批 8 接口卫生。提示形态:catch ⇒ 不切视图 + 保持编辑态 + 就近一行 StatusLine kind="error"(不用 toast);错误行宿主在非 NON_MIGRATED_14 的层(NoteReadingView.tsx / NotesReadingColumn.tsx),两条 B1/B2 守卫一字未改且绿。
  • ⚠️ 引用变异体时必须带任务号(ID 撞名):T10 的 M1 =「默认视图条件渲染」= 约束①的反向对照(6 条红);T14 的 M1 =「catch 分支继续切换」= 约束③的反向对照(唯一红 F1)。不是同一个变异体。
  • ⚠️ 两条「无专属变异体」的判据(只登记、不在收口提交里补测):F9 是文件头 @ai-context 注释而不是 it(它断的是跨文件的「既有断言未被动过」,单文件变异体无法表达)· A5①/A5②(architecture.guard.test.ts 的默认视图 display 三元)没有以自己为目标的变异记录 —— 这两条性质分别由 G2(registry.test.ts)与行为级 F5/H1 守着 ⇒ 不是覆盖漏洞,只是这条判据自己没有专属变异体。逐条四要素见 tmp/t18/measurements.md §9.1,去向 批 6/7。

7.4 笔记「带证据三轨」—— 批 7 规格章(🔴 本批只交规格,实现未做)

出处与状态(这一行是本章的定位,别跳读):docs/versions/v0.22.md:561 逐字「笔记「带证据三轨」→ 批 7(必须先写规格)」,判否依据 = 批 5 task-13-report.md §④(B7-1…B7-9)+ §②(探针读数)。🔴 本批只交规格,实现未做 —— 本章是批 8 的输入契约,不是交付证明;🔴 全仓 NoteEvidenceTrack* = 0 命中(本批独立复测:app/src/** 的 .ts/.tsx PROD 0 处 / 0 文件 · TEST 0 处 / 0 文件;仪器 .superpowers/sdd/2026-09-12-frontend-redesign-batch7-unwired/tmp/t20/probe-spec-readings.mjs,负控 zzz_no_such_symbol_zzz = 0)⇒ 不存在「第三种笔记视图已经交付」这回事,本章也不得被读成它。

  • 🔻 口径更正(2026-09-13 · 批 8 T3;上一行的实测域收窄,原文保留):上一行逐字写的「全仓」是字面假命题 —— 它包含本行自身(上一行既含「全仓」也含那个以 Track 结尾的符号名)⇒ 自我指涉(与 §11-7 在 :905-906 的更正同源、同形)。🔴 T3 于 7c1fcc41 实测(入库域 = git ls-files,1,615 文件;口径 = 行含子串,剥注释抹等长空白保偏移):app/src/**(生产 + 测试)= RAW 0 行 / 剥注释 0 行 / 0 文件 ✅ · **app/src/** 之外 = RAW 27 行 / 剥注释 27 行 / 4 文件,全部在 docs/**(按顶层目录 = docs:27 行 / docs:4 文件)—— 含上一行自身。
  • ⚠️ 口径注(勿与 §11-7 的读数混用):本注的「剥注释」只对 .rs / .ts / .tsx 生效(.md 无注释语义,原样比对)⇒ 本注域内两者恒等;§11-7 的 :905 那组读数(RAW 16 / 剥注释 6 / 3 文件,时点 53bd0759)口径与时点都不同 ⇒ 🔴 两组数不得互推、不得当趋势读(§3.5「数字必须带口径与时点」)。本注自身含该符号名恰 1 次 ⇒ 落注后全仓 RAW +1 行 / +0 文件(即 28 行 / 4 文件),app/src/** 仍 0 行 / 0 文件。
  • ⇒ 🔴 正确表述:「app/src/**(生产 + 测试)= 0 处 / 0 文件」(这才是「实现未做」的有效证据域);app/src 之外的 27 行全是文档与计划里的提及(其中含本章自身),那不是实现。上一行的「全仓」一律按本注收窄读(收窄后与 §11-7 的 :906 现口径一致)。阴性对照 = 本任务现造的随机串(入库域实测 0 命中;因它一旦入库即失效,故本注不写它,读数见批 8 task-3-report.md)。
  • 🔧 T3 自捉更正(2026-09-13,落注后复测;上面三条原文保留):① ⚠️ 上一条「口径注」末句的自指预测「本注自身含该符号名恰 1 次 ⇒ 落注后全仓 RAW +1 行(即 28 行 / 4 文件)」实测不成立 —— 上一条刻意用「以 Track 结尾的符号名」指代、并未复写该串 ⇒ 它不含该串,不产生自增。🔴 实测(同域 = git ls-files · 同口径 · 落注后):全仓仍 27 行 / 4 文件,app/src/** 仍 0 行 / 0 文件 ⇒ 以本实测为准;「+1 行 / 28 行」按本注作废(本条更正同样不复写该串 ⇒ 也不改变该计数域)。② 上一条「正确表述」里的「上一行的「全仓」」指代错位 —— 应为本章定位行 :542**(:544 是「口径注」、不含「全仓」)。
  • 📌 本节 T3 各注的行号锚口径(2026-09-13 · T3;P-36 的落地):上面三条注(含本条)里的行号锚一律是 7c1fcc41 时点的编号(即本注自身插入行之前的编号)—— 🔴 T3 的实测证据:7c1fcc41 时点它写 :905-906 / :565,而 4966d9ee 时点实测已漂到 :921-922 / :576(漂移量 = 本任务在它们上方插入的 16 / 11 行;本章定位行 :542 未变)。⇒ 🔴 跨时点引用一律按「锚名」读::905-906 ⇒ §11-7 的 T21 口径更正 · :565(见 §D 表下的 T3 加注)⇒ §D 标题 · :540 ⇒ 本章定位行。⚠️ 行号随时点漂移(P-36),数字只作时点读数、不作锚;本条声明本身也不再给「当前绝对值」。

§A 三条验收口径的改写(E1 / E2 / E3 —— 模型 / 视图层的全部判据面)

# 旧口径(今日规格原文) 🔴 改写成 为什么必须改(类型上不可能满足旧口径)
E1 「每段有 data-evidence-for」 「每段都有 data-evidence-for(含无锚点段)∧ 只有带锚点段才渲染证据列」 无锚点段落客观存在(§C 的四类)⇒ 旧口径把「无锚点」与「缺陷」混为一谈;新口径把无锚点本身变成一等状态(有锚点无证据列 = 缺陷;无锚点无证据列 = 正常)
E2 「data-evidence-id 与关联 id 逐字一致」 🔴 降级为 ms 最近邻,且必须显式给出三件事:① 容差(|锚点 ms − 候选 ms| ≤ 容差才算命中;容差值由实现批定,必须写进判据而非注释)② tie-break(多个候选同 ms 或同距离 ⇒ 取确定序:start_ms 升序 → 同值取 id 升序;不得依赖数组顺序)③ 失配的显式标记(容差内无候选 ⇒ 出显式「无证据」标记,不许静默取最近的一个) 段首锚点只带 ms([[ts:63000]]),而证据本体是 session_segments.id / session_ocr_blocks.id(artifact.rs:62-69)⇒ ms 在类型上无法与 id 逐字一致;旧口径是一条永远不可能满足的断言
E3 「派生段落数」当分母 🔴 两个分母并列、不许混用:分母甲 = 派生段落总数(渲染器实际产出的段落节点数)· 分母乙 = 带锚点段落数(其中含 [[ts:ms]] 锚点者)。⇒ 报告与判据必须分别给出两个比值(「有锚点的覆盖率」用分母乙;「全篇覆盖率」用分母甲),🔴 禁止只报一个数就称「覆盖率」 分母只有一个时,无锚点段落会被算成「未覆盖」⇒ 读数结构性偏低,而真实缺陷面(有锚点却无证据列)会被稀释

🔻 批 8 T3 就地加注 · 上表 E2 行的出处更正(2026-09-13;上表原文与各格一字未改):E2 行的「为什么必须改」列逐字用 artifact.rs:62-69 支撑「证据本体 = session_segments.id / session_ocr_blocks.id」。

  • 🔴 实测该处是 BlockRefs(app/src-tauri/src/artifact.rs:60-69::62 pub struct BlockRefs {,字段 :64 segment_id: Option<i64> · :66 ocr_block_id: Option<i64> · :68 frame_ms: Option<u64>)—— 它是产物块引用结构(ArtifactBlock.refs: BlockRefs,同文件 :127),维度是 session 而非 note,且 0 个 #[tauri::command] 返回该类型(0 IPC 出口;域 = 入库 app/src-tauri/**/*.rs,实测 BlockRefs 只出现在结构定义 / 字段 / 内部构造 / 测试四类位置)。
  • ⚠️ 照字面读会误判成「必须新开 command」(这正是本章 §D3 的由来)⇒ 🔴 实际不需要:证据 id 的定义处 = app/src/types/session.ts 的 SessionSegment.id(:22-23)与 SessionOcrBlock.id(:33-34),及表 session_segments(app/src-tauri/src/db_migrations.rs:45)/ session_ocr_blocks(同文件 :56);前端取数走既有 get_session_detail(app/src-tauri/src/commands_session.rs:137 → :164;:146 list_segments(id) · :147 list_ocr_blocks(id),:164 一并返回)⇒ 零新 IPC / 零 schema(控制方 §2 A1)。
  • ⇒ E2 行的技术结论不变(ms 在类型上无法与 id 逐字一致);变的只是出处 —— 支撑该结论的是 session.ts 的类型 + get_session_detail,不是 artifact.rs:62-69。
  • 🔻 批 8 T6 就地加注(2026-09-13;上一行原文保留)—— 「三轨」的实现口径(控制方 §2 A3):笔记段落轨(data-evidence-for 挂在其上)+ 转写段轨(session_segments.id)+ OCR 块轨(session_ocr_blocks.id)。🔴 规格原文未逐字给这三条 ⇒ 以本注为准;证据本体是 id、段落锚点只有 ms ⇒ E2 的「降级为 ms 最近邻」是类型上的必然,不是实现偷懒。

§B 无锚点四类 + 各一份 fixture(关闭开关的用例必须有)

# 类 出处(发射路径) fixture 必须断言什么
1 OcrDirect(图文会话) note_filter_render.rs:85(走 note_filter_ocr::rebuild_ocr_markdown;该文件里 format_timestamp 调用点 = 0 处) 段落全部无锚点 ⇒ 每段仍有 data-evidence-for、0 个证据列、0 个「无证据」告警(无锚点 ≠ 异常)
2 Web(网页正文) note_body_source.rs:24-27(BodySource::Web 是 #[allow(dead_code)] 预留变体,正文直取 web_session_pages.markdown) 同上;且这条 fixture 是「未接线变体」的唯一覆盖面(它今天没有生产发射路径)
3 手动笔记 无发射路径(用户手写/改写) 段落无锚点 ⇒ 同 1;另断言:用户编辑不得被自动补锚点(§4.4 的「绝不猜」同源纪律)
4 🔴 anchor_timestamps=false(关闭开关的用例必须有) purify_config.rs:66,96(默认 true);金测试 note_filter_golden_tests.rs:125-127 已逐字钉住「关闭后无锚点」 同一份输入在开关 true / false 两态下各跑一次:true ⇒ 有锚点段 > 0 且证据列只在那些段上;false ⇒ 锚点 0 个、证据列 0 个、data-evidence-for 仍逐段在。🔴 只测默认配置 = 假绿(会让实现退化成「只在默认配置下绿」)

§C 契约:精修前后两态(「精修后段落锚点丢失」是有意,不是 bug)

  • 事实:anchor_strip.rs:8-16 主动剥离段落锚点;消费点 = ai_refine_task.rs:155 · commands_ai_refine/workbench.rs:81 · commands_ai_refine/session.rs:57;剥离后只回挂章节锚点(ai_refine_protocol.rs:46)。
  • 🔴 写成契约(本节的交付物):精修产物的段落级锚点不保证存在;精修输入路径上「段落锚点消失」是设计行为(防止模型把 in-band 锚点当正文改写/编造)。⇒ 带证据三轨在精修后的段落上必须走 §A 的 E2 失配分支(显式「无证据」标记),不得把它报成缺陷或回滚精修。
  • 两态 fixture 清单:① 精修前(notes.content 原文,锚点在段首)② 精修后(章节锚点回挂、段落锚点 0)。两态各一份 fixture,同一套断言(E1 逐段锚 + E2 三件 + E3 双分母),差值必须逐条归因到本节契约。

§D 数据路径与红线(需要控制方另裁,不在执行单元权限内)

  1. 段落无稳定 id 的根因:notes.content 是扁平 TEXT(db_migrations.rs:18-27),段落身份由 noteCardModel 派生 ⇒ 派生序号会随编辑漂移(用户在上方插入一段 ⇒ 后面所有段落身份全变)。
  2. 要稳定配对的两条路:(a) 派生序号(零 schema 改动,但漂移可观测)· (b) 新增段级身份 ⇒ 🔴 触及规格 §3 红线 6「后端数据模型零改动」 ⇒ 必须控制方另裁,本批不开。
  3. 唯一段级引用:artifact_blocks.refs_json.segment_id(artifact.rs:64)—— 维度是 session 而非 note,且 0 个 #[tauri::command] 返回该类型(0 IPC 出口)⇒ 若要用必须新开 command,同 2 的红线裁决。🔴 不得在执行单元内顺手开。
  4. 🔴 不得新写第 3 支手写 markdown 解析器(与 §7 的 markdown 归一冲突);实现若落地,复用 utils/markdownLine.ts / NoteMarkdown 的既有渲染链。

🔻 批 8 T3 就地加注 · 本节 §D 的两条红线裁决(2026-09-13;§D 的标题与第 1–4 条的原文一字未改**)**:第 2 / 3 条逐字写的「必须控制方另裁」(标题 :565 同写「需要控制方另裁,不在执行单元权限内」)已由控制方裁决(控制方 §2 A2 逐字):

  • 🔴 走 (a) 派生序号 —— 即第 2 条的 (a):零 schema 改动;§D1 的漂移事实不变(漂移可观测,按其登记,不因此改设计)。
  • 🔴 明确不开 (b)「新增段级身份」(第 2 条 (b),仍触 §3 红线 6),也不开 §D3 的「新开 command」 —— 前端取数走既有 get_session_detail(出处与实测见 §A 表下的 T3 加注)⇒ 第 3 条末句「若要用必须新开 command」按本注不再成立(不开 command 就够)。
  • ⇒ 本节标题与第 2 / 3 条不再是「待裁」状态;下游实现单元不需要任何新增 IPC / schema。§E 逐条登记的「批 8 输入 ②(§D 的两条裁决)」按本注闭合。
  • 📌 锚口径(承接 §7.4 定位行下 T3 的同类声明;2026-09-13 · T3):本注里的「标题 :565」是 7c1fcc41 时点的编号 ⇒ 🔴 一律按锚名「§D 标题」读(本任务在其上方插入行后已漂移;绝对值见上引声明)。

§E 批 8 输入(逐条登记)+ 诚实边界

  • 批 8 输入:① 本章的实现(视图 + ms 最近邻纯函数 + 四类 fixture)② §D 的两条裁决(段级身份 / 新 command)③ E2 的容差取值(本章只定「必须有」与「三件形态」,不定数值)。
  • 诚实边界:本章没有任何实现,因此没有可跑的判据、没有覆盖率读数、没有观感结论;NoteEvidenceTrack* 的 0 命中是本章未实现的证据,不是「已交付但没测」。

8. L4 · 动效纲领「活的纸」

用户纲领原文:「我希望我的软件是充满动效的、充满创新设计、充满生命力的,而不是呆板、死板的。」 本节各项均以此为准绳。

8.1 四层纲领(取代「克制清单」)

层 时间尺度 解决什么 具体点
响应层 80–180ms 应用在听你说话 墨渍 hover(从指针位置扩散)· 按下微陷 · 焦点环落纸 · 勾选框落笔
环境层 2–6s 循环 应用在呼吸 探针摆动 · 采集脉冲 · 未确认段落墨度极缓慢起伏(幅度 2%) · 到期刻度微光
编排层 400–900ms 签名时刻 对齐 · 显影编排 · 相变凝固 · 时间码回跳 · 记忆浮现 · 图谱浮现
生长层 秒 → 天 熵减看得见 到期刻度生长 · 笔记树生长 · 知识图谱次第落位 · 一场课结构化过程中的收拢

8.2 引擎与分界规则

引擎:GSAP(core + Flip + ScrollTo + CustomEase)+ @gsap/react 的 useGSAP() 做清理(React 19 StrictMode 会双调用 effect,裸用 useEffect + gsap.to 会留下重复 tween)。GSAP 只进自己的 chunk 懒加载。

判据 用什么
单属性 · 无时序 · <200ms CSS transition(hover 底色、焦点环、按下 scale)
多元素 · 有时序 · 需中断/反向/seek GSAP timeline
「从 A 布局滑到 B 布局」 GSAP Flip
循环环境动效(骨架 / 探针 / 脉冲) CSS keyframes(不占 JS 主线程,reduced-motion 一条媒体查询即静态)

Flip 解锁了布局动画:先改布局 → 测量差异 → 用 transform 补回起点 → 动画到 0,全程只动 transform。因此列折叠、列表排序、视图重排、卡片提拔全部可做连续运动。

8.3 双基调(用户裁决丙)

面 基调 缓动 适用
有「读数」的界面 精密仪器 power3.inOut,匀速段更长,沿轴线、带刻度感 采集 · 复习 · 时间轴 · 到期刻度
有「文字」的界面 活的纸 power2.out / 自定义「洇开」曲线,带惯性沉降(不是回弹) 笔记 · 会话 · 体系

8.4 动效 token 与清单

--dur-micro 120ms(响应)· --dur-card 220ms(面板/视图/列折叠)· --dur-reveal 500ms(显影)· --dur-skeleton 1200ms · --dur-page 150ms · --ease cubic-bezier(0.2,0,0,1) · 位移上限 8px。

进出场:弹层 200/160(出场比进场快)· Toast 180/140 · 相变 chrome 用绝对定位交叉淡入,不 animate height · 列折叠内容先淡出 120ms 后宽度瞬跳(宽度本身不做 transition,除非走 Flip)。

usePresence(约 40–80 行 + 单测):只负责卸载时机(GSAP 不管这个);transitionend 监听 + 超时兜底(reduced-motion 下不会有 transitionend,缺兜底会「关不掉的弹层」);matchMedia 必须自带守卫(vitest 全局 node 环境、setup.ts 无 matchMedia 桩)。

8.5 强度三档(敢做满的前提)

档 行为
节能 只留响应层;编排层直接跳终态
标准(默认) 四层全开,环境层幅度减小
丰富 环境层幅度与频率提高,编排层加长、错开更明显

系统 prefers-reduced-motion 优先于档位,档位跟随系统初值。

8.6 6 个签名动效

# 名称 内容 只有这个产品才有的理由
1 对齐 转写/画面/笔记三轨从错位滑到对齐 双通道融合本体的可视化
2 显影编排 沿时间轴逐段显影,节奏 = 语速函数 「熵减」的字面可视化
3 相变凝固 波形收束成直线、琥珀退去、导航淡入;可反向 形态变化而非状态切换
4 刻度生长 到期刻度按真实间隔生长;答「忘了」则回缩 让 FSRS 调度可以被感觉
5 时间码回跳 播放头沿时间轨滑到目标位置,掠过几帧缩略 把「每句话可追溯」变成可见动作
6 记忆浮现 墨色洇开 + 字距极轻收敛(用 x 而非 letterSpacing)+ 剪报底纹左刷 + 评分按钮随后浮起 揭晓是有节奏的一件事

🔻 批 5 收口就地加注 · 第 1 行「对齐」的第三轨口径(A4 = C2 的连带硬要求;2026-09-12,上表原文一字未改)—— 第三轨 = OCR 文字,不是「笔记」:

  • 上表第 1 行逐字写「转写/画面/笔记三轨」,而 C2 裁决的会话三轨 = 转写 / 画面 / OCR 文字(segments / screens / ocr_blocks 全在 SessionDetail 里 ⇒ 零新数据面、不碰 Rust,符合规格 §3 红线 6)。理由:SessionDetail 不含笔记(实测),且笔记是产物、不是原料 —— 把「笔记轨」并进「会话三轨对齐」会混淆两个对象;「双通道融合」的产品价值在 OCR 口径下依然成立(双通道 = 语音 + 画面,OCR 是画面的文字面)。
  • ⇒ 批 6 的动效纲领按本加注后的口径执行(「对齐」动效的第三轨取 OCR);「笔记轨」未交付的原因与去向(= 笔记侧「带证据三轨」的 C2 条件项判否)见 §7.2 与 §11-6 的批 5 加注。
  • 批 5 只落「响应层接缝」,不装引擎:ViewSwitcher 落 --ed-dur-micro(120 ms)接缝,实测 ViewSwitcher.css 63 行(预算 ≤80)· ViewSwitcher.tsx 135 行(预算 ≤150)· 零行内 style;不装 GSAP、不写 @keyframes(B9 中间态声明:「只有接缝、没有纲领」—— 三档强度 / 双基调 / GSAP 在批 6)。

8.6.1 用户答复(2026-09-11):「我希望我在对其进行交互时,它是活的」

原文(用户,回答 §12 的留空问题):「我希望我在对其进行交互时,它是活的」。 控制方解读(若与你本意有偏差请直接推翻):这句把"活"的重心放在用户动作的那一刻,而不是"没人操作时它自己在动"。

由它推出的四条硬约束(批 0-D / 3 / 4 / 6 都要照此,不是新纲领、是 §8.1 四层的配重):

  1. 配重(2026-09-11 用户二次确认后定稿):两个主场,交互优先 ——

    • 第一优先 = 响应层(80–180ms)与"用户动作触发的编排层":交互的那一刻是"活"的主战场(见第 2 条)。
    • 第二优先 = 环境层(2–6s)保持"可感知的生命感":用户原话是"交互时最重要,但闲置时也要有生命感(环境层别退太多)"⇒ 不得把环境层降为纯背景、不得把它压到看不见;它按 §8.1 已定的项目与量级存在(探针摆动 · 采集脉冲 · 未确认段落墨度极缓慢起伏 2% · 到期刻度微光),并在 §8.5 的「丰富」档如实变丰富(幅度与频率提高),而不是只在响应层加料。
    • 两者的分界:环境层永远不抢注意力(幅度上限不动)、不阻塞交互、prefers-reduced-motion 下整体静态;响应层必须每次都给回执。
  2. 每个用户动作都要有即时回执:点击 / 拖拽 / 悬停 / 键入 / 勾选 / 展开折叠 / 切换视图 —— 每一类都必须有有质感的即时反馈("做完了"与"收到了"必须可区分)。这是验收口径,不是形容词:批 6 收口时逐类动作列出其响应层动效,缺一即不达标。

  3. 可中断、可反向是"活"的硬指标:交互触发的动效必须能被下一个输入打断(下一个动作接管当前动效,而不是排队等待)。⇒ 批 0-D 的原语必须预留动效接缝:Button(hover / active / focus-visible / disabled 四态 + 按下微陷)· Surface(hover 升起 / 边框墨度)· Text(墨度与字距的可动画钩子)· Modal/ConfirmDialog(进出场 presence 钩子 + 遮罩淡入,走 §8.4 的 200/160)· Toast(进出场 180/140 + 可打断)· Loading/Skeleton/Probe(骨架微光 / 探针的 CSS @keyframes 钩子 —— 只有这两个"循环环境动效"用 keyframes) · EmptyState(一次性入场钩子 .ed-empty-enter) · StatusLine(一次性浮现 transition,无循环动画)。否则批 6 只能回头改每一处调用点,而批 0 交付的正是"让后面每一批都能一次改对所有地方"的能力(§10)。

    分桶裁定(控制方 2026-09-11,回应 T12 评审 I-2):本节原先把 StatusLine 与 Loading 并列为「CSS keyframes 钩子」,与计划给它的实现(transition + 明确「无循环动画」)分叉。裁定:StatusLine 归 transition(无循环),keyframes 桶只留 Loading/Skeleton/Probe。 依据是本节第 1 条「环境层永远不抢注意力」——状态行是信息不是「呼吸物」,循环动效会让它在长列表里变成噪音;「闲置时也有生命感」由 Probe 承担。同裁定记入 ADR-033。

  4. "活"不得以无障碍为代价:prefers-reduced-motion 优先于强度档位(§8.5);键盘路径(Tab 顺序 / 焦点可见 / Esc 退出)与"活"冲突时,无障碍优先。

8.7 明确不做(3 条,材质判断而非设计原则,可随时推翻)

3D 翻转 / 弹性回弹 / 粒子 · 对用户输入的夸张响应 · 页面转场滑动。

🔻 批 6 收口就地加注 · §8 的实现面回填(§8.1–§8.7 逐小节;2026-09-13,本节所有原文与批 5 加注一字未改):

  • §8.1 四层纲领的逐层落点:响应层 = motion.css 的元素级全局回执 + 原语层四类回执(T12,零调用点改动)· 环境层 = motion.css 的环境层四件 + app/src/motion/env.ts(幅度真源,T13)· 编排层 = useGSAP() + 六个签名 hook(波 C)· 生长层 = 本批只做「到期刻度生长」(components/review/useScaleGrowth.ts),其余登记。

  • §8.2 引擎:gsap@3.15.0 + @gsap/react@2.1.2(本批唯一一次「零新增依赖」破例,R2.1);唯一入口 = app/src/motion/engine.ts,顶层 gsap.registerPlugin(useGSAP, Flip, ScrollToPlugin, CustomEase)(恰 4 个、顺序逐字);🔴 双实例陷阱(测试底座独有):@gsap/react@2.1.2 无 exports、main 指 CJS ⇒ Vitest external 后拿到另一份 gsap 运行时,不 registerPlugin 时 context.revert() 静默空转(实测:裸 useEffect = 2 tween、useGSAP() 也是 2 个;注册后 = 1 个、卸载后 transform === "")。✅ 真实产物上成立:vendor-gsap-BQLVn3Z6.js(105,584 B)在产物里、不在 firstScreen.chunks(首屏仍 3 chunk)。

  • §8.3 双基调:载体 data-tone(取值逐字 "instrument" | "paper")+ 两个缓动 token(gen-tokens.mjs 真源)+ CustomEase 具名 ease。⚠️ 量化事实:CSS 侧 cubic-bezier 只是近似 ⇒ maxDiff ≈ 0.409 @ x=0.40(本批接受该差,但接受 ≠ 可两路横跳:同一落点只许一条路径)。

  • §8.4 token 与清单:10 个变量已迁入 gen-tokens.mjs 单一真源(motion.css 的 --ed-* 定义恒 0,由 style-seams.test.ts 的回归封条钉住);「不 animate height」已判据化(T19 的 V1:相变 chrome 走绝对定位 + 交叉淡入)。

  • §8.5 三档:载体 data-motion("eco" | "standard" | "rich",默认 standard;持久化键 motion:intensity;初值跟随系统);源序 = 防御性钦定(真实承重机理 = reduced-motion 块逐条带 !important;🔴 不得给档位块加 !important);三档的机器面 = 档位映射 + DOM 属性 + 源序 + 时长参数,观感面进「只能登记」清单。

  • 🔴 §8.6 六个签名动效逐条兑现度(规格 §11-10 的四行判据「持有 / 中断 / 降级 / 三档」各有专属变异体;生产调用点读数出处 = task-34-report.md §10):

    # 名称 生产调用点 落点(逐字) 兑现度(未兑现的带原因)
    1 对齐 1 views/session/SessionTriTrackView.tsx:251 ✅ 可判(三轨共轴 + 同 ms 高亮 + 位移 ≤8px);真实数据上「同 ms」极少成立 ⇒ 共轴容差 = 规格未给数字 ⇒ 不发明,去向产品裁决
    2 显影编排 1 components/session-detail/SessionRawView.tsx:96 🟡 部分:编排可判;节奏 = charRate 字符率近似(不得称为「真实语速」—— 规格未定义语速函数);🔴 反向 / 重播 replay() 入口 = 0(状态机与判据齐备、无生产入口)
    3 相变凝固 1 shell/LiveBar.tsx:90 ✅ 可判(波形收束成直线 / 琥珀退去 / 导航淡入三件 + 可反向)
    4 刻度生长 1 components/review/ReviewSessionPanel.tsx:98 ✅ 可判(生长 / 回缩;回缩用 review_card 的精确 intervalDays)
    5 时间码回跳 1 views/session/TimeRail.tsx:140 🟡 部分:落位 + ≤8px 掠过已交付;长距离连续滑动未交付
    6 记忆浮现 1 components/review/ReviewSessionPanel.tsx:102 ✅ 可判(墨色洇开 + 字距用 x 而非 letterSpacing + 剪报底纹左刷 + 评分按钮浮起)
    (附) 列折叠 Flip 2 / 8 处 components/ChatSidebar.tsx:80 · pages/GoalsPage.tsx:42 🟡 走授权路径 B(data-flip-id 跨元素匹配、零既有断言改动);其余 6 处未接线;🔴 Flip 会写 width/height(layout 属性)⇒ 与 §11-10 的属性集合判据天然冲突,如实登记(不为让判据变绿而放宽 R8.4)
  • 🔴 [[ts:ms]] 端到端只通一半:NoteMarkdown 的芯片已带上毫秒(T26 修掉「芯片是死的」这一真实缺陷),但会话页那一跳未交付 ⇒ 登记为批 7 首要候选(task-26-report.md 的逐行成本与 patch 落点保留)。

  • §8.7 三条不做:未触碰(bounce / elastic / 粒子 / 页面转场滑动 = 0 处)。

  • 🔴 像素面全部未测:本节的观感面(真的直线 / 真的 0.72 / 真的 220ms 淡入 / 真的 58px)jsdom 不可判 ⇒ 只登记,见 v0.22.md 批 6 节的「诚实代价」。


9. L5 · 47 条未接线命令最终处置表

# 命令 处置 说明
1 ai_clear_key 删 旧单 provider 密钥链
2 ai_enhance_mock 删 补缝三连(宿主 ArtifactView 已下线)
3 ai_enhance_status 删 返回常量;自包含零外溢
4 ai_goal_plan_estimate 补 UI · 登记不排期 目标规划成本预估
5 ai_save_key 删 旧单 provider
6 ai_test_connection 删 旧单 provider
7 artifact_to_note 删 产物视图 v0.11.5 已删
8 build_draft 删 旧草稿链
9 build_session_artifact 删 产物视图
10 export_manual_fill_done 补 UI · 登记不排期 周回顾手动回填
11 export_write_todotxt_file 补 UI · 登记不排期 迁出到 .todo.txt
12 get_note_group 删 已被 list_note_groups 全量 + 前端 map 取代
13 get_session_artifact 删 产物视图
14 kb_search 补 UI(本批) ⌘K 的数据源(ADR-029 RAG 层)
15 learning_metrics 补 UI · 登记不排期 学习指标面板
16 list_group_fragments 删 被 list_fragments 全量 + 收件箱取代;DB 层保留(flashcards/settlement 仍在用)
17 quiz_group_cards 补 UI · 登记不排期 组级自测;selfTestPassedRate 恒 null 的根因
18 recognize_image 删 旧直调
19 reset_tag_color 补 UI(本批) 标签线;与 set_tag_color 成对处理,不可只删 reset
20 save_draft_as_note 删 旧草稿链
21 scan_ai_candidates 删 补缝三连
22 session_outline 补 UI · 登记不排期 与笔记大纲回归一起评估
23 structure_models_dir_cmd 删 —
24 transcribe_audio 删 旧直调
25 update_note_tags 补 UI(本批) 标签可写;全仓现无任何写 notes.tags 的生产路径
26 vad_threshold_diag 删 开发诊断用;未来若需要,届时按诊断需求重新引入,不留半成品
27 video_profile_by_kind 删 前端已有等价的 profiles.find(...)
28 video_profile_for_spec 留(本批接线) 档位通道的读端
29 video_profile_memory 有意保留 自述「诊断用」,零成本
30 video_profile_spec_by_kind 删 前端已有 KIND_TO_FORM / KIND_TO_TIER 双写
31 action_badge_count 有意保留 文档明确「保留命令无前端调用方」
32 add_session_ocr_block 撤下 IPC,保留内部函数 低级写原语,暴露只会造脏数据
33 add_session_segment 撤下 IPC,保留内部函数 同上;「手动补录」应另立新命令(带来源标记与校验)
34 analyze_session_command 补 UI(本批) 会话详情「重新分析」
35 audit_due_for_system 补 UI · 登记不排期 REQ-212 审计报告视图
36 create_session 撤下 IPC,保留内部函数 手动建空会话无意义
37 delete_session_images_all 补 UI(本批) 整场图集批量删(现只能单张)
38 detect_video_domain 删 上游 OCR 标签通道从未建起来;平台信号不是瓶颈(infer_platform 已从标题/URL 自行补标签)
39 finish_session 补 UI(本批) 恢复动作「结束会话」(崩溃后卡在录制态的收尾通道)
40 get_decision 补 UI(本批) 决策日志单条详情 —— 顺带修审计 H7
41 open_capture_float 删 2026-09-11 结清:前端 0 调用者;开路径由 float_toggle(useClassroomFloat.ts:38)与 FloatAction::Open => float_open_core(commands_window.rs:250)承载,本命令只是又包一层 float_open_core ⇒ 批 1 Task 8 删
42 refine_session 补 UI(本批) 会话详情「手动精修」(UI 现只调 auto 版)
43 release_live_prepare 有意保留 前端注释「保留供未来显式调用」
44 remember_video_profile 删 功能被 remember_video_profile_form 完整覆盖,双重死亡
45 set_tag_color 补 UI(本批) 标签线
46 update_fragment_group 补 UI(本批) REQ-201 声称已接线但实际无调用方 —— 需同步修正记录
47 update_knowledge_system 补 UI(本批) 体系改名 / 核心问题 / 状态 —— 最明确的功能缺口

🔻 状态更正(2026-09-13)· 指上表 #14 kb_search 行:本行的「补 UI(本批)」已在批 3 Task 12 交付(kbCommands.ts:116 → useKbPaletteSearch.ts:61 → CommandPalette.tsx:127,提交 b567f0e6,带 9 条用例)。原文保留(本行是规格原文,批 8 对账须能看见「规格写 12、实做 10」的差额来源)。🔴 T21 实测复核:kb_search 的前端生产调用点 = 1(app/src/shell/kbCommands.ts,五形态并列探针)⇒ 本行的「已交付」为实测事实。

🔻 角色变更登记(2026-09-13,批 7 T19)· 指上表 #30 video_profile_spec_by_kind 行:本行当年删除 video_profile_spec_by_kind 的理由是「前端已有 KIND_TO_FORM / KIND_TO_TIER 双写」。批 7 把读端接到后端 video_profile_for_spec(#28)之后,那张前端映射的角色从「并行真源」变成「离线降级路径」(兜底) —— 它仍然存在、也仍有生产调用点(AGENTS.md §3.4 要求本地兜底路径必须留),不是被删除。⇒ 本行的删除理由在"真源"这一层不再成立,在"兜底"这一层仍成立;原文保留,本注为准。🔴 实测:video_profile_for_spec 前端生产调用点 4 处 / 1 文件(components/ProfileDetector.tsx);KIND_TO_FORM / KIND_TO_TIER 仍在且仍有生产调用点(R-11 剥注释实测 6 处 / 2 处;T21 未剥注释粗测 7 / 3,差额为注释行)⇒ 两张映射一个字未删。

🔻 状态更正(2026-09-13)· 指上表 #46 update_fragment_group 行:本行的「需同步修正记录」已在批 1 完成(docs/product/requirements-pool.md:337 含 2026-09-12 批 1 更正)。批 7 只做"接线"那一半(命令本身的生产调用点)。🔴 但 T21 实测推翻了后半句的兑现:update_fragment_group 在 app/src/**(含测试)命中 0 文件 / 0 处,而它仍在 registry(定义 311 / 注册 311 / 重复 0 含该条)⇒ 「接线」那一半在本批未做。处置:与 §C9.1 的裁决不冲突(该行被排除出 10 条实做名单的依据是「记录同步已做」,不是「接线已做」)⇒ 逐字登记为批 8 输入,不得读成「本批 12 条补 UI 已全部接线」。

🔻 收口更正(2026-09-13 · T21 追加轮;上一注的实测发现保留**、处置作废):上一注把「接线」那一半登记为「→ 批 8 输入」—— 🔴 该处置作废。依据 rulings.md §C56.2 逐字:「这是批 7 的交付缺口,不是「批 8 输入」……不得让一次漏做在事后读成一次有意的延期」**。理由:本行处置栏逐字是「补 UI(本批)」,且计划的指派三处一致 —— :532 A-2「不做记录;命令本身接线」→ 本批 · :2299 C11 给了落点与判据 · :2309 Step 3 逐字「C 组(C5–C11 共 7 条)—— 逐条单独提交」。

  • ✅ 已在批 7 内由 T23 补做并交付(3 条提交):125eec85 feat(components): 片段行补上移动到组入口(4 路径,含新件 app/src/components/FragmentGroupAction.tsx)· 5f662811 test(components): 把源码探针标题同步到七条命令 · 5d59e332 fix(components): 碎片归组候选改用 feed 地形组(地形返工:候选集由 container 改为 feed)。
  • ✅ T21 于追加轮独立复测(口径 = node fs 全仓扫描;正控 invoke( 68 个生产文件 · 负控 0):update_fragment_group 前端命中 = PROD app/src/components/FragmentGroupAction.tsx(4 处;:92 逐字 invoke("update_fragment_group", { fragmentId, groupId: target })) + 测试侧 FeedFragmentList.test.tsx(9) · batch7UiWiring.test.tsx(2) ⇒ PROD 0 → 1(≥1)。
  • ✅ 落点形态:FragmentGroupAction.tsx:71 invoke<NoteGroup[]>("list_note_groups", { terrain: "feed" }) + :72 .filter((g) => g.terrain === "feed")(后端真源 + 客户端第二道)· 载荷键名 { fragmentId, groupId }(Tauri 2 camelCase;Rust 真身 app/src-tauri/src/commands_fragments.rs:141-145 = update_fragment_group(state, fragment_id: i64, group_id: Option<i64>))· groupId: null = 移出组(与 Option<i64> 的 None 逐字对齐)。
  • ✅ 判据(T23 自报 + 控制方 §C57.5 逐项复核):V7 正控(feed 组入候选 + 查询载荷逐字 { terrain: "feed" })+ 🔴 负控有效(容器组不入候选)· V8 父层 ≤300 · V9 两株("feed"→"container" 红在 FeedFragmentList.test.tsx:225:33;删客户端收口红在 V7 负控 :227:82)· V1–V6 重跑通过 · CONTROL 同树 29/29 绿。
  • 🔴 缺口成因(逐字登记,供 T22 的二分清单收录):计划口径正确且甚至预警过 —— :2304 自陈「数一数是 11 个名字」、:2305 裁决「按名字列全 11 个,不删任何一个……不自行删减」;而 T20 报告的计数口径「6 + T18/T19 的 4 = 10」恰好吞掉了被删减的那一个名字 ⇒ 缺口成因 = 实做方的计数口径,不是计划缺陷。

汇总:删 22 · 本批补 UI 12 · 登记不排期 7 · 撤下 IPC 3 · 有意保留 3 · 待核实 0 = 47。

2026-09-11 改判:#41 open_capture_float 由「待核实」改为「删」(依据见该行说明)⇒ 删除批由 21 条升至 22 条。

两条更正,可单点回退:#38 detect_video_domain 与 #44 remember_video_profile 在早期分桶中被列入「A 桶 · 补 UI」,经专项侦察后更正为删除 —— 前者的上游 OCR 标签通道从未建起来(平台信号不是瓶颈,infer_platform 已从标题/URL 自行补标签),后者的功能被 remember_video_profile_form 完整覆盖。这两行独立于其他决策,若需回退为「补 UI」,只改这两行即可。

删除的通用影响面:仅 lib.rs 的 generate_handler!;capabilities/*.json 无自定义命令 ACL;无 bench/集成测试目录;单测全为 #[cfg(test)] 内联且不引用这些命令。唯一例外是补缝三连的模块级连带死代码。

🔻 就地更正(2026-09-13):注册清单已搬到 app/src-tauri/src/app_commands.rs(批 0-C3;check-command-registry.mjs:22 的 DEFAULT_REGISTRY),lib.rs 里已无 generate_handler!(line-limit-exemptions.md:26 自证)。本行的"仅 lib.rs"按 app_commands.rs 读;原文保留。🔴 T21 实测复核:app_commands.rs 现含 311 条 generate_handler! 条目(定义 311 / 注册 311 / 重复 0)—— 即本行「删除的通用影响面 = 注册清单一处」这一判断仍然成立,只是那"一处"换了文件(批 1 删 22 条、批 6 增 1 条、批 7 撤 3 增 1 条,全部落在这一个文件里)。


10. 批次划分

批 内容 验收
0 基座 4 条 ADR + 修订 ADR-010 · 豁免表纠偏 · 拆 4 个超限文件 · token 层 · z-index 标尺 · 图标集 + 单测 · 原语三层 4 个 >600 行 → 0;豁免表 100% 一致
↳ 批 0 的 ADR 归属(批 0-D 收口时更新) ADR-032 ✅(批 0-A/0-D) · ADR-033 ✅(批 0-D,L1 原语层与视图层契约) · ADR-034/035 顺延(推定为批 3 壳层 / 批 6 动效,见 ADR-033 的「登记」节)· 修订 ADR-010 属批 1 ⚠️ 批 3 收口更正(2026-09-12):本行「推定为批 3」未兑现 —— ADR-034 在批 3 未写,实盘最高仍是 ADR-033(已列目录复核)⇒ 见 §14 该行的登记与建议归属 —
1 删除批 22 条命令 + 补缝三连连带模块 + ADR-010 修订 + 清理 structuredBlocks.ts:63 死文案 ✅ 已落(2026-09-11 立案 / 2026-09-12 落地) 注册表 334 → 312(−22);cargo test 2300 / 0 / 6 全绿;ADR-010 已废弃;structuredBlocks:63 死文案已清
2 包体治理(可并行) manualChunks + 按页动态 import + 量首屏 gzip ✅ 已落(2026-09-12 收口,e46e0e82..9921767c,11 个提交) 从 651KB gzip 降到达标或给出瓶颈清单 ⇒ ✅ 达标:首屏 654.72 → 92.79 kB(−85.83%),守卫 exit 0、余量 107.21 kB(原「651KB」为过期快照,实测 654.72)
↳ 批 2 的收口(批 2 完成时更新) 口径注:manualChunks 本身一个字节都不降首屏(只把同一批字节切成多文件,入口仍静态依赖全部 chunk);它的价值是 ① 让批 6 的 GSAP 落进独立懒 chunk ② 给 import() 产生的懒 chunk 稳定的共享 vendor 边界。首屏下降全部来自 import()(Task 6 页级 + Task 7 窗口变体 + Task 8 对话面板,三条缺一不可)。交付形态 = 8 页懒 + 课堂页静态(懒课堂页会把「首屏即被抓取的 20.04 kB」记作 lazy,低估首屏读数 20.04 kB)。终态 25 chunk = 首屏 3 + 懒 22;两窗变体首屏 97.29 / 94.19 kB(计划原推理「入口 + 零 vendor」为假,实测 = 入口 + 4 个 vendor chunk;「变体 < 60 kB」结构性不可达 —— vendor-react 单独即 60,367 B,且三窗共用同一份 index.html、静态闭包逐字节相同) 收口六门禁(2026-09-12 实测):line-limits --full 0 / 123 / 123 · docs-check exit 0 · registry 312/312/0 · tsc 0 错 · vitest 125 文件 / 1233 用例 · cargo test 2300 / 0 / 6 · clippy 19(集合 identical) · 首屏预算守卫 exit 0 · 92.79 kB。包体瓶颈清单见 批 2 计划 §收口回写
3 壳层落地 A′ 顶栏 + ⌘K + 溢出策略 + 窗口尺寸 + 列注册表 + 断点 + --nav-h ✅ 已落(2026-09-12 收口,85d51d83^.. 至收口提交,22 个提交) 9 页全走注册表 ✅;1024 无溢出 ✅(限定词:常态;toast 已按 A3 移出导航行);7 处魔数归零 **✅(判据重述:已吸收 8 · 有目标未吸收 1 · 无目标 2,见下注 —— 不压回「7」)
↳ 批 3 的收口(批 3 完成时更新) 改动面 8 个文件族:shell/{navRegistry,columnRegistry,breakpoints,windowSize,TopBar,CommandPalette,ShellFallback,kbCommands,useKbPaletteSearch} + App.tsx + 4 处页面/组件接线(ChatSidebar / GoalsPage / SettingsPage / ClassroomRightPane / NoteReadingView / NotesReadingColumn);--nav-h 的落点是 token 生成器(app/scripts/gen-tokens.mjs 单一真源 → ui/tokens.css 产物,不是手改产物);列契约的执行器归属(A5 裁决):注册表持有规格、useColumnLayout 仍是执行器(选项 (b)「注册表自实现」登记为日后可能的合并方向,且不得声称能力对等);pinnable 字段是本计划取的默认(规格 §6.2 给了字段但未给逐行值,批 3 全行 false,无消费方);本批四个非目标:① 不重做批 2 包体工作(守住)② 不迁 L1 原语(守住,三条 primitives=false 断言)③ 不建视图层(守住)④ 不装 GSAP / 不加动效(守住,CSS 静态属性断言) 三条验收的机器判据与原始读数:批 3 计划 §收口回写 · tmp/acceptance.md(不入库)

🔻 批 6 收口就地加注 · pinnable 的批 3 实际取值(R6.6;2026-09-13,上格原文一字未改):上格「批 3 全行 false,无消费方」的取值半句与代码不符 ⇒ 代码是权威:app/src/shell/columnRegistry.ts 的 notes-outline 那一行逐字含 pinnable: true,且该文件的口径注释自称「除笔记大纲列…为 true」⇒ 批 3 的实际取值 = 除 notes-outline 外全 false(13 行逐行值:12 行 false / 1 行 true)。本规格两处(本行与 §6.2 的批 3 回写②)都写「批 3 全行 false」⇒ 两处原文均按本加注读;「无消费方」半句成立(全 app/src 实测 15 处 pinnable 命中全在同一注册表文件内,无任何读取点)⇒ 本批(列折叠 Flip)只登记消费方缺口,不接线。

🔻 收口评审 M-2 就地加注(2026-09-12,上格原文保留)——「8 个文件族 / 4 处接线」两个数字不可追溯,实测口径如下:

  • 改动面实测 = 31 个生产文件(命令:git diff --name-only 85d51d83^..42e88740 -- app/src app/src-tauri app/index.html app/scripts app/package.json,再剔除 *.test.* / test.mjs):app/src/** 28(shell/* 11 · ui/* 2 · components/* 5 · pages/* 9 · App.tsx 1)+ app/index.html + app/scripts/gen-tokens.mjs + app/src-tauri/tauri.conf.json。⇒ 上格「8 个文件族」是计划期陈旧估算(照抄批 3 计划 Task 3 Step 5 的「写清 8 个文件的改动面」),既非实测、也与它自己列的 shell/{…} 清单(9 个名字)不符。
  • 接线实测 = 6 处页面/组件(上格写「4 处」却列了 6 个名字 ⇒ 同句内部矛盾):ChatSidebar / GoalsPage / SettingsPage / ClassroomRightPane / NoteReadingView / NotesReadingColumn。
  • 另有 18 个新测试文件(app/src/shell/* 14 + components|pages 4;git diff --name-only --diff-filter=A … 实测),不含在上面的「生产文件」计数里。
  • 判据按实测口径读(本仓口径:按实测/判据重述,不迁就数字);上格其余内容(--nav-h 落点、A5 裁决、pinnable 默认、四条非目标)不受影响。 | 4 原语迁移 | 20 弹层 / 44 空态 / 85 加载 / 196 错误行 / 4 toast / 2 confirm / Button/Surface/Text | z-index ≤6;role="dialog" 20/20 | | ↳ 批 4 的收口(批 4 完成时更新 · 2026-09-12) | ✅ 已落(4905d4d8^..<收口提交>,49 个提交 = 48 个实施/评审/修复 + 本节所在的收口提交;含左端点口径见 v0.22 批 4 节);上格原文一字未改。两条验收全部达标:① z-index ≤6 —— 裸值 58 处 / 43 文件 / 17 值 → 3 处 / 3 文件 / 3 值(10 / 999 / 1000,全是 B2 例外且逐条带理由;冻结名单 58 → 3;六档 = raised 10 / panel 100 / popover 200 / modal 300 / modalNested 400 / toast 500);② role="dialog" 20/20 —— DIALOG_20 的 20 个文件全部只经原语持有弹层机制(barrel ∧ 无深导入 ∧ 无自建遮罩 ∧ 无 keydown ∧ 无裸 z-index),role="dialog" 的源码命中只出现在 ui/primitives/Modal.tsx。R2 的中间态(必读,否则会引用一句已变形的话):验收第 3 条「五类重复 → 各自 1 个原语」在批 4 结束时并不成立 —— 批 4 走的是 B11「切片 + 棘轮」:只有切片内清零,切片外余量逐类冻结并登记去向(见 §11-3 加注)。八门禁终态:line-limits --full 0 / 122 / 122 · docs-check exit 0 · registry 312/312/0 · tsc 0 错 · vitest 166 文件 / 1608 用例 / 0 失败(对拍批 4 开工基线 LOST=0 · SHRUNK=0)· 首屏预算 100.30 kB gzip(真实构建,余量 99.70 kB;CSS 62.63 kB 不计入判据只报告)· eager 89 文件(源 76 + CSS 13)/ TS-API 真实边 65 / npm 包 4(Δ 与机理见 v0.22 批 4 节)· Rust 未复跑(本批零 Rust 改动 —— git log 42e88740..HEAD -- app/src-tauri 为空)。观测口径:观感从本批真的开始变(叠放值收敛 · 面板宽度档位 · 圆角 6→8 9 处 · 边框/底色冷灰→暖纸 · 20 处阴影换 token),但只有接缝、没有纲领(三档强度/双基调/GSAP 属批 6)。 | | 5 视图层样板 | viewRegistry + ViewSwitcher + 会话 4 视图 + 笔记 3 视图 + 惰性挂载 + flushSave 守卫 | 各 ≥2 种形式可用;原文不丢 | | ↳ 批 5 的收口(批 5 完成时更新 · 2026-09-12) | ✅ 已落(ad9d80d2^.. 至收口提交,31 个提交 = 30 个 T0–T17 实施/守卫提交 + 本节所在的收口提交;含左端点口径见 v0.22 批 5 节);上格原文一字未改。上格两处数字与实盘不符,按实测读:① 「会话 4 视图」→ 5 个(raw/tritrack/proof/cardflow/preview —— 多出的 preview 是既有笔记预览,按 §7.3② 一并进注册表并转为惰性);② 「笔记 3 视图」→ 2 个(raw/cardflow):第三种「带证据三轨」是 C2 的条件项,只读探针判不存在逐段级「笔记 ↔ 证据」读取路径 ⇒ 按 C2 逐字「交付 2 视图 + 就地加注 §7.2/§11-6 + 登记批 7」,不建空壳视图(见 §7.2 与 §11-6 的批 5 加注)。验收「各 ≥2 种形式可用;原文不丢」✅:会话 5 ≥ 2 · 笔记 2 ≥ 2;「原文不丢」= 默认视图 raw 常驻不卸载且有行为级判据(H1 11/11 · F5 11/11)。三条硬约束 9/9 判据全绿(判据落点与变异体出处见 §7.3 加注)。八门禁终态(HEAD = b4edf8e4 · 真实构建产出 app/dist mtime 2026-09-12 22:58:51 +08:00):line-limits --full 0 / 122 / 122(零新增登记)· docs-check exit 0(扫描 277 / 检查 177)· registry 312/312/0 · tsc --noEmit 0 错 · vitest 184 文件 / 1770 用例 / 0 失败 / 0 skip(逐文件对拍批 5 开工基线 2559a3fd 166 文件 / 1608 用例 ⇒ LOST=0 · SHRUNK=0;GROWN 4 · ADDED 18)· check-bundle-budget.mjs(真实构建,取 tmp/build.lock,attempt=1、finally 释放)exit 0 · 首屏 100.49 kB gzip(原始 318,299 B;余量 99.52 kB;CSS 63.23 kB 原始 / 14.51 kB gzip 不计入判据,只报告;入口 index-Bn5oI23G.js 108,620 B)· bundle-eager-graph 91 文件(源 77 + CSS 14)/ npm 包 4,TS-API 真实边口径 66(结论口径;--keep-type-only = 77 自证 ⇒ 差额确由 14 CSS + 11 条纯类型边构成)· Rust 未复跑(本批零 Rust 改动:git log 091d1c3d..HEAD -- app/src-tauri 与 git log 4905d4d8..HEAD -- app/src-tauri 两条均为空)—— 判据仍是批 3 的 2300 / 0 / 6,不得读成「已跑」。观测口径(B9 逐字):「只有接缝、没有纲领」—— 三档强度 / 双基调 / GSAP 在批 6;本批交付的是内容与结构(视图层骨架 + 多视图可用),不是手感。 | | 6 动效系统 | GSAP 接入 + token + 四层 + 三档 + 相变两态 + 6 个签名动效 + usePresence | 可中断可反向;三档正确;60fps | | ↳ 批 6 的收口(批 6 完成时更新 · 2026-09-13) | ✅ 已落(ac504b06^..975b3285,77 个提交 = 批 6 计划 1 + 36 个任务中已提交的 34 个(T15 / T34 各零提交)+ 微单元;含左端点口径见 v0.22 批 6 节);上格原文一字未改。验收面的逐条实际结果(口径 = R0.1 第 2 条「6 个签名动效 + 相变两态 + 承载面 3 项」,读数出处 task-34-report.md §10,T36 出最终表):6 个签名动效 6/6 各有 ≥1 生产调用点 —— 唯一缺口 = #2 的反向 / 重播入口 = 0(状态机与判据齐备、无生产入口)· 相变两态 = 单一写入方(shell/phaseSource.ts:84)+ 载体 1 处(App.tsx:394)· 承载面 3/3 链路闭合(采集态 LIVE 仪表/波形 → 到期刻度 → 时间轨/播放头)· 环境层第 3 件(未确认段落墨度)= 1 处(R12.4 要求 ≥2,差 1) · 列折叠 Flip = 2/8 处(授权路径 B) · [[ts:ms]] 端到端只通一半(会话页那一跳未交付 ⇒ 批 7 首要候选)。上格验收列三项:可中断 / 可反向 ✅(双断言模板 + 逐条专属变异体)· 三档正确 ✅(机器面 = 档位映射 + DOM 属性 + 源序 + 时长参数)· 🔴 「60fps」= 未测(jsdom 无合成器;代理判据 = 未动 layout 属性)—— 不得读成「60fps 已达成」。八门禁终态(HEAD = 975b3285 · 真实构建产出 app/dist mtime 2026-09-13T05:10:24.838+08:00 · 入口 index-CBZzLV5U.js 117,002 B):line-limits --full 0 / 122 / 122(零新增登记)· docs-check exit 0(扫描 280 / 检查 180)· registry 313/313/0 · tsc --noEmit 0 错 · vitest 221 文件 / 2132 用例 / 0 失败 / 0 skip(一次通过、零 flake)· check-bundle-budget.mjs(真实构建,取锁 attempt=1、finally 释放)exit 0 · 首屏 103.60 kB gzip(原始 326,681 B;余量 96.40 kB;CSS 4 个 68,778 B 不计入判据、只报告)· bundle-eager-graph 103 文件(源 89 + CSS 14)/ npm 包 7(结论口径 = TS-API 真实边:75 源文件 / 4 包)· 🔴 cargo test --test app_lib_tests 真跑:running 2335 tests → test result: ok. 2329 passed; 0 failed; 6 ignored,exit 0(R0.1 第 1 条③ 兑现)· cargo clippy --all-targets:error 0 · lib warnings 15 = 基线。观测口径:从批 5 的「只有接缝、没有纲领」变为纲领与承载面均已落地(B9 的中间态声明在本批到期作废,由「已交付 / 未交付」二分清单取代);但像素面与真实帧率仍未测(jsdom 不可达)。 | | 7 未接线落地 | 12 条补 UI(含 kb_search → ⌘K)· 标签线 · 档位通道 · B 桶 3 条撤下 IPC · structuredBlocks 整模块存废(批 1 控制方裁决:接线(4 个导出全接入、真实置信度渲染「低置信点线」)或删除(连同类名与规格/登记表一并移除)二选一,批 1 未决前不得删) | 标签能写进去;档位选完真生效;structuredBlocks 已作出接线或删除的明确裁决 |

🔻 批 7 收口就地加注(2026-09-13,上格原文一字未改):structuredBlocks 的"二选一"由控制方按「逐导出裁决」(第三条路)执行 —— 这不是绕过二选一,而是二选一在两个粒度上各自取正解:① escapeHtml(:13 的再导出,真源在 utils/html.ts:11)⇒ 只摘 import 与再导出,真源一个字不动;② renderLatex / renderMarkdownTable(唯一消费者是它们自己的测试)⇒ 删;③ lowConfidenceClass(唯一生产消费者 session-detail/SessionRawView.tsx:138,import 在 :50)⇒ 留 + 加宽到 ≥2 生产调用点(顺带兑现批 6 R12.4 环境层第 ③ 件的「≥2」)。本加注即控制方的明文授权(依据 rulings.md §C4.1/§C4.2)。

🔻 批 7 收口就地加注 · 上格其余三处状态的实测落位(2026-09-13,上格原文一字未改):

  • 🔴 「12 条补 UI」的实做面 = 10 条(§C9.1 逐字裁决):kb_search(#14) 批 3 Task 12 已交付、update_fragment_group(#46) 的记录同步批 1 已完成 ⇒ 本批实做 10 条 = reset_tag_color(#19) · update_note_tags(#25) · video_profile_for_spec(#28) · analyze_session_command(#34) · delete_session_images_all(#37) · finish_session(#39) · get_decision(#40) · refine_session(#42) · set_tag_color(#45) · update_knowledge_system(#47)。🔴 上格的「12」是规格原文,一字不改 —— 批 8 对账必须能看见「规格写 12、实做 10」的差额来源。
  • 🔴 registry 的批 7 净值 = 313 − 3 + 1 = 311(−3 = §1 L5 行 31 的三条撤下;+1 = 行 34 新增的 remember_video_profile_tier)⇒ 收口判据 = 311 / 311 / 0。
  • 🔴 「B 桶 3 条撤下 IPC」的落地形态(行 31 逐字「撤下但保留内部函数」):摘 #[tauri::command] 属性 + 删注册条目,函数体逐字保留、不改 *_inner 名(create_session / add_session_segment / add_session_ocr_block)。 | 8 治理收口 | 豁免表终态 · 回写 ui-ux-system.md / theme.md · 新增动效规范章节 · 需求池同步 | 11 条验收全达标 | | ↳ 批 1 的收口(批 1 完成时更新) | 22 条命令已删(本表原写 21 + #41 open_capture_float 改判;git diff --name-status --diff-filter=D e96ab63d HEAD = 10 个文件)· 补缝三连连带模块已删(ai_judge + AiMockAdapter::enhance + ai_protocol.rs 的 AiEnhance* 半边,−26 用例 = 9+7+10;另 T3 整族删除 −25、T6 −4、T7 −4 ⇒ 全批 −59)· ADR-010 已废弃(文件保留)· structuredBlocks 死文案已清(整模块存废登记给批 7) | 收口门禁(2026-09-12 实测):registry 312/312/0 · cargo test 2300/0/6 · cargo build 0 dead_code · clippy 19(集合与开工基线 identical)· vitest 124 文件 / 1124 用例 · tsc 0 错 · line-limits --full 0 / 123 / 123 · docs-check exit 0 |

🔻 批 8 终态就地加注(2026-09-13 · T21;上格与上面各 > 块的原文一字未改) —— 上表 批 8 行(| **8 治理收口** | 豁免表终态 · 回写 ui-ux-system.md / theme.md · 新增动效规范章节 · 需求池同步 | 11 条验收全达标 |)有两处不得照字面读:

  • 🔴 「11 条验收全达标」= §11 的 11 条整册验收,不是批 8 的行内验收。批 8 计划 ## Global Constraints 二 逐字写死:「本批的「三条验收」= 三条主线的验收……不是规格 §11 的 11 条整册验收 —— 那 11 条逐条登记去向」⇒ 🔴 批 8 只兑现与三条主线直接相关的部分,不得声称「11 条全达标」。
  • 🔴 「新增动效规范章节」已在批 6 落地(docs/standards/motion.md,bffed928)⇒ 留批 8 的是 docs/product/ui-ux-system.md / theme.md 的四层动效 / 三档强度回写,🔴 而批 8 仍未做(它只回写了「审校模式」那一节:b077d530 · ffb7e28a)⇒ 登记为下一批输入。
  • 批 8 的三条主线兑现度(逐条机器读数;出处 = docs/versions/v0.22.md 的「批 8 · 治理收口与三条主线」节): · 主线一(「带证据三轨」+「审校模式」两条能力)✅ 成立 —— 注册表第三项落地:app/src/views/registry.ts 的 FROZEN_VIEW_KEYS.note 由 2 键变 3 键 = ["raw", "cardflow", "evidence"](T21 于 2026-09-13 逐行直读);全量 vitest 235 文件 / 2300 passed / 0 failed / 0 pending(T21 亲测,时点 2026-09-13T17:34+08:00)。 · 主线二(观感/像素面「收编,不立项」)✅ 成立(含「有读数 ≠ 已统一」的限定) —— 仪器 scripts/viewport-probe.mjs 298 行在盘、按需入口 app/package.json 的 check:visual 在盘、仪器自检 9/9 ✅、14 条观感欠账逐条有读数或具名归属。 · 主线三(治理余项)✅ 四条 4/4 落地 —— CI 挂载(pr-check.yml 的无条件 job + 散文探针单列 step)· scripts/validate-all.mjs 已删 · SURFACE_TAG_REGISTRY 的键改为 (file, tier) · subject ≤50 已装上(commitlint.config.js)。
  • 🔴 段 8f 的四项用户裁决(U1–U4)另计,不并入三条主线:U1 = 音频两条都修 ✅(配置通道 + 面板真开关 + 删会话级联清音频;命令注册表 311 → 312,依据用户裁决 A)· U2 = 只移除凭据槽的代码路径 ✅(已存密钥数据保留、应用内不可达)· U3 = docs/tech-debt/ 归档到 docs/archive/2026-09-13/ ✅ · U4 = 观感按需入口入库 + 触发条件写死 ✅(不接 husky、不进 CI)。🔴 U5(真机 / WebView2 冒烟)沿用「跳过」 ⇒ 7 条真机类继续登记、不假装完成。
  • ⚠️ 两条口径注:① 本批不新增门禁成员(八闸集合不变;check-exemption-prose 只以 CI step 转正、不进八闸对账)② 🔴 批 8 的提交留在本地、未 push(用户前提;「不 push」是本批的唯一强制约束)。
  • 🔴 本节的自引用边界:上格那「批 8 行」所在的本文件没有在批 8 内被改写成「已交付」—— 本注是追加,− 列为 0(承 docs/standards/documentation.md 第四部分之补)。

批 0–1–2 期间界面几乎不变 —— 这是设计意图,不是失败。 批 0–1 交付的是「让后面每一批都能一次改对所有地方」的能力。观感从批 4 开始变,骨架在批 3,内容在批 5,手感在批 6。

实施计划的粒度:本规格覆盖 9 批,不建议一次性生成全部 9 批的实施计划。正确做法是每批各自一份实施计划,且后一批的计划在前一批验收通过后再写 —— 因为批 3 之后的细节依赖批 0–2 的实测结果(尤其是包体治理量出的瓶颈清单与拆件后的真实文件结构)。


11. 验收口径

  1. 4 个 >600 行文件 → 0;>300 行 100% 在豁免表内且数值与实测一致。
  2. 手写弹层 20 → 全走 Modal;z-index 不同值 17 → ≤6 档;role="dialog" + 焦点陷阱 20/20。

    🔻 收口评审 M-1 就地加注(2026-09-12,上句原文保留)—— 档名写错:zIndex("palette") 档不存在,实盘是 zIndex("modal"):app/src/ui/zIndex.ts 的六档是 raised / panel / popover / modal / modalNested / toast,没有 palette(node -e "…/palette/.test(readFileSync('app/src/ui/zIndex.ts','utf8'))" ⇒ false)。实现用的是 zIndex("modal")(app/src/shell/CommandPalette.tsx 两处调用;被 CommandPalette.test.tsx ⑤ 与 ui/zIndex.guard.test.ts 钉住)。⇒ 本行应按此读:CommandPalette 用 zIndex("modal")、AI toast 用 zIndex("toast")。原文保留的理由:本仓文档回写的一贯口径是「原文 + 加注」,且此处没有改判任何验收判据(口径仍是「全走六档标尺」)。风险已消:批 4 的层级归并不得按 palette 这个类型系统都不接受的档名做判断。

    ↳ 批 4 收口后更正(收口评审 M-13,2026-09-12):上句里「CommandPalette.tsx 两处调用」是批 3 时点的读数;bde807dc(T9 的 palette 半程)已把层级交给 Modal ⇒ HEAD(7d541254)实测 0 命中:git grep -n 'zIndex(' -- app/src/shell/CommandPalette.tsx 空(bde807dc^ 上为 2 处)。⇒ 本行的正确读法 = CommandPalette 今日不含 zIndex("modal") 调用,层级由 Modal 承担;「palette 档不存在」的结论不变。 另:读数锚里那句歧义 —— 「手写弹层 20」(§2 弹层行)与 §5.2 的「28 个手写弹层迁移后同样不得…」并存(28 = 弹层 + 确认框 + toast 一类手写覆盖层的合计口径)。批 3 未涉及这组数字中的任何一个,两者都只登记,消歧归批 4/批 8。

    🔻 批 4 收口就地加注 · 三口径并列 + 例外表 + 有意值收敛(2026-09-12,上面两句原文一字未改;批 4 给出的消歧如下,「消歧归批 8」仍适用于跨文档的对账):

    • 验收口径 = 20(规范文本优先,B3)。20 的构成(钉死在 app/src/ui/primitives/dialogMigration.e.test.ts 的 DIALOG_20,不是散文):A 组 14(T5 的 7 + T6 的 7)+ B 组 4(工作台:KnowledgeSystemWizard · ProofreadPanel · RefineWorkbench · SecondPassPanel)+ E 组 2(PracticeQuestionsOverlays · SopRunOverlay)= 20,逐文件 4 条形态判据(barrel 含 Modal ∧ 无深导入 ∧ 无自建遮罩 ∧ 无 keydown ∧ 无裸 z-index)。

    • 三个口径并列(不得混用):20 = 本条的验收口径(对话框类);28 = ADR-033 的同行口径(更宽的手写覆盖层合计);34 = 跨行容忍口径(把「position:fixed 与 inset:0 分写两行」的形态也算进来)。等式(机器判据,双向):34 − 20 = 14 = 11 锚定菜单 + 3 覆盖层;且 28 ⊆ 34 ∧ |28| = 28 ∧ 28 == 34 − 6(CROSS_LINE_ONLY_6 是跨行口径多出来的 6 条)。逐文件清单与 size/testId 表见批 4 计划 §收口回写 §一。

    • 例外表(B1 + B2,逐条带理由,不许静默):

      # 文件 类 为什么不迁
      1 components/BrowserChrome.tsx 锚定菜单(B1) data-app-menu 右键菜单:无对话框语义,只落 popover(200)
      2 components/GroupRowContextMenu.tsx 锚定菜单(B1) 透明点击层 + 锚定面板成对;层内相对序由 DOM 序保持
      3 components/GroupSidebarRow.tsx 锚定菜单(B1) position:absolute; top:100% 行内下拉
      4 components/NoteHeaderActions.tsx 锚定菜单(B1) 笔记头操作菜单
      5 components/NoteListBatchMenu.tsx 锚定菜单(B1) 批量操作菜单
      6 components/NoteMoveToGroupMenu.tsx 锚定菜单(B1) 「移动到组」菜单
      7 components/NoteRowContextMenu.tsx 锚定菜单(B1) 行右键菜单
      8 components/RouteInfoPopover.tsx 锚定菜单(B1) 路由理由气泡
      9 components/SessionRowContextMenu.tsx 锚定菜单(B1) 会话行右键菜单
      10 components/chat/ChatLaunchMenu.tsx 锚定菜单(B1) T13-b 从 ChatPage 拆出的发起菜单(透明点击层 + 菜单,落 popover)
      11 components/note-selection/SelectionActionMenu.tsx 锚定菜单(B1) 选区操作菜单(全仓唯一 window 捕获相 ESC 监听在此,一并冻结)
      12 components/CaptureOverlayPanel.tsx 覆盖层(B2) 采集覆盖层的子操作条(嵌在全屏采集面内部;迁档会踢出「覆盖层内部层」语义);其根遮罩今日仍未声明 zIndex —— 登记在案
      13 components/ImagePreviewOverlay.tsx 覆盖层(B2) 图片查看(zoom-out 遮罩 + 92vw 大图):迁 modal(300) 会让大图落到对话框档之下
      14 components/ScreenSelectOverlay.tsx 覆盖层(B2) 全屏十字光标屏幕点选:迁档会让采集面被任何弹层盖住(只登记根;其子层在根建栈内 ⇒ 已迁 raised)
      • 这 14 条的硬守卫:dialogMigration.e.test.ts ② 断言「登记为「不迁」的文件不得 import 原语层」+ buttonMigration.test.ts ④ 同向复核 ⇒ 本批有 9 处(b1-non-migrated,2.54:1)因此回退并冻结(B21:维持守卫、不放开;批 5/7 若要迁这 14 个文件的排版,必须先由控制方裁决 B1/B2 的守卫范围)。
      • 🔻 批 4 收口后更正表(收口评审 I-7;上面这张表与它的原文一字未改):上表第 1–11 行自称是「34 − 20 = 14 里的 11 锚定菜单」,但与机器真源 NON_MIGRATED_14(dialogMigration.e.test.ts:115-122)的非 *Overlay 那 11 条差 3 个成员 —— 文档多 BrowserChrome / GroupSidebarRow / NoteHeaderActions,文档缺 NoteEditView / NoteLinkToSystem / RichEditorView。表格第 12–14 行(3 个 *Overlay)正确。 独立复算(node .superpowers/sdd/2026-09-12-frontend-redesign-batch4-primitives/tmp/docfix/d4-membership.mjs,从 .e 的导出数组解析、不手抄):
      文档表行 文件 在 NON_MIGRATED_14? 在 ADR033_28? 在 CROSS_LINE_34? 真源里的类
      1 components/BrowserChrome.tsx ❌ 不在 ❌ 不在 ❌ 不在 —(文档误列;.e 里它只作 TABLE4_ALIAS,并有机器断言 CROSS_LINE_34.includes(TABLE4_ALIAS) === false)
      2 components/GroupRowContextMenu.tsx ✅ ✅ ✅ 锚定菜单(B1)
      3 components/GroupSidebarRow.tsx ❌ 不在 ❌ 不在 ❌ 不在 —(文档误列)
      4 components/NoteHeaderActions.tsx ❌ 不在 ❌ 不在 ❌ 不在 —(文档误列)
      5 components/NoteListBatchMenu.tsx ✅ ✅ ✅ 锚定菜单(B1)
      6 components/NoteMoveToGroupMenu.tsx ✅ ✅ ✅ 锚定菜单(B1)
      7 components/NoteRowContextMenu.tsx ✅ ✅ ✅ 锚定菜单(B1)
      8 components/RouteInfoPopover.tsx ✅ ✅ ✅ 锚定菜单(B1)
      9 components/SessionRowContextMenu.tsx ✅ ✅ ✅ 锚定菜单(B1)
      10 components/chat/ChatLaunchMenu.tsx ✅ ✅ ✅ 锚定菜单(B1,T13-b 拆件新家)
      11 components/note-selection/SelectionActionMenu.tsx ✅ ✅ ✅ 锚定菜单(B1)
      补 components/NoteEditView.tsx ✅(文档原表缺席) ✅ ✅ 透明层(非 *Overlay,属那 11)
      补 components/NoteLinkToSystem.tsx ✅(文档原表缺席) ✅ ✅ 透明层(非 *Overlay,属那 11)
      补 components/RichEditorView.tsx ✅(文档原表缺席) ✅ ✅ 透明层(非 *Overlay,属那 11)
      12–14 CaptureOverlayPanel · ImagePreviewOverlay · ScreenSelectOverlay ✅ ✅ ✅ ✅ ✅ ❌(ImagePreviewOverlay 不在 28、属 CROSS_LINE_ONLY_6) ✅ ✅ ✅ 覆盖层(B2)

      ⇒ 根因:文档把「recon 的 B1 锚定菜单清单(11 条,多为 position:absolute 形态)」与「34 − 20 = 14 的非覆盖层那 11 条(含 3 条透明层)」当成了同一个 11;而该表上下紧邻的两句正是「34 − 20 = 14 = 11 锚定菜单 + 3 覆盖层」与「这 14 条的硬守卫」⇒ 表与它自称分解的对象不符。ADR-033:162 已同步同一更正。

      • 🔻 同一行的 b1-non-migrated「9 处」更正(收口评审 M-12):上行的 9 = 回退动作数(tmp/t16b/revert-b1.mjs 的 SITES = 9 条 / 7 文件:NoteEditView:373 · NoteLinkToSystem:258 · NoteLinkToSystem:301 · NoteListBatchMenu:82 · NoteMoveToGroupMenu:135 · NoteRowContextMenu:202 · RichEditorView:363 · RouteInfoPopover:310 · RouteInfoPopover:351);该类的冻结处数另算 —— textBaseline.RESIDUAL 里 kind="b1-non-migrated" 的 7 个文件在 FROZEN_MUTED_GRAY_BY_FILE 的计数之和 = 14(收口修复单元 ed07d491 之前是 13;差额 1 = components/NoteRowContextMenu.tsx 的 </x> 假阴被修掉,该格 1 → 2)。⇒ 引用时必须写明是哪一个数(回退动作 9 / 冻结处数 14)。
    • 🔴 例外只覆盖那 3 条裸值;其余 55 处按 B2 口径登记为「有意值收敛」(durable 登记,出处 task-4-report.md:208-212,由 T4 的 8d84ecfe 落地):50/51/60/999/1000/1100/1150 → modal(300) · 30–61 → popover(200) · 900 → panel(100)。这不是「写法变了、观感不变」 —— 叠放值真的变了(如 1150 → 300),只是相对序在设计上保持不变;规格 §10 逐字「观感从批 4 开始变」在这里的形态就是「叠放值收敛」。与 docs/versions/v0.22.md 批 4 节的「诚实代价」同段。

  3. 空态 / 加载 / 错误行 / 弱化文本 / 卡片边框 五类重复 → 各自 1 个原语。

    🔻 批 4 收口就地加注 · 本条的中间态(2026-09-12,上句原文一字未改;控制方 B11 的附带硬要求:不加这条注,批 8 会引用一句已变形的话**)**:

    • 批 4 结束时,上句「五类 → 各自 1 个原语」在字面上并不成立。 批 4 走的是 B11 的 (B)「切片 + 棘轮」中间态:只有切片内清零,切片外的余量逐类冻结(棘轮只许降)并登记去向(批 5/7)。终局口径不变 —— 上句仍是目标;变的只是批 4 的达成度。
    • 五类的终态读数(每类的「切片内 → 0 / 切片外余量 / 冻结数」;命令与逐文件表见批 4 计划 §收口回写 §一):
    类 切片内 切片外余量 棘轮冻结数(终态)
    空态 EmptyState 0(切片 28 文件,非例外命中 0) 5 文件(FROZEN_REST,只许减)+ 例外 8 条 全仓命中 ≤44(域内 33 文件)
    加载 Loading/Skeleton 0(迁移面 10 文件 0 命中) 8(4 backlog 文件 + 3 button-busy + 1 例外) FROZEN_LOADING_TEXT_TOTAL = 8 == RESIDUAL 8(冻结表 18 键逐格钉死)
    错误行 StatusLine 0(迁移面 49 文件全走原语,含逐文件处数下界) 三红字面量 114 处 / 67 文件 FROZEN_RED_TOTAL = 114(剥注释口径)
    弱化文本 Text 0(切片内 186 处迁完) 63 处 / 44 文件 / 7 类(RESIDUAL 逐条带理由) FROZEN_MUTED_GRAY_TOTAL = 63;字号越界 558 处 / 120 文件本批只冻结不迁
    卡片边框 Surface 0(够格 21 处里实迁 14) 边框 226 处 / 108 文件 · 越界圆角 261 / 109 · 阴影 24 处 / 24 文件 三族 FROZEN_*_TOTAL = 226 / 261 / 24;FROZEN_SURFACE_TAG_TOTAL = 14
    • 🔻 批 4 收口后更正(2026-09-12 · 收口评审 I-6 + 修复单元 ed07d491;上表原文一字未改):上表「弱化文本 Text」行的 63 处 / 44 文件 / 7 类 与 FROZEN_MUTED_GRAY_TOTAL = 63 是旧剥注释仪器的读数。修掉 sliceScan.stripComments 把 </x> 的 / 当正则起点的假阴后,终态 = 64 处 / 44 文件 / 7 类、FROZEN_MUTED_GRAY_TOTAL = **64**(逐文件格 components/NoteRowContextMenu.tsx 1 → 2;条目数/文件数仍 44)。逐类真源(我自测,tmp/docfix/d2-kind.mjs):条目数 14 / 8 / 9 / 7 / 4 / 1 / 1(Σ44)· 处数 19 / 11 / 11 / **14** / 7 / 1 / 1(Σ64);不得再引用旧分解「interactive* 35 · ternary-no-equivalent 10 · nontext 6 · b1-non-migrated 9 · tag 1 · colorMap 2 · C 类 2」(自列之和 = 65,且 colorMap 与「C 类 2」是同一类重复计数)。物理恒等式 = 250 = 迁移 186 + 例外 64。
    • 「切片」的判据(B11,机器可算):① 本批其它任务已触碰的文件 ∪ ② pages/** 全部 ∪ ③ 该类中有同名测试文件的文件。
    • 若控制方当初选 (A) 全量:余量清单就在各类的 *Baseline.ts 的 RESIDUAL / FROZEN_*_BY_FILE 表里(每一条都带非空理由,且「僵尸豁免」判据会红),不需要重新普查。
    • ⚠️ 判据的限度(诚实登记):emptyStateRatchet 的判据来源里,「① 本批已触碰」无法在测试内复算(需要 git 历史)⇒ 那一半只在 gitignored 探针在场时由 ⑥a 覆盖(净克隆里 ⑥a skipIf 跳过、⑥b 用「盘上可复算的第二源」顶上)。出处:task-t13t15-fix-report.md §六 (a) 5 · §六 (b)。
  4. CSS 变量 0 → 覆盖全部语义色 / 字阶 / 间距 / 圆角 / 时长 / 缓动;hex 88 → ≤ token 数。
  5. prefers-reduced-motion 覆盖率 100%,且系统优先于强度档位。 判据(批 0-D 拍定)= 基类名单 + 动画落点名单,不是「全类集合 ⊇」:每一类原语的根类(动效挂在它身上的选择器)必须在 motion.css 的媒体查询名单里;且每一处 animation 声明的选择器原文(含伪元素)也必须在名单里 —— animation-duration / animation-iteration-count 不是可继承属性,覆盖写在宿主元素上伪元素拿不到 (T11 评审用 headless Chromium --force-prefers-reduced-motion 实测:.ed-skeleton 是 0.001s/1,而 .ed-skeleton::after 仍是 1.2s / infinite ⇒ 当时「让两者都静止」未达成)。名单双向一致(漏一个 = 照旧动;多一个 = 死名字)。 修饰类(--)与子元素类(.ed-modal-head/body/foot · .ed-confirm-seal/-impacts/-keep · .ed-empty__title)不进名单 —— 它们与基类同在一个元素上、已被同一条规则覆盖,逐字枚举只会假红(T10 评审曾担心这条守卫会假红,实测 26/26 绿 ⇒ 不必另设兜底方案)。 机器判据在 app/src/ui/primitives/motion-coverage.test.ts。

    🔻 批 6 收口就地加注 · 本条在批 6 的机器判据与读数(R11.3 / R12.4 / R40.2;2026-09-13,上面原文一字未改):

    • 判据落点:app/src/ui/primitives/motion-coverage.test.ts(名单 + 源序 describe)+ app/src/motion/responseSeams.test.ts(I-2 逐条声明带 !important / I-3 两块之间零规则块)+ app/src/motion/responseCoverage.test.ts(响应层七类动作回执)+ app/src/motion/env.test.ts + app/src/motion/shift.guard.test.ts。✅ 终态(T34 逐件复跑):motion-coverage 9/9 passed · style-seams 18/18 passed。
    • 🔴 已知残洞两条(如实登记,R46② / R47⑤):① TopBar.css 的动画落点仍无「必须在 reduced-motion 名单里」判据(结构性缺口只补到扫描器这一层;TopBar.test.tsx 余 2 行 ⇒ 任何追加必须先拆件)· ② reduced-motion 名单今天只能容纳「基类 或 有 animation 声明的选择器」,无法枚举 transition-only 落点 ⇒ Waveform.tsx 的行内 transition 接缝与 ColumnResizer 的余量同源(机制性缺口,不是「忘了列」)。
    • 环境层四件的兑现度见 §4.3 的批 6 加注(第 ③ 件 = 1 处调用点,要求 ≥2);「落了 CSS seam」不算已交付(R11.3 逐字)。
  6. 会话与笔记各 ≥2 种展示形式可用,原文形态不丢。

    ↳ 批 5 收口就地加注(C2 的条件项 + C17①;2026-09-12,上句原文一字未改)—— 本条终态读数:会话 5 种 · 笔记 2 种,两种都 ≥2 ✅;「笔记 3 种」是条件项、未兑现:

    • 会话 = 5 种:FROZEN_VIEW_KEYS.session = ["raw","tritrack","proof","cardflow","preview"](app/src/views/registry.ts:107)。
    • 笔记 = 2 种:FROZEN_VIEW_KEYS.note = ["raw","cardflow"](app/src/views/registry.ts:108)—— 第三种的「带证据三轨」不存在(C2 的探针判否:task-1-report.md · tmp/t1/c2-evidence-probe.md;T13 独立复现见 task-13-report.md §②)⇒ 按 C2 逐字「不存在 ⇒ 交付 2 视图 + 就地加注 + 登记批 7」,不建空壳视图(实测全仓 NoteEvidenceTrack* = 0 命中)。
    • 本条验收不因此变红:2 ≥ 2 ✅,且「原文形态不丢」成立(默认视图 = raw,常驻不卸载,§7.3① 的判据另见 §三条硬约束)。
    • §7.2 矩阵的同一更正见 §7.2 的批 5 加注;产品机会登记批 7:锚点机制确实存在(concat.rs:121-127 发射 · anchor_strip.rs:3-4 契约 · note_filter_golden_tests.rs:117-124 金测试),批 7 若要做「带证据三轨」必须先写规格——E2 降级为 ms 最近邻、E1/E3 改为「带锚点段落数」,并覆盖 OcrDirect/Web/手动笔记/anchor_timestamps=false 的无锚点情形(逐条见 task-13-report.md §④)。 ↳ 批 7 收口就地加注(C9.17 / C9.10;2026-09-13,本条第 6 行的原文与批 5 加注一字未改)—— 第三种的规格已在 §7.4 交付,实现仍未做:
    • 🔴 逐字状态:本批只交规格,实现未做。「带证据三轨」的验收口径改写(E1/E2/E3)、两个分母、无锚点四类 fixture 清单、精修前后两态契约全部写在 §7.4;代码零行。
    • 🔴 本条验收仍按批 5 的读数成立(2 ≥ 2):笔记视图键终态 = ["raw","cardflow"](app/src/views/registry.ts 的 FROZEN_VIEW_KEYS.note),本节不改变这个数。
    • 实测(批 7 独立复测):全仓 NoteEvidenceTrack* = 0 处 / 0 文件(PROD 与 TEST 双侧;负控 zzz_no_such_symbol_zzz = 0)⇒ 不得读成「已交付」。
    • 批 8 输入:§7.4 的实现 + 它的两条红线裁决(段级身份 / 新 command)。
    • 🔻 口径更正(2026-09-13 · T21 补轮;上一行的实测域收窄,原文保留):上一行逐字写的「全仓」是字面假命题 —— 它包含本行自身(本行就含该串)⇒ 自我指涉。🔴 T21 于 53bd0759 实测(入库域 = git ls-files,1,614 文件;口径 = 行含子串,剥注释抹等长空白保偏移):app/src/**(生产 + 测试)= RAW 0 行 / 剥注释 0 行 / 0 文件 ✅ · **app/src/** 之外 = RAW 16 行 / 剥注释 6 行 / 3 文件,全部在 docs/**(按顶层目录 docs:16)—— 含本行自身。
    • ⇒ 🔴 正确表述:「app/src/**(生产 + 测试)= 0 处 / 0 文件」(这才是「实现未做」的有效证据域);app/src 之外仍有 16 行 / 剥注释 6 行的文档提及,那不是实现。上句的「全仓」一律按本注收窄读(本页 §7.4 的 :538 早已按 app/src/** 写,两处现口径一致)。
    • ⚠️ 探针卫生教训(本轮实测复现):该声明的阴性对照 zzz_no_such_symbol_zzz 在入库域命中 7 行 / 5 行(剥注释)/ 3 文件(全在 docs/**) ⇒ 🔴 对照串被自己的产物污染(它被写进了报告与文档)⇒ 阴性对照一旦入库就不再有效;改用每次现造的随机串,或把对照域明确限定在 app/src/**。(阳性对照 useTriTrackAlign 在 app/src = 17 行 / 12 行(剥注释)/ 6 文件 ⇒ 非假 0。)

🔻 批 8 终态就地加注 · 本条上面那句「批 8 输入」已闭合并给终态读数(2026-09-13 · T21;上面各块原文一字未改):

  • 🔴 第三种「带证据三轨」已于批 8 交付:模型层 app/src/views/note/noteEvidenceModel.ts(202 行)+ 视图片 app/src/views/note/NoteEvidenceTrackView.tsx(178 行)+ 容器取数 app/src/hooks/useNoteEvidence.ts(157 行),提交 = de1f43aa · 8cc627de · 08b1afda · e493eac8(行数为 T21 于 2026-09-13 用 countLines() 口径实测)。
  • 🔴 笔记视图键由 2 键变 3 键:app/src/views/registry.ts 的 FROZEN_VIEW_KEYS.note 逐字 = ["raw", "cardflow", "evidence"](T21 逐行直读)。
  • 🔴 但本条验收的口径不变,仍是「各 ≥2 种」 ⇒ 会话 5 / 笔记 3,3 ≥ 2 ✅;不得把「终态 3」写成「本条要求 3」或「本条因批 8 而改变」。
  • 两条红线裁决的落地(承 §7.4 §D 的批 8 加注):① 段级身份走「派生序号」(零 schema 改动)② 不开新 command(复用既有 get_session_detail 已返回的 segments + ocr_blocks)。
  • 🔴 仍未做 / 未覆盖:真机 / WebView2 冒烟由用户裁决跳过(U5) ⇒ 本条实现面的「端到端」止于 jsdom / Rust 单测层;像素 / 观感一致性未验证(≥2 是形式数,不是「两种形式看起来对」)。
  1. 47 条未接线命令逐个有结论,无「不知道」。

    进度(批 1 收口,2026-09-12):47 条中 22 条已删(含 #41 改判)、25 条处置已定但未执行(补 UI 12 → 批 7 · 登记不排期 7 → 批 7/无期 · 撤下 IPC 3 → 批 7 · 有意保留 3 → 无期)。无「不知道」。批 1 计划 §现状普查第三节给出 47/47 的逐条处置表;删除后复算 = registry 312 / 前端零引用 25 条(= 47 − 22,逐条与上列归属一致)。 进度(批 7 收口,2026-09-13):registry 313 → 311(−3 撤下 IPC + 1 新增 remember_video_profile_tier)· 前端零引用 24 → 11(实测值见收口报告;口径 = 上格那 47 条中仍在 registry 者 22 条里前端生产零引用的条数;仪器 = 五形态并列带引号定界字面量探针,正控 invoke( 命中 68 个生产文件、负控 0;kb_search 已交付的 1 条不计入)· 本次加注不改上格原文。

    • 差额逐条:24 → 11 = −13,分解 = −10(本批实做补 UI 的 10 条各得 ≥1 生产调用点)+ −3(撤下 IPC 的三条随注册一起离开本集合)。同格局:上格「25 条」是剩余命令条数(含 kb_search)⇒ 现 22 条(−3 撤下);条数 − 零引用 = 11 = 有引用数(与逐条读数一致)。
    • 🔴 新命令 remember_video_profile_tier 不在上格的 47 条内:前端生产调用点 2(components/ProfileDetector.tsx)⇒ 不计入零引用(它的义务是 §1 L5 行 34 的「新增命令」,不是本格的补 UI)。
    • 🔴 残留 1 条挂着「补 UI(本批)」却仍零引用:update_fragment_group(#46) 全 app/src/**(含测试)命中 0 文件 / 0 处,而它仍在 registry ⇒ 本批未接线(它被 §C9.1 排除出 10 条名单,理由是「记录同步」那一半批 1 已做,不是因为接线已做)⇒ 登记为批 8 输入,并见 §9 该行的状态更正注。
    • 🔻 收口更正(2026-09-13 · T21 追加轮;上面三条原读数一律保留为「T21 首轮」的历史读数):update_fragment_group(#46) 已在批 7 内由 T23 补做(125eec85 + 5f662811 + 5d59e332;T21 独立复测 PROD 0 → 1,落点 components/FragmentGroupAction.tsx:92 逐字 invoke("update_fragment_group", { fragmentId, groupId: target }))⇒ 🔴 残留 1 条 → 残留 0 条;🔴 前端零引用 11 → 10(口径不变:上格那 47 条中仍在 registry 者 22 条里的前端生产零引用数)⇒ 有引用数 11 → 12(其中本批实做补 UI = 11 条:T20 的 6 + T23 的 1 + T18/T19 的 4;kb_search 是第 12 个有引用者,但它属批 3 交付)。⇒ 上面那条第 4 项(「残留 1 条」)的处置改为「已交付」,§9 该行的状态更正注已在表末就地更正。 ⚠️ 批 1 的实测更正(供批 2+ 引用本条时注意):计划的「不删清单」在本批被证伪五次(artifact_templates 族 / AiEnhance* 半边 / vad_threshold_slot 三符号 / spec_from_kind / 调用点计数),根因是**用「看起来还有人用」代替「删除后可达性」**作保留判据 ⇒ 后续批次判断连通性请照「收口回写」节的四条约纪律(计划 docs/superpowers/plans/2026-09-11-frontend-redesign-batch1-deletions.md)。
  2. 标签能写进去且标签过滤面板有内容;画面档位选完真的生效且跨会话记住。

🔻 批 8 终态就地加注 · 本条在批 8 的状态(2026-09-13 · T21;本行原文一字未改):

  • 🔴 批 8 对本条零改动、零复跑:批 8 的三条主线(两条能力 / 观感收编 / 治理余项)与四项用户裁决(U1 音频两条 · U2 凭据槽代码路径 · U3 docs/tech-debt/ 归档 · U4 观感按需入口)没有一条落在这条验收上 ⇒ 🔴 本条的结论仍以批 7 的收口读数为准,不得读成「批 8 重新验证过」。
  • 🔴 防误读(逐字):批 8 的 U1 是「音频存储开关 + 删会话删音频」,与本条的「标签 / 档位」无关 ⇒ 不得把它算进这一条;批 8 也未触碰任何标签线或档位通道的生产代码。
  • 🔴 本条的「真机面」仍未覆盖:批 7 的「档位选完真生效」判据是代理判据(记忆体往返 + 负控),不是「采样参数真被采集链路消费」的像素级证据;且真机 / WebView2 冒烟由用户裁决跳过(U5)。
  • 批 8 的相关读数(只作旁证,不作本条判据):check-command-registry 312 / 312 / 0 · cargo test --test app_lib_tests 2378 passed / 0 failed / 6 ignored · npx tsc --noEmit 0 错(均为 T21 亲测,时点 2026-09-13T17:32–17:35+08:00)。
  1. JS 包首屏 gzip 达标或给出瓶颈清单;GSAP 只在独立 chunk 懒加载。

    进度(批 2 收口,2026-09-12):✅ 前半达标 —— 首屏 JS gzip 92.79 kB < 200 kB(预算真源 docs/standards/performance.md:28),判据工具 scripts/check-bundle-budget.mjs exit 0、余量 107.21 kB、懒加载 22 个 574.84 kB 不计入。 后半 ✅ 已就位但未装 GSAP(「不装 GSAP」是批 2 的硬非目标):app/src/build/manualChunks.ts 的 vendor-gsap 精确包名槽位已预留并被单测钉住(含反例守卫,实测真实构建 746 次调用中 vendor-gsap 返回 0 次、dist 无该 chunk);批 6 装 GSAP 时必须用 import(),判据见批 2 计划 Task 5。 ⚠️ 口径警告(勿误读本条的达成原因):manualChunks 自身不降低首屏字节;本条达标全部来自 import()(页级 + 窗口变体 + 对话面板)。 进度(批 5 收口,2026-09-12):✅ 前半继续达标且几乎持平 —— 首屏 JS gzip 100.49 kB < 200 kB(余量 99.52 kB),scripts/check-bundle-budget.mjs(真实构建,取 tmp/build.lock)exit 0;相对批 5 前基线(091d1c3d 冻结值 100.30 kB / 100,297 B)Δ = +188 B gzip(+0.19%),全部落在入口 chunk(108,099 → 108,620 B,+521 B);首屏chunk 个数 3 → 3(Δ=0,构造性成立)。CSS 63.23 kB 原始 / 14.51 kB gzip 不计入判据,只报告(基线 62.63 kB ⇒ +599 B)。懒 chunk 22 → 31 个 · 583.98 kB gzip(1,805,017 B 原始;不计入预算)。后半仍 ✅ 未装 GSAP:app/src/build/manualChunks.ts 本批零改动、零新增依赖(npm 包 4 → 4),@keyframes 零新增、vendor-gsap 槽位仍 0 命中 ⇒ 交接批 6。 诚实代价(本条的批 5 形态,逐条带读数):① ViewSwitcher 进 barrel 的首屏 Δ = JS 0 B + CSS +604 B —— 计划 C14① 的「首屏 Δ 不是 0」被实测否证:T5 的时点读数(dist = 25 个 .js)显示 ed-btn-group/ed-btn--segment 0 命中任何 JS、只在首屏 CSS 里命中 ⇒ rollup 把 ViewSwitcher 的 JS tree-shake 掉了(当时无调用点),终态(34 个 .js)该字面量命中懒 chunk registry-C-RvXIfE.js ⇒ 机理 = barrel 里有导出 ≠ 产物里有模块。② 「页 chunk 变小、总懒基本持平」:SessionsPage 页 chunk −10,288 B · NotesPage 页 chunk −4,815 B(视图代码搬出页面 chunk ⇒ 懒 chunk 增 9 个真新增块)。 🔻 批 6 收口就地加注 · 本条后半(GSAP 懒加载)在批 6 的落地读数(R2.1 · R11.1 · R63.3;2026-09-13,上面原文与批 2/批 5 进度注一字未改):

    • ✅ 前半继续达标:首屏 JS gzip 103.60 kB < 200 kB(余量 96.40 kB),scripts/check-bundle-budget.mjs(真实构建,取锁 attempt=1 + finally 释放)exit 0。Δ(对批 5 收口的 100.49 kB)= +3,118 B(+3.10%);原始 +8,382 B(+2.63%)全部落在入口 chunk(108,620 → 117,002 B);vendor-react / vendor-tauri 逐字节不变;首屏 chunk 数 3 → 3(Δ 0,构造性成立)。CSS 68.78 kB 原始 / 15.62 kB gzip(4 个)不计入判据、只报告(批 5 = 63.23 kB / 3 个 ⇒ +5,549 B;归因必须含 T12 / T13 / T19 / T29,不止 T5/T6)。
    • ✅ 后半「GSAP 只在独立 chunk 懒加载」成立:产物有 vendor-gsap-BQLVn3Z6.js(105,584 B)且 firstScreen.chunks 不含它;判据 = engine.guard.test.ts 的闭包交集(不是白名单),真实构建后复跑 11/11 passed、exit 0。
    • 🔴 结论口径唯一(R63.3 钉死):首屏静态可达的源文件数只许用 TS-API 真实边口径 = 66 → 75(+9)、npm 包 4 → 4(Δ 0);bundle-eager-graph 的工具读数(91 → 103 文件 / 4 → 7 包)只作旁证,且必须注明「其中 2 个是纯类型边(gsap / @gsap/react,由 import type … from "../motion/engine" 顺边走到值导入)+ 1 个是注释字面量假命中(views,出自 app/src/types/session.ts:347 的 JSDoc)」。
    • 懒侧账(不计入预算,只报告):懒 chunk 31 → 36、原始 +354,344 B、gzip 583.98 → 698.86 kB(+114.88 kB);归因 94.1% / 92.0% 来自两块 —— vendor-katex +227,858 B(T28 让 structuredBlocks.ts 首次进图 ⇒ katex@0.18.4 的第二份副本进产物;懒侧今天没有任何预算门禁)+ 新块 vendor-gsap +105,584 B。🔴 该代价逐字进 v0.22 批 6 节的「诚实代价」,并登记两条批 7/8 候选:「把 lowConfidenceClass 从 structuredBlocks.ts 析出轻模块」+「懒侧字节纳入门禁」。
  2. 6 个签名动效各自可中断、可反向,reduced-motion 下正确降级。

🔻 批 6 收口就地加注 · 本条 6 个签名动效的四行判据落点(R8.1–R8.6 · R11.4;2026-09-13,上面原文一字未改):

  • 「可中断 / 可反向」的判据形态 = 双断言(同时断 gsap.globalTimeline.getChildren().length(或旧 tween 的 totalTime() 冻结)与目标元素 style.transform)—— 只看 style.transform 会假绿(GSAP 3 默认 overwrite:false)。唯一出口 = app/src/motion/controls.ts(interrupt() 真 kill,且 killTweensOf 先、timeline.kill() 后)。⚠️ getChildren() 默认是递归口径(本实现下的正确值是 2);只有 getChildren(false, true, true) 才 === 1。
  • 四行判据的落点(逐条在 task-34-report.md §10 给出生产调用点):#1 对齐 views/session/useTriTrackAlign.ts · #2 显影 components/session-detail/useRevealChoreography.ts · #3 相变凝固 shell/usePhaseFreeze.ts · #4 刻度生长 components/review/useScaleGrowth.ts · #5 回跳 views/session/usePlayheadJump.ts · #6 记忆浮现 components/review/useRevealMemory.ts(每条各带专属变异体与「三档 / reduced-motion 降级」用例)。
  • 🔴 降级路径的诚实边界:prefers-reduced-motion 下跳终态由 controls.ts 统一实现(matchMedia 自带守卫);像素面(真的直线 / 真的 220ms 淡入)不可判;Flip 的几何位移零可观测 ⇒ 只用「结构契约 + Flip 已注册 + timeline 存在 + 属性集合 ⊆ transform 族」的弱判据。
  1. npx tsc --noEmit 0 错 · npx vitest run 全绿 · cargo test 全绿(每批)。

    进度(批 3 收口,2026-09-12):八门禁全绿(line-limits --full 0/123/123 · docs-check exit 0 · registry 312/312/0 · tsc 0 错 · vitest 143 文件 / 1370 用例 / 0 失败 · cargo test --test app_lib_tests 2300/0/6 逐字持平 · 首屏预算 97.16 kB < 200 kB(exit 0,余量 102.85 kB;CSS 不计入该判据,只报告 52.27 kB 原始 / 12.58 kB gzip)· clippy 位置集合 20 = 基线,SET-IDENTICAL)。⚠️ 「vitest 全绿」本轮含一条既有 flake(components/KnowledgeGraphView.test.tsx > 单击节点…):本次全量未复现,但在批 3 开工基线树 a7bd1899 上复现过(隔离复跑 11 passed / exit 0)⇒ 既有负载敏感 flake,非本批引入(判据与两种读数见批 3 计划 §收口回写)。 进度(批 4 收口,2026-09-12):八门禁终态在静止干净树上重跑(HEAD = 199da54b · app/dist mtime 2026-09-12 19:54:33 · 采集 19:5x):line-limits --full 0 / 122 / 122(--write 复跑零 diff ⇒ 豁免表不在收口提交路径里)· docs-check exit 0(扫描 276 / 检查 176,五项全 ✅)· registry 312/312/0 · tsc --noEmit 0 错 · vitest 166 文件 / 1608 用例 / 0 失败(逐文件对拍批 4 开工基线 143/1370 ⇒ LOST=0 · SHRUNK=0;GROWN 6 条 · 新增 23 个测试文件,逐条见批 4 计划 §收口回写 §二)· check-bundle-budget.mjs(真实构建,取 tmp/build.lock)exit 0 · 首屏 100.30 kB gzip(余量 99.70 kB;CSS 62.63 kB 原始 / 14.42 kB gzip 不计入判据,只报告)· bundle-eager-graph 89 文件(源 76 + CSS 13)· npm 包 4,TS-API 真实边口径 65,Δ = 0(机理见批 4 计划 §收口回写 §二)· cargo test 未复跑 —— 本批零 Rust 改动(git log 42e88740..HEAD -- app/src-tauri 实测为空),判据仍是批 3 收口的 2300 / 0 / 6;不得把「未跑」写成「已跑」。⚠️ 一条已知 flake(components/KnowledgeGraphView.test.tsx 的负载敏感用例)在本批多份报告里各出现过 1 次、孤立复跑均全绿 ⇒ 既有现象,非本批引入。 ↳ 收口后处置(2026-09-12,cd85e4c3;控制方已裁):该 flake 的状态记为「已加固、未复现」—— 收口修复单元做了最小稳定性修改(components/KnowledgeGraphView.test.tsx 的 18 处 waitFor 超时 5000 → 15000,与 vitest.config.ts 的 testTimeout: 15000 对齐;未放宽任何断言、未加睡眠、未改配置、未加 retry),但本机两种负载各 8 次 + 无负载 6 次均未复现 ⇒ 不声称「已修好」。纪律:① 变异体实验不得与全量测试并发(负载会把 flake 变成假红);② 今后若在顺序执行下再红 ⇒ 按新 flake 重新定位。 进度(批 5 收口,2026-09-12):八门禁终态在 HEAD = b4edf8e4 上采集(采集窗口 22:57:07 – 22:58:51 +08:00;真实构建产出 app/dist mtime 2026-09-12T14:58:51.595Z = 22:58:51 +08:00,入口 index-Bn5oI23G.js):line-limits --full 0 / 122 / 122(--write 复跑逐字节零 diff,sha256 相同 ⇒ 豁免表不在收口提交路径里)· docs-check exit 0(扫描 277 / 检查 177,五项全 ✅)· registry 312/312/0 · tsc --noEmit 0 错 · vitest 184 文件 / 1770 用例 / 0 失败 / 0 skip(--frozen 逐文件对拍批 5 开工基线 2559a3fd 的 166 文件 / 1608 用例 ⇒ LOST=0 · SHRUNK=0;GROWN 4 · ADDED 18)· check-bundle-budget.mjs exit 0 · 首屏 100.49 kB gzip(余量 99.52 kB)· bundle-eager-graph 91 文件(源 77 + CSS 14)/ npm 包 4,TS-API 真实边 66 · cargo test 未复跑 —— 本批零 Rust 改动(git log 091d1c3d..HEAD -- app/src-tauri 与 git log 4905d4d8..HEAD -- app/src-tauri 两条均为空);判据仍是批 3 收口的 2300 / 0 / 6,不得把「未跑」写成「已跑」。 ⚠️ 本条的仪器口径(批 5 实测,供后续批次引用):vitest 的「文件数」必须用 testResults.length —— vitest 4.1.11 的 JSON 顶层没有 numTotalTestFiles 字段(Object.keys(json) 实测仅 12 个键;hasOwnProperty = false、typeof = undefined),而 numTotalTestSuites = 647 是 describe 块数、与文件数差 463 ⇒ 两个字段都不能当文件数用。另:numPendingTests 1 → 0(skipped 归零,全由批 5 收口的两个守卫提交解释;闭合式 1750 + 19 + 1 = 1770)。 批 5 的既有 flake 状态:components/KnowledgeGraphView.test.tsx 的负载敏感用例本批全量未复现(批 4 的「已加固、未复现」状态继续有效);批 5 的 vitest 终态 0 失败。 进度(批 6 收口,2026-09-13):八门禁终态在 HEAD = 975b3285 上采集(真实构建产出 app/dist mtime 2026-09-13T05:10:24.838+08:00,入口 index-CBZzLV5U.js):line-limits --full >600 0 · 301–600 档 122 · 登记条目 122(零新增登记)· docs-check exit 0(扫描 280 / 检查 180,五项全 ✅)· registry 313/313/0 · tsc --noEmit 0 错 · vitest 221 文件 / 2132 用例 / 0 失败 / 0 skip(一次通过、零 flake;对拍批 5 收口的 184 / 1770 ⇒ +37 文件 / +362 用例)· check-bundle-budget.mjs exit 0 · 首屏 103.60 kB gzip · bundle-eager-graph 103 文件(源 89 + CSS 14)/ TS-API 真实边 75 · 🔴 cargo test --test app_lib_tests 真跑(本批有 Rust 改动 ⇒ 必跑):running 2335 tests → 2329 passed; 0 failed; 6 ignored,exit 0(与批 3 基线 / 批 5 持平 ⇒ 用例数 Δ = 0)· cargo clippy --all-targets:error 0 · lib warnings 15 = 基线。 ⚠️ Rust 侧的判据口径(R25.1):不得要求逐字复现 2303/0/6 —— ffmpeg::tests::run_captured_handles_large_output 有基线树复现(含 10s 墙钟超时,与全量并发时被拖超时)⇒ 判据 = 「本批相关用例全绿 + 唯一红若是该 flake 则须单跑复绿」。🔴 六个负载敏感 flake 全列(本批登记,判据 = 单跑复绿):components/KnowledgeGraphView.test.tsx · components/notes/NotesReadingColumn.views.test.tsx · components/ChatSaveNoteDialog.test.tsx · views/registryResolution.test.ts · components/KnowledgeSampleView.test.tsx(第 6 个候选) · ffmpeg::tests::run_captured_handles_large_output。⚠️ 但**「并行窗口内的全量偶发红」是一类现象、不是固定名单**(评审另实测出 3 个不在名单内的瞬时红:pages/NotesPage.test.tsx 编辑标题→完成 · pages/ReviewPage.test.tsx 打开即见到期数 · components/SessionDetailPanel.test.tsx P1 默认视图是原文)⇒ 判据 = 「单跑复绿 + 提交树全量绿」,不是「命中某个名单」;且全量 vitest 必须在无并发单元时跑(R40 附)。


12. 仍未决与登记不排期

项 状态
open_capture_float 前端现有开法 ✅ 已结清 2026-09-11:前端 0 调用者(活路径为 float_toggle + close_capture_float)⇒ 改判为「删」,落地于批 1
软删 / 回收站(审计 D2 丙方案) 独立技术债 —— 需 schema 迁移
T7 档案记忆增量(命中计数 + 会话后自动修正) 基础闭环已通,增量登记
image_stream_store.rs 图像流接线(L6) 档位通道做完后才具备前提
滚动驱动动效 · 图谱浮现 · 笔记树生长 · 熵减收拢 登记不排期(已从「不做」改为「待议」)
3 套 markdown 渲染器归一 可并入批 4 → ⚖️ 2026-09-12 已裁定:不并入批 4(批 4 侦察的裁决 B8 .superpowers/sdd/2026-09-12-frontend-redesign-batch4-primitives/rulings.md:批 4 已背 510 按钮 + 34 弹层 + 44 空态 + 88 加载 + 190 错误行 + 1271 字号,再并入大模块会让单批不可验收)⇒ 登记去向:批 5(视图层)或批 7(未接线落地)。本行「可并入批 4」是许可不是命令,原文保留
审计文档 D1–D10 编号复用(§4 与 §6 批 3 两套 D 编号) 需消歧,否则引用会出错
「某个具体的瞬间希望它是活的」 ✅ 已答复 2026-09-11:「我希望我在对其进行交互时,它是活的」 ⇒ 已并入 §8.6.1(四条硬约束:配重翻转 / 每个动作都要有即时回执 / 可中断可反向 / 无障碍优先)。批 6 的 6 个签名动效照此配重。
旧单 provider 凭据槽 "default" 在应用内永久不可撤销(ai_save_key/ai_clear_key/ai_test_connection 删除后无 IPC 写/清路径) 登记不排期 · 归属「处理 AI 凭据」的批次做产品裁决(建议与批 7/批 8 的产品文档 pass 同批)。正解 = 新增一条 provider 通道的「清理遗留 scope」命令,不是恢复旧命令;读兜底与启动迁移仍在,前端本就 0 引用(非回归)。出处:批 1 计划「未做(登记)」表
批 1 后的 requirements-pool.md 功能级陈旧表述(REQ-050「走 AI 补缝(V1.0)」· REQ-052「五档案模板 + 产物视图 + 落笔记」· REQ-053「低置信/AI 占位样式」) 登记不排期 · 产品文档 pass(与批 7 的未接线落地判定同批):这三条不点名已删符号,属产品功能级状态重写,删除批不做。出处:批 1 Task 10 报告 §6/§9

🔻 批 5 收口就地加注 · 「3 套 markdown 渲染器归一」的去向(A8 = C8 = D8;2026-09-12,上表原文一字未改)—— 批 5 未承接,转批 7****:

  • 裁决:批 5 不做归一(理由与批 4 的 B8 逐字同族:批 5 已背 5 个新视图 + 注册表 + 1 个新原语 + 惰性挂载 + flushSave 守卫 + 2 个贴边文件拆件,再加渲染层归一同样不可验收;且 NotePreviewView 无测试面,替换它没有回归网)。ChatMessageMarkdown → NoteMarkdown 的归并、NotePreviewView 手写解析器的替换都转批 7。
  • 批 5 只保证「不新增站点」(防「3 套变 4 套」的硬要求):react-markdown 运行时站点 = 2(NoteMarkdown / ChatMessageMarkdown,不变)· app/src/views/** 的 react-markdown 站点 = 0(含 import type;实测含 type 也 0)· remark-* + rehype-* = 8(不变)· 零新增依赖。笔记卡片流的派生器 views/note/noteCardModel.ts 不手写 markdown 解析器(复用既有 react-markdown 栈 + 自定义组件映射产出卡片结构),文件头显式命名「结构派生器,批 7 归一」(判据:该行存在)。
  • 口径更正(C17②,文档与后续批次一律照此写):本行的「3 套」应读作「4 套活 + 1 套死」—— NoteMarkdown.tsx:18 · ChatMessageMarkdown.tsx:8 · utils/refineDiff.ts:78 mdLineHtml(计划未点名的第 4 套手写解析器) · structuredBlocks.ts(第 5 套、无生产消费者)。T12 的判据不受影响。
  • 行数代价:components/NoteMarkdown.tsx 244 → 266 行(只追加一个槽 + 一个类型导出,删除行 = 0;预算 ≤270 ✅)。
  • 出处:批 5 计划 §收口回写 §七 · tmp/t18/measurements.md §9 A8。

🔻 批 7 收口就地加注 · 「3 套 markdown 渲染器归一」的去向终态(2026-09-13,上表与批 5 加注原文一字未改):批 7 已完成归一,形态 = C10.1 的「两支手写链合成一支 + #2 并入 #1」:新建 utils/markdownLine.ts(100 行:#3 refineDiff.ts 的 mdLineHtml + #4 NotePreviewView.tsx 的 renderMarkdown 合成一支,两个具名模式常量逐字保留原样式 ⇒ 零观感变化)· ChatMessageMarkdown 并入 NoteMarkdown 后删除(站点 2 → 1、插件站点 8 → 4)· structuredBlocks 按 §C4。终态 = 2 套活(NoteMarkdown + markdownLine)。🔴 未做:把两支手写链换成 react-markdown(禁止 —— 它们是给 dangerouslySetInnerHTML 的串生成器,换 = 改 DOM 形态/样式/安全面,而 #4 无回归网)。

  • 🔴 口径逐字(§C11.1 第 ③ 条要求「报告与规格回写里逐字写」):归一 = 逻辑归一;观感统一未做,归批 8。
  • 🔴 补登规格加注的漏点(C4.3)—— 第 4 套活渲染器 = components/NotePreviewView.tsx:40 renderMarkdown:上表与批 5 加注把「第 4 套」记作 utils/refineDiff.ts:78 mdLineHtml、把 structuredBlocks.ts 记作「第 5 套」,漏点了 NotePreviewView.tsx:40 renderMarkdown —— 它才是 NotePreviewView 那条独立渲染链的真身(批 7 开工实测 346 行,终态 364 行;dangerouslySetInnerHTML 两处在 :295/:301,图片分支的容器侧前置判断在 :61)。🔴 批 7 开工时它"无测试面"(§C9.3 因此把「先补表征测试再替换」定为硬顺序)⇒ T16 6509e04c 已补 components/NotePreviewView.render.test.ts(34 用例 / 53 处 expect,T21 实测 234 行),T15 的 M1 与 T17 的迁移各有回归网(洞已补上 = 实测事实,不再是推断)。本加注补上这个漏点。
  • 执行顺序与残余(如实登记):先 T16 补回归网 → T15/T17 迁移(NotePreviewView 迁入 markdownLine、RefineWorkbench 侧同支受益)⇒ [[ts:ms]] 芯片在两条串渲染链上均已可点(data-ts-ms + 容器侧事件委托);🔴 两处页内 seek 未接(AiRefineCard / NoteAiDialog,§C49.3 判为另一条线 ⇒ 批 8 输入)—— 不得声称「[[ts:ms]] 全链已通」。

🔻 批 6 收口就地加注 · 「登记不排期」四项的自证 + 本批新增登记项(R0.2 · R5.5-b · R63.1;2026-09-13,上表原文与批 5 加注一字未改):

  • 🔴 R0.2 的自证:本批对四项的被做数 = 0**。逐项(自证口径 = 全批 77 个提交的改动面 + app/src 实测):① 滚动驱动动效 = 0 处(ScrollTrigger 未装、未注册;engine.ts 的插件集合恰 4 个)· ② 图谱浮现 = 0 处(KnowledgeGraphView 零改动)· ③ 笔记树生长 = 0 处 · ④ 熵减收拢 = 0 处。⇒ 上表第 5 行的「登记不排期」在本批未被触碰(批 6 承接的 6 个签名动效不在此列,R0.2 已逐字判过)。
  • 本批新增的登记项(逐条带去向):① aligned 的残余偏差 D3–D6(每次暂停 ≤300ms · 停止尾块 ≤200ms · 逐包重采样亚样本;量级未实测、漂移分布今天不存在)→ 批 7/8 · ② 导入会话无音频承载面(音轨在 %TEMP% 且导入结束即删、sessions 表无源视频路径列 ⇒ session_audio_path 恒 Ok(None))→ 产品裁决 · ③ 跨窗口档位 / 相位同步(?float=1 / ?overlay=1 各自独立)→ 批 7/8 · ④ 孤儿 sidecar({id}.wav.meta.json 不在 cleanup 的 .wav 过滤面内)+ 删会话不删音频 → 与 ② 一并裁决 · ⑤ 🔴 Flip 的 width/height 与 §11-10 属性集合判据的冲突(Flip 会写 layout 属性)⇒ 如实登记为「属性集合判据在 Flip 上不成立」,不得为让判据变绿而放宽 R8.4 → 批 7/8 与判据纪律 · ⑥ ADR-019 缺号(不补、不复用,只登记)· ⑦ docs/tech-debt/ 仍挂起 → 用户裁决(T36 收口报告列三个选项) · ⑧ 懒侧字节无预算门禁 + katex@0.18.4 第二份副本(R63.1)→ 批 7/8 候选:把 lowConfidenceClass 从 structuredBlocks.ts 析出轻模块 / 懒侧纳入门禁 · ⑨ [[ts:ms]] 的会话页那一跳未交付 → 批 7 首要候选 · ⑩ usePlayheadJump 等 hook 无 unmount 清理(时间线在卸载后仍存活,潜在残留)→ 波 D 已评估、登记批 7/8。

🔻 批 8 终态就地加注 · §12 表内「旧单 provider 凭据槽 "default" 在应用内永久不可撤销」那一行(2026-09-13 · T21 落库时该行在 :979) —— ⚠️ 表内不能插块引用(会截断表格) ⇒ 按批 7 §9 的先例整表之后就地在注、并逐字点名该行;该行原文一字未改(见上表)。

  • 🔴 原文那句「读兜底与启动迁移仍在」现在是假话:批 8 的 T25 已移除那条读兜底与启动迁移路径 —— 提交 a054ec73「fix(ai): 移除默认凭据槽作为合法槽位的路径」(parent 9b1e8aa8;写集恰 5 条 = ai_credentials.rs · ai_credentials_tests.rs · app_setup.rs · commands_ai_providers.rs · commands_ai_providers_tests.rs),紧随其后的是 ae3bba3e「test(ai): 收紧遗留凭据槽判据的具名红点」(parent = a054ec73)。 ⇒ 🔴 原文保留、本注为准:上表该行的「读兜底与启动迁移仍在,前端本就 0 引用(非回归)」描述的是批 1 时点;批 8 时点已不成立。
  • 🔴 必须并读的另一半(否则会把「移除了代码路径」误读成「数据被抹除」):已存密钥数据保留,且应用内不可达 —— 这是 U2 的 (ii)「不做」的刻意边界(无迁移即销毁不可逆)⇒ 本批只移除代码路径,不物理抹除任何已存数据;将来若要清理需新的用户裁决(登记为下一批输入)。
  • ⚠️ 同源文本另有两处、按「时态判据」不动的:docs/superpowers/plans/2026-09-11-frontend-redesign-batch1-deletions.md 与 docs/superpowers/plans/2026-09-13-frontend-redesign-batch8-groundwork.md 各一处「读兜底与启动迁移仍在」⇒ 历史计划文本、一字不动。
  • ⚠️ 真实盲区(如实登记,不掩饰):启动期行为无单测(该判据在行为侧不可达)⇒ T25 的那条红点由静态判据承担;且 「仅有 default 槽的真机用户凭据会失效(需重填)」这一风险在本环境测不了(真机 / WebView2 冒烟由用户裁决跳过,U5)。
  • ⚠️ 引述来源:本节的两条事实(T25 的五条写集 / 三处文本定位)出处 = .superpowers/sdd/2026-09-13-frontend-redesign-batch8/controller-rulings.md 的 §33.1 与 §33.4① —— 🔴 该目录整体 gitignored、不入库,故此处只给路径不给链接;提交 sha 与写集已由 T21 用 git show --name-only -1 a054ec73 独立复核。

13. 风险与回归

风险 触发条件 缓解
拆件行为不等价 拆 4 个超限文件时夹带功能改动 本批不加任何新功能;现有测试全绿 + 手工走查
删命令删到活代码 [DEAD] 判定被测试文件污染 每条删除前重新确认调用方(一次 grep)—— 批 1 实测升级为三向复核(前端生产字面量 / 前端测试 / Rust 调用形态)+同名三域归属(命令 vs 同名活函数 vs 注释提及):22 条全部为空,OCR 引擎与导入转写链路的同名活函数一处未伤。⚠️ 裸 includes() / 子串 grep 会误判(本批实测 6 例:refine_session ⊂ "auto_refine_session" · finish_session ⊂ "finish_photo_session" · video_profile_memory ⊂ "video_profile_memory.json" · recognize_image ⊂ recognize_image_timeout · remember_video_profile ⊂ remember_video_profile_form/_domain)⇒ 必须用引号定界字面量 / 整词边界,且任何「0 命中」结论都要点名仪器并先自检(计划 §收口三)。代价:本批因此额外删除 59 条测试(计划原预测 16 条),每一笔都由控制方逐条裁决 —— 见批 1 计划「收口回写」节
未确认档 3.22:1 被误用 有人把关键信息放进未确认档 4.3 三条规则写进规范 + review 检查
剪报底纹标错来源 历史数据缺来源标记时「猜」 4.4 防御性规则:无标记即无标记,绝不猜
视图层内存膨胀 每对象多视图全部常驻 7.3 惰性挂载契约 + 重挂载恢复 scrollTop
编辑丢稿 切视图时 RichEditorView 卸载 flushSave 先行,失败则阻断并提示
包体继续膨胀 引入 GSAP 与新视图 批 2 先治理;GSAP 独立 chunk 懒加载。批 2 已落(2026-09-12):vendor-gsap 精确包名槽位(app/src/build/manualChunks.ts)+ 单测三条(含反例)+ 首屏预算守卫 scripts/check-bundle-budget.mjs(首屏 92.79 kB,exit 0)。⚠️ 该守卫未接 .husky/pre-commit / CI(批 2 有意不接:它要跑一次真实构建并写 app/dist/,塞进共享的 pre-commit 会让并行期每次提交都依赖「此刻工作树可构建」)⇒ 接线归属批 8,且接线时必须一并修它两个已实测缺口(嵌套输出路径静默假绿 · 把「非首屏」说成「仅动态可达」的假文案)。批 6 接入 GSAP 必须用 import()。另注:本行是「批 2 先治理」的兑现 —— 但治理的是首屏,dist 总量只降 4,415 B,安装包体积由批 8 另裁。
动效过度导致不适 「充满动效」纲领被无限放大 三档强度 + 系统 reduced-motion 优先 + 环境层幅度上限 2%
红线名存实亡 E1/E2 两处例外被当成先例 例外须各自登记原因/影响面/回滚;新增例外需再次裁决
[需真机确认] 项 审计标注的运行态未知项(window.confirm 行为、录制中删会话、任务勾选是否 bump updated_at 等) 相关批次开工前先真机验证

🔻 批 5 收口就地加注 · 本表三条风险的实际兑现度(2026-09-12,上表原文一字未改):

  • 视图层内存膨胀(缓解写「7.3 惰性挂载契约 + 重挂载恢复 scrollTop」) —— 契约已落:默认视图常驻(resident-probe 锚点,SessionViewHost.test.tsx 5 处)、非默认视图模块级惰性 + 切走卸载(H2/F6 各 11/11,反向对照 T10-M2c/T14-M6 各自唯一红);重挂载恢复 scrollTop 的真实性不可验(jsdom 的 scrollingElement 不滚动 —— T17 已留 probe-jsdom-scroll*.json 证明探针本身有效)⇒ 只登记,批 8。惰性挂载的「运行时内存效果」亦不可测(字节面可测:懒 chunk 22 → 31 / 583.98 kB,内存面不可测)。
  • 编辑丢稿(缓解写「flushSave 先行,失败则阻断并提示」) —— 已落且有牙:F1(reject ⇒ 值未变 + 目标未挂载 + role="alert" + 记忆未写)· F2(resolve ⇒ 切换发生)· F3(非编辑态直通)· F4(阻断全过程 0 个 toast 节点),四条 it 在 NotesReadingColumn.views.test.tsx 11/11 passed;反向对照 T14-M1(把 catch 改成继续)⇒ 唯一红 = F1。未消除的路径显式登记:RichEditorView 内的 Ctrl+E / 完成按钮仍不阻断(今天就是 fire-and-forget,非本批引入的回归)⇒ 批 8。
  • 包体继续膨胀(缓解写「批 2 先治理;GSAP 独立 chunk 懒加载」) —— 批 5 实测:首屏 100.49 kB < 200 kB(余量 99.52 kB;Δ = +188 B gzip 相对批 4 收口基线);懒 chunk 31 个 · 583.98 kB gzip(不计入预算);三处可见/观感代价登记(拆件顺带迁移:字阶 11 → 11.5 · 13 → 12 · #9ca3af → ink-3 · 圆角 6 → 5 落 control 档)—— 出处见 docs/versions/v0.22.md 批 5 节「诚实代价」。GSAP 本批仍未装(零新增依赖、manualChunks 零改动、@keyframes 零新增)⇒ 批 6 接入时必须用 import(),判据见批 2 计划 Task 5。

14. 文档与提交

本设计文档:docs/superpowers/specs/2026-09-11-frontend-redesign-design.md

同类需同步更新的文档

文档 动作
docs/product/ui-ux-system.md · docs/product/theme.md 回写为目标态(C+D、四层动效、三档强度、断点与窗口)—— 属 AGENTS.md §10「额外审查文件」。批 0-D 已落:--ed-shadow-card → --ed-shadow-1/2 的命名关系与定值 · 「L1 原语层」章节(§十一:10 类原语清单/消费场景/禁止事项 · 交互态契约 · 动效接缝与 reduced-motion 承诺 · token 消费纪律与两条用色禁令) · 「剪报底纹上只用 ink-3 及更深」规则 · --due 第二次对比度修正。四层动效 / 三档强度 / 断点与窗口 / §五§十 的旧参数仍留批 8(T1 已登记:§十 tokens 代码块整体是旧版,与 ADR-032 不一致,未顺手重写)
docs/adr/ADR-032..035 新建 4 条 —— ADR-032 ✅ · ADR-033 ✅(批 0-D)· ADR-034/035 顺延至批 3 / 批 6。
⚠️ 2026-09-12 批 3 收口登记(未做,需裁决):ADR-034 在批 3 未写(docs/adr/ 实盘最高仍是 ADR-033,已当场列目录复核)。批 3 的壳层决策全部落在代码与守卫里(shell/ 四个纯数据模块 + TopBar/CommandPalette/ShellFallback + 三组守卫),A1–A7 七处控制方裁决散在批次台账(.superpowers/**,不入库)⇒ 这些决策今天没有 durable 的 ADR 载体。⇒ 建议归属:批 4 开工前补写 ADR-034(壳层:导航注册表 / 列契约 / 断点 / 窗口尺寸 / 溢出两级),或并入批 8 的治理收口;由控制方裁决,本行只登记不擅自补。
↳ 上格的就地加注(批 4 收口,2026-09-12;原文一字未改) ✅ 批 4 已补写 docs/adr/ADR-034-l2-shell-navigation-and-column-contract.md —— 控制方 2026-09-12 14:20 裁决「批 4 一开工就先补写」,落在批 4 计划同一个提交 4905d4d8(docs(plan): 批 4 原语迁移实施计划(19 任务),2 路径 = 计划 + ADR-034);随后 783a62ae 按 T0 对码给它补 5 条加注 + 1 个新守卫对(shellReset.test.ts / columnKeys.freeze.test.ts)(+27/−0,正文一字未改),a25ed2c6 再补「⑥ 钉单源 + 回写 B15 授权」(+5/−4)。ADR-034 已进 docs/adr/README.md 索引(docs-check 的「索引覆盖完整 ✅」为此项)。⇒ 剩余:ADR-035 = L4 动效仍顺延(批 6);批 4 未新增任何 ADR。⚠️ 一并登记的后续项:ADR-034 的 §登记 :115 两条(会话详情头改粘性 / 笔记工具栏三层合并)批 4 的 19 个任务无人承接 ⇒ 控制方裁决「由 T18 收口承接或按批次归属登记」⇒ T18 的处置:登记为批 5(视图层)follow-up(不是批 4 的交付),见批 4 计划 §收口回写 §七。
docs/adr/ADR-010-gap-filling-ai.md 修订为退役 —— ✅ 批 1 已落(2026-09-11 裁决 / 2026-09-12 落地):状态转已废弃 + 文内「退役修订」节,索引与交叉引用同步(保留文件,不删)
docs/standards/line-limit-exemptions.md 纠偏 + 新拆文件登记 —— ✅ 批 1 收口态:--full exit 0,>600 0 · 301–600 档 123 · 登记条目 123;app_commands.rs 行的人工理由列已按实测更正为「312 条」。⚠️ 该表末尾的人工引用块(app/src/ui/primitives/** 42 个 ≤300 行文件)不在生成器视野内,本批已按 countLines() 逐值复核(42/42 一致),改它不会被门禁发现、也不会被门禁保护。批 2 收口(2026-09-12):app/src/App.tsx 439 → 519 行(Task 6/7/8 三次改同一文件),登记值已与实测同步 —— 收口时 node scripts/line-limits.mjs --write 无 diff(前序任务已刷新),故批 2 的收口提交不含本文件;app_commands.rs 行实测 478 与登记值逐字相等(批 1 的「503 → ~478」预算已在其计划「收口回写」节结清,无矛盾)。
docs/standards/ 新增动效规范章节(现对动效零命中)
docs/product/requirements-pool.md REQ-201 修正(update_fragment_group 实际无调用方)· 新增 REQ 登记
docs/versions/v0.21.md(或 v0.20.14) 本系列交付记录 —— 实际落点:docs/versions/v0.22.md(版本归属理由见该文件 §版本归属说明);批 0-A / 0-B / 批 1 已落(批 1 = 「删除批」节,含交付/验收/规格漂移纠正/过程中纠正的计划错误/未做登记);批 2 ✅ 已落(2026-09-12) = 「批 2 · 包体治理」节(同七段结构 + 瓶颈清单指针)· 批 3 ✅ 已落(2026-09-12) = 「批 3 · 壳层落地」节(同七段结构;含 tauri.conf.json 的 §10 额外审查记录 —— T4 已执行,之前只留在 gitignored 的报告里,现已 durable)
↳ 上格的就地加注(批 4 收口,2026-09-12;上格原文一字未改) 批 4 ✅ 已落(2026-09-12) = 「批 4 · 原语迁移」节(同七段结构:交付 / 验收(两条 + 八门禁终态)/ 规格漂移纠正 / 过程中纠正的计划错误(①–⑲)/ 未做登记(逐条带归属批次)/ 诚实代价 / 提交清单 4905d4d8^..<收口提交> 含左端点 = 49 个提交)。该节自带的首屏账、五类中间态与「只有接缝、没有纲领」声明是本批最需要下游读到的三块。
↳ 上格的就地加注(批 5 收口,2026-09-12;上格原文一字未改) 批 5 ✅ 已落(2026-09-12) = 「批 5 · 视图层样板」节(同七段结构:交付 / 验收(三条硬约束 + 八门禁终态)/ 规格漂移纠正 / 过程中纠正的计划错误 / 诚实代价 / 未做登记(逐条带归属批次)/ 提交清单 ad9d80d2^.. 至收口提交 含左端点 = 31 个提交)。该节自带的三块(三条硬约束的判据与变异体出处 · 首屏 100.49 kB + 懒 chunk 31 个的逐块台账 · B9 的「只有接缝、没有纲领」声明)是本批最需要下游读到的。ADR 面:批 5 未新增任何 ADR(docs/adr/ 实盘最高仍是 ADR-034)⇒ docs/adr/README.md 索引本批零改动;ADR-035 = L4 动效纲领与引擎仍顺延批 6;批 5 的 ADR 动作只有就地加注(ADR-033 §1/§4/§登记 三段 + ADR-034 §登记 一段,只加注、不改结论)。

🔻 批 6 收口就地加注 · 本表四行的落地(R6.7 · R6.9 · R14.7;2026-09-13,上表原文与批 4 / 批 5 加注一字未改):

  • 🔴 :836 的「docs/standards/ 新增动效规范章节(现对动效零命中)」= 批 6 已做(批次归属的就地消歧):落点 = docs/standards/motion.md(批 6 T1,提交 bffed928;已进 docs/standards/README.md 索引)。⚠️ 同一件事在本行与 v0.22.md:35 的「批 8 治理收口」行两说(:35 逐字写「新增动效规范章节」属批 8)⇒ 两处按本加注读:规范章节本身已在批 6 落地;留批 8 的是 docs/product/ui-ux-system.md / theme.md 的四层动效 / 三档强度回写(批 6 未写它们,progress.md §一 的「🟡 待裁」仍在)。
  • docs/adr/ADR-032..035 行:ADR-035 ✅ 已写(批 6 T2,提交 97295d64;文件 docs/adr/ADR-035-l4-motion-grammar-and-engine.md,含**「后端契约例外」节并在批 6 收口由 T35 定稿回填)⇒ 本行「ADR-034/035 顺延至批 3 / 批 6」两件均已兑现(ADR-034 批 4 补写 + ADR-035 批 6 落地)。docs/adr/README.md 索引本批 +1 行**(T2)并在 T35 更正状态列措辞。
  • docs/adr/ADR-010-gap-filling-ai.md 行:批 1 已落(退役修订),批 6 零动作。
  • docs/versions/v0.21.md(或 v0.20.14)行:批 6 ✅ 已落(2026-09-13) = 「批 6 · 动效系统」节(同七段结构:交付 / 验收(八门禁终态 + 验收面逐条兑现度)/ 规格漂移纠正 / 过程中纠正的计划错误 / 诚实代价 / 未做登记(逐条带归属批次)/ 提交清单 ac504b06^..975b3285 含左端点 = 77 个提交)。
  • docs/adr/ADR-019 缺号:🔴 不补、不复用,只登记(docs/adr/README.md 的「编号规则」逐字「编号一经分配不再复用」);批 6 实盘最高 ADR = ADR-035。
  • ⚠️ docs/product/ui-ux-system.md / theme.md 的四层动效 / 三档强度:本任务不写(§14 上格逐字仍留批 8)⇒ 只在 v0.22 批 6 节的「未做登记」里点名。

🔻 批 8 终态就地加注 · 本表各行在批 8 的落地(2026-09-13 · T21;上表原文与批 4 / 批 5 / 批 6 加注一字未改):

  • 🔴 docs/versions/v0.21.md(或 v0.20.14)行:批 8 ✅ 已落(2026-09-13) = 「批 8 · 治理收口与三条主线」节(同七段结构:交付 / 验收(三条主线兑现度 + 八闸终态)/ 规格漂移纠正 / 过程中纠正的计划错误(E8-1 … E8-25)/ 诚实代价 / 未做登记(逐条带归属批次)/ 提交清单 5852cdc4^..396b5e5c 含左端点 = 63 个提交)。🔴 该节自带的三块(懒侧基线的两个时点与「重冻后总量 == 实测 ⇒ 零余量」的结构性发现 · 八闸第 ⑤ 闸的懒侧写 ⏭ 未判 · T15 / T15b 的两条实测边界)是本批最需要下游读到的。
  • 🔴 docs/standards/ 行(「新增动效规范章节(现对动效零命中)」):批 6 已落(docs/standards/motion.md)⇒ 🔴 批 8 未新增任何 docs/standards/ 章节;批 8 在该目录的动作只有就地加注与数值同步(line-limit-exemptions.md 的行数值与「312 条」散文 · performance.md 的「懒侧执行者缺口三段式」加注 · motion.md 的判据纪律小段 · testing.md 的门禁稳定性纪律)。
  • 🔴 docs/product/ui-ux-system.md / theme.md 行:批 0-D 已落原语层与阴影命名;🔴 「四层动效 / 三档强度 / 断点与窗口 / §五§十 的旧参数」批 8 仍未写 ⇒ 登记为下一批输入(批 8 只回写了「审校模式」那一节)。
  • docs/adr/ADR-032..035 行:批 8 未新增任何 ADR(实盘最高仍是 ADR-035);批 8 的 ADR 动作 = 两处前向指针加注(ADR-034 / ADR-035 各纯插入 1 行、删除列 0,指令落 5018c7ab)。
  • docs/adr/ADR-010-gap-filling-ai.md 行:批 1 已落(退役修订),批 8 零动作。
  • docs/standards/line-limit-exemptions.md 行:批 8 的终态 = node scripts/line-limits.mjs --full exit 0「>600 硬限 0(棘轮内)· 301–600 档 121 · 登记条目 121」(T21 亲测,时点 2026-09-13T17:32+08:00)。
  • ⚠️ docs/product/requirements-pool.md 行(「REQ-201 修正(update_fragment_group 实际无调用方)· 新增 REQ 登记」):🔴 批 8 未动它(update_fragment_group 的接线由批 7 的 T23 补齐)⇒ 登记。
  • 🔴 本表未列、批 8 新增的一行(归档面):docs/tech-debt/ 按用户裁决 U3 = a 归档到 docs/archive/2026-09-13/(335a6155 · 630d2bfa)⇒ docs-check 的扫描 / 检查数由 282 / 182 变为 284 / 181(282→284 / 182→181,已逐字登记在 docs/archive/2026-09-13/README.md 与 docs/archive/README.md;T21 亲测现读数 = 284 / 181)。

提交策略:批 0 拆成 13 个原子提交(3 条 ADR → 豁免表纠偏 → 拆 NotesPage/NoteListView → 拆 SessionDetailPanel → 拆 ClassroomPage → token 层 + z-index → 图标集 → 原语第一批 → 第二批 → 第三批 → 回写规格文档);后续每批各自原子提交,遵循 Conventional Commits。


附:本设计的三个判断(供复审时质疑)

  1. 缺的不是「更好的组件」,是缺失的两层 —— 原语层与视图层。131 个现存组件里 0 个死组件,职责基本清晰。
  2. 「一个对象只有一种样子」是最大的产品税 —— 9 个域里 8 个如此,而体系域用它自己证明了多视图的价值。
  3. 动效不是装饰,是状态的直接可视化 —— 四档墨度显示「机器有多确定」,剪报底纹显示「这句从哪来」,刻度生长显示「记忆有多牢」。这三样别的笔记软件都没有。