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