# Friday QA Report

Generated at 2026-04-21T10:39:43.866Z. Only scripted evidence is treated as valid. Legacy screenshot-only evidence is rejected until rebuilt.

Total Rules 433
PASS 433
FAIL 0
Pending Rebuild 0

Item | Rule | Status | Acceptance | Evidence | Summary
--- | --- | --- | --- | --- | ---
P2A-01 | 1 | PASS | H01 · snapshot 调用唯一 · 打开 /home 后 Network panel 只见 1 次 `getHome()`(mock 场景替换成 1 次 `__fridayMock.getState()`)· 无重复 fetch 风暴 | screenshots/P2A-01/h01-home-snapshot-call.json | boot getState=1, navigationEntries=1
P2A-01 | 2 | PASS | H02 · 可见 polling 5s · tab active 时 `advanceClock(5000)` 后触发一次 refetch · 误差 ≤ 200ms | screenshots/P2A-01/h02-visible-polling.json | calls 1 -> 2, latency=2.1ms
P2A-01 | 3 | PASS | H03 · 后台 polling 60s · 切 tab 后 `document.visibilityState === 'hidden'` 时,间隔变 60s · 用 `visibilitychange` 事件验证 | screenshots/P2A-01/h03-hidden-polling.json | hidden calls 1 -> 2, latency=2.3ms
P2A-01 | 4 | PASS | H04 · 三段独立 loading · 任一段数据未到时只显示该段 skeleton(3 个 shimmer row)· 其他段正常渲染 · MUST NOT 整页 spinner | screenshots/P2A-01/h04-loading-sections.json | live skeletons=3, scheduled skeletons=3, approvals=3
P2A-01 | 5 | PASS | H05 · realtime 淡入 · dispatch `run.state_changed(id, 'running' → 'succeeded')` 时对应 card 500ms opacity 0.5 → 1 淡入 + 轻微 translateY(-2px) · 其他 card MUST NOT 重排 | screenshots/P2A-01/h05-realtime-transition.json | animation=run-fade 0.5s, stable=true
P2A-01 | 6 | PASS | H06 · approvals 跳转 · 点"全部查看" · URL 变 `/assistant?tab=approvals` · 若 approval 带 packId · `?packId=xxx` 保留 | screenshots/P2A-01/h06-approvals-navigation.json | href=/assistant?tab=approvals&packId=industry-cross-border-ecommerce
P2A-01 | 7 | PASS | H07 · approvals 0 条时 · 段不渲染标题,显示空态文案 "暂时没有需要你决定的事" · 不留空白块 | screenshots/P2A-01/h07-approvals-empty.json | headerPresent=false, text=暂时没有需要你决定的事
P2A-01 | 8 | PASS | H08 · scheduled 排序 · 按 `nextRunAt` 升序 · 造 6 条数据时只显示前 5 条 + "+1 更多" link 在段底 | screenshots/P2A-01/h08-scheduled-order.json | rows=5, more=+1 更多
P2A-01 | 9 | PASS | H09 · scheduled disabled 置灰 · `automation.enabled === false` 时 row `opacity: 0.35` · next run 文字替换成 "已暂停" | screenshots/P2A-01/h09-scheduled-disabled.json | opacity=0.35, time=已暂停
P2A-01 | 10 | PASS | H10 · snapshot 失败 · `__fridayMock.forceState('p2a-01', 'snapshot-error')` 后页面显示 inline 错误条(段 1 位置)+ "刷新"按钮 · MUST NOT 跳 splash · shell 保留 | screenshots/P2A-01/h10-snapshot-error.json | errorBar=true, topbar=true
P2A-01 | 11 | PASS | H11 · partial failure · 只 approvals 挂 · 该段显示行内错误 · 其他段正常 | screenshots/P2A-01/h11-partial-failure.json | approvalsError=审批快照暂时不可用, runs=3, scheduled=5
P2A-01 | 12 | PASS | H12 · dev switcher · 6 状态按钮全可切 · 切换无 reload · URL ?__state= 同步 | screenshots/P2A-01/h12-dev-switcher.json | states=6, navigationEntries=1
P2A-01 | 13 | PASS | H13 · intent ribbon · 至少 4 个 pill · 点 pill 滚动到对应段(smooth · 80px offset 避开 topbar) | screenshots/P2A-01/h13-intent-ribbon.json | pillCount=4, behavior=smooth, top=782.484375
P2A-01 | 14 | PASS | H14 · 空状态整页 · runs + approvals + scheduled 全 0 时 · 整页居中友好文案 "今天很清静,来开一场对话?" + 跳 /chat 的 CTA | screenshots/P2A-01/h14-empty-hero.json | title=今天很清静,来开一场对话?, cta=/chat
P2A-02 | 1 | PASS | runs 为 `[]` 时不渲染段标题,仅空态 · 非空时段标题 "正在进行中 (N)" | screenshots/P2A-02/l01-livework-title.json | emptyHeader=false, filledTitle=正在进行中 (2)
P2A-02 | 2 | PASS | status pill 按 `ACTIVE_RUN_STATUSES` 9 种状态各有独立配色 · 只能用 tokens 里的 `--success/--warning/--error/--accent/--ink-3` | screenshots/P2A-02/l02-status-pills.json | rendered=9, inlineStyles=0
P2A-02 | 3 | PASS | health tone 用 `toneForRunHealth(run)` · 返回 `healthy/warning/error` 三档 | screenshots/P2A-02/l03-run-health-tone.json | tones=healthy,warning,error
P2A-02 | 4 | PASS | 悬浮卡片 300ms delay 后显示 `summarizeRunContext()` tooltip · 位置 top · 箭头对齐 | screenshots/P2A-02/l04-run-tooltip.json | before=false, after=true, copyLength=50
P2A-02 | 5 | PASS | 进度条颜色按 health tone · warning 黄 / error 红 / healthy 琥珀 | screenshots/P2A-02/l05-progress-colors.json | tokens=qa-progress-healthy:--accent, qa-progress-warning:--warning, qa-progress-error:--error
P2A-02 | 6 | PASS | 时长实时更新 · `advanceClock` 推进时自动变 · 格式 `HH:mm:ss` 或 `m 分 s 秒`(locale) | screenshots/P2A-02/l06-duration-update.json | 1 分 5 秒 -> 1 分 6 秒
P2A-02 | 7 | PASS | 卡片点击 · 若 run 有 sessionId · 跳 `/chat?session=xxx` · 否则跳 `/sessions?runId=xxx` | screenshots/P2A-02/l07-run-navigation.json | /chat?session=session-route-proof | /sessions?runId=qa-route-run
P2A-02 | 8 | PASS | 键盘 · Tab 可聚焦 · Enter 触发点击 · focus ring 2px | screenshots/P2A-02/l08-run-keyboard.json | focus=qa-keyboard-run, route=/chat?session=keyboard-proof
P2A-02 | 9 | PASS | `run.state_changed` event · 卡片 500ms 淡入 · 不 reflow 其他卡片 | screenshots/P2A-02/l09-state-change-fade.json | animation=run-fade, stable=true
P2A-03 | 1 | PASS | 行高 72 · 横向 flex · 左风险 badge 48×48 · 中标题 + meta · 右 "去处理" 按钮 | screenshots/P2A-03/a01-approvals-layout.json | row=74px badge=48x48
P2A-03 | 2 | PASS | 最多渲染 3 条 · 超过显示 "全部查看 (N)" link · 跳 /assistant?tab=approvals · 保留 packId | screenshots/P2A-03/a02-approvals-cap.json | rows=3, route=/assistant?tab=approvals&packId=industry-cross-border-ecommerce
P2A-03 | 3 | PASS | 风险 badge 3 色 · `low` 琥珀 / `medium` 黄 / `high` 红 · 图标对应 ShieldCheck / ShieldAlert / Siren | screenshots/P2A-03/a03-approval-badges.json | approval-policy:ShieldCheck/--accent, approval-provider:ShieldAlert/--warning, approval-warehouse:Siren/--error
P2A-03 | 4 | PASS | 标题 2 行截断 · meta 1 行 · 显示 "来自 {channelName} · {timeAgo}" | screenshots/P2A-03/a04-approval-copy.json | 来自 邮件 · 11 分钟前
P2A-03 | 5 | PASS | 点"去处理" · 跳 /assistant?approvalId=xxx · MUST NOT 在 home 行内处理 | screenshots/P2A-03/a05-approval-navigation.json | /assistant?approvalId=approval-warehouse
P2A-03 | 6 | PASS | 键盘 Tab 遍历 3 条 + "全部查看" · focus ring | screenshots/P2A-03/a06-approval-keyboard.json | /assistant?tab=approvals&packId=industry-cross-border-ecommerce -> /assistant?approvalId=approval-warehouse -> /assistant?approvalId=approval-provider -> /assistant?approvalId=approval-policy
P2A-03 | 7 | PASS | 空 · 段 3 条文案 "没有待决定事项" · 不留灰块 | screenshots/P2A-03/a07-approval-empty.json | 暂时没有需要你决定的事没有待决定事项Friday 会把真正跨过人工边界的动作放回这里。
P2A-04 | 1 | PASS | 行高 56 · 左时间刻度 80px · 右内容 · 时间格式 `formatAutomationNextRun`(locale 敏感) | screenshots/P2A-04/s01-scheduled-layout.json | 4月20日 14:36 @ 69.671875px
P2A-04 | 2 | PASS | 按 `nextRunAt` 升序 · null 排最后 · 过滤 `enabled === true` 优先 | screenshots/P2A-04/s02-scheduled-order.json | early -> late -> manual -> disabled-timed -> disabled-manual
P2A-04 | 3 | PASS | 最多 5 行 · 第 6 行替换成 "+{N-5} 更多" link · 跳 /automations | screenshots/P2A-04/s03-scheduled-cap.json | rows=5, route=/automations
P2A-04 | 4 | PASS | disabled 行 opacity 0.35 · time 显示 "已暂停" · cron 文字也置灰 | screenshots/P2A-04/s04-scheduled-disabled.json | 已暂停, opacity=0.35
P2A-04 | 5 | PASS | 悬浮 row · 底色 `--bg-sunken` · cursor pointer · 点跳 /automations?automationId=xxx | screenshots/P2A-04/s05-scheduled-hover.json | hover=--bg-sunken, route=/automations?automationId=qa-hover-auto
P2A-04 | 6 | PASS | 时间变化(`advanceClock`)· time 列重算 · 文字更新 | screenshots/P2A-04/s06-scheduled-clock.json | 4月20日 14:28 -> 4月20日 14:36
P2A-04 | 7 | PASS | 0 条 · 段替换成 "接下来没有计划中的自动任务" 文案 · 不渲染段标题 | screenshots/P2A-04/s07-scheduled-empty.json | 接下来没有计划中的自动任务
P2A-05 | 1 | PASS | C01 · empty · 首次进入 composer 居中(vertical + horizontal)· 快捷 prompt 3 张 · 点击填入 composer | screenshots/P2A-05/c01-empty-layout.json | quickCards=3, prompt="复核今天的高风险运行"
P2A-05 | 2 | PASS | C02 · user-sent · 发送后 user bubble 右侧 · 紧接 "Friday 在思考…" 左侧 typing indicator · 3 个点循环动画 400ms 一轮 | screenshots/P2A-05/c02-user-thinking.json | rows=2, dots=3, duration=0.4s
P2A-05 | 3 | PASS | C03 · streaming · assistant bubble 字符逐字进入(每 30ms 一字)· 末尾光标闪烁 500ms 周期 · 流结束光标消失 | screenshots/P2A-05/c03-streaming-timing.json | mean=30.07ms, stddev=0.76ms, caretStates=1
P2A-05 | 4 | PASS | C04 · tool-call 展开 · ChatToolActivity 入流 · 默认折叠 · 显示工具名 + 状态 dot · 点击展开 parameters + result JSON | screenshots/P2A-05/c04-tool-call-expand.json | tool=inventory.fetch, open=true
P2A-05 | 5 | PASS | C05 · approval · ChatActionCard 流转 3 态 pending → approved → used · 状态变化有图标切换 + 底色过渡 250ms | screenshots/P2A-05/c05-approval-flow.json | 等待决定 -> 已批准 -> 已使用
P2A-05 | 6 | PASS | C06 · autonomous-step · AutonomousStepIndicator 独立行 · "步骤 3/7 · 正在查询渠道日志" · 有小 spinner | screenshots/P2A-05/c06-step-indicator.json | 步骤 3/7 · 正在查询渠道日志
P2A-05 | 7 | PASS | C07 · grant-evidence · grantId 悬浮卡 · 显示 `{ scope, expiresAt, source }` · 空字段不显示 · 位置 top + 箭头 | screenshots/P2A-05/c07-grant-tooltip.json | tooltip="Scope: supplier.switchExpires: 2026-04-20T15:10:00-07:00Source: approval-warehouse"
P2A-05 | 8 | PASS | C08 · session-resume · 刷新后 sessionKey 从 `localStorage.getItem('friday-chat-session-key')` 读 · 历史气泡顺序与刷新前一致 | screenshots/P2A-05/c08-session-resume.json | hash=cd9e78dbb973…, session=chat:default:resume-evidence
P2A-05 | 9 | PASS | C09 · disconnected · WS 断开 3 秒内顶部红色条 "连接已断开 · 正在重试 ({retry_count})" · 重连成功后 500ms 淡出 | screenshots/P2A-05/c09-disconnected-banner.json | 连接已断开 · 正在重试 (1) -> fading -> hidden
P2A-05 | 10 | PASS | 消息最大 840px · 中央对齐 · 左右各 flex gutter | screenshots/P2A-05/c10-message-width.json | timeline=436px, bubble=288px
P2A-05 | 11 | PASS | 新消息自动滚底 · 若用户手动向上滚超过 200px · 不自动滚 · 改显右下 "新消息 ↓" 浮标 | screenshots/P2A-05/c11-auto-scroll.json | nearBottom=5908/5908, farGap=829
P2A-05 | 12 | PASS | > 50 条消息启用虚拟化(react-window 或等价)· 滚动 FPS ≥ 55 | screenshots/P2A-05/c12-virtualized-fps.json | rendered=21, fps=60.00
P2A-05 | 13 | PASS | composer Enter 发 · Shift+Enter 换行 · / 唤起 slash 菜单 · Escape 关菜单 | screenshots/P2A-05/c13-composer-keys.json | newline preserved, slashItems=6, history=4
P2A-05 | 14 | PASS | composer 附件支持拖入 + 点击上传 · 上传中显示进度 bar · 失败有 retry | screenshots/P2A-05/c14-upload-flow.json | 上传失败，可重试 -> 上传完成
P2A-05 | 15 | PASS | activity-rail 显示最近 10 个 session · 当前 session 高亮 · 点击切换不丢当前输入 | screenshots/P2A-05/c15-session-rail.json | sessions=10, restored="draft-alpha"
P2A-05 | 16 | PASS | side-panel 显示 run 详情 / grant 详情 / trace · Ctrl+\ 切换开关 | screenshots/P2A-05/c16-side-panel-toggle.json | Run detail运行侧栏run=session-replenish-watchgrant=grant-warehouse-budgettrace=trace-home-0420ws=1audit
P2A-05 | 17 | PASS | Data contract 严格用 `useChatSession` + `useAgentRunEvents` · 不开第二个 WS · Network panel 只 1 条 WS | screenshots/P2A-05/c17-data-contract.json | logicalWs=1, networkWs=0, eventSources=0
P2A-05 | 18 | PASS | git diff 证明 ChatMessageBubble / ChatToolActivity / ChatActionCard / AutonomousStepIndicator 四个组件文件无修改(本静态原型内复制品亦不改 props) | screenshots/P2A-05/c18-props-diff.json | ChatMessageBubble: missing=0, extra=0 | ChatToolActivity: missing=0, extra=0 | ChatActionCard: missing=0, extra=0 | AutonomousStepIndicator: missing=0, extra=0
P2A-06 | 1 | PASS | `turn.kind` switch 穷举 6 值 · `message | tool | action | step | time-divider | grant` · default branch throw · 不允许 fallback silent | screenshots/P2A-06/l01-switch-exhaustive.json | Unsupported turn.kind: unknown-kind
P2A-06 | 2 | PASS | time-divider · 横线 + 中间日期 · 当某 turn 与上一条相隔 > 10 分钟自动插入 | screenshots/P2A-06/l02-time-divider.json | 4月20日 3:15
P2A-06 | 3 | PASS | 自动滚底逻辑 · 新 turn 进入时若 scrollTop >= scrollHeight - 300 · 平滑滚底 | screenshots/P2A-06/l03-auto-bottom.json | top=4526, max=4526
P2A-06 | 4 | PASS | > 50 条时虚拟化 · 仅渲染可视 + 上下 5 条 buffer | screenshots/P2A-06/l04-virtual-window.json | rendered=21/21
P2A-06 | 5 | PASS | scroll 位置在 session 切换前写 `localStorage['friday-chat-scroll-' + sessionId]` · 切回恢复 | screenshots/P2A-06/l05-scroll-restore.json | restored=860, localStorage=860
P2A-06 | 6 | PASS | 每 turn 有 `data-turn-id` 属性 · 便于 e2e 定位 | screenshots/P2A-06/l06-turn-ids.json | turn-msg, tool-check, turn-action, goal-test, divider-4, turn-grant
P2A-06 | 7 | PASS | 同一 turn id 两次更新(stream 过程中)不重渲染整 bubble · React key 稳定 | screenshots/P2A-06/l07-stable-row.json | sameRow=true, chars=11->18
P2A-06 | 8 | PASS | 键盘 · Tab 可进 turn · 方向键在 turns 间切换 focus · Enter 展开工具 / 审批 | screenshots/P2A-06/l08-keyboard-nav.json | tab=turn-user-1, down=tool-inventory-fetch, actionFocus=chat-action-pill is-approve
P2A-07 | 1 | PASS | textarea 初始 80px · 内容增多自动长到 320 · 超过出滚动条 · 不撑破 composer 容器 | screenshots/P2A-07/i01-composer-height.json | 80 -> 320px -> 320px
P2A-07 | 2 | PASS | Enter 发送 · Shift+Enter 换行 · Cmd+Enter 也发送 · Escape 清空当前内容(需确认) | screenshots/P2A-07/i02-send-keys.json | newline ok, cmdSend=4, confirm="清空当前输入?"
P2A-07 | 3 | PASS | / 作为首字符唤起 slash 菜单 · 菜单条目从 `slashCommands` 静态数组来 · 至少 6 条 | screenshots/P2A-07/i03-slash-source.json | new, clear, skills, workflows, settings, help
P2A-07 | 4 | PASS | slash 菜单在 composer 上方 · max-h 320 带滚动 · ↑↓ 选择 · Enter 确认 · Escape 关 | screenshots/P2A-07/i04-slash-navigation.json | active=/skills技能库, value="/skills "
P2A-07 | 5 | PASS | 附件 · 支持拖入 composer 区域 · drop 区 dash border 琥珀 · 非允许类型 toast 错误 | screenshots/P2A-07/i05-invalid-drop.json | ERROR不支持的附件类型
P2A-07 | 6 | PASS | streaming 期间 composer disabled · placeholder 变 "Friday 正在回答,请稍候" · 发送按钮灰 | screenshots/P2A-07/i06-streaming-disabled.json | Friday 正在回答,请稍候, opacity=0.4
P2A-07 | 7 | PASS | 发送按钮 · 琥珀 fill · 空输入时 opacity 0.4 · 点击 200ms press feedback | screenshots/P2A-07/i07-send-feedback.json | empty=0.4, filled=rgb(154, 93, 37), pressed=matrix(0.991181, 0, 0, 0.991181, 0, -0.706019)
P2A-07 | 8 | PASS | 字符计数 · 2000 字以下不显 · 超过显示 "1923 / 2000" · 超限红色 + 禁发 | screenshots/P2A-07/i08-char-count.json | 1923 / 2000 / 2001 / 2000
P2A-07 | 9 | PASS | 草稿 auto-save · 每 500ms debounce 写 `localStorage['friday-chat-draft-' + sessionId]` · 切换 session 恢复 | screenshots/P2A-07/i09-draft-save.json | drafts=draft-one/draft-two
P2A-08 | 1 | PASS | localStorage 键名严格 · `friday-chat-session-key` + `friday-chat-history` · 命名不得变 | screenshots/P2A-08/h01-storage-keys.json | sessionKey=true, historyKey=true
P2A-08 | 2 | PASS | history 结构 · `{ sessionKey: string, turns: Turn[], updatedAt: number }` · Turn 类型照真仓 | screenshots/P2A-08/h02-history-shape.json | turns=2, updatedAt=1776767457634
P2A-08 | 3 | PASS | 新建 session · 先 POST 后拿到 key 再写入 · 不得先写本地 fake key | screenshots/P2A-08/h03-create-session.json | sessionKey=chat:default:chat-mo8hgx7d-aqe77x
P2A-08 | 4 | PASS | 刷新后 turns 顺序 index === 刷新前 · 对比 `JSON.stringify` 结构等价 | screenshots/P2A-08/h04-refresh-hash.json | hash=2ab1bca10ee8…
P2A-08 | 5 | PASS | 过期(> 7 天 updatedAt)· 清除 · 进入空态 · 不报错 | screenshots/P2A-08/h05-expired-history.json | emptyVisible=true, history={}
P2A-08 | 6 | PASS | localStorage 不可用(隐身 / 禁用)· graceful 降级 · 页面可用 · 不得 crash | screenshots/P2A-08/h06-storage-blocked.json | composer="still usable", errors=0
P2B-01 | 1 | PASS | snapshot 从 `uixSnapshotsApi.getAssistantInbox()` · polling 12s active / 36s background | screenshots/P2B-01/b01-polling.json | active=12000ms, hidden=36000ms
P2B-01 | 2 | PASS | URL `?tab=` 3 值有效 · 其他值 fallback approvals · 历史栈前进后退同步 | screenshots/P2B-01/b02-tabs-history.json | invalid=approvals, back=approvals, forward=issues
P2B-01 | 3 | PASS | tab 切换不刷页 · 仅 section 切换动画 fade 150ms | screenshots/P2B-01/b03-tab-fade.json | assistant-approvals-section -> assistant-issues-section (0.15s)
P2B-01 | 4 | PASS | `?packId=` 存在时 · 左列自动 scroll 到该 card · 该 card 高亮 2 秒后恢复 | screenshots/P2B-01/b04-pack-scroll.json | scrollTarget=industry-cross-border-ecommerce, restored=assistant-pack-group
P2B-01 | 5 | PASS | Approvals 段显示 `pendingApprovals` 全部(不像 home 切 3) | screenshots/P2B-01/b05-all-approvals.json | rendered=5, snapshot=5
P2B-01 | 6 | PASS | Issues 段调 `assistantDiagnosticsApi` · 按时间降序 | screenshots/P2B-01/b06-issues-order.json | issues=3, descending=true
P2B-01 | 7 | PASS | Recovery 段列出 learning insight + 建议动作 · 调 `learningApi` | screenshots/P2B-01/b07-learning-recovery.json | cards=3
P2B-01 | 8 | PASS | 右 insight panel 默认显示"今日学习" · 可折叠 · 状态持久化 localStorage | screenshots/P2B-01/b08-panel-persist.json | title=今日学习, storage=collapsed
P2B-01 | 9 | PASS | pack-related card 用 `PackAssistantHandoffCard` · cross-border 用 `CrossBorderAssistantHandoffCard` | screenshots/P2B-01/b09-pack-components.json | pack=true, crossBorder=true
P2B-01 | 10 | PASS | 确认 / 拒绝动作触发 `ConfirmDialog` · 二次确认 · high-risk 需输入 pack 名确认 | screenshots/P2B-01/b10-confirm-dialog.json | highRiskInput=跨境经营动作板, mediumHasInput=false
P2B-01 | 11 | PASS | 空态 · "没有待办、没有问题、一切正常" · 米色插图(允许 line-art SVG · 不用 emoji) | screenshots/P2B-01/b11-empty-state.json | svgPaths=1, title=没有待办、没有问题、一切正常
P2B-01 | 12 | PASS | 错误态 · inline 错误条 + retry · 不影响其他段 | screenshots/P2B-01/b12-error-bar.json | issues=3, recovery=3
P2B-01 | 13 | PASS | 计数 badge 实时更新 · dispatch `approval.resolved` 后 -1 | screenshots/P2B-01/b13-badge-realtime.json | 4 -> 3
P2B-02 | 1 | PASS | 卡片宽满列 · min-h 180 · padding 20 · radius 14 | screenshots/P2B-02/a01-card-box.json | width=448px, radius=14px
P2B-02 | 2 | PASS | 顶行 · 风险 badge(low/med/high)+ 来源 channel + 时间 · 右侧 Copy ID 按钮 | screenshots/P2B-02/a02-top-row.json | high, 邮件 · 9 分钟前
P2B-02 | 3 | PASS | 标题行 · h3 font serif 20 · 不截断 · 允许 2 行 | screenshots/P2B-02/a03-title-wrap.json | lineCount=1.00, font=20px
P2B-02 | 4 | PASS | Capabilities 列表 · tag 样式 · 每个 capability 有图标 + 文字 · 悬浮 tooltip scope 描述 | screenshots/P2B-02/a04-capability-tooltip.json | icon=◎, tooltip="允许 Friday 在本次补货决策里读取并比较预算方案。"
P2B-02 | 5 | PASS | Evidence 展开 · 默认折叠 · 点 "查看证据" 展开 · 显示 grantId / toolCalls / timestamps | screenshots/P2B-02/a05-evidence-expand.json | grantId=grant-warehouse-shift-2042, calls=3
P2B-02 | 6 | PASS | pending 状态 · 主按钮 "批准" + 次按钮 "拒绝" · high-risk 时主按钮灰 · 需输入确认词 | screenshots/P2B-02/a06-pending-actions.json | high=action-button action-button-secondary, medium=action-button action-button-primary
P2B-02 | 7 | PASS | approving/denying 中 · 按钮 spinner 图标 + 禁用 · 250ms 内给反馈 | screenshots/P2B-02/a07-busy-feedback.json | elapsed=3.7ms
P2B-02 | 8 | PASS | approved · 整卡 opacity 0.6 · 顶部勾图标 · 文字 "已批准 · {time}" | screenshots/P2B-02/a08-approved-card.json | opacity=0.6, icon=✓
P2B-02 | 9 | PASS | denied · 整卡红底 · 文字 "已拒绝 · {reason}" | screenshots/P2B-02/a09-denied-card.json | medSlack · 22 分钟前×Copy ID是否允许 Friday 在 provider 降级后自动改用次优模型◌路由切换允许 Friday 在当前运行里把模型切到降级 provider。△SLA 兜底允许 Friday 在质量下降前提下优先守住响应时延。查看证据{ "grantId": "grant-provider-fallback-1881", "toolCalls": [ "provider.health", "routing.preview" ], "timestamps": [ "2026-04-20T20:56:00.000Z", "2026-04-20T21:18:00.000Z" ] }已拒绝 · 暂不接受降级到次优模型
P2B-02 | 10 | PASS | expired · 虚线 border · 文字 "已过期 · 请重新请求" | screenshots/P2B-02/a10-expired-card.json | medSlack · 22 分钟前Copy ID是否允许 Friday 在 provider 降级后自动改用次优模型◌路由切换允许 Friday 在当前运行里把模型切到降级 provider。△SLA 兜底允许 Friday 在质量下降前提下优先守住响应时延。查看证据{ "grantId": "grant-provider-fallback-1881", "toolCalls": [ "provider.health", "routing.preview" ], "timestamps": [ "2026-04-20T20:56:00.000Z", "2026-04-20T21:18:00.000Z" ] }已过期 · 请重新请求
P2B-03 | 1 | PASS | 卡片结构 · 状态 dot + 标题 + "根因" 展开 + recovery 链 + 操作栏 | screenshots/P2B-03/i01-card-structure.json | recoveryRows=2, timelineRows=3
P2B-03 | 2 | PASS | 状态 · open(红 dot)/ investigating(黄 + spinner)/ recovered(绿 + 勾)/ dismissed(灰 + 叉) | screenshots/P2B-03/i02-status-variants.json | open=rgb(165, 48, 40), investigating=rgb(143, 84, 19), dismissed=rgba(45, 33, 24, 0.34)
P2B-03 | 3 | PASS | 根因展开 · 默认折叠 · 点击展开 technical detail · 不暴露堆栈 · 用 `describeRunHealth` 文字 | screenshots/P2B-03/i03-root-cause-expand.json | 这是阻塞性失败，需要先处理。
P2B-03 | 4 | PASS | recovery 链 · 最多 3 步 · 每步有 "执行" 按钮 · 执行中 disabled | screenshots/P2B-03/i04-recovery-chain.json | rows=2, spinner=2
P2B-03 | 5 | PASS | dismiss · 二次确认 · "确定不追查?" + 理由文本框(选填) | screenshots/P2B-03/i05-dismiss-confirm.json | 确定不追查?, textarea=true
P2B-03 | 6 | PASS | 时间线 · 事件有时间戳 · 倒序最新在上 | screenshots/P2B-03/i06-timeline-order.json | 14:08 > 14:05 > 14:02
P2B-03 | 7 | PASS | 关联 runId · 点击跳 `/sessions?runId=xxx` | screenshots/P2B-03/i07-run-link.json | /sessions?runId=run-provider-circuit
P2B-03 | 8 | PASS | recovered 后卡片 30 秒后自动 collapse 到 60 高 · 可展开 | screenshots/P2B-03/i08-recovered-collapse.json | collapsed=60, expanded=330.046875
P2B-04 | 1 | PASS | 复用 `LearningInsightCard` · props 不改 · wrap 只负责 data binding | screenshots/P2B-04/l01-signature-bindings.json | source=true, wrapper=true
P2B-04 | 2 | PASS | tone 3 档 · positive 绿边 / warning 黄边 / neutral 米边 | screenshots/P2B-04/l02-tone-borders.json | insight-route-learning:rgba(47, 122, 73, 0.26) | insight-provider-warning:rgba(184, 106, 23, 0.28) | insight-workflow-neutral:rgba(45, 33, 24, 0.1)
P2B-04 | 3 | PASS | 点 "应用建议" · 跳对应页(skill / workflow / settings) | screenshots/P2B-04/l03-apply-routes.json | /skills, /settings?tab=runtime, /workflows
P2B-04 | 4 | PASS | dismiss 写 localStorage `friday-insights-dismissed` set | screenshots/P2B-04/l04-dismiss-storage.json | storage=["insight-route-learning"]
P2B-05 | 1 | PASS | 左 sub-nav 200 固定宽 · 4 tab · 竖排 · 当前 tab 左 2px 琥珀竖线 + 粗体 | screenshots/P2B-05/s01-subnav-layout.json | width=200, tabs=4, stripe=2px
P2B-05 | 2 | PASS | URL `?tab=providers|runtime|diagnostics|account` · 默认 providers · 不识别 fallback | screenshots/P2B-05/s02-tab-url-sync.json | ?dev=1 -> ?tab=runtime&__state=runtime -> ?dev=1
P2B-05 | 3 | PASS | 右主区 · 顶 h2 tab 名 · 下表单 + 内容 | screenshots/P2B-05/s03-main-region.json | diagnostics=诊断与保留策略, account=身份与偏好
P2B-05 | 4 | PASS | 底部吸附 Save bar · 128 高 · 仅有 dirty 时显示 · 显示 "有 N 项未保存" | screenshots/P2B-05/s04-save-bar-sticky.json | visible=true, bottom=1200, height=128
P2B-05 | 5 | PASS | dirty 时切换 tab · `ConfirmDialog` "放弃修改?" · 确认走 / 取消留 | screenshots/P2B-05/s05-tab-switch-confirm.json | 运行时 -> 账号
P2B-05 | 6 | PASS | dirty 时离开 route · `beforeunload` 确认 | screenshots/P2B-05/s06-beforeunload.json | dirty=true, clean=false
P2B-05 | 7 | PASS | Save 按钮 · 禁用当 invalid · loading 时 spinner | screenshots/P2B-05/s07-save-disabled-loading.json | invalid=true, loadingSpinner=true
P2B-05 | 8 | PASS | Saved 后 toast 右下 · 3 秒淡出 · "已保存" | screenshots/P2B-05/s08-save-toast.json | toast=SUCCESS已保存, fading=toast-card is-success is-fading
P2B-05 | 9 | PASS | 字段级错误 · 红边 + 下方错误文字 · i18n | screenshots/P2B-05/s09-field-errors.json | 请输入有效邮箱地址。 | Enter a valid email address.
P2B-05 | 10 | PASS | tab 之间 URL 切换不丢未保存(本 tab) | screenshots/P2B-05/s10-tab-draft-preserved.json | restored=15000, dirty=["runtime-budget"]
P2B-05 | 11 | PASS | Reset 按钮 · 位于 save bar 左 · 二次确认 | screenshots/P2B-05/s11-reset-confirm.json | resetX=896.765625, saveX=979.453125, restored=12000
P2B-06 | 1 | PASS | 每 provider 一行 · 左 logo 24 + 名 · 中 health dot + latency · 右 配额 + actions | screenshots/P2B-06/p01-provider-row-structure.json | rows=5, actions=3/3/3/3/3
P2B-06 | 2 | PASS | health dot 3 色 · healthy 绿 / degraded 黄 / offline 红 · 动态脉冲(reduced-motion 下静态) | screenshots/P2B-06/p02-health-dots.json | states={"healthy":"--success","degraded":"--warning","offline":"--error","cooldown":"--ink-3"}, interval=30000
P2B-06 | 3 | PASS | latency · 显示 p50 / p95 · 数值 mono 字体 · p95 > 1000ms 黄色 | screenshots/P2B-06/p03-latency-metrics.json | p50 530ms | p95 1320ms
P2B-06 | 4 | PASS | circuit breaker · 3 态 closed(绿)/ open(红)/ half-open(黄)· tooltip 解释 | screenshots/P2B-06/p04-circuit-tooltip.json | closed当前没有命中熔断条件，流量仍走主路径。 | half-open正在小流量试探恢复，下一轮健康检查决定是否回到 closed。 | open熔断已打开，等待冷却结束后才会重试主 provider。
P2B-06 | 5 | PASS | open 时 cooldown 倒计时 · 实时更新 · `mm:ss` | screenshots/P2B-06/p05-cooldown-countdown.json | cooldown 05:05 -> cooldown 05:04
P2B-06 | 6 | PASS | edit key · 弹出 modal · input 隐藏(••••)· show/hide toggle | screenshots/P2B-06/p06-edit-key-modal.json | secret=password -> text
P2B-06 | 7 | PASS | test connection 按钮 · 点后 loading 3 秒 · 返回结果 toast | screenshots/P2B-06/p07-test-connection.json | elapsed=3056.6ms
P2B-06 | 8 | PASS | disable provider · 二次确认 · disabled 后整行置灰 + opacity 0.5 | screenshots/P2B-06/p08-disable-provider.json | opacity=0.5, class=provider-row is-disabled
P2B-06 | 9 | PASS | 添加 provider · 底部 + 按钮 · 弹出 modal · 预设 5 个官方 preset + "自定义" | screenshots/P2B-06/p09-add-provider.json | presets=6, rows=5->6
P2B-07 | 1 | PASS | 5 focus 全部可切 · 切换无页面重载 · URL 同步 | screenshots/P2B-07/o01-focus-tabs-url.json | mount=observability-shell-v1, deeplink=audit, nav=1
P2B-07 | 2 | PASS | 时间窗切换 refetch · loading 态显 skeleton · 不清空现有数据 | screenshots/P2B-07/o02-window-loading.json | skeletons=3->0, rows=12
P2B-07 | 3 | PASS | alerts focus · list 显示 firing + snoozed + resolved 三段 · 按 severity 排序 | screenshots/P2B-07/o03-alert-sections.json | firing=2, snoozed=2, resolved=2
P2B-07 | 4 | PASS | traces focus · list 显示 trace row(traceId + duration + spans + status)· 点开 detail drawer(§P2B-08) | screenshots/P2B-07/o04-trace-drawer.json | rows=3, drawer=trace
P2B-07 | 5 | PASS | audit focus · list 显示审计事件 · actor + action + target + time · filter 按 actor | screenshots/P2B-07/o05-audit-filter.json | filters=all/friday/jarvis, rows=1
P2B-07 | 6 | PASS | health focus · 显示系统健康大盘 · CPU / mem / queue 三个 sparkline · `selectObservabilityPrimaryHealthComponent` | screenshots/P2B-07/o06-health-dashboard.json | primary=queue, cards=3
P2B-07 | 7 | PASS | acceptance focus · 显示 acceptance criteria pass/fail 聚合 · 点击跳 assistant | screenshots/P2B-07/o07-acceptance-navigation.json | rows=6, route=/assistant?tab=approvals&phase=2a
P2B-07 | 8 | PASS | 每 focus 的 list 空态有独立文案 · 不共用 | screenshots/P2B-07/o08-empty-copy.json | titles=5, focuses=alerts/traces/audit/health/acceptance
P2B-07 | 9 | PASS | 每 focus 的 error 态 inline 错误条 · 不跳 splash | screenshots/P2B-07/o09-error-state.json | focuses=alerts/traces/audit/health/acceptance
P2B-07 | 10 | PASS | 时间窗 15m 时 polling 5s · 1h 时 15s · 24h 时 60s | screenshots/P2B-07/o10-poll-intervals.json | 15m=5000, 1h=15000, 24h=60000
P2B-07 | 11 | PASS | list 行 hover 底色 · 点击左边 2px 琥珀条标记 active | screenshots/P2B-07/o11-row-hover-active.json | hover=rgba(246, 237, 222, 0.973), stripe=2px
P2B-07 | 12 | PASS | detail 区 · focus 切换时清空并显示 "选择一条查看详情" 空态 | screenshots/P2B-07/o12-detail-reset.json | before=alert-1, after=选择一条查看详情
P2B-07 | 13 | PASS | alert actions · create(§P2B-09)· snooze · edit · delete | screenshots/P2B-07/o13-alert-actions.json | actions=新建告警/批量暂停 1h/导出列表, delete=删除这个告警?
P2B-07 | 14 | PASS | traces 行 · 超过 2000ms 红色 latency · 500-2000ms 黄 | screenshots/P2B-07/o14-trace-latency-colors.json | --error/--warning/null
P2B-07 | 15 | PASS | audit 行有 diff 按钮 · 点开二级 drawer 显示 before / after JSON | screenshots/P2B-07/o15-audit-drawer.json | drawer=audit, selected=audit-1
P2B-07 | 16 | PASS | health sparkline · 60 个 data point · hover 显具体时间 + 值 | screenshots/P2B-07/o16-health-tooltip.json | cpu=60, tooltip=14:18 · 44%
P2B-07 | 17 | PASS | `buildObservabilityActionQueue` 驱动右上角 action 按钮组 · 按 focus 显示不同按钮 | screenshots/P2B-07/o17-action-queue.json | queues=5, alerts=新建告警/批量暂停 1h/导出列表
P2B-08 | 1 | PASS | drawer 右侧滑入 420 · 200ms ease-out · Escape 关 | screenshots/P2B-08/t01-trace-drawer-shell.json | width=420, animation=0.2s, closed=false
P2B-08 | 2 | PASS | 头部 · traceId + duration + status + close 按钮 | screenshots/P2B-08/t02-trace-header.json | title=trace-932af, header=2430ms/failed
P2B-08 | 3 | PASS | span tree 竖排 · 缩进表达 parent-child · 每 span 一行 · 行高 28 | screenshots/P2B-08/t03-span-tree-indent.json | rows=4, depths=span-route-root:0,span-tool-inventory:1,span-db-audit:2,span-tool-pricing:1
P2B-08 | 4 | PASS | span 有 3 色 dot · tool 琥珀 / LLM 紫 / DB 青 | screenshots/P2B-08/t04-span-dot-colors.json | span-tool-inventory:--warning, span-route-root:--trace-llm, span-db-audit:--trace-db
P2B-08 | 5 | PASS | span 点击展开 · 显示 start/end/duration/attributes · 默认折叠 | screenshots/P2B-08/t05-span-expand.json | visible=1->3, attrs=1
P2B-08 | 6 | PASS | span 超过 1000ms · 红文字 | screenshots/P2B-08/t06-slow-span-color.json | 1180ms -> --error
P2B-08 | 7 | PASS | 可搜索 spans · 输入框顶部 · 即时过滤 | screenshots/P2B-08/t07-trace-search.json | visible=4->2
P2B-08 | 8 | PASS | export JSON 按钮 · 下载 trace.json | screenshots/P2B-08/t08-trace-export.json | trace.json spans=4
P2B-09 | 1 | PASS | list · 每 alert 一行 · 状态 dot + 名称 + 指标 + 阈值 + 最近触发 + actions | screenshots/P2B-09/m01-alert-list-row.json | provider p95 超阈值 · latency.p95 / > 2000ms / 14:10
P2B-09 | 2 | PASS | compose modal · 3 字段必填 · 指标(select 20+ option)+ 阈值(数字 + 比较符)+ 通知渠道(multi-select) | screenshots/P2B-09/m02-compose-modal.json | options=23, comparators=5, channels=7
P2B-09 | 3 | PASS | compose 验证 · 空字段红边 + 下方错误 · 阈值非数字禁交 | screenshots/P2B-09/m03-compose-validation.json | step1=名称不能为空。, step2=阈值必须是数字。
P2B-09 | 4 | PASS | snooze · 弹出 4 档选项 1h / 4h / 24h / 永久 · 选后 row 变虚线 border + "snoozed until ..." | screenshots/P2B-09/m04-alert-snooze.json | menu=1 小时/4 小时/24 小时/永久, row=obs-row obs-alert-row is-snoozed is-active
P2B-09 | 5 | PASS | firing row · 红底 + 脉冲 dot · 顶部有 "N 正在触发" banner | screenshots/P2B-09/m05-firing-banner.json | 2 正在触发 / --error-soft
P2B-09 | 6 | PASS | edit · 行内按钮 · 打开 modal 预填当前值 | screenshots/P2B-09/m06-edit-prefill.json | 队列堆积 / >50
P2B-09 | 7 | PASS | delete · 二次确认 · 输入 alert 名二次验证 | screenshots/P2B-09/m07-delete-confirm.json | confirm=删除这个告警?, exact=false
P2B-09 | 8 | PASS | 新建后自动刷 list · row 入场 fade-in · 滚到其位置 | screenshots/P2B-09/m08-new-alert-row.json | row=alert-custom-0000, scroll=smooth
P2B-09 | 9 | PASS | dev switcher · 4 状态 list / creating / snoozed-sample / firing-sample | screenshots/P2B-09/m09-dev-switcher.json | states=list/creating/snoozed-sample/firing-sample
P2C-01 | 1 | PASS | 双列 · 左 pinned 280(单列 card)· 右 catalog flex(3 列 grid)· 1920 下 4 列 | screenshots/P2C-01/p01-layout.json | 1440=3 cols, 1920=4 cols
P2C-01 | 2 | PASS | 顶部 · search input 400 + category filter chips(6-8 个)+ "+ 自定义 pack" 按钮 | screenshots/P2C-01/p02-topbar.json | search=400px, chips=6
P2C-01 | 3 | PASS | search 即时过滤(debounce 150ms)· 匹配 pack.title + description + tags | screenshots/P2C-01/p03-search-debounce.json | title=task-support-ops description=task-release-ops
P2C-01 | 4 | PASS | filter chips 多选 · active 琥珀底 · clear all 按钮 | screenshots/P2C-01/p04-filter-chips.json | active=release/security
P2C-01 | 5 | PASS | pack card · 240 高 · 顶部产品预览(PackProductPreview 24)· 中标题 · 底 meta + "打开" 按钮 | screenshots/P2C-01/p05-pack-card.json | height=998.375, tiles=4
P2C-01 | 6 | PASS | pinned 列 · 拖拽重排 · 拖后写 `useHomeSurfacePreferences` | screenshots/P2C-01/p06-pinned-drag.json | industry-cross-border-ecommerce -> task-ship-fast => task-ship-fast -> industry-cross-border-ecommerce
P2C-01 | 7 | PASS | 卡片 hover · 显 PackQuickSheet tooltip(350ms delay) | screenshots/P2C-01/p07-hover-sheet.json | delay=356.5ms, visible@300=false, visible@420=true
P2C-01 | 8 | PASS | 点击 pack · 跳 `/packs/{packId}/setup`(目前只 cross-border 有 setup · 其他 toast "即将上线") | screenshots/P2C-01/p08-launch-route.json | /packs/industry-cross-border-ecommerce/setup / toast=WARNING即将上线
P2C-01 | 9 | PASS | cross-border pack 显示特殊 badge "专属引导" | screenshots/P2C-01/p09-guided-badge.json | badge=专属引导
P2C-01 | 10 | PASS | 空态(search 无结果)· "没找到相关 pack · 试试其他关键词" | screenshots/P2C-01/p10-empty-search.json | filtered=0
P2C-01 | 11 | PASS | empty pinned · 左列显示 "置顶你常用的 pack" 空态 · 有拖拽动画提示 | screenshots/P2C-01/p11-empty-pinned.json | title=置顶你常用的 pack
P2C-01 | 12 | PASS | custom pack 入口 · 跳 `CustomPackBuilder` modal(复用 core/custom-pack-builder.tsx) | screenshots/P2C-01/p12-custom-pack-modal.json | 自定义 Pack Builder
P2C-01 | 13 | PASS | `FRIDAY_PACKS` registry 穷举 · 不得 hardcode pack 数据在此页 | screenshots/P2C-01/p13-registry-source.json | usesRegistry=true, literals=0
P2C-02 | 1 | PASS | 顶部 stepper · 4 步 · 当前步琥珀底 + 已完成绿勾 + 未来灰 | screenshots/P2C-02/w01-stepper.json | ✓/✓/3/4
P2C-02 | 2 | PASS | step 1 目标 · 多选 cards(增长 / 合规 / 成本 / 扩张)· 至少选 1 · 不选禁下一步 | screenshots/P2C-02/w02-goal-step.json | selected=成本
P2C-02 | 3 | PASS | step 2 产品 · 表格输入或 CSV 上传 · 每行 SKU + 标题 + 类目 + 目标市场 | screenshots/P2C-02/w03-products-table.json | rows=2->3
P2C-02 | 4 | PASS | step 3 渠道 · 多选 channel card(Amazon / Shopify / TikTok / Temu 等)· 每个 card 显示连接状态 | screenshots/P2C-02/w04-channel-cards.json | Amazon:连接正常 | Shopify:连接正常 | TikTok:连接延迟 | Temu:连接正常
P2C-02 | 5 | PASS | step 4 确认 · 汇总前 3 步 · 可点各 section 回编辑 | screenshots/P2C-02/w05-confirm-edit.json | step=2
P2C-02 | 6 | PASS | success · 全屏祝贺 + "打开 Assistant 查看下一步" · 跳 `/assistant?packId=industry-cross-border-ecommerce` | screenshots/P2C-02/w06-success.json | height=980, href=/assistant?packId=industry-cross-border-ecommerce
P2C-02 | 7 | PASS | 每步 prev / next 底部固定 · prev 在左 · next 在右 | screenshots/P2C-02/w07-footer-bar.json | position=fixed, bottomGap=24
P2C-02 | 8 | PASS | step 切换动画 fade 200ms · URL 同步 `?step=1..4` | screenshots/P2C-02/w08-step-url.json | ?dev=1&__state=step2-products&step=2 / 0.2s
P2C-02 | 9 | PASS | 退出(点 rail 导航)· `ConfirmDialog` "离开会丢失未保存?" | screenshots/P2C-02/w09-leave-confirm.json | 离开会丢失未保存?
P2C-02 | 10 | PASS | 草稿自动保存 · 每步切换写 `crossBorderPackApi.saveDraft()` · 重进自动恢复 | screenshots/P2C-02/w10-draft-resume.json | savedStep=2, resume=true
P2C-02 | 11 | PASS | 验证 · 每步字段 level inline 错误 · 步级错误 step 头红点 | screenshots/P2C-02/w11-validation.json | 至少选择 1 个目标。 / dots=1
P2C-02 | 12 | PASS | keyboard · Enter 下一步(step 4 变 "完成")· Shift+Enter 上一步 · Escape 退出确认 | screenshots/P2C-02/w12-keyboard.json | Enter=3, Shift+Enter=2
P2C-03 | 1 | PASS | 双列 · 左 categories tree 240 · 右 skill list flex | screenshots/P2C-03/s01-layout.json | aside=240px, display=grid
P2C-03 | 2 | PASS | 顶栏 · search + 2 入口按钮 "导入" "扫描" · 右侧 view toggle(grid / list) | screenshots/P2C-03/s02-toolbar.json | 导入/扫描 / grid/list
P2C-03 | 3 | PASS | categories tree · 无限层级 · 展开 / 折叠 · 选中类目过滤右列 | screenshots/P2C-03/s03-tree-filter.json | active=operations, filtered=skill-cross-border-review
P2C-03 | 4 | PASS | skill list · grid 3 列 · 每 card 240 高 · 名称 + 版本 + 状态 pill + 动作 | screenshots/P2C-03/s04-skill-card-grid.json | cols=3, height=348.53125
P2C-03 | 5 | PASS | status · installed(绿)/ available(灰)/ deprecated(警示橙)/ needs-update(琥珀) | screenshots/P2C-03/s05-status-pills.json | skill-cross-border-review:--success, skill-finance-watch:--ink-3, skill-ticket-triage:--warning, skill-release-guardian:--accent
P2C-03 | 6 | PASS | deprecated skill · 顶部 banner "此技能已弃用 · 建议替换为 X" · 不让启用 | screenshots/P2C-03/s06-deprecated-banner.json | 此技能已弃用 · 建议替换为 Release Guardian / 替换
P2C-03 | 7 | PASS | import wizard 入口 · 打开 `SkillImportWizard` · wizard 不在本页渲染 · 跳 modal | screenshots/P2C-03/s07-import-modal.json | 导入技能
P2C-03 | 8 | PASS | scan 入口 · 打开 `SkillScannerPanel` · 同上 | screenshots/P2C-03/s08-scan-modal.json | 扫描技能
P2C-03 | 9 | PASS | discovery panel 在右下浮层 · 显示 Friday 推荐的未安装 skill · 可关闭 · localStorage 记忆 | screenshots/P2C-03/s09-discovery-panel.json | recommendations=Finance Watch,Workflow Scaffold
P2C-03 | 10 | PASS | skill card 点击 · 跳 `buildSkillHref(skill)` · 不在本页展开 detail | screenshots/P2C-03/s10-skill-href.json | /skills?skillId=skill-finance-watch -> /skills?skillId=skill-finance-watch
P2C-03 | 11 | PASS | empty · "还没有技能" · 大 CTA "浏览技能库" + "创建你的第一个技能"(跳 generator) | screenshots/P2C-03/s11-empty-state.json | 浏览技能库:/skills?__state=available | 创建你的第一个技能:/skills/generator
P2C-04 | 1 | PASS | 双列 · 左 prompt + 配置 480 · 右 preview + test 480 | screenshots/P2C-04/g01-layout.json | 480 / 480
P2C-04 | 2 | PASS | 左列 · intent 多行文本(min-h 200)+ scope 选择 + inputs 定义表格 + outputs 定义表格 | screenshots/P2C-04/g02-left-fields.json | scope=workspace, headers=Name/Type
P2C-04 | 3 | PASS | 右列 · 顶 "生成" 按钮 · 生成中 shimmer · 完成显示 skill YAML / TS 代码 | screenshots/P2C-04/g03-generate-preview.json | generating=3, preview=preview
P2C-04 | 4 | PASS | test runner · 底部 fixed · 输入 inputs · 点 "run test" · 显示 output + duration + token cost | screenshots/P2C-04/g04-test-runner.json | Test runner输入测试 payload测试通过 · 输出了 3 条候选检查与 1 条回退建议。 · 780ms · 1364 tokensrun test
P2C-04 | 5 | PASS | error 态 · 生成失败 · 显示 error message + suggestions · retry 按钮 | screenshots/P2C-04/g05-error-retry.json | 生成失败 / generating
P2C-04 | 6 | PASS | publish · 确认 modal · 显示 skill diff(新增)· 输入 skill name · 发布后跳 /skills | screenshots/P2C-04/g06-publish-flow.json | 确认发布技能 -> /skills?__state=installed
P2C-04 | 7 | PASS | cancel · 二次确认 · 保留草稿到 localStorage `friday-skill-draft` | screenshots/P2C-04/g07-cancel-draft.json | 取消当前草稿? -> /skills
P2C-04 | 8 | PASS | state · draft / generating / preview / published / error 五档 URL ?state= 同步 | screenshots/P2C-04/g08-state-query-sync.json | draft:dev=1&__state=draft&state=draft | generating:dev=1&__state=generating&state=generating | preview:dev=1&__state=preview&state=preview | published:dev=1&__state=published&state=published | error:dev=1&__state=error&state=error
P2C-04 | 9 | PASS | 再次进入若有草稿 · 提示 "继续上次的草稿?" · yes / no | screenshots/P2C-04/g09-resume-prompt.json | 继续上次的草稿? / 不用，重置/继续
P2C-04 | 10 | PASS | keyboard Cmd+Enter = 生成 · Cmd+Shift+Enter = test · Cmd+S = publish confirm | screenshots/P2C-04/g10-keyboard-shortcuts.json | generate=preview, publish=确认发布技能
P2C-05 | 1 | PASS | 双列 · 左 workflow list 420 · 右 detail(最近运行 + 部署状态)flex | screenshots/P2C-05/w01-layout.json | left=420px
P2C-05 | 2 | PASS | 顶栏 · "+ 新建" 按钮跳 `/workflows/builder` + search + status filter | screenshots/P2C-05/w02-toolbar.json | /workflows/builder / filters=3
P2C-05 | 3 | PASS | workflow row · 名称 + 当前版本 + 最近运行状态 + 下次触发时间 + menu(编辑 / 复制 / 归档) | screenshots/P2C-05/w03-workflow-row.json | 跨境上新守护 / 编辑/复制/归档
P2C-05 | 4 | PASS | 点 row · 右侧 detail 加载 · 无页面跳转 | screenshots/P2C-05/w04-select-detail.json | workflow-cross-border-launch -> workflow-incident-brief
P2C-05 | 5 | PASS | detail · 顶 "编辑" 按钮(跳 builder) · 中 runs 表(最近 10)· 底 deploy 状态 | screenshots/P2C-05/w05-detail-panel.json | 10 runs / 重新部署
P2C-05 | 6 | PASS | run 行 · 状态 pill + 时长 + 触发源 + logs 按钮 · 失败有 retry 按钮 | screenshots/P2C-05/w06-run-row.json | failed -> running
P2C-05 | 7 | PASS | running 状态 · 行底 2px 琥珀 progress bar · 宽度实时更新 | screenshots/P2C-05/w07-running-progress.json | 77% -> 82%
P2C-05 | 8 | PASS | deploy 状态 · 显示 template version + env + 上次部署时间 · redeploy 按钮 | screenshots/P2C-05/w08-deploy-state.json | 模板版本: v12 | prod-us | 上次部署 14:02
P2C-05 | 9 | PASS | archive · 二次确认 · archived workflow 单独 section 显示 · 可 restore | screenshots/P2C-05/w09-archive-restore.json | 归档这个工作流? / restored=true
P2C-05 | 10 | PASS | 空态 · "还没有工作流 · 从模板开始" · 展示 5-6 个模板 card · 点击跳 builder preload template | screenshots/P2C-05/w10-empty-templates.json | 6 templates -> /workflows/builder?template=release-qa
P2C-05 | 11 | PASS | 多个 running 时 · 全局顶 banner "N 个工作流正在运行" | screenshots/P2C-05/w11-running-banner.json | 2 个工作流正在运行
P2C-06 | 1 | PASS | 左 lib · 节点按类分组(Triggers / Actions / Logic / Integrations)· 搜索过滤 | screenshots/P2C-06/b01-library-groups.json | 8 entries -> integration-github
P2C-06 | 2 | PASS | 节点拖入 canvas · 创建 node · drop 处 snap to grid | screenshots/P2C-06/b02-drop-node.json | node=node-action-648054-95 @ (240, 180)
P2C-06 | 3 | PASS | 节点之间连线 · hover node 边缘出连接点 · 拖连接点到另一 node 建 edge | screenshots/P2C-06/b03-connect-edge.json | 4 -> 5 edges
P2C-06 | 4 | PASS | 连线 · SVG 贝塞尔曲线 · 琥珀色 · 悬浮加粗 · 右键删除 | screenshots/P2C-06/b04-edge-delete.json | 2px -> 4px, edges=4->3
P2C-06 | 5 | PASS | 选中 node · 右 inspector 显示 node 配置 · field 级表单 | screenshots/P2C-06/b05-inspector-fields.json | 利润阈值判断 / fields=expression
P2C-06 | 6 | PASS | 多选 · Cmd+click · 选中多 node · 可批量删除 · 可整体拖动 | screenshots/P2C-06/b06-multiselect-drag-delete.json | selected=2, nodes=5->3
P2C-06 | 7 | PASS | undo/redo · 最近 20 步 · Cmd+Z / Shift+Cmd+Z | screenshots/P2C-06/b07-undo-redo.json | history=20, nodes=21->22
P2C-06 | 8 | PASS | save · Cmd+S · 验证失败显 toast + 点亮错误 node · 成功 toast + 顶部 "saved {time}" | screenshots/P2C-06/b08-save-validation.json | ERROR校验失败，请先修复高亮节点 / saved 14:18
P2C-06 | 9 | PASS | dirty 指示 · 顶栏文字 "• 未保存" 琥珀 | screenshots/P2C-06/b09-dirty-indicator.json | • 未保存 / --warning
P2C-06 | 10 | PASS | test · Cmd+Shift+T · 底部 slide up 测试面板 · 输入 trigger payload · 运行 · 每 node 亮绿/红 | screenshots/P2C-06/b10-test-panel.json | passed=2, failed=1
P2C-06 | 11 | PASS | publish · 弹出 modal · 显示 diff · 输入版本号 · 确认后部署 | screenshots/P2C-06/b11-publish-modal.json | 发布工作流 / SUCCESS工作流已部署
P2C-06 | 12 | PASS | validation 规则 · 必连触发器 · 必连至少 1 action · 无孤立 node · 无环 · 具体错误 UI 提示 | screenshots/P2C-06/b12-validation-rules.json | 至少需要一个 trigger。 | 至少需要一个 action。 | 存在孤立节点，请补上连线或删除它们。 | 存在环路，请打断循环依赖。
P2C-06 | 13 | PASS | canvas 缩放 · Cmd+scroll / Cmd+= / Cmd+- / Cmd+0 重置 | screenshots/P2C-06/b13-zoom-shortcuts.json | 1 -> 1.1 -> 1.2000000000000002 -> 1.1 -> 1
P2C-06 | 14 | PASS | minimap 右下角 200×150 · 反映 canvas 整体 | screenshots/P2C-06/b14-minimap.json | 200x150, rects=6
P2C-06 | 15 | PASS | 键盘选中后 Delete / Backspace 删 node · Escape 取消选中 | screenshots/P2C-06/b15-delete-backspace-escape.json | nodes=1->0
P2C-06 | 16 | PASS | 复制粘贴 Cmd+C / Cmd+V · 复制到右下偏移 20px | screenshots/P2C-06/b16-copy-paste.json | node-cross-assistant -> node-cross-assistant-copy-5841-12
P2C-06 | 17 | PASS | URL `?workflowId=xxx&template=yyy` · 加载对应模板或已存 workflow | screenshots/P2C-06/b17-url-loading.json | workflow:workflow-incident-brief / template:release-qa
P2C-06 | 18 | PASS | 退出未保存 · `beforeunload` 确认 | screenshots/P2C-06/b18-beforeunload.json | dirty=true, prevented=true
P2C-07 | 1 | PASS | list · 每 plugin 一行 · 图标 32 + 名 + 版本 + 状态 + 动作 | screenshots/P2C-07/p01-plugin-row.json | GitHub / update-available
P2C-07 | 2 | PASS | 状态 · installed / enabled / disabled / error / update-available | screenshots/P2C-07/p02-statuses.json | update-available, enabled, installed, disabled, error
P2C-07 | 3 | PASS | version 比较 · 当前版本旁显"最新 v2.1.0 ↑" 琥珀 link · 点跳 update modal | screenshots/P2C-07/p03-version-link.json | 最新 v2.1.0 ↑ / 更新插件
P2C-07 | 4 | PASS | enable/disable toggle · 立刻生效 · error 状态 toggle 禁用 | screenshots/P2C-07/p04-toggle-state.json | enabled -> disabled
P2C-07 | 5 | PASS | permissions · 点击 row 展开详情 · 列出 scopes + filesystem + network 权限 · 与 manifest 对齐 | screenshots/P2C-07/p05-permissions.json | repo, pull_request, workflow, filesystem:read, network:github.com
P2C-07 | 6 | PASS | error row · 红边 + 错误文字 + "查看日志" 按钮 · 打开日志 drawer | screenshots/P2C-07/p06-error-log-drawer.json | rgb(165, 48, 40) / 插件日志
P2C-07 | 7 | PASS | update · modal 显示 changelog + permissions diff · 需确认新权限 | screenshots/P2C-07/p07-update-confirm.json | 2.1.0 / enabled
P2C-07 | 8 | PASS | uninstall · 二次确认 · 保留数据 toggle | screenshots/P2C-07/p08-uninstall.json | 卸载插件 / remaining=4
P2C-07 | 9 | PASS | 顶部 search · 过滤名称 + 描述 · 右 "+ 添加" 按钮 · 接 registry 或手动上传 | screenshots/P2C-07/p09-search-add.json | plugin-computer-use / 添加插件
P2C-08 | 1 | PASS | list · 每 server 一行 · 名 + URL + 状态 + 工具数 + 动作 | screenshots/P2C-08/m01-server-list.json | Docs MCP / connected
P2C-08 | 2 | PASS | 状态 · connected(绿)/ disconnected(灰)/ connecting(琥珀脉冲)/ error(红) | screenshots/P2C-08/m02-status-dots.json | mcp-docs:--success, mcp-warehouse:--ink-3, mcp-github:--warning, mcp-browse:--error
P2C-08 | 3 | PASS | 点 row 展开工具列表 · 每 tool 名 + 描述 + schema 按钮(打开 JSON drawer) | screenshots/P2C-08/m03-tools-schema.json | search_docs,fetch_page,list_sections / Tool schema
P2C-08 | 4 | PASS | add · modal 3 字段 · name + url + auth token · 连接测试按钮 | screenshots/P2C-08/m04-add-modal.json | 添加 MCP 服务器 / SUCCESSMCP 测试连接通过
P2C-08 | 5 | PASS | disconnected · "重连" 按钮 + "查看错误" link | screenshots/P2C-08/m05-reconnect-error.json | 重连 / connected
P2C-08 | 6 | PASS | remove · 二次确认 · 确认后 row fade-out 移除 | screenshots/P2C-08/m06-remove-fade.json | 移除这个 MCP 服务器? / rows=3
P2C-08 | 7 | PASS | empty · "还没有连接的 MCP 服务器" + add CTA + 文档 link | screenshots/P2C-08/m07-empty-state.json | 还没有连接的 MCP 服务器 / 添加服务器
P2C-08 | 8 | PASS | server 实时 ping · 每 30s · 状态 dot 自动更新 | screenshots/P2C-08/m08-ping-trace.json | 最近 ping 14:18:00 -> 最近 ping 14:18:30
P2D-01 | 1 | PASS | 双列 · 左 list 420 · 右 config drawer 460(可关闭后右列消失 · 只 list) | screenshots/P2D-01/n01-channel-layout.json | list=420, drawer=460, closed=true
P2D-01 | 2 | PASS | channel row · type 图标 24 + 名 + 状态 dot + 最近 session time + menu | screenshots/P2D-01/n02-channel-row.json | Amazon 店铺 / recent=14:18
P2D-01 | 3 | PASS | 状态 · healthy / degraded / error · degraded 时显示具体指标(latency 高 / 限流) | screenshots/P2D-01/n03-channel-statuses.json | channel-amazon:healthy, channel-shopify:degraded, channel-tiktok:error
P2D-01 | 4 | PASS | click row · 右侧 config drawer 滑入 · 200ms | screenshots/P2D-01/n04-channel-drawer-transition.json | trace-drawer-slide-in / 0.2s
P2D-01 | 5 | PASS | ChannelConfigForm 复用 · 包含 webhook url / auth / rate-limit / timeout 字段 | screenshots/P2D-01/n05-channel-config-form.json | source=true, fields=4/4
P2D-01 | 6 | PASS | "测试连接" 按钮 · loading → result toast · 失败显示具体 error | screenshots/P2D-01/n06-channel-test.json | 测试中... -> 连接测试失败
P2D-01 | 7 | PASS | save · dirty 状态持续显 · diff 对比按钮(旧 vs 新) | screenshots/P2D-01/n07-channel-diff.json | 有 1 项配置未保存 / dirtyKeys=auth
P2D-01 | 8 | PASS | add channel · 顶部 + 按钮 · 弹 modal · 选 type → 填配置 | screenshots/P2D-01/n08-channel-add-modal.json | 添加渠道 / created=channel-github-8170
P2D-01 | 9 | PASS | 最近 sessions 用 `useChannelSessions` hook · 每 channel 显示最近 5 个 session | screenshots/P2D-01/n09-channel-sessions-hook.json | hook=true, sessions=5
P2D-01 | 10 | PASS | disable channel · 二次确认 · disabled 的 row 整行 opacity 0.5 · sessions 显示历史 | screenshots/P2D-01/n10-channel-disable.json | 停用这个渠道? / opacity=0.5 / sessions=4
P2D-01 | 11 | PASS | 空态 · "连接你的第一个渠道" + 支持渠道 grid(逐个 provider icon) | screenshots/P2D-01/n11-channel-empty.json | 连接你的第一个渠道 / providers=5
P2D-02 | 1 | PASS | 3 tab · Queued · Running · History · 计数 badge 实时 | screenshots/P2D-02/o01-automation-tabs.json | queued 2 -> 1
P2D-02 | 2 | PASS | 每 tab 一列表 · row 含 name + 触发源 + 进度 bar(running)/ 时长 + actions | screenshots/P2D-02/o02-automation-row-shape.json | queued=午后审批窗口, runningProgress=true, history=failed
P2D-02 | 3 | PASS | running row · progress bar 实时更新 · 来自 `advanceClock` + mock state | screenshots/P2D-02/o03-automation-progress.json | 98% -> 100%
P2D-02 | 4 | PASS | queued row · 显示"将于 {time} 运行" · 可 cancel | screenshots/P2D-02/o04-automation-queued.json | cron / 将于 15:00 运行 / / cancel
P2D-02 | 5 | PASS | history · 最近 50 条 · paginate 或虚拟化 · 按时间倒序 | screenshots/P2D-02/o05-automation-history.json | history=50, page1=10, descending=true
P2D-02 | 6 | PASS | click row · 打开 logs drawer · 日志行带 timestamp + level(info/warn/error) | screenshots/P2D-02/o06-automation-log-drawer.json | 风险汇总简报 / 14:09:18 | info | 开始汇总 provider 与渠道告警。
P2D-02 | 7 | PASS | logs drawer · tail 模式 · 顶部 auto-scroll toggle · search 过滤 | screenshots/P2D-02/o07-automation-log-tail.json | autoScroll true -> false, filtered=1
P2D-02 | 8 | PASS | failed automation · retry 按钮 · 点击 dispatch mock event 重新 queue | screenshots/P2D-02/o08-automation-retry.json | history-1 -> history-1-retry
P2D-02 | 9 | PASS | 空 · 每 tab 有独立文案 · Queued "没有在排队的任务" | screenshots/P2D-02/o09-automation-empty-copy.json | 没有在排队的任务 / 当前没有运行中的任务 / 还没有历史记录
P2D-02 | 10 | PASS | filter · 按 trigger source(cron / manual / webhook / chat)· chip 多选 | screenshots/P2D-02/o10-automation-filters.json | filters=cron,manual rows=1
P2D-03 | 1 | PASS | 双列 · 左 filter + list 420 · 右 detail preview flex | screenshots/P2D-03/p01-sessions-layout.json | 420px 388px
P2D-03 | 2 | PASS | filter · time(<24h / 7d / 30d / custom)· channel(multi)· type(chat / workflow / automation) | screenshots/P2D-03/p02-session-filters.json | time=7d, channels=channel-shopify, types=automation
P2D-03 | 3 | PASS | session row · title + channel icon + last message time + message count + status | screenshots/P2D-03/p03-session-row.json | 跨境补货监控 / active
P2D-03 | 4 | PASS | click row · 右侧 preview · 显示最近 10 条消息 · 不可编辑 · "打开完整会话" 按钮跳 /chat?session=xxx | screenshots/P2D-03/p04-session-preview.json | preview=10, href=/chat?session=session-refund-triage
P2D-03 | 5 | PASS | preview 不自动 realtime 更新 · 靠 refresh 按钮 | screenshots/P2D-03/p05-session-refresh.json | stable=true, refreshed=true
P2D-03 | 6 | PASS | 顶部 search · 搜 session title + message content(mock 模拟) | screenshots/P2D-03/p06-session-search.json | title=session-refund-triage content=10
P2D-03 | 7 | PASS | session 操作 · rename · archive · delete · 每个二次确认 | screenshots/P2D-03/p07-session-actions.json | 重命名会话? / archivedGone=0 / deletedGone=0
P2D-03 | 8 | PASS | keyboard · Tab list · 方向键选 session · Enter 打开 /chat | screenshots/P2D-03/p08-session-keyboard.json | selected=session-provider-circuit, route=/chat?session=session-provider-circuit
P2D-03 | 9 | PASS | empty · "开始你的第一次对话" + CTA | screenshots/P2D-03/p09-session-empty.json | 开始你的第一次对话 / /chat
P2D-03 | 10 | PASS | filter active 时右上角显 filter chip 组 · 点 × 移除单个 · clear all 按钮 | screenshots/P2D-03/p10-session-filter-chips.json | chips=3, afterClearAll=0
P2D-04 | 1 | PASS | 顶 summary 3 数 · "本月 $X" + "预测 $Y" + "上限 $Z" · 大数字 font serif 40 · 每数下有 label + 对比上月变化 | screenshots/P2D-04/q01-usage-summary.json | Spend:US$10,980 | Forecast:US$12,298 | Cap:US$12,000
P2D-04 | 2 | PASS | 主图 · area chart 横轴 日期 / 纵轴 $ · 高 320 · 响应式 | screenshots/P2D-04/q02-usage-chart-size.json | wide=1242x320, narrow=894x320
P2D-04 | 3 | PASS | chart hover · tooltip 显示当日 $ + 拆分 provider | screenshots/P2D-04/q03-usage-tooltip.json | 4/4 / US$490 / providers=5
P2D-04 | 4 | PASS | chart 预警阈值线 · 水平虚线 · 红色 · 显示 "budget cap" | screenshots/P2D-04/q04-usage-cap-line.json | 6 6 / --error / budget cap
P2D-04 | 5 | PASS | 时间窗 toggle · this-month / last-30d / custom · URL `?range=` 同步 | screenshots/P2D-04/q05-usage-range-query.json | /usage?dev=1&__state=this-month&range=last-30d -> /usage?dev=1&__state=this-month&range=custom
P2D-04 | 6 | PASS | provider 表 · 每 provider 一行 · 用量 + 成本 + % 占比 bar + 趋势(up/down/flat) | screenshots/P2D-04/q06-usage-provider-table.json | rows=5 / trend=up
P2D-04 | 7 | PASS | sort provider 表 · 按成本 / 用量 · 点 header 切换 | screenshots/P2D-04/q07-usage-sort.json | cost[0]=provider-openai, usage[0]=provider-local
P2D-04 | 8 | PASS | provider filter · 表头 chips · 主图同步 | screenshots/P2D-04/q08-usage-provider-filter.json | filters=provider-openai rows=1 tooltipProviders=1
P2D-04 | 9 | PASS | 超支预警 · 当预测 > 上限 · 页顶 red banner + "调整预算" 按钮跳 /settings?tab=runtime | screenshots/P2D-04/q09-usage-budget-banner.json | true / /settings?tab=runtime
P2D-04 | 10 | PASS | UsageCharts 组件复用 · props 不改 | screenshots/P2D-04/q10-usagecharts-props.json | fields=cap,points
P2D-04 | 11 | PASS | export CSV 按钮 · 下载当前时间窗原始数据 | screenshots/P2D-04/q11-usage-export.json | usage-last-30d.csv / rows=31
P2D-04 | 12 | PASS | 空 · "还没有用量数据" · "开始对话后这里会出现详情" | screenshots/P2D-04/q12-usage-empty.json | 还没有用量数据 / 开始对话后这里会出现详情。
P2D-05 | 1 | PASS | 双列 · 左 tree 260 + 右 detail flex | screenshots/P2D-05/r01-memory-layout.json | tree=260, detail=548
P2D-05 | 2 | PASS | tree · 按 scope 分组(user / team / project / session)· 可展开 · 每 scope 计数 | screenshots/P2D-05/r02-memory-tree.json | scopes=user,team,project,session expanded=user,project,session
P2D-05 | 3 | PASS | 选 memory · 右显 content + metadata + source session link | screenshots/P2D-05/r03-memory-detail.json | 记忆树 / /sessions?sessionId=session-provider-circuit
P2D-05 | 4 | PASS | 编辑 · 点 "编辑" 按钮进入编辑模式 · textarea · save/cancel | screenshots/P2D-05/r04-memory-edit-mode.json | 保存 / 取消
P2D-05 | 5 | PASS | 编辑需二次确认 · "memory 将影响未来所有会话" | screenshots/P2D-05/r05-memory-save-confirm.json | 保存这条记忆? / memory 将影响未来所有会话
P2D-05 | 6 | PASS | delete · 二次确认 · 输入 memory title 验证 | screenshots/P2D-05/r06-memory-delete-confirm.json | 删除这条记忆? / disabled true -> false
P2D-05 | 7 | PASS | source session link · 点跳 /sessions?sessionId=xxx | screenshots/P2D-05/r07-memory-source-link.json | /sessions?sessionId=session-provider-circuit -> /sessions?sessionId=session-provider-circuit
P2D-05 | 8 | PASS | 顶部 search · 全文搜索 · 命中关键词黄色高亮 | screenshots/P2D-05/r08-memory-search-highlight.json | 预警 / rows=1
P2D-05 | 9 | PASS | empty · "Friday 还没有记住任何事" + 说明 link | screenshots/P2D-05/r09-memory-empty.json | Friday 还没有记住任何事 / /memory?help=1
P2D-06 | 1 | PASS | grid · 每 device 一 card 240×180 · 型号 + 状态 dot + 心跳 + 资源 bar + 管理 | screenshots/P2D-06/s01-fleet-grid.json | cards=3 size=240x235.65625
P2D-06 | 2 | PASS | 状态 · online(绿)/ offline(灰)/ warning(黄) | screenshots/P2D-06/s02-fleet-status-colors.json | device-mac-mini:--success, device-apac-gateway:--ink-3, device-eu-runner:--warning
P2D-06 | 3 | PASS | 心跳 · 秒级 "最近心跳 3s 前" · 随 advanceClock 实时更新 | screenshots/P2D-06/s03-fleet-heartbeat.json | 最近心跳 3s 前 -> 最近心跳 5s 前
P2D-06 | 4 | PASS | 资源 bar · CPU / RAM / disk 三条 · 超过 80% 变黄 · 超过 95% 红 | screenshots/P2D-06/s04-fleet-resource-bars.json | --warning / --error / --success
P2D-06 | 5 | PASS | 管理 · menu · restart / logs / remove | screenshots/P2D-06/s05-fleet-manage-menu.json | restart, logs, remove
P2D-06 | 6 | PASS | add device · + 按钮 · modal · 显示 install 命令 mono 字体 | screenshots/P2D-06/s06-fleet-add-modal.json | 添加设备 / curl -fsSL https://fleet.friday.dev/install.sh | sh -s -- --name Warehouse Edge
P2D-06 | 7 | PASS | select device · card 琥珀 border + detail drawer 右出 | screenshots/P2D-06/s07-fleet-selected-drawer.json | true / --accent
P2D-06 | 8 | PASS | empty · "还没有连接设备" | screenshots/P2D-06/s08-fleet-empty.json | 还没有连接设备
P2E-01 | 1 | PASS | 全屏居中 · 卡片 440×auto · radius 28 · shadow 大柔光 | screenshots/P2E-01/lg01-login-layout.json | size=440x580.609375 center=(0,31.9921875)
P2E-01 | 2 | PASS | logo 顶部 · Fraunces "Friday" · 下副标题 "你的工作助手" | screenshots/P2E-01/lg02-login-brand.json | Friday / 你的工作助手
P2E-01 | 3 | PASS | email 输入 + "发送登录链接" 按钮 · 按钮琥珀 fill | screenshots/P2E-01/lg03-login-email-cta.json | 发送登录链接 / --accent
P2E-01 | 4 | PASS | OAuth 区(分割线 + 按钮)· Google / GitHub · 左图标右文字 | screenshots/P2E-01/lg04-login-oauth.json | 或者 / Google, GitHub
P2E-01 | 5 | PASS | submit 失败不清空 email · 错误显红色 inline "邮箱格式不正确" 或 "发送失败 · 请重试" | screenshots/P2E-01/lg05-login-errors.json | 邮箱格式不正确 / 发送失败 · 请重试
P2E-01 | 6 | PASS | submit 成功 · 全卡 fade 到 "已发送" 态 · 显示 email · "没收到?重试" link | screenshots/P2E-01/lg06-login-success.json | 已发送 / ops@friday.dev
P2E-01 | 7 | PASS | 登录成功跳 `state.redirectTo` · 默认 / | screenshots/P2E-01/lg07-login-redirect.json | default=/ custom=/command-center
P2E-02 | 1 | PASS | 4 步 · profile / providers / first-skill / done · stepper 顶部 | screenshots/P2E-02/su01-setup-stepper.json | Profile / Providers / First skill / Done
P2E-02 | 2 | PASS | step 1 profile · name + role select + company optional | screenshots/P2E-02/su02-setup-profile-fields.json | Jarvis / Operator / Friday Labs
P2E-02 | 3 | PASS | step 2 providers · 至少配置 1 个 · 提供 OpenAI / Anthropic / 本地 · 每个有 key 输入 + 测试连接 | screenshots/P2E-02/su03-setup-providers.json | providers=3 configured=openai,local
P2E-02 | 4 | PASS | step 3 first-skill · 3 推荐 skill · 点卡片 install · 至少选 1 · 或跳过 | screenshots/P2E-02/su04-setup-skills.json | cards=3 selected=1 skippedStep=4
P2E-02 | 5 | PASS | step 4 done · "准备就绪" + 跳 /home | screenshots/P2E-02/su05-setup-done.json | 准备就绪 / 进入首页
P2E-02 | 6 | PASS | 每步 prev / next · step 1 无 prev | screenshots/P2E-02/su06-setup-prev-next.json | step1 prev=null step2 prev=上一步
P2E-02 | 7 | PASS | 断线 · 重进 · 从上次完成步继续(`useSetupStatusQuery` 提供) | screenshots/P2E-02/su07-setup-resume.json | lastCompleted=2 resumedStep=3
P2E-02 | 8 | PASS | 每步 URL `?step=1..4` · 刷新不丢 | screenshots/P2E-02/su08-setup-step-url.json | 1:1 / 2:2 / 3:3 / 4:4
P2E-02 | 9 | PASS | 退出需确认 · "设置未完成" | screenshots/P2E-02/su09-setup-exit-confirm.json | 设置未完成 / setup-leave
P2E-02 | 10 | PASS | 完成后 POST `setup/complete` 再跳转 | screenshots/P2E-02/su10-setup-complete-post.json | /home / setup/complete
P2E-02 | 11 | PASS | 失败降级 · "稍后设置" link · 跳 /onboarding | screenshots/P2E-02/su11-setup-later.json | /onboarding -> /onboarding
P2E-03 | 1 | PASS | 3-4 屏滚动式 · 每屏满视口 · scroll snap · 大标题 + 插图 + 副文 | screenshots/P2E-03/ob01-onboarding-layout.json | screens=4 snap=y mandatory
P2E-03 | 2 | PASS | 屏 1 welcome · "欢迎使用 Friday" · 3 句价值主张 | screenshots/P2E-03/ob02-onboarding-welcome.json | 欢迎使用 Friday / values=3
P2E-03 | 3 | PASS | 屏 2 profile-pick · 4 cards(个人 / 小团队 / 企业 / 开发者)· 选中后记录 `useUserProfile` | screenshots/P2E-03/ob03-onboarding-profile.json | cards=4 profile=team
P2E-03 | 4 | PASS | 屏 3 pack-pick · 基于 profile 推荐 3 pack · 可多选 · 也可跳过 | screenshots/P2E-03/ob04-onboarding-packs.json | recommendations=3 selected=2
P2E-03 | 5 | PASS | 屏 4 done · "开始吧" + CTA 跳 /home | screenshots/P2E-03/ob05-onboarding-done.json | 开始吧 -> /home
P2E-03 | 6 | PASS | 右上跳过 · 写 profile 默认 · 跳 /home | screenshots/P2E-03/ob06-onboarding-skip.json | solo -> /home
P2E-03 | 7 | PASS | scroll 指示 · 右侧 dots 3-4 个 · click 跳到对应屏 | screenshots/P2E-03/ob07-onboarding-dots.json | dots=4 active=2
P2E-03 | 8 | PASS | keyboard · ↓ / space 下屏 · ↑ 上屏 · Enter CTA | screenshots/P2E-03/ob08-onboarding-keyboard.json | screens=1->2->1 final=/home
P2E-04 | 1 | PASS | URL 参数 wizardId · 从 guided registry 加载 · 不存在跳 /assistant | screenshots/P2E-04/gf01-guided-routing.json | inventory-triage / invalid->/assistant
P2E-04 | 2 | PASS | 布局 · 顶 journey tracker 80px · 主区 2 列 · 左 step 自动 · 右 investigation panel 360 | screenshots/P2E-04/gf02-guided-layout.json | tracker=88.15625 panel=360
P2E-04 | 3 | PASS | journey tracker · 显示所有 step · 完成绿勾 · 当前琥珀 · 未来灰 · 分支 fork 图 | screenshots/P2E-04/gf03-guided-tracker.json | icons=•,⑂,3,4 classes=guided-step-pill is-complete | guided-step-pill is-current | guided-step-pill | guided-step-pill
P2E-04 | 4 | PASS | step 内容 · 复用 goal-card / choice-card / one-click-action / plan-review-visual · 按 step.kind 渲染 | screenshots/P2E-04/gf04-guided-step-kinds.json | intent -> choice -> action -> review
P2E-04 | 5 | PASS | investigation panel · 显示 Friday 正在做的调查 · 有 spinner · 可展开详情 | screenshots/P2E-04/gf05-guided-investigation.json | Friday 正在调查 / detail=true
P2E-04 | 6 | PASS | step-progress bar 顶部 · 当前 step / 总 step · 百分比 | screenshots/P2E-04/gf06-guided-progress.json | 第 3 / 4 步 / 50%
P2E-04 | 7 | PASS | choice-card · 选择后自动进下一 step · 轻过渡 | screenshots/P2E-04/gf07-guided-choice-auto-next.json | latency=199.4ms
P2E-04 | 8 | PASS | 回退 · 左下角 "上一步" button · 保留 state · 不 re-fetch | screenshots/P2E-04/gf08-guided-prev.json | step 2 -> 1, fetch=1
P2E-04 | 9 | PASS | 完成所有 step · 跳 final step · CTA 返回来源页 | screenshots/P2E-04/gf09-guided-final.json | 引导已完成 / /assistant?packId=industry-cross-border-ecommerce
P2E-04 | 10 | PASS | state 持久化 `guided-flow-{wizardId}` localStorage · 断线恢复 | screenshots/P2E-04/gf10-guided-storage.json | storageStep=2 restored=2
P2E-04 | 11 | PASS | 空 wizardId 路径 · show "找不到引导" + 跳 /assistant link | screenshots/P2E-04/gf11-guided-empty.json | 找不到引导 / /assistant
P2E-04 | 12 | PASS | skip current step · 允许 · 标记 step skipped(灰勾)· tracker 显示 | screenshots/P2E-04/gf12-guided-skip.json | ✓ / --ink-3
P2E-04 | 13 | PASS | 每 step 右下 "寻求帮助" · 打开 contextual-help | screenshots/P2E-04/gf13-guided-help.json | 上下文帮助已打开 · Friday 会把当前 step、调查摘要和回退边界一起带给你。
P2E-05 | 1 | PASS | 3 栏 · 左 agent list 280 · 中 activity timeline flex · 右 control 320 | screenshots/P2E-05/cc01-command-layout.json | 280 / 488 / 320
P2E-05 | 2 | PASS | agent list · 每 agent 一 card · name + status + current run | screenshots/P2E-05/cc02-command-agents.json | agent-ops, agent-qa, agent-builder
P2E-05 | 3 | PASS | timeline · `ActivityTimeline` 复用 · 显示所有 agent 的事件合流 | screenshots/P2E-05/cc03-command-activitytimeline.json | callSite=true props=expandedEventId,lanes,locale,runs
P2E-05 | 4 | PASS | 多 run 并列 · 用 lane 区分 · 每 agent 一 lane | screenshots/P2E-05/cc04-command-lanes.json | agent-ops:1 / agent-qa:1 / agent-builder:1
P2E-05 | 5 | PASS | control · 选中 agent 后显示 · pause / resume / stop / inspect | screenshots/P2E-05/cc05-command-control.json | QA Agent / inspect
P2E-05 | 6 | PASS | summary panel 顶部 · 总运行数 + 失败率 + 平均时长 | screenshots/P2E-05/cc06-command-summary.json | 3 / 0% / 17 分 0 秒
P2E-05 | 7 | PASS | 点 timeline event · 展开 context | screenshots/P2E-05/cc07-command-event-expand.json | run-replenish-watch / lines=3
P2E-05 | 8 | PASS | 空 · "暂无 agent 活动" | screenshots/P2E-05/cc08-command-empty.json | 暂无 agent 活动
P2E-05 | 9 | PASS | mobile/窄屏 · 单列 · 只显 timeline | screenshots/P2E-05/cc09-command-mobile.json | 988px / asides=none,none
P2F-01 | 1 | PASS | route 切换 < 150ms · 用 `recordNavVisit` + `completeClientRouteTransition` 埋点 | screenshots/P2F-01/f01-01-nav-metric.json | duration=1.6ms route=/assistant
P2F-01 | 2 | PASS | 切换不 FOUC · 下页 mount 前旧页保留 100ms | screenshots/P2F-01/f01-02-no-fouc.json | hold route=/home, overlay=shell-transition-hold-fade, after=/assistant
P2F-01 | 3 | PASS | rail 当前路由高亮 · 点击前琥珀 · 点击后立即 active | screenshots/P2F-01/f01-03-rail-highlight.json | before=/home during=/assistant
P2F-01 | 4 | PASS | scroll 位置保留 · 回退 / 前进 恢复 · 新进 top | screenshots/P2F-01/f01-04-scroll-restore.json | home=1000 restored=1000 assistant=0
P2F-01 | 5 | PASS | right rail slot 切换 · fade 150ms · 不闪 · 不 layout shift | screenshots/P2F-01/f01-05-right-rail.json | overlay=shell-transition-right-rail-fade stable=true
P2F-01 | 6 | PASS | topbar breadcrumb 同步 · URL 变时 300ms 内更新 | screenshots/P2F-01/f01-06-breadcrumbs.json | latency=0.0ms
P2F-01 | 7 | PASS | loading 超过 500ms 才显 splash · 小于不显 | screenshots/P2F-01/f01-07-splash.json | short=false long=true
P2F-01 | 8 | PASS | 切换失败 · inline error 保留上页 · 不替换 | screenshots/P2F-01/f01-08-nav-fail.json | route=/home retry=retry-last-navigation
P2F-02 | 1 | PASS | Tweaks 面板右下浮层 240×auto · 开关在 topbar | screenshots/P2F-02/f02-01-panel.json | width=240px right=24px bottom=24px
P2F-02 | 2 | PASS | 分组 5 个 · Density / Radius / Motion / Accent / Locale Preview | screenshots/P2F-02/f02-02-groups.json | 密度 / 圆角 / 动效 / 主色强度 / 语言预览
P2F-02 | 3 | PASS | Density · compact / cozy / comfortable · 影响 padding + line-height | screenshots/P2F-02/f02-03-density.json | padding=14px/17px/18px
P2F-02 | 4 | PASS | Radius · sharp(4) / default(14) / soft(22) · 全局 radius 变量替换 | screenshots/P2F-02/f02-04-radius.json | 4px / 14px / 22px
P2F-02 | 5 | PASS | Motion · full / reduced / none · 匹配 prefers-reduced-motion 默认 | screenshots/P2F-02/f02-05-motion.json | default=reduced none=0.01ms linear
P2F-02 | 6 | PASS | Accent · intensity 50-150% · 改 --accent 饱和度 | screenshots/P2F-02/f02-06-accent.json | rgb(185, 144, 103) | rgb(196, 135, 83) | rgb(154, 93, 37) | rgb(205, 111, 38) | rgb(210, 95, 20)
P2F-02 | 7 | PASS | Locale Preview · zh / en · 不改 `friday-locale` · 只临时 preview | screenshots/P2F-02/f02-07-locale-preview.json | locale=zh render=en storage=zh
P2F-02 | 8 | PASS | 切换立刻生效 · CSS variables 改 · 无 flash | screenshots/P2F-02/f02-08-live-apply.json | 14px -> 4px
P2F-02 | 9 | PASS | localStorage 持久化 · 键 `friday-tweaks` · reload 保留 | screenshots/P2F-02/f02-09-persist.json | {"density":"compact","radius":"soft","motion":"full","accentIntensity":"150","localePreview":"zh"}
P2F-03 | 1 | PASS | 每页 empty 文案双语 · zh + en 对齐 | screenshots/P2F-03/f03-01-bilingual.json | 6 audited routes
P2F-03 | 2 | PASS | 语气统一 · 友好 · 有引导 · 不用 "no data"(太冷)或 "好像什么都没有"(太幼稚) | screenshots/P2F-03/f03-02-tone.json | no banned cold/childish phrasing found
P2F-03 | 3 | PASS | 每 empty 有下一步 CTA · 明确动作 · 带 arrow icon | screenshots/P2F-03/f03-03-cta.json | Home:1, Chat:3, Channels:1, Usage:1, Memory:1, Fleet:1
P2F-03 | 4 | PASS | 无 lorem / TBD · grep 检测 0 命中 | screenshots/P2F-03/f03-04-grep.json | matches=0
P2F-03 | 5 | PASS | 插图 · 米色 line-art SVG · 最多一个 · 不过度 | screenshots/P2F-03/f03-05-line-art.json | Home:1, Chat:1, Channels:1, Usage:1, Memory:1, Fleet:1
P2F-03 | 6 | PASS | copy 审核清单单独 HTML · 每条 page 一行 · zh 列 + en 列 + 审校人 | screenshots/P2F-03/f03-06-copy-audit.json | 6 rows in screenshots/qa/p2f-03-copy-audit.html
P2F-04 | 1 | PASS | 每页 error 有 retry 按钮 · 主色 | screenshots/P2F-04/f04-01-retry-primary.json | 重试 / action-button action-button-primary
P2F-04 | 2 | PASS | "为什么?" 可展开 · 默认折叠 · 展开技术描述(人话) | screenshots/P2F-04/f04-02-why.json | 刷新助手收件箱时遇到暂时性错误。
P2F-04 | 3 | PASS | 不暴露堆栈 / SQL / internal paths | screenshots/P2F-04/f04-03-sanitized.json | no stack/sql/internal path leak
P2F-04 | 4 | PASS | 每 error 有 errorId · 可 copy · 7 字母数字 | screenshots/P2F-04/f04-04-error-id.json | OUP0E69
P2F-04 | 5 | PASS | 双语 | screenshots/P2F-04/f04-05-bilingual.json | Assistant 快照暂时不可用 -> Assistant snapshot is unavailable
P2F-04 | 6 | PASS | 网络断 · 特定文案 "你似乎离线了 · 正在自动重试" | screenshots/P2F-04/f04-06-offline.json | 你似乎离线了 · 正在自动重试
P2F-04 | 7 | PASS | rate-limited · "操作太频繁 · 请 N 秒后再试" + 倒计时 | screenshots/P2F-04/f04-07-rate-limit.json | 操作太频繁 · 请 5 秒后再试 -> 操作太频繁 · 请 4 秒后再试
P2F-05 | 1 | PASS | Tab 顺序符合视觉顺序 · 无跳跃 | screenshots/P2F-05/f05-01-tab-order.json | 导航已按响应式自动收起 -> 首页 -> 对话 -> 助手收件箱 -> 可观测性 -> 入口库 -> 技能 -> 工作流 -> 渠道 -> 自动化 -> 会话记录 -> 用量 -> 记忆 -> 设备 -> 插件 -> MCP
P2F-05 | 2 | PASS | focus ring 2px 琥珀 + 2px offset · 所有可交互元素 | screenshots/P2F-05/f05-02-focus-ring.json | 2px rgb(154, 93, 37) offset=2px
P2F-05 | 3 | PASS | 所有 icon-only 按钮有 aria-label | screenshots/P2F-05/f05-03-icon-labels.json | scanned=48 missing=0
P2F-05 | 4 | PASS | modal / drawer Escape 关 · 开时 focus trap · 关时 focus 回触发源 | screenshots/P2F-05/f05-04-modal-drawer.json | modal=toggle-command-palette drawer=open-approval-drawer
P2F-05 | 5 | PASS | 列表方向键 · ↑↓ 移动 · Home/End 首末 | screenshots/P2F-05/f05-05-list-keys.json | 首页 -> 对话 -> 打开 Workflow 测试面板
P2F-05 | 6 | PASS | form label 关联 · input 有 id + label for | screenshots/P2F-05/f05-06-form-labels.json | provider=4 alert=2
P2F-05 | 7 | PASS | 错误与 input 用 aria-describedby 连 | screenshots/P2F-05/f05-07-describedby.json | observability-alert-name-error, observability-alert-threshold-error
P2F-05 | 8 | PASS | 运行 axe-core · 0 critical · 记录 report | screenshots/P2F-05/f05-08-axe-report.json | routes=33 critical=0
P2F-05 | 9 | PASS | 色对比 WCAG AA · body text ≥ 4.5 · large ≥ 3 | screenshots/P2F-05/f05-09-contrast.json | home-body:14.71, home-heading:14.33, assistant-heading:15.64, settings-hint:14.71, usage-table:14.71
P2F-05 | 10 | PASS | Cmd+K 可导航到所有路由 · 14+ 条目 | screenshots/P2F-05/f05-10-command-palette.json | count=27
P2F-06 | 1 | PASS | 1280 下三栏(rail 240 · main 996 · rail collapsible)· 无横向滚动 | screenshots/P2F-06/f06-01-1280-home.json | rail=64 main=996 right=220
P2F-06 | 2 | PASS | 1440 下三栏舒展 · rail 可选 collapse | screenshots/P2F-06/f06-02-1440-home.json | rail=240 main=896 right=304
P2F-06 | 3 | PASS | 1920 下多列布局激活(如 pack 4 列) | screenshots/P2F-06/f06-03-1920-packs.json | columns=5
P2F-06 | 4 | PASS | 每档截图 5 个关键页 · 对比放入 qa-report | screenshots/P2F-06/f06-04-screenshot-matrix.json | 15 responsive screenshots
P2F-06 | 5 | PASS | 字体大小不缩 · 仍 14/15 基线 | screenshots/P2F-06/f06-05-font-baseline.json | 1280:15px/14px, 1440:15px/14px, 1920:15px/14px
P2F-06 | 6 | PASS | rail 64 窄态下 icon 居中 + tooltip hover 显示文字 | screenshots/P2F-06/f06-06-compact-rail-tooltip.json | rail=64 tooltip="首页"
P2F-08 | 1 | PASS | Ctrl+P · Home / Assistant / Observability 三页支持 · 其他页显示 "该页不支持打印" | screenshots/P2F-08/f08-01-supported-routes.json | supported=3 unsupported=该页不支持打印
P2F-08 | 2 | PASS | print CSS · rail + topbar + tweaks 隐藏 · 主内容满宽 | screenshots/P2F-08/f08-02-print-layout.json | main=1440/1440
P2F-08 | 3 | PASS | 分页正确 · 卡片不被裁断 · 加 `page-break-inside: avoid` | screenshots/P2F-08/f08-03-page-breaks.json | .obs-row:avoid
P2F-08 | 4 | PASS | 颜色 · 保留琥珀 accent · 其他灰度 | screenshots/P2F-08/f08-04-print-colors.json | accent=rgb(154, 93, 37) status=rgba(82, 82, 82, 0.86)
P2F-08 | 5 | PASS | print 顶部加 "Friday · {pageName} · {date}" 眉头 | screenshots/P2F-08/f08-05-print-header.json | Friday首页2026/04/21
P2F-09 | 1 | PASS | grep 无裸中文 / 英文 · 用脚本 check: `grep -rE '(>[\u4e00-\u9fa5])|(>[A-Z][a-z]{4,})' src/` | screenshots/P2F-09/f09-01-bare-string-grep.json | matches=0
P2F-09 | 2 | PASS | 每 user-facing 字符串走 `localize()` | screenshots/P2F-09/f09-02-localize-audit.json | localize=1127 localizedText=315
P2F-09 | 3 | PASS | 切换 locale 无 flash / FOUC | screenshots/P2F-09/f09-03-locale-switch.json | latency=1.6ms
P2F-09 | 4 | PASS | 日期 / 数字 / 货币 按 locale 格式 | screenshots/P2F-09/f09-04-locale-formatting.json | US$10,980 -> $10,980; 2026/04/21 -> 04/21/2026
P2F-09 | 5 | PASS | 中文长英文短(或反之)不裂 layout · test case 5 页面对比 | screenshots/P2F-09/f09-05-layout-compare.json | home:1440/1440, assistant:1440/1440, observability:1440/1440, settings:1440/1440, usage:1440/1440
P2F-10 | 1 | PASS | 聚合 G01-G12 通过 · 12 条 | screenshots/P2F-10/f10-01-guardrails.json | G01:PASS, G02:PASS, G03:PASS, G04:PASS, G05:PASS, G06:PASS, G07:PASS, G08:PASS, G09:PASS, G10:PASS, G11:PASS, G12:PASS
P2F-10 | 2 | PASS | 聚合 Phase 2A 12 验收(H01-H06 + C01-C06) | screenshots/P2F-10/f10-02-phase-2a.json | 12/12 targeted Phase 2A checks
P2F-10 | 3 | PASS | 聚合 Phase 2B 18(B01-B06 + S01-S05 + O01-O07) | screenshots/P2F-10/f10-03-phase-2b.json | 18/18 targeted Phase 2B checks
P2F-10 | 4 | PASS | 聚合 Phase 2C 27 | screenshots/P2F-10/f10-04-phase-2c.json | items=8 rules=92 fail=0
P2F-10 | 5 | PASS | 聚合 Phase 2D 若干 | screenshots/P2F-10/f10-05-phase-2d.json | items=6 rules=60 fail=0
P2F-10 | 6 | PASS | 聚合 Phase 2E 若干 | screenshots/P2F-10/f10-06-phase-2e.json | items=5 rules=48 fail=0
P2F-10 | 7 | PASS | 聚合 Phase 2F-01..09 | screenshots/P2F-10/f10-07-phase-2f.json | items=8 fail=0
P2F-10 | 8 | PASS | qa-report.html 每条一 row · PASS/FAIL + 证据(截图 / cmd 输出) | screenshots/P2F-10/f10-08-qa-report.json | rows=433/433 missingEvidence=0
P2F-10 | 9 | PASS | failing 条不得超过 2 · 超过 retry | screenshots/P2F-10/f10-09-fail-threshold.json | fail=0 pending=0
P2F-10 | 10 | PASS | 最终 qa-report 连同 friday-static.html 一起交付 | screenshots/P2F-10/f10-10-deliverables.json | static=1123308 qa=151635