Files
contentm_agent/执行会话/目标-根据1a需求文档生成MCN短视频整合营销-d85687/ui/3b-实测记录.md
T
WorkBuddy df56c2c137 初始化提交:contentm_agent 工作区全量快照
内容分四块:
1、产品规划产出 —— MCN 短视频整合营销工作台的①段五份(1a 需求/1b 竞品/1c 画像/1d 策略/1e 场景)、②段两份(2a 功能/2b 布局)、③段界面(DESIGN.md 契约与令牌表 + mcn-workbench.html 原型 + 实测/会诊/审查三份 + 23 张闸门截图)。
2、开源竞品调研 —— 5 个内容工作台项目的取证原始件与 1b 系列分析文档。
3、参考资料 —— 竞品视频抽帧 1145 张 + 2 个源视频 + 功能点截图。
4、机制侧 —— 协作脚本与状态台账、工作区记忆日志、抽帧/OCR 脚本。

.gitignore 只排运行时日志、脚本备份副本与一次性探针输出,其余按原样入库。
2026-10-08 08:13:02 +08:00

22 KiB
Raw Blame History

3b · 实测记录(③段界面交互)

落点:执行会话/目标-根据1a需求文档生成MCN短视频整合营销-d85687/ui/3b-实测记录.md 判据源:stage-delivery/SKILL.md §3b/§供给;references/execution-runbook.md §3.5、§5.3、§六;references/layouts-tooling.md §1、§5.5 交付物:ui/mcn-workbench.html(单文件自包含原型)+ ui/DESIGN.md(规范)

〇、声明制(四字段)

字段 内容
当前段与子步 ③段 / 3b 原型构建 + 3b 实测(实测未过不进 3c)
主干与挡位 供给=oil-ui-pro(方法主线)+ 本段自带样式库 design-system-tiaoyue(定调与令牌)/ 挡位 标准
依据文件 DESIGN.md(本段契约)、prd/2b-界面布局.md(冻结骨架)、prd/2a-产品功能.md(F1–F14)
产出落点 ui/mcn-workbench.html、ui/_tools/(三个可复跑脚本)、ui/_gate_shots/(23 张视口帧)

一、怎么复跑(三条命令,全部只读原型)

PY="E:/ProgramData/.workbuddy/binaries/python/versions/3.13.12/python.exe"
cd "…/ui"
$PY _tools/cap.py --mode dom  --route selftest --viewport 1440x900 --out _scratch/st.html   # 取自检输出
$PY _tools/mutate_test.py                                                                  # 变异对照
$PY _tools/check_skeleton.py                                                               # 骨架冻结闸门

cap.py 对外只收 --viewport;它内部按模式补偏移量(原因见 §五 的通道口径一节)。_scratch/ 是跑出来的中间件、目录不存在时脚本会自建,看完随手删掉不影响任何结论;交付目录里只留脚本、截图与四份文档。

二、自检 81 项(1440×900 主视口,PASS 81 / FAIL 0)

自检是原型自带的:#/selftest 路由会在真实场景里逐个点过每个可点元素。全文落盘如下,⛔ 不是摘要。

自检结果:PASS 81 / FAIL 0

PASS  路由 #/p/p1 渲染出 5 个顶层节点
PASS  路由 #/p/p2 渲染出 6 个顶层节点
PASS  路由 #/p/p3 渲染出 4 个顶层节点
PASS  路由 #/p/p4 渲染出 6 个顶层节点
PASS  路由 #/p/p5 渲染出 5 个顶层节点
PASS  路由 #/p/p6 渲染出 4 个顶层节点
PASS  路由 #/p/p7 渲染出 5 个顶层节点
PASS  路由 #/p/p8 渲染出 4 个顶层节点
PASS  六态 × 八页 = 48 个组合,全部渲染出内容
PASS  侧栏二级页项 8 个,与八页一致
PASS  清单里每个 ID 都在页面上出现过(75 个,无悬空声明)
PASS  页面上每个 data-act 都在清单里(共 703 次出现,无未登记的可点元素)
PASS  清单里 71 个 ID 都真驱动过并给出可见反馈,0 个死控件(22 个场景,每个实例各重置一次)
PASS  当刻被禁用的控件 4 个(有 disabled 属性:按契约在特定条件下不可点):P4-03, P5-06, P6-03, R-02
PASS  清单里每个 ID 都至少被驱动过一次(无「从未跑过」的条目)
PASS  侧栏 8 项逐个点过,每一项都跳到了自己标的那一页
PASS  五处「输入后才可用」的联动都通:其中 4 处在空输入时确实是禁用的 → 输入后转可用 → 点了有反应
PASS  工具型纪律:零 .eyebrow / 零 .lead / 零 .hero
PASS  零 @keyframes(工具型不做入场编排)
PASS  骨架冻结:八页 37 个板块的数量与顺序与 ②段 2b「板块清单」逐位一致,⛔ 未增删挪
PASS  结构 ①:八页各一个 h1,标题逐级不跳;每个板块都有文字标题
PASS  结构 ②:字号 12/14/16/18/24px 共 5 档,相邻差 2/2/2/6,正文档 14–16 在用
PASS  结构 ④:逐页查过,首屏同屏主档按钮 ≤1 个;本页实底按钮 1 种
PASS  交互态与可读性::hover / :focus-visible 都在且没被 outline:none 抹掉;tabular-nums、prefers-reduced-motion、防溢出三件套齐全
PASS  阴影:面板/卡片都用多层叠层,无单层死黑重阴影
PASS  间距:flex/grid 的 gap 取值全部落在登记刻度内(9 个刻度)
PASS  浮层「随手记」中性关闭出口 = 1(右上 ×)|另有决策型关闭 1 个(R-03);遮罩是「点层外」通道,不计作出口
PASS  浮层「随手记」打开后焦点在层内(当前=X-close)
PASS  浮层「随手记」连按 8 次 Tab,焦点始终锁在层内
PASS  浮层「随手记」Esc 能关
PASS  浮层「随手记」关掉后焦点回到触发件 G-06
PASS  浮层「随手记」点遮罩(层外空白)能关
PASS  浮层「全局检索」中性关闭出口 = 1(右上 ×)|另有决策型关闭 0 个;遮罩是「点层外」通道,不计作出口
PASS  浮层「全局检索」打开后焦点在层内(当前=X-close)
PASS  浮层「全局检索」连按 8 次 Tab,焦点始终锁在层内
PASS  浮层「全局检索」Esc 能关
PASS  浮层「全局检索」关掉后焦点回到触发件 G-10
PASS  浮层「全局检索」点遮罩(层外空白)能关
PASS  浮层「两处假设说明」中性关闭出口 = 1(右上 ×)|另有决策型关闭 0 个;遮罩是「点层外」通道,不计作出口
PASS  浮层「两处假设说明」打开后焦点在层内(当前=X-close)
PASS  浮层「两处假设说明」连按 8 次 Tab,焦点始终锁在层内
PASS  浮层「两处假设说明」Esc 能关
PASS  浮层「两处假设说明」关掉后焦点回到触发件 G-08
PASS  浮层「两处假设说明」点遮罩(层外空白)能关
PASS  浮层「资产选择器」中性关闭出口 = 1(右上 ×)|另有决策型关闭 0 个;遮罩是「点层外」通道,不计作出口
PASS  浮层「资产选择器」打开后焦点在层内(当前=X-close)
PASS  浮层「资产选择器」连按 8 次 Tab,焦点始终锁在层内
PASS  浮层「资产选择器」Esc 能关
PASS  浮层「资产选择器」关掉后焦点回到触发件 P4-04
PASS  浮层「资产选择器」点遮罩(层外空白)能关
PASS  浮层「版本列表」中性关闭出口 = 1(右上 ×)|另有决策型关闭 0 个;遮罩是「点层外」通道,不计作出口
PASS  浮层「版本列表」打开后焦点在层内(当前=X-close)
PASS  浮层「版本列表」连按 8 次 Tab,焦点始终锁在层内
PASS  浮层「版本列表」Esc 能关
PASS  浮层「版本列表」关掉后焦点回到触发件 P4-07
PASS  浮层「版本列表」点遮罩(层外空白)能关
PASS  浮层「发布二次确认」中性关闭出口 = 1(右上 ×)|另有决策型关闭 1 个(P5-10);遮罩是「点层外」通道,不计作出口
PASS  浮层「发布二次确认」打开后焦点在层内(当前=X-close)
PASS  浮层「发布二次确认」连按 8 次 Tab,焦点始终锁在层内
PASS  浮层「发布二次确认」Esc 能关
PASS  浮层「发布二次确认」关掉后焦点回到触发件 P5-08
PASS  浮层「发布二次确认」点遮罩(层外空白)能关
PASS  浮层「核销二次确认」中性关闭出口 = 1(右上 ×)|另有决策型关闭 1 个(CHECKOUT-CANCEL);遮罩是「点层外」通道,不计作出口
PASS  浮层「核销二次确认」打开后焦点在层内(当前=X-close)
PASS  浮层「核销二次确认」连按 8 次 Tab,焦点始终锁在层内
PASS  浮层「核销二次确认」Esc 能关
PASS  浮层「核销二次确认」关掉后焦点回到触发件 P8-03
PASS  浮层「核销二次确认」点遮罩(层外空白)能关
--- 数值取证(布局视口 1440 × 900 ;window.innerWidth 1440)---
PASS  对比度 --warn 对 --surface = 5.93:1(契约 ≥4.5:1)
PASS  对比度 --accent-text 对 --surface = 4.76:1(契约 ≥4.5:1)
PASS  对比度 --ink-faint 对 --surface = 3.55:1(契约 ≥3:1)
PASS  强调色填充面积占比:八页最高 创作台 = 0.52%(1 个填充块,契约 ≤5%;不含 3px 左侧标条 / 描边 / 文字色)
量尺寸时 scrollY = 0(P1)
PASS  量尺寸前页面停在滚动零点(P1):rect 取到的是相对视口的真值
PASS  P1 账号矩阵:首屏从 214px 起,行距 67px,一屏装得下 10 行(契约 ≥6 行/900px 高,实测宽 1440)|示意数据 5 条 ⇒ 实际可见 5 行(数据用尽,非被截断)
PASS  P1 账号矩阵行高齐:67–68px,差 ≤2px(没有哪一行被状态标或长文字撑高)
PASS  P1 实际在用字号档 5 档:12px / 14px / 16px / 18px / 24px(契约 ≥3)
PASS  视觉层间距 32px / 块内相邻行分隔 1px(发丝线,行本身不套框)⇒ 比值 32 ≥2(4 个视觉层)
P1 各块落位(顶 / 高):今日待处理@149+360 · 账号矩阵列表@149+417 · 异常值提示@599+215 · 本月表现最好的一条@845+122 · 全局检索与随手记@1000+113(视口高 900;并排的两块顶相同)
量尺寸时 scrollY = 0(P2)
PASS  量尺寸前页面停在滚动零点(P2):rect 取到的是相对视口的真值
PASS  P2 对标内容列表:首屏从 402px 起,行距 67px,一屏装得下 7 行(契约 ≥6 行/900px 高,实测宽 1440)|示意数据 5 条 ⇒ 实际可见 5 行(数据用尽,非被截断)
P2 各块落位(顶 / 高):对标监控概览@165+139 · 对标内容列表@336+417 · 评论区洞察@785+108 · 选题库@925+330 · 灵感速记@1287+113
PASS  横向溢出 = 0(scrollWidth 1440 = clientWidth 1440)
PASS  八页逐页查横向溢出:全为 0(最差 p1)
(附)P1 行高 67/67/67/68/67px · P1 按钮高度集合 32px · 量时 scrollY=0 · 顶栏高 69px · 导航占 1105px(column)· 主区内衬上 24px · innerWidth 1440 − clientWidth 1440 = 0px(窗口滚动条宽度)
控制台报错:0 条

截图:_gate_shots/selftest-1440.png。

三、变异对照 13 / 13(证明上面那串 PASS 不是恒绿)

一个永远 PASS 的自检等于没有自检。做法:复制原型 → 注入一处已知的假病 → 再跑一次自检 → 要求它报红、且红在预期那一条上。

基线(未注入):PASS 81 / FAIL 0
✅ M1  死控件(掏空 P2-01 实现)                ⇒ PASS 80/FAIL 1 |红在 P2-01
✅ M2  未登记的可点元素(塞一个 ZZ-99)          ⇒ PASS 79/FAIL 2 |红在「未登记的 data-act」
✅ M3  悬空声明(清单里加一个页面没有的 ID)      ⇒ PASS 79/FAIL 2 |红在「声明了但页面里没有」
✅ M4  骨架漂移(P3 多塞一块)                  ⇒ PASS 80/FAIL 1 |红在「骨架与②段不一致」
✅ M5  对比度越界(--ink-faint → #c9c7c0)      ⇒ PASS 80/FAIL 1 |红在「对比度 --ink-faint」
✅ M6  点击无反馈(go() 退回只改 hash)          ⇒ PASS 79/FAIL 2 |红在「驱动了没反应」
✅ M7  行高不齐(放开行内换行,768x900)          ⇒ PASS 79/FAIL 2 |红在「行高不齐」
✅ M8  密度退化(行内衬 12px → 36px)            ⇒ PASS 79/FAIL 2 |红在「低于契约 6 行」
✅ M9  量尺寸时页面不在零点(量前先滚 300px)      ⇒ PASS 80/FAIL 1 |红在「被滚到」
✅ M10 行分隔线被关掉(退回 .list-row:first-child)⇒ PASS 80/FAIL 1 |红在「行分隔线根本没画出来」
✅ M11 浮层没有遮罩                             ⇒ PASS 72/FAIL 9 |红在「没有遮罩」
✅ M12 关掉浮层后焦点不还回去                    ⇒ PASS 74/FAIL 7 |红在「关掉后焦点没回到」
✅ M13 浮层多出一个中性关闭出口(再加一颗 ×)      ⇒ PASS 74/FAIL 7 |红在「中性关闭出口 = 2」

变异对照:13 / 13 通过
⇒ 13 处假病全部被自检抓住,且基线回绿:这套判据不是恒绿的。

⚠️ M9 换过一次。原来那版是「拿掉 applyScene 末尾的滚动复位」,但实测它已经观测不到差别了:重绘 #view 时浏览器把滚动自然夹回 0,量出来的数确实干净 ⇒ 那条变异变成了一条抓不到东西的假变异。改成直接注入「量之前把页面滚下去」,也就是这条守卫真正声称要拦的那件事。

四、骨架冻结闸门(正向 + 双向变异)

把②段 prd/2b-界面布局.md §二 当权威读,与原型 FROZEN 表逐页逐位比(数量相等、顺序相等、名字互为前缀)。

正向:PASS  骨架冻结:八页 37 个板块,②段 2b-界面布局.md §二 与原型 FROZEN 表逐页逐位一致
反向变异 1(改原型 p3 多一块):p3 板块数 ②段 4 ≠ 原型 5
反向变异 2(改②段副本 p1 第 2 块名):p1 第 2 块 ②段「账号矩阵一览」≠ 原型「账号矩阵列表」

两条反向变异改的都是副本(_probe/2b-mut.md),权威件 prd/2b-界面布局.md 全程只读。为了事后能复核这件事,这里钉一条指纹:

prd/2b-界面布局.md   md5 = 1dcc69f952df97a394481658e84d90b2   15036 字节  mtime 2026-10-08 02:30

✅ 复核方式:md5sum ../prd/2b-界面布局.md,与上面比。一致 ⇒ ③段确实没动过冻结骨架。 (这条指纹是删 _probe/ 之前补登的 —— 备份删掉后,只剩这一个数能证明骨架没被污染,所以先写进正文再清临时件。)

五、多视口密度核对(契约:一屏可见行数 ≥6 / 900px 高,layouts-tooling.md §1 原文,⛔ 不分档)

视口 自检 P1 账号矩阵 P2 对标列表 顶栏高 导航 结论
1440×900 PASS 81/0 首行 214px,10 行 首行 402px,7 行 69px 1105px(竖栏) ✅
1280×900 PASS 81/0 首行 216px,10 行 首行 403px,7 行 69px 竖栏 ✅
1024×900 PASS 81/0 首行 495px,6 行 首行 402px,7 行 69px 竖栏 ✅
768×900 PASS 81/0 首行 495px,6 行 首行 402px,7 行 69px 竖栏 ✅
641×900 PASS 80/1 首行 535px,5 行 ❌ 首行 442px,6 行 109px(折两行) 竖栏 缺口见 §六
640×900 PASS 80/1 首行 532px,5 行 ❌ 首行 439px,6 行 61px 53px(横条) 缺口见 §六
500×844 PASS 79/2 首行 572px,4 行 ❌ 首行 479px,5 行 ❌ 101px(折两行) 53px(横条) 缺口见 §六

边界是量出来的,不是我定的:视口 ≥768px 时顶栏单行、导航竖排,一屏 6–10 行;≤767px 时顶栏折成两行(641px 实测 109px)、≤640px 导航变标签栏,首屏被页面外壳吃掉,容量掉到 4–5 行。行本身没变肥 —— 行高 67px、各行差 ≤2px、块内分隔 1px,这几条在任何视口都 PASS。

反事实(把 P1 前两块强制落回单列,1440×900):账号矩阵列表@530+413 ⇒ 首行顶 530px、容量 floor((900−530)/67) = 5 行,够不着 6 ⇒ 并排是密度判据逼出来的,不是审美选择。

通道口径(本轮实测发现,已修)

同一台机器、同一个 --window-size,Chrome 两条通道给出的视口不是同一个:

--window-size=656,995 ─┬─ --dump-dom   ⇒ 视口 640 × 900  (mq<=640 true)
                       └─ --screenshot ⇒ 视口 656 × 995  (mq<=640 false)

量这件事用的探针页只有 18 行,为了让它跟着报告一起活下去(临时目录已清),源码附在下面 —— 想自己复核就把这段存成 _scratch/vp.html,两种模式各跑一次同一尺寸,读第 1、3 行:

<!DOCTYPE html><html><head><meta charset="utf-8"><title>vp</title>
<style>body{margin:0;font:700 34px/1.5 monospace;background:#fff;color:#111}</style></head><body>
<div id="o">…</div>
<script>
function r(){
  document.getElementById('o').innerHTML =
    'innerW '+window.innerWidth+'<br>'+
    'innerH '+window.innerHeight+'<br>'+
    'clientW '+document.documentElement.clientWidth+'<br>'+
    'clientH '+document.documentElement.clientHeight+'<br>'+
    'outerW '+window.outerWidth+'<br>'+
    'mq<=640 '+matchMedia('(max-width:640px)').matches+'<br>'+
    'mq<=1024 '+matchMedia('(max-width:1024px)').matches+'<br>'+
    'dpr '+window.devicePixelRatio;
}
r(); addEventListener('resize',r);
</script>
</body></html>

⇒ 截图通道原样等于视口,dump-dom 通道要补回被扣掉的 16 / 95。cap.py 已按模式分开算,并且每次截图后直接从 PNG 的 IHDR 读真实像素跟目标视口比,不等就当场报红 —— 这条守卫是补回来的,因为本轮之前所有截图都比标称大 16×95(「1440×900」实际是 1456×995)。

六、本轮修掉的真缺陷(每条:现象 → 根因 → 证据)

# 现象 根因 证据
1 整份设计系统在屏幕上失效 样式表顶部注释里 --t-*/--raw-* 的 */ 提前闭掉注释,吞掉整个 :root 令牌块 修复后对比度取证才量得出来
2 全表行分隔线一直没画出来 .list-row:first-child{border-top:0}:每行都包在 <li> 里,于是每行都是自己 <li> 的 first-child,borderTopStyle 全是 none 变异 M10;另修了判据除数为 0 时恒 PASS(比值 Infinity)
3 浮层不浮 #layer-root 与 .layer 都没有定位样式,浮层渲染在文档流末尾,得滚到页尾才看得见 改为遮罩 fixed inset:0 + 层本体 fixed 居中
4 行高随文字长短乱跳(66/82/100px) 状态标放在操作列把 auto 列撑宽,只有那一行折行 状态标移到名称旁;再加「行内每段不许换行 + 省略号」;判据新增「行高齐」
5 自检在窄视口给出假 PASS「装得下 10 行」 驱动几百次点击后浏览器为聚焦元素自动滚动,rect.top 变负,容量公式虚高 加「量尺寸时页面在滚动零点」守卫 + 变异 M9
6 手机导航「既不是一条、也没滚动」,占 161px .sidebar 只给了 flex-direction:row,可导航分组挂在 <nav id="nav"> 里,而 #nav 一条样式都没有 ⇒ 仍是块级容器,4 个分组照旧竖堆 补 #nav{display:flex;flex-flow:row nowrap} + 整条链 flex:0 0 auto;161px → 53px
7 顶栏「演示 · 账号」折成两行,顶栏被顶到 70px .topbar-label 没有 white-space:nowrap 独立评审第一眼点出;补 nowrap 后顶栏 69px
8 截图尺寸全部比标称大 16×95 cap.py 对两条通道用了同一个偏移量 §五「通道口径」;已加 PNG 尺寸守卫
9 两个二次确认浮层其实各有两个可见关闭控件,判据却报「出口 = 1」 判据只数 .layer-close 这个类名,把可见的「取消」从眼皮底下藏掉了;核销层的「取消」还复用了 X-close 判据改为「中性出口只认 X-close」+「决策型关闭单列报数」;核销取消拆出 CHECKOUT-CANCEL;加变异 M13
10 「今日该发:待发:3 分钟快手菜」冒号连用,评审读成错字 示意数据 today 字段的值里自带冒号,被拼进「今日该发:…」 数据改为空格分隔
11 制作黑话写在界面上(「这里是 X 的 empty 态」) 状态面板上另贴了一句给做原型的人看的说明,而 STATE_COPY 已铺好用户向文案 删掉那句话;按钮改「回到正常内容」
12 手机形态看不出当前在哪一页 横滚标签栏里当前项滚在视野外 只调 .sidebar 自己的 scrollLeft 把当前项带进视野(⛔ 不碰 window,否则会误触滚动守卫)
13 layerTrigger 放进状态快照 S 点任何元素都会改 S ⇒ 状态快照跟着变 ⇒ 没有一个控件会被判死,死控件判据整条失效 移出 S;变异 M1/M6 当场回归报红
14 浮层焦点与可见性判据全部失效 offsetParent 对 position:fixed 元素恒为 null 改用 getClientRects().length
15 --full(整页长图)静默失效,产出的还是视口帧 无头 CLI 这条通道只能截视口 参数已删除,_full/ 目录一并清掉,不留一个撒谎的开关

七、已记录的偏离(⛔ 不许静默)

# 原要求 实际做法 原因 影响 处置
D1 3b 实测走 browser-harness(SKILL.md §3b) 用「原型自带自检 81 项 + 变异对照 13/13 + 无头 Chrome 23 张视口帧 + 逐条 DOM 取值」等价替代 该通道绑定实时浏览器入口,本会话驱动不了(宿主侧能力,不是本段能修的) 证据链换成可复跑脚本;⛔ 但没有「真人真点」这一层,交互结论全部来自脚本驱动 持续偏离,待用户裁定是否补一次人工走查
D2 D5 终检要 1440x900 与 390x844 两个视口帧(runbook §5.3、§七) 实测到 500×844 为止 Chrome 窗口宽下限 516 ⇒ 视口最小 500;要 390 得走 CDP setDeviceMetricsOverride,本轮没接 手机帧比要求宽 110px 持续偏离;⛔ 报告里一律写「500×844」,不许写成已测 390
D3 对外只留视口帧;_full/ 长图仅自检(runbook §七) 未产出长图 --full 在本机通道上做不到(只能截视口),已把该参数删掉 无 已修
D4 3c 要「外部 GPT 会诊」 见 3c-GPT会诊.md:本会话无对外模型通道,改用隔离上下文的独立评审执行者 没有外部通道可用 与「异厂商外部审查」不是同一条通道 持续偏离,已明示

八、这一步的门槛结论

  • 3a 已过 Gate-1(见 DESIGN.md §四)。
  • 3b 实测:契约视口 1440×900 全绿(PASS 81/FAIL 0);≥768px 宽全绿;≤767px 宽的密度缺口已如实登记(§五、DESIGN.md §五)。
  • 可以进 3c。

九、说人话自评(判据源 humanizer-zh,五维各 10 分)

维度 分 说明
直接性 9 开头就是复跑命令,没有「本次工作取得进展」这类开场
节奏 8 表格 + 短断言为主,句式偏单一,⚠️ 弱项
信任度 10 把改坏的、没测到的、换了通道的都写出来了,没有藏
真实性 9 每条缺陷都带现象、根因与证据(脚本名 / 变异编号 / 实测数)
精炼度 9 15 条缺陷各一行,不重复叙述;自检全文照录是留证需要,不是凑字
总分 45 / 50 门槛 ≥45,压线过;扣分点写在第 2 行